keyset.pyx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import logging
  2. from libc.string cimport memcmp, memcpy
  3. from cpython.mem cimport PyMem_Malloc, PyMem_Free
  4. from lakesuperior.model.base cimport TripleKey, TRP_KLEN
  5. logger = logging.getLogger(__name__)
  6. cdef class Keyset:
  7. """
  8. Pre-allocated result set.
  9. Data in the set are stored as a 1D contiguous array of characters.
  10. Access to elements at an arbitrary index position is achieved by using the
  11. ``itemsize`` property multiplied by the index number.
  12. Key properties:
  13. ``ct``: number of elements in the set.
  14. ``itemsize``: size of each element, in bytes. All elements have the same
  15. size.
  16. ``size``: Total size, in bytes, of the data set. This is the product of
  17. ``itemsize`` and ``ct``.
  18. """
  19. def __cinit__(self, size_t ct=0):
  20. """
  21. Initialize and allocate memory for the data set.
  22. :param size_t ct: Number of elements to be accounted for.
  23. """
  24. self.ct = ct
  25. self.data = <TripleKey*>PyMem_Malloc(self.ct * TRP_KLEN)
  26. logger.info(f'data address: 0x{<size_t>self.data:02x}')
  27. if ct and not self.data:
  28. raise MemoryError('Error allocating Keyset data.')
  29. self._cur = 0
  30. self._free_i = 0
  31. def __dealloc__(self):
  32. """
  33. Free the memory.
  34. This is called when the Python instance is garbage collected, which
  35. makes it handy to safely pass a Keyset instance across functions.
  36. """
  37. #logger.debug(
  38. # 'Releasing {0} ({1}x{2}) bytes of Keyset @ {3:x}...'.format(
  39. # self.size, self.conf.capacity, self.itemsize,
  40. # <unsigned long>self.data))
  41. PyMem_Free(self.data)
  42. #logger.debug('...done releasing.')
  43. # Access methods.
  44. cdef void seek(self, size_t idx=0):
  45. """
  46. Place the cursor at a certain index, 0 by default.
  47. """
  48. self._cur = idx
  49. cdef size_t tell(self):
  50. """
  51. Tell the position of the cursor in the keyset.
  52. """
  53. return self._cur
  54. cdef bint get_at(self, size_t i, TripleKey* item):
  55. """
  56. Get an item at a given index position. Cython-level method.
  57. :rtype: TripleKey
  58. """
  59. if i >= self._free_i:
  60. return False
  61. self._cur = i
  62. item[0] = self.data[i]
  63. return True
  64. cdef bint get_next(self, TripleKey* item):
  65. """
  66. Populate the current value and advance the cursor by 1.
  67. :param void *val: Addres of value returned. It is NULL if
  68. the end of the buffer was reached.
  69. :rtype: bint
  70. :return: True if a value was found, False if the end of the buffer
  71. has been reached.
  72. """
  73. if self._cur >= self._free_i:
  74. return False
  75. item[0] = self.data[self._cur]
  76. self._cur += 1
  77. return True
  78. cdef void add(self, const TripleKey* val) except *:
  79. """
  80. Add a triple key to the array.
  81. """
  82. logger.info('Adding triple to key set.')
  83. logger.info(f'triple: {val[0][0]} {val[0][1]} {val[0][2]}')
  84. logger.info(f'_free_i: {self._free_i}')
  85. if self._free_i >= self.ct:
  86. raise MemoryError('No slots left in key set.')
  87. self.data[self._free_i] = val[0]
  88. self._free_i += 1
  89. cdef bint contains(self, const TripleKey* val):
  90. """
  91. Whether a value exists in the set.
  92. """
  93. cdef TripleKey stored_val
  94. self.seek()
  95. while self.get_next(&stored_val):
  96. if memcmp(val, stored_val, TRP_KLEN) == 0:
  97. return True
  98. return False