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