#include #include #include #include "common.h" #include "debug.h" #include "utils.h" #include "string.h" #include "function.h" #include "collection.h" #include "array.h" #include "set.h" #include "dictionary.h" #include "qidgenerator.h" enum { DEBUG_QIDGENERATOR = false }; typedef struct RefDir { uint count; Dir *d; } RefDir; RefDir *refdir_new(Dir *d) { RefDir *result; assert_valid(d); result = (RefDir *)emallocz_fs(sizeof(*result)); result->count = 1; result->d = d; return result; } void refdir_free(RefDir *self) { if(self == nil) { return; } dir_free(self->d); free(self); } void refdir_ref(RefDir *self) { ++(self->count); } uint refdir_deref(RefDir *self) { assert_valid(self); assert(self->count > 0); --(self->count); if(self->count == 0) { refdir_free(self); return 0; } return self->count; } void refdir_void_deref(RefDir *self) { assert_valid(self); refdir_deref(self); } typedef struct RefString { uint count; char *str; } RefString; void refstring_init(RefString *self, char *str) { assert_valid(self); assert_valid(str); self->count = 1; self->str = str; } RefString *refstring_new(char *str) { RefString *result; assert_valid(str); result = (RefString *)emallocz_fs(sizeof(RefString)); refstring_init(result, str); return result; } void refstring_free(RefString *self) { if(self == nil) { return; } free(self->str); free(self); } void refstring_ref(RefString *self) { ++(self->count); } uint refstring_deref(RefString *self) { assert_valid(self); assert(self->count > 0); --(self->count); if(self->count == 0) { refstring_free(self); return 0; } return self->count; } void refstring_void_deref(RefString *self) { assert_valid(self); refstring_deref(self); } bool refstring_isequal(void *p1, void *p2) { RefString *self = (RefString *)p1; RefString *r = (RefString *)p2; assert_valid(self); assert_valid(r); return string_isequal(self->str, r->str); } uint refstring_hash(void *p) { RefString *self = (RefString *)p; assert_valid(self); return string_hash(self->str); } static bool qidpath_isequal(void *p1, void *p2) { uvlong *self = (uvlong *)p1; uvlong *v = (uvlong *)p2; assert_valid(self); assert_valid(v); return *self == *v; } static uint qidpath_hash(void *p) { uvlong *self = (uvlong *)p; uint result; assert_valid(self); NOISE(DEBUG_QIDGENERATOR, "qidpath hash upper half: %ulld", *self >> (sizeof(uint) * 8)); result = (*self >> (sizeof(uint) * 8)) ^ (*self); NOISE(DEBUG_QIDGENERATOR, "qidpath hash %ulld to %ud", *self, result); return result; } static bool dir_isequal(void *p1, void *p2) { Dir *self = (Dir *)p1; Dir *d = (Dir *)p2; assert_valid(self); assert_valid(d); return (self->qid.path == d->qid.path) && (self->qid.type == d->qid.type) && (self->dev == d->dev) && (self->type == d->type); } static uint dir_hash(void *p) { uint result; Dir *self = (Dir *)p; assert_valid(self); result = self->type + self->dev + self->qid.type + qidpath_hash(&(self->qid.path)); NOISE(DEBUG_QIDGENERATOR, "dir_hash result: %ud", result); return result; } static bool dirs_areequal(void *p1, void *p2) { int i; Array *a1 = (Array *)p1; Array *a2 = (Array *)p2; assert_valid(a1); assert_valid(a2); if(array_size(a1) != array_size(a2)) { return false; } for(i = 0; i < array_size(a1); ++i) { if(!dir_isequal(array_at(a1, i), array_at(a2, i))) { return false; } } return true; } static collection_do_ret dirs_hash_each(void *p, void *arg) { Dir *d = (Dir *)p; uint *hash = (uint *)arg; assert_valid(d); assert_valid(hash); *hash += dir_hash(d); return COLLECTION_DO_CONTINUE; } static uint dirs_hash(void *p) { uint result = 0; Array *self = (Array *)p; assert_valid(self); array_do(self, dirs_hash_each, &result); NOISE(DEBUG_QIDGENERATOR, "dirs_hash result: %ud", result); return result; } struct QidGenerator { Set *qidpaths; Dictionary *dirs2refdir; Dictionary *name2dirs; uvlong probebits; }; QidGenerator *qidgenerator_new(void) { QidGenerator *result = (QidGenerator *)malloc(sizeof(QidGenerator)); result->qidpaths = set_new(qidpath_isequal, qidpath_hash); result->dirs2refdir = dictionary_new(dirs_areequal, dirs_hash); result->name2dirs = dictionary_new(refstring_isequal, refstring_hash); result->probebits = 0; return result; } static void dirs_free(Array *self) { if(self == nil) { return; } array_free_with(self, (functionunary)dir_free); } void qidgenerator_free(QidGenerator *self) { if(self == nil) { return; } dictionary_free_with(self->dirs2refdir, (functionunary)dirs_free, (functionunary)refdir_void_deref); dictionary_free_with(self->name2dirs, (functionunary)refstring_void_deref, nil); set_free(self->qidpaths); free(self); } /** @todo keep a counter to make sure we never end in infinite loop */ static void qidgenerator_new_qid(QidGenerator *self, Array *dirs, Dir *d) { enum { PROBED_BYTES = 2, UNPROBED_BYTES = (sizeof(uvlong) - PROBED_BYTES)}; uvlong *path; assert_valid(self); assert_valid(dirs); assert_valid(d); path = &(d->qid.path); while(set_includes(self->qidpaths, path)) { INFO(DEBUG_QIDGENERATOR, "qidgnerator_new_qid detects collision with %ulld", *path); ++(self->probebits); if(self->probebits >= (1 << (PROBED_BYTES * 8))) { INFO(DEBUG_QIDGENERATOR, "qidgenerator_new_qid probebits wrap around"); self->probebits = 1; } *path |= self->probebits << (UNPROBED_BYTES * 8); INFO(DEBUG_QIDGENERATOR, "qidgenerator_new_qid generating new path: %ulld", *path); } INFO(DEBUG_QIDGENERATOR, "qidgenerator_new_qid adding path: %ulld", *path); set_add(self->qidpaths, path); } static void dirs_deref(Array *self, QidGenerator *qidgen) { bool result; RefDir *ref; uvlong path; assert_valid(self); assert_valid(qidgen); result = dictionary_get(qidgen->dirs2refdir, self, &ref); assert(result); path = ref->d->qid.path; if(refdir_deref(ref) == 0) { dictionary_remove(qidgen->dirs2refdir, self); dirs_free(self); set_remove(qidgen->qidpaths, &path); } } static char *dir_updatename(char *self, char *new) { assert_valid(self); assert_valid(new); if(strcmp(self, new) == 0) { return self; } if(strlen(self) > strlen(new)) { return strcpy(self, new); } free(self); return estrdup_fs(new); } static bool dir_update(Dir *self, Dir *new) { bool ischanged; char *name; char *uid; char *gid; char *muid; assert_valid(self); assert_valid(new); assert(dir_isequal(self, new)); ischanged = (self->qid.vers != new->qid.vers) || (self->mode != new->mode) || // (self->atime != new->atime) || (self->mtime != new->mtime) || (self->length != new->length); name = self->name; uid = self->uid; gid = self->gid; muid = self->muid; memcpy(self, new, sizeof(Dir)); self->name = dir_updatename(name, new->name); self->uid = dir_updatename(uid, new->uid); self->gid = dir_updatename(gid, new->gid); self->muid = dir_updatename(muid, new->muid); ischanged = ischanged || (name != self->name) || (uid != self->uid) || (gid != self->gid) || (muid != self->muid); return ischanged; } /* @todo get the latest time */ static void qidgenerator_update_dir(Array *dirs, Dir *d, bool ischanged) { Dir *source; assert_valid(dirs); assert_valid(d); assert(array_size(dirs) > 0); source = array_at(dirs, 0); if(ischanged) { ++(d->qid.vers); } d->mode = source->mode; d->atime = source->atime; d->mtime = source->mtime; d->length = source->length; d->name = dir_updatename(d->name, source->name); d->uid = dir_updatename(d->uid, source->uid); d->gid = dir_updatename(d->gid, source->gid); d->muid = dir_updatename(d->muid, source->muid); } static Dir *qidgenerator_new_dir(QidGenerator *self, Array *dirs) { Dir *result; assert_valid(self); assert_valid(dirs); assert(array_size(dirs) > 0); result = (Dir *)emallocz_fs(sizeof(*result)); dir_copy((Dir *)array_at(dirs, 0), result); qidgenerator_update_dir(dirs, result, false); /** @todo need to get the latest time */ return result; } /** @todo need to check any changes and update refresult's vers accordingly */ static void dirs_update(Array *self, Array *new, Dictionary *dirs2refdir) { bool result; bool ischanged = false; int i; RefDir *ref = nil; assert_valid(self); assert_valid(new); assert_valid(dirs2refdir); assert(array_size(self) == array_size(new)); for(i = 0; i < array_size(self); ++i) { ischanged = ischanged || dir_update(array_at(self, i), array_at(new, i)); } result = dictionary_get(dirs2refdir, self, (void **)&ref); assert(result); assert_valid(ref); qidgenerator_update_dir(self, ref->d, ischanged); } static collection_do_ret dirs_copy_each(void *p, void *arg) { Dir *d; assert_valid(p); assert_valid(arg); d = (Dir *)emallocz_fs(sizeof(*d)); dir_copy((Dir *)p, d); array_add((Array *)arg, d); return COLLECTION_DO_CONTINUE; } static Array *dirs_copy(Array *self) { Array *result; assert_valid(self); result = array_new_size(array_size(self)); array_do(self, dirs_copy_each, result); return result; } RefDir *qidgenerator_add_dirs( QidGenerator *self, RefString *reffilename, Array *dirs) { RefDir *result; Array *newdirs; assert_valid(self); assert_valid(reffilename); assert_valid(dirs); NOISE(DEBUG_QIDGENERATOR, "entering qidgenerator_add_dirs"); result = refdir_new(qidgenerator_new_dir(self, dirs)); NOISE(DEBUG_QIDGENERATOR, "qidgenerator_add_dirs generating qid"); qidgenerator_new_qid(self, dirs, result->d); NOISE(DEBUG_QIDGENERATOR, "qidgenerator_add_dirs copying dirs"); newdirs = dirs_copy(dirs); NOISE(DEBUG_QIDGENERATOR, "qidgenerator_add_dirs updating dirs2refdir"); dictionary_add(self->dirs2refdir, newdirs, result); NOISE(DEBUG_QIDGENERATOR, "qidgenerator_add_dirs updating name2dirs"); dictionary_add(self->name2dirs, reffilename, newdirs); NOISE(DEBUG_QIDGENERATOR, "leaving qidgenerator_add_dirs with result: %uX", result); return result; } Dir *qidgenerator_get(QidGenerator *self, char *filename, Array *dirs) { Array *d = nil; Array *old = dirs; RefDir *refresult = nil; RefString refstring; assert_valid(self); assert_valid(filename); assert_valid(dirs); assert(array_size(dirs) > 0); refstring_init(&refstring, filename); NOISE(DEBUG_QIDGENERATOR, "entering qidgenerator_get with filename: %s", filename); if(dictionary_get(self->name2dirs, &refstring, &d)) { /* if dirs == d, then update dirs * else * * if found dirs in dirs2refdir, * increase ref, update filename -> dirs , and update dirs * else * free d=>refdir * create new qid */ NOISE(DEBUG_QIDGENERATOR, "qidgenerator_get got %s Dir", filename); if(dirs_areequal(d, dirs)) { NOISE(DEBUG_QIDGENERATOR, "qidgenerator_get dirs equal"); dirs_update(d, dirs, self->dirs2refdir); dictionary_get(self->dirs2refdir, d, (void **)&refresult); } else { if(dictionary_get_key_value(self->dirs2refdir, &old, (void **)&refresult)) { NOISE(DEBUG_QIDGENERATOR, "qidgenerator_get dirs exists from others"); refdir_ref(refresult); dirs_update(old, dirs, self->dirs2refdir); dictionary_add(self->name2dirs, &refstring, old); } else { NOISE(DEBUG_QIDGENERATOR, "qidgenerator_get dirs does not exist"); refresult = qidgenerator_add_dirs(self, &refstring, dirs); } NOISE(DEBUG_QIDGENERATOR, "qidgenerator_get dereffing old dirs"); dirs_deref(d, self); } } else if(dictionary_get_key_value( self->dirs2refdir, &old, (void **)&refresult)) { /* increase ref, and add filename -> dirs */ NOISE(DEBUG_QIDGENERATOR, "qidgenerator_get new mapping dirs exists from others"); refdir_ref(refresult); dirs_update(old, dirs, self->dirs2refdir); dictionary_add(self->name2dirs, refstring_new(estrdup_fs(filename)), old); } else { NOISE(DEBUG_QIDGENERATOR, "qidgenerator_get new mapping generating"); refresult = qidgenerator_add_dirs(self, refstring_new(estrdup_fs(filename)), dirs); } NOISE(DEBUG_QIDGENERATOR, "qidgenerator_get about to return"); return dir_clone(refresult->d); } void qidgenerator_file_ref(QidGenerator *self, char *filename) { bool result; RefString refstring; RefString *key = &refstring; assert_valid(self); assert_valid(filename); NOISE(DEBUG_QIDGENERATOR, "entering qidgenerator_file_ref with: %s", filename); refstring_init(key, filename); result = dictionary_get_key_value(self->name2dirs, (void **)&key, nil); NOISE(DEBUG_QIDGENERATOR, "qidgenerator_file_ref finding name result: %d", result); assert(result); refstring_ref(key); } void qidgenerator_file_deref(QidGenerator *self, char *filename) { bool result; RefString refstring; RefString *key = &refstring; Array *value = nil; assert_valid(self); assert_valid(filename); NOISE(DEBUG_QIDGENERATOR, "entering qidgenerator_file_deref with: %s", filename); refstring_init(key, filename); result = dictionary_get_key_value( self->name2dirs, (void **)&key, (void **)&value); NOISE(DEBUG_QIDGENERATOR, "qidgenerator_file_deref finding name result: %d", result); assert(result); if(refstring_deref(key) == 0) { NOISE(DEBUG_QIDGENERATOR, "qidgenerator_file_deref deleting"); key = &refstring; result = dictionary_remove(self->name2dirs, key); assert(result); dirs_deref(value, self); } }