11from typing import Dict , Iterator , Optional , TypeVar
22
3- from ._cstd import c_calloc
3+ from ._cstd import c_calloc , c_free
44from ._pointer import BaseAllocatedPointer
5- from .exceptions import AllocationError , NotEnoughChunks
5+ from .exceptions import AllocationError
66
77__all__ = ("AllocatedArrayPointer" , "calloc" )
88
99
1010T = 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
1322class 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
103119def calloc (num : int , size : int ) -> AllocatedArrayPointer :
104120 """Allocate a number of blocks with a given size."""
0 commit comments