#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <inttypes.h>
#include <assert.h>
#if !(defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER))
#include <unistd.h>
#include <sys/types.h>
#include <sys/file.h>
static void FLOCK(FILE *fp) { flock(fileno(fp), LOCK_EX); }
static void FUNLOCK(FILE *fp) { flock(fileno(fp), LOCK_UN); }
static void FTRUNCATE(FILE *fp, off_t z) {
if (ftruncate(fileno(fp), z))
;
}
static FILE *OPENTMPFILE() { return tmpfile(); }
static void CLOSETMPFILE(FILE *fp) { fclose(fp); }
#else
#include <Windows.h>
static void FLOCK(FILE *fp) { }
static void FUNLOCK(FILE *fp) { }
static void FTRUNCATE(FILE *fp, long z) {
fseek(fp, 0, SEEK_SET);
SetEndOfFile((HANDLE)_get_osfhandle(_fileno(fp)));
}
static FILE *OPENTMPFILE() { return fopen("tmpfile.txt", "w+"); }
static void CLOSETMPFILE(FILE *fp) {
fclose(fp);
remove("tmpfile.txt");
}
#endif
#define MAGIC_ARRAYMAPNODE 0xf73130fa
#define MAGIC_ARRAYMAP 0x8693bd21
#define LOGNBUCKETS 8
#define NBUCKETS (1 << LOGNBUCKETS)
static int hash(uint64_t key) {
return (key ^ (key >> LOGNBUCKETS) ^ (key >> (LOGNBUCKETS*2)) ^ (key >> (LOGNBUCKETS*3))) & (NBUCKETS-1);
}
static void String_trim(char *str) {
char *dst = str, *src = str, *pterm = src;
while(*src != '\0' && isspace(*src)) src++;
for(;*src != '\0';src++) {
*dst++ = *src;
if (!isspace(*src)) pterm = dst;
}
*pterm = '\0';
}
typedef struct ArrayMapNode {
uint32_t magic;
uint64_t key;
void *value;
} ArrayMapNode;
typedef struct ArrayMap {
uint32_t magic;
ArrayMapNode *array[NBUCKETS];
int size[NBUCKETS], capacity[NBUCKETS], totalSize;
} ArrayMap;
ArrayMap *initArrayMap() {
ArrayMap *thiz = (ArrayMap *)calloc(1, sizeof(ArrayMap));
thiz->magic = MAGIC_ARRAYMAP;
for(int i=0;i<NBUCKETS;i++) {
thiz->capacity[i] = 8;
thiz->array[i] = (ArrayMapNode *)malloc(thiz->capacity[i] * sizeof(ArrayMapNode));
thiz->size[i] = 0;
}
thiz->totalSize = 0;
return thiz;
}
void ArrayMap_dispose(ArrayMap *thiz) {
assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
for(int j=0;j<NBUCKETS;j++) {
for(int i=0;i<thiz->size[j];i++) {
assert(thiz->array[j][i].magic == MAGIC_ARRAYMAPNODE);
thiz->array[j][i].magic = 0;
}
free(thiz->array[j]);
}
thiz->magic = 0;
free(thiz);
}
int ArrayMap_size(ArrayMap *thiz) {
assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
return thiz->totalSize;
}
uint64_t *ArrayMap_keyArray(ArrayMap *thiz) {
assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
uint64_t *a = (uint64_t *)malloc(sizeof(uint64_t) * thiz->totalSize);
int p = 0;
for(int j=0;j<NBUCKETS;j++) {
for(int i=0;i<thiz->size[j];i++) {
assert(thiz->array[j][i].magic == MAGIC_ARRAYMAPNODE);
a[p++] = thiz->array[j][i].key;
}
}
return a;
}
void **ArrayMap_valueArray(ArrayMap *thiz) {
assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
void **a = (void **)malloc(sizeof(void *) * thiz->totalSize);
int p = 0;
for(int j=0;j<NBUCKETS;j++) {
for(int i=0;i<thiz->size[j];i++) {
assert(thiz->array[j][i].magic == MAGIC_ARRAYMAPNODE);
a[p++] = thiz->array[j][i].value;
}
}
return a;
}
void *ArrayMap_remove(ArrayMap *thiz, uint64_t key) {
assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
int h = hash(key);
for(int i=0;i<thiz->size[h];i++) {
assert(thiz->array[h][i].magic == MAGIC_ARRAYMAPNODE);
if (thiz->array[h][i].key == key) {
void *old = thiz->array[h][i].value;
thiz->array[h][i].key = thiz->array[h][thiz->size[h]-1].key;
thiz->array[h][i].value = thiz->array[h][thiz->size[h]-1].value;
thiz->array[h][thiz->size[h]-1].magic = 0;
thiz->size[h]--;
thiz->totalSize--;
return old;
}
}
return NULL;
}
void *ArrayMap_put(ArrayMap *thiz, uint64_t key, void *value) {
if (value == NULL) return ArrayMap_remove(thiz, key);
assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
int h = hash(key);
for(int i=0;i<thiz->size[h];i++) {
assert(thiz->array[h][i].magic == MAGIC_ARRAYMAPNODE);
if (thiz->array[h][i].key == key) {
void *old = thiz->array[h][i].value;
thiz->array[h][i].value = value;
return old;
}
}
if (thiz->size[h] >= thiz->capacity[h]) {
thiz->capacity[h] *= 2;
thiz->array[h] = (ArrayMapNode *)realloc(thiz->array[h], thiz->capacity[h] * sizeof(ArrayMapNode));
}
ArrayMapNode *n = &(thiz->array[h][thiz->size[h]++]);
n->magic = MAGIC_ARRAYMAPNODE;
n->key = key;
n->value = value;
thiz->totalSize++;
return NULL;
}
void *ArrayMap_get(ArrayMap *thiz, uint64_t key) {
assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
int h = hash(key);
for(int i=0;i<thiz->size[h];i++) {
assert(thiz->array[h][i].magic == MAGIC_ARRAYMAPNODE);
if (thiz->array[h][i].key == key) {
return thiz->array[h][i].value;
}
}
return NULL;
}
#define LINELEN (1024*1024)
ArrayMap *ArrayMap_load(const char *fn, const char *prefix, const char *idstr, int doLock) {
const int idstrlen = strlen(idstr);
int prefixLen = strlen(prefix) + 3;
if (prefixLen >= LINELEN-10 || idstrlen >= LINELEN-10) return NULL;
FILE *fp = fopen(fn, "r");
if (fp == NULL) return NULL;
if (doLock) FLOCK(fp);
ArrayMap *thiz = initArrayMap();
char *prefix2 = malloc(prefixLen+10);
strcpy(prefix2, prefix);
String_trim(prefix2);
for(char *p = prefix2;*p != '\0';p++) {
if (*p == ':') *p = ';';
if (*p == ' ') *p = '_';
}
strcat(prefix2, " : ");
prefixLen = strlen(prefix2);
char *line = malloc(sizeof(char) * (LINELEN+10));
line[idstrlen] = '\0';
if (fread(line, sizeof(char), idstrlen, fp) != idstrlen ||
strcmp(idstr, line) != 0) {
if (doLock) FUNLOCK(fp);
fclose(fp);
free(prefix2);
free(line);
return NULL;
}
int found = 0;
for(;;) {
line[LINELEN] = '\0';
if (fgets(line, LINELEN, fp) == NULL) break;
if (strncmp(line, prefix2, prefixLen) != 0) continue;
uint64_t key;
char *value = malloc(sizeof(char) * LINELEN);
if (sscanf(line + prefixLen, "%" SCNx64 " : %s\n", &key, value) == 2) {
found = 1;
ArrayMap_put(thiz, (uint64_t)key, (void *)value);
} else {
free(value);
}
}
if (doLock) FUNLOCK(fp);
fclose(fp);
free(prefix2);
free(line);
return thiz;
}
int ArrayMap_save(ArrayMap *thiz, const char *fn, const char *prefix, const char *idstr) {
assert(thiz != NULL && thiz->magic == MAGIC_ARRAYMAP);
const int idstrlen = strlen(idstr);
int prefixLen = strlen(prefix) + 3;
if (prefixLen >= LINELEN-10 || idstrlen >= LINELEN-10) return -1;
char *prefix2 = malloc(prefixLen+10);
strcpy(prefix2, prefix);
String_trim(prefix2);
for(char *p = prefix2;*p != '\0';p++) {
if (*p == ':') *p = ';';
if (*p == ' ') *p = '_';
}
strcat(prefix2, " : ");
prefixLen = strlen(prefix2);
FILE *fp = fopen(fn, "a+");
if (fp == NULL) return -1;
FLOCK(fp);
fseek(fp, 0, SEEK_SET);
FILE *tmpfp = OPENTMPFILE();
if (tmpfp == NULL) {
FUNLOCK(fp);
fclose(fp);
return -1;
}
char *line = malloc(sizeof(char) * (LINELEN+10));
line[idstrlen] = '\0';
if (fread(line, sizeof(char), idstrlen, fp) == idstrlen && strcmp(idstr, line) == 0) {
for(;;) {
line[LINELEN] = '\0';
if (fgets(line, LINELEN, fp) == NULL) break;
if (strncmp(line, prefix2, prefixLen) != 0) fputs(line, tmpfp);
}
}
uint64_t *keys = ArrayMap_keyArray(thiz);
int s = ArrayMap_size(thiz);
for(int i=0;i<s;i++) {
char *value = ArrayMap_get(thiz, keys[i]);
if (strlen(value) + prefixLen >= LINELEN-10) continue;
fprintf(tmpfp, "%s %" PRIx64 " : %s\n", prefix2, keys[i], value);
}
free(keys);
fseek(fp, 0, SEEK_SET);
FTRUNCATE(fp, 0);
fwrite(idstr, sizeof(char), strlen(idstr), fp);
fseek(tmpfp, 0, SEEK_SET);
for(;;) {
size_t s = fread(line, 1, LINELEN, tmpfp);
if (s == 0) break;
fwrite(line, 1, s, fp);
}
FUNLOCK(fp);
fclose(fp);
CLOSETMPFILE(tmpfp);
free(prefix2);
free(line);
return 0;
}