pamoja.codec

Idiomatic codec facade.

Python already has json, so this facade takes and returns ordinary values and does the encoding itself, leaving callers to think in documents rather than buffers. The conversion and the packing happen in the Rust core.

  1"""Idiomatic codec facade.
  2
  3Python already has :mod:`json`, so this facade takes and returns ordinary values
  4and does the encoding itself, leaving callers to think in documents rather than
  5buffers. The conversion and the packing happen in the Rust core.
  6"""
  7
  8from __future__ import annotations
  9
 10import json
 11from typing import Any, Sequence
 12
 13from pamoja._native import Quantizer as _NativeQuantizer
 14from pamoja._native import cbor_to_json_bytes as _cbor_to_json_bytes
 15from pamoja._native import decode_delta_samples as _decode_delta_samples
 16from pamoja._native import encode_delta_samples as _encode_delta_samples
 17from pamoja._native import json_to_cbor_bytes as _json_to_cbor_bytes
 18
 19__all__ = ["Quantizer", "from_cbor", "pack_samples", "to_cbor", "unpack_samples"]
 20
 21
 22def to_cbor(value: Any) -> bytes:
 23    """Encode a value as CBOR, typically much smaller than its JSON form.
 24
 25    :param value: Any JSON-serializable value, or the raw bytes of a JSON
 26        document.
 27    :returns: The CBOR encoding.
 28    :raises PamojaError: If the value cannot be encoded.
 29    """
 30    if isinstance(value, (bytes, bytearray, memoryview)):
 31        document = bytes(value)
 32    else:
 33        document = json.dumps(value).encode("utf-8")
 34    return _json_to_cbor_bytes(document)
 35
 36
 37def from_cbor(cbor: bytes) -> Any:
 38    """Decode a CBOR document back into an ordinary Python value.
 39
 40    :param cbor: The CBOR document to decode.
 41    :returns: The decoded value.
 42    :raises PamojaError: If the document is malformed, or holds a construct with
 43        no JSON equivalent such as a non-string map key.
 44    """
 45    return json.loads(_cbor_to_json_bytes(bytes(cbor)).decode("utf-8"))
 46
 47
 48def pack_samples(samples: Sequence[int]) -> bytes:
 49    """Delta-encode a series of integer samples into a compact buffer.
 50
 51    :param samples: The samples, in order.
 52    :returns: The packed encoding, far smaller than the samples for a
 53        slow-moving series.
 54    """
 55    return _encode_delta_samples(list(samples))
 56
 57
 58def unpack_samples(data: bytes) -> list[int]:
 59    """Unpack a buffer produced by :func:`pack_samples`.
 60
 61    :param data: The packed encoding.
 62    :returns: The samples, in order.
 63    :raises PamojaError: If the buffer is malformed.
 64    """
 65    return _decode_delta_samples(bytes(data))
 66
 67
 68class Quantizer:
 69    """Packs float readings to a fixed precision, for a link charging per byte.
 70
 71    Example::
 72
 73        quantizer = Quantizer(100)  # keep two decimal places
 74        packed = quantizer.encode([20.0, 20.1, 20.2])
 75        quantizer.decode(packed)  # [20.0, 20.1, 20.2], to within 0.01
 76    """
 77
 78    __slots__ = ("_native",)
 79
 80    def __init__(self, scale: float) -> None:
 81        """Create a quantizer at the given precision.
 82
 83        :param scale: The multiplier applied before rounding; ``100`` keeps two
 84            decimal places. Must be positive and finite.
 85        :raises ValueError: If the scale is not positive and finite.
 86        """
 87        self._native = _NativeQuantizer(scale)
 88
 89    def encode(self, readings: Sequence[float]) -> bytes:
 90        """Quantize and pack a batch of readings.
 91
 92        :param readings: The readings, in order.
 93        :returns: The packed encoding.
 94        """
 95        return self._native.encode(list(readings))
 96
 97    def decode(self, data: bytes) -> list[float]:
 98        """Unpack a batch, to within this quantizer's precision.
 99
100        :param data: The encoding produced by :meth:`encode` at the same scale.
101        :returns: The readings, in order.
102        :raises PamojaError: If the buffer is malformed.
103        """
104        return self._native.decode(bytes(data))
class Quantizer:
 69class Quantizer:
 70    """Packs float readings to a fixed precision, for a link charging per byte.
 71
 72    Example::
 73
 74        quantizer = Quantizer(100)  # keep two decimal places
 75        packed = quantizer.encode([20.0, 20.1, 20.2])
 76        quantizer.decode(packed)  # [20.0, 20.1, 20.2], to within 0.01
 77    """
 78
 79    __slots__ = ("_native",)
 80
 81    def __init__(self, scale: float) -> None:
 82        """Create a quantizer at the given precision.
 83
 84        :param scale: The multiplier applied before rounding; ``100`` keeps two
 85            decimal places. Must be positive and finite.
 86        :raises ValueError: If the scale is not positive and finite.
 87        """
 88        self._native = _NativeQuantizer(scale)
 89
 90    def encode(self, readings: Sequence[float]) -> bytes:
 91        """Quantize and pack a batch of readings.
 92
 93        :param readings: The readings, in order.
 94        :returns: The packed encoding.
 95        """
 96        return self._native.encode(list(readings))
 97
 98    def decode(self, data: bytes) -> list[float]:
 99        """Unpack a batch, to within this quantizer's precision.
100
101        :param data: The encoding produced by :meth:`encode` at the same scale.
102        :returns: The readings, in order.
103        :raises PamojaError: If the buffer is malformed.
104        """
105        return self._native.decode(bytes(data))

Packs float readings to a fixed precision, for a link charging per byte.

Example::

quantizer = Quantizer(100)  # keep two decimal places
packed = quantizer.encode([20.0, 20.1, 20.2])
quantizer.decode(packed)  # [20.0, 20.1, 20.2], to within 0.01
Quantizer(scale: float)
81    def __init__(self, scale: float) -> None:
82        """Create a quantizer at the given precision.
83
84        :param scale: The multiplier applied before rounding; ``100`` keeps two
85            decimal places. Must be positive and finite.
86        :raises ValueError: If the scale is not positive and finite.
87        """
88        self._native = _NativeQuantizer(scale)

Create a quantizer at the given precision.

Parameters
  • scale: The multiplier applied before rounding; 100 keeps two decimal places. Must be positive and finite.
Raises
  • ValueError: If the scale is not positive and finite.
def encode(self, readings: Sequence[float]) -> bytes:
90    def encode(self, readings: Sequence[float]) -> bytes:
91        """Quantize and pack a batch of readings.
92
93        :param readings: The readings, in order.
94        :returns: The packed encoding.
95        """
96        return self._native.encode(list(readings))

Quantize and pack a batch of readings.

Parameters
  • readings: The readings, in order. :returns: The packed encoding.
def decode(self, data: bytes) -> list[float]:
 98    def decode(self, data: bytes) -> list[float]:
 99        """Unpack a batch, to within this quantizer's precision.
100
101        :param data: The encoding produced by :meth:`encode` at the same scale.
102        :returns: The readings, in order.
103        :raises PamojaError: If the buffer is malformed.
104        """
105        return self._native.decode(bytes(data))

Unpack a batch, to within this quantizer's precision.

Parameters
  • data: The encoding produced by encode() at the same scale. :returns: The readings, in order.
Raises
  • PamojaError: If the buffer is malformed.
def from_cbor(cbor: bytes) -> Any:
38def from_cbor(cbor: bytes) -> Any:
39    """Decode a CBOR document back into an ordinary Python value.
40
41    :param cbor: The CBOR document to decode.
42    :returns: The decoded value.
43    :raises PamojaError: If the document is malformed, or holds a construct with
44        no JSON equivalent such as a non-string map key.
45    """
46    return json.loads(_cbor_to_json_bytes(bytes(cbor)).decode("utf-8"))

Decode a CBOR document back into an ordinary Python value.

Parameters
  • cbor: The CBOR document to decode. :returns: The decoded value.
Raises
  • PamojaError: If the document is malformed, or holds a construct with no JSON equivalent such as a non-string map key.
def pack_samples(samples: Sequence[int]) -> bytes:
49def pack_samples(samples: Sequence[int]) -> bytes:
50    """Delta-encode a series of integer samples into a compact buffer.
51
52    :param samples: The samples, in order.
53    :returns: The packed encoding, far smaller than the samples for a
54        slow-moving series.
55    """
56    return _encode_delta_samples(list(samples))

Delta-encode a series of integer samples into a compact buffer.

Parameters
  • samples: The samples, in order. :returns: The packed encoding, far smaller than the samples for a slow-moving series.
def to_cbor(value: Any) -> bytes:
23def to_cbor(value: Any) -> bytes:
24    """Encode a value as CBOR, typically much smaller than its JSON form.
25
26    :param value: Any JSON-serializable value, or the raw bytes of a JSON
27        document.
28    :returns: The CBOR encoding.
29    :raises PamojaError: If the value cannot be encoded.
30    """
31    if isinstance(value, (bytes, bytearray, memoryview)):
32        document = bytes(value)
33    else:
34        document = json.dumps(value).encode("utf-8")
35    return _json_to_cbor_bytes(document)

Encode a value as CBOR, typically much smaller than its JSON form.

Parameters
  • value: Any JSON-serializable value, or the raw bytes of a JSON document. :returns: The CBOR encoding.
Raises
  • PamojaError: If the value cannot be encoded.
def unpack_samples(data: bytes) -> list[int]:
59def unpack_samples(data: bytes) -> list[int]:
60    """Unpack a buffer produced by :func:`pack_samples`.
61
62    :param data: The packed encoding.
63    :returns: The samples, in order.
64    :raises PamojaError: If the buffer is malformed.
65    """
66    return _decode_delta_samples(bytes(data))

Unpack a buffer produced by pack_samples().

Parameters
  • data: The packed encoding. :returns: The samples, in order.
Raises
  • PamojaError: If the buffer is malformed.