gdnative_python/generation/pool_arrays_templates/pool_x_array.tmpl.pyx

327 lines
11 KiB
Cython

{% macro gd_to_py(type, src, dst) %}
{% if type['gd_value'] == type['py_value'] %}
{{ dst }} = {{ src }}
{% else %}
dst = pandemonium_string_to_pyobj(&src)
gdapi10.pandemonium_string_destroy(&src)
{% endif %}
{% endmacro %}
{% macro py_to_gd(target) %}
{% endmacro %}
{% macro render_pool_array_pyx(t) %}
@cython.final
cdef class {{ t.py_pool }}:
def __init__(self, other=None):
cdef {{ t.py_pool }} other_as_pool_array
cdef Array other_as_array
if other is None:
gdapi10.{{ t.gd_pool }}_new(&self._gd_data)
else:
try:
other_as_pool_array = <{{ t.py_pool }}?>other
gdapi10.{{ t.gd_pool }}_new_copy(&self._gd_data, &other_as_pool_array._gd_data)
except TypeError:
try:
other_as_array = <Array?>other
gdapi10.{{ t.gd_pool }}_new_with_array(&self._gd_data, &other_as_array._gd_data)
except TypeError:
gdapi10.{{ t.gd_pool }}_new(&self._gd_data)
for item in other:
{% if t.is_base_type %}
{{ t.py_pool }}.append(self, item)
{% else %}
{{ t.py_pool }}.append(self, (<{{ t.py_value }}?>item))
{% endif %}
def __dealloc__(self):
# /!\ if `__init__` is skipped, `_gd_data` must be initialized by
# hand otherwise we will get a segfault here
gdapi10.{{ t.gd_pool }}_destroy(&self._gd_data)
@staticmethod
cdef inline {{ t.py_pool }} new():
# Call to __new__ bypasses __init__ constructor
cdef {{ t.py_pool }} ret = {{ t.py_pool }}.__new__({{ t.py_pool }})
gdapi10.{{ t.gd_pool }}_new(&ret._gd_data)
return ret
@staticmethod
cdef inline {{ t.py_pool }} new_with_array(Array other):
# Call to __new__ bypasses __init__ constructor
cdef {{ t.py_pool }} ret = {{ t.py_pool }}.__new__({{ t.py_pool }})
gdapi10.{{ t.gd_pool }}_new_with_array(&ret._gd_data, &other._gd_data)
return ret
def __repr__(self):
return f"<{{ t.py_pool }}([{', '.join(repr(x) for x in self)}])>"
# Operators
def __getitem__(self, index):
cdef pandemonium_int size = self.size()
cdef pandemonium_int start
cdef pandemonium_int stop
cdef pandemonium_int step
if isinstance(index, slice):
step = index.step if index.step is not None else 1
if step == 0:
raise ValueError("range() arg 3 must not be zero")
elif step > 0:
start = index.start if index.start is not None else 0
stop = index.stop if index.stop is not None else size
else:
start = index.start if index.start is not None else size
stop = index.stop if index.stop is not None else -size - 1
return self.operator_getslice(
start,
stop,
step,
)
else:
if index < 0:
index = index + size
if index < 0 or index >= size:
raise IndexError("list index out of range")
return self.operator_getitem(index)
cdef inline {{ t.py_value }} operator_getitem(self, pandemonium_int index):
{% if t.is_base_type %}
return gdapi10.{{ t.gd_pool }}_get(&self._gd_data, index)
{% else %}
cdef {{ t.py_value }} ret = {{ t.py_value }}.__new__({{ t.py_value }})
ret._gd_data = gdapi10.{{ t.gd_pool }}_get(&self._gd_data, index)
return ret
{% endif %}
cdef inline {{ t.py_pool }} operator_getslice(self, pandemonium_int start, pandemonium_int stop, pandemonium_int step):
cdef {{ t.py_pool }} ret = {{ t.py_pool }}.new()
cdef pandemonium_int size = self.size()
if start > size - 1:
start = size - 1
elif start < 0:
start += size
if start < 0:
start = 0
if stop > size:
stop = size
elif stop < -size:
stop = -1
elif stop < 0:
stop += size
if step > 0:
if start >= stop:
return ret
items = 1 + (stop - start - 1) // step
if items <= 0:
return ret
else:
if start <= stop:
return ret
items = 1 + (stop - start + 1) // step
if items <= 0:
return ret
ret.resize(items)
cdef {{ t.gd_pool }}_read_access *src_access = gdapi10.{{ t.gd_pool }}_read(
&self._gd_data
)
cdef {{ t.gd_pool }}_write_access *dst_access = gdapi10.{{ t.gd_pool }}_write(
&ret._gd_data
)
cdef const {{ t.gd_value }} *src_ptr = gdapi10.{{ t.gd_pool }}_read_access_ptr(src_access)
cdef {{ t.gd_value }} *dst_ptr = gdapi10.{{ t.gd_pool }}_write_access_ptr(dst_access)
cdef pandemonium_int i
for i in range(items):
{% if t.is_stack_only %}
dst_ptr[i] = src_ptr[i * step + start]
{% else %}
gdapi10.{{ t.gd_value }}_destroy(&dst_ptr[i])
gdapi10.{{ t.gd_value }}_new_copy(&dst_ptr[i], &src_ptr[i * step + start])
{% endif %}
gdapi10.{{ t.gd_pool }}_read_access_destroy(src_access)
gdapi10.{{ t.gd_pool }}_write_access_destroy(dst_access)
return ret
# TODO: support slice
def __setitem__(self, pandemonium_int index, {{ t.py_value }} value):
cdef pandemonium_int size
size = self.size()
if index < 0:
index += size
if index < 0 or index >= size:
raise IndexError("list index out of range")
{% if t.is_base_type %}
gdapi10.{{ t.gd_pool }}_set(&self._gd_data, index, value)
{% else %}
gdapi10.{{ t.gd_pool }}_set(&self._gd_data, index, &value._gd_data)
{% endif %}
# TODO: support slice
def __delitem__(self, pandemonium_int index):
cdef pandemonium_int size
size = self.size()
if index < 0:
index += size
if index < 0 or index >= size:
raise IndexError("list index out of range")
gdapi10.{{ t.gd_pool }}_remove(&self._gd_data, index)
def __len__(self):
return self.size()
def __iter__(self):
# TODO: mid iteration mutation should throw exception ?
cdef int i
{% if not t.is_base_type %}
cdef {{ t.py_value }} item
{% endif %}
for i in range(self.size()):
{% if t.is_base_type %}
yield gdapi10.{{ t.gd_pool }}_get(&self._gd_data, i)
{% else %}
item = {{ t.py_value }}.__new__({{ t.py_value }})
item._gd_data = gdapi10.{{ t.gd_pool }}_get(&self._gd_data, i)
yield item
{% endif %}
def __copy__(self):
return self.copy()
def __eq__(self, other):
try:
return {{ t.py_pool }}.operator_equal(self, other)
except TypeError:
return False
def __ne__(self, other):
try:
return not {{ t.py_pool }}.operator_equal(self, other)
except TypeError:
return True
def __iadd__(self, {{ t.py_pool }} items not None):
self.append_array(items)
return self
def __add__(self, {{ t.py_pool }} items not None):
cdef {{ t.py_pool }} ret = {{ t.py_pool }}.copy(self)
ret.append_array(items)
return ret
cdef inline bint operator_equal(self, {{ t.py_pool }} other):
if other is None:
return False
# TODO `pandemonium_array_operator_equal` is missing in gdapi, submit a PR ?
cdef pandemonium_int size = self.size()
if size != other.size():
return False
cdef {{ t.gd_pool }}_read_access *a_access = gdapi10.{{ t.gd_pool }}_read(
&self._gd_data
)
cdef {{ t.gd_pool }}_read_access *b_access = gdapi10.{{ t.gd_pool }}_read(
&other._gd_data
)
cdef const {{ t.gd_value }} *a_ptr = gdapi10.{{ t.gd_pool }}_read_access_ptr(a_access)
cdef const {{ t.gd_value }} *b_ptr = gdapi10.{{ t.gd_pool }}_read_access_ptr(b_access)
cdef pandemonium_int i
cdef bint ret = True
for i in range(size):
{% if t.is_base_type %}
if a_ptr[i] != b_ptr[i]:
{% else %}
if not gdapi10.{{ t.gd_value }}_operator_equal(&a_ptr[i], &b_ptr[i]):
{% endif %}
ret = False
break
gdapi10.{{ t.gd_pool }}_read_access_destroy(a_access)
gdapi10.{{ t.gd_pool }}_read_access_destroy(b_access)
return ret
# Methods
cpdef inline {{ t.py_pool }} copy(self):
# Call to __new__ bypasses __init__ constructor
cdef {{ t.py_pool }} ret = {{ t.py_pool }}.__new__({{ t.py_pool }})
gdapi10.{{ t.gd_pool }}_new_copy(&ret._gd_data, &self._gd_data)
return ret
cpdef inline void append(self, {{ t.py_value }} data):
{% if t.is_base_type %}
gdapi10.{{ t.gd_pool }}_append(&self._gd_data, data)
{% else %}
gdapi10.{{ t.gd_pool }}_append(&self._gd_data, &data._gd_data)
{% endif %}
cdef inline void append_array(self, {{ t.py_pool }} array):
gdapi10.{{ t.gd_pool }}_append_array(&self._gd_data, &array._gd_data)
cpdef inline void invert(self):
gdapi10.{{ t.gd_pool }}_invert(&self._gd_data)
cpdef inline void push_back(self, {{ t.py_value }} data):
{% if t.is_base_type %}
gdapi10.{{ t.gd_pool }}_push_back(&self._gd_data, data)
{% else %}
gdapi10.{{ t.gd_pool }}_push_back(&self._gd_data, &data._gd_data)
{% endif %}
cpdef inline void resize(self, pandemonium_int size):
gdapi10.{{ t.gd_pool }}_resize(&self._gd_data, size)
cdef inline pandemonium_int size(self):
return gdapi10.{{ t.gd_pool }}_size(&self._gd_data)
# Raw access
@contextmanager
def raw_access(self):
cdef {{ t.gd_pool }}_write_access *access = gdapi10.{{ t.gd_pool }}_write(
&self._gd_data
)
cdef {{ t.py_pool }}WriteAccess pyaccess = {{ t.py_pool }}WriteAccess.__new__({{ t.py_pool }}WriteAccess)
pyaccess._gd_ptr = gdapi10.{{ t.gd_pool }}_write_access_ptr(access)
try:
yield pyaccess
finally:
gdapi10.{{ t.gd_pool }}_write_access_destroy(access)
@cython.final
cdef class {{ t.py_pool }}WriteAccess:
def get_address(self):
return <uintptr_t>self._gd_ptr
def __getitem__(self, int idx):
{% if t.is_base_type %}
return self._gd_ptr[idx]
{% else %}
cdef {{ t.py_value }} ret = {{ t.py_value }}.__new__({{ t.py_value }})
{% if t.is_stack_only %}
ret._gd_data = self._gd_ptr[idx]
{% else %}
gdapi10.{{ t.gd_value }}_new_copy(&ret._gd_data, &self._gd_ptr[idx])
{% endif %}
return ret
{% endif %}
def __setitem__(self, int idx, {{ t.py_value }} val):
{% if t.is_base_type %}
self._gd_ptr[idx] = val
{% elif t.is_stack_only %}
self._gd_ptr[idx] = val._gd_data
{% else %}
gdapi10.{{ t.gd_value }}_new_copy(&self._gd_ptr[idx], &val._gd_data)
{% endif %}
{% endmacro %}