开发者

INI library: linked list (abstract) issue

I'm working on my own - simple, short, stupid - ini library that I will use for other projects. I coded an ini parser, pretty simple, and effective so far and now it's time to move to the harder part - how, and where to store parsed data from an ini file.

I thought of using simple linked list (maybe double linked list) but I can't really picturise it in my head.

struct keys
{
 char *keyname;
 char *keyval;
 unsigned long hashkey;
 struct keys *next;
 struct keys *prev;
}

struct ini
{
 char *section;
 struct keys *okeys;
 unsigned long hashkey;
 struct ini *next;
 struct ini *prev;
}

First, I thought to code it this way, but I kinda looks stupid, and not effective enough, I know there is better way to store sections, keys and key values in a linked list but I can't think of any. Or, maybe I should try using some other data structure? I donno, doing an ini library this way would require too many functions like - a separate function for adding sections, and the one for adding keys, etc. I'm really trying to figure out what to use and I'm stuck.

Here's the code of my simple ini parser:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#define BUFF_SIZE 1024
#define INI_FILE "config.ini"
#define ERROR_FILE "error.log"

void load_ini(const char *name);
void log_it(const char *filename, const char *format, ...);
void trim(char *str);
unsigned int hash(const char *key);

int main(int argc, char *argv[])
{
 load_ini(INI_FILE);
 return 0;
}

/*2. load ini function */
void load_ini(const char *name)
{
 char *line = NULL;
 char *p = NULL;
 FILE *ifile = NULL;

 ifile = fopen(name, "r");
 if(ifile == NULL)
 {
  log_it(ERROR_FILE, "(load_ini) can't open %s file", name);
  return;
 }

 line = malloc(BUFF_SIZE * sizeof(char) + 1);
 if(line == NULL)
 {
  log_it(ERROR_FILE, "(load_ini) malloc fail");
  return;
 }

 while(fgets(line, BUFF_SIZE+1, ifile) != NULL)
 {
  trim(line);

  if(strlen(line) < 3 || *line == ';' || *line == '#')
  {
   continue;
  }

  if(*line == '[')
  {
   if(p = strchr(line, ']'))
   {
    *p = '\0';
    trim(line+1);
    /* add section here */
   }
  }
  else if(p = strchr(line, '='))
  {
   *p = '\0';
   trim(line);
   trim(p+1);
   /* add key, and key value here */
  }
 }
 free(line);
 fclose(ifile);
}

/* 3. multifunctional log function with variable number of arg开发者_JS百科uments */
void log_it(const char *filename, const char *format, ...)
{
 va_list arglist;
 FILE *fp = NULL; 

 fp = fopen(filename,"a");
 if(fp == NULL)
 {
  /* :) */
  return;
 }

 va_start(arglist, format);
 vfprintf (fp, format, arglist);
 va_end (arglist);
 fclose (fp);
}

/* 4. trim function */

void trim(char *str)
{
 char *start = str;
 char *end = start + strlen(str);

 while (--end >= start) 
 { 
  if (!isspace(*end))
  {
   break;
  }
 }

 *(++end) = '\0';

 while (isspace(*start)) 
 {
  start++;
 }

 if (start != str)
 {
  memmove(str, start, end - start + 1);
 }
}

/* Bernstein hash */
unsigned int hash(const char *key) 
{
 unsigned int hash = 5381;
 unsigned int i = 0;
 int len = strlen(key);

 for(i; i < len; ++i)
 {
  hash = 33 * hash + key[i];
 }
 return hash ^ (hash >> 16);
}


I would keep the sections in a linked list. Chances are there are not nearly enough sections in the average ini file to warrant setting up a hash table. Alternatively, you can use a binary search tree that requires minimal effort.

Another approach is to use one hash table and prefix each value with the section name and a separation token. The drawback to this approach is that you cannot use the separation token in the key names and section names. If you control the ini format, then this is no problem. If this is a general ini parser, it will be.

For either approach you need to come up with a section name for the section-less i.e. global section.

While binary search trees and hash tables are faster lookups then linked lists, you need to consider whether it's worth it to your application. If your application only reads the ini file once and the values set states within the application that do not queried anymore during it's lifetime, lookup speed is not important. However, if the application constantly queries these values, then hash tables are preferred. If certain values are more likely to be queried during the application lifetime, splay trees may work favorably. Then again if accessing configuration values is a significant and measurable part of the application, then maybe it's time to split off some functionality.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜