-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME.in
141 lines (117 loc) · 5.35 KB
/
README.in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
## 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. All exceptions raised by the API are
[structured errors](https://www.io7m.com/software/seltzer/):
```
com.io7m.seltzer.io.SIOException: 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 error 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);
}
}
}
```