1
2(********************************************************************)
3(*                                                                  *)
4(*  hash.s7i      Hash map support library                          *)
5(*  Copyright (C) 1989 - 2012  Thomas Mertes                        *)
6(*                                                                  *)
7(*  This file is part of the Seed7 Runtime Library.                 *)
8(*                                                                  *)
9(*  The Seed7 Runtime Library is free software; you can             *)
10(*  redistribute it and/or modify it under the terms of the GNU     *)
11(*  Lesser General Public License as published by the Free Software *)
12(*  Foundation; either version 2.1 of the License, or (at your      *)
13(*  option) any later version.                                      *)
14(*                                                                  *)
15(*  The Seed7 Runtime Library is distributed in the hope that it    *)
16(*  will be useful, but WITHOUT ANY WARRANTY; without even the      *)
17(*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *)
18(*  PURPOSE.  See the GNU Lesser General Public License for more    *)
19(*  details.                                                        *)
20(*                                                                  *)
21(*  You should have received a copy of the GNU Lesser General       *)
22(*  Public License along with this program; if not, write to the    *)
23(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
24(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
25(*                                                                  *)
26(********************************************************************)
27
28
29(**
30 *  Abstract data type, describing hash maps.
31 *  A hash map stores key-value pairs. A hash map guarantees that a
32 *  key can be mapped quickly to its corresponding value. The keys
33 *  of a hash map have the type ''keyType'' and the value have the
34 *  type ''baseType''. A hash map is only possible, if ''keyType''
35 *  supports the functions ''hashCode'' and ''compare''.
36 *)
37const func type: hash [ (in type: keyType) ] (in type: baseType) is func
38  result
39    var type: hashType is void;
40  begin
41    hashType := get_type(getfunc(hash [ (attr keyType) ] (attr baseType)));
42    if hashType = void then
43      global
44      hashType := newtype;
45      IN_PARAM_IS_REFERENCE(hashType);
46      const type: hash [ (attr keyType) ] (attr baseType)                      is hashType;
47      const type: key_type (attr hashType)                                     is keyType;
48      const type: base_type (attr hashType)                                    is baseType;
49
50      const reference: (attr hashType) . keyCreate   is getfunc((ref keyType: dest) ::= (in keyType: source));
51      const reference: (attr hashType) . keyDestroy  is getfunc(destroy(ref keyType: aValue));
52      const reference: (attr hashType) . keyCopy     is getfunc((inout keyType: dest) := (in keyType: source));
53      const reference: (attr hashType) . keyCompare  is getfunc(compare(in keyType: key1, in keyType: key2));
54      const reference: (attr hashType) . dataCreate  is getfunc((ref baseType: dest) ::= (in baseType: source));
55      const reference: (attr hashType) . dataDestroy is getfunc(destroy(ref baseType: aValue));
56      const reference: (attr hashType) . dataCopy    is getfunc((inout baseType: dest) := (in baseType:source));
57
58      const proc: CREATE (ref hashType: dest, in hashType: source,
59                          in reference: keyCreate, in reference: keyDestroy,
60                          in reference: dataCreate, in reference: dataDestroy) is action "HSH_CREATE";
61      const proc: DESTROY (ref hashType: aValue, in reference: keyDestroy,
62                           in reference: dataDestroy)                          is action "HSH_DESTR";
63      const proc: COPY (inout hashType: dest, in hashType: source,
64                        in reference: keyCreate, in reference: keyDestroy,
65                        in reference: dataCreate, in reference: dataDestroy)   is action "HSH_CPY";
66      const proc: FOR_DATA (inout baseType: forVar, in hashType: aHashMap,
67                            in proc: statements, in reference: dataCopy)       is action "HSH_FOR";
68      const proc: FOR_KEY (inout keyType: keyVar, in hashType: aHashMap,
69                           in proc: statements, in reference: keyCop)          is action "HSH_FOR_KEY";
70      const proc: FOR_DATA_KEY (inout baseType: forVar, inout keyType: keyVar,
71                                in hashType: aHashMap, in proc: statements,
72                                in reference: dataCopy, in reference: keyCopy) is action "HSH_FOR_DATA_KEY";
73      const func array keyType: KEYS (in hashType: aHashMap, in reference: keyCreate,
74                                      in reference: keyDestroy)                is action "HSH_KEYS";
75      const func array baseType: VALUES (in hashType: aHashMap, in reference: dataCreate,
76                                         in reference: dataDestroy)            is action "HSH_VALUES";
77
78      const proc: (ref hashType: dest) ::= (in hashType: source) is func
79        begin
80          CREATE(dest, source, hashType.keyCreate, hashType.keyDestroy,
81              hashType.dataCreate, hashType.dataDestroy);
82        end func;
83
84      const proc: destroy (ref hashType: oldHash) is func
85        begin
86          DESTROY(oldHash, hashType.keyDestroy, hashType.dataDestroy);
87        end func;
88
89      const proc: (inout hashType: dest) := (in hashType: source) is func
90        begin
91          COPY(dest, source, hashType.keyCreate, hashType.keyDestroy,
92              hashType.dataCreate, hashType.dataDestroy);
93        end func;
94
95      (**
96       *  Number of elements in the hash map ''aHashMap''.
97       *  @return the number of elements in ''aHashMap''.
98       *)
99      const func integer: length (in hashType: aHashMap)                       is action "HSH_LNG";
100
101      const func baseType: INDEX (in hashType: aHashMap, in keyType: aKey,
102                                  in integer: hashCode,
103                                  in reference: keyCompare)                    is action "HSH_IDX";
104      const varfunc baseType: INDEX (inout hashType: aHashMap, in keyType: aKey,
105                                     in integer: hashCode,
106                                     in reference: keyCompare)                 is action "HSH_IDX";
107
108      const func baseType: INDEX2 (in hashType: aHashMap, in keyType: aKey,
109                                   in integer: hashCode, in baseType: defaultValue,
110                                   in reference: keyCompare,
111                                   in reference: dataCreate)                   is action "HSH_IDX2";
112
113      const func ptr baseType: REFINDEX (in hashType: aHashMap, in keyType: aKey,
114                                         in integer: hashCode,
115                                         in reference: keyCompare)             is action "HSH_REFIDX";
116      const proc: INCL (inout hashType: aHashMap, in keyType: aKey,
117                        in baseType: anElem, in integer: hashCode,
118                        in reference: keyCompare, in reference: keyCreate,
119                        in reference: dataCreate, in reference: dataCopy)      is action "HSH_INCL";
120      const proc: EXCL (inout hashType: aHashMap, in keyType: aKey,
121                        in integer: hashCode, in reference: keyCompare,
122                        in reference: keyDestroy, in reference: dataDestroy)   is action "HSH_EXCL";
123      const func baseType: UPDATE (inout hashType: aHashMap, in keyType: aKey,
124                                   in baseType: anElem, in integer: hashCode,
125                                   in reference: keyCompare, in reference: keyCreate,
126                                   in reference: dataCreate)                   is action "HSH_UPDATE";
127      const func boolean: CONTAINS (in hashType: aHashMap, in keyType: aKey,
128                                    in integer: hashCode,
129                                    in reference: keyCompare)                  is action "HSH_CONTAINS";
130(*
131      const func hashType: (attr hashType) conv (in hashType: aHashMap)        is action "HSH_CONV";
132      const varfunc hashType: (attr hashType) conv (inout hashType: aHashMap)  is action "TYP_VARCONV";
133*)
134      const func hashType: (attr hashType) . _GENERATE_EMPTY_HASH              is action "HSH_EMPTY";
135      const hashType: (attr hashType) . EMPTY_HASH                             is hashType._GENERATE_EMPTY_HASH;
136      const hashType: (attr hashType) . value                                  is hashType._GENERATE_EMPTY_HASH;
137
138      (**
139       *  Access one value from the hash table ''aHashMap''.
140       *  @return the element with the key ''aKey'' from ''aHashMap''.
141       *  @exception RANGE_ERROR If ''aHashMap'' does not have an element
142       *             with the key ''aKey''.
143       *)
144      const func baseType: (in hashType: aHashMap) [ (in keyType: aKey) ] is
145        return INDEX(aHashMap, aKey, hashCode(aKey), hashType.keyCompare);
146
147      const varfunc baseType: (inout hashType: aHashMap) [ (in keyType: aKey) ] is
148        return var INDEX(aHashMap, aKey, hashCode(aKey), hashType.keyCompare);
149
150      (**
151       *  Access one value from the hash table ''aHashMap''.
152       *  @return the element with the key ''aKey'' from ''aHashMap'' or
153       *          ''defaultValue'' if ''aHashMap'' does not have an element
154       *          with the key ''aKey''.
155       *)
156      const func baseType: (in hashType: aHashMap) [ (in keyType: aKey) default (in baseType: defaultValue) ] is
157        return INDEX2(aHashMap, aKey, hashCode(aKey), defaultValue,
158                      hashType.keyCompare, hashType.dataCreate);
159
160      (**
161       *  Hash membership test.
162       *  Determine if ''aKey'' is a member of the hash map ''aHashMap''.
163       *  @return TRUE if ''aKey'' is a member of ''aHashMap'',
164       *          FALSE otherwise.
165       *)
166      const func boolean: (in keyType: aKey) in (in hashType: aHashMap) is
167        return CONTAINS(aHashMap, aKey, hashCode(aKey), hashType.keyCompare);
168
169      (**
170       *  Negated hash membership test.
171       *  Determine if ''aKey'' is not a member of the hash map ''aHashMap''.
172       *  @return FALSE if ''aKey'' is a member of ''aHashMap'',
173       *          TRUE otherwise.
174       *)
175      const func boolean: (in keyType: aKey) not in (in hashType: aHashMap) is
176        return not CONTAINS(aHashMap, aKey, hashCode(aKey), hashType.keyCompare);
177
178      (**
179       *  Add ''anElem'' with the key ''aKey'' to the hash map ''aHashMap''.
180       *  If an element with the key ''aKey'' already exists,
181       *  it is overwritten with ''anElem''.
182       *  @exception MEMORY_ERROR If there is not enough memory.
183       *)
184      const proc: incl (inout hashType: aHashMap, in keyType: aKey, in baseType: anElem) is func
185        begin
186          INCL(aHashMap, aKey, anElem, hashCode(aKey), hashType.keyCompare,
187              hashType.keyCreate, hashType.dataCreate, hashType.dataCopy);
188        end func;
189
190      (**
191       *  Remove the element with the key ''aKey'' from the hash map ''aHashMap''.
192       *)
193      const proc: excl (inout hashType: aHashMap, in keyType: aKey) is func
194        begin
195          EXCL(aHashMap, aKey, hashCode(aKey), hashType.keyCompare,
196              hashType.keyDestroy, hashType.dataDestroy);
197        end func;
198
199      (**
200       *  Add ''anElem'' with the key ''aKey'' to the hash map ''aHashMap''.
201       *  If an element with the key ''aKey'' already exists,
202       *  it is overwritten with ''anElem''.
203       *  @exception MEMORY_ERROR If there is not enough memory.
204       *)
205      const proc: (inout hashType: aHashMap) @:= [ (in keyType: aKey) ] (in baseType: anElem) is func
206        begin
207          INCL(aHashMap, aKey, anElem, hashCode(aKey), hashType.keyCompare,
208              hashType.keyCreate, hashType.dataCreate, hashType.dataCopy);
209        end func;
210
211      const func baseType: update (inout hashType: aHashMap, in keyType: aKey, in baseType: anElem) is
212        return UPDATE(aHashMap, aKey, anElem, hashCode(aKey), hashType.keyCompare,
213                      hashType.keyCreate, hashType.dataCreate);
214
215(*
216      const proc: clear (inout hashType: aHashMap) is func
217        local
218          var baseType: anElem is baseType.value;
219        begin
220          for anElem range source do
221            excl(dest, anElem);
222          end for;
223        end func;
224*)
225
226      (**
227       *  For-loop where ''forVar'' loops over the values of the hash map ''aHashMap''.
228       *)
229      const proc: for (inout baseType: forVar) range (in hashType: aHashMap) do
230                    (in proc: statements)
231                  end for is func
232        begin
233          FOR_DATA(forVar, aHashMap, statements, hashType.dataCopy);
234        end func;
235
236      (**
237       *  For-loop where ''keyVar'' loops over the keys of the hash map ''aHashMap''.
238       *)
239      const proc: for key (inout keyType: keyVar) range (in hashType: aHashMap) do
240                    (in proc: statements)
241                  end for is func
242        begin
243          FOR_KEY(keyVar, aHashMap, statements, hashType.keyCopy);
244        end func;
245
246      (**
247       *  For-loop where ''forVar'' and ''keyVar' loop over the hash map ''aHashMap''.
248       *)
249      const proc: for (inout baseType: forVar) key (inout keyType: keyVar) range (in hashType: aHashMap) do
250                    (in proc: statements)
251                  end for is func
252        begin
253          FOR_DATA_KEY(forVar, keyVar, aHashMap, statements, hashType.dataCopy, hashType.keyCopy);
254        end func;
255
256      (**
257       *  Obtain the keys of the hash map ''aHashMap''.
258       *  @return the keys of the hash map.
259       *)
260      const func array keyType: keys (in hashType: aHashMap) is
261        return KEYS(aHashMap, hashType.keyCreate, hashType.keyDestroy);
262
263      (**
264       *  Obtain the values of the hash map ''aHashMap''.
265       *  @return the values of the hash map.
266       *)
267      const func array baseType: values (in hashType: aHashMap) is
268        return VALUES(aHashMap, hashType.dataCreate, hashType.dataDestroy);
269
270      if getfunc(hashCode(in baseType: anElem)) <> NIL and
271          getfunc(compare(in baseType: elem1, in baseType: elem2)) <> NIL then
272
273        (**
274         *  Create a hash map from ''aHashMap'' where key and value are exchanged.
275         *  Since a hash value can correspond to many keys the type returned
276         *  is ''hash [baseType] array keyType''.
277         *)
278        const func hash [baseType] array keyType: flip (in hashType: aHashMap) is func
279          result
280            var hash [baseType] array keyType: inverseHash is (hash [baseType] array keyType).value;
281          local
282            var keyType: aKey is keyType.value;
283            var baseType: aValue is baseType.value;
284          begin
285            for aValue key aKey range aHashMap do
286              if aValue in inverseHash then
287                inverseHash[aValue] &:= aKey;
288              else
289                inverseHash @:= [aValue] [] (aKey);
290              end if;
291            end for;
292          end func;
293      end if;
294
295      end global;
296    end if;
297  end func;
298