1 // Copyright Naoki Shibata and contributors 2010 - 2020.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <inttypes.h>
12 #include <assert.h>
13
14 //
15
16 #if !(defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER))
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/file.h>
20
FLOCK(FILE * fp)21 static void FLOCK(FILE *fp) { flock(fileno(fp), LOCK_EX); }
FUNLOCK(FILE * fp)22 static void FUNLOCK(FILE *fp) { flock(fileno(fp), LOCK_UN); }
FTRUNCATE(FILE * fp,off_t z)23 static void FTRUNCATE(FILE *fp, off_t z) {
24 if (ftruncate(fileno(fp), z))
25 ;
26 }
OPENTMPFILE()27 static FILE *OPENTMPFILE() { return tmpfile(); }
CLOSETMPFILE(FILE * fp)28 static void CLOSETMPFILE(FILE *fp) { fclose(fp); }
29 #else
30 #include <Windows.h>
31 #include <io.h>
32
FLOCK(FILE * fp)33 static void FLOCK(FILE *fp) { }
FUNLOCK(FILE * fp)34 static void FUNLOCK(FILE *fp) { }
FTRUNCATE(FILE * fp,long z)35 static void FTRUNCATE(FILE *fp, long z) {
36 fseek(fp, 0, SEEK_SET);
37 SetEndOfFile((HANDLE)_get_osfhandle(_fileno(fp)));
38 }
OPENTMPFILE()39 static FILE *OPENTMPFILE() { return fopen("tmpfile.txt", "w+"); }
CLOSETMPFILE(FILE * fp)40 static void CLOSETMPFILE(FILE *fp) {
41 fclose(fp);
42 remove("tmpfile.txt");
43 }
44 #endif
45
46 //
47
48 #define MAGIC_ARRAYMAPNODE 0xf73130fa
49 #define MAGIC_ARRAYMAP 0x8693bd21
50 #define LOGNBUCKETS 8
51 #define NBUCKETS (1 << LOGNBUCKETS)
52
hash(uint64_t key)53 static int hash(uint64_t key) {
54 return (key ^ (key >> LOGNBUCKETS) ^ (key >> (LOGNBUCKETS*2)) ^ (key >> (LOGNBUCKETS*3))) & (NBUCKETS-1);
55 }
56
String_trim(char * str)57 static void String_trim(char *str) {
58 char *dst = str, *src = str, *pterm = src;
59
60 while(*src != '\0' && isspace(*src)) src++;
61
62 for(;*src != '\0';src++) {
63 *dst++ = *src;
64 if (!isspace(*src)) pterm = dst;
65 }
66
67 *pterm = '\0';
68 }
69
70 typedef struct ArrayMapNode {
71 uint32_t magic;
72 uint64_t key;
73 void *value;
74 } ArrayMapNode;
75
76 typedef struct ArrayMap {
77 uint32_t magic;
78 ArrayMapNode *array[NBUCKETS];
79 int size[NBUCKETS], capacity[NBUCKETS], totalSize;
80 } ArrayMap;
81
initArrayMap()82 ArrayMap *initArrayMap() {
83 ArrayMap *thiz = (ArrayMap *)calloc(1, sizeof(ArrayMap));
84 thiz->magic = MAGIC_ARRAYMAP;
85
86 for(int i=0;i<NBUCKETS;i++) {
87 thiz->capacity[i] = 8;
88 thiz->array[i] = (ArrayMapNode *)malloc(thiz->capacity[i] * sizeof(ArrayMapNode));
89 thiz->size[i] = 0;
90 }
91
92 thiz->totalSize = 0;
93 return thiz;
94 }
95
ArrayMap_dispose(ArrayMap * thiz)96 void ArrayMap_dispose(ArrayMap *thiz) {
97 assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
98
99 for(int j=0;j<NBUCKETS;j++) {
100 for(int i=0;i<thiz->size[j];i++) {
101 assert(thiz->array[j][i].magic == MAGIC_ARRAYMAPNODE);
102 thiz->array[j][i].magic = 0;
103 }
104 free(thiz->array[j]);
105 }
106
107 thiz->magic = 0;
108 free(thiz);
109 }
110
ArrayMap_size(ArrayMap * thiz)111 int ArrayMap_size(ArrayMap *thiz) {
112 assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
113 return thiz->totalSize;
114 }
115
ArrayMap_keyArray(ArrayMap * thiz)116 uint64_t *ArrayMap_keyArray(ArrayMap *thiz) {
117 assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
118 uint64_t *a = (uint64_t *)malloc(sizeof(uint64_t) * thiz->totalSize);
119 int p = 0;
120 for(int j=0;j<NBUCKETS;j++) {
121 for(int i=0;i<thiz->size[j];i++) {
122 assert(thiz->array[j][i].magic == MAGIC_ARRAYMAPNODE);
123 a[p++] = thiz->array[j][i].key;
124 }
125 }
126 return a;
127 }
128
ArrayMap_valueArray(ArrayMap * thiz)129 void **ArrayMap_valueArray(ArrayMap *thiz) {
130 assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
131 void **a = (void **)malloc(sizeof(void *) * thiz->totalSize);
132 int p = 0;
133 for(int j=0;j<NBUCKETS;j++) {
134 for(int i=0;i<thiz->size[j];i++) {
135 assert(thiz->array[j][i].magic == MAGIC_ARRAYMAPNODE);
136 a[p++] = thiz->array[j][i].value;
137 }
138 }
139 return a;
140 }
141
ArrayMap_remove(ArrayMap * thiz,uint64_t key)142 void *ArrayMap_remove(ArrayMap *thiz, uint64_t key) {
143 assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
144
145 int h = hash(key);
146 for(int i=0;i<thiz->size[h];i++) {
147 assert(thiz->array[h][i].magic == MAGIC_ARRAYMAPNODE);
148 if (thiz->array[h][i].key == key) {
149 void *old = thiz->array[h][i].value;
150 thiz->array[h][i].key = thiz->array[h][thiz->size[h]-1].key;
151 thiz->array[h][i].value = thiz->array[h][thiz->size[h]-1].value;
152 thiz->array[h][thiz->size[h]-1].magic = 0;
153 thiz->size[h]--;
154 thiz->totalSize--;
155 return old;
156 }
157 }
158
159 return NULL;
160 }
161
ArrayMap_put(ArrayMap * thiz,uint64_t key,void * value)162 void *ArrayMap_put(ArrayMap *thiz, uint64_t key, void *value) {
163 if (value == NULL) return ArrayMap_remove(thiz, key);
164
165 assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
166
167 int h = hash(key);
168 for(int i=0;i<thiz->size[h];i++) {
169 assert(thiz->array[h][i].magic == MAGIC_ARRAYMAPNODE);
170 if (thiz->array[h][i].key == key) {
171 void *old = thiz->array[h][i].value;
172 thiz->array[h][i].value = value;
173 return old;
174 }
175 }
176
177 if (thiz->size[h] >= thiz->capacity[h]) {
178 thiz->capacity[h] *= 2;
179 thiz->array[h] = (ArrayMapNode *)realloc(thiz->array[h], thiz->capacity[h] * sizeof(ArrayMapNode));
180 }
181
182 ArrayMapNode *n = &(thiz->array[h][thiz->size[h]++]);
183 n->magic = MAGIC_ARRAYMAPNODE;
184 n->key = key;
185 n->value = value;
186
187 thiz->totalSize++;
188
189 return NULL;
190 }
191
ArrayMap_get(ArrayMap * thiz,uint64_t key)192 void *ArrayMap_get(ArrayMap *thiz, uint64_t key) {
193 assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
194
195 int h = hash(key);
196 for(int i=0;i<thiz->size[h];i++) {
197 assert(thiz->array[h][i].magic == MAGIC_ARRAYMAPNODE);
198 if (thiz->array[h][i].key == key) {
199 return thiz->array[h][i].value;
200 }
201 }
202
203 return NULL;
204 }
205
206 #define LINELEN (1024*1024)
207
ArrayMap_load(const char * fn,const char * prefix,const char * idstr,int doLock)208 ArrayMap *ArrayMap_load(const char *fn, const char *prefix, const char *idstr, int doLock) {
209 const int idstrlen = strlen(idstr);
210 int prefixLen = strlen(prefix) + 3;
211
212 if (prefixLen >= LINELEN-10 || idstrlen >= LINELEN-10) return NULL;
213
214 FILE *fp = fopen(fn, "r");
215 if (fp == NULL) return NULL;
216
217 if (doLock) FLOCK(fp);
218
219 ArrayMap *thiz = initArrayMap();
220
221 char *prefix2 = malloc(prefixLen+10);
222 strcpy(prefix2, prefix);
223 String_trim(prefix2);
224 for(char *p = prefix2;*p != '\0';p++) {
225 if (*p == ':') *p = ';';
226 if (*p == ' ') *p = '_';
227 }
228 strcat(prefix2, " : ");
229 prefixLen = strlen(prefix2);
230
231 char *line = malloc(sizeof(char) * (LINELEN+10));
232 line[idstrlen] = '\0';
233
234 if (fread(line, sizeof(char), idstrlen, fp) != idstrlen ||
235 strcmp(idstr, line) != 0) {
236 if (doLock) FUNLOCK(fp);
237 fclose(fp);
238 free(prefix2);
239 free(line);
240 return NULL;
241 }
242
243 int found = 0;
244
245 for(;;) {
246 line[LINELEN] = '\0';
247 if (fgets(line, LINELEN, fp) == NULL) break;
248 if (strncmp(line, prefix2, prefixLen) != 0) continue;
249
250 uint64_t key;
251 char *value = malloc(sizeof(char) * LINELEN);
252
253 if (sscanf(line + prefixLen, "%" SCNx64 " : %s\n", &key, value) == 2) {
254 found = 1;
255 ArrayMap_put(thiz, (uint64_t)key, (void *)value);
256 } else {
257 free(value);
258 }
259 }
260
261 if (doLock) FUNLOCK(fp);
262 fclose(fp);
263
264 free(prefix2);
265 free(line);
266
267 return thiz;
268 }
269
ArrayMap_save(ArrayMap * thiz,const char * fn,const char * prefix,const char * idstr)270 int ArrayMap_save(ArrayMap *thiz, const char *fn, const char *prefix, const char *idstr) {
271 assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
272
273 const int idstrlen = strlen(idstr);
274 int prefixLen = strlen(prefix) + 3;
275
276 if (prefixLen >= LINELEN-10 || idstrlen >= LINELEN-10) return -1;
277
278 // Generate prefix2
279
280 char *prefix2 = malloc(prefixLen+10);
281 strcpy(prefix2, prefix);
282 String_trim(prefix2);
283 for(char *p = prefix2;*p != '\0';p++) {
284 if (*p == ':') *p = ';';
285 if (*p == ' ') *p = '_';
286 }
287 strcat(prefix2, " : ");
288 prefixLen = strlen(prefix2);
289
290 //
291
292 FILE *fp = fopen(fn, "a+");
293 if (fp == NULL) return -1;
294
295 FLOCK(fp);
296 fseek(fp, 0, SEEK_SET);
297
298 // Copy the file specified by fn to tmpfile
299
300 FILE *tmpfp = OPENTMPFILE();
301 if (tmpfp == NULL) {
302 FUNLOCK(fp);
303 fclose(fp);
304 return -1;
305 }
306
307 char *line = malloc(sizeof(char) * (LINELEN+10));
308 line[idstrlen] = '\0';
309
310 if (fread(line, sizeof(char), idstrlen, fp) == idstrlen && strcmp(idstr, line) == 0) {
311 for(;;) {
312 line[LINELEN] = '\0';
313 if (fgets(line, LINELEN, fp) == NULL) break;
314 if (strncmp(line, prefix2, prefixLen) != 0) fputs(line, tmpfp);
315 }
316 }
317
318 // Write the contents in the map into tmpfile
319
320 uint64_t *keys = ArrayMap_keyArray(thiz);
321 int s = ArrayMap_size(thiz);
322
323 for(int i=0;i<s;i++) {
324 char *value = ArrayMap_get(thiz, keys[i]);
325 if (strlen(value) + prefixLen >= LINELEN-10) continue;
326 fprintf(tmpfp, "%s %" PRIx64 " : %s\n", prefix2, keys[i], value);
327 }
328
329 free(keys);
330
331 fseek(fp, 0, SEEK_SET);
332 FTRUNCATE(fp, 0);
333 fwrite(idstr, sizeof(char), strlen(idstr), fp);
334
335 fseek(tmpfp, 0, SEEK_SET);
336
337 for(;;) {
338 size_t s = fread(line, 1, LINELEN, tmpfp);
339 if (s == 0) break;
340 fwrite(line, 1, s, fp);
341 }
342
343 FUNLOCK(fp);
344 fclose(fp);
345
346 CLOSETMPFILE(tmpfp);
347 free(prefix2);
348 free(line);
349 return 0;
350 }
351