Skip to content

Commit 0167e97

Browse files
authored
Merge pull request #24 from dapper91/dev
- some refactoring done.
2 parents 8d25c79 + 34b29f1 commit 0167e97

File tree

5 files changed

+157
-188
lines changed

5 files changed

+157
-188
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Changelog
22
=========
33

4+
0.6.1 (2023-10-06)
5+
------------------
6+
7+
- some refactoring done.
8+
9+
410
0.6.0 (2023-10-05)
511
------------------
612

generic_connection_pool/common.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,21 +279,21 @@ class BaseEventQueue(Generic[KeyType]):
279279
"""
280280

281281
def __init__(self) -> None:
282-
self._queue: RankMap[Event[KeyType]] = RankMap()
282+
self._queue: RankMap[KeyType, Event[KeyType]] = RankMap()
283283

284284
def _insert(self, timestamp: float, key: KeyType) -> None:
285285
"""
286286
Adds a new event to the queue.
287287
"""
288288

289-
self._queue.insert_or_replace(Event(timestamp, key))
289+
self._queue.insert_or_replace(key, Event(timestamp, key))
290290

291291
def _remove(self, key: KeyType) -> None:
292292
"""
293293
Remove an event from the queue.
294294
"""
295295

296-
self._queue.remove(Event(0.0, key))
296+
self._queue.remove(key)
297297

298298
def _clear(self) -> None:
299299
"""

generic_connection_pool/rankmap.py

Lines changed: 91 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,184 +1,183 @@
1-
from typing import Any, Dict, Generic, Hashable, List, Optional, Protocol, TypeVar
1+
from typing import Any, Dict, Hashable, Iterator, List, MutableMapping, Optional, Protocol, Tuple, TypeVar
22

33

44
class ComparableP(Protocol):
55
def __lt__(self, other: Any) -> bool: ...
66
def __eq__(self, other: Any) -> bool: ...
77

88

9-
class ComparableAndHashable(ComparableP, Protocol, Hashable):
10-
pass
9+
Key = TypeVar('Key', bound=Hashable)
10+
Value = TypeVar('Value', bound=ComparableP)
1111

1212

13-
Item = TypeVar('Item', bound=ComparableAndHashable)
14-
15-
16-
class RankMap(Generic[Item]):
13+
class RankMap(MutableMapping[Key, Value]):
1714
"""
18-
Extended heap data structure implementation.
19-
Similar to `heapq` but supports remove and replace operations.
20-
To support remove and replace operations items must be unique.
15+
A hash-map / binary-heap combined data structure that additionally supports the following operations:
16+
- top: get the element with the lowest value in O(1)
17+
- next: removes the element with the lowest value in O(log(n))
2118
"""
2219

2320
def __init__(self) -> None:
24-
self._heap: List[Item] = []
25-
self._index: Dict[Item, int] = {}
21+
self._heap: List[Tuple[Value, Key]] = []
22+
self._index: Dict[Key, int] = {}
2623

2724
def __len__(self) -> int:
2825
return len(self._heap)
2926

3027
def __bool__(self) -> bool:
3128
return bool(self._heap)
3229

33-
def __contains__(self, item: Item) -> bool:
34-
return item in self._index
30+
def __getitem__(self, key: Key) -> Value:
31+
idx = self._index[key]
32+
value, key = self._heap[idx]
33+
34+
return value
35+
36+
def __setitem__(self, key: Key, value: Value) -> None:
37+
self.insert_or_replace(key, value)
38+
39+
def __delitem__(self, key: Key) -> None:
40+
self.remove(key)
41+
42+
def __iter__(self) -> Iterator[Key]:
43+
return (key for value, key in self._heap)
3544

3645
def clear(self) -> None:
3746
"""
38-
Remove all items from the heap.
47+
Remove all items from the map.
3948
"""
4049

4150
self._heap.clear()
4251
self._index.clear()
4352

44-
def insert(self, item: Item) -> None:
53+
def insert(self, key: Key, value: Value) -> None:
4554
"""
46-
Inserts an item onto the heap, maintaining the heap invariant.
47-
If the item already presented raises `KeyError`.
55+
Inserts an item into the map.
56+
If an item with the provided key already presented raises `KeyError`.
4857
49-
:param item: item to be pushed
58+
:param key: item key
59+
:param value: item value
5060
"""
5161

52-
if item in self._index:
53-
raise KeyError("item already exists")
62+
if key in self._index:
63+
raise KeyError("key already exists")
5464

5565
item_idx = len(self._heap)
56-
self._heap.append(item)
57-
self._index[item] = item_idx
66+
self._heap.append((value, key))
67+
self._index[key] = item_idx
5868

5969
self._siftdown(item_idx)
6070

61-
def insert_or_replace(self, item: Item) -> None:
71+
def insert_or_replace(self, key: Key, value: Value) -> Optional[Value]:
6272
"""
63-
Inserts an item onto the heap or replaces it if it already exists.
73+
Inserts an item into the map or replaces it if it already exists.
74+
75+
:param key: item key
76+
:param value: item value
77+
:return: previous item or `None`
6478
"""
6579

66-
if item in self._index:
67-
self.remove(item)
80+
if key in self._index:
81+
prev = self.remove(key)
82+
else:
83+
prev = None
84+
85+
self.insert(key, value)
6886

69-
self.insert(item)
87+
return prev
7088

71-
def pop(self) -> Optional[Item]:
89+
def next(self) -> Optional[Value]:
7290
"""
73-
Pops the smallest item off the heap, maintaining the heap invariant.
91+
Pops the smallest item from the map.
7492
7593
:return: the smallest item
7694
"""
7795

7896
if len(self._heap) == 0:
7997
return None
8098

81-
last_item = self._heap.pop()
82-
self._index.pop(last_item)
99+
value, key = self._heap[0]
100+
return self.remove(key)
83101

84-
if self._heap:
85-
first_item = self._heap[0]
86-
self._index.pop(first_item)
87-
self._heap[0] = last_item
88-
self._index[last_item] = 0
89-
self._siftup(0)
90-
return first_item
102+
def top(self) -> Optional[Value]:
103+
"""
104+
Returns the smallest item from the map.
91105
92-
else:
93-
return last_item
106+
:return: the smallest item
107+
"""
94108

95-
def top(self) -> Optional[Item]:
96109
if len(self._heap) != 0:
97-
return self._heap[0]
110+
first_value, first_key = self._heap[0]
111+
return first_value
98112
else:
99113
return None
100114

101-
def remove(self, item: Item) -> None:
115+
def remove(self, key: Key) -> Optional[Value]:
102116
"""
103-
Removes an item from the heap.
117+
Removes an item from the map
104118
105-
:param item: item to be removed
119+
:param key: item key
120+
:returns: removed item value
106121
"""
107122

108-
if item not in self._index:
109-
return
110-
111-
idx = self._index.pop(item)
112-
if idx == len(self._heap) - 1:
113-
self._heap.pop()
114-
else:
115-
last_item = self._heap[-1]
116-
self._heap[idx] = last_item
117-
self._heap.pop()
118-
self._index[last_item] = idx
119-
self._siftup(idx)
123+
if (idx := self._index.get(key)) is None:
124+
return None
120125

121-
def replace(self, old_item: Item, new_item: Item) -> None:
122-
"""
123-
Replaces an item with the new one, maintaining the heap invariant.
124-
If the old_item not presented raises `KeyError`.
125-
If the new_item already presented raises `KeyError`.
126+
self._swap_heap_elements(idx, len(self._heap) - 1)
126127

127-
:param old_item: item to be replaces
128-
:param new_item: item the old one is replaced by
129-
"""
128+
value, key = self._heap.pop()
129+
self._index.pop(key)
130130

131-
if old_item not in self._index:
132-
raise KeyError("item not found")
131+
if idx < len(self._heap):
132+
self._siftdown(idx)
133+
self._siftup(idx)
133134

134-
if new_item in self._index:
135-
raise KeyError("item already exists")
135+
return value
136136

137-
old_idx = self._index.pop(old_item)
138-
self._heap[old_idx] = new_item
139-
self._index[new_item] = old_idx
137+
def _swap_heap_elements(self, idx1: int, idx2: int) -> None:
138+
key1 = self._heap[idx1][1]
139+
key2 = self._heap[idx2][1]
140140

141-
if new_item < old_item:
142-
self._siftdown(old_idx)
143-
else:
144-
self._siftup(old_idx)
141+
self._heap[idx1], self._heap[idx2] = self._heap[idx2], self._heap[idx1]
142+
self._index[key1] = idx2
143+
self._index[key2] = idx1
145144

146145
def _siftup(self, idx: int) -> None:
147-
item = self._heap[idx]
146+
value, key = self._heap[idx]
148147

149148
left_child_idx = 2 * idx + 1
150149
while left_child_idx < len(self._heap):
151150
right_child_idx = left_child_idx + 1
152-
if right_child_idx >= len(self._heap) or self._heap[left_child_idx] < self._heap[right_child_idx]:
151+
if right_child_idx >= len(self._heap) or self._heap[left_child_idx][0] < self._heap[right_child_idx][0]:
153152
min_child_idx = left_child_idx
154153
else:
155154
min_child_idx = right_child_idx
156155

157-
child = self._heap[min_child_idx]
158-
if item < child or item == child:
156+
child_value, child_key = self._heap[min_child_idx]
157+
if value < child_value or value == child_value:
159158
break
160159

161-
self._heap[idx] = child
162-
self._index[child] = idx
160+
self._heap[idx] = child_value, child_key
161+
self._index[child_key] = idx
163162

164163
idx = min_child_idx
165164
left_child_idx = 2 * idx + 1
166165

167-
self._heap[idx] = item
168-
self._index[item] = idx
166+
self._heap[idx] = value, key
167+
self._index[key] = idx
169168

170169
def _siftdown(self, idx: int) -> None:
171-
item = self._heap[idx]
170+
value, key = self._heap[idx]
172171

173172
while idx > 0:
174173
parent_idx = (idx - 1) // 2
175-
parent = self._heap[parent_idx]
176-
if item < parent:
177-
self._heap[idx] = parent
178-
self._index[parent] = idx
174+
parent_value, parent_key = self._heap[parent_idx]
175+
if value < parent_value:
176+
self._heap[idx] = parent_value, parent_key
177+
self._index[parent_key] = idx
179178
idx = parent_idx
180179
else:
181180
break
182181

183-
self._heap[idx] = item
184-
self._index[item] = idx
182+
self._heap[idx] = value, key
183+
self._index[key] = idx

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "generic-connection-pool"
3-
version = "0.6.0"
3+
version = "0.6.1"
44
description = "generic connection pool"
55
authors = ["Dmitry Pershin <[email protected]>"]
66
license = "Unlicense"

0 commit comments

Comments
 (0)