Skip to content

Commit 5027d6e

Browse files
committed
broken as of now
1 parent 7d75e6d commit 5027d6e

File tree

11 files changed

+185
-83
lines changed

11 files changed

+185
-83
lines changed

src/pointers/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
from .decay import decay
1010
from .exceptions import (
1111
AllocationError, DereferenceError, FreedMemoryError,
12-
InvalidBindingParameter, InvalidSizeError, IsFrozenError,
13-
IsMallocPointerError, NotEnoughChunks
12+
InvalidBindingParameter, InvalidSizeError
1413
)
1514
from .magic import _
1615
from .malloc import AllocatedPointer, free, malloc, realloc

src/pointers/_cstd.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ctypes
2+
from ctypes.util import find_library
23
from platform import system
34
from typing import Dict, Type
45

@@ -27,7 +28,7 @@
2728
"Lconv",
2829
)
2930

30-
dll = ctypes.CDLL(platforms[system().lower()])
31+
dll = ctypes.CDLL(platforms.get(system().lower()) or find_library("c"))
3132

3233

3334
class tm(ctypes.Structure):

src/pointers/_pointer.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,12 @@ def _make_stream_and_ptr(
177177
...
178178

179179

180-
class BaseObjectPointer(Typed[T], BasePointer[T], ABC):
180+
class BaseObjectPointer(
181+
IterDereferencable[T],
182+
Typed[T],
183+
BasePointer[T],
184+
ABC,
185+
):
181186
def __init__(
182187
self,
183188
address: int,
@@ -348,6 +353,7 @@ def move(
348353
data: Union[BasePointer[T], T],
349354
unsafe: bool = False,
350355
) -> None:
356+
self.ensure_valid()
351357
from .object_pointer import to_ptr
352358

353359
data_ptr = data if isinstance(data, BasePointer) else to_ptr(data)
@@ -394,3 +400,23 @@ def _make_stream_and_ptr(
394400

395401
bytes_a = (ctypes.c_ubyte * size).from_address(address) # fmt: off
396402
return self.make_ct_pointer(), bytes(bytes_a)
403+
404+
@abstractmethod
405+
def free(self) -> None:
406+
"""Free the memory."""
407+
...
408+
409+
def ensure_valid(self) -> None:
410+
"""Ensure the memory has not been freed."""
411+
if self.freed:
412+
raise FreedMemoryError(
413+
f"{self} has been freed",
414+
)
415+
416+
@property
417+
def size(self) -> int:
418+
return self._size
419+
420+
@size.setter
421+
def size(self, value: int) -> None:
422+
self._size = value

src/pointers/bindings.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,12 @@ def _decode_type(
195195
) # fmt: off
196196

197197
res = (
198-
TypedCPointer(ctypes.addressof(res), res_typ, ctypes.sizeof(res))
198+
TypedCPointer(
199+
ctypes.addressof(res),
200+
res_typ,
201+
ctypes.sizeof(res),
202+
alt=True,
203+
)
199204
if not issubclass(type(res.contents), ctypes.Structure)
200205
else StructPointer(id(struct), type(_not_null(struct)), struct)
201206
)
@@ -366,7 +371,9 @@ def _make_char_pointer(data: StringLike) -> Union[bytes, ctypes.c_char_p]:
366371
f"{data} does not point to bytes",
367372
)
368373

369-
return ctypes.c_char_p(data.address)
374+
if is_typed_ptr and (not data.alt): # type: ignore
375+
return ctypes.c_char_p.from_address(data.ensure())
376+
return ctypes.c_char_p(data.ensure())
370377

371378
if isinstance(data, str):
372379
return data.encode()

src/pointers/c_pointer.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,18 @@ def __init__(
7575
size: int,
7676
void_p: bool = True,
7777
decref: bool = True,
78+
alt: bool = False,
7879
):
7980
self._void_p = void_p
8081
super().__init__(address, size)
8182
self._type = data_type
8283
self._decref = decref
84+
self._alt = alt
85+
86+
@property
87+
def alt(self) -> bool:
88+
"""Whether to use the alternative method for dereferencing."""
89+
return self._alt
8390

8491
@property
8592
def size(self) -> int:
@@ -137,8 +144,10 @@ def dereference(self) -> T:
137144
"""Dereference the pointer."""
138145
ctype = get_mapped(self.type)
139146

140-
if ctype is ctypes.c_char_p:
141-
return ctypes.c_char_p(self.ensure()).value # type: ignore
147+
if (ctype is ctypes.c_char_p) and (self._alt):
148+
res = ctypes.c_char_p(self.ensure()).value
149+
assert res
150+
return res # type: ignore
142151

143152
ptr = (
144153
ctype.from_address(self.ensure())
@@ -212,6 +221,7 @@ def cast(ptr: VoidPointer, data_type: Type[T]) -> TypedCPointer[T]:
212221
ptr.size,
213222
decref=False,
214223
void_p=True,
224+
alt=True,
215225
)
216226

217227

src/pointers/calloc.py

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
from typing import Dict, Iterator, Optional, TypeVar
22

3-
from ._cstd import c_calloc
3+
from ._cstd import c_calloc, c_free
44
from ._pointer import BaseAllocatedPointer
5-
from .exceptions import AllocationError, NotEnoughChunks
5+
from .exceptions import AllocationError
66

77
__all__ = ("AllocatedArrayPointer", "calloc")
88

99

1010
T = TypeVar("T")
1111

12+
"""
13+
FOR FUTURE REFERENCE:
14+
15+
_chunk_store is needed to hold each index in the pointer array accordingly.
16+
We can't just lookup each index via a memory offset, since we can't verify that the memory actually contains something.
17+
18+
If the memory is empty, then Python will segfault as it can't convert it to a PyObject*.
19+
""" # noqa
20+
1221

1322
class AllocatedArrayPointer(BaseAllocatedPointer[T]):
1423
"""Class representing memory created by calloc()"""
@@ -20,19 +29,23 @@ def __init__(
2029
chunk_size: int,
2130
current_index: int,
2231
chunk_cache: Optional[Dict[int, "AllocatedArrayPointer[T]"]] = None,
32+
freed: bool = False,
33+
origin_address: Optional[int] = None,
2334
) -> None:
35+
self._origin_address = origin_address or address
2436
self._address = address
25-
self._chunk_size = chunk_size
37+
self._size = chunk_size
2638
self._current_index = current_index
2739
self._chunks = chunks
28-
self._chunk_cache = chunk_cache or {0: self}
40+
self._chunk_store = chunk_cache or {0: self}
2941
self._assigned = True
3042
self._tracked = False
31-
self._freed = False
43+
self._freed = freed
3244

3345
if chunk_cache:
34-
self._chunk_cache[self.current_index] = self
46+
self._chunk_store[self.current_index] = self
3547

48+
# https://github.com/python/mypy/issues/4125
3649
@property # type: ignore
3750
def address(self) -> Optional[int]:
3851
return self._address
@@ -47,41 +60,31 @@ def chunks(self) -> int:
4760
"""Number of allocated chunks."""
4861
return self._chunks
4962

50-
@property
51-
def chunk_size(self) -> int:
52-
"""Size of each chunk."""
53-
return self._chunk_size
54-
55-
@property
56-
def size(self) -> int:
57-
"""Size of the current chunk."""
58-
return self._chunk_size
59-
60-
@size.setter
61-
def size(self, value: int) -> None:
62-
self._chunk_size = value # this might break things but idk
63-
64-
def __add__(self, amount: int) -> "AllocatedArrayPointer[T]":
65-
index: int = self.current_index + amount
66-
63+
def _get_chunk_at(self, index: int) -> "AllocatedArrayPointer[T]":
6764
if index > self.chunks:
68-
raise NotEnoughChunks(
69-
f"chunk index is {index}, while allocation is {self.chunks}"
65+
raise IndexError(
66+
f"index is {index}, while allocation is {self.chunks}",
7067
)
7168

7269
if index < 0: # for handling __sub__
73-
raise IndexError("chunk index is below zero")
70+
raise IndexError("index is below zero")
7471

75-
if index not in self._chunk_cache:
76-
self._chunk_cache[index] = AllocatedArrayPointer(
77-
self.ensure() + (amount * self.size),
72+
if index not in self._chunk_store:
73+
self._chunk_store[index] = AllocatedArrayPointer(
74+
self._origin_address + (index * self.size),
7875
self.chunks,
79-
self.chunk_size,
76+
self.size,
8077
index,
81-
self._chunk_cache,
78+
self._chunk_store,
79+
self._freed,
80+
self._origin_address,
8281
)
8382

84-
return self._chunk_cache[index]
83+
return self._chunk_store[index]
84+
85+
def __add__(self, amount: int) -> "AllocatedArrayPointer[T]":
86+
self.ensure_valid()
87+
return self._get_chunk_at(self._current_index + amount)
8588

8689
def __sub__(self, amount: int) -> "AllocatedArrayPointer[T]":
8790
return self.__add__(-amount)
@@ -96,9 +99,22 @@ def __iter__(self) -> Iterator["AllocatedArrayPointer[T]"]:
9699
for i in range(self.current_index, self.chunks):
97100
yield self + i
98101

102+
def __getitem__(self, index: int) -> "AllocatedArrayPointer[T]":
103+
return self._get_chunk_at(index)
104+
99105
def __del__(self):
100106
pass
101107

108+
def free(self) -> None:
109+
first = self[0]
110+
first.ensure_valid()
111+
112+
for i in range(self._chunks): # using __iter__ breaks here
113+
chunk = self._get_chunk_at(i)
114+
chunk.freed = True
115+
116+
c_free(first.make_ct_pointer())
117+
102118

103119
def calloc(num: int, size: int) -> AllocatedArrayPointer:
104120
"""Allocate a number of blocks with a given size."""

src/pointers/exceptions.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
__all__ = (
2-
"IsMallocPointerError",
32
"AllocationError",
4-
"NotEnoughChunks",
5-
"IsFrozenError",
63
"DereferenceError",
74
"FreedMemoryError",
85
"InvalidSizeError",
@@ -11,30 +8,12 @@
118
)
129

1310

14-
class IsMallocPointerError(Exception):
15-
"""Raised when trying perform an operation on a malloc pointer that isn't supported.""" # noqa
16-
17-
pass
18-
19-
2011
class AllocationError(Exception):
2112
"""Raised when a memory allocation fails."""
2213

2314
pass
2415

2516

26-
class NotEnoughChunks(Exception):
27-
"""Raised when there aren't enough chunks in a CallocPointer."""
28-
29-
pass
30-
31-
32-
class IsFrozenError(Exception):
33-
"""Raised when trying to move the address of a frozen pointer."""
34-
35-
pass
36-
37-
3817
class DereferenceError(Exception):
3918
"""Raised when dereferencing an address fails.""" # noqa
4019

src/pointers/malloc.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99

1010
T = TypeVar("T")
11+
A = TypeVar("A", bound=BaseAllocatedPointer)
1112

1213

1314
class AllocatedPointer(IterDereferencable[T], BaseAllocatedPointer[T]):
@@ -25,14 +26,6 @@ def __init__(
2526
self._assigned = assigned
2627
self._tracked = False
2728

28-
@property
29-
def size(self) -> int:
30-
return self._size
31-
32-
@size.setter
33-
def size(self, value: int) -> None:
34-
self._size = value
35-
3629
@property
3730
def address(self) -> Optional[int]:
3831
return self._address
@@ -61,6 +54,11 @@ def __sub__(self, amount: int):
6154
self.assigned,
6255
)
6356

57+
def free(self) -> None:
58+
self.ensure_valid()
59+
c_free(self.make_ct_pointer())
60+
self.freed = True
61+
6462

6563
def malloc(size: int) -> AllocatedPointer[Any]:
6664
"""Allocate memory for a given size."""
@@ -74,17 +72,10 @@ def malloc(size: int) -> AllocatedPointer[Any]:
7472

7573
def free(target: BaseAllocatedPointer):
7674
"""Free allocated memory."""
77-
if target.freed:
78-
raise ValueError(
79-
f"{target} has already been freed",
80-
)
81-
82-
ct_ptr = target.make_ct_pointer()
83-
c_free(ct_ptr)
84-
target.freed = True
75+
target.free()
8576

8677

87-
def realloc(target: AllocatedPointer, size: int) -> None:
78+
def realloc(target: A, size: int) -> A:
8879
"""Resize a memory block created by malloc."""
8980
addr = c_realloc(target.address, size)
9081

@@ -93,3 +84,4 @@ def realloc(target: AllocatedPointer, size: int) -> None:
9384

9485
target.size = size
9586
target.address = addr
87+
return target

0 commit comments

Comments
 (0)