Пример #1
0
/*.......................................................................
 * Private function used to allocate a hash-table node.
 * The caller is responsible for checking that the specified symbol
 * is unique and for installing the returned entry in the table.
 *
 * Input:
 *  hash     HashTable *  The table to allocate the node for.
 *  name    const char *  The name of the new entry.
 *  code           int    A user-supplied context code.
 *  fn  void (*)(void)    A user-supplied function pointer.
 *  data          void *  A user-supplied data pointer.
 *  del_fn  SYM_DEL_FN(*) An optional 'data' destructor function.
 * Output:
 *  return    HashNode *  The new node, or NULL on error.
 */
static HashNode *_new_HashNode(HashTable *hash, const char *name, int code,
			      void (*fn)(void), void *data, SYM_DEL_FN(*del_fn))
{
  HashNode *node;  /* The new node */
  size_t len;
/*
 * Allocate the new node from the free list.
 */
  node = (HashNode *) _new_FreeListNode(hash->mem->node_memory);
  if(!node)
    return NULL;
/*
 * Before attempting any operation that might fail, initialize the
 * contents of 'node' at least up to the point at which it can be
 * safely passed to _del_HashNode().
 */
  node->symbol.name = NULL;
  node->symbol.code = code;
  node->symbol.fn = fn;
  node->symbol.data = data;
  node->symbol.del_fn = del_fn;
  node->next = NULL;
/*
 * Allocate a copy of 'name'.
 */
  len = strlen(name) + 1;
  node->symbol.name = _new_StringMemString(hash->mem->string_memory, len);
  if(!node->symbol.name)
    return _del_HashNode(hash, node);
/*
 * If character-case is insignificant in the current table, convert the
 * name to lower case while copying it.
 */
  if(hash->case_sensitive) {
    strlcpy(node->symbol.name, name, len);
  } else {
    const char *src = name;
    char *dst = node->symbol.name;
    for( ; *src; src++,dst++)
      *dst = tolower(*src);
    *dst = '\0';
  };
  return node;
}
Пример #2
0
/*.......................................................................
 * Allocate an array of 'length' chars.
 *
 * Input:
 *  sm      StringMem *  The string free-list to allocate from.
 *  length     size_t    The length of the new string (including '\0').
 * Output:
 *  return       char *  The new string or NULL on error.
 */
char *_new_StringMemString(StringMem *sm, size_t length)
{
  char *string;   /* The string to be returned */
  int was_malloc; /* True if malloc was used to allocate the string */
/*
 * Check arguments.
 */
  if(!sm)
    return NULL;
  if(length < 1)
    length = 1;
/*
 * Allocate the new node from the free list if possible.
 */
  if(length < SM_STRLEN) {
    string = (char *)_new_FreeListNode(sm->fl);
    if(!string)
      return NULL;
    was_malloc = 0;
  } else {
    string = (char *) malloc(length+1); /* Leave room for the flag byte */
    if(!string)
      return NULL;
/*
 * Count malloc allocations.
 */
    was_malloc = 1;
    sm->nmalloc++;
  };
/*
 * Use the first byte of the string to record whether the string was
 * allocated with malloc or from the free-list. Then return the rest
 * of the string for use by the user.
 */
  string[0] = (char) was_malloc;
  return string + 1;
}
Пример #3
0
/*.......................................................................
 * Create a new hash table.
 *
 * Input:
 *  mem       HashMemory *  An optional free-list for use in allocating
 *                          HashTable containers and nodes. See explanation
 *                          in hash.h. If you are going to allocate more
 *                          than one hash table, then it will be more
 *                          efficient to allocate a single free-list for
 *                          all of them than to force each hash table
 *                          to allocate its own private free-list.
 *  size             int    The size of the hash table. Best performance
 *                          will be acheived if this is a prime number.
 *  hcase       HashCase    Specify how symbol case is considered when
 *                          looking up symbols, from:
 *                           IGNORE_CASE - Upper and lower case versions
 *                                         of a letter are treated as
 *                                         being identical.
 *                           HONOUR_CASE - Upper and lower case versions
 *                                         of a letter are treated as
 *                                         being distinct.
 *                          characters in a lookup name is significant.
 *  app_data        void *  Optional application data to be registered
 *                          to the table. This is presented to user
 *                          provided SYM_DEL_FN() symbol destructors along
 *                          with the symbol data.
 *  del_fn() HASH_DEL_FN(*) If you want app_data to be free'd when the
 *                          hash-table is destroyed, register a suitable
 *                          destructor function here.
 * Output:
 *  return HashTable *  The new hash table, or NULL on error.
 */
HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase,
			 void *app_data, HASH_DEL_FN(*del_fn))
{
  HashTable *hash;         /* The table to be returned */
  int allocate_mem = !mem; /* True if mem should be internally allocated */
  int i;
/*
 * Check arguments.
 */
  if(size <= 0) {
    errno = EINVAL;
    return NULL;
  };
/*
 * Allocate an internal free-list?
 */
  if(allocate_mem) {
    mem = _new_HashMemory(1, 100);
    if(!mem)
      return NULL;
  };
/*
 * Allocate the container.
 */
  hash = (HashTable *) _new_FreeListNode(mem->hash_memory);
  if(!hash) {
    errno = ENOMEM;
    if(allocate_mem)
      mem = _del_HashMemory(mem, 1);
    return NULL;
  };
/*
 * Before attempting any operation that might fail, initialize
 * the container at least up to the point at which it can safely
 * be passed to _del_HashTable().
 */
  hash->mem = mem;
  hash->internal_mem = allocate_mem;
  hash->case_sensitive = hcase==HONOUR_CASE;
  hash->size = size;
  hash->bucket = NULL;
  hash->keycmp = hash->case_sensitive ? _ht_strcmp : _ht_lower_strcmp;
  hash->app_data = app_data;
  hash->del_fn = del_fn;
/*
 * Allocate the array of 'size' hash buckets.
 */
  hash->bucket = (HashBucket *) malloc(sizeof(HashBucket) * size);
  if(!hash->bucket) {
    errno = ENOMEM;
    return _del_HashTable(hash);
  };
/*
 * Initialize the bucket array.
 */
  for(i=0; i<size; i++) {
    HashBucket *b = hash->bucket + i;
    b->head = NULL;
    b->count = 0;
  };
/*
 * The table is ready for use - albeit currently empty.
 */
  return hash;
}
Пример #4
0
/*.......................................................................
 * Get a new directory reader object from the cache.
 *
 * Input:
 *  ef        ExpandFile *  The pathname expansion resource object.
 *  pathname  const char *  The pathname of the directory.
 * Output:
 *  return       DirNode *  The cache entry of the new directory reader,
 *                          or NULL on error. On error, ef->errmsg will
 *                          contain a description of the error.
 */
static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname)
{
  char *errmsg = NULL;  /* An error message from a called function */
  DirNode *node;        /* The cache node used */
/*
 * Get the directory reader cache.
 */
  DirCache *cache = &ef->cache;
/*
 * Extend the cache if there are no free cache nodes.
 */
  if(!cache->next) {
    node = (DirNode *) _new_FreeListNode(cache->mem);
    if(!node) {
      snprintf(ef->errmsg, sizeof(ef->errmsg),
	       "Insufficient memory to open a new directory");
      return NULL;
    };
/*
 * Initialize the cache node.
 */
    node->next = NULL;
    node->prev = NULL;
    node->dr = NULL;
/*
 * Allocate a directory reader object.
 */
    node->dr = _new_DirReader();
    if(!node->dr) {
      snprintf(ef->errmsg, sizeof(ef->errmsg),
	       "Insufficient memory to open a new directory");
      node = (DirNode *) _del_FreeListNode(cache->mem, node);
      return NULL;
    };
/*
 * Append the node to the cache list.
 */
    node->prev = cache->tail;
    if(cache->tail)
      cache->tail->next = node;
    else
      cache->head = node;
    cache->next = cache->tail = node;
  };
/*
 * Get the first unused node, but don't remove it from the list yet.
 */
  node = cache->next;
/*
 * Attempt to open the specified directory.
 */
  if(_dr_open_dir(node->dr, pathname, &errmsg)) {
    strncpy(ef->errmsg, errmsg, ERRLEN);
    ef->errmsg[ERRLEN] = '\0';
    return NULL;
  };
/*
 * Now that we have successfully opened the specified directory,
 * remove the cache node from the list, and relink the list around it.
 */
  cache->next = node->next;
  if(node->prev)
    node->prev->next = node->next;
  else
    cache->head = node->next;
  if(node->next)
    node->next->prev = node->prev;
  else
    cache->tail = node->prev;
  node->next = node->prev = NULL;
/*
 * Return the successfully initialized cache node to the caller.
 */
  return node;
}
Пример #5
0
/*.......................................................................
 * Append an array of n characters to a character queue.
 *
 * Input:
 *  cq        GlCharQueue *  The queue to append to.
 *  chars      const char *  The array of n characters to be appended.
 *  n                 int    The number of characters in chars[].
 *  write_fn  GL_WRITE_FN *  The function to call to output characters,
 *                           or 0 to simply discard the contents of the
 *                           queue. This will be called whenever the
 *                           buffer becomes full. If it fails to release
 *                           any space, the buffer will be extended.
 *  data             void *  Anonymous data to pass to write_fn().
 * Output:
 *  return        int    The number of characters successfully
 *                       appended. This will only be < n on error.
 */
int _glq_append_chars(GlCharQueue *cq, const char *chars, int n,
		      GlWriteFn *write_fn, void *data)
{
  int ndone = 0;  /* The number of characters appended so far */
/*
 * Check the arguments.
 */
  if(!cq || !chars) {
    errno = EINVAL;
    return 0;
  };
/*
 * The appended characters may have to be split between multiple
 * buffers, so loop for each buffer.
 */
  while(ndone < n) {
    int ntodo;     /* The number of characters remaining to be appended */
    int nleft;     /* The amount of space remaining in cq->buffers.tail */
    int nnew;      /* The number of characters to append to cq->buffers.tail */
/*
 * Compute the offset at which the next character should be written
 * into the tail buffer segment.
 */
    int boff = cq->ntotal % GL_CQ_SIZE;
/*
 * Since we don't allocate a new buffer until we have at least one
 * character to write into it, if boff is 0 at this point, it means
 * that we hit the end of the tail buffer segment on the last append,
 * so we need to allocate a new one.
 *
 * If allocating this new node will require a call to malloc(), as
 * opposed to using a currently unused node in the freelist, first try
 * flushing the current contents of the buffer to the terminal. When
 * write_fn() uses blocking I/O, this stops the buffer size ever getting
 * bigger than a single buffer node. When it is non-blocking, it helps
 * to keep the amount of memory, but it isn't gauranteed to do so.
 */
    if(boff == 0 && _idle_FreeListNodes(cq->bufmem) == 0) {
      switch(_glq_flush_queue(cq, write_fn, data)) {
      case GLQ_FLUSH_DONE:
	break;
      case GLQ_FLUSH_AGAIN:
	errno = 0;          /* Don't confuse the caller */
	break;
      default:
	return ndone;       /* Error */
      };
      boff = cq->ntotal % GL_CQ_SIZE;
    };
/*
 * Since we don't allocate a new buffer until we have at least one
 * character to write into it, if boff is 0 at this point, it means
 * that we hit the end of the tail buffer segment on the last append,
 * so we need to allocate a new one.
 */
    if(boff == 0) {
/*
 * Allocate the new node.
 */
      CqCharBuff *node = (CqCharBuff *) _new_FreeListNode(cq->bufmem);
      if(!node) {
	_err_record_msg(cq->err, "Insufficient memory to buffer output.",
			END_ERR_MSG);
	return ndone;
      };
/*
 * Initialize the node.
 */
      node->next = NULL;
/*
 * Append the new node to the tail of the list.
 */
      if(cq->buffers.tail)
	cq->buffers.tail->next = node;
      else
	cq->buffers.head = node;
      cq->buffers.tail = node;
    };
/*
 * How much room is there for new characters in the current tail node?
 */
    nleft = GL_CQ_SIZE - boff;
/*
 * How many characters remain to be appended?
 */
    ntodo = n - ndone;
/*
 * How many characters should we append to the current tail node?
 */
    nnew = nleft < ntodo ? nleft : ntodo;
/*
 * Append the latest prefix of nnew characters.
 */
    memcpy(cq->buffers.tail->bytes + boff, chars + ndone, nnew);
    cq->ntotal += nnew;
    ndone += nnew;
  };
/*
 * Return the count of the number of characters successfully appended.
 */
  return ndone;
}