Skip to content

Commit

Permalink
Set JDK 17, update README.
Browse files Browse the repository at this point in the history
  • Loading branch information
io7m committed May 10, 2024
1 parent 70dabea commit 050d1a2
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 7 deletions.
140 changes: 140 additions & 0 deletions README.in
@@ -0,0 +1,140 @@

## jbssio

The `jbssio` package implements a set of types and functions for efficient,
structural binary I/O.

## Features

* Efficient binary I/O over streams, channels, and byte buffers.
* Nested, bounded stream abstraction to increase readability of code and
improve diagnostic information.
* High coverage test suite.
* [OSGi-ready](https://www.osgi.org/)
* [JPMS-ready](https://en.wikipedia.org/wiki/Java_Platform_Module_System)
* ISC license.

## Usage

The `jbssio` package provides imperative _reader_ and _writer_
abstractions. Readers and writers may be _sequential_, meaning that
they encapsulate a _stream_ in which only forward seeking is allowed,
or _random access_, meaning that they encapsulate a resource that
allows for arbitrary forwards and backwards seeking. The API attempts, as
much as is possible, to present a common API for both _sequential_
and _random access_ so that code using _readers_ and _writers_
can potentially be agnostic of the seekability of the underlying resources.
Current implementations can encapsulate:

* `java.io.OutputStream`
* `java.io.InputStream`
* `java.nio.channels.SeekableByteChannel`
* `java.nio.ByteBuffer`

_Readers_ and _writers_ are explicitly _nested_: All
offsets/positions of _readers_ and _writers_ are given relative
to their _parent_. A _reader_ or _writer_ with no parent
effectively works with _absolute_ positions within an encapsulated
resource. _Readers_ and _writers_ may be given explicit _bounds_
to limit the region of the resource from which they may read or write.
Bounds, combined with nesting, allow for very easy-to-understand code
when parsing complex binary structures. Individual _readers_ and
_writers_ are assigned names as they are created, and these names are
appended together during nesting to construct detailed diagnostic messages
when errors occur. For example:

```
java.io.IOException: Out of bounds.
Reader URI: file://tmp/example.data
Reader path: header/info:size
Reader bounds: absolute [0x0, 0x10)
Target offset: absolute 0x16
Offset: absolute 0x0
```

The above message clearly indicates that the code that was attempting
to read the `size` field, of the `info` member of the
`header` structure, ended up trying to read more data than the
_reader_ was configured to allow: The _reader_ was specified
to be allowed to read within the bounds `[0x0, 0x10)` but the code
tried to read beyond these bounds. Note that _readers_ and _writers_
do _not_ know anything about the actual binary structures being parsed;
the programmer specifies names that correspond to the structures that the
programmer is trying to parse, and the _reader_ and _writer_
implementations use these names in an attempt to construct useful
diagnostics.

_Readers_ and _writers_ operate on their underlying resources
using simple, explicit read/write methods. For example, the `readU32BE`
method defined for _readers_ reads a single, unsigned, big-endian,
32-bit integer from the current position. Methods exist for all of the
common machine types. Additionally, _readers_ and _writers_
contain convenient methods for aligning data, and for reading arbitrary byte
sequences. Calls to _reader_ or _writer_ methods advance the
current position of the _reader_ or _writer_.

### Reading Data

Retrieve a `BSSReaderType` and use it to read data:

```
ServiceLoader.load(BSSReaderProviderType.class)
.findFirst()
.orElseThrow(() -> new IllegalStateException("No reader service available"));

try (var channel = Files.newByteChannel(path, READ)) {
// Create an unbounded reader that will read from the start of the channel
try (var reader = readers.createReaderFromChannel(pathURI, channel, "root")) {

// Create a reader that is permitted to read [0, 8) relative to "reader"
try (var sr = reader.createSubReaderAtBounded("head", 0L, 8L)) {
var x = sr.readS32BE();
var y = sr.readS32BE();
}

// Create a reader that is permitted to read [8, 16) relative to "reader"
try (var sr = reader.createSubReaderAtBounded("body", 8L, 16L)) {
var a = sr.readS32BE();
var b = sr.readS32BE();
var c = sr.readS32BE();
var d = sr.readS32BE();
}
}
}
```

Using `ServiceLoader` is _not_ required: The
various providers in the `jbssio` package can
be used via `ServiceLoader`, via OSGi declarative
services, or simply instantiated manually. See the JavaDoc.

### Writing Data

Retrieve a `BSSWriterType` and use it to write data:

```
final var readers =
ServiceLoader.load(BSSWriterProviderType.class)
.findFirst()
.orElseThrow(() -> new IllegalStateException("No writer service available"));

try (var channel = Files.newByteChannel(path, WRITE, CREATE)) {
// Create an unbounded writer that will write from the start of the channel
try (var writer = writers.createWriterFromChannel(pathURI, channel, "root")) {

// Create a writer that is permitted to write [0, 8) relative to "writer"
try (var sw = writer.createSubWriterAtBounded("head", 0L, 8L)) {
sw.writeS32BE(0x10203040L);
sw.writeS32BE(0x50607080L);
}

// Create a writer that is permitted to write [8, 16) relative to "writer"
try (var sw = writer.createSubWriterAtBounded("body", 8L, 16L)) {
sw.writeS32BE(0x90909090L);
sw.writeS32BE(0x80808080L);
sw.writeS32BE(0xa0a0a0a0L);
sw.writeS32BE(0xb0b0b0b0L);
}
}
}
```
143 changes: 142 additions & 1 deletion README.md
Expand Up @@ -4,7 +4,7 @@ jbssio
[![Maven Central](https://img.shields.io/maven-central/v/com.io7m.jbssio/com.io7m.jbssio.svg?style=flat-square)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.io7m.jbssio%22)
[![Maven Central (snapshot)](https://img.shields.io/nexus/s/com.io7m.jbssio/com.io7m.jbssio?server=https%3A%2F%2Fs01.oss.sonatype.org&style=flat-square)](https://s01.oss.sonatype.org/content/repositories/snapshots/com/io7m/jbssio/)
[![Codecov](https://img.shields.io/codecov/c/github/io7m-com/jbssio.svg?style=flat-square)](https://codecov.io/gh/io7m-com/jbssio)
![Java Version](https://img.shields.io/badge/21-java?label=java&color=e6c35c)
![Java Version](https://img.shields.io/badge/17-java?label=java&color=e65cc3)

![com.io7m.jbssio](./src/site/resources/jbssio.jpg?raw=true)

Expand All @@ -14,3 +14,144 @@ jbssio
| OpenJDK (Temurin) LTS | Linux | [![Build (OpenJDK (Temurin) LTS, Linux)](https://img.shields.io/github/actions/workflow/status/io7m-com/jbssio/main.linux.temurin.lts.yml)](https://www.github.com/io7m-com/jbssio/actions?query=workflow%3Amain.linux.temurin.lts)|
| OpenJDK (Temurin) Current | Windows | [![Build (OpenJDK (Temurin) Current, Windows)](https://img.shields.io/github/actions/workflow/status/io7m-com/jbssio/main.windows.temurin.current.yml)](https://www.github.com/io7m-com/jbssio/actions?query=workflow%3Amain.windows.temurin.current)|
| OpenJDK (Temurin) LTS | Windows | [![Build (OpenJDK (Temurin) LTS, Windows)](https://img.shields.io/github/actions/workflow/status/io7m-com/jbssio/main.windows.temurin.lts.yml)](https://www.github.com/io7m-com/jbssio/actions?query=workflow%3Amain.windows.temurin.lts)|

## jbssio

The `jbssio` package implements a set of types and functions for efficient,
structural binary I/O.

## Features

* Efficient binary I/O over streams, channels, and byte buffers.
* Nested, bounded stream abstraction to increase readability of code and
improve diagnostic information.
* High coverage test suite.
* [OSGi-ready](https://www.osgi.org/)
* [JPMS-ready](https://en.wikipedia.org/wiki/Java_Platform_Module_System)
* ISC license.

## Usage

The `jbssio` package provides imperative _reader_ and _writer_
abstractions. Readers and writers may be _sequential_, meaning that
they encapsulate a _stream_ in which only forward seeking is allowed,
or _random access_, meaning that they encapsulate a resource that
allows for arbitrary forwards and backwards seeking. The API attempts, as
much as is possible, to present a common API for both _sequential_
and _random access_ so that code using _readers_ and _writers_
can potentially be agnostic of the seekability of the underlying resources.
Current implementations can encapsulate:

* `java.io.OutputStream`
* `java.io.InputStream`
* `java.nio.channels.SeekableByteChannel`
* `java.nio.ByteBuffer`

_Readers_ and _writers_ are explicitly _nested_: All
offsets/positions of _readers_ and _writers_ are given relative
to their _parent_. A _reader_ or _writer_ with no parent
effectively works with _absolute_ positions within an encapsulated
resource. _Readers_ and _writers_ may be given explicit _bounds_
to limit the region of the resource from which they may read or write.
Bounds, combined with nesting, allow for very easy-to-understand code
when parsing complex binary structures. Individual _readers_ and
_writers_ are assigned names as they are created, and these names are
appended together during nesting to construct detailed diagnostic messages
when errors occur. For example:

```
java.io.IOException: Out of bounds.
Reader URI: file://tmp/example.data
Reader path: header/info:size
Reader bounds: absolute [0x0, 0x10)
Target offset: absolute 0x16
Offset: absolute 0x0
```

The above message clearly indicates that the code that was attempting
to read the `size` field, of the `info` member of the
`header` structure, ended up trying to read more data than the
_reader_ was configured to allow: The _reader_ was specified
to be allowed to read within the bounds `[0x0, 0x10)` but the code
tried to read beyond these bounds. Note that _readers_ and _writers_
do _not_ know anything about the actual binary structures being parsed;
the programmer specifies names that correspond to the structures that the
programmer is trying to parse, and the _reader_ and _writer_
implementations use these names in an attempt to construct useful
diagnostics.

_Readers_ and _writers_ operate on their underlying resources
using simple, explicit read/write methods. For example, the `readU32BE`
method defined for _readers_ reads a single, unsigned, big-endian,
32-bit integer from the current position. Methods exist for all of the
common machine types. Additionally, _readers_ and _writers_
contain convenient methods for aligning data, and for reading arbitrary byte
sequences. Calls to _reader_ or _writer_ methods advance the
current position of the _reader_ or _writer_.

### Reading Data

Retrieve a `BSSReaderType` and use it to read data:

```
ServiceLoader.load(BSSReaderProviderType.class)
.findFirst()
.orElseThrow(() -> new IllegalStateException("No reader service available"));
try (var channel = Files.newByteChannel(path, READ)) {
// Create an unbounded reader that will read from the start of the channel
try (var reader = readers.createReaderFromChannel(pathURI, channel, "root")) {
// Create a reader that is permitted to read [0, 8) relative to "reader"
try (var sr = reader.createSubReaderAtBounded("head", 0L, 8L)) {
var x = sr.readS32BE();
var y = sr.readS32BE();
}
// Create a reader that is permitted to read [8, 16) relative to "reader"
try (var sr = reader.createSubReaderAtBounded("body", 8L, 16L)) {
var a = sr.readS32BE();
var b = sr.readS32BE();
var c = sr.readS32BE();
var d = sr.readS32BE();
}
}
}
```

Using `ServiceLoader` is _not_ required: The
various providers in the `jbssio` package can
be used via `ServiceLoader`, via OSGi declarative
services, or simply instantiated manually. See the JavaDoc.

### Writing Data

Retrieve a `BSSWriterType` and use it to write data:

```
final var readers =
ServiceLoader.load(BSSWriterProviderType.class)
.findFirst()
.orElseThrow(() -> new IllegalStateException("No writer service available"));
try (var channel = Files.newByteChannel(path, WRITE, CREATE)) {
// Create an unbounded writer that will write from the start of the channel
try (var writer = writers.createWriterFromChannel(pathURI, channel, "root")) {
// Create a writer that is permitted to write [0, 8) relative to "writer"
try (var sw = writer.createSubWriterAtBounded("head", 0L, 8L)) {
sw.writeS32BE(0x10203040L);
sw.writeS32BE(0x50607080L);
}
// Create a writer that is permitted to write [8, 16) relative to "writer"
try (var sw = writer.createSubWriterAtBounded("body", 8L, 16L)) {
sw.writeS32BE(0x90909090L);
sw.writeS32BE(0x80808080L);
sw.writeS32BE(0xa0a0a0a0L);
sw.writeS32BE(0xb0b0b0b0L);
}
}
}
```

2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -33,7 +33,7 @@
<properties>
<!-- Configuration -->
<io7m.api.previousVersion>2.0.0</io7m.api.previousVersion>
<io7m.java.targetJavaVersion>21</io7m.java.targetJavaVersion>
<io7m.java.targetJavaVersion>17</io7m.java.targetJavaVersion>

<!-- Third-party dependencies. -->
<com.io7m.junit.version>5.10.2</com.io7m.junit.version>
Expand Down
10 changes: 5 additions & 5 deletions src/site/resources/features.xml
Expand Up @@ -5,10 +5,10 @@
<li>Efficient binary I/O over streams, channels, and byte buffers.</li>
<li>Nested, bounded stream abstraction to increase readability of code and
improve diagnostic information.</li>
<li>Fully documented (JavaDoc)</li>
<li><a href="http://www.osgi.org">OSGi</a>-ready</li>
<li><a href="https://en.wikipedia.org/wiki/Java_Platform_Module_System">JPMS</a>-ready</li>
<li>ISC license</li>
<li>High-coverage automated test suite</li>
<li>Fully documented (JavaDoc).</li>
<li><a href="http://www.osgi.org">OSGi</a>-ready.</li>
<li><a href="https://en.wikipedia.org/wiki/Java_Platform_Module_System">JPMS</a>-ready.</li>
<li>ISC license.</li>
<li>High-coverage automated test suite.</li>
</ul>
</div>

0 comments on commit 050d1a2

Please sign in to comment.