<file_sep>/**
 * @file   lasttest.c
 * @author blackball <<EMAIL>>
 * @date   Mon Oct 17 13:13:01 2011
 * 
 * @brief  test lastdbio
 * 
 * 
 */

#include "lastdbio.h"
#include <string.h>
#include <stdio.h>

void pack_double(struct db *database, double d[], int n)
{
  int len = sizeof(double) * n;
  struct db_node *node = new_node(len);
  memcpy(node->data, d, len);
  dump(database, node);
}

// more consideration on unpack 
int unpack_double(struct db *database, double d[], int n, int idx)
{
	int code = -1;
	int len = sizeof(double) * n;

	if (idx > -1)
	{
		struct db_node *node = undump(database, idx);
		if (node != NULL)
		{
			memcpy(d, node->data, len);
			code = 0;
		}
	}

	return code;
}

int main2(int argc, char *argv[])
{

  int magic = 123;
  struct db *database = createdb(magic, "lastest.d", sizeof(double)*5);
  double d[5] = {2,3,4,5,1};
  double s[5] = {1,2,3,4,5};
  pack_double(database, d, 5);
  pack_double(database, s, 5);

  writedb(database);
  
  return 0;
}

int main(int argc ,char *argv[])
{

  struct db * database = opendb("lastest.d");
  double d[5];
  if(unpack_double(database, d, 5, 1))
  {
	  printf("invalid index!\n");
  }
  else
  for(int i = 0; i < 5; ++i)
	  printf("%lf\n", d[i]);

  closedb(database);

  getchar();
  return 0;
}
<file_sep>/**
 * @file   _dbio.h
 * @author blackball <<EMAIL>>
 * @date   Mon Oct 17 10:51:06 2011
 * 
 * @brief  binary data io
 * 
 */

#ifndef _MX_BINARY_IO_H_INCLUDED_
#define _MX_BINARY_IO_H_INCLUDED_

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

struct db_header
{
  int magic;     // file identity
  int num;       // number of nodes 
  int len;       // length of one node(fixed)
  int reserved;  //reserved flag
};

struct db_node
{
  int len;
  struct db_node *next;
  void* data;
};
struct db_node* new_node(int len);
void free_node(struct db_node *n);


#define DB_NAME_LEN 64
struct db
{
  struct db_header hdr;   // header of db
  char fname[DB_NAME_LEN];// file name of db
  struct db_node *head;   // head of list
  struct db_node *tail;   // tail of list
  struct db_node *curr;   // current pointer,
                          // only used for indexing
};

struct db* createdb(int magic, const char *fname,
                    int fixed_len);

void writedb(struct db* d);
void fast_writedb(struct db *d);

struct db* opendb(const char *fname);
struct db* fast_opendb(const char *fname);

void closedb(struct db* d);
void fast_closedb(struct db* d);

void dump(struct db* d, struct db_node *n);
struct db_node* undump(struct db *d, int idx);


#ifdef __cplusplus
}
#endif

#endif
<file_sep>/**
 * @file   example.cpp
 * @author blackball <<EMAIL>>
 * @date   Mon Oct 17 18:17:04 2011
 * 
 * @brief  test dbio
 * 
 */

#include <stdio.h>
#include <windows.h>
#include "dbio.h"
#include "galleryio.h"

int test_pack(void)
{
  int arr[10] = {1,2,3,4,5,4,3,2,1,0};
  int len = sizeof(int) * 10;
  
  struct db *dbase = createdb(123, "data.db", len);
  if ( dbase == NULL )
  {
    fprintf(stderr, "unknow error!\n");
    return 0;
  }
  
  for(int i = 0; i < 1000000; ++i)
    if (0 != pack_arr(dbase, arr, len))
    {
      fprintf(stderr, "unknow error!\n");
      return 0;
    }
  
  writedb(dbase);
  return 0;
}

int test_fast_unpack(void)
{
  int arr[10];
  int len = sizeof(int)*10;
  struct db *dbase = fast_opendb("data.db");
  if (dbase == NULL)
  {
    fprintf(stderr, "can not open database!\n");
    return 0;
  }

  if (0 != unpack_arr(dbase, arr, len, -1))
  {
    fprintf(stderr, "unknow error!\n");
  }
  
  for(int i = 0; i < 10; ++i)
    printf("%d ", arr[i]);

  fast_closedb(dbase);
  return 0;
}

int test_unpack(void)
{
  int arr[10];
  int len = sizeof(int)*10;
  struct db *dbase = opendb("data.db");
  if (dbase == NULL)
  {
    fprintf(stderr, "can not open database!\n");
    return 0;
  }

  if (0 != unpack_arr(dbase, arr, len, 1000))
  {
    fprintf(stderr, "unknow error!\n");
  }
  
  for(int i = 0; i < 10; ++i)
    printf("%d ", arr[i]);

  closedb(dbase);
  return 0;
}

void test_gallery()
{
  // Create a item
  GItem *items[5];
  int j;
  for (j = 0; j < 5; ++j)
    items[j] = new_gitem(4800);
	
  int i = 0;
  for (j = 0; j < 5; ++j)
    for (i = 0; i < 4800; ++i)
    {
      items[j]->feature[i] = i + j;
    }
  items[0]->imgID = 0;
  items[0]->label = 0;
  items[1]->imgID = 1;
  items[1]->label = 0;
  items[2]->imgID = 2;
  items[2]->label = 0;
  items[3]->imgID = 3;
  items[3]->label = 1;
  items[4]->imgID = 4;
  items[4]->label = 1;

  // Create a person
  Person *person0 = new_person(3);
  person0->label = 0;
  person0->items[0] = items[0];
  person0->items[1] = items[1];
  person0->items[2] = items[2];

  Person *person1 = new_person(2);
  person1->label = 1;
  person1->items[0] = items[3];
  person1->items[1] = items[4];


  // create a gallery
  Gallery *gallery = new_gallery(2);
  gallery->persons[0] = person0;
  gallery->persons[1] = person1;

  // create a database 
  // in fact, its the content length of GItem
  int len = sizeof(int)*2 + sizeof(double)*4800;
  struct db* gdb = createdb(123, "gallery.db", len);
  if (!gdb)
  {
    fprintf(stderr, "can not create databse!\n");
    return ;
  }

  create_gallerydb(gdb, gallery);
  writedb(gdb);
  free_gallery(gallery);
}

void test_load_gallery()
{
  struct db* gdb = fast_opendb("gallery.db");
  if (!gdb)
  {
    fprintf(stderr, "can not open gallery db!\n");
    return ;
  }

  Gallery *gallery = open_gallery(gdb);
  if (!gallery)
  {
    fprintf(stderr, "can not construct gallery from your DB!\n");
    fast_closedb(gdb);	
    return ;
  }
	
  printf("%d\n%d\n", gallery->n, gallery->persons[0]->n);

  for (int i = 0; i < 80; ++i)
  {
    printf("%lf ", gallery->persons[1]->items[1]->feature[i]);
  }

  printf("\n");

  close_gallery(gallery);
	
  fast_closedb(gdb);

}
int main(int argc, char *argv[])
{
  long t = GetTickCount();
  //test_pack();

  //test_gallery();
  
  test_load_gallery();
  
  //test_unpack();
  //test_fast_unpack();
  t = GetTickCount() - t;

  printf("%f", t/1000.);
  getchar();

  return 0;
}
<file_sep>/**
 * @file   dbio.h
 * @author blackball <<EMAIL>>
 * @date   Mon Oct 17 16:05:47 2011
 * 
 * @brief  user defined IO functions for database 
 * operations. define your pack/unpack methods here.
 * 
 */

#ifndef _MX_DB_IO_H_INCLUDED_
#define _MX_DB_IO_H_INCLUDED_

#include "_dbio.h"

#ifdef __cplusplus
extern "C" {
#endif

int pack_arr(struct db *dbase, void *data, int len);
int unpack_arr(struct db *dbase, void *data, int len, int idx);

#ifdef __cplusplus
}
#endif


#endif
<file_sep>/**
 * @file   _dbio.cpp
 * @author blackball <<EMAIL>>
 * @date   Mon Oct 17 16:30:20 2011
 * 
 * @brief  implementation of binary data IO
 * 
 *
 */


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


#ifdef __cplusplus
extern "C" {
#endif

// hooks for future customizing 
typedef int (db_log_fnc)(const char *fmt, ... );
typedef void* (db_malloc_fnc)(size_t sz);
typedef void* (db_realloc_fnc)(void *ptr, size_t sz);
typedef void (db_free_fnc)(void *ptr);
typedef void (db_exit_fnc)(int status);

struct _db_hook
{
  db_log_fnc *log;
  db_malloc_fnc *malloc;
  db_realloc_fnc *realloc;
  db_free_fnc *free;
  db_exit_fnc *exit;
};

/** 
 * default logging function, print warning,error msg
 * out default tp stderr.
 *
 * @param fmt format string 
 * 
 * @return 0 for now, nothing happend
 */
static int _default_log(const char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);
  return 0;
}

// customize by your needs 
struct _db_hook hooks =
{ 
  _default_log,
  malloc,
  realloc,
  free,
  exit
};


/** 
 * check if the database header is valid
 * 
 * @param dhr pointer to database header
 * 
 * @return 0 if OK, else wrong 
 */
static int _check_header(struct db_header* hdr)
{
  int s = -1;

  if ((hdr->num > 0) && (hdr->len > 0) &&
      (hdr->reserved == 0))
    s = 0;
  
  return s;
}


/** 
 * create databse 
 * 
 * @param magic a unique identity for database
 * @param fname file name of database file
 * @param fixed_len len of item in database
 * 
 * @return a new db handle
 */
struct db* createdb(int magic, const char *fname,
                  int fixed_len)
{
  struct db *d = (struct db*)hooks.malloc(sizeof(struct db));
  d->hdr.magic = magic;
  d->hdr.num = 0;
  d->hdr.len = fixed_len;
  d->hdr.reserved = 0;
  d->head = NULL;
  d->tail = NULL;
  d->curr = NULL;
  
  if (DB_NAME_LEN < strlen(fname))
  {
    hooks.log("file name is too long! Use a part of it.\n");
  }
  
  strncpy(d->fname, fname, DB_NAME_LEN - 1);

  return d;
}


/** 
 * free database handle
 * 
 * @param d database handle
 */
static void _free_db(struct db *d)
{
  int n;
  struct db_node *curr, *next;
  
  if (d == NULL)
    return ;

  n = d->hdr.num;
  curr = d->head;
  
  int i = 0;
  for (; i < n; ++i)
  {
    if ( curr == NULL ) break; 

    next = curr->next;
    free_node(curr);
    curr = next;
  }

  if ( i != n )
  {
    hooks.log("unexpected error !\n");
    return ;
  }
  
  free(d);
  d = NULL;
}

/** 
 * write data base into disk
 * 
 * @param d database handle
 */
void writedb(struct db *d)
{
  FILE *f = NULL;
  
  if ((f = fopen(d->fname, "wb")) == NULL)
  {
    hooks.log("failed to open file %s!\n", d->fname);
    return ;
  }

  // simple safety check
  if(d)
  if ( _check_header(&(d->hdr)))
  {
    hooks.log("invalid databse!\n");
    fclose(f);
    return ;
  }
  
  // first write header
  fwrite(&(d->hdr), sizeof(struct db_header), 1, f);

  // write content
  struct db_node *curr = d->head;
  int i = 0;
  int num = d->hdr.num;
  for(; i < num; ++i)
  {
    if (curr == NULL ) break;
    fwrite(curr->data, sizeof(char)*(d->hdr.len), 1,  f);
    curr = curr->next;    
  }

  if (i != num)
  {
    hooks.log("unexpected error happens!\n");
  }

  fclose(f);
  _free_db(d);
}

/** 
 * open an exist database with its name
 * 
 * @param fname name of database
 * 
 * @return opened database handle
 */
struct db* opendb(const char *fname)
{
  FILE *f;
  struct db *d = NULL;

  if ((f = fopen(fname, "r")) == NULL)
  {
    hooks.log("can not open file %s!\n", fname);
    return NULL;
  }

  d = (struct db*)hooks.malloc(sizeof(struct db));
  strncpy(d->fname, fname, DB_NAME_LEN-1);
  d->head = NULL;
  d->tail = NULL;
  d->curr = NULL;
  d->hdr.len = 0;
  d->hdr.magic = 0;
  d->hdr.num = 0;
  d->hdr.reserved = 0;

  // constuct database handle
  if (0 ==
      fread(&(d->hdr), sizeof(struct db_header), 1,
            f))
  {
    hooks.log("invalid database file!\n");
    hooks.free(d);
    d = NULL;
  }

  if (d)
  if ( _check_header(&(d->hdr)) )
  {
    hooks.log("invalid databse header!\n");
    hooks.free(d);
    d = NULL;
  }

  if (d)
  {
    // construct node list
    int cnt = d->hdr.num;
    
    while(!feof(f) && cnt > 0)
    {
      struct db_node *n = new_node(d->hdr.len);
      fread(n->data, sizeof(char)*(d->hdr.len),
            1, f);
      dump(d, n);d->hdr.num--;
      --cnt;
    }

    if (cnt != 0)
    {
      hooks.log("unexpected file end!\n");
      hooks.free(d);
      d = NULL;
    }
    
  }
  fclose(f);
  return d;    
}

/** 
 * close database 
 * 
 * @param d 
 */
void closedb(struct db *d)
{
  _free_db(d);
}

/** 
 * create a new node by specify its lenth
 * 
 * @param len bytes in node's data domain
 * 
 * @return new node with allocated memory
 */
struct db_node* new_node(int len)
{
  struct db_node *n = (struct db_node*)
      hooks.malloc(sizeof(struct db_node)+ sizeof(char)*(len));
  n->len = len;
  n->data =(void*)(n+1);
  return n;
}

/** 
 * free node 
 * 
 * @param n a node 
 */
void free_node(struct db_node* n)
{
  hooks.free((void*)n);
  n = NULL;
}

/** 
 * Macro to add new node at the tail of a list
 * 
 * @param tail tail of list, tail = last node
 * @param add new node
 * 
 */
#define _add_node(tail, add)                    \
  do{                                           \
    tail->next = add;                           \
    tail = add;                                 \
    tail->next = NULL;                          \
  }while(0)


/** 
 * insert a node into database
 * 
 * @param d databse handle
 * @param n new node
 */
void dump(struct db* d, struct db_node *n)
{
  if (!d || !n || (d->hdr.len != n->len))
  {
    hooks.log("invalid database or node !\n");
    return ;
  }
  // insert node into db
  if (d->head == NULL) // first node
  {
    d->head = n;
    d->tail = n;
    d->tail->next = NULL;
  }
  else
  _add_node(d->tail, n);

  d->hdr.num++;
}


/** 
 * index and return the item specified
 * 
 * @param d database 
 * @param idx -1 for accessing one by
 * one, [0,1,..,num-1] for indexing
 * 
 * @return pointer to the specified node 
 */
struct db_node* undump(struct db *d, int idx)
{
  struct db_node *node;

  if(idx == -1)
  {
    if (d->curr == NULL) d->curr = d->head;
    else
    {
      d->curr = d->curr->next;
    }
    node = d->curr;
  }
  else if( (idx >= 0) && (idx < d->hdr.num) )
  {
    d->curr = d->head;
    int i = 0;
    for(; i < idx; ++i)
    {
      d->curr = d->curr->next;
    }
    node = d->curr;
  }
  else
  {
    hooks.log("invalid index!\n");
    node = NULL;
  }
  return node;
}


/** 
 * fast version of open database
 * 
 * @param fname 
 * 
 * @return 
 */
struct db* fast_opendb(const char* fname)
{
  // get the header
  FILE *f;
  struct db *d = NULL;

  if ((f = fopen(fname, "r")) == NULL)
  {
    hooks.log("can not open file %s!\n", fname);
    return NULL;
  }

  d = (struct db*)hooks.malloc(sizeof(struct db));
  strncpy(d->fname, fname, DB_NAME_LEN-1);
  d->head = NULL;
  d->tail = NULL;
  d->curr = NULL;
  d->hdr.len = 0;
  d->hdr.magic = 0;
  d->hdr.num = 0;
  d->hdr.reserved = 0;
  
  // constuct database handle
  if (0 ==
      fread(&(d->hdr), sizeof(struct db_header), 1,
            f))
  {
    hooks.log("invalid database file!\n");
    hooks.free(d);
    d = NULL;
  }

  if (d)
  if ( _check_header(&(d->hdr)) )
  {
    hooks.log("invalid databse header!\n");
    hooks.free(d);
    d = NULL;
  }
  
  // because the len of every node is fixed, we could
  // read all nodes' data once, and link them to a list
  if (d)
  {
    int len_total = d->hdr.num * d->hdr.len;
    // for all nodes
    int node_total = d->hdr.num * sizeof(struct db_node);
    void *nodes = hooks.malloc(node_total);
    void *chunk = hooks.malloc(len_total);
    if (chunk == NULL || nodes == NULL)
    {
      hooks.log("insufficient memory!\n");
      hooks.exit(-1);
    }

    // read all data at once
    fread(chunk, len_total, 1, f);

    // constuct database linked list
    int cnt = d->hdr.num;
    int len = d->hdr.len;

    // init every node
    struct db_node *curr = (struct db_node*)nodes;
    int i = 0;
    for(; i < cnt-1; ++i)
    {
      curr->len = len;
      curr->data = NULL;
      curr->next = (curr + 1);
      curr = curr->next;
    }
    d->head = (struct db_node*)nodes;
    d->tail = curr;
    curr->len = len;
    curr->data = NULL;
    curr->next = NULL;
    d->curr = NULL;

    curr = d->head;
    void *data_head = chunk;
    for(i = 0; i < cnt; ++i)
    {
	  if (curr == NULL) break;

      curr->data = data_head;
      curr = curr->next;
      data_head = (unsigned char*)data_head + len;
    }
	curr = NULL;
	data_head = NULL;
  }
 
  return d;
}

/** 
 * fast version of write the database
 * 
 * @param d database handle
 */
void fast_writedb(struct db *d)
{
  // when you create a new database,
  // you don't know the size of it,
  // so we can't write all stuffs once
  // what we could make it fast is,
  // not free the list, left it to OS
  FILE *f = NULL;
  
  if ((f = fopen(d->fname, "wb")) == NULL)
  {
    hooks.log("failed to open file %s!\n", d->fname);
    return ;
  }

  // simple safety check
  if(!d)
  if ( _check_header(&(d->hdr)))
  {
    hooks.log("invalid databse!\n");
    fclose(f);
    return ;
  }
  
  // first write header
  fwrite(&(d->hdr), sizeof(struct db_header), 1, f);

  // write content
  struct db_node *curr = d->head;
  int i = 0;
  int num = d->hdr.num;
  for(; i < num; ++i)
  {
    if (curr == NULL ) break;
    fwrite(curr->data, sizeof(char)*(d->hdr.len), 1,  f);
    curr = curr->next;    
  }

  if (i != num)
  {
    hooks.log("unexpected error happens!\n");
  }
  fclose(f);
  // no memory free here, leave it to OS
}

/** 
 * fast version of close database
 * 
 * @param d 
 */
void fast_closedb(struct db *d)
{
  // if you use fast_opendb(),
  // then you could use this fast close
  // version.
  hooks.free(d->head->data);
  hooks.free(d->head);  
}

#ifdef __cplusplus
}
#endif
<file_sep>/**
 * @file   dbio.cpp
 * @author blackball <<EMAIL>>
 * @date   Mon Oct 17 16:08:13 2011
 * 
 * @brief  definitions of user defined pack/unpack methods
 * 
 * 
 */

#include "dbio.h"
#include "_dbio.h"
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

/** 
 * pack any type of array 
 * 
 * @param dbase database 
 * @param data data chunk
 * @param len sizeof(type)*n
 * 
 * @return error code, 0 means OK, -1 means failed
 */
int pack_arr(struct db *dbase, void *data, int len)
{
  int code = -1;

  struct db_node *node = new_node(len);
  memcpy(node->data, data, len);
  // insert node into dbase
  dump(dbase, node);
  
  code = 0;
  return code;
}

  /** 
   * unpack a array data from database
   * 
   * @param dbase database handle
   * @param data array 
   * @param len sizeof(type)*n
   * @param idx -1 for continious, [0, len-1] for idx
   * 
   * @return 
   */
int unpack_arr(struct db* dbase, void *data, int len, int idx)
{
  int code = -1;
  struct db_node *node = undump(dbase, idx);
  if (node != NULL)
  {
    memcpy(data,node->data,len);
    code = 0;
  }
  return code;
}


#ifdef __cplusplus
}
#endif
