Pointer to a `PyObject`
| 12 | |
| 13 | |
| 14 | class Pointer(BaseObjectPointer[T]): |
| 15 | """Pointer to a `PyObject`""" |
| 16 | |
| 17 | def __repr__(self) -> str: |
| 18 | return f"Pointer(address={self.address})" |
| 19 | |
| 20 | @handle |
| 21 | def move( |
| 22 | self, |
| 23 | target: Union[T, "BasePointer[T]"], |
| 24 | *, |
| 25 | unsafe: bool = False, |
| 26 | ): |
| 27 | data = target if isinstance(target, BasePointer) else to_ptr(target) |
| 28 | |
| 29 | if not isinstance(data, BaseObjectPointer): |
| 30 | raise ValueError( |
| 31 | "pointer is not pointing to an object", |
| 32 | ) |
| 33 | |
| 34 | deref_a: T = ~data # type: ignore |
| 35 | deref_b: T = ~self |
| 36 | |
| 37 | size_a: int = sys.getsizeof(deref_a) |
| 38 | size_b: int = sys.getsizeof(deref_b) |
| 39 | refcnt = sys.getrefcount(deref_b) |
| 40 | refcnt_a = sys.getrefcount(deref_a) |
| 41 | |
| 42 | if (self._origin_size < size_a) and (not unsafe): |
| 43 | raise InvalidSizeError( |
| 44 | f"target size may not exceed current size ({size_a} < {size_b})", # noqa |
| 45 | ) |
| 46 | |
| 47 | if type(deref_a) is not type(deref_b): |
| 48 | raise TypeError( |
| 49 | "cannot move object of a different type", |
| 50 | ) |
| 51 | |
| 52 | current_address: int = self.ensure() |
| 53 | bytes_a = (ctypes.c_ubyte * size_a).from_address(data.ensure()) |
| 54 | bytes_b = (ctypes.c_ubyte * size_b).from_address(current_address) |
| 55 | |
| 56 | self.assign(~data) |
| 57 | ctypes.memmove(bytes_b, bytes_a, len(bytes_a)) |
| 58 | set_ref(deref_b, (refcnt - 1) + (refcnt_a - 2)) |
| 59 | |
| 60 | @classmethod |
| 61 | def make_from(cls, obj: Nullable[T]) -> "Pointer[T]": |
| 62 | is_null = obj is NULL |
| 63 | return Pointer( |
| 64 | id(obj) if not is_null else None, |
| 65 | not is_null, |
| 66 | ) |
| 67 | |
| 68 | |
| 69 | @handle |