1
0

CMap.py 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  1. #!/usr/bin/env python
  2. # The contents of this file are subject to the BitTorrent Open Source License
  3. # Version 1.1 (the License). You may not copy or use this file, in either
  4. # source code or executable form, except in compliance with the License. You
  5. # may obtain a copy of the License at http://www.bittorrent.com/license/.
  6. #
  7. # Software distributed under the License is distributed on an AS IS basis,
  8. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  9. # for the specific language governing rights and limitations under the
  10. # License.
  11. #
  12. # By David Harrison
  13. # I was playing with doctest when I wrote this. I still haven't
  14. # decided how useful doctest is as opposed to implementing unit tests
  15. # directly. --Dave
  16. if __name__ == '__main__':
  17. import sys
  18. sys.path = ['.','..'] + sys.path # HACK to simplify unit testing.
  19. from BTL.translation import _
  20. class BEGIN: # represents special BEGIN location before first next.
  21. pass
  22. from UserDict import DictMixin
  23. from cmap_swig import *
  24. import sys
  25. from weakref import WeakKeyDictionary
  26. LEAK_TEST = False
  27. class CMap(object,DictMixin):
  28. """In-order mapping. Provides same operations and behavior as a dict,
  29. but provides in-order iteration. Additionally provides operations to
  30. find the nearest key <= or >= a given key.
  31. This provides a significantly wider set of operations than
  32. berkeley db BTrees, but it provides no means for persistence.
  33. LIMITATION: The key must be a python numeric type, e.g., an integer
  34. or a float. The value can be any python object.
  35. Operation: Time Applicable
  36. Complexity: Methods:
  37. ---------------------------------------------------
  38. Item insertion: O(log n) append, __setitem__
  39. Item deletion: O(log n + k) __delitem__, erase
  40. Key search: O(log n) __getitem__, get, find,
  41. __contains__
  42. Value search: n/a
  43. Iteration step: amortized O(1), next, prev
  44. worst-case O(log n)
  45. Memory: O(n)
  46. n = number of elements in map. k = number of iterators pointing
  47. into map. CMap assumes there are few iterators in existence at
  48. any given time.
  49. Iterators are not invalidated by insertions. Iterators are
  50. invalidated by deletions only when the key-value pair
  51. referenced is deleted. Deletion has a '+k' because the
  52. __delitem__ searches linearly through the set of iterators
  53. pointing into this map to find any iterator pointing at the
  54. deleted item and then invalidates the iterator.
  55. This class is backed by the C++ STL map class, but conforms
  56. to the Python container interface."""
  57. class _AbstractIterator:
  58. """Iterates over elements in the map in order."""
  59. def __init__(self, m, si = BEGIN ): # "s.." implies swig object.
  60. """Creates an iterator pointing to element si in map m.
  61. Do not instantiate directly. Use iterkeys, itervalues, or
  62. iteritems.
  63. The _AbstractIterator takes ownership of any C++ iterator
  64. (i.e., the swig object 'si') and will deallocate it when
  65. the iterator is deallocated.
  66. Examples of typical behavior:
  67. >>> from CMap import *
  68. >>> m = CMap()
  69. >>> m[12] = 6
  70. >>> m[9] = 4
  71. >>> for k in m:
  72. ... print int(k)
  73. ...
  74. 9
  75. 12
  76. >>>
  77. Example edge cases (empty map):
  78. >>> from CMap import *
  79. >>> m = CMap()
  80. >>> try:
  81. ... i = m.__iter__()
  82. ... i.value()
  83. ... except IndexError:
  84. ... print 'IndexError.'
  85. ...
  86. IndexError.
  87. >>> try:
  88. ... i.next()
  89. ... except StopIteration:
  90. ... print 'stopped'
  91. ...
  92. stopped
  93. @param map: CMap.
  94. @param node: Node that this iterator will point at. If None
  95. then the iterator points to end(). If BEGIN
  96. then the iterator points to one before the beginning.
  97. """
  98. assert isinstance(m, CMap)
  99. assert not isinstance(si, CMap._AbstractIterator)
  100. if si == None:
  101. self._si = map_end(m._smap)
  102. else:
  103. self._si = si # C++ iterator wrapped by swig.
  104. self._map = m
  105. m._iterators[self] = 1 # using map as set of weak references.
  106. def __hash__(self):
  107. return id(self)
  108. def __cmp__(self, other):
  109. if not self._si or not other._si:
  110. raise RuntimeError( _("invalid iterator") )
  111. if self._si == BEGIN and other._si == BEGIN: return 0
  112. if self._si == BEGIN and other._si != BEGIN: return -1
  113. elif self._si != BEGIN and other._si == BEGIN: return 1
  114. return iter_cmp(self._map._smap, self._si, other._si )
  115. def at_begin(self):
  116. """equivalent to self == m.begin() where m is a CMap.
  117. >>> from CMap import CMap
  118. >>> m = CMap()
  119. >>> i = m.begin()
  120. >>> i == m.begin()
  121. True
  122. >>> i.at_begin()
  123. True
  124. >>> i == m.end() # no elements so begin()==end()
  125. True
  126. >>> i.at_end()
  127. True
  128. >>> m[6] = 'foo' # insertion does not invalidate iterators.
  129. >>> i = m.begin()
  130. >>> i == m.end()
  131. False
  132. >>> i.value()
  133. 'foo'
  134. >>> try: # test at_begin when not at beginning.
  135. ... i.next()
  136. ... except StopIteration:
  137. ... print 'ok'
  138. ok
  139. >>> i.at_begin()
  140. False
  141. """
  142. if not self._si:
  143. raise RuntimeError( _("invalid iterator") )
  144. if self._si == BEGIN: # BEGIN is one before begin(). Yuck!!
  145. return False
  146. return map_iter_at_begin(self._map._smap, self._si)
  147. def at_end(self):
  148. """equivalent to self == m.end() where m is a CMap, but
  149. at_end is faster because it avoids the dynamic memory
  150. alloation in m.end().
  151. >>> from CMap import CMap
  152. >>> m = CMap()
  153. >>> m[6] = 'foo'
  154. >>> i = m.end() # test when at end.
  155. >>> i == m.end()
  156. True
  157. >>> i.at_end()
  158. True
  159. >>> int(i.prev())
  160. 6
  161. >>> i.at_end() # testing when not at end.
  162. False
  163. """
  164. if not self._si:
  165. raise RuntimeError( _("invalid iterator") )
  166. if self._si == BEGIN:
  167. return False
  168. return map_iter_at_end(self._map._smap, self._si)
  169. def key(self):
  170. """@return: the key of the key-value pair referenced by this
  171. iterator.
  172. """
  173. if not self._si:
  174. raise RuntimeError( _("invalid iterator") )
  175. if self._si == BEGIN:
  176. raise IndexError(_("Cannot dereference iterator until after "
  177. "first call to .next."))
  178. elif map_iter_at_end(self._map._smap, self._si):
  179. raise IndexError()
  180. return iter_key(self._si)
  181. def value(self):
  182. """@return: the value of the key-value pair currently referenced
  183. by this iterator.
  184. """
  185. if not self._si:
  186. raise RuntimeError( _("invalid iterator") )
  187. if self._si == BEGIN:
  188. raise IndexError(_("Cannot dereference iterator until after "
  189. "first call to next."))
  190. elif map_iter_at_end(self._map._smap, self._si):
  191. raise IndexError()
  192. return iter_value(self._si)
  193. def item(self):
  194. """@return the key-value pair referenced by this iterator.
  195. """
  196. if not self._si:
  197. raise RuntimeError( _("invalid iterator") )
  198. return self.key(), self.value()
  199. def _next(self):
  200. if not self._si:
  201. raise RuntimeError( _("invalid iterator") )
  202. if self._si == BEGIN:
  203. self._si = map_begin(self._map._smap)
  204. if map_iter_at_end(self._map._smap,self._si):
  205. raise StopIteration
  206. return
  207. if map_iter_at_end(self._map._smap,self._si):
  208. raise StopIteration
  209. iter_incr(self._si)
  210. if map_iter_at_end(self._map._smap,self._si):
  211. raise StopIteration
  212. def _prev(self):
  213. if not self._si:
  214. raise RuntimeError( _("invalid iterator") )
  215. if self._si == BEGIN:
  216. raise StopIteration()
  217. elif map_iter_at_begin(self._map._smap, self._si):
  218. self._si = BEGIN
  219. raise StopIteration
  220. iter_decr(self._si)
  221. def __del__(self):
  222. # Python note: if a reference to x is intentionally
  223. # eliminated using "del x" and there are other references
  224. # to x then __del__ does not get called at this time.
  225. # Only when the last reference is deleted by an intentional
  226. # "del" or when the reference goes out of scope does
  227. # the __del__ method get called.
  228. self._invalidate()
  229. def _invalidate(self):
  230. if self._si == None:
  231. return
  232. try:
  233. del self._map._iterators[self]
  234. except KeyError:
  235. pass # could've been removed because weak reference,
  236. # and because _invalidate is called from __del__.
  237. if self._si != BEGIN:
  238. iter_delete(self._si)
  239. self._si = None
  240. def __iter__(self):
  241. """If the iterator is itself iteratable then we do things like:
  242. >>> from CMap import CMap
  243. >>> m = CMap()
  244. >>> m[10] = 'foo'
  245. >>> m[11] = 'bar'
  246. >>> for x in m.itervalues():
  247. ... print x
  248. ...
  249. foo
  250. bar
  251. """
  252. return self
  253. def __len__(self):
  254. return len(self._map)
  255. class KeyIterator(_AbstractIterator):
  256. def next(self):
  257. """Returns the next key in the map.
  258. Insertion does not invalidate iterators. Deletion only
  259. invalidates an iterator if the iterator pointed at the
  260. key-value pair being deleted.
  261. This is implemented by moving the iterator and then
  262. dereferencing it. If we dereferenced and then moved
  263. then we would get the odd behavior:
  264. Ex: I have keys [1,2,3]. The iterator i points at 1.
  265. print i.next() # prints 1
  266. print i.next() # prints 2
  267. print i.prev() # prints 3
  268. print i.prev() # prints 2
  269. However, because we move and then dereference, when an
  270. iterator is first created it points to nowhere
  271. so that the first next moves to the first element.
  272. Ex:
  273. >>> from CMap import *
  274. >>> m = CMap()
  275. >>> m[5] = 1
  276. >>> m[8] = 4
  277. >>> i = m.__iter__()
  278. >>> print int(i.next())
  279. 5
  280. >>> print int(i.next())
  281. 8
  282. >>> print int(i.prev())
  283. 5
  284. We are still left with the odd behavior that an
  285. iterator cannot be dereferenced until after the first next().
  286. Ex edge cases:
  287. >>> from CMap import CMap
  288. >>> m = CMap()
  289. >>> i = m.__iter__()
  290. >>> try:
  291. ... i.prev()
  292. ... except StopIteration:
  293. ... print 'StopIteration'
  294. ...
  295. StopIteration
  296. >>> m[5]='a'
  297. >>> i = m.iterkeys()
  298. >>> int(i.next())
  299. 5
  300. >>> try: i.next()
  301. ... except StopIteration: print 'StopIteration'
  302. ...
  303. StopIteration
  304. >>> int(i.prev())
  305. 5
  306. >>> try: int(i.prev())
  307. ... except StopIteration: print 'StopIteration'
  308. ...
  309. StopIteration
  310. >>> int(i.next())
  311. 5
  312. """
  313. self._next()
  314. return self.key()
  315. def prev(self):
  316. """Returns the previous key in the map.
  317. See next() for more detail and examples.
  318. """
  319. self._prev()
  320. return self.key()
  321. class ValueIterator(_AbstractIterator):
  322. def next(self):
  323. """@return: next value in the map.
  324. >>> from CMap import *
  325. >>> m = CMap()
  326. >>> m[5] = 10
  327. >>> m[6] = 3
  328. >>> i = m.itervalues()
  329. >>> int(i.next())
  330. 10
  331. >>> int(i.next())
  332. 3
  333. """
  334. self._next()
  335. return self.value()
  336. def prev(self):
  337. self._prev()
  338. return self.value()
  339. class ItemIterator(_AbstractIterator):
  340. def next(self):
  341. """@return: next item in the map's key ordering.
  342. >>> from CMap import CMap
  343. >>> m = CMap()
  344. >>> m[5] = 10
  345. >>> m[6] = 3
  346. >>> i = m.iteritems()
  347. >>> k,v = i.next()
  348. >>> int(k)
  349. 5
  350. >>> int(v)
  351. 10
  352. >>> k,v = i.next()
  353. >>> int(k)
  354. 6
  355. >>> int(v)
  356. 3
  357. """
  358. self._next()
  359. return self.key(), self.value()
  360. def prev(self):
  361. self._prev()
  362. return self.key(), self.value()
  363. def __init__(self, d={} ):
  364. """Instantiate RBTree containing values from passed dict and
  365. ordered based on cmp.
  366. >>> m = CMap()
  367. >>> len(m)
  368. 0
  369. >>> m[5]=2
  370. >>> len(m)
  371. 1
  372. >>> print m[5]
  373. 2
  374. """
  375. #self._index = {} # to speed up searches.
  376. self._smap = map_constructor() # C++ map wrapped by swig.
  377. for key, value in d.items():
  378. self[key]=value
  379. self._iterators = WeakKeyDictionary()
  380. # whenever node is deleted. search iterators
  381. # for any iterator that becomes invalid.
  382. def __contains__(self,x):
  383. return self.get(x) != None
  384. def __iter__(self):
  385. """@return: KeyIterator positioned one before the beginning of the
  386. key ordering so that the first next() returns the first key."""
  387. return CMap.KeyIterator(self)
  388. def begin(self):
  389. """Returns an iterator pointing at first key-value pair. This
  390. differs from iterkeys, itervalues, and iteritems which return an
  391. iterator pointing one before the first key-value pair.
  392. @return: key iterator to first key-value.
  393. >>> from CMap import *
  394. >>> m = CMap()
  395. >>> m[5.0] = 'a'
  396. >>> i = m.begin()
  397. >>> int(i.key()) # raises no IndexError.
  398. 5
  399. >>> i = m.iterkeys()
  400. >>> try:
  401. ... i.key()
  402. ... except IndexError:
  403. ... print 'IndexError raised'
  404. ...
  405. IndexError raised
  406. """
  407. i = CMap.KeyIterator(self, map_begin(self._smap) )
  408. return i
  409. def end(self):
  410. """Returns an iterator pointing after end of key ordering.
  411. The iterator's prev method will move to the last
  412. key-value pair in the ordering. This in keeping with
  413. the notion that a range is specified as [i,j) where
  414. j is not in the range, and the range [i,j) where i==j
  415. is an empty range.
  416. This operation takes O(1) time.
  417. @return: key iterator one after end.
  418. """
  419. i = CMap.KeyIterator(self,None) # None means one after last node.
  420. return i
  421. def iterkeys(self):
  422. return CMap.KeyIterator(self)
  423. def itervalues(self):
  424. return CMap.ValueIterator(self)
  425. def iteritems(self):
  426. return CMap.ItemIterator(self)
  427. def __len__(self):
  428. return map_size(self._smap)
  429. def __str__(self):
  430. s = "{"
  431. first = True
  432. for k,v in self.items():
  433. if first:
  434. first = False
  435. else:
  436. s += ", "
  437. if type(v) == str:
  438. s += "%s: '%s'" % (k,v)
  439. else:
  440. s += "%s: %s" % (k,v)
  441. s += "}"
  442. return s
  443. def __repr__(self):
  444. return self.__str__()
  445. def __getitem__(self, key):
  446. # IMPL 1: without _index
  447. return map_find(self._smap,key) # raises KeyError if key not found
  448. # IMPL 2: with _index.
  449. #return iter_value(self._index[key])
  450. def __setitem__(self, key, value):
  451. """
  452. >>> from CMap import CMap
  453. >>> m = CMap()
  454. >>> m[6] = 'bar'
  455. >>> m[6]
  456. 'bar'
  457. >>>
  458. """
  459. assert type(key) == int or type(key) == float
  460. # IMPL 1. without _index.
  461. map_set(self._smap,key,value)
  462. ## IMPL 2. with _index
  463. ## If using indices following allows us to perform only one search.
  464. #i = map_insert_iter(self._smap,key,value)
  465. #if iter_value(i) != value:
  466. # iter_set(i,value)
  467. #else: self._index[key] = i
  468. ## END IMPL2
  469. def __delitem__(self, key):
  470. """Deletes the item with matching key from the map.
  471. This takes O(log n + k) where n is the number of elements
  472. in the map and k is the number of iterators pointing into the map.
  473. Before deleting the item it linearly searches through
  474. all iterators pointing into the map and invalidates any that
  475. are pointing at the item about to be deleted.
  476. >>> from CMap import CMap
  477. >>> m = CMap()
  478. >>> m[12] = 'foo'
  479. >>> m[13] = 'bar'
  480. >>> m[14] = 'boo'
  481. >>> del m[12]
  482. >>> try:
  483. ... m[12]
  484. ... except KeyError:
  485. ... print 'ok'
  486. ...
  487. ok
  488. >>> j = m.begin()
  489. >>> int(j.next())
  490. 14
  491. >>> i = m.begin()
  492. >>> i.value()
  493. 'bar'
  494. >>> del m[13] # delete object referenced by an iterator
  495. >>> try:
  496. ... i.value()
  497. ... except RuntimeError:
  498. ... print 'ok'
  499. ok
  500. >>> j.value() # deletion should not invalidate other iterators.
  501. 'boo'
  502. """
  503. #map_erase( self._smap, key ) # map_erase is dangerous. It could
  504. # delete the node causing an iterator
  505. # to become invalid. --Dave
  506. si = map_find_iter( self._smap, key ) # si = swig'd iterator.
  507. if map_iter_at_end(self._smap, si):
  508. iter_delete(si)
  509. raise KeyError(key)
  510. for i in list(self._iterators):
  511. if iter_cmp( self._smap, i._si, si ) == 0:
  512. i._invalidate()
  513. map_iter_erase( self._smap, si )
  514. iter_delete(si)
  515. #iter_delete( self._index[key] ) # IMPL 2. with _index.
  516. #del self._index[key] # IMPL 2. with _index.
  517. def erase(self, iter):
  518. """Remove item pointed to by the iterator. All iterators that
  519. point at the erased item including the passed iterator
  520. are immediately invalidated after the deletion completes.
  521. >>> from CMap import CMap
  522. >>> m = CMap()
  523. >>> m[12] = 'foo'
  524. >>> i = m.find(12)
  525. >>> m.erase(i)
  526. >>> len(m) == 0
  527. True
  528. """
  529. if not iter._si:
  530. raise RuntimeError( _("invalid iterator") )
  531. if iter._si == BEGIN:
  532. raise IndexError(_("Iterator does not point at key-value pair" ))
  533. if self is not iter._map:
  534. raise IndexError(_("Iterator points into a different CMap."))
  535. if map_iter_at_end(self._smap, iter._si):
  536. raise IndexError( _("Cannot erase end() iterator.") )
  537. # invalidate iterators.
  538. for i in list(self._iterators):
  539. if iter._si is not i._si and iiter_cmp( self._smmap, iter._si, i._si ) == 0:
  540. i._invalidate()
  541. # remove item from the map.
  542. map_iter_erase( self._smap, iter._si )
  543. # invalidate last iterator pointing to the deleted location in the map.
  544. iter._invalidate()
  545. def __del__(self):
  546. # invalidate all iterators.
  547. for i in list(self._iterators):
  548. i._invalidate()
  549. map_delete(self._smap)
  550. def get(self, key, default=None):
  551. """@return value corresponding to specified key or return 'default'
  552. if the key is not found.
  553. """
  554. try:
  555. return map_find(self._smap,key) # IMPL 1. without _index.
  556. #return iter_value(self._index[key]) # IMPL 2. with _index.
  557. except KeyError:
  558. return default
  559. def keys(self):
  560. """
  561. >>> from CMap import *
  562. >>> m = CMap()
  563. >>> m[4.0] = 7
  564. >>> m[6.0] = 3
  565. >>> [int(x) for x in m.keys()] # m.keys() but guaranteed integers.
  566. [4, 6]
  567. """
  568. k = []
  569. for key in self:
  570. k.append(key)
  571. return k
  572. def values(self):
  573. """
  574. >>> from CMap import CMap
  575. >>> m = CMap()
  576. >>> m[4.0] = 7
  577. >>> m[6.0] = 3
  578. >>> m.values()
  579. [7, 3]
  580. """
  581. i = self.itervalues()
  582. v = []
  583. try:
  584. while True:
  585. v.append(i.next())
  586. except StopIteration:
  587. pass
  588. return v
  589. def items(self):
  590. """
  591. >>> from CMap import CMap
  592. >>> m = CMap()
  593. >>> m[4.0] = 7
  594. >>> m[6.0] = 3
  595. >>> [(int(x[0]),int(x[1])) for x in m.items()]
  596. [(4, 7), (6, 3)]
  597. """
  598. i = self.iteritems()
  599. itms = []
  600. try:
  601. while True:
  602. itms.append(i.next())
  603. except StopIteration:
  604. pass
  605. return itms
  606. def has_key(self, key):
  607. """
  608. >>> from CMap import CMap
  609. >>> m = CMap()
  610. >>> m[4.0] = 7
  611. >>> if m.has_key(4): print 'ok'
  612. ...
  613. ok
  614. >>> if not m.has_key(7): print 'ok'
  615. ...
  616. ok
  617. """
  618. try:
  619. self[key]
  620. except KeyError:
  621. return False
  622. return True
  623. def clear(self):
  624. """delete all entries
  625. >>> from CMap import CMap
  626. >>> m = CMap()
  627. >>> m[4] = 7
  628. >>> m.clear()
  629. >>> print len(m)
  630. 0
  631. """
  632. self.__del__()
  633. self._smap = map_constructor()
  634. def copy(self):
  635. """return shallow copy"""
  636. return CMap(self)
  637. def lower_bound(self,key):
  638. """
  639. Finds smallest key equal to or above the lower bound.
  640. Takes O(log n) time.
  641. @param x: Key of (key, value) pair to be located.
  642. @return: Key Iterator pointing to first item equal to or greater
  643. than key, or end() if no such item exists.
  644. >>> from CMap import CMap
  645. >>> m = CMap()
  646. >>> m[10] = 'foo'
  647. >>> m[15] = 'bar'
  648. >>> i = m.lower_bound(11) # iterator.
  649. >>> int(i.key())
  650. 15
  651. >>> i.value()
  652. 'bar'
  653. Edge cases:
  654. >>> from CMap import CMap
  655. >>> m = CMap()
  656. >>> i = m.lower_bound(11)
  657. >>> if i == m.end(): print 'ok'
  658. ...
  659. ok
  660. >>> m[10] = 'foo'
  661. >>> i = m.lower_bound(11)
  662. >>> if i == m.end(): print 'ok'
  663. ...
  664. ok
  665. >>> i = m.lower_bound(9)
  666. >>> if i == m.begin(): print 'ok'
  667. ...
  668. ok
  669. """
  670. return CMap.KeyIterator(self, map_lower_bound( self._smap, key ))
  671. def upper_bound(self, key):
  672. """
  673. Finds largest key equal to or below the upper bound. In keeping
  674. with the [begin,end) convention, the returned iterator
  675. actually points to the key one above the upper bound.
  676. Takes O(log n) time.
  677. @param x: Key of (key, value) pair to be located.
  678. @return: Iterator pointing to first element equal to or greater than
  679. key, or end() if no such item exists.
  680. >>> from CMap import CMap
  681. >>> m = CMap()
  682. >>> m[10] = 'foo'
  683. >>> m[15] = 'bar'
  684. >>> m[17] = 'choo'
  685. >>> i = m.upper_bound(11) # iterator.
  686. >>> i.value()
  687. 'bar'
  688. Edge cases:
  689. >>> from CMap import CMap
  690. >>> m = CMap()
  691. >>> i = m.upper_bound(11)
  692. >>> if i == m.end(): print 'ok'
  693. ...
  694. ok
  695. >>> m[10] = 'foo'
  696. >>> i = m.upper_bound(9)
  697. >>> i.value()
  698. 'foo'
  699. >>> i = m.upper_bound(11)
  700. >>> if i == m.end(): print 'ok'
  701. ...
  702. ok
  703. """
  704. return CMap.KeyIterator(self, map_upper_bound( self._smap, key ))
  705. def find(self,key):
  706. """
  707. Finds the item with matching key and returns a KeyIterator
  708. pointing at the item. If no match is found then returns end().
  709. Takes O(log n) time.
  710. >>> from CMap import CMap
  711. >>> m = CMap()
  712. >>> i = m.find(10)
  713. >>> if i == m.end(): print 'ok'
  714. ...
  715. ok
  716. >>> m[10] = 'foo'
  717. >>> i = m.find(10)
  718. >>> int(i.key())
  719. 10
  720. >>> i.value()
  721. 'foo'
  722. """
  723. return CMap.KeyIterator(self, map_find_iter( self._smap, key ))
  724. def update_key( self, iter, key ):
  725. """
  726. Modifies the key of the item referenced by iter. If the
  727. key change is small enough that no reordering occurs then
  728. this takes amortized O(1) time. If a reordering occurs then
  729. this takes O(log n).
  730. WARNING!!! The passed iterator MUST be assumed to be invalid
  731. upon return and should be deallocated.
  732. Typical use:
  733. >>> from CMap import CMap
  734. >>> m = CMap()
  735. >>> m[10] = 'foo'
  736. >>> m[8] = 'bar'
  737. >>> i = m.find(10)
  738. >>> m.update_key(i,7) # i is assumed to be invalid upon return.
  739. >>> del i
  740. >>> [(int(x[0]),x[1]) for x in m.items()] # reordering occurred.
  741. [(7, 'foo'), (8, 'bar')]
  742. >>> i = m.find(8)
  743. >>> m.update_key(i,9) # no reordering.
  744. >>> del i
  745. >>> [(int(x[0]),x[1]) for x in m.items()]
  746. [(7, 'foo'), (9, 'bar')]
  747. Edge cases:
  748. >>> i = m.find(7)
  749. >>> i.value()
  750. 'foo'
  751. >>> try: # update to key already in the map.
  752. ... m.update_key(i,9)
  753. ... except KeyError:
  754. ... print 'ok'
  755. ...
  756. ok
  757. >>> m[7]
  758. 'foo'
  759. >>> i = m.iterkeys()
  760. >>> try: # updating an iter pointing at BEGIN.
  761. ... m.update_key(i,10)
  762. ... except IndexError:
  763. ... print 'ok'
  764. ...
  765. ok
  766. >>> i = m.end()
  767. >>> try: # updating an iter pointing at end().
  768. ... m.update_key(i,10)
  769. ... except IndexError:
  770. ... print 'ok'
  771. ...
  772. ok
  773. """
  774. assert isinstance(iter,CMap._AbstractIterator)
  775. if iter._si == BEGIN:
  776. raise IndexError( _("Iterator does not point at key-value pair") )
  777. if self is not iter._map:
  778. raise IndexError(_("Iterator points into a different CIndexedMap."))
  779. if map_iter_at_end(self._smap, iter._si):
  780. raise IndexError( _("Cannot update end() iterator.") )
  781. map_iter_update_key(self._smap, iter._si, key)
  782. def append(self, key, value):
  783. """Performs an insertion with the hint that it probably should
  784. go at the end.
  785. Raises KeyError if the key is already in the map.
  786. >>> from CMap import CMap
  787. >>> m = CMap()
  788. >>> m.append(5.0,'foo') # append to empty map.
  789. >>> len(m)
  790. 1
  791. >>> [int(x) for x in m.keys()] # see note (1)
  792. [5]
  793. >>> m.append(10.0, 'bar') # append in-order
  794. >>> [(int(x[0]),x[1]) for x in m.items()]
  795. [(5, 'foo'), (10, 'bar')]
  796. >>> m.append(3.0, 'coo') # out-of-order.
  797. >>> [(int(x[0]),x[1]) for x in m.items()]
  798. [(3, 'coo'), (5, 'foo'), (10, 'bar')]
  799. >>> try:
  800. ... m.append(10.0, 'blah') # append key already in map.
  801. ... except KeyError:
  802. ... print 'ok'
  803. ...
  804. ok
  805. >>> [(int(x[0]),x[1]) for x in m.items()]
  806. [(3, 'coo'), (5, 'foo'), (10, 'bar')]
  807. >>>
  808. note (1): int(x[0]) is used because 5.0 can appear as either 5
  809. or 5.0 depending on the version of python.
  810. """
  811. map_append(self._smap,key,value)
  812. class CIndexedMap(CMap):
  813. """This is an ordered mapping, exactly like CMap except that it
  814. provides a cross-index allowing average O(1) searches based on value.
  815. This adds the constraint that values must be unique.
  816. Operation: Time Applicable
  817. Complexity: Methods:
  818. ---------------------------------------------------
  819. Item insertion: O(log n) append, __setitem__
  820. Item deletion: O(log n + k) __delitem__, erase
  821. Key search: O(log n) __getitem__, get, find,
  822. __contains__
  823. Value search: average O(1) as per dict
  824. Iteration step: amortized O(1), next, prev
  825. worst-case O(log n)
  826. Memory: O(n)
  827. n = number of elements in map. k = number of iterators pointing
  828. into map. CIndexedMap assumes there are few iterators in existence
  829. at any given time.
  830. The hash table increases the factor in the
  831. O(n) memory cost of the Map by a constant
  832. """
  833. def __init__(self, dict={} ):
  834. CMap.__init__(self,dict)
  835. self._value_index = {} # cross-index. maps value->iterator.
  836. def __setitem__(self, key, value):
  837. """
  838. >>> from CMap import *
  839. >>> m = CIndexedMap()
  840. >>> m[6] = 'bar'
  841. >>> m[6]
  842. 'bar'
  843. >>> int(m.get_key_by_value('bar'))
  844. 6
  845. >>> try:
  846. ... m[7] = 'bar'
  847. ... except ValueError:
  848. ... print 'value error'
  849. value error
  850. >>> m[6] = 'foo'
  851. >>> m[6]
  852. 'foo'
  853. >>> m[7] = 'bar'
  854. >>> m[7]
  855. 'bar'
  856. >>> m[7] = 'bar' # should not raise exception
  857. >>> m[7] = 'goo'
  858. >>> m.get_key_by_value('bar') # should return None.
  859. >>>
  860. """
  861. assert type(key) == int or type(key) == float
  862. if self._value_index.has_key(value) and \
  863. iter_key(self._value_index[value]) != key:
  864. raise ValueError( _("Value %s already exists. Values must be "
  865. "unique.") % str(value) )
  866. si = map_insert_iter(self._smap,key,value) # si points where insert
  867. # should occur whether
  868. # insert succeeded or not.
  869. # si == "swig iterator"
  870. sival = iter_value(si)
  871. if sival != value: # if insert failed because k already exists
  872. iter_set(si,value) # then force set.
  873. self._value_index[value] = si
  874. viter = self._value_index[sival]
  875. iter_delete(viter) # remove old value from index
  876. del self._value_index[sival]
  877. else: # else insert succeeded so update index.
  878. self._value_index[value] = si
  879. #self._index[key] = si # IMPL 2. with _index.
  880. def __delitem__(self, key):
  881. """
  882. >>> from CMap import CIndexedMap
  883. >>> m = CIndexedMap()
  884. >>> m[6] = 'bar'
  885. >>> m[6]
  886. 'bar'
  887. >>> int(m.get_key_by_value('bar'))
  888. 6
  889. >>> del m[6]
  890. >>> if m.get_key_by_value('bar'):
  891. ... print 'found'
  892. ... else:
  893. ... print 'not found.'
  894. not found.
  895. """
  896. i = map_find_iter( self._smap, key )
  897. if map_iter_at_end( self._smap, i ):
  898. iter_delete(i)
  899. raise KeyError(key)
  900. else:
  901. value = iter_value(i)
  902. for i in list(self._iterators):
  903. if iter_cmp( self._smap, i._si, iter._si ) == 0:
  904. i._invalidate()
  905. map_iter_erase( self._smap, i )
  906. viter = self._value_index[value]
  907. iter_delete(i)
  908. iter_delete( viter )
  909. del self._value_index[value]
  910. #del self._index[key] # IMPL 2. with _index.
  911. assert map_size(self._smap) == len(self._value_index)
  912. def has_value(self, value):
  913. return self._value_index.has_key(value)
  914. def get_key_by_value(self, value):
  915. """Returns the key cross-indexed from the passed unique value, or
  916. returns None if the value is not in the map."""
  917. si = self._value_index.get(value) # si == "swig iterator"
  918. if si == None: return None
  919. return iter_key(si)
  920. def append( self, key, value ):
  921. """See CMap.append
  922. >>> from CMap import CIndexedMap
  923. >>> m = CIndexedMap()
  924. >>> m.append(5,'foo')
  925. >>> [(int(x[0]),x[1]) for x in m.items()]
  926. [(5, 'foo')]
  927. >>> m.append(10, 'bar')
  928. >>> [(int(x[0]),x[1]) for x in m.items()]
  929. [(5, 'foo'), (10, 'bar')]
  930. >>> m.append(3, 'coo') # out-of-order.
  931. >>> [(int(x[0]),x[1]) for x in m.items()]
  932. [(3, 'coo'), (5, 'foo'), (10, 'bar')]
  933. >>> int(m.get_key_by_value( 'bar' ))
  934. 10
  935. >>> try:
  936. ... m.append(10, 'blah') # append key already in map.
  937. ... except KeyError:
  938. ... print 'ok'
  939. ...
  940. ok
  941. >>> [(int(x[0]),x[1]) for x in m.items()]
  942. [(3, 'coo'), (5, 'foo'), (10, 'bar')]
  943. >>> try:
  944. ... m.append(10, 'coo') # append value already in map.
  945. ... except ValueError:
  946. ... print 'ok'
  947. ...
  948. ok
  949. """
  950. if self._value_index.has_key(value) and \
  951. iter_key(self._value_index[value]) != key:
  952. raise ValueError(_("Value %s already exists and value must be "
  953. "unique.") % str(value) )
  954. si = map_append_iter(self._smap,key,value)
  955. if iter_value(si) != value:
  956. iter_delete(si)
  957. raise KeyError(key)
  958. self._value_index[value] = si
  959. def find_key_by_value(self, value):
  960. """Returns a key iterator cross-indexed from the passed unique value
  961. or end() if no value found.
  962. >>> from Map import *
  963. >>> m = CIndexedMap()
  964. >>> m[6] = 'abc'
  965. >>> i = m.find_key_by_value('abc')
  966. >>> int(i.key())
  967. 6
  968. >>> i = m.find_key_by_value('xyz')
  969. >>> if i == m.end(): print 'i points at end()'
  970. i points at end()
  971. """
  972. si = self._value_index.get(value) # si == "swig iterator."
  973. if si != None:
  974. si = iter_copy(si); # copy else operations like increment on the
  975. # KeyIterator would modify the value index.
  976. return CMap.KeyIterator(self,si)
  977. def copy(self):
  978. """return shallow copy"""
  979. return CIndexedMap(self)
  980. def update_key( self, iter, key ):
  981. """
  982. see CMap.update_key.
  983. WARNING!! You MUST assume that the passed iterator is invalidated
  984. upon return.
  985. Typical use:
  986. >>> from CMap import CIndexedMap
  987. >>> m = CIndexedMap()
  988. >>> m[10] = 'foo'
  989. >>> m[8] = 'bar'
  990. >>> i = m.find(10)
  991. >>> m.update_key(i,7) # i is assumed to be invalid upon return.
  992. >>> del i
  993. >>> int(m.get_key_by_value('foo'))
  994. 7
  995. >>> [(int(x[0]),x[1]) for x in m.items()] # reordering occurred.
  996. [(7, 'foo'), (8, 'bar')]
  997. >>> i = m.find(8)
  998. >>> m.update_key(i,9) # no reordering.
  999. >>> del i
  1000. >>> [(int(x[0]),x[1]) for x in m.items()]
  1001. [(7, 'foo'), (9, 'bar')]
  1002. Edge cases:
  1003. >>> i = m.find(7)
  1004. >>> i.value()
  1005. 'foo'
  1006. >>> try:
  1007. ... m.update_key(i,9)
  1008. ... except KeyError:
  1009. ... print 'ok'
  1010. ...
  1011. ok
  1012. >>> m[7]
  1013. 'foo'
  1014. >>> int(m.get_key_by_value('foo'))
  1015. 7
  1016. >>> i = m.iterkeys()
  1017. >>> try: # updating an iter pointing at BEGIN.
  1018. ... m.update_key(i,10)
  1019. ... except IndexError:
  1020. ... print 'ok'
  1021. ...
  1022. ok
  1023. >>> i = m.end()
  1024. >>> try: # updating an iter pointing at end().
  1025. ... m.update_key(i,10)
  1026. ... except IndexError:
  1027. ... print 'ok'
  1028. ...
  1029. ok
  1030. """
  1031. if not iter._si:
  1032. raise RuntimeError( _("invalid iterator") )
  1033. if iter._si == BEGIN:
  1034. raise IndexError(_("Iterator does not point at key-value pair" ))
  1035. if self is not iter._map:
  1036. raise IndexError(_("Iterator points into a different "
  1037. "CIndexedMap."))
  1038. if map_iter_at_end(self._smap, iter._si):
  1039. raise IndexError( _("Cannot update end() iterator.") )
  1040. si = map_iter_update_key_iter(self._smap, iter._si, key)
  1041. # raises KeyError if key already in map.
  1042. if si != iter._si: # if map is reordered...
  1043. value = iter.value();
  1044. val_si = self._value_index[value]
  1045. iter_delete(val_si)
  1046. self._value_index[value] = si
  1047. def erase(self, iter):
  1048. """Remove item pointed to by the iterator. Iterator is immediately
  1049. invalidated after the deletion completes."""
  1050. if not iter._si:
  1051. raise RuntimeError( _("invalid iterator") )
  1052. if iter._si == BEGIN:
  1053. raise IndexError(_("Iterator does not point at key-value pair." ))
  1054. if self is not iter._map:
  1055. raise IndexError(_("Iterator points into a different "
  1056. "CIndexedMap."))
  1057. if map_iter_at_end(self._smap, iter._si):
  1058. raise IndexError( _("Cannot update end() iterator.") )
  1059. value = iter.value()
  1060. CMap.erase(self,iter)
  1061. del self._value_index[value]
  1062. if __name__ == "__main__":
  1063. import doctest
  1064. import random
  1065. ##############################################
  1066. # UNIT TESTS
  1067. print "Testing module"
  1068. doctest.testmod(sys.modules[__name__])
  1069. print "doctest complete."
  1070. ##############################################
  1071. # MEMORY LEAK TESTS
  1072. if LEAK_TEST:
  1073. i = 0
  1074. import gc
  1075. class X:
  1076. x = range(1000) # something moderately big.
  1077. # TEST 1. This does not cause memory to grow.
  1078. #m = CMap()
  1079. #map_insert(m._smap,10,X())
  1080. #while True:
  1081. # i += 1
  1082. # it = map_find_iter( m._smap, 10 )
  1083. # iter_delete(it)
  1084. # del it
  1085. # if i % 100 == 0:
  1086. # gc.collect()
  1087. # TEST 2: This does not caus a memory leak.
  1088. #m = map_constructor_double()
  1089. #while True:
  1090. # i += 1
  1091. # map_insert_double(m,10,5) # here
  1092. # it = map_find_iter_double( m, 10 )
  1093. # map_iter_erase_double( m, it ) # or here is the problem.
  1094. # iter_delete_double(it)
  1095. # del it
  1096. # #assert len(m) == 0
  1097. # assert map_size_double(m) == 0
  1098. # if i % 100 == 0:
  1099. # gc.collect()
  1100. # TEST 3. No memory leak
  1101. #m = CMap()
  1102. #while True:
  1103. # i += 1
  1104. # map_insert(m._smap,10,X()) # here
  1105. # it = map_find_iter( m._smap, 10 )
  1106. # map_iter_erase( m._smap, it ) # or here is the problem.
  1107. # iter_delete(it)
  1108. # del it
  1109. # assert len(m) == 0
  1110. # assert map_size(m._smap) == 0
  1111. # if i % 100 == 0:
  1112. # gc.collect()
  1113. # TEST 4: map creation and deletion.
  1114. #while True:
  1115. # m = map_constructor()
  1116. # map_delete(m);
  1117. # TEST 5: test iteration.
  1118. #m = map_constructor()
  1119. #for i in xrange(10):
  1120. # map_insert(m,i,X())
  1121. #while True:
  1122. # i = map_begin(m)
  1123. # while not map_iter_at_begin(m,i):
  1124. # iter_incr(i)
  1125. # iter_delete(i)
  1126. # TEST 6:
  1127. #m = map_constructor()
  1128. #for i in xrange(10):
  1129. # map_insert(m,i,X())
  1130. #while True:
  1131. # map_find( m, random.randint(0,9) )
  1132. # TEST 7:
  1133. #m = map_constructor()
  1134. #for i in xrange(50):
  1135. # map_insert( m, i, X() )
  1136. #while True:
  1137. # for i in xrange(50):
  1138. # map_set( m, i, X() )
  1139. # TEST 8
  1140. # aha! Another leak! Fixed.
  1141. #m = map_constructor()
  1142. #while True:
  1143. # i += 1
  1144. # map_insert(m,10,X())
  1145. # map_erase(m,10)
  1146. # assert map_size(m) == 0
  1147. # TEST 9
  1148. m = map_constructor()
  1149. for i in xrange(50):
  1150. map_insert( m, i, X() )
  1151. while True:
  1152. it = map_find_iter( m, 5 )
  1153. map_iter_update_key( m, it, 1000 )
  1154. iter_delete(it)
  1155. it = map_find_iter( m, 1000 )
  1156. map_iter_update_key( m, it, 5)
  1157. iter_delete(it)