Plan 9 from Bell Labs’s /usr/web/sources/contrib/de0u/root/sys/src/cmd/divergefs/qidgenerator.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.



#include <u.h>
#include <libc.h>
#include <fcall.h>
#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);
  }
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.