/src/lib/storage/bijective_dictionary/hashed_bijective_dictionary.e

http://github.com/tybor/Liberty · Specman e · 601 lines · 493 code · 42 blank · 66 comment · 21 complexity · 33b6e50ac277f250e1ed7c6d11e419cd MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. class HASHED_BIJECTIVE_DICTIONARY[V_ -> HASHABLE, K_ -> HASHABLE]
  5. inherit
  6. BIJECTIVE_DICTIONARY[V_, K_]
  7. insert
  8. HASH_TABLE_SIZE
  9. undefine
  10. out_in_tagged_out_memory
  11. end
  12. create {ANY}
  13. make, with_capacity, manifest_creation
  14. feature {ANY}
  15. count: INTEGER
  16. has (k: K_): BOOLEAN
  17. local
  18. node: like cache_node
  19. do
  20. from
  21. node := key_buckets.item(k.hash_code #\\ capacity)
  22. until
  23. node = Void or else key_safe_equal(node.key, k)
  24. loop
  25. node := node.next_key
  26. end
  27. Result := node /= Void
  28. end
  29. at (k: K_): V_
  30. local
  31. node: like cache_node
  32. do
  33. from
  34. node := key_buckets.item(k.hash_code #\\ capacity)
  35. until
  36. key_safe_equal(node.key, k)
  37. loop
  38. node := node.next_key
  39. end
  40. Result := node.val
  41. end
  42. reference_at (k: K_): V_
  43. local
  44. node: like cache_node
  45. do
  46. from
  47. node := key_buckets.item(k.hash_code #\\ capacity)
  48. until
  49. node = Void or else key_safe_equal(node.key, k)
  50. loop
  51. node := node.next_key
  52. end
  53. if node /= Void then
  54. Result := node.val
  55. end
  56. end
  57. fast_has (k: K_): BOOLEAN
  58. local
  59. node: like cache_node
  60. do
  61. from
  62. node := key_buckets.item(k.hash_code #\\ capacity)
  63. until
  64. node = Void or else node.key = k
  65. loop
  66. node := node.next_key
  67. end
  68. Result := node /= Void
  69. end
  70. fast_at (k: K_): V_
  71. local
  72. node: like cache_node
  73. do
  74. from
  75. node := key_buckets.item(k.hash_code #\\ capacity)
  76. until
  77. node.key = k
  78. loop
  79. node := node.next_key
  80. end
  81. Result := node.val
  82. end
  83. fast_reference_at (k: K_): V_
  84. local
  85. node: like cache_node
  86. do
  87. from
  88. node := key_buckets.item(k.hash_code #\\ capacity)
  89. until
  90. node = Void or else node.key = k
  91. loop
  92. node := node.next_key
  93. end
  94. if node /= Void then
  95. Result := node.val
  96. end
  97. end
  98. has_value (v: V_): BOOLEAN
  99. local
  100. node: like cache_node
  101. do
  102. from
  103. node := val_buckets.item(v.hash_code #\\ capacity)
  104. until
  105. node = Void or else val_safe_equal(node.val, v)
  106. loop
  107. node := node.next_val
  108. end
  109. Result := node /= Void
  110. end
  111. key_at (v: V_): K_
  112. local
  113. node: like cache_node
  114. do
  115. from
  116. node := val_buckets.item(v.hash_code #\\ capacity)
  117. until
  118. val_safe_equal(node.val, v)
  119. loop
  120. node := node.next_val
  121. end
  122. Result := node.key
  123. end
  124. fast_has_value (v: V_): BOOLEAN
  125. local
  126. node: like cache_node
  127. do
  128. from
  129. node := val_buckets.item(v.hash_code #\\ capacity)
  130. until
  131. node = Void or else node.val = v
  132. loop
  133. node := node.next_val
  134. end
  135. Result := node /= Void
  136. end
  137. fast_key_at (v: V_): K_
  138. local
  139. node: like cache_node
  140. do
  141. from
  142. node := val_buckets.item(v.hash_code #\\ capacity)
  143. until
  144. node.val = v
  145. loop
  146. node := node.next_val
  147. end
  148. Result := node.key
  149. end
  150. put (v: V_; k: K_)
  151. local
  152. key_idx, val_idx: INTEGER; node: like cache_node
  153. do
  154. from
  155. key_idx := k.hash_code #\\ capacity
  156. node := key_buckets.item(key_idx)
  157. until
  158. node = Void or else key_safe_equal(node.key, k)
  159. loop
  160. node := node.next_key
  161. end
  162. if node = Void then
  163. if should_increase_capacity(capacity, count) then
  164. increase_capacity
  165. key_idx := k.hash_code #\\ capacity
  166. end
  167. val_idx := v.hash_code #\\ capacity
  168. node := new_node(v, val_buckets.item(val_idx), k, key_buckets.item(key_idx))
  169. key_buckets.put(node, key_idx)
  170. val_buckets.put(node, val_idx)
  171. count := count + 1
  172. cache_user := -1
  173. else
  174. remove(k)
  175. add(v, k)
  176. end
  177. next_generation
  178. end
  179. add (v: V_; k: K_)
  180. local
  181. key_idx, val_idx: INTEGER; node: like cache_node
  182. do
  183. cache_user := -1
  184. if should_increase_capacity(capacity, count) then
  185. increase_capacity
  186. end
  187. key_idx := k.hash_code #\\ capacity
  188. val_idx := v.hash_code #\\ capacity
  189. node := new_node(v, val_buckets.item(val_idx), k, key_buckets.item(key_idx))
  190. key_buckets.put(node, key_idx)
  191. val_buckets.put(node, val_idx)
  192. -- Finally:
  193. count := count + 1
  194. next_generation
  195. end
  196. remove (k: K_)
  197. local
  198. key_idx: INTEGER; node, previous_node: like cache_node
  199. do
  200. cache_user := -1
  201. key_idx := k.hash_code #\\ capacity
  202. node := key_buckets.item(key_idx)
  203. if node /= Void then
  204. if key_safe_equal(node.key, k) then
  205. val_buckets_remove(node)
  206. count := count - 1
  207. node := dispose_node(node)
  208. key_buckets.put(node, key_idx)
  209. else
  210. from
  211. previous_node := node
  212. node := node.next_key
  213. until
  214. node = Void or else key_safe_equal(node.key, k)
  215. loop
  216. previous_node := node
  217. node := node.next_key
  218. end
  219. if node /= Void then
  220. val_buckets_remove(node)
  221. count := count - 1
  222. previous_node.set_next_key(dispose_node(node))
  223. end
  224. end
  225. end
  226. next_generation
  227. end
  228. clear_count, clear_count_and_capacity
  229. require
  230. capacity > 0
  231. local
  232. i: INTEGER; node: like cache_node
  233. do
  234. cache_user := -1
  235. count := 0
  236. from
  237. i := capacity - 1
  238. until
  239. i < 0
  240. loop
  241. node := key_buckets.item(i)
  242. key_buckets.put(Void, i)
  243. val_buckets.put(Void, i)
  244. from
  245. until
  246. node = Void
  247. loop
  248. node := dispose_node(node)
  249. end
  250. i := i - 1
  251. end
  252. next_generation
  253. ensure then
  254. capacity = old capacity
  255. end
  256. item (index: INTEGER): V_
  257. do
  258. set_cache_user(index)
  259. Result := cache_node.val
  260. end
  261. key (index: INTEGER): K_
  262. do
  263. set_cache_user(index)
  264. Result := cache_node.key
  265. end
  266. Default_size: INTEGER 193
  267. -- Default size for the storage area in number of items.
  268. internal_key (k: K_): K_
  269. -- Retrieve the internal key object which correspond to the existing
  270. -- entry `k' (the one memorized into the `Current' dictionary).
  271. local
  272. node: like cache_node
  273. do
  274. from
  275. node := key_buckets.item(k.hash_code #\\ capacity)
  276. Result := node.key
  277. until
  278. key_safe_equal(Result, k)
  279. loop
  280. node := node.next_key
  281. Result := node.key
  282. end
  283. end
  284. feature {HASHED_BIJECTIVE_DICTIONARY}
  285. key_buckets: NATIVE_ARRAY[like cache_node]
  286. -- The `key_buckets' storage area is the primary hash table of `capacity' elements. To search some
  287. -- key, the first access is done in `key_buckets' using the remainder of the division of the key
  288. -- `hash_code' by `capacity'. In order to try to avoid clashes, `capacity' is always a prime
  289. -- number (selected using HASHED_CAPACITY).
  290. val_buckets: NATIVE_ARRAY[like cache_node]
  291. -- The `val_buckets' storage area is the primary hash table of `capacity' elements. To search some
  292. -- value, the first access is done in `val_buckets' using the remainder of the division of the value
  293. -- `hash_code' by `capacity'. In order to try to avoid clashes, `capacity' is always a prime
  294. -- number (selected using HASHED_CAPACITY).
  295. feature {ANY}
  296. capacity: INTEGER
  297. -- Approximation of the actual internal storage `capacity'. The `capacity' will grow automatically
  298. -- when needed (i.e. `capacity' is not a limit for the number of values stored). Also note that
  299. -- the `capacity' value may not be always accurate depending of the implementation (anyway, this
  300. -- `capacity' value is at least equals to `count').
  301. copy (other: like Current)
  302. local
  303. i: INTEGER
  304. do
  305. if capacity = 0 then
  306. -- It is a brand new one probably created by `twin':
  307. make
  308. else
  309. clear_count
  310. end
  311. from
  312. i := 1
  313. until
  314. i > other.count
  315. loop
  316. add(other.item(i), other.key(i))
  317. i := i + 1
  318. end
  319. next_generation
  320. end
  321. feature {}
  322. cache_user: INTEGER
  323. -- The last user's external index in range [1 .. `count'] (see `item' and `valid_index' for example)
  324. -- may be saved in `cache_user' otherwise -1 to indicate that the cache is not active. When the
  325. -- cache is active, the corresponding index in `key_buckets' is save in `cache_buckets' and the
  326. -- corresponding node in `cache_node'.
  327. cache_node: HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_]
  328. -- Meaningful only when `cache_user' is not -1.
  329. cache_buckets: INTEGER
  330. -- Meaningful only when `cache_user' is not -1.
  331. free_nodes: WEAK_REFERENCE[HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_]]
  332. -- If any, they are ready to be recycled.
  333. set_cache_user (index: INTEGER)
  334. -- Set the internal memory cache (`cache_user', `cache_node' and `cache_buckets') to the appropriate
  335. -- valid value.
  336. require
  337. valid_index(index)
  338. do
  339. if index = cache_user + 1 then
  340. from
  341. cache_user := index
  342. cache_node := cache_node.next_key
  343. until
  344. cache_node /= Void
  345. loop
  346. cache_buckets := cache_buckets + 1
  347. cache_node := key_buckets.item(cache_buckets)
  348. end
  349. elseif index = cache_user then
  350. elseif index = 1 then
  351. from
  352. cache_user := 1
  353. cache_buckets := 0
  354. cache_node := key_buckets.item(cache_buckets)
  355. until
  356. cache_node /= Void
  357. loop
  358. cache_buckets := cache_buckets + 1
  359. cache_node := key_buckets.item(cache_buckets)
  360. end
  361. else
  362. from
  363. set_cache_user(1)
  364. until
  365. cache_user = index
  366. loop
  367. set_cache_user(cache_user + 1)
  368. end
  369. end
  370. ensure
  371. cache_user = index
  372. cache_buckets.in_range(0, capacity - 1)
  373. cache_node /= Void
  374. end
  375. make
  376. -- Create an empty dictionary. Internal storage `capacity' of the dictionary is initialized using
  377. -- the `Default_size' value. Then, tuning of needed storage `capacity' is performed automatically
  378. -- according to usage. If you are really sure that your dictionary is always really bigger than
  379. -- `Default_size', you may consider to use `with_capacity' to save some execution time.
  380. do
  381. basic_make(Default_size)
  382. ensure
  383. capacity = Default_size
  384. end
  385. with_capacity (medium_size: INTEGER)
  386. -- May be used to save some execution time if one is sure that storage size will rapidly become
  387. -- really bigger than `Default_size'.
  388. -- When first `remove' occurs, storage size may naturally become smaller than `medium_size'.
  389. -- Afterall, tuning of storage size is done automatically according to usage.
  390. require
  391. medium_size > 0
  392. local
  393. new_capacity: INTEGER
  394. do
  395. new_capacity := prime_capacity(medium_size)
  396. basic_make(new_capacity)
  397. ensure
  398. is_empty
  399. capacity >= medium_size
  400. end
  401. basic_make (new_capacity: like capacity)
  402. require
  403. new_capacity = prime_number_ceiling(new_capacity)
  404. do
  405. free_nodes ::= common_free_nodes.fast_reference_at(generating_type)
  406. if free_nodes = Void then
  407. create free_nodes.set_item(Void)
  408. common_free_nodes.add(free_nodes, generating_type)
  409. end
  410. key_buckets := key_buckets.calloc(new_capacity)
  411. val_buckets := val_buckets.calloc(new_capacity)
  412. capacity := new_capacity
  413. cache_user := -1
  414. count := 0
  415. ensure
  416. is_empty
  417. capacity = new_capacity
  418. end
  419. increase_capacity
  420. -- There is no more free slots: the dictionary must grow.
  421. require
  422. should_increase_capacity(capacity, count)
  423. local
  424. i, idx, new_capacity: INTEGER; old_key_buckets: like key_buckets; node1, node2: like cache_node
  425. old_val_buckets: like val_buckets
  426. do
  427. new_capacity := prime_capacity(capacity + 1)
  428. -- Rebuilding `key_buckets':
  429. from
  430. old_key_buckets := key_buckets
  431. key_buckets := key_buckets.calloc(new_capacity)
  432. i := capacity - 1
  433. until
  434. i < 0
  435. loop
  436. from
  437. node1 := old_key_buckets.item(i)
  438. until
  439. node1 = Void
  440. loop
  441. node2 := node1.next_key
  442. idx := node1.key.hash_code #\\ new_capacity
  443. node1.set_next_key(key_buckets.item(idx))
  444. key_buckets.put(node1, idx)
  445. node1 := node2
  446. end
  447. i := i - 1
  448. end
  449. -- Rebuilding `val_buckets':
  450. from
  451. old_val_buckets := val_buckets
  452. val_buckets := val_buckets.calloc(new_capacity)
  453. i := capacity - 1
  454. until
  455. i < 0
  456. loop
  457. from
  458. node1 := old_val_buckets.item(i)
  459. until
  460. node1 = Void
  461. loop
  462. node2 := node1.next_val
  463. idx := node1.val.hash_code #\\ new_capacity
  464. node1.set_next_val(val_buckets.item(idx))
  465. val_buckets.put(node1, idx)
  466. node1 := node2
  467. end
  468. i := i - 1
  469. end
  470. -- Finally:
  471. capacity := new_capacity
  472. cache_user := -1
  473. ensure
  474. count = old count
  475. capacity > old capacity
  476. end
  477. common_free_nodes: HASHED_DICTIONARY[WEAK_REFERENCE[ANY_HASHED_BIJECTIVE_DICTIONARY_NODE], STRING]
  478. once
  479. create Result.make
  480. end
  481. dispose_node (node: HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_]): HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_]
  482. -- Add `node' in the `free_nodes' list.
  483. require
  484. node /= Void
  485. do
  486. Result := node.next_key
  487. node.set_next_key(free_nodes.item)
  488. free_nodes.set_item(node)
  489. ensure
  490. Result = old node.next_key
  491. end
  492. new_node (v: V_; nv: HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_]; k: K_; nk: HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_]): HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_]
  493. -- Recycle from `free_nodes' or create a new one.
  494. do
  495. Result := free_nodes.item
  496. if Result = Void then
  497. create Result.make(v, nv, k, nk)
  498. else
  499. free_nodes.set_item(Result.next_key)
  500. Result.make(v, nv, k, nk)
  501. end
  502. ensure
  503. Result.val = v
  504. Result.next_val = nv
  505. Result.key = k
  506. Result.next_key = nk
  507. end
  508. val_buckets_remove (node: HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_])
  509. require
  510. node /= Void
  511. local
  512. val_idx: INTEGER; other_node: HASHED_BIJECTIVE_DICTIONARY_NODE[V_, K_]
  513. do
  514. --|*** like node -- *** DEBUG
  515. val_idx := node.val.hash_code #\\ capacity
  516. other_node := val_buckets.item(val_idx)
  517. if other_node = node then
  518. -- Head removal:
  519. val_buckets.put(node.next_val, val_idx)
  520. else
  521. from
  522. until
  523. other_node.next_val = node
  524. loop
  525. other_node := other_node.next_val
  526. end
  527. other_node.set_next_val(node.next_val)
  528. end
  529. end
  530. feature {} -- Implement manifest generic creation:
  531. manifest_make (needed_capacity: INTEGER)
  532. -- Manifest creation of a dictionary.
  533. do
  534. with_capacity(needed_capacity.max(Default_size))
  535. end
  536. invariant
  537. capacity >= count
  538. free_nodes /= Void
  539. end -- class HASHED_BIJECTIVE_DICTIONARY
  540. --
  541. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  542. --
  543. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  544. -- of this software and associated documentation files (the "Software"), to deal
  545. -- in the Software without restriction, including without limitation the rights
  546. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  547. -- copies of the Software, and to permit persons to whom the Software is
  548. -- furnished to do so, subject to the following conditions:
  549. --
  550. -- The above copyright notice and this permission notice shall be included in
  551. -- all copies or substantial portions of the Software.
  552. --
  553. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  554. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  555. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  556. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  557. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  558. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  559. -- THE SOFTWARE.