note description: "[ Hash tables, used to store items identified by string keys that are compared with or without case sensitivity. ]" legal: "See notice at end of class." status: "See notice at end of class." warning: "[ Modifying an object used as a key by an item present in a table will cause incorrect behavior. If you will be modifying key objects, pass a clone, not the object itself, or an immutable object, as key argument to put and replace_key. ]" date: "$Date: 2016-12-08 10:30:24 +0000 (Thu, 08 Dec 2016) $" revision: "$Revision: 99662 $" class STRING_TABLE [G] create make, make_equal, make_caseless, make_equal_caseless feature {NONE} -- Initialization default_create -- Process instances of classes with no creation clause. -- (Default: do nothing.) -- (from ANY) do end make_caseless (n: INTEGER_32) -- Allocate hash table for at least n items. -- The table will be resized automatically -- if more than n items are inserted. -- Keys will be compared caseless. require n_non_negative: n >= 0 do is_case_insensitive := True make (n) ensure breathing_space: n < capacity more_than_minimum: capacity > Minimum_capacity no_status: not special_status is_case_insensitive: is_case_insensitive end make_equal_caseless (n: INTEGER_32) -- Allocate hash table for at least n items. -- The table will be resized automatically -- if more than n items are inserted. -- Keys will be compared caseless. -- Items will be compared using ~. require n_non_negative: n >= 0 do is_case_insensitive := True make_equal (n) ensure breathing_space: n < capacity more_than_minimum: capacity > Minimum_capacity no_status: not special_status is_case_insensitive: is_case_insensitive compare_objects: object_comparison end feature -- Initialization accommodate (n: INTEGER_32) -- Reallocate table with enough space for n items; -- keep all current items. -- (from HASH_TABLE) require -- from HASH_TABLE n >= 0 local i, nb: INTEGER_32 new_table: STRING_TABLE [G] l_content: like content l_keys: like keys do from new_table := empty_duplicate (keys.count.max (n)) l_content := content l_keys := keys nb := l_keys.count until i = nb loop if occupied (i) then new_table.put (l_content.item (i), l_keys.item (i)) end i := i + 1 end if has_default then i := indexes_map.item (capacity); new_table.put (l_content.item (i), keys.item (i)) end set_content (new_table.content) set_keys (new_table.keys) set_deleted_marks (new_table.deleted_marks) set_indexes_map (new_table.indexes_map) capacity := new_table.capacity iteration_position := new_table.iteration_position ensure -- from HASH_TABLE count_not_changed: count = old count breathing_space: count < capacity end make (n: INTEGER_32) -- Allocate hash table for at least n items. -- The table will be resized automatically -- if more than n items are inserted. -- (from HASH_TABLE) require -- from HASH_TABLE n_non_negative: n >= 0 local clever: PRIMES l_default_value: detachable G l_default_key: detachable READABLE_STRING_GENERAL l_size: INTEGER_32 do create clever l_size := n.max (Minimum_capacity) l_size := l_size + l_size // 2 + 1 l_size := clever.higher_prime (l_size) capacity := l_size create content.make_empty (n + 1) create keys.make_empty (n + 1) create deleted_marks.make_filled (False, n + 1) create indexes_map.make_filled (Ht_impossible_position, l_size + 1) iteration_position := n + 1 count := 0 deleted_item_position := Ht_impossible_position control := 0 found_item := l_default_value has_default := False item_position := 0 ht_lowest_deleted_position := Ht_max_position ht_deleted_item := l_default_value ht_deleted_key := l_default_key ensure -- from HASH_TABLE breathing_space: n < capacity more_than_minimum: capacity > Minimum_capacity no_status: not special_status end make_equal (n: INTEGER_32) -- Allocate hash table for at least n items. -- The table will be resized automatically -- if more than n items are inserted. -- Use ~ to compare items. -- (from HASH_TABLE) require -- from HASH_TABLE n_non_negative: n >= 0 do make (n) compare_objects ensure -- from HASH_TABLE breathing_space: n < capacity more_than_minimum: capacity > Minimum_capacity no_status: not special_status compare_objects: object_comparison end feature -- Access at alias "@" (key: READABLE_STRING_GENERAL): detachable G assign force -- Item associated with key, if present -- otherwise default value of type G. -- Was declared in HASH_TABLE as synonym of item. -- (from HASH_TABLE) note option: stable local l_default_key: detachable READABLE_STRING_GENERAL hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32 l_first_deleted_position: INTEGER_32 stop: INTEGER_32 l_keys: like keys l_indexes: like indexes_map l_deleted_marks: like deleted_marks l_key: READABLE_STRING_GENERAL do l_first_deleted_position := Ht_impossible_position if key = l_default_key or key = Void then if has_default then Result := content.item (indexes_map.item (capacity)) end else from l_keys := keys l_indexes := indexes_map l_deleted_marks := deleted_marks l_capacity := capacity stop := l_capacity hash_value := hash_code_of (key) increment := 1 + hash_value \\ (l_capacity - 1) l_item_pos := (hash_value \\ l_capacity) - increment until stop = 0 loop l_item_pos := (l_item_pos + increment) \\ l_capacity l_pos := l_indexes [l_item_pos] if l_pos >= 0 then l_key := l_keys.item (l_pos) debug ("detect_hash_table_catcall") check catcall_detected: l_key /= Void and then l_key.same_type (key) end end if same_keys (l_key, key) then stop := 1 Result := content.item (l_pos) end elseif l_pos = Ht_impossible_position then stop := 1 elseif l_first_deleted_position = Ht_impossible_position then l_pos := - l_pos + Ht_deleted_position check l_pos_valid: l_pos < l_deleted_marks.count end if not l_deleted_marks [l_pos] then stop := 1 else l_first_deleted_position := l_item_pos end end stop := stop - 1 end end ensure then -- from HASH_TABLE default_value_if_not_present: (not has (key)) implies (Result = computed_default_value) end current_keys: ARRAY [READABLE_STRING_GENERAL] -- New array containing actually used keys, from 1 to count -- (from HASH_TABLE) local j: INTEGER_32 old_iteration_position: INTEGER_32 do if is_empty then create Result.make_empty else old_iteration_position := iteration_position from start create Result.make_filled (key_for_iteration, 1, count) j := 1 forth until off loop j := j + 1; Result.put (key_for_iteration, j) forth end iteration_position := old_iteration_position end ensure -- from HASH_TABLE good_count: Result.count = count end cursor: CURSOR -- Current cursor position -- (from HASH_TABLE) do create {HASH_TABLE_CURSOR} Result.make (iteration_position) ensure -- from HASH_TABLE cursor_not_void: Result /= Void end definite_item (key: READABLE_STRING_GENERAL): G -- Entry of key k. -- (from HASH_TABLE) require -- from TABLE valid_key: has (key) require -- from TABLE valid_key: has (key) local old_position: like item_position old_control: like control do old_position := item_position old_control := control internal_search (key) Result := content.item (position) control := old_control item_position := old_position end found_item: detachable G -- Item, if any, yielded by last search operation -- (from HASH_TABLE) generating_type: TYPE [detachable STRING_TABLE [G]] -- Type of current object -- (type of which it is a direct instance) -- (from ANY) external "built_in" ensure -- from ANY generating_type_not_void: Result /= Void end generator: STRING_8 -- Name of current object's generating class -- (base class of the type of which it is a direct instance) -- (from ANY) external "built_in" ensure -- from ANY generator_not_void: Result /= Void generator_not_empty: not Result.is_empty end has (key: READABLE_STRING_GENERAL): BOOLEAN -- Is there an item in the table with key key? -- (from HASH_TABLE) require -- from TABLE True local l_default_key: detachable READABLE_STRING_GENERAL hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32 l_first_deleted_position: INTEGER_32 stop: INTEGER_32 l_keys: like keys l_indexes: like indexes_map l_deleted_marks: like deleted_marks l_key: READABLE_STRING_GENERAL do l_first_deleted_position := Ht_impossible_position if key = l_default_key or key = Void then if has_default then Result := True end else from l_keys := keys l_indexes := indexes_map l_deleted_marks := deleted_marks l_capacity := capacity stop := l_capacity hash_value := hash_code_of (key) increment := 1 + hash_value \\ (l_capacity - 1) l_item_pos := (hash_value \\ l_capacity) - increment until stop = 0 loop l_item_pos := (l_item_pos + increment) \\ l_capacity l_pos := l_indexes [l_item_pos] if l_pos >= 0 then l_key := l_keys.item (l_pos) debug ("detect_hash_table_catcall") check catcall_detected: l_key /= Void and then l_key.same_type (key) end end if same_keys (l_key, key) then stop := 1 Result := True end elseif l_pos = Ht_impossible_position then stop := 1 elseif l_first_deleted_position = Ht_impossible_position then l_pos := - l_pos + Ht_deleted_position check l_pos_valid: l_pos < l_deleted_marks.count end if not l_deleted_marks [l_pos] then stop := 1 else l_first_deleted_position := l_item_pos end end stop := stop - 1 end end ensure then -- from HASH_TABLE default_case: (key = computed_default_key) implies (Result = has_default) end has_item (v: G): BOOLEAN -- Does structure include v? -- (Reference or object equality, based on object_comparison.) -- (from HASH_TABLE) local i, nb: INTEGER_32 l_content: like content do if has_default then Result := v = content.item (indexes_map.item (capacity)) end if not Result then l_content := content nb := l_content.count if object_comparison then from until i = nb or else Result loop Result := occupied (i) and then (v ~ l_content.item (i)) i := i + 1 end else from until i = nb or else Result loop Result := occupied (i) and then (v = l_content.item (i)) i := i + 1 end end end ensure -- from CONTAINER not_found_in_empty: Result implies not is_empty end has_key (key: READABLE_STRING_GENERAL): BOOLEAN -- Is there an item in the table with key key? -- Set found_item to the found item. -- (from HASH_TABLE) local old_position: INTEGER_32 l_default_value: detachable G do old_position := item_position internal_search (key) Result := found if Result then found_item := content.item (position) else found_item := l_default_value end item_position := old_position ensure then -- from HASH_TABLE default_case: (key = computed_default_key) implies (Result = has_default) found: Result = found item_if_found: found implies (found_item = item (key)) end item alias "[]" (key: READABLE_STRING_GENERAL): detachable G assign force -- Item associated with key, if present -- otherwise default value of type G. -- Was declared in HASH_TABLE as synonym of at. -- (from HASH_TABLE) note option: stable local l_default_key: detachable READABLE_STRING_GENERAL hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32 l_first_deleted_position: INTEGER_32 stop: INTEGER_32 l_keys: like keys l_indexes: like indexes_map l_deleted_marks: like deleted_marks l_key: READABLE_STRING_GENERAL do l_first_deleted_position := Ht_impossible_position if key = l_default_key or key = Void then if has_default then Result := content.item (indexes_map.item (capacity)) end else from l_keys := keys l_indexes := indexes_map l_deleted_marks := deleted_marks l_capacity := capacity stop := l_capacity hash_value := hash_code_of (key) increment := 1 + hash_value \\ (l_capacity - 1) l_item_pos := (hash_value \\ l_capacity) - increment until stop = 0 loop l_item_pos := (l_item_pos + increment) \\ l_capacity l_pos := l_indexes [l_item_pos] if l_pos >= 0 then l_key := l_keys.item (l_pos) debug ("detect_hash_table_catcall") check catcall_detected: l_key /= Void and then l_key.same_type (key) end end if same_keys (l_key, key) then stop := 1 Result := content.item (l_pos) end elseif l_pos = Ht_impossible_position then stop := 1 elseif l_first_deleted_position = Ht_impossible_position then l_pos := - l_pos + Ht_deleted_position check l_pos_valid: l_pos < l_deleted_marks.count end if not l_deleted_marks [l_pos] then stop := 1 else l_first_deleted_position := l_item_pos end end stop := stop - 1 end end ensure then -- from HASH_TABLE default_value_if_not_present: (not has (key)) implies (Result = computed_default_value) end item_for_iteration: G -- Element at current iteration position -- (from HASH_TABLE) require -- from HASH_TABLE not_off: not off do Result := content.item (iteration_position) end iteration_item (i: INTEGER_32): G -- Entry at position i. -- (from HASH_TABLE) require -- from READABLE_INDEXABLE valid_index: valid_iteration_index (i) do Result := content.item (i) end key_for_iteration: READABLE_STRING_GENERAL -- Key at current iteration position -- (from HASH_TABLE) require -- from HASH_TABLE not_off: not off do Result := keys.item (iteration_position) end new_cursor: HASH_TABLE_ITERATION_CURSOR [G, READABLE_STRING_GENERAL] -- Fresh cursor associated with current structure -- (from HASH_TABLE) require -- from ITERABLE True do create Result.make (Current); Result.start ensure -- from ITERABLE result_attached: Result /= Void end feature -- Measurement capacity: INTEGER_32 -- Number of items that may be stored. -- (from HASH_TABLE) count: INTEGER_32 -- Number of items in table -- (from HASH_TABLE) index_set: INTEGER_INTERVAL obsolete "Use `lower' and `upper' instead. [2017-05-31]" -- Range of acceptable indexes. -- (from READABLE_INDEXABLE) do create Result.make (iteration_lower, iteration_upper) ensure -- from READABLE_INDEXABLE not_void: Result /= Void empty_if_not_in_order: (iteration_lower > iteration_upper) implies Result.is_empty same_lower_if_not_empty: (iteration_lower <= iteration_upper) implies Result.lower = iteration_lower same_upper_if_not_empty: (iteration_lower <= iteration_upper) implies Result.upper = iteration_upper end iteration_lower: INTEGER_32 -- Minimum index. -- (from HASH_TABLE) do Result := next_iteration_position (-1) end iteration_upper: INTEGER_32 -- Maximum index. -- (from HASH_TABLE) do Result := previous_iteration_position (keys.count) end occurrences (v: G): INTEGER_32 -- Number of table items equal to v. -- (from HASH_TABLE) local old_iteration_position: INTEGER_32 do old_iteration_position := iteration_position if object_comparison then from start until off loop if item_for_iteration ~ v then Result := Result + 1 end forth end else from start until off loop if item_for_iteration = v then Result := Result + 1 end forth end end iteration_position := old_iteration_position ensure -- from BAG non_negative_occurrences: Result >= 0 end feature {NONE} -- Measurement estimated_count_of (other: ITERABLE [G]): INTEGER_32 -- Estimated number of elements in other. -- (from CONTAINER) do if attached {FINITE [G]} other as f then Result := f.count elseif attached {READABLE_INDEXABLE [G]} other as r then Result := r.upper - r.lower + 1 end ensure -- from CONTAINER instance_free: class non_negative_result: Result >= 0 end feature -- Comparison frozen deep_equal (a: detachable ANY; b: like arg #1): BOOLEAN -- Are a and b either both void -- or attached to isomorphic object structures? -- (from ANY) do if a = Void then Result := b = Void else Result := b /= Void and then a.is_deep_equal (b) end ensure -- from ANY instance_free: class shallow_implies_deep: standard_equal (a, b) implies Result both_or_none_void: (a = Void) implies (Result = (b = Void)) same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b)) symmetric: Result implies deep_equal (b, a) end disjoint (other: HASH_TABLE [G, READABLE_STRING_GENERAL]): BOOLEAN -- Is Current and other disjoint on their keys? -- Use same_keys for comparison. -- (from HASH_TABLE) do Result := is_empty or else other.is_empty or else not across other as o some has (o.key) end end frozen equal (a: detachable ANY; b: like arg #1): BOOLEAN -- Are a and b either both void or attached -- to objects considered equal? -- (from ANY) do if a = Void then Result := b = Void else Result := b /= Void and then a.is_equal (b) end ensure -- from ANY instance_free: class definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.is_equal (b)) end frozen is_deep_equal alias "≡≡≡" (other: STRING_TABLE [G]): BOOLEAN -- Are Current and other attached to isomorphic object structures? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" ensure -- from ANY shallow_implies_deep: standard_is_equal (other) implies Result same_type: Result implies same_type (other) symmetric: Result implies other.is_deep_equal (Current) end is_equal (other: like Current): BOOLEAN -- Does table contain the same information as other? require -- from ANY other_not_void: other /= Void do if is_case_insensitive = other.is_case_insensitive then Result := Precursor (other) end ensure -- from ANY symmetric: Result implies other ~ Current consistent: standard_is_equal (other) implies Result end same_keys (a_search_key, a_key: READABLE_STRING_GENERAL): BOOLEAN -- Is a_search_key the same key as a_key ? do if is_case_insensitive then Result := a_search_key.is_case_insensitive_equal (a_key) else Result := a_search_key.same_string (a_key) end end frozen standard_equal (a: detachable ANY; b: like arg #1): BOOLEAN -- Are a and b either both void or attached to -- field-by-field identical objects of the same type? -- Always uses default object comparison criterion. -- (from ANY) do if a = Void then Result := b = Void else Result := b /= Void and then a.standard_is_equal (b) end ensure -- from ANY instance_free: class definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.standard_is_equal (b)) end frozen standard_is_equal alias "≜" (other: STRING_TABLE [G]): BOOLEAN -- Is other attached to an object of the same type -- as current object, and field-by-field identical to it? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" ensure -- from ANY same_type: Result implies same_type (other) symmetric: Result implies other.standard_is_equal (Current) end feature -- Status report after: BOOLEAN -- Is cursor past last item? -- Was declared in HASH_TABLE as synonym of off. -- (from HASH_TABLE) do Result := is_off_position (iteration_position) end changeable_comparison_criterion: BOOLEAN -- May object_comparison be changed? -- (Answer: yes by default.) -- (from CONTAINER) do Result := True end conflict: BOOLEAN -- Did last operation cause a conflict? -- (from HASH_TABLE) do Result := control = Conflict_constant end conforms_to (other: ANY): BOOLEAN -- Does type of current object conform to type -- of other (as per Eiffel: The Language, chapter 13)? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" end empty: BOOLEAN obsolete "ELKS 2000: Use `is_empty' instead. [2017-05-31]" -- Is there no element? -- (from CONTAINER) do Result := is_empty end Extendible: BOOLEAN = False -- May new items be added? -- (from HASH_TABLE) found: BOOLEAN -- Did last operation find the item sought? -- (from HASH_TABLE) do Result := control = Found_constant end Full: BOOLEAN = False -- Is structure filled to capacity? -- (from HASH_TABLE) inserted: BOOLEAN -- Did last operation insert an item? -- (from HASH_TABLE) do Result := control = Inserted_constant end is_case_insensitive: BOOLEAN -- Ignoring case when comparing keys? -- (Default: False) is_empty: BOOLEAN -- Is structure empty? -- (from FINITE) require -- from CONTAINER True do Result := count = 0 end is_inserted (v: G): BOOLEAN -- Has v been inserted by the most recent insertion? -- (By default, the value returned is equivalent to calling -- has (v). However, descendants might be able to provide more -- efficient implementations.) -- (from COLLECTION) do Result := has_item (v) end not_found: BOOLEAN -- Did last operation fail to find the item sought? -- (from HASH_TABLE) do Result := control = Not_found_constant end object_comparison: BOOLEAN -- Must search operations use equal rather than = -- for comparing references? (Default: no, use =.) -- (from CONTAINER) off: BOOLEAN -- Is cursor past last item? -- Was declared in HASH_TABLE as synonym of after. -- (from HASH_TABLE) do Result := is_off_position (iteration_position) end prunable: BOOLEAN -- May items be removed? (Answer: yes.) -- (from DYNAMIC_TABLE) do Result := True end removed: BOOLEAN -- Did last operation remove an item? -- (from HASH_TABLE) do Result := control = Removed_constant end replaced: BOOLEAN -- Did last operation replace an item? -- (from HASH_TABLE) do Result := control = Replaced_constant end same_type (other: ANY): BOOLEAN -- Is type of current object identical to type of other? -- (from ANY) require -- from ANY other_not_void: other /= Void external "built_in" ensure -- from ANY definition: Result = (conforms_to (other) and other.conforms_to (Current)) end valid_cursor (c: CURSOR): BOOLEAN -- Can cursor be moved to position c? -- (from HASH_TABLE) require -- from HASH_TABLE c_not_void: c /= Void local i: INTEGER_32 do if attached {HASH_TABLE_CURSOR} c as ht_cursor then i := ht_cursor.position Result := is_off_position (i) or else truly_occupied (i) end end valid_iteration_index (i: INTEGER_32): BOOLEAN -- Is i a valid index? -- (from HASH_TABLE) do Result := truly_occupied (i) ensure -- from READABLE_INDEXABLE only_if_in_index_set: Result implies (iteration_lower <= i and i <= iteration_upper) end valid_key (k: READABLE_STRING_GENERAL): BOOLEAN obsolete "Remove the call to this feature or use `has` instead. [2018-11-30]" -- Is k a valid key? -- (from HASH_TABLE) do Result := True debug ("prevent_hash_table_catcall") if not ({READABLE_STRING_GENERAL}).is_expanded and then attached k then Result := k.generating_type ~ {detachable READABLE_STRING_GENERAL} end end end feature -- Status setting compare_objects -- Ensure that future search operations will use equal -- rather than = for comparing references. -- (from CONTAINER) require -- from CONTAINER changeable_comparison_criterion: changeable_comparison_criterion do object_comparison := True ensure -- from CONTAINER object_comparison end compare_references -- Ensure that future search operations will use = -- rather than equal for comparing references. -- (from CONTAINER) require -- from CONTAINER changeable_comparison_criterion: changeable_comparison_criterion do object_comparison := False ensure -- from CONTAINER reference_comparison: not object_comparison end feature -- Cursor movement forth -- Advance cursor to next occupied position, -- or off if no such position remains. -- (from HASH_TABLE) require -- from HASH_TABLE not_off: not off do iteration_position := next_iteration_position (iteration_position) end go_to (c: CURSOR) -- Move to position c. -- (from HASH_TABLE) require -- from HASH_TABLE c_not_void: c /= Void valid_cursor: valid_cursor (c) do if attached {HASH_TABLE_CURSOR} c as ht_cursor then iteration_position := ht_cursor.position end end search (key: READABLE_STRING_GENERAL) -- Search for item of key key. -- If found, set found to true, and set -- found_item to item associated with key. -- (from HASH_TABLE) local old_position: INTEGER_32 l_default_value: detachable G do old_position := item_position internal_search (key) if found then found_item := content.item (position) else found_item := l_default_value end item_position := old_position ensure -- from HASH_TABLE found_or_not_found: found or not_found item_if_found: found implies (found_item = item (key)) end search_item: detachable G obsolete "Use `found_item` instead. [2017-05-31]" -- (from HASH_TABLE) do Result := found_item end start -- Bring cursor to first position. -- (from HASH_TABLE) do iteration_position := next_iteration_position (-1) end feature {HASH_TABLE_ITERATION_CURSOR} -- Cursor movement next_iteration_position (a_position: like iteration_position): like iteration_position -- Given an iteration position, advanced to the next one taking into account deleted -- slots in the content and keys structures. -- (from HASH_TABLE) require -- from HASH_TABLE a_position_big_enough: a_position >= -1 a_position_small_enough: a_position < keys.count local l_deleted_marks: like deleted_marks l_table_size: INTEGER_32 do l_deleted_marks := deleted_marks l_table_size := content.count from Result := a_position + 1 until Result >= l_table_size or else not l_deleted_marks.item (Result) loop Result := Result + 1 end ensure -- from HASH_TABLE is_increased: Result > a_position is_below_upper_bound: Result <= keys.count end previous_iteration_position (a_position: like iteration_position): like iteration_position -- Given an iteration position, go to the previous one taking into account deleted -- slots in the content and keys structures. -- (from HASH_TABLE) require -- from HASH_TABLE a_position_big_enough: a_position >= 0 a_position_small_enough: a_position <= keys.count local l_deleted_marks: like deleted_marks do l_deleted_marks := deleted_marks from Result := a_position - 1 until Result <= -1 or else not l_deleted_marks.item (Result) loop Result := Result - 1 end ensure -- from HASH_TABLE is_decreased: Result < a_position is_above_lower_bound: Result >= -1 end feature -- Element change extend (new: G; key: READABLE_STRING_GENERAL) -- Assuming there is no item of key key, -- insert new with key. -- Set inserted. -- -- To choose between various insert/replace procedures, -- see instructions in the Indexing clause. -- (from HASH_TABLE) require -- from HASH_TABLE not_present: not has (key) local l_default_key: detachable READABLE_STRING_GENERAL l_new_pos, l_new_index_pos: like position do search_for_insertion (key) if soon_full then add_space search_for_insertion (key) end if deleted_item_position = Ht_impossible_position then l_new_pos := keys.count l_new_index_pos := item_position else l_new_pos := deleted_position (deleted_item_position) l_new_index_pos := deleted_item_position; deleted_marks.force (False, l_new_pos) end; indexes_map.put (l_new_pos, l_new_index_pos); content.force (new, l_new_pos); keys.force (key, l_new_pos) if key = l_default_key then has_default := True end count := count + 1 control := Inserted_constant ensure -- from HASH_TABLE inserted: inserted insertion_done: item (key) = new one_more: count = old count + 1 default_property: has_default = ((key = computed_default_key) or (old has_default)) end fill (other: CONTAINER [G]) -- Fill with as many items of other as possible. -- The representations of other and current structure -- need not be the same. -- (from COLLECTION) require -- from COLLECTION other_not_void: other /= Void extendible: Extendible local lin_rep: LINEAR [G] do lin_rep := other.linear_representation from lin_rep.start until not Extendible or else lin_rep.off loop collection_extend (lin_rep.item); lin_rep.forth end end force (new: G; key: READABLE_STRING_GENERAL) -- Update table so that new will be the item associated -- with key. -- If there was an item for that key, set found -- and set found_item to that item. -- If there was none, set not_found and set -- found_item to the default value. -- -- To choose between various insert/replace procedures, -- see instructions in the Indexing clause. -- (from HASH_TABLE) require -- from TABLE valid_key: has (key) require else -- from HASH_TABLE True local l_default_key: detachable READABLE_STRING_GENERAL l_default_value: detachable G l_new_pos, l_new_index_pos: like position do internal_search (key) if not_found then if soon_full then add_space internal_search (key) end if deleted_item_position = Ht_impossible_position then l_new_pos := keys.count l_new_index_pos := item_position else l_new_pos := deleted_position (deleted_item_position) l_new_index_pos := deleted_item_position; deleted_marks.force (False, l_new_pos) end; indexes_map.put (l_new_pos, l_new_index_pos); keys.force (key, l_new_pos) if key = l_default_key then has_default := True end count := count + 1 found_item := l_default_value else l_new_pos := position found_item := content.item (l_new_pos) end; content.force (new, l_new_pos) ensure -- from TABLE inserted: definite_item (key) = new ensure then -- from HASH_TABLE insertion_done: item (key) = new now_present: has (key) found_or_not_found: found or not_found not_found_if_was_not_present: not_found = not (old has (key)) same_count_or_one_more: (count = old count) or (count = old count + 1) found_item_is_old_item: found implies (found_item = old (item (key))) default_value_if_not_found: not_found implies (found_item = computed_default_value) default_property: has_default = ((key = computed_default_key) or ((key /= computed_default_key) and (old has_default))) end merge (other: HASH_TABLE [G, READABLE_STRING_GENERAL]) -- Merge other into Current. If other has some elements -- with same key as in Current, replace them by one from -- other. -- (from HASH_TABLE) require -- from HASH_TABLE other_not_void: other /= Void do across other as other_cursor loop force (other_cursor.item, other_cursor.key) end ensure -- from HASH_TABLE inserted: across other as other_cursor all has (other_cursor.key) end end put (new: G; key: READABLE_STRING_GENERAL) -- Insert new with key if there is no other item -- associated with the same key. -- Set inserted if and only if an insertion has -- been made (i.e. key was not present). -- If so, set position to the insertion position. -- If not, set conflict. -- In either case, set found_item to the item -- now associated with key (previous item if -- there was one, new otherwise). -- -- To choose between various insert/replace procedures, -- see instructions in the Indexing clause. -- (from HASH_TABLE) require -- from TABLE valid_key: has (key) require else -- from HASH_TABLE True local l_default_key: detachable READABLE_STRING_GENERAL l_new_pos, l_new_index_pos: like position do internal_search (key) if found then set_conflict found_item := content.item (position) else if soon_full then add_space internal_search (key) check not_present: not found end end if deleted_item_position = Ht_impossible_position then l_new_pos := keys.count l_new_index_pos := item_position else l_new_pos := deleted_position (deleted_item_position) l_new_index_pos := deleted_item_position; deleted_marks.force (False, l_new_pos) end; indexes_map.put (l_new_pos, l_new_index_pos); content.force (new, l_new_pos); keys.force (key, l_new_pos) if key = l_default_key then has_default := True end count := count + 1 found_item := new control := Inserted_constant end ensure then -- from HASH_TABLE conflict_or_inserted: conflict or inserted insertion_done: inserted implies item (key) = new now_present: inserted implies has (key) one_more_if_inserted: inserted implies (count = old count + 1) unchanged_if_conflict: conflict implies (count = old count) same_item_if_conflict: conflict implies (item (key) = old (item (key))) found_item_associated_with_key: found_item = item (key) new_item_if_inserted: inserted implies (found_item = new) old_item_if_conflict: conflict implies (found_item = old (item (key))) default_property: has_default = ((inserted and (key = computed_default_key)) or ((conflict or (key /= computed_default_key)) and (old has_default))) end replace (new: G; key: READABLE_STRING_GENERAL) -- Replace item at key, if present, -- with new; do not change associated key. -- Set replaced if and only if a replacement has been made -- (i.e. key was present); otherwise set not_found. -- Set found_item to the item previously associated -- with key (default value if there was none). -- -- To choose between various insert/replace procedures, -- see instructions in the Indexing clause. -- (from HASH_TABLE) local l_default_item: detachable G do internal_search (key) if found then found_item := content.item (position); content.put (new, position) control := Replaced_constant else found_item := l_default_item end ensure -- from HASH_TABLE replaced_or_not_found: replaced or not_found insertion_done: replaced implies item (key) = new no_change_if_not_found: not_found implies item (key) = old item (key) found_item_is_old_item: found_item = old item (key) end replace_key (new_key: READABLE_STRING_GENERAL; old_key: READABLE_STRING_GENERAL) -- If there is an item of key old_key and no item of key -- new_key, replace the former's key by new_key, -- set replaced, and set found_item to the item -- previously associated with old_key. -- Otherwise set not_found or conflict respectively. -- If conflict, set found_item to the item previously -- associated with new_key. -- -- To choose between various insert/replace procedures, -- see instructions in the Indexing clause. -- (from HASH_TABLE) local l_item: G do internal_search (new_key) if not found then internal_search (old_key) if found then l_item := content.item (position) remove (old_key) put (l_item, new_key) control := Replaced_constant end else set_conflict found_item := content.item (position) end ensure -- from HASH_TABLE same_count: count = old count replaced_or_conflict_or_not_found: replaced or conflict or not_found old_absent: (replaced and not same_keys (new_key, old_key)) implies (not has (old_key)) new_present: (replaced or conflict) = has (new_key) new_item: replaced implies (item (new_key) = old (item (old_key))) not_found_implies_no_old_key: not_found implies old (not has (old_key)) conflict_iff_already_present: conflict = old has (new_key) not_inserted_if_conflict: conflict implies (item (new_key) = old item (new_key)) end feature -- Removal clear_all obsolete "Use `wipe_out' instead. [2017-05-31]" -- (from HASH_TABLE) do wipe_out end prune (v: G) -- Remove first occurrence of v, if any, -- after cursor position. -- Move cursor to right neighbor. -- (or after if no right neighbor or v does not occur) -- (from HASH_TABLE) require -- from COLLECTION prunable: prunable do if object_comparison then from until after or else item_for_iteration ~ v loop forth end else from until after or else item_for_iteration = v loop forth end end if not after then remove (key_for_iteration) end end prune_all (v: G) -- Remove all occurrences of v. -- (Reference or object equality, -- based on object_comparison.) -- (from COLLECTION) require -- from COLLECTION prunable: prunable do from until not has_item (v) loop prune (v) end ensure -- from COLLECTION no_more_occurrences: not has_item (v) end remove (key: READABLE_STRING_GENERAL) -- Remove item associated with key, if present. -- Set removed if and only if an item has been -- removed (i.e. key was present); -- if so, set position to index of removed element. -- If not, set not_found. -- Reset found_item to its default value if removed. -- (from HASH_TABLE) require -- from DYNAMIC_TABLE prunable: prunable valid_key: has (key) require else -- from HASH_TABLE prunable local l_default_key: detachable READABLE_STRING_GENERAL l_default_value: detachable G l_pos: like position l_nb_removed_items: INTEGER_32 do internal_search (key) if found then l_pos := position if key = l_default_key then has_default := False end; deleted_marks.put (True, l_pos); indexes_map.put (- l_pos + Ht_deleted_position, item_position) if iteration_position = l_pos then forth end count := count - 1 ht_lowest_deleted_position := l_pos.min (ht_lowest_deleted_position) if ht_lowest_deleted_position = count then l_nb_removed_items := content.count - ht_lowest_deleted_position; content.remove_tail (l_nb_removed_items); keys.remove_tail (l_nb_removed_items); deleted_marks.fill_with (False, ht_lowest_deleted_position, deleted_marks.count - 1) ht_deleted_item := l_default_value ht_deleted_key := l_default_key ht_lowest_deleted_position := Ht_max_position elseif attached ht_deleted_item as l_item and attached ht_deleted_key as l_key then content.put (l_item, l_pos); keys.put (l_key, l_pos) else ht_deleted_item := content.item (l_pos) ht_deleted_key := keys.item (l_pos) end control := Removed_constant found_item := l_default_value end ensure then -- from HASH_TABLE removed_or_not_found: removed or not_found not_present: not has (key) one_less: found implies (count = old count - 1) default_case: (key = computed_default_key) implies (not has_default) non_default_case: (key /= computed_default_key) implies (has_default = old has_default) end wipe_out -- Reset all items to default values; reset status. -- (from HASH_TABLE) require -- from COLLECTION prunable: prunable local l_default_value: detachable G do content.wipe_out; keys.wipe_out; deleted_marks.fill_with (False, 0, deleted_marks.upper); indexes_map.fill_with (Ht_impossible_position, 0, capacity) found_item := l_default_value count := 0 item_position := 0 iteration_position := keys.count control := 0 has_default := False ensure -- from COLLECTION wiped_out: is_empty ensure then -- from HASH_TABLE position_equal_to_zero: item_position = 0 count_equal_to_zero: count = 0 has_default_set: not has_default no_status: not special_status end feature -- Transformation correct_mismatch -- Attempt to correct object mismatch during retrieve using Mismatch_information. -- (from HASH_TABLE) local l_old_deleted_marks: detachable SPECIAL [BOOLEAN] i, l_capacity, l_count: INTEGER_32 l_new_table: STRING_TABLE [G] l_default_item: like ht_deleted_item l_default_key: like ht_deleted_key do if not Mismatch_information.has ("hash_table_version_64") then if attached {ARRAY [G]} Mismatch_information.item ("content") as array_content then content := array_content.area end if attached {ARRAY [READABLE_STRING_GENERAL]} Mismatch_information.item ("keys") as array_keys then keys := array_keys.area end if attached {ARRAY [BOOLEAN]} Mismatch_information.item ("deleted_marks") as array_marks then deleted_marks := array_marks.area end if deleted_marks /= Void and keys /= Void and then not Mismatch_information.has ("hash_table_version_57") and then deleted_marks.count /= keys.count then l_old_deleted_marks := deleted_marks create deleted_marks.make_empty (keys.count); deleted_marks.copy_data (l_old_deleted_marks, 0, 0, l_old_deleted_marks.count) end if attached {INTEGER_32} Mismatch_information.item ("count") as l_retrieved_count then l_count := l_retrieved_count end if content = Void or keys = Void or deleted_marks = Void then Precursor {MISMATCH_CORRECTOR} else from l_capacity := keys.count l_new_table := empty_duplicate (l_count) until i = l_capacity loop if attached keys.item (i) as l_key_item and then l_key_item /= l_default_key then l_new_table.put (content.item (i), l_key_item) end i := i + 1 end if attached {BOOLEAN} Mismatch_information.item ("has_default") as l_bool and then l_bool then l_new_table.put (content.item (content.capacity - 1), keys.item (l_capacity - 1)) end set_content (l_new_table.content) set_keys (l_new_table.keys) set_deleted_marks (l_new_table.deleted_marks) set_indexes_map (l_new_table.indexes_map) capacity := l_new_table.capacity iteration_position := l_new_table.iteration_position deleted_item_position := l_new_table.deleted_item_position item_position := l_new_table.item_position ht_lowest_deleted_position := Ht_max_position ht_deleted_item := l_default_item ht_deleted_key := l_default_key end control := 0 end end feature {NONE} -- Transformation hash_table_version_64: BOOLEAN -- Fake attribute for versioning purposes. Used in correct_mismatch. -- (from HASH_TABLE) feature -- Conversion linear_representation: ARRAYED_LIST [G] -- Representation as a linear structure -- (from HASH_TABLE) require -- from CONTAINER True local old_iteration_position: INTEGER_32 do old_iteration_position := iteration_position from create Result.make (count) start until off loop Result.extend (item_for_iteration) forth end iteration_position := old_iteration_position ensure then -- from HASH_TABLE result_exists: Result /= Void good_count: Result.count = count end feature {NONE} -- Duplication empty_duplicate (n: INTEGER_32): like Current -- Create an empty copy of Current that can accommodate n items require -- from HASH_TABLE n_non_negative: n >= 0 do if is_case_insensitive then create Result.make_caseless (n) else create Result.make (n) end if object_comparison then Result.compare_objects end ensure -- from HASH_TABLE empty_duplicate_attached: Result /= Void end feature -- Duplication frozen clone (other: detachable ANY): like other obsolete "Use `twin' instead. [2017-05-31]" -- Void if other is void; otherwise new object -- equal to other -- -- For non-void other, clone calls copy; -- to change copying/cloning semantics, redefine copy. -- (from ANY) do if other /= Void then Result := other.twin end ensure -- from ANY instance_free: class equal: Result ~ other end copy (other: STRING_TABLE [G]) -- Re-initialize from other. -- (from HASH_TABLE) require -- from ANY other_not_void: other /= Void type_identity: same_type (other) do if other /= Current then standard_copy (other) set_content (other.content.twin) set_keys (other.keys.twin) set_deleted_marks (other.deleted_marks.twin) set_indexes_map (other.indexes_map.twin) end ensure -- from ANY is_equal: Current ~ other end frozen deep_clone (other: detachable ANY): like other obsolete "Use `deep_twin' instead. [2017-05-31]" -- Void if other is void: otherwise, new object structure -- recursively duplicated from the one attached to other -- (from ANY) do if other /= Void then Result := other.deep_twin end ensure -- from ANY instance_free: class deep_equal: deep_equal (other, Result) end frozen deep_copy (other: STRING_TABLE [G]) -- Effect equivalent to that of: -- copy (other . deep_twin) -- (from ANY) require -- from ANY other_not_void: other /= Void do copy (other.deep_twin) ensure -- from ANY deep_equal: deep_equal (Current, other) end frozen deep_twin: STRING_TABLE [G] -- New object structure recursively duplicated from Current. -- (from ANY) external "built_in" ensure -- from ANY deep_twin_not_void: Result /= Void deep_equal: deep_equal (Current, Result) end frozen standard_clone (other: detachable ANY): like other obsolete "Use `standard_twin' instead. [2017-05-31]" -- Void if other is void; otherwise new object -- field-by-field identical to other. -- Always uses default copying semantics. -- (from ANY) do if other /= Void then Result := other.standard_twin end ensure -- from ANY instance_free: class equal: standard_equal (Result, other) end frozen standard_copy (other: STRING_TABLE [G]) -- Copy every field of other onto corresponding field -- of current object. -- (from ANY) require -- from ANY other_not_void: other /= Void type_identity: same_type (other) external "built_in" ensure -- from ANY is_standard_equal: standard_is_equal (other) end frozen standard_twin: STRING_TABLE [G] -- New object field-by-field identical to other. -- Always uses default copying semantics. -- (from ANY) external "built_in" ensure -- from ANY standard_twin_not_void: Result /= Void equal: standard_equal (Result, Current) end frozen twin: STRING_TABLE [G] -- New object equal to Current -- twin calls copy; to change copying/twinning semantics, redefine copy. -- (from ANY) external "built_in" ensure -- from ANY twin_not_void: Result /= Void is_equal: Result ~ Current end feature -- Basic operations frozen as_attached: attached STRING_TABLE [G] obsolete "Remove calls to this feature. [2017-05-31]" -- Attached version of Current. -- (Can be used during transitional period to convert -- non-void-safe classes to void-safe ones.) -- (from ANY) do Result := Current end frozen default: detachable STRING_TABLE [G] -- Default value of object's type -- (from ANY) do end frozen default_pointer: POINTER -- Default value of type POINTER -- (Avoid the need to write p.default for -- some p of type POINTER.) -- (from ANY) do ensure -- from ANY instance_free: class end default_rescue -- Process exception for routines with no Rescue clause. -- (Default: do nothing.) -- (from ANY) do end frozen do_nothing -- Execute a null action. -- (from ANY) do ensure -- from ANY instance_free: class end feature -- Inapplicable bag_put (v: G) -- Ensure that structure includes v. -- (from TABLE) require -- from COLLECTION extendible: Extendible do ensure -- from COLLECTION item_inserted: is_inserted (v) end collection_extend (v: G) -- Insert a new occurrence of v. -- (from HASH_TABLE) require -- from COLLECTION extendible: Extendible do ensure -- from COLLECTION item_inserted: is_inserted (v) end feature {NONE} -- Implementation add_space -- Increase capacity. -- (from HASH_TABLE) do accommodate (count + count // 2) ensure -- from HASH_TABLE count_not_changed: count = old count breathing_space: count < capacity end computed_default_key: detachable READABLE_STRING_GENERAL -- Default key -- (For performance reasons, used only in assertions; -- elsewhere, see use of local entity l_default_key.) -- (from HASH_TABLE) do end computed_default_value: detachable G -- Default value of type G -- (For performance reasons, used only in assertions; -- elsewhere, see use of local entity l_default_value.) -- (from HASH_TABLE) do end Conflict_constant: INTEGER_32 = 1 -- Could not insert an already existing key -- (from HASH_TABLE) default_key_value: G -- Value associated with the default key, if any -- (from HASH_TABLE) require -- from HASH_TABLE has_default: has_default do Result := content [indexes_map [capacity]] end deleted (i: INTEGER_32): BOOLEAN -- Is position i that of a deleted item? -- (from HASH_TABLE) require -- from HASH_TABLE in_bounds: i >= 0 and i <= capacity do Result := indexes_map.item (i) <= Ht_deleted_position end deleted_position (a_pos: INTEGER_32): INTEGER_32 -- Given the position of a deleted item at a_pos gives the associated position -- in content/keys. -- (from HASH_TABLE) require -- from HASH_TABLE deleted: deleted (a_pos) do Result := - indexes_map.item (a_pos) + Ht_deleted_position Result := Result.min (keys.count) ensure -- from HASH_TABLE deleted_position_non_negative: Result >= 0 deleted_position_valid: Result <= keys.count and Result <= content.count end Found_constant: INTEGER_32 = 2 -- Key found -- (from HASH_TABLE) ht_deleted_item: detachable G -- (from HASH_TABLE) ht_deleted_key: detachable READABLE_STRING_GENERAL -- Store the item and key that will be used to replace an element of the HASH_TABLE -- that will be removed. If elements being removed are at the end of content or keys -- then they are both Void. It is only used when removing an element at a position strictly -- less than count. -- (from HASH_TABLE) Ht_deleted_position: INTEGER_32 = -2 -- Marked a deleted position. -- (from HASH_TABLE) Ht_impossible_position: INTEGER_32 = -1 -- Position outside the array indices. -- (from HASH_TABLE) ht_lowest_deleted_position: INTEGER_32 -- Index of the lowest deleted position thus far. -- (from HASH_TABLE) Ht_max_position: INTEGER_32 = 2147483645 -- Maximum possible position -- (from HASH_TABLE) initial_position (hash_value: INTEGER_32): INTEGER_32 -- Initial position for an item of hash code hash_value -- (from HASH_TABLE) do Result := hash_value \\ capacity end Inserted_constant: INTEGER_32 = 4 -- Insertion successful -- (from HASH_TABLE) internal_search (key: READABLE_STRING_GENERAL) -- Search for item of key key. -- If successful, set position to index -- of item with this key (the same index as the key's index). -- If not, set position to possible position for insertion, -- and set status to found or not_found. -- (from HASH_TABLE) local l_default_key: detachable READABLE_STRING_GENERAL hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32 l_first_deleted_position: INTEGER_32 stop: INTEGER_32 l_keys: like keys l_indexes: like indexes_map l_deleted_marks: like deleted_marks l_key: READABLE_STRING_GENERAL do l_first_deleted_position := Ht_impossible_position if key = l_default_key or key = Void then item_position := capacity if has_default then control := Found_constant else control := Not_found_constant end else from l_keys := keys l_indexes := indexes_map l_deleted_marks := deleted_marks l_capacity := capacity stop := l_capacity hash_value := hash_code_of (key) increment := 1 + hash_value \\ (l_capacity - 1) l_item_pos := (hash_value \\ l_capacity) - increment control := Not_found_constant until stop = 0 loop l_item_pos := (l_item_pos + increment) \\ l_capacity l_pos := l_indexes [l_item_pos] if l_pos >= 0 then l_key := l_keys.item (l_pos) debug ("detect_hash_table_catcall") check catcall_detected: l_key /= Void and then l_key.same_type (key) end end if same_keys (l_key, key) then stop := 1 control := Found_constant end elseif l_pos = Ht_impossible_position then stop := 1 elseif l_first_deleted_position = Ht_impossible_position then l_pos := - l_pos + Ht_deleted_position check l_pos_valid: l_pos < l_deleted_marks.count end if not l_deleted_marks [l_pos] then stop := 1 else l_first_deleted_position := l_item_pos end end stop := stop - 1 end item_position := l_item_pos end deleted_item_position := l_first_deleted_position ensure -- from HASH_TABLE found_or_not_found: found or not_found deleted_item_at_deleted_position: (deleted_item_position /= Ht_impossible_position) implies deleted (deleted_item_position) default_iff_at_capacity: (item_position = capacity) = (key = computed_default_key) end is_off_position (pos: INTEGER_32): BOOLEAN -- Is pos a cursor position outside the authorized range? -- (from HASH_TABLE) do Result := pos < 0 or pos >= keys.count end key_at (n: INTEGER_32): detachable READABLE_STRING_GENERAL -- Key at position n -- (from HASH_TABLE) do if keys.valid_index (n) then Result := keys.item (n) end end Minimum_capacity: INTEGER_32 = 2 -- (from HASH_TABLE) Not_found_constant: INTEGER_32 = 8 -- Key not found -- (from HASH_TABLE) occupied (i: INTEGER_32): BOOLEAN -- Is position i occupied by a non-default key and a value? -- (from HASH_TABLE) require -- from HASH_TABLE in_bounds: deleted_marks.valid_index (i) do if has_default then Result := i /= indexes_map.item (capacity) and then not deleted_marks.item (i) else Result := not deleted_marks.item (i) end end position_increment (hash_value: INTEGER_32): INTEGER_32 -- Distance between successive positions for hash code -- hash_value (computed for no cycle: capacity is prime) -- (from HASH_TABLE) do Result := 1 + hash_value \\ (capacity - 1) end Removed_constant: INTEGER_32 = 16 -- Remove successful -- (from HASH_TABLE) Replaced_constant: INTEGER_32 = 32 -- Replaced value -- (from HASH_TABLE) search_for_insertion (key: READABLE_STRING_GENERAL) -- Assuming there is no item of key key, compute -- position at which to insert such an item. -- (from HASH_TABLE) require -- from HASH_TABLE not_present: not has (key) local l_default_key: detachable READABLE_STRING_GENERAL hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32 l_first_deleted_position: INTEGER_32 stop: INTEGER_32 l_indexes: like indexes_map l_deleted_marks: like deleted_marks do l_first_deleted_position := Ht_impossible_position if key = l_default_key or key = Void then check not has_default end item_position := capacity else from l_indexes := indexes_map l_deleted_marks := deleted_marks l_capacity := capacity stop := l_capacity hash_value := hash_code_of (key) increment := 1 + hash_value \\ (l_capacity - 1) l_item_pos := (hash_value \\ l_capacity) - increment until stop = 0 loop l_item_pos := (l_item_pos + increment) \\ l_capacity l_pos := l_indexes [l_item_pos] if l_pos >= 0 then elseif l_pos = Ht_impossible_position then stop := 1 elseif l_first_deleted_position = Ht_impossible_position then l_pos := - l_pos + Ht_deleted_position check l_pos_valid: l_pos < l_deleted_marks.count end if not l_deleted_marks [l_pos] then stop := 1 else l_first_deleted_position := l_item_pos end end stop := stop - 1 end item_position := l_item_pos end deleted_item_position := l_first_deleted_position ensure -- from HASH_TABLE deleted_item_at_deleted_position: (deleted_item_position /= Ht_impossible_position) implies deleted (deleted_item_position) default_iff_at_capacity: (item_position = capacity) = (key = computed_default_key) end set_conflict -- Set status to conflict. -- (from HASH_TABLE) do control := Conflict_constant ensure -- from HASH_TABLE conflict: conflict end set_content (c: like content) -- Assign c to content. -- (from HASH_TABLE) require -- from HASH_TABLE c_attached: c /= Void do content := c ensure -- from HASH_TABLE content_set: content = c end set_deleted_marks (d: like deleted_marks) -- Assign c to content. -- (from HASH_TABLE) require -- from HASH_TABLE d_attached: d /= Void do deleted_marks := d ensure -- from HASH_TABLE deleted_marks_set: deleted_marks = d end set_found -- Set status to found. -- (from HASH_TABLE) do control := Found_constant ensure -- from HASH_TABLE found: found end set_indexes_map (v: like indexes_map) -- Assign v to indexes_map. -- (from HASH_TABLE) do indexes_map := v ensure -- from HASH_TABLE indexes_map_set: indexes_map = v end set_inserted -- Set status to inserted. -- (from HASH_TABLE) do control := Inserted_constant ensure -- from HASH_TABLE inserted: inserted end set_keys (c: like keys) -- Assign c to keys. -- (from HASH_TABLE) require -- from HASH_TABLE c_attached: c /= Void do keys := c ensure -- from HASH_TABLE keys_set: keys = c end set_no_status -- Set status to normal. -- (from HASH_TABLE) do control := 0 ensure -- from HASH_TABLE default_status: not special_status end set_not_found -- Set status to not found. -- (from HASH_TABLE) do control := Not_found_constant ensure -- from HASH_TABLE not_found: not_found end set_removed -- Set status to removed. -- (from HASH_TABLE) do control := Removed_constant ensure -- from HASH_TABLE removed: removed end set_replaced -- Set status to replaced. -- (from HASH_TABLE) do control := Replaced_constant ensure -- from HASH_TABLE replaced: replaced end special_status: BOOLEAN -- Has status been set to some non-default value? -- (from HASH_TABLE) do Result := control > 0 ensure -- from HASH_TABLE Result = (control > 0) end truly_occupied (i: INTEGER_32): BOOLEAN -- Is position i occupied by a key and a value? -- (from HASH_TABLE) do if i >= 0 and i < keys.count then Result := (has_default and i = indexes_map.item (capacity)) or else occupied (i) end ensure -- from HASH_TABLE normal_key: (i >= 0 and i < keys.count and i /= indexes_map.item (capacity)) implies (occupied (i) implies Result) default_key: (i = indexes_map.item (capacity)) implies (Result = has_default) end feature -- Correction Mismatch_information: MISMATCH_INFORMATION -- Original attribute values of mismatched object -- (from MISMATCH_CORRECTOR) once create Result end feature -- Hash code hash_code_of (a_key: READABLE_STRING_GENERAL): INTEGER_32 -- Hash code value do if is_case_insensitive then Result := a_key.case_insensitive_hash_code else Result := a_key.hash_code end ensure -- from HASH_TABLE non_negative: Result >= 0 end feature {HASH_TABLE, HASH_TABLE_ITERATION_CURSOR} -- Implementation: content attributes and preservation content: SPECIAL [G] -- Array of contents -- (from HASH_TABLE) keys: SPECIAL [READABLE_STRING_GENERAL] -- Array of keys -- (from HASH_TABLE) feature {HASH_TABLE} -- Implementation: content attributes and preservation deleted_marks: SPECIAL [BOOLEAN] -- Indexes of deleted positions in content and keys. -- (from HASH_TABLE) has_default: BOOLEAN -- Is the default key present? -- (from HASH_TABLE) indexes_map: SPECIAL [INTEGER_32] -- Indexes of items in content, and keys. -- If item is not present, then it has Ht_impossible_position. -- If item is deleted, then it has Ht_deleted_position. -- (from HASH_TABLE) item_position: INTEGER_32 -- Position in indexes_map for item at position position. Set by internal_search. -- (from HASH_TABLE) feature {HASH_TABLE} -- Implementation: search attributes control: INTEGER_32 -- Control code set by operations that may produce -- several possible conditions. -- (from HASH_TABLE) deleted_item_position: INTEGER_32 -- Place where a deleted element was found during a search -- (from HASH_TABLE) iteration_position: INTEGER_32 -- Cursor for iteration primitives -- (from HASH_TABLE) position: INTEGER_32 -- Hash table cursor, updated after each operation: -- put, remove, has, replace, force, change_key... -- (from HASH_TABLE) do Result := indexes_map.item (item_position) end soon_full: BOOLEAN -- Is table close to being filled to current capacity? -- (from HASH_TABLE) do Result := keys.count = keys.capacity ensure -- from HASH_TABLE Result = (keys.count = keys.capacity) end feature -- Output Io: STD_FILES -- Handle to standard file setup -- (from ANY) once create Result; Result.set_output_default ensure -- from ANY instance_free: class io_not_void: Result /= Void end out: STRING_8 -- New string containing terse printable representation -- of current object -- (from ANY) do Result := tagged_out ensure -- from ANY out_not_void: Result /= Void end print (o: detachable ANY) -- Write terse external representation of o -- on standard output. -- (from ANY) local s: READABLE_STRING_8 do if attached o then s := o.out if attached {READABLE_STRING_32} s as s32 then Io.put_string_32 (s32) elseif attached {READABLE_STRING_8} s as s8 then Io.put_string (s8) else Io.put_string_32 (s.as_string_32) end end ensure -- from ANY instance_free: class end frozen tagged_out: STRING_8 -- New string containing terse printable representation -- of current object -- (from ANY) external "built_in" ensure -- from ANY tagged_out_not_void: Result /= Void end feature -- Platform Operating_environment: OPERATING_ENVIRONMENT -- Objects available from the operating system -- (from ANY) once create Result ensure -- from ANY instance_free: class operating_environment_not_void: Result /= Void end feature {NONE} -- Retrieval frozen internal_correct_mismatch -- Called from runtime to perform a proper dynamic dispatch on correct_mismatch -- from MISMATCH_CORRECTOR. -- (from ANY) local l_msg: STRING_32 l_exc: EXCEPTIONS do if attached {MISMATCH_CORRECTOR} Current as l_corrector then l_corrector.correct_mismatch else create l_msg.make_from_string ("Mismatch: ".as_string_32) create l_exc; l_msg.append (generating_type.name_32); l_exc.raise_retrieval_exception (l_msg) end end invariant -- from HASH_TABLE keys_not_void: keys /= Void content_not_void: content /= Void keys_enough_capacity: keys.count <= capacity + 1 content_enough_capacity: content.count <= capacity + 1 valid_iteration_position: off or truly_occupied (iteration_position) control_non_negative: control >= 0 special_status: special_status = (conflict or inserted or replaced or removed or found or not_found) count_big_enough: 0 <= count count_small_enough: count <= capacity slot_count_big_enough: 0 <= count -- from FINITE empty_definition: is_empty = (count = 0) -- from ANY reflexive_equality: standard_is_equal (Current) reflexive_conformance: conforms_to (Current) -- from READABLE_INDEXABLE consistent_boundaries: iteration_lower <= iteration_upper or else iteration_lower = iteration_upper + 1 note copyright: "Copyright (c) 1984-2016, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software 5949 Hollister Ave., Goleta, CA 93117 USA Telephone 805-685-1006, Fax 805-685-6869 Website http://www.eiffel.com Customer support http://support.eiffel.com ]" end -- class STRING_TABLE
Generated by ISE EiffelStudio