/
libDict.c
336 lines (267 loc) · 7.43 KB
/
libDict.c
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libDict.h"
#define DEBUG
#define DEBUG_LEVEL 3
#ifdef DEBUG
# define DEBUG_PRINT(x) printf x
#else
# define DEBUG_PRINT(x) do {} while (0)
#endif
#define DICT_INIT_ROWS 1024
#define DICT_GROW_FACTOR 2
#define ROW_INIT_ENTRIES 8
#define ROW_GROW_FACTOR 2
#define PRIME1 77933 // a large prime
#define PRIME2 119557 // a large prime
/**
* hash *c as a sequence of bytes mod m
*/
int dictHash(char *c, int m){
int sum=0;
while(*c!='\0'){
int num = *c;
sum+= PRIME1*num+PRIME2*sum;
c++;
}
if(sum<0)sum=-sum;
sum = sum%m;
return sum;
}
/**
* Print the dictionary,
* level==0, dict header
* level==1, dict header, rows headers
* level==2, dict header, rows headers, and keys
*/
void dictPrint(Dict *d, int level){
if(d==NULL){
printf("\tDict==NULL\n");
return;
}
printf("Dict\n");
printf("\tnumRows=%d\n",d->numRows);
if(level<1)return;
for(int i=0;i<d->numRows;i++){
printf("\tDictRow[%d]: numEntries=%d capacity=%d keys=[", i, d->rows[i].numEntries, d->rows[i].capacity);
if(level>=2){
for(int j=0;j<d->rows[i].numEntries;j++){
printf("%s, ",d->rows[i].entries[j].key);
}
}
printf("]\n");
}
}
/**
* Return the DictEntry for the given key, NULL if not found.
* This is so we can store NULL as a value.
*/
DictEntry *dictGet(Dict *d, char *key){
// find row
int h = dictHash(key, d->numRows);
DictRow *r = &d->rows[h];
// use binary search if the number of entries
int left = 0, right = r->numEntries - 1, mid;
while (left <= right) {
mid = (left + right) / 2;
int cmp = strncmp(r->entries[mid].key, key, 1023);
if (cmp == 0) {
return &r->entries[mid];
} else if (cmp < 0) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return NULL;
}
/**
* Delete key from dict if its found in the dictionary
* Returns 1 if found and deleted
* Returns 0 otherwise
*/
int dictDel(Dict *d, char *key){
if (d == NULL) {
return 0;
}
if (key == NULL ) {
return 0;
}
int index = dictHash(key, d->numRows);
// find row
DictRow *r = &d->rows[index];
#ifdef DEBUG
printf("dictDel(d,%s) hash=%d\n",key, index);
dictPrint(d,DEBUG_LEVEL);
#endif
// find key in row
for (int i = 0; i < r->numEntries; i++) {
if (strncmp(r->entries[i].key, key, 1023) == 0 ) {
if (r->entries[i].key != NULL) {
free(r->entries[i].key );
r->entries[i].key = NULL;
r->entries[i].value = NULL;
}
r->numEntries--;
for (int j = i; j < r->numEntries; j++) {
r->entries[j] = r->entries[j+1];
}
return 1;
}
}
#ifdef DEBUG
dictPrint(d,DEBUG_LEVEL);
#endif
return 0;
}
/**
* put (key, value) in Dict
* return 1 for success and 0 for failure
*/
/**
* put (key, value) in Dict
* return 1 for success and 0 for failure
*/
int dictPut(Dict *d, char *key, void *value) {
int h = dictHash(key, d->numRows);
#ifdef DEBUG
printf("dictPut(d,%s) hash=%d\n", key, h);
dictPrint(d, DEBUG_LEVEL);
#endif
// If key is already here, just replace value
DictEntry *exitingenty = dictGet(d, key);
if (exitingenty != NULL ) {
free( exitingenty-> value );
exitingenty->value = value;
return 1;
}
#ifdef DEBUG
dictPrint(d, DEBUG_LEVEL);
#endif
/**
* else we need to place (key,value) as a new entry in this row
* if there is no space, expand the row
*/
DictRow *r = &d->rows[h];
if (r->numEntries == r->capacity) {
int cap = ROW_GROW_FACTOR * r->capacity;
DictEntry *nwenty = realloc(r->entries, cap * sizeof(DictEntry));
if (nwenty == NULL ) {
return 0;
}
r->capacity = cap;
r->entries = nwenty;
}
// find the infex to insert (key, value) based on the order of
int i;
if ( r->numEntries > 0 ) {
if (r->numEntries >= 20) {
// if bigger then 20, do binary search
int left = 0, right = r->numEntries - 1, mid;
while ( left <= right) {
mid = (left + right) / 2;
int cmp = strcmp( r->entries[mid].key, key);
if (cmp == 0) {
return 0;
} else if (cmp < 0) {
left = mid + 1;
} else {
right = mid - 1;
}
}
i = left;
} else {
// linear seach
for (i = 0; i < r->numEntries; i++ ) {
int cmp = strncmp(r->entries[i].key, key, 1024 );
if (cmp == 0 ) {
return 0;
} else if ( cmp > 0) {
break;
}
}
}
} else {
i = 0;
}
// shift existing entries to make space for new entry
for (int j = r->numEntries; j > i; j--) {
r->entries[j] = r->entries [j - 1];
}
// insert new entry
r->entries[i].key = malloc(strlen(key) + 1);
if (r->entries[i].key == NULL) {
// memory allocation error
return 0;
}
strcpy(r->entries[i].key, key);
r->entries[i].value = value;
r->numEntries++;
#ifdef DEBUG
dictPrint(d, DEBUG_LEVEL);
#endif
return 1;
}
/**
* free all resources allocated for this Dict. Everything, and only those things
* allocated by this code should be freed.
*/
void dictFree(Dict *d){
// go through each row
for(int i = 0; i < d->numRows; i++ ) {
DictRow *r = &d->rows[i];
for(int j = 0; j < r->numEntries; j++) {
DictEntry *entry = &r->entries[j];
free(entry->key);
free(entry->value);
}
// free the row's entries and the row itself
free(r->entries);
//row->entries = NULL;
}
// free the rows array and the dictionary itself
free(d->rows);
free(d);
}
/**
* Allocate and initialize a new Dict. Initially this dictionary will have initRows
* hash slots. If initRows==0, then it defaults to DICT_INIT_ROWS
* Returns the address of the new Dict on success
* Returns NULL on failure
*/
Dict * dictNew(int initRows){
// Allocate memory for the new dictionary
Dict *d = malloc(sizeof(Dict));
if (d == NULL) {
return NULL; // Failed to allocate memory
}
// Initialize the number of rows
if (initRows <= 0) {
initRows = DICT_INIT_ROWS; // Default number of rows
}
d->numRows = initRows;
// Allocate memory for the rows
d->rows = malloc(initRows * sizeof(DictRow));
if (d->rows == NULL) {
free(d); // Free previously allocated memory
return NULL; // Failed to allocate memory
}
// Initialize each row
for (int i = 0; i < initRows; i++) {
d->rows[i].numEntries = 0;
d->rows[i].capacity = ROW_INIT_ENTRIES;
d->rows[i].entries = malloc(d->rows[i].capacity * sizeof(DictEntry));
if (d->rows[i].entries == NULL) {
// Failed to allocate memory for the entries
// Free previously allocated memory
for (int j = 0; j < i; j++) {
free(d->rows[j].entries);
}
free(d->rows);
free(d);
return NULL; // Failed to allocate memory
}
}
return d;
}