scikit-image
131 строка · 4.1 Кб
1"""FIFO queue that preserves history until explicitly cleared.
2
3.. warning::
4
5One must define the type "QueueItem" before including this file. This makes
6it possible to store different types as QueueItems.
7
8This queue can be operated like a class. The structure stores the state of the
9instance and the contained functions act as instance methods. The important
10distinction compared to normal queues is that popping an element doesn't remove
11it internally. Thus unless the queue's internal buffer is explicitly cleared
12(see `queue_clear`) popped items can be restored at anytime using
13`queue_restore`.
14
15A possible application of this special functionality might be when actions
16performed on popped items must be undone at a later stage.
17
18Example
19-------
20::
21
22cdef QueueWithHistory queue
23cdef QueueItem item
24
25queue_init(&queue, 5)
26try:
27for i in range(10):
28item = <QueueItem>i
29queue_push(&queue, &item)
30
31while queue_pop(&queue, &item):
32print(item) # Prints 0 to 9
33
34queue_restore(&queue)
35
36while queue_pop(&queue, &item):
37print(item) # Prints 0 to 9 again
38finally:
39queue_exit(&queue)
40"""
41
42from libc.stdlib cimport malloc, realloc, free
43
44
45# Store state of queue
46cdef struct QueueWithHistory:
47QueueItem* _buffer_ptr
48Py_ssize_t _buffer_size # Maximal number of elements the buffer can store
49Py_ssize_t _index_valid # Index to most recently inserted item
50Py_ssize_t _index_consumed # Index to most recently consumed item
51
52
53cdef inline void queue_init(QueueWithHistory* self, Py_ssize_t buffer_size) noexcept nogil:
54"""Initialize the queue and its buffer size.
55
56The size is defined as the number of queue items to fit into the initial
57buffer, thus its true memory size is `buffer_size * sizeof(QueueItem)`. Be
58sure to call `queue_exit` after calling this function to free the allocated
59memory!
60"""
61self._buffer_ptr = <QueueItem*>malloc(buffer_size * sizeof(QueueItem))
62if not self._buffer_ptr:
63with gil:
64raise MemoryError("couldn't allocate buffer")
65self._buffer_size = buffer_size
66self._index_consumed = -1
67self._index_valid = -1
68
69
70cdef inline void queue_push(QueueWithHistory* self, QueueItem* item_ptr) noexcept nogil:
71"""Enqueue a new item."""
72self._index_valid += 1
73if self._buffer_size <= self._index_valid:
74_queue_grow_buffer(self)
75self._buffer_ptr[self._index_valid] = item_ptr[0]
76
77
78cdef inline unsigned char queue_pop(QueueWithHistory* self,
79QueueItem* item_ptr) noexcept nogil:
80"""If not empty pop an item and return 1 otherwise return 0.
81
82The item is still preserved in the internal buffer and can be restored with
83`queue_restore`. To truly clear the internal buffer use `queue_clear`.
84"""
85if 0 <= self._index_consumed + 1 <= self._index_valid:
86self._index_consumed += 1
87item_ptr[0] = self._buffer_ptr[self._index_consumed]
88return 1
89return 0
90
91
92cdef inline void queue_restore(QueueWithHistory* self) noexcept nogil:
93"""Restore all consumed items to the queue.
94
95The order of the restored queue is the same as previous one, meaning older
96items are popped first.
97"""
98self._index_consumed = -1
99
100
101cdef inline void queue_clear(QueueWithHistory* self) noexcept nogil:#
102"""Remove all consumable items.
103
104After this the old items can't be restored with `queue_restore`.
105"""
106self._index_consumed = -1
107self._index_valid = -1
108
109
110cdef inline void queue_exit(QueueWithHistory* self) noexcept nogil:
111"""Free the buffer of the queue.
112
113Don't use the queue after this command unless `queue_init` is called again.
114"""
115free(self._buffer_ptr)
116
117
118cdef inline int _queue_grow_buffer(QueueWithHistory* self) except -1 nogil:
119"""Double the memory used for the buffer."""
120cdef QueueItem* new_buffer
121self._buffer_size *= 2
122new_buffer_ptr = <QueueItem*>realloc(
123self._buffer_ptr,
124self._buffer_size * sizeof(QueueItem)
125)
126if not new_buffer_ptr:
127with gil:
128raise MemoryError("couldn't reallocate buffer")
129self._buffer_ptr = new_buffer_ptr
130
131return 0
132