PageRenderTime 18ms CodeModel.GetById 7ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 1ms

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