Wednesday 22 August 2018

Doubly linked list

/*
 * File name: m_list.h
 * Author: Seree Rakwong
 * Date: 13-SEP-2013
 *
 * A list is implemented by the range as [a, b) (including a, but b)
 */

#ifndef _MLIST_H_
#define _MLIST_H_

#include <stdlib.h>
#include <string.h>
#include "m_node.h"

#ifdef __cplusplus
extern "C" {
#endif
struct _LIST_STRUCT;
typedef struct _LIST_STRUCT list_t;

struct _LIST_ITER_STRUCT;
typedef struct _LIST_ITER_STRUCT* list_iter_t;

list_t* list_create();
void    list_destroy(list_t*);

typedef long (*fn_list_insert_first)(list_t*, const void*, unsigned long);
typedef long (*fn_list_insert_last)(list_t*, const void*, unsigned long);
typedef long (*fn_list_insert_before)(list_t*, list_iter_t, const void*, unsigned long);
typedef long (*fn_list_insert_after)(list_t*, list_iter_t, const void*, unsigned long);
typedef long (*fn_list_remove_at)(list_t*, list_iter_t);
typedef long (*fn_list_remove_all)(list_t*);
typedef long (*fn_list_remove_first)(list_t*);
typedef long (*fn_list_remove_last)(list_t*);
typedef unsigned long (*fn_list_get_count)(list_t*);
typedef list_iter_t   (*fn_list_begin)(list_t*);
typedef list_iter_t   (*fn_list_end)(list_t*);
typedef node_t        (*fn_list_at)(list_iter_t iter);

/*
long list_insert_first(list_t*, const void*, unsigned long);
long list_insert_last(list_t*, const void*, unsigned long);
long list_insert_before(list_t*, list_iter_t, const void*, unsigned long);
long list_insert_after(list_t*, list_iter_t, const void*, unsigned long);
long list_remove_at(list_t*, list_iter_t);
long list_remove_all(list_t*);
long list_remove_first(list_t*);
long list_remove_last(list_t*);
unsigned long list_get_count(list_t*);
list_iter_t   list_begin(list_t*);
list_iter_t   list_end(list_t*);
node_t        list_at(list_iter_t iter);
*/

struct _LIST_ITER_STRUCT
{
    node_t      node;
    list_iter_t prev;
    list_iter_t next;
};

struct _LIST_STRUCT
{
    list_iter_t     head;
    list_iter_t     tail;
    unsigned long   nodes;
    /* methods */
    fn_list_insert_first        insert_first;
    fn_list_insert_last         insert_last;
    fn_list_insert_before       insert_before;
    fn_list_insert_after        insert_after;
    fn_list_remove_at           remove_at;
    fn_list_remove_all          remove_all;
    fn_list_remove_first        remove_first;
    fn_list_remove_last         remove_last;
    fn_list_get_count           count;
    fn_list_begin               begin;
    fn_list_end                 end;
    fn_list_at                  at;
};

#ifdef __cplusplus
}
#endif

#endif /* _MLIST_H_ */

/*
 * File name: m_list.c
 * Author: Seree Rakwong
 * Date: 13-SEP-2013
 */
#include "m_list.h"

#ifdef __cplusplus
extern "C" {
#endif

list_iter_t _list_iterator_init(const void* vp, unsigned long size)
{
    list_iter_t iter = (list_iter_t)malloc(sizeof(struct _LIST_ITER_STRUCT));
    if (iter != 0)
    {
        memset(iter, 0, sizeof(struct _LIST_ITER_STRUCT));
        if (vp && size > 0)
        {
            iter->node = node_init(vp, size);
        }
    }
    return iter;
}

void _list_iterator_release(list_iter_t iter)
{
    if (iter)
    {
        if (iter->node)
        {
            node_release(iter->node);
        }
        free(iter);
    }
}

/*------------------------------------------------------------*/
long list_insert_first(list_t*, const void*, unsigned long);
long list_insert_last(list_t*, const void*, unsigned long);
long list_insert_before(list_t*, list_iter_t, const void*, unsigned long);
long list_insert_after(list_t*, list_iter_t, const void*, unsigned long);
void list_free(list_t*);
long list_remove_at(list_t*, list_iter_t);
long list_remove_all(list_t*);
long list_remove_first(list_t*);
long list_remove_last(list_t*);
unsigned long list_get_count(list_t*);
list_iter_t   list_begin(list_t*);
list_iter_t   list_end(list_t*);
node_t        list_at(list_iter_t iter);

/*------------------------------------------------------------*/
list_t* list_create()
{
    list_t* list = (list_t*)malloc(sizeof(list_t));
    
    if (list != 0)
    {
        list->nodes = 0;
        list->head = list->tail = _list_iterator_init(0, 0);
        /* init methods */
        list->insert_first  = list_insert_first;
        list->insert_last   = list_insert_last;
        list->insert_before = list_insert_before;
        list->insert_after  = list_insert_after;
        list->remove_at     = list_remove_at;
        list->remove_all    = list_remove_all;
        list->remove_first  = list_remove_first;
        list->remove_last   = list_remove_last;
        list->count         = list_get_count;
        list->begin         = list_begin;
        list->end           = list_end;
        list->at            = list_at;
    }
    return list;
}

void    list_destroy(list_t* list)
{
    if (list)
    {
        list->remove_all(list);
        free(list);
    }
}

long list_insert_first(list_t* list, const void* vp, unsigned long size)
{
    long rc = 0;
    list_iter_t iter = 0;
    
    if (vp && size)
    {
        iter = _list_iterator_init(vp, size);
        if (iter != 0)
        {
            iter->next = list->head;
            list->head->prev = iter;
            list->head = iter;

            ++list->nodes;
        }
        else
        {
            rc = -2;
        }
    }
    else
    {
        rc = -1;
    }
    return rc;
}

long list_insert_last(list_t* list, const void* vp, unsigned long size)
{
    long rc = 0;
    list_iter_t iter = 0;
    
    if (vp && size)
    {
        iter = _list_iterator_init(vp, size);
        if (iter != 0)
        {
            iter->next = list->tail;
            iter->prev = list->tail->prev;

            if (list->tail->prev)
            {
                list->tail->prev->next = iter;
            }
            list->tail->prev = iter;

            if (list->head == list->tail)
            {
                list->head = iter;
            }

            ++list->nodes;
        }
        else
        {
            rc = -2;
        }
    }
    else
    {
        rc = -1;
    }
    return rc;
}

long list_insert_before(list_t* list, list_iter_t iter_after, const void* vp, unsigned long size)
{
    long rc = 0;
    list_iter_t iter = 0;
    
    if (vp && size > 0)
    {
        iter = _list_iterator_init(vp, size);
        if (iter != 0)
        {
            iter->next = iter_after;
            iter->prev = iter_after->prev;

            if (iter_after->prev)
            {
                iter_after->prev->next = iter;
            }
            iter_after->prev = iter;

            ++list->nodes;
        }
        else
        {
            rc = -2;
        }
    }
    else
    {
        rc = -1;
    }
    return rc;
}

long list_insert_after(list_t* list, list_iter_t iter_before, const void* vp, unsigned long size)
{
    long rc = 0;
    list_iter_t iter = 0;
    
    if (vp && size > 0)
    {
        iter = _list_iterator_init(vp, size);
        if (iter != 0)
        {
            iter->prev = iter_before;
            iter->next = iter_before->next;

            if (iter_before->next)
            {
                iter_before->next->prev = iter;
            }
            iter_before->next = iter;

            ++list->nodes;
        }
        else
        {
            rc = -2;
        }
    }
    else
    {
        rc = -1;
    }
    return rc;
}

long list_remove_at(list_t* list, list_iter_t iter)
{
    long rc = 0;

    /* is it a head item? */
    if (iter == list->head)
    {
        return list->remove_first(list);
    }

    /* is it a tail item? */
    if (iter == list->tail)
    {
        return list->remove_last(list);
    }

    /* link them */
    if (iter->next)
    {
        iter->next->prev = iter->prev;
    }
    if (iter->prev)
    {
        iter->prev->next = iter->next;
    }

    /* remove it */
    _list_iterator_release(iter);

    if (list->nodes > 0)
    {
        --list->nodes;
    }

    return rc;
}

long list_remove_all(list_t* list)
{
    long rc = 0;
    list_iter_t iter = list->head;

    while (iter != list->tail)
    {
        list->remove_first(list);
        iter = list->head;
    }
    /*
    * set the tail and head of the list
    */
    list->tail->prev = 0;
    list->head = list->tail;
    list->nodes = 0;

    return rc;
}

long list_remove_first(list_t* list)
{
    long rc = 0;
    list_iter_t iter = list->head;

    if (iter == list->tail)
    {
        /* no more item to remove */
        return 0;
    }

    list->head = iter->next;
    list->head->prev = 0;
    /* ok to free the memory */
    _list_iterator_release(iter);

    if (list->nodes > 0)
    {
        --list->nodes;
    }

    return rc;
}

long list_remove_last(list_t* list)
{
    long rc = 0;
    list_iter_t iter = list->tail->prev;

    if (!iter)
    {
        /* no more item to remove */
        return 0;
    }

    list->tail->prev = iter->prev;
    if (iter->prev)
    {
        iter->prev->next = list->tail;
    }
    /* ok to free the memory */
    _list_iterator_release(iter);
    
    if (list->nodes > 0)
    {
        --list->nodes;
    }
    return rc;
}

unsigned long list_get_count(list_t* list)
{
    return list->nodes;
}

list_iter_t list_begin(list_t* list)
{
    return list->head;
}

list_iter_t list_end(list_t* list)
{
    return list->tail;
}

node_t list_at(list_iter_t iter)
{
    if (iter)
    {
        return iter->node;
    }
    return 0;
}

#ifdef __cplusplus
}
#endif

Tuesday 21 August 2018

m_map: How to use SQLite as same as STL::map

/*
 * File name: m_map.h
 * Author: Seree Rakwong
 * Date: 15-AUG-2018
 *
 * NOTE:
 *   VMS  - LINK proc,bos_lib:sqlite3.opt/opt
 *   UNIX - gcc proc.c -lsqlite3
 */
#ifndef __M_MAP_H__
#define __M_MAP_H__

#include "sqlite3.h"
#include "m_node.h"

#ifdef __cplusplus
extern "C" {
#endif

struct _MAP_STRUCT;
typedef struct _MAP_STRUCT map_t;

map_t* map_create();
void   map_destroy(map_t*);

typedef long (*fn_map_populate_callback)(char*, node_t, const void*);

/* map object functions */
typedef int (*fn_map_free)(map_t*);
typedef int (*fn_map_init)(map_t*, char*);
typedef int (*fn_map_release)(map_t*, char*);
typedef int (*fn_map_insert)(map_t*, char*, char*, const void*, unsigned long);
typedef int (*fn_map_erase)(map_t*, char*, char*);
typedef int (*fn_map_clear)(map_t*, char*);
typedef node_t (*fn_map_find)(map_t*, char*, char*);
typedef long (*fn_map_count)(map_t*, char*);
typedef long (*fn_map_populate)(map_t*, char*, fn_map_populate_callback, const void*);
typedef long (*fn_map_reverse_populate)(map_t*, char*, fn_map_populate_callback, const void*);

/*
int map_free(map_t* map);
int map_init(map_t* map, char* name);
int map_release(map_t* map, char* name);
int map_insert(map_t* map, char* name, char* key, const void* vp, unsigned long size);
int map_erase(map_t* map, char* name, char* key);
int map_clear(map_t* map, char* name);
node_t map_find(map_t* map, char* name, char* key);
long map_count(map_t* map, char* name);

/////////////////////////////////////////////////////////////
// fn_map_populate_callback
// Return: 0 to continue to next pair, otherwise stop populating
//
long map_populate(  map_t* map, 
                    char* name, 
                    fn_map_populate_callback callback, 
                    const void* multi_puposed_usage);
long map_reverse_populate(  map_t* map, 
                            char* name, 
                            fn_map_populate_callback callback, 
                            const void* multi_puposed_usage);
*/

#define SYS_MAP_NAME    "S00"   /* Application CANNOT use this name */

struct _MAP_STRUCT
{
    sqlite3* memdb; 
    /* methods */
    fn_map_free         free;           /* release all map tables   */
    fn_map_init         init;           /* init the new map table   */
    fn_map_release      release;        /* release the specific name    */
    fn_map_insert       insert;         /* insert the new pair      */
    fn_map_erase        erase;          /* delete the specified pair    */
    fn_map_clear        clear;          /* delete all pairs         */
    fn_map_find         find;           /* find the pair            */
    fn_map_count        count;          /* cound the pairs          */
    fn_map_populate     populate;       /* populate all pairs       */
    fn_map_reverse_populate     rpopulate;   /* reverse populate all pairs */
};


#ifdef __cplusplus
}
#endif

#endif /* __M_MAP_H__ */


/*
 * File name: m_map.c
 * Author: Seree Rakwong
 * Date: 15-AUG-2018
 *
 * NOTE:
 *   VMS  - LINK proc,bos_lib:sqlite3.opt/opt
 *   UNIX - gcc proc.c -lsqlite3
 */
#include "m_map.h"
#include <stdlib.h>
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

long _map_populate( map_t* map, 
                    char* name, 
                    fn_map_populate_callback callback, 
                    const void* vp,
                    int reverse);
long _map_clear_callback(char* key, node_t, const void*);
/*---------------------------------------------------------------------------*/

int map_free(map_t* map);
int map_init(map_t* map, char* name);
int map_release(map_t* map, char* name);
int map_insert(map_t* map, char* name, char* key, const void* vp, unsigned long size);
int map_erase(map_t* map, char* name, char* key);
int map_clear(map_t* map, char* name);
node_t map_find(map_t* map, char* name, char* key);
long map_count(map_t* map, char* name);
long map_populate(  map_t* map, 
                    char* name, 
                    fn_map_populate_callback callback,
                    const void* vp);
long map_reverse_populate(  map_t* map, 
                            char* name, 
                            fn_map_populate_callback callback,
                            const void* vp);

/*---------------------------------------------------------------------------*/
#define USING_MEMDB                         ":memory:"
#define SELECT_FROM_NAME_ORDER_MAPKEY_QS    "SELECT MAPVAL, MAPKEY FROM %q ORDER BY MAPKEY %s;"
#define SELECT_FROM_NAME_ORDER_MAPKEY_DESC_Q    "SELECT MAPVAL, MAPKEY FROM %q ORDER BY MAPKEY DESC;"
#define SELECT_FROM_NAME_ORDER_MAPKEY_ASC_Q     "SELECT MAPVAL, MAPKEY FROM %q ORDER BY MAPKEY ASC;"
#define CREATE_TABLE_SYS_MAP_NAME_Q         "CREATE TABLE %q("\
                                            " MAPNAME VARCHAR(256) PRIMARY KEY NOT NULL"\
                                            ");"
#define CREATE_TABLE_NAME_Q                 "CREATE TABLE %q("\
                                            " MAPKEY VARCHAR(256) PRIMARY KEY NOT NULL,"\
                                            " MAPVAL INTEGER NOT NULL"\
                                            ");"
#define INSERT_INTO_SYS_MAP_NAME_Q          "INSERT INTO %q VALUES('%q');"
#define SELECT_FROM_SYS_MAP_NAME_Q          "SELECT MAPNAME FROM %q;"
#define INSERT_INTO_NAME_QQL                "INSERT INTO %q VALUES('%q',%ld);"
#define DELETE_FROM_NAME_WHERE_QQ           "DELETE FROM %q WHERE MAPKEY='%q';"
#define DELETE_FROM_NAME_Q                  "DELETE FROM %q WHERE 1=1;"
#define SELECT_FROM_NAME_WHERE_QQ           "SELECT MAPVAL FROM %q WHERE MAPKEY='%q';"
#define SELECT_COUNT_FROM_NAME_Q            "SELECT COUNT(*) CNT FROM %q;"
#define DROP_TABLE_NAME_Q                   "DROP TABLE %q;"

/*---------------------------------------------------------------------------*/
long _map_populate( map_t* map, 
                    char* name, 
                    fn_map_populate_callback callback, 
                    const void* vp,
                    int reverse)
{
    int rc = SQLITE_OK;
    node_t iter = 0;
    const unsigned char* key;
    char* zSQL;
    sqlite3_stmt* stmt;

    if (0 == callback)
    {
        return rc;
    }
    zSQL = sqlite3_mprintf(
                (reverse != 0 ? SELECT_FROM_NAME_ORDER_MAPKEY_DESC_Q :
                    SELECT_FROM_NAME_ORDER_MAPKEY_ASC_Q), 
                name);
    rc = sqlite3_prepare(map->memdb,
            zSQL,
            -1,
            &stmt,
            0);
    sqlite3_free(zSQL);

    if (SQLITE_OK == rc)
    {
        rc = sqlite3_step(stmt);
        while (SQLITE_ROW == rc)
        {
            iter = (node_t)(sqlite3_column_int(stmt, 0));
            key = sqlite3_column_text(stmt, 1);
            if (callback((char*)key, iter, vp) != 0)
            {
                break;
            }
            rc = sqlite3_step(stmt);
        }
        sqlite3_finalize(stmt);
    }

    return rc;
}

long _map_clear_callback(char* key, node_t iter, const void* vp)
{
    node_release(iter);
    return 0;
}

/*---------------------------------------------------------------------------*/

map_t* map_create()
{
    map_t* map = (map_t*)malloc(sizeof(map_t));
    if (map != NULL)
    {
        char* zSQL;
        int rc = sqlite3_open(USING_MEMDB, &map->memdb);
        if (rc != SQLITE_OK)
        {
            free(map);
            return 0;
        }

        zSQL = sqlite3_mprintf(
                    CREATE_TABLE_SYS_MAP_NAME_Q, 
                    SYS_MAP_NAME);
        rc = sqlite3_exec(map->memdb,
            zSQL,
            0,
            0,
            0);

        sqlite3_free(zSQL);
        if (rc != SQLITE_OK)
        {
            sqlite3_close(map->memdb);
            free(map);
            return 0;
        }

        /* init methods */
        map->free       = map_free;
        map->init       = map_init;
        map->release    = map_release;
        map->insert     = map_insert;
        map->erase      = map_erase;
        map->clear      = map_clear;
        map->find       = map_find;
        map->count      = map_count;
        map->populate   = map_populate;
        map->rpopulate  = map_reverse_populate;
    }
    return map;
}

void  map_destroy(map_t* map)
{
    if (map != 0)
    {
        map->free(map);
        free(map);
    }
}

/* map methods */
int map_init(map_t* map, char* name)
{
    int rc = SQLITE_OK;
    char* zSQL = sqlite3_mprintf(
                    CREATE_TABLE_NAME_Q, 
                    name);
    rc = sqlite3_exec(map->memdb,
            zSQL,
            0,
            0,
            0);
    sqlite3_free(zSQL);

    if (rc != SQLITE_OK)
    {
        return rc;
    }
    zSQL = sqlite3_mprintf(
                INSERT_INTO_SYS_MAP_NAME_Q,
                SYS_MAP_NAME, name);
    rc = sqlite3_exec(map->memdb,
            zSQL,
            0,
            0,
            0);

    sqlite3_free(zSQL);
    return rc;
}

int map_release(map_t* map, char* name)
{
    int rc = SQLITE_OK;
    char* zSQL;

    rc = map->clear(map, name);
    if (SQLITE_OK == rc)
    {
        zSQL = sqlite3_mprintf(
                    DROP_TABLE_NAME_Q, 
                    name);
        rc = sqlite3_exec(map->memdb,
                zSQL,
                0,
                0,
                0);
        sqlite3_free(zSQL);
    }

    return rc;
}

int map_free(map_t* map)
{
    int rc = SQLITE_OK;
    sqlite3_stmt* stmt;
    char* zSQL;

    if (0 == map->memdb)
    {
        return rc;
    }
    zSQL = sqlite3_mprintf(
                SELECT_FROM_SYS_MAP_NAME_Q, 
                SYS_MAP_NAME);
    rc = sqlite3_prepare(map->memdb,
            zSQL,
            -1,
            &stmt,
            0);

    sqlite3_free(zSQL);

    if (SQLITE_OK == rc)
    {
        rc = sqlite3_step(stmt);
        while (SQLITE_ROW == rc)
        {
            const unsigned char* name = sqlite3_column_text(stmt, 0);
            map->clear(map, (char*)name);
            rc = sqlite3_step(stmt);
        }
        sqlite3_finalize(stmt);
    }
        
    rc = sqlite3_close(map->memdb);
    map->memdb = 0;

    return rc;
}

int map_insert(map_t* map, char* name, char* key, const void* vp, unsigned long size)
{
    int rc = SQLITE_OK;
    char *zSQL;

    node_t iter = node_init(vp, size);
    if (0 == iter)
    {
        return SQLITE_NOMEM;
    }
    zSQL = sqlite3_mprintf(
                INSERT_INTO_NAME_QQL, 
                name, key, (long)iter);

    rc = sqlite3_exec(map->memdb, zSQL, 0, 0, 0);
    sqlite3_free(zSQL);
    return rc;
}

int map_erase(map_t* map, char* name, char* key)
{
    int rc = SQLITE_OK;
    node_t iter = 0;
    /* find the key if it is existing */
    iter = map->find(map, name, key);
    /* if found */
    if (iter != 0)
    {
        char* zSQL = sqlite3_mprintf(
                DELETE_FROM_NAME_WHERE_QQ, 
                name, key);
        rc = sqlite3_exec(map->memdb, zSQL, 0, 0, 0);
        sqlite3_free(zSQL);

        node_release(iter);
    }
    else
    {
        rc = SQLITE_NOTFOUND;
    }
    return rc;
}

int map_clear(map_t* map, char* name)
{
    int rc = SQLITE_OK;
    char* zSQL = sqlite3_mprintf(
                    DELETE_FROM_NAME_Q,
                    name);

    rc = map->populate(map, name, _map_clear_callback, 0);

    rc = sqlite3_exec(map->memdb, zSQL, 0, 0, 0);
    sqlite3_free(zSQL);
    return rc;
}

node_t map_find(map_t* map, char* name, char* key)
{
    int rc = SQLITE_OK;
    node_t iter = 0;
    sqlite3_stmt* stmt;
    char* zSQL = sqlite3_mprintf(
                    SELECT_FROM_NAME_WHERE_QQ, 
                    name, key);

    rc = sqlite3_prepare(map->memdb,
            zSQL,
            -1,
            &stmt,
            0);
    sqlite3_free(zSQL);

    if (SQLITE_OK == rc)
    {
        rc = sqlite3_step(stmt);
        if (SQLITE_ROW == rc)
        {
            iter = (node_t)(sqlite3_column_int(stmt, 0));
        }
        sqlite3_finalize(stmt);
    }
    return iter;
}

long map_count(map_t* map, char* name)
{
    int rc = SQLITE_OK;
    long cnt = 0;
    sqlite3_stmt* stmt;
    char* zSQL = sqlite3_mprintf(
                    SELECT_COUNT_FROM_NAME_Q, 
                    name);
    rc = sqlite3_prepare(map->memdb,
            zSQL,
            -1,
            &stmt,
            0);
    sqlite3_free(zSQL);

    if (SQLITE_OK == rc)
    {
        rc = sqlite3_step(stmt);
        if (SQLITE_ROW == rc)
        {
            cnt = sqlite3_column_int(stmt, 0);
        }
        sqlite3_finalize(stmt);
    }
    else
    {
        /* something errors */
        cnt = -1;
    }
    return cnt;
}

long map_populate(
    map_t* map, 
    char* name, 
    fn_map_populate_callback callback,
    const void* vp)
{
    return _map_populate(map, name, callback, vp, 0); /* 0 = not reverse */
}

long map_reverse_populate(  
    map_t* map, 
    char* name, 
    fn_map_populate_callback callback,
    const void* vp)
{
    return _map_populate(map, name, callback, vp, 1); /* 1 = reverse */
}

#ifdef __cplusplus
}
#endif