-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathCollectionKeys.Mod
More file actions
151 lines (131 loc) · 3.71 KB
/
CollectionKeys.Mod
File metadata and controls
151 lines (131 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
(** CollectionKeys.mod - Key types and operations for key-value collections.
Copyright (C) 2025
Released under The 3-Clause BSD License.
*)
MODULE CollectionKeys;
IMPORT Collections, Bitwise;
TYPE
(** Base type for all keys *)
Key* = RECORD(Collections.Item)
(* Base key type - extend this for specific key types *)
END;
KeyPtr* = POINTER TO Key;
(** Integer key implementation *)
IntegerKey* = RECORD(Key)
value*: INTEGER
END;
IntegerKeyPtr* = POINTER TO IntegerKey;
(** String key implementation *)
StringKey* = RECORD(Key)
value*: ARRAY 256 OF CHAR
END;
StringKeyPtr* = POINTER TO StringKey;
(** Key operations interface *)
KeyOps* = RECORD
hash*: PROCEDURE(key: KeyPtr; size: INTEGER): INTEGER;
equals*: PROCEDURE(key1, key2: KeyPtr): BOOLEAN
END;
(** Create integer key *)
PROCEDURE NewIntegerKey*(value: INTEGER): IntegerKeyPtr;
VAR key: IntegerKeyPtr;
BEGIN
NEW(key);
key.value := value;
RETURN key
END NewIntegerKey;
(** Create string key *)
PROCEDURE NewStringKey*(value: ARRAY OF CHAR): StringKeyPtr;
VAR key: StringKeyPtr;
i: INTEGER;
BEGIN
NEW(key);
(* Copy the string *)
i := 0;
WHILE (i < LEN(key.value) - 1) & (i < LEN(value)) & (value[i] # 0X) DO
key.value[i] := value[i];
INC(i)
END;
key.value[i] := 0X; (* Null terminate *)
RETURN key
END NewStringKey;
(* Hash function for integer keys *)
PROCEDURE HashInteger(key: KeyPtr; size: INTEGER): INTEGER;
VAR
intKey: IntegerKeyPtr;
hash: INTEGER;
BEGIN
intKey := key(IntegerKeyPtr);
hash := Bitwise.Xor(intKey.value, Bitwise.ShiftRight(intKey.value, 16));
hash := hash * 73;
hash := Bitwise.Xor(hash, Bitwise.ShiftRight(hash, 13));
hash := hash * 37;
hash := Bitwise.Xor(hash, Bitwise.ShiftRight(hash, 9));
IF hash < 0 THEN hash := -hash END;
RETURN hash MOD size
END HashInteger;
(* Equality function for integer keys *)
PROCEDURE EqualsInteger(key1, key2: KeyPtr): BOOLEAN;
VAR
intKey1, intKey2: IntegerKeyPtr;
result: BOOLEAN;
BEGIN
intKey1 := key1(IntegerKeyPtr);
intKey2 := key2(IntegerKeyPtr);
result := intKey1.value = intKey2.value;
RETURN result
END EqualsInteger;
(* Hash function for string keys - djb2 algorithm *)
PROCEDURE HashString(key: KeyPtr; size: INTEGER): INTEGER;
VAR
strKey: StringKeyPtr;
hash, i: INTEGER;
c: CHAR;
BEGIN
strKey := key(StringKeyPtr);
hash := 5381; (* djb2 magic number *)
i := 0;
c := strKey.value[i];
WHILE c # 0X DO
hash := hash * 33 + ORD(c);
INC(i);
c := strKey.value[i]
END;
IF hash < 0 THEN hash := -hash END;
RETURN hash MOD size
END HashString;
(* Equality function for string keys *)
PROCEDURE EqualsString(key1, key2: KeyPtr): BOOLEAN;
VAR
strKey1, strKey2: StringKeyPtr;
i: INTEGER;
result: BOOLEAN;
BEGIN
strKey1 := key1(StringKeyPtr);
strKey2 := key2(StringKeyPtr);
result := TRUE;
i := 0;
WHILE (strKey1.value[i] # 0X) & (strKey2.value[i] # 0X) & result DO
IF strKey1.value[i] # strKey2.value[i] THEN
result := FALSE
END;
INC(i)
END;
(* Check if both strings ended at the same position *)
IF result THEN
result := (strKey1.value[i] = 0X) & (strKey2.value[i] = 0X)
END;
RETURN result
END EqualsString;
(** Get integer key operations *)
PROCEDURE IntegerKeyOps*(VAR ops: KeyOps);
BEGIN
ops.hash := HashInteger;
ops.equals := EqualsInteger
END IntegerKeyOps;
(** Get string key operations *)
PROCEDURE StringKeyOps*(VAR ops: KeyOps);
BEGIN
ops.hash := HashString;
ops.equals := EqualsString
END StringKeyOps;
END CollectionKeys.