structo 0.0.2__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {structo-0.0.2 → structo-0.0.3}/PKG-INFO +1 -1
  2. {structo-0.0.2 → structo-0.0.3}/examples/custom_serializer.py +12 -14
  3. {structo-0.0.2 → structo-0.0.3}/examples/iterable_serializer.py +19 -19
  4. structo-0.0.3/examples/wav.py +39 -0
  5. structo-0.0.3/pytest.ini +4 -0
  6. {structo-0.0.2 → structo-0.0.3}/structo/__init__.py +5 -7
  7. structo-0.0.3/structo/object.py +58 -0
  8. structo-0.0.3/structo/serialise.py +41 -0
  9. structo-0.0.3/structo/serializer.py +20 -0
  10. {structo-0.0.2 → structo-0.0.3}/structo/types/__init__.py +6 -2
  11. structo-0.0.3/structo/types/array.py +34 -0
  12. structo-0.0.3/structo/types/blob.py +19 -0
  13. structo-0.0.3/structo/types/buffer.py +21 -0
  14. structo-0.0.3/structo/types/list.py +27 -0
  15. structo-0.0.3/structo/types/literal.py +30 -0
  16. structo-0.0.3/structo/types/object.py +51 -0
  17. structo-0.0.3/structo/types/primatives.py +101 -0
  18. structo-0.0.3/structo/types/string.py +21 -0
  19. {structo-0.0.2 → structo-0.0.3}/structo/utils.py +5 -39
  20. structo-0.0.3/tests/test_array.py +7 -0
  21. {structo-0.0.2/structo → structo-0.0.3}/tests/test_primatives.py +21 -30
  22. structo-0.0.3/tests/test_size.py +68 -0
  23. structo-0.0.3/tests/utils.py +22 -0
  24. structo-0.0.3/union.py +8 -0
  25. structo-0.0.2/examples/wav.py +0 -40
  26. structo-0.0.2/structo/object.py +0 -48
  27. structo-0.0.2/structo/serialise.py +0 -77
  28. structo-0.0.2/structo/serializer.py +0 -13
  29. structo-0.0.2/structo/tests/utils.py +0 -12
  30. structo-0.0.2/structo/types/dynamic.py +0 -98
  31. structo-0.0.2/structo/types/fixed.py +0 -82
  32. structo-0.0.2/structo/types/literal.py +0 -34
  33. structo-0.0.2/structo/types/primatives.py +0 -89
  34. {structo-0.0.2 → structo-0.0.3}/.gitignore +0 -0
  35. {structo-0.0.2 → structo-0.0.3}/LICENSE +0 -0
  36. {structo-0.0.2 → structo-0.0.3}/README.md +0 -0
  37. {structo-0.0.2 → structo-0.0.3}/pyproject.toml +0 -0
  38. {structo-0.0.2 → structo-0.0.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: structo
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: Structify
5
5
  Author-email: Ben Brady <benbradybusiness@gmail.com>
6
6
  License-Expression: MIT
@@ -1,7 +1,8 @@
1
- from structo import Serializer, serialize_to_bytes, deserialize_from_bytes
1
+ from structo import Serializer
2
2
  from dataclasses import dataclass
3
3
  import typing as t
4
4
 
5
+
5
6
  # We don't inherit from SerialiableObject
6
7
  # since we're using a custom serializer
7
8
  @dataclass
@@ -10,14 +11,9 @@ class Flags:
10
11
  flag_b: bool
11
12
  flag_c: bool
12
13
 
13
- class FlagsSerialiser(Serializer[Flags]):
14
- # This is optional, but allows for size calculations
15
- @staticmethod
16
- def length(format):
17
- return 1
18
14
 
19
- @staticmethod
20
- def write(buf, format, value):
15
+ class FlagsSerialiser(Serializer[Flags]):
16
+ def write(self, buf, value):
21
17
  byte = 0
22
18
  if value.flag_a:
23
19
  byte += 1 << 0
@@ -28,8 +24,7 @@ class FlagsSerialiser(Serializer[Flags]):
28
24
 
29
25
  byte = buf.write(bytes([byte]))
30
26
 
31
- @staticmethod
32
- def read(buf, format):
27
+ def read(self, buf):
33
28
  byte = buf.read(1)[0]
34
29
  flag_a = ((byte >> 0) & 1) != 0
35
30
  flag_b = ((byte >> 1) & 1) != 0
@@ -40,7 +35,11 @@ class FlagsSerialiser(Serializer[Flags]):
40
35
  flag_c=flag_c,
41
36
  )
42
37
 
43
- type FlagsDatatype = t.Annotated[Flags, FlagsSerialiser]
38
+ def sizeof(self):
39
+ return 1
40
+
41
+
42
+ type FlagsDatatype = t.Annotated[Flags, FlagsSerialiser()]
44
43
 
45
44
 
46
45
  value = Flags(
@@ -48,7 +47,6 @@ value = Flags(
48
47
  flag_b=True,
49
48
  flag_c=False,
50
49
  )
51
- output = serialize_to_bytes(flags_datatype, value)
50
+ output = FlagsSerialiser().to_bytes(value)
52
51
  print(output)
53
- print(deserialize_from_bytes(flags_datatype, output))
54
-
52
+ print(FlagsSerialiser().from_bytes(output))
@@ -1,42 +1,42 @@
1
+ import typing as t
2
+ from typing import Annotated
1
3
  from structo import (
2
4
  SerializableObject,
3
5
  Serializer,
4
- write_serializable,
5
- read_serializable,
6
6
  uint32_LE,
7
7
  String,
8
8
  List,
9
9
  )
10
10
  import random
11
- import typing as t
12
11
  from pathlib import Path
13
12
 
14
13
 
15
14
  class Post(SerializableObject):
16
- id: uint32_LE
17
- author: String[uint32_LE]
18
- tags: List[uint32_LE, String[uint32_LE]]
15
+ id: Annotated[int, uint32_LE]
16
+ author: Annotated[str, String(uint32_LE)]
17
+ tags: Annotated[list[str], List(String(uint32_LE), uint32_LE)]
18
+
19
+ CONTINUE_BYTE = bytes([255])
20
+ NULL_TERMINATOR = bytes([0])
19
21
 
20
22
 
21
23
  class PostsSerialiser(Serializer[t.Iterable[Post]]):
22
- @staticmethod
23
- def write(buf, format, value):
24
+ def write(self, buf, value):
24
25
  for item in value:
25
- buf.write(bytes([255]))
26
- write_serializable(buf, Post, item)
26
+ buf.write(CONTINUE_BYTE)
27
+ item.write(buf)
27
28
 
28
- buf.write(bytes([0]))
29
+ buf.write(NULL_TERMINATOR)
29
30
 
30
- @staticmethod
31
- def read(buf, format):
31
+ def read(self, buf):
32
32
  while True:
33
- continue_byte = buf.read(1)[0]
34
- if continue_byte == 0:
33
+ continue_byte = buf.read(1)
34
+ if continue_byte == NULL_TERMINATOR:
35
35
  break
36
- assert continue_byte == 255, "Continue byte was not 255"
36
+ assert continue_byte == CONTINUE_BYTE, "Continue byte was not 255"
37
37
 
38
38
  print("Loading...") # to prove it's interspliced loading and yielding
39
- yield read_serializable(buf, Post)
39
+ yield Post.read(buf)
40
40
 
41
41
 
42
42
  type PostsIterable = t.Annotated[t.Iterable[Post], PostsSerialiser()]
@@ -56,9 +56,9 @@ output = Path("output.raw")
56
56
  if not output.exists():
57
57
  with open(output, "wb") as f:
58
58
  posts = generate_posts()
59
- write_serializable(f, PostsIterable, posts)
59
+ PostsSerialiser().write(f, posts)
60
60
 
61
61
  with open(output, "rb") as f:
62
- iterable_posts = read_serializable(f, PostsIterable)
62
+ iterable_posts = PostsSerialiser().read(f)
63
63
  for post in iterable_posts:
64
64
  print(post)
@@ -0,0 +1,39 @@
1
+ from typing import Annotated
2
+ from structo import uint16_LE, uint32_LE, SerializableObject, Literal
3
+
4
+
5
+ class WavHeader(SerializableObject):
6
+ chunk_id: Annotated[bytes, Literal(b"RIFF")]
7
+ chunk_size: Annotated[int, uint32_LE]
8
+ format: Annotated[bytes, Literal(b"WAVE")]
9
+
10
+
11
+ class ChunkHeader(SerializableObject):
12
+ id: Annotated[bytes, Literal(b"fmt ", b"data")]
13
+ size: Annotated[int, uint32_LE]
14
+
15
+
16
+ class WavFormat(SerializableObject):
17
+ audio_format: Annotated[int, uint16_LE]
18
+ num_channels: Annotated[int, uint16_LE]
19
+ sample_rate: Annotated[int, uint32_LE]
20
+ byte_range: Annotated[int, uint32_LE]
21
+ block_align: Annotated[int, uint16_LE]
22
+ bits_per_sample: Annotated[int, uint16_LE]
23
+
24
+
25
+ with open("example.wav", "rb") as f:
26
+ WavHeader.read(f)
27
+
28
+ format_header = ChunkHeader.read(f)
29
+ assert format_header.id == b'fmt '
30
+
31
+ format_data = f.read(format_header.size)
32
+ format = WavFormat.from_bytes(format_data)
33
+
34
+ data_header = ChunkHeader.read(f)
35
+ assert data_header.id == b"data"
36
+ wav_data = f.read(data_header.size)
37
+
38
+ print(format)
39
+ print(len(wav_data))
@@ -0,0 +1,4 @@
1
+ [pytest]
2
+ testpaths =
3
+ tests
4
+ python_functions = " *"
@@ -2,11 +2,9 @@
2
2
  Structify
3
3
  """
4
4
 
5
- __version__ = "0.0.2"
5
+ __version__ = "0.0.3"
6
6
 
7
- from .object import SerializableObject
8
- from .serialise import write_serializable, read_serializable
9
- from .serializer import Serializer, Format
7
+ from .serializer import Serializer
10
8
  from .types import (
11
9
  uint64_BE,
12
10
  uint64_LE,
@@ -39,11 +37,11 @@ from .types import (
39
37
  List,
40
38
  String,
41
39
  Blob,
42
- Literal
40
+ Literal,
41
+ ObjectSerializer,
43
42
  )
43
+ from .object import SerializableObject
44
44
  from .utils import (
45
45
  StructoReader,
46
46
  StructifyWriter,
47
- serialize_to_bytes,
48
- deserialize_from_bytes,
49
47
  )
@@ -0,0 +1,58 @@
1
+ import io
2
+ import annotationlib
3
+ import typing as t
4
+ from dataclasses import dataclass
5
+
6
+ from .serializer import Serializer
7
+ from .serialise import get_serializer
8
+
9
+
10
+ class SerializableObjectMeta(type):
11
+ def __new__(cls, name, bases, dct):
12
+ new_class = t.cast(type, super().__new__(cls, name, bases, dct))
13
+ if name == "SerializableObject":
14
+ return new_class
15
+
16
+ annotate = annotationlib.get_annotate_from_class_namespace(dct)
17
+ _annotations: dict[str, Serializer] = {}
18
+
19
+ if annotate:
20
+ attrs = annotate(annotationlib.Format.VALUE_WITH_FAKE_GLOBALS)
21
+ for key, value in attrs.items():
22
+ try:
23
+ serializer = get_serializer(value)
24
+ _annotations[key] = serializer
25
+ except Exception as e:
26
+ raise ValueError(
27
+ f"Invalid attribute defintion for {name}.{key}"
28
+ ) from e
29
+
30
+ return t.cast(SerializableObject, dataclass(new_class))
31
+
32
+
33
+ # This is very messed up since we
34
+ @t.dataclass_transform()
35
+ class SerializableObject(metaclass=SerializableObjectMeta):
36
+ @classmethod
37
+ def serializer(cls) -> Serializer[t.Self]:
38
+ from .types import ObjectSerializer
39
+
40
+ return ObjectSerializer(cls)
41
+
42
+ @classmethod
43
+ def sizeof(cls) -> int | None:
44
+ return cls.serializer().sizeof()
45
+
46
+ def write(self, buf: io.Writer):
47
+ return self.serializer().write(buf, self)
48
+
49
+ @classmethod
50
+ def read(cls, buf: io.Reader) -> t.Self:
51
+ return cls.serializer().read(buf)
52
+
53
+ def to_bytes(self) -> bytes:
54
+ return self.serializer().to_bytes(self)
55
+
56
+ @classmethod
57
+ def from_bytes(cls, data: bytes) -> t.Self:
58
+ return cls.serializer().from_bytes(data)
@@ -0,0 +1,41 @@
1
+ import io
2
+ import typing as t
3
+ from .serializer import Serializer
4
+
5
+
6
+ def get_serializer(format: type) -> Serializer:
7
+ from .serializer import Serializer
8
+ from .object import SerializableObject
9
+ from .types.object import ObjectSerializer
10
+
11
+ if t.get_origin(format) is t.Annotated:
12
+ args = t.get_args(format)
13
+ serializers = [arg for arg in args if isinstance(arg, Serializer)]
14
+ assert len(serializers) != 0, f"No serializers for {format} found"
15
+ assert len(serializers) == 1, f"More than one serializers for {format} found"
16
+ serializer = serializers[0]
17
+
18
+ return serializer
19
+
20
+ if issubclass(format, SerializableObject):
21
+ return ObjectSerializer(format)
22
+
23
+ # Nicely formatted errors:
24
+ if format == int:
25
+ raise ValueError(
26
+ f"No serializer for int, you need to use structo.int32, structo.int32 or similar instead"
27
+ )
28
+ if format == float:
29
+ raise ValueError(
30
+ f"No serializer for float, you need to use structo.float32 or structo.float64 instead"
31
+ )
32
+ if format == bytes:
33
+ raise ValueError(
34
+ f"No serializer for bytes, you need to use structo.Buffer or structo.Blob instead"
35
+ )
36
+ if format == list:
37
+ raise ValueError(
38
+ f"No serializer for list, you need to use structo.List or structo.Array instead"
39
+ )
40
+
41
+ raise NotImplementedError(f"No serializer found for {format}")
@@ -0,0 +1,20 @@
1
+ import io
2
+
3
+
4
+ class Serializer[T]:
5
+ def sizeof(self) -> int | None:
6
+ return None
7
+
8
+ def write(self, buf: io.Writer, value: T): ...
9
+
10
+ def read(self, buf: io.Reader) -> T: ...
11
+
12
+ def to_bytes(self, value: T) -> bytes:
13
+ buf = io.BytesIO()
14
+ self.write(buf, value)
15
+ buf.seek(0)
16
+ return buf.getvalue()
17
+
18
+ def from_bytes(self, data: bytes) -> T:
19
+ buf = io.BytesIO(data)
20
+ return self.read(buf)
@@ -26,6 +26,10 @@ from .primatives import (
26
26
  float32_LE,
27
27
  float32,
28
28
  )
29
- from .fixed import Array, Buffer
30
- from .dynamic import List, String, Blob
29
+ from .buffer import Buffer
30
+ from .array import Array
31
+ from .blob import Blob
32
+ from .list import List
33
+ from .string import String
31
34
  from .literal import Literal
35
+ from .object import ObjectSerializer
@@ -0,0 +1,34 @@
1
+ from ..serializer import Serializer
2
+
3
+
4
+ class Array[T](Serializer[list[T]]):
5
+ length: int
6
+ type: Serializer[T]
7
+
8
+ def __init__(self, length: int, type: Serializer[T]) -> None:
9
+ assert length > 0, "Array must be longer than 0"
10
+ self.length = length
11
+ self.type = type
12
+
13
+ def write(self, buf, value):
14
+ assert (
15
+ len(value) == self.length
16
+ ), f"expected array with {self.length} length, receieved {len(value)}"
17
+
18
+ for item in value:
19
+ self.type.write(buf, item)
20
+
21
+ def read(self, buf):
22
+ items = []
23
+ for _ in range(self.length):
24
+ items.append(self.type.read(buf))
25
+
26
+ return items
27
+
28
+ def sizeof(self):
29
+ element_length = self.type.sizeof()
30
+ if element_length is None:
31
+ return None
32
+ else:
33
+ return element_length * self.length
34
+
@@ -0,0 +1,19 @@
1
+ from ..serializer import Serializer
2
+
3
+
4
+ class Blob(Serializer[bytes]):
5
+ "A set of arbitrary bytes, prefixed with it's length"
6
+
7
+ length_type: Serializer[int]
8
+
9
+ def __init__(self, length_type: Serializer[int]) -> None:
10
+ self.length_type = length_type
11
+
12
+ def write(self, buf, value):
13
+ self.length_type.write(buf, len(value))
14
+ buf.write(value)
15
+
16
+ def read(self, buf):
17
+ length = self.length_type.read(buf)
18
+ data = buf.read(length)
19
+ return data
@@ -0,0 +1,21 @@
1
+ from ..serializer import Serializer
2
+
3
+
4
+ class Buffer(Serializer[bytes]):
5
+ length: int
6
+
7
+ def __init__(self, length: int) -> None:
8
+ self.length = length
9
+
10
+ def write(self, buf, value):
11
+ assert (
12
+ len(value) == self.length
13
+ ), f"expected data with length {self.length}, received {len(value)}"
14
+ buf.write(value)
15
+
16
+ def read(self, buf):
17
+ data = buf.read(self.length)
18
+ return data
19
+
20
+ def sizeof(self):
21
+ return self.length
@@ -0,0 +1,27 @@
1
+ from ..serializer import Serializer
2
+
3
+
4
+ class List[T](Serializer[list[T]]):
5
+ "A list of items, prefixed with it's length"
6
+
7
+ value_type: Serializer[T]
8
+ length_type: Serializer[int]
9
+
10
+ def __init__(self, value_type: Serializer[T], length_type: Serializer[int]) -> None:
11
+ self.value_type = value_type
12
+ self.length_type = length_type
13
+
14
+ def write(self, buf, value):
15
+ length = len(value)
16
+ self.length_type.write(buf, length)
17
+ for item in value:
18
+ self.value_type.write(buf, item)
19
+
20
+ def read(self, buf):
21
+ length = self.length_type.read(buf)
22
+ values = []
23
+ for _ in range(length):
24
+ value = self.value_type.read(buf)
25
+ values.append(value)
26
+
27
+ return values
@@ -0,0 +1,30 @@
1
+ from ..serializer import Serializer
2
+
3
+
4
+ class Literal(Serializer[bytes]):
5
+ values: list[bytes]
6
+ _length: int
7
+
8
+ def __init__(self, *values: bytes) -> None:
9
+ assert len(values) > 0, "foo"
10
+ length = len(values[0])
11
+
12
+ for value in values:
13
+ assert (
14
+ len(value) == length
15
+ ), f"All values in structo.Literal have to be the same length, expected {value} to be length {length}"
16
+
17
+ self._length = length
18
+ self.values = list(values)
19
+
20
+ def write(self, buf, value):
21
+ assert value in self.values, f"{value} not in {b", ".join(self.values)}"
22
+ buf.write(self.values)
23
+
24
+ def read(self, buf):
25
+ value = buf.read(self._length)
26
+ assert value in self.values, f"{value} not in {b", ".join(self.values)}"
27
+ return value
28
+
29
+ def sizeof(self):
30
+ return self._length
@@ -0,0 +1,51 @@
1
+ import io
2
+ import annotationlib
3
+
4
+ from ..serializer import Serializer
5
+ from ..object import SerializableObject
6
+ from ..serialise import get_serializer
7
+
8
+
9
+ class ObjectSerializer[T: SerializableObject](Serializer[T]):
10
+ _annotations: dict[str, Serializer] = {}
11
+ _type: type
12
+
13
+ def __init__(self, type: type[T]) -> None:
14
+ annotations: dict[str, Serializer] = {}
15
+ self._type = type
16
+
17
+ attrs = annotationlib.get_annotations(type)
18
+ for key, value in attrs.items():
19
+ try:
20
+ annotations[key] = get_serializer(value)
21
+ except Exception as e:
22
+ raise ValueError(
23
+ f"Invalid attribute defintion for {type.__name__}.{key}"
24
+ ) from e
25
+
26
+ self._annotations = annotations
27
+
28
+ def sizeof(self) -> int | None:
29
+ total_size = 0
30
+ for serializer in self._annotations.values():
31
+ field_size = serializer.sizeof()
32
+ if field_size is None:
33
+ return None
34
+ else:
35
+ total_size += field_size
36
+
37
+ return total_size
38
+
39
+ def write(self, buf: io.Writer, value: T):
40
+ for field_key, field_format in self._annotations.items():
41
+ field_value = getattr(value, field_key)
42
+ field_format.write(buf, field_value)
43
+
44
+ def read(self, buf: io.Reader) -> T:
45
+ attrs = {}
46
+
47
+ for field_key, field_format in self._annotations.items():
48
+ field_value = field_format.read(buf)
49
+ attrs[field_key] = field_value
50
+
51
+ return self._type(**attrs)
@@ -0,0 +1,101 @@
1
+ import typing as t
2
+ import io
3
+ import struct
4
+ from ..serializer import Serializer
5
+
6
+
7
+ def struct_serializer(sformat: str, bytes: int) -> Serializer:
8
+ class StructSerializer(Serializer):
9
+ def sizeof(self):
10
+ return bytes
11
+
12
+ def write(self, buf: io.Writer, value: int):
13
+ data = struct.pack(sformat, value)
14
+ return buf.write(data)
15
+
16
+ def read(self, buf: io.Reader):
17
+ data = buf.read(bytes)
18
+ return struct.unpack(sformat, data)[0]
19
+
20
+ return StructSerializer()
21
+
22
+
23
+ uint64_BE: Serializer[int] = struct_serializer(">Q", bytes=8)
24
+ "**unsigned 64bit integer - big endian**"
25
+
26
+ uint64_LE: Serializer[int] = struct_serializer("<Q", bytes=8)
27
+ "**unsigned 64bit integer - little endian**"
28
+
29
+ uint64 = uint64_BE
30
+ "**unsigned 64bit integer - big endian**"
31
+
32
+ uint32_BE: Serializer[int] = struct_serializer(">I", bytes=4)
33
+ "**unsigned 32bit integer - big endian**"
34
+
35
+ uint32_LE: Serializer[int] = struct_serializer("<I", bytes=4)
36
+ "**unsigned 32bit integer - little endian**"
37
+
38
+ uint32 = uint32_BE
39
+ "**unsigned 32bit integer - big endian**"
40
+
41
+ uint16_BE: Serializer[int] = struct_serializer(">H", bytes=2)
42
+ "**unsigned 16bit integer - big endian**"
43
+
44
+ uint16_LE: Serializer[int] = struct_serializer("<H", bytes=2)
45
+ "**unsigned 16bit integer - little endian**"
46
+
47
+ uint16 = uint16_BE
48
+ "**unsigned 16bit integer - big endian**"
49
+
50
+ uint8: Serializer[int] = struct_serializer("B", bytes=1)
51
+ "**unsigned 8bit integer**"
52
+
53
+ int64_BE: Serializer[int] = struct_serializer(">q", bytes=8)
54
+ "**signed 64bit integer - big endian**"
55
+
56
+ int64_LE: Serializer[int] = struct_serializer("<q", bytes=8)
57
+ "**signed 64bit integer - little endian**"
58
+
59
+ int64 = int64_BE
60
+ "**signed 64bit integer - big endian**"
61
+
62
+ int32_BE: Serializer[int] = struct_serializer(">i", bytes=4)
63
+ "**signed 32bit integer - big endian**"
64
+
65
+ int32_LE: Serializer[int] = struct_serializer("<i", bytes=4)
66
+ "**signed 32bit integer - little endian**"
67
+
68
+ int32 = int32_BE
69
+ "**signed 32bit integer - big endian**"
70
+
71
+ int16_BE: Serializer[int] = struct_serializer(">h", bytes=2)
72
+ "**signed 16bit integer - big endian**"
73
+
74
+ int16_LE: Serializer[int] = struct_serializer("<h", bytes=2)
75
+ "**signed 16bit integer - little endian**"
76
+
77
+ int16 = int16_BE
78
+ "**signed 16bit integer - big endian**"
79
+
80
+ int8: Serializer[int] = struct_serializer("b", bytes=1)
81
+ "**signed 8bit integer**"
82
+
83
+
84
+ float64_BE: Serializer[float] = struct_serializer(">d", bytes=8)
85
+ "**64bit float - big endian**"
86
+
87
+ float64_LE: Serializer[float] = struct_serializer("<d", bytes=8)
88
+ "**64bit float - little endian**"
89
+
90
+ float64 = float64_BE
91
+ "**64bit float - big endian**"
92
+
93
+ float32_BE: Serializer[float] = struct_serializer(">f", bytes=4)
94
+ "**32bit float - big endian**"
95
+
96
+ float32_LE: Serializer[float] = struct_serializer("<f", bytes=4)
97
+ "**32bit float - little endian**"
98
+
99
+ float32 = float32_BE
100
+ "**32bit float - big endian**"
101
+
@@ -0,0 +1,21 @@
1
+ import typing as t
2
+ from ..serializer import Serializer
3
+
4
+
5
+ class String(Serializer[str]):
6
+ "A unicode string, prefixed with it's byte length"
7
+
8
+ length_type: Serializer[int]
9
+
10
+ def __init__(self, length_type: Serializer[int]) -> None:
11
+ self.length_type = length_type
12
+
13
+ def write(self, buf, value):
14
+ data = value.encode("utf-8")
15
+ self.length_type.write(buf, len(data))
16
+ buf.write(data)
17
+
18
+ def read(self, buf):
19
+ length = self.length_type.read(buf)
20
+ data = buf.read(length)
21
+ return data.decode("utf-8")