Machine-oriented binary syntax
The preserves.binary module implements the Preserves machine-oriented binary syntax.
The main entry points are functions encode, canonicalize, decode, and decode_with_annotations.
>>> encode(Record(Symbol('hi'), []))
b'\xb4\xb3\x02hi\x84'
>>> decode(b'\xb4\xb3\x02hi\x84')
#hi()
Decoder(packet=b'', include_annotations=False, decode_embedded=lambda x: x)
Bases: BinaryCodec
Implementation of a decoder for the machine-oriented binary Preserves syntax.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
packet |
bytes
|
initial contents of the input buffer; may subsequently be extended by calling extend. |
b''
|
include_annotations |
bool
|
if |
False
|
decode_embedded |
function accepting a |
lambda x: x
|
Normal usage is to supply a buffer, and keep calling next until a ShortPacket exception is raised:
>>> d = Decoder(b'\xa0{\xb1\x05hello\x85\xb3\x01x\xb5\x84')
>>> d.next()
123
>>> d.next()
'hello'
>>> d.next()
()
>>> d.next()
Traceback (most recent call last):
...
preserves.error.ShortPacket: Short packet
Alternatively, keep calling try_next until it yields
None
, which is not in the domain of Preserves Value
s:
>>> d = Decoder(b'\xa0{\xb1\x05hello\x85\xb3\x01x\xb5\x84')
>>> d.try_next()
123
>>> d.try_next()
'hello'
>>> d.try_next()
()
>>> d.try_next()
For convenience, Decoder implements the iterator interface, backing it with try_next, so you can simply iterate over all complete values in an input:
>>> d = Decoder(b'\xa0{\xb1\x05hello\x85\xb3\x01x\xb5\x84')
>>> list(d)
[123, 'hello', ()]
>>> for v in Decoder(b'\xa0{\xb1\x05hello\x85\xb3\x01x\xb5\x84'):
... print(repr(v))
123
'hello'
()
Supply include_annotations=True
to read annotations alongside the annotated values:
>>> d = Decoder(b'\xa0{\xb1\x05hello\x85\xb3\x01x\xb5\x84', include_annotations=True)
>>> list(d)
[123, 'hello', @#x ()]
If you are incrementally reading from, say, a socket, you can use extend to add new input as if comes available:
>>> d = Decoder(b'\xa0{\xb1\x05he')
>>> d.try_next()
123
>>> d.try_next() # returns None because the input is incomplete
>>> d.extend(b'llo')
>>> d.try_next()
'hello'
>>> d.try_next()
Attributes:
Name | Type | Description |
---|---|---|
packet |
bytes
|
buffered input waiting to be processed |
index |
int
|
read position within |
Source code in preserves/binary.py
127 128 129 130 131 132 |
|
extend(data)
Appends data
to the remaining bytes in self.packet
, trimming already-processed
bytes from the front of self.packet
and resetting self.index
to zero.
Source code in preserves/binary.py
134 135 136 137 138 |
|
next()
Reads the next complete Value
from the internal buffer, raising
ShortPacket if too few bytes are available, or
DecodeError if the input is invalid somehow.
Source code in preserves/binary.py
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
|
try_next()
Like next, but returns None
instead of raising
ShortPacket.
Source code in preserves/binary.py
224 225 226 227 228 229 230 231 232 |
|
Encoder(encode_embedded=lambda x: x, canonicalize=False, include_annotations=None)
Bases: BinaryCodec
Implementation of an encoder for the machine-oriented binary Preserves syntax.
>>> e = Encoder()
>>> e.append(123)
>>> e.append('hello')
>>> e.append(annotate([], Symbol('x')))
>>> e.contents()
b'\xa0{\xb1\x05hello\x85\xb3\x01x\xb5\x84'
Parameters:
Name | Type | Description | Default |
---|---|---|---|
encode_embedded |
function accepting an Embedded.embeddedValue and
returning a |
lambda x: x
|
|
canonicalize |
bool
|
if |
False
|
include_annotations |
bool | None
|
if |
None
|
Attributes:
Name | Type | Description |
---|---|---|
buffer |
bytearray
|
accumulator for the output of the encoder |
Source code in preserves/binary.py
294 295 296 297 298 299 300 301 302 303 304 305 |
|
append(v)
Extend self.buffer
with an encoding of v
.
Source code in preserves/binary.py
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
|
contents()
Returns a bytes
constructed from the contents of self.buffer
.
Source code in preserves/binary.py
316 317 318 |
|
reset()
Clears self.buffer
to a fresh empty bytearray
.
Source code in preserves/binary.py
307 308 309 |
|
canonicalize(v, **kwargs)
As encode, but sets canonicalize=True
in the
Encoder constructor.
Source code in preserves/binary.py
434 435 436 437 438 439 |
|
decode(bs, **kwargs)
Yields the first complete encoded value from bs
, passing kwargs
through to the
Decoder constructor. Raises exceptions as per
next.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
bs |
bytes
|
encoded data to decode |
required |
Source code in preserves/binary.py
243 244 245 246 247 248 249 250 251 252 |
|
decode_with_annotations(bs, **kwargs)
Like decode, but supplying include_annotations=True
to the
Decoder constructor.
Source code in preserves/binary.py
254 255 256 257 |
|
encode(v, **kwargs)
Encode a single Value
v
to a byte string. Any supplied kwargs
are passed on to the
underlying Encoder constructor.
Source code in preserves/binary.py
427 428 429 430 431 432 |
|
Created: March 16, 2023