-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathArrayList.Mod
More file actions
230 lines (210 loc) · 6.32 KB
/
ArrayList.Mod
File metadata and controls
230 lines (210 loc) · 6.32 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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
(*
Copyright (c) 2025, Artemis Project
All rights reserved. 3-clause BSD license.
Module: ArrayList
Description: Chunked array list for dynamic, indexable collections built on LinkedList.
Author: Artemis Contributors
*)
MODULE ArrayList;
IMPORT Collections, LinkedList;
CONST
ChunkSize* = 64; (* Tune as needed *)
TYPE
ArrayList* = POINTER TO ArrayListDesc;
ArrayListDesc = RECORD
chunks: LinkedList.List;
count: INTEGER
END;
ChunkPtr = POINTER TO Chunk;
Chunk = RECORD (Collections.Item)
items: ARRAY ChunkSize OF Collections.ItemPtr;
count: INTEGER
END;
(** Create a new, empty ArrayList *)
PROCEDURE New*(): ArrayList;
VAR
list: ArrayList;
chunk: ChunkPtr;
result: ArrayList;
BEGIN
NEW(list);
list.chunks := LinkedList.New();
NEW(chunk);
chunk.count := 0;
LinkedList.Append(list.chunks, chunk);
list.count := 0;
result := list;
RETURN result
END New;
(** Free the ArrayList and all its chunks *)
PROCEDURE Free*(VAR list: ArrayList);
BEGIN
LinkedList.Clear(list.chunks);
LinkedList.Free(list.chunks);
list.count := 0
END Free;
(** Append an item to the end of the list. Returns TRUE if successful *)
PROCEDURE Append*(list: ArrayList; item: Collections.ItemPtr): BOOLEAN;
VAR
result: BOOLEAN;
chunk: ChunkPtr;
lastChunk: Collections.ItemPtr;
found: BOOLEAN;
chunkCount: INTEGER;
BEGIN
chunkCount := LinkedList.Count(list.chunks);
found := LinkedList.GetAt(list.chunks, chunkCount - 1, lastChunk);
chunk := lastChunk(ChunkPtr);
IF chunk.count = ChunkSize THEN
NEW(chunk);
chunk.count := 0;
LinkedList.Append(list.chunks, chunk)
END;
chunk.items[chunk.count] := item;
INC(chunk.count);
INC(list.count);
result := TRUE;
RETURN result
END Append;
(** Get the item at the given index. Returns TRUE if found, item in VAR result *)
PROCEDURE GetAt*(list: ArrayList; index: INTEGER; VAR result: Collections.ItemPtr): BOOLEAN;
VAR
found: BOOLEAN;
chunkIndex, base: INTEGER;
chunk: ChunkPtr;
chunkItem: Collections.ItemPtr;
chunkCount: INTEGER;
BEGIN
found := FALSE;
IF (index >= 0) & (index < list.count) THEN
base := 0;
chunkCount := LinkedList.Count(list.chunks);
chunkIndex := 0;
WHILE (chunkIndex < chunkCount) & ~found DO
IF LinkedList.GetAt(list.chunks, chunkIndex, chunkItem) THEN
chunk := chunkItem(ChunkPtr);
IF index < base + chunk.count THEN
result := chunk.items[index - base];
found := TRUE
ELSE
base := base + chunk.count;
INC(chunkIndex)
END
ELSE
chunkIndex := chunkCount (* Exit loop *)
END
END
END;
RETURN found
END GetAt;
(** Set the item at the given index. Returns TRUE if successful *)
PROCEDURE SetAt*(list: ArrayList; index: INTEGER; item: Collections.ItemPtr): BOOLEAN;
VAR
result: BOOLEAN;
chunkIndex, base: INTEGER;
chunk: ChunkPtr;
chunkItem: Collections.ItemPtr;
chunkCount: INTEGER;
BEGIN
result := FALSE;
IF (index >= 0) & (index < list.count) THEN
base := 0;
chunkCount := LinkedList.Count(list.chunks);
chunkIndex := 0;
WHILE (chunkIndex < chunkCount) & ~result DO
IF LinkedList.GetAt(list.chunks, chunkIndex, chunkItem) THEN
chunk := chunkItem(ChunkPtr);
IF index < base + chunk.count THEN
chunk.items[index - base] := item;
result := TRUE
ELSE
base := base + chunk.count;
INC(chunkIndex)
END
ELSE
chunkIndex := chunkCount (* Exit loop *)
END
END
END;
RETURN result
END SetAt;
(** Return the number of items in the list *)
PROCEDURE Count*(list: ArrayList): INTEGER;
VAR
result: INTEGER;
BEGIN
result := list.count;
RETURN result
END Count;
(** Returns TRUE if the list is empty *)
PROCEDURE IsEmpty*(list: ArrayList): BOOLEAN;
VAR
result: BOOLEAN;
BEGIN
result := list.count = 0;
RETURN result
END IsEmpty;
(** Remove all items from the list *)
PROCEDURE Clear*(list: ArrayList);
VAR
chunk: ChunkPtr;
BEGIN
LinkedList.Clear(list.chunks);
NEW(chunk);
chunk.count := 0;
LinkedList.Append(list.chunks, chunk);
list.count := 0
END Clear;
(** Remove the last item from the list. Returns TRUE if successful *)
PROCEDURE RemoveLast*(list: ArrayList): BOOLEAN;
VAR
result: BOOLEAN;
chunkCount: INTEGER;
lastChunk, removedChunk: Collections.ItemPtr;
chunk: ChunkPtr;
success: BOOLEAN;
BEGIN
result := FALSE;
IF list.count > 0 THEN
chunkCount := LinkedList.Count(list.chunks);
IF LinkedList.GetAt(list.chunks, chunkCount - 1, lastChunk) THEN
chunk := lastChunk(ChunkPtr);
DEC(chunk.count);
DEC(list.count);
(* If the chunk becomes empty and it's not the only chunk, remove it *)
IF (chunk.count = 0) & (chunkCount > 1) THEN
success := LinkedList.RemoveAt(list.chunks, chunkCount - 1, removedChunk);
ASSERT(success)
END;
result := TRUE
END
END;
RETURN result
END RemoveLast;
(** Iterate over all items, calling visitor for each *)
PROCEDURE Foreach*(list: ArrayList; visit: Collections.VisitProc; VAR state: Collections.VisitorState);
VAR
chunkIndex, itemIndex: INTEGER;
chunk: ChunkPtr;
chunkItem: Collections.ItemPtr;
chunkCount: INTEGER;
continueVisiting: BOOLEAN;
BEGIN
chunkCount := LinkedList.Count(list.chunks);
chunkIndex := 0;
continueVisiting := TRUE;
WHILE (chunkIndex < chunkCount) & continueVisiting DO
IF LinkedList.GetAt(list.chunks, chunkIndex, chunkItem) THEN
chunk := chunkItem(ChunkPtr);
itemIndex := 0;
WHILE (itemIndex < chunk.count) & continueVisiting DO
continueVisiting := visit(chunk.items[itemIndex], state);
INC(itemIndex)
END;
INC(chunkIndex)
ELSE
chunkIndex := chunkCount (* Exit loop *)
END
END
END Foreach;
END ArrayList.