Skip to content

Commit 14122c7

Browse files
committed
feat: add linear constant-size array implementation
Additionally, fix some typing issues
1 parent b6cc155 commit 14122c7

14 files changed

Lines changed: 230 additions & 59 deletions

libdestruct/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313

1414
from libdestruct.c import c_int, c_long, c_str, c_uint, c_ulong
1515
from libdestruct.common import ptr
16+
from libdestruct.common.array import array, array_of
1617
from libdestruct.common.struct import ptr_to, ptr_to_self, struct
1718
from libdestruct.libdestruct import inflater
1819

19-
__all__ = ["c_int", "c_long", "c_str", "c_uint", "c_ulong", "inflater", "struct", "ptr", "ptr_to", "ptr_to_self"]
20+
__all__ = ["array", "array_of", "c_int", "c_long", "c_str", "c_uint", "c_ulong", "inflater", "struct", "ptr", "ptr_to", "ptr_to_self"]

libdestruct/c/c_str.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
from __future__ import annotations
88

99
from libdestruct.common.array.array import array
10-
from libdestruct.common.obj import obj
1110

1211

13-
class c_str(obj, array):
12+
class c_str(array):
1413
"""A C string."""
1514

1615
def size(self: c_str) -> int:
@@ -35,7 +34,7 @@ def get(self: c_str, index: int = -1) -> bytes:
3534

3635
return bytes([self.memory[self.address + index]])
3736

38-
def to_bytes(self: obj) -> bytes:
37+
def to_bytes(self: c_str) -> bytes:
3938
"""Return the serialized representation of the object."""
4039
return self.memory[self.address : self.address + self.size()]
4140

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#
2+
# This file is part of libdestruct (https://github.com/mrindeciso/libdestruct).
3+
# Copyright (c) 2024 Roberto Alessandro Bertolini. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE file in the project root for details.
5+
#
6+
7+
from libdestruct.common.array.array import array
8+
from libdestruct.common.array.array_of import array_of
9+
10+
__all__ = ["array", "array_of"]
11+
12+
import libdestruct.common.array.array_field_inflater # noqa: F401

libdestruct/common/array/arr_impl.py

Lines changed: 0 additions & 46 deletions
This file was deleted.

libdestruct/common/array/array.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66

77
from __future__ import annotations
88

9-
from abc import ABC, abstractmethod
9+
from abc import abstractmethod
1010

11+
from libdestruct.common.obj import obj
1112

12-
class array(ABC):
13+
14+
class array(obj):
1315
"""An array of objects."""
1416

1517
@abstractmethod
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#
2+
# This file is part of libdestruct (https://github.com/mrindeciso/libdestruct).
3+
# Copyright (c) 2024 Roberto Alessandro Bertolini. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE file in the project root for details.
5+
#
6+
7+
from __future__ import annotations
8+
9+
from abc import ABC, abstractmethod
10+
from typing import TYPE_CHECKING
11+
12+
if TYPE_CHECKING:
13+
from collections.abc import MutableSequence
14+
15+
from libdestruct.common.array import array
16+
from libdestruct.common.obj import obj
17+
18+
19+
class ArrayField(ABC):
20+
"""A generator for an array of items."""
21+
22+
@abstractmethod
23+
def inflate(self: ArrayField, memory: MutableSequence, address: int | tuple[obj, int]) -> array:
24+
"""Inflate the field.
25+
26+
Args:
27+
memory: The backing memory view.
28+
address: The address of the field in the memory view.
29+
"""
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#
2+
# This file is part of libdestruct (https://github.com/mrindeciso/libdestruct).
3+
# Copyright (c) 2024 Roberto Alessandro Bertolini. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE file in the project root for details.
5+
#
6+
7+
8+
from __future__ import annotations
9+
10+
from typing import TYPE_CHECKING
11+
12+
from libdestruct.common.array.linear_array_field import LinearArrayField
13+
from libdestruct.common.struct.struct import struct
14+
from libdestruct.common.struct.struct_impl import struct_impl
15+
from libdestruct.common.type_registry import TypeRegistry
16+
17+
if TYPE_CHECKING:
18+
from collections.abc import Callable, MutableSequence
19+
20+
from libdestruct.common.obj import obj
21+
22+
registry = TypeRegistry()
23+
24+
25+
def linear_array_field_inflater(
26+
field: LinearArrayField,
27+
_: type[obj],
28+
__: tuple[obj, type[obj]] | None,
29+
) -> Callable[[MutableSequence, int | tuple[obj, int]], obj]:
30+
"""Returns the inflater for an array field of a struct."""
31+
if issubclass(field.item, struct) and not issubclass(field.item, struct_impl):
32+
field.item = registry.inflater_for(field.item)
33+
34+
return field.inflate
35+
36+
37+
registry.register_instance_handler(LinearArrayField, linear_array_field_inflater)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#
2+
# This file is part of libdestruct (https://github.com/mrindeciso/libdestruct).
3+
# Copyright (c) 2024 Roberto Alessandro Bertolini. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE file in the project root for details.
5+
#
6+
7+
from __future__ import annotations
8+
9+
from typing import TYPE_CHECKING
10+
11+
from libdestruct.common.array.array import array
12+
from libdestruct.common.obj import obj
13+
14+
if TYPE_CHECKING:
15+
from collections.abc import Generator, MutableSequence
16+
17+
from libdestruct.common.obj import obj
18+
19+
20+
class array_impl(array):
21+
"""A linear sequential array."""
22+
23+
size: int
24+
"""The size of the array."""
25+
26+
def __init__(
27+
self: array_impl,
28+
memory: MutableSequence,
29+
address: int | tuple[obj, int],
30+
backing_type: obj,
31+
count: int,
32+
) -> None:
33+
"""Initialize the array."""
34+
self.memory = memory
35+
self.backing_type = backing_type
36+
self.count = count
37+
38+
if isinstance(address, tuple):
39+
self._address = None
40+
self._reference = address[0]
41+
self._offset = address[1]
42+
else:
43+
self._address = address
44+
45+
self.size = self.backing_type.size * self.count
46+
47+
def size(self: array_impl) -> int:
48+
"""Get the size of the array."""
49+
return self.size
50+
51+
def get(self: array_impl) -> str:
52+
"""Get the array as a string."""
53+
return f"[{', '.join(str(i) for i in self)}]"
54+
55+
def _set(self: array_impl, _: list[obj]) -> None:
56+
"""Set the array from a list."""
57+
raise NotImplementedError("Cannot set items in an array.")
58+
59+
def set(self: array_impl, _: list[obj]) -> None:
60+
"""Set the array from a list."""
61+
raise NotImplementedError("Cannot set items in an array.")
62+
63+
def to_bytes(self: array_impl) -> bytes:
64+
"""Return the serialized representation of the array."""
65+
return b"".join(x for x in self)
66+
67+
def __getitem__(self: array_impl, index: int) -> obj:
68+
"""Get an item from the array."""
69+
if self._address:
70+
return self.backing_type(self.memory, (self._address, index * self.backing_type.size))
71+
72+
return self.backing_type(self.memory, (self._reference, self._offset + index * self.backing_type.size))
73+
74+
def __setitem__(self: array_impl, index: int, value: obj) -> None:
75+
"""Set an item in the array."""
76+
raise NotImplementedError("Cannot set items in an array.")
77+
78+
def __iter__(self: array_impl) -> Generator[obj, None, None]:
79+
"""Iterate over the array."""
80+
for i in range(self.count):
81+
yield self[i]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#
2+
# This file is part of libdestruct (https://github.com/mrindeciso/libdestruct).
3+
# Copyright (c) 2024 Roberto Alessandro Bertolini. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE file in the project root for details.
5+
#
6+
7+
from __future__ import annotations
8+
9+
from typing import TYPE_CHECKING
10+
11+
from libdestruct.common.array.linear_array_field import LinearArrayField
12+
13+
if TYPE_CHECKING:
14+
from libdestruct.common.array.array_field import ArrayField
15+
from libdestruct.common.obj import obj
16+
17+
18+
def array_of(item: type[obj], size: int) -> ArrayField:
19+
"""Create an array of items."""
20+
return LinearArrayField(item, size)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#
2+
# This file is part of libdestruct (https://github.com/mrindeciso/libdestruct).
3+
# Copyright (c) 2024 Roberto Alessandro Bertolini. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE file in the project root for details.
5+
#
6+
7+
from __future__ import annotations
8+
9+
from typing import TYPE_CHECKING
10+
11+
from libdestruct.common.array.array_field import ArrayField
12+
from libdestruct.common.array.array_impl import array_impl
13+
14+
if TYPE_CHECKING:
15+
from libdestruct.common.array.array import array
16+
from libdestruct.common.obj import obj
17+
18+
19+
class LinearArrayField(ArrayField):
20+
"""A generator for an array of items."""
21+
22+
def __init__(self: LinearArrayField, item: type[obj], size: int) -> None:
23+
"""Initialize the field."""
24+
self.item = item
25+
self.size = size
26+
27+
def inflate(self: LinearArrayField, memory: list, address: int | tuple[obj, int]) -> array:
28+
"""Inflate the field.
29+
30+
Args:
31+
memory: The backing memory view.
32+
address: The address of the field in the memory view.
33+
"""
34+
return array_impl(memory, address, self.item, self.size)

0 commit comments

Comments
 (0)