{% 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 = 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 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 %}