[core] added ydaemon data manipulation stuff;

v0.5.xx
Alexander Vdolainen 9 years ago
parent 77d0ee4a0b
commit 23b19029f4

@ -4,4 +4,4 @@ nobase_include_HEADERS = sxmp/sxmp.h sxmp/errno.h sxmp/limits.h sxmp/version.h \
sexpr/sexp_memory.h sexpr/sexp_ops.h sexpr/sexp_vis.h \
tdata/bitwise.h tdata/idx_allocator.h tdata/macro.h tdata/tree.h \
tdata/usrtc.h tdata/list.h tdata/ctrie.h \
ydaemon/ydaemon.h
ydaemon/ydaemon.h ydaemon/cache.h ydaemon/dataobject.h

@ -0,0 +1,95 @@
/*
* Yet another daemon library especially designed to be used
* with libsxmp based daemons.
*
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
*
* libsxmp is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libsxmp is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#ifndef __YDATA_CACHE_H__
#define __YDATA_CACHE_H__
#include <pthread.h>
#define YD_CACHE_SPACER_MAGIC 0xbeafbeef
/* item flags */
#define YDC_INVALIDATE (1 << 1)
#define YDC_UNUSED (1 << 2)
#define YDC_DIRTY (1 << 3)
typedef struct __ydata_cache_item_type {
oid_t oid;
uint32_t idx;
uint8_t attr;
usrtc_node_t idxnode;
list_node_t listnode;
} ydata_cache_item_t;
#define yd_item_idx(item) (item)->idx
#define yd_item_setoid(it, id) (it)->oid = id
typedef struct __ydata_qitem_type {
ydata_cache_item_t *item;
list_node_t node;
} ydata_qitem_t;
#define YDC_SYNCTHRESHOLD 64
#define YDC_SYNCDELTASEC 30
typedef struct __ydata_cache_t {
usrtc_t idx_tree;
list_head_t free_poll;
list_head_t dirty_poll;
list_head_t pending_poll;
void *cache;
size_t object_size;
size_t objects_amount;
size_t cache_size;
size_t dirties;
pthread_t syncthread;
pthread_rwlock_t rwlock;
pthread_mutex_t dirtlock;
} ydata_cache_t;
#define yd_cache_rdlock(c) pthread_rwlock_rdlock(&(c)->rwlock)
#define yd_cache_wrlock(c) pthread_rwlock_wrlock(&(c)->rwlock)
#define yd_cache_unlock(c) pthread_rwlock_unlock(&(c)->rwlock)
#define yd_cache_ptrbyidx(c, n) (char *)(c)->cache + n*((c)->object_size + sizeof(uint32_t))
/* API, mostly internally used, but might be useful */
ydata_cache_t *yd_cache_init(dataobject_t *, size_t);
void *yd_cache_alloc_item(ydata_cache_t *, ydata_cache_item_t **);
void yd_cache_discard_alloc_item(ydata_cache_t *, ydata_cache_item_t *);
void yd_cache_confirm_alloc_item(ydata_cache_t *, ydata_cache_item_t *, oid_t);
void *yd_cache_lookup(ydata_cache_t *, oid_t);
list_head_t *yd_cache_getrslist(ydata_cache_t *, uint8_t);
void yd_cache_qitems_pullback(ydata_cache_t *, list_head_t *);
void yd_cache_item_dirtyoid(ydata_cache_t *, oid_t);
void yd_cache_item_invalidateoid(ydata_cache_t *, oid_t);
int yd_cache_start_thread(domx_t *);
#endif /* __YDATA_CACHE_H__ */

@ -0,0 +1,287 @@
/*
* Yet another daemon library especially designed to be used
* with libsxmp based daemons.
*
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
*
* libsxmp is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libsxmp is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#ifndef __DM_DATAOBJECT_H__
#define __DM_DATAOBJECT_H__
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>
#include <tdata/usrtc.h>
#include <tdata/list.h>
#define MAX_ITEM_NAME 32
#define MAX_ITEMS 255
typedef uint64_t oid_t;
enum {
U8 = 0,
S8,
U16,
S16,
U32,
S32,
U64,
S64,
CSTR,
TBLOB,
TACRT,
};
struct typelen {
uint8_t type;
uint64_t len;
};
typedef struct __access_rights_type {
uint32_t ouid;
uint32_t ogid;
uint8_t domainid;
uint8_t sal;
uint8_t amask;
uint8_t reserve;
}__attribute__((packed)) acc_right_t;
typedef struct __dataacc_pemctx_type {
acc_right_t *uobj;
uint32_t gids[16];
} dataacc_pemctx_t;
#ifdef DMUX_USE
static struct typelen tltable[] = {
{.type = U8, .len = 1},
{.type = S8, .len = 1},
{.type = U16, .len = 2},
{.type = S16, .len = 2},
{.type = U32, .len = 4},
{.type = S32, .len = 4},
{.type = U64, .len = 8},
{.type = S64, .len = 8},
{.type = CSTR, .len = 0},
{.type = TBLOB, .len = 0},
{.type = TACRT, .len = sizeof(acc_right_t) },
};
#endif
typedef struct __dataobject_item_type {
uint8_t id;
uint8_t type;
uint64_t len;
char name[MAX_ITEM_NAME];
list_node_t node;
}__attribute__ ((packed)) dataobject_item_t;
typedef struct __item_idx_type {
void *key;
dataobject_item_t *dtr;
usrtc_node_t node;
} item_idx_t;
typedef struct __dataobject_type {
char name[MAX_ITEMS];
list_head_t description; /* description of the data */
usrtc_t id_index;
usrtc_t name_index;
} dataobject_t;
/* API */
dataobject_t *dotr_create(const char *name);
int dotr_set_item(dataobject_t *dotr, uint8_t type, uint64_t len, const char *name);
uint8_t dotr_item_type(dataobject_t *obj, const char *name, uint64_t *len);
char *dotr_item_nameptr(dataobject_t *obj, const char *name);
int dotr_item_nameidx(dataobject_t *obj, const char *name);
void dotr_destroy(dataobject_t *dotr);
/* dataobject misc operations */
int dtocmp(const dataobject_t *src, const dataobject_t *dst);
size_t dtolen(const dataobject_t *src);
/*
* Filtering used for access to the data stream.
*/
typedef enum {
YDM_NO_FILTER = 0, /* no access right filter */
YDM_DOMAIN_FILTER, /* filter only by domain */
YDM_USERDAC_FILTER, /* filter by domain and user with DAC */
YDM_FULL_FILTER, /* all above filter plus additional filtering */
} ydm_access_filter_t;
typedef struct __sxmp_ch_entry_type {
char instance[32];
uint16_t chtid;
ydm_access_filter_t filter;
list_node_t node;
} sxmp_ch_entry_t;
struct __domx_dsbe_type;
/* some intermodular structures */
typedef struct __domx_type {
dataobject_t *dox;
char *name;
list_head_t sxmp_channels;
struct __domx_dsbe_type *be;
void *cache;
} domx_t;
/* initialization */
int domx_init(domx_t *dt, dataobject_t *dodesc);
int domx_set_be(domx_t *dt, void *be, const char *key);
int domx_set_cache(domx_t *dt, size_t size);
int domx_set_sxmpchannel(domx_t *dt, const char *instance, int typeid, int access);
/* data operations */
int domx_get(domx_t *dt, uint64_t oid, void **refdata);
int domx_set(domx_t *dt, uint64_t oid, const void *refdata);
int domx_remove(domx_t *dt, uint64_t oid);
int domx_creat(domx_t *dt, uint64_t *oid, const void *refdata);
/* cache ops for backends */
int domx_cache_invalidate(domx_t *dt, uint64_t oid);
int domx_cache_invalidate_list(domx_t *dt, list_head_t *oids);
int domx_cache_getdirtylist(domx_t *dt, uint16_t amount, list_head_t **list);
int domx_cache_freelist(domx_t *dt, list_head_t *list);
int domx_cache_freeitems(domx_t *dt, uint16_t amount);
/* data filters */
typedef enum {
YDEQUAL = 0,
YDNOTEQUAL,
YDGREATER,
YDLESS,
YDEQOGREATER,
YDEQOLESS,
YDINRANGE,
YDINLIST,
} filt_t;
typedef struct __yd_inlist_item_type {
union {
uint64_t val;
char *dta;
};
list_node_t node;
}__attribute__((packed)) yd_inlist_item_t;
typedef struct __yd_filter_item_type {
char *name; /* dataobject item name ptr */
filt_t ftype; /* filtering type */
union {
struct {
uint64_t vf; /* first value */
uint64_t vc; /* second value if applicable */
};
struct {
char *cstr;
list_head_t *inlist;
};
};
list_node_t node;
}__attribute__((packed)) yd_filter_item_t;
typedef struct __yd_filter_type {
domx_t *domx;
list_head_t filter;
} yd_filter_t;
yd_filter_t *yd_filter_create(domx_t *refobj);
void yd_filter_destroy(yd_filter_t *f);
int yd_filter_add_sf(yd_filter_t *f, const char *name, filt_t ftype,
uint64_t vf, uint64_t vc);
int yd_filter_add_str(yd_filter_t *f, const char *name, const char *value, int eq);
int yd_filter_add_inlist(yd_filter_t *f, const char *name, int length, void **array);
#define YD_CHUNK_AMOUNT 128
typedef struct __yd_wlist_node_type {
oid_t oid;
list_node_t node;
} yd_wlist_node_t;
typedef struct __yd_index_stream_type {
yd_filter_t *filter;
uint32_t offset;
uint8_t amount;
list_head_t entries_wlist;
void *priv;
} yd_idx_stream_t;
#define yd_index_stream_setfilter(sw, filt) (sw)->filter = (filt)
#define yd_index_stream_setpriv(sw, prv) (sw)->priv = prv
#define yd_index_stream_getpriv(sw) (sw)->priv
yd_idx_stream_t *yd_index_stream_init(void);
void yd_index_stream_destroy(yd_idx_stream_t *);
void yd_index_stream_emptylist(yd_idx_stream_t *);
typedef struct __yd_index_stream_window_type {
uint8_t amount;
list_head_t *wlist;
} yd_idx_stream_win_t;
#define yd_idx_stream_win_size(sw) (sw)->amount
/* domx stream operations */
yd_idx_stream_t *domx_idxl_open(domx_t *dt, ydm_access_filter_t afilter,
dataacc_pemctx_t *dacc, yd_filter_t *datafilter);
void domx_idxl_close(domx_t *dt, yd_idx_stream_t *idxl);
yd_idx_stream_win_t *domx_idxl_read(domx_t *dt, yd_idx_stream_t *idxl);
struct be_ops;
#define BEMAGIC 0xffffbeef
typedef struct __domx_dsbe_type {
domx_t *domx;
void *priv;
struct be_ops *f;
} domx_dsbe_t;
struct be_ops {
uint32_t be_magic;
/* initialization part */
int (*init)(domx_dsbe_t *, const char *);
/* data manipulation */
int (*get)(domx_dsbe_t *, oid_t, void *);
int (*set)(domx_dsbe_t *, oid_t, void *);
oid_t (*creat)(domx_dsbe_t *, const void *);
int (*remove)(domx_dsbe_t *, oid_t);
/* list requesting */
yd_idx_stream_t* (*create_idx_stream)(domx_dsbe_t *, ydm_access_filter_t,
dataacc_pemctx_t *, yd_filter_t *);
void (*destroy_idx_stream)(yd_idx_stream_t *);
yd_idx_stream_win_t* (*getportion_idx_stream)(yd_idx_stream_t *);
};
#endif /* __YD_DATAOBJECT_H__ */

@ -15,7 +15,8 @@ lib_LTLIBRARIES = libydaemon.la
libydaemon_la_SOURCES = \
values.c functions.c log.c module.c \
daemon.c context.c
daemon.c context.c data.c cache.c \
domx.c filter.c idxstream.c
libydaemon_la_LDFLAGS = -Wl,--export-dynamic

@ -0,0 +1,465 @@
/*
* Yet another daemon library especially designed to be used
* with libsxmp based daemons.
*
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
*
* libsxmp is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libsxmp is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#include <sys/mman.h>
#include <unistd.h>
#include <time.h>
#include <ydaemon/dataobject.h>
#include <ydaemon/cache.h>
static long __cmp_oids(const void *a, const void *b)
{
return (long)(*(oid_t *)a - *(oid_t *)b);
}
static ydata_cache_item_t *__alloc_ydcitem(uint32_t idx)
{
ydata_cache_item_t *out = malloc(sizeof(ydata_cache_item_t));
if(out) {
memset(out, 0, sizeof(ydata_cache_item_t));
out->idx = idx;
out->attr |= YDC_UNUSED;
list_init_node(&out->listnode);
usrtc_node_init(&out->idxnode, out);
}
return out;
}
static void *__sync_thread(void *p);
int yd_cache_start_thread(domx_t *domx)
{
ydata_cache_t *cc;
if(!domx || !(cc = (ydata_cache_t *)domx->cache)) return EINVAL;
return pthread_create(&cc->syncthread, NULL, __sync_thread, (void *)domx);
}
static void *__sync_thread(void *p)
{
domx_t *domx = (domx_t *)p;
ydata_cache_t *cc;
domx_dsbe_t *be;
list_node_t *iter, *siter;
ydata_cache_item_t *citem;
struct timespec tio;
int r, c, o;
/* check up, we shouldn't falls to segmentation */
if(!p || !(cc = (ydata_cache_t *)domx->cache)) goto __fini;
if(!(be = domx->be)) goto __fini;
/* everyday life is here */
while(1) {
/* get our tio, yes it's not so green ... but it works */
tio.tv_sec = time(NULL) + YDC_SYNCDELTASEC;
/* lock on mutex for a time being or will wake up on request */
r = pthread_mutex_timedlock(&cc->dirtlock, &tio);
__retrywolock:
yd_cache_rdlock(cc);
if(!cc->dirties) {
yd_cache_unlock(cc);
goto __again;
}
yd_cache_unlock(cc);
yd_cache_wrlock(cc);
c = 0;
list_for_each_safe(&cc->dirty_poll, iter, siter) {
citem = container_of(iter, ydata_cache_item_t, listnode);
list_del(&citem->listnode);
if(citem->attr & YDC_INVALIDATE) { /* invalidated item, just pullback it */
citem->attr = 0;
citem->attr |= YDC_UNUSED;
list_add2tail(&cc->free_poll, &citem->listnode);
usrtc_delete(&cc->idx_tree, &citem->idxnode);
} else {
o = be->f->set(be, citem->oid, yd_cache_ptrbyidx(cc, citem->idx));
if(o != 0) { /* invalidate */
citem->attr = 0;
citem->attr |= YDC_UNUSED;
list_add2tail(&cc->free_poll, &citem->listnode);
usrtc_delete(&cc->idx_tree, &citem->idxnode);
} else citem->attr &= ~YDC_DIRTY; /* just remove this flag */
}
cc->dirties--;
c++;
if(c >= YDC_SYNCTHRESHOLD) break;
}
yd_cache_unlock(cc);
if(c >= YDC_SYNCTHRESHOLD) {
usleep(100); /* looks like a huge load, let's wait and retry without lock */
goto __retrywolock;
}
__again:
if(r != ETIMEDOUT) pthread_mutex_lock(&cc->dirtlock); /* acquire lock again */
}
__fini:
return NULL;
}
ydata_cache_t *yd_cache_init(dataobject_t *object, size_t cache_size)
{
ydata_cache_t *cache = NULL;
char *iter;
ydata_cache_item_t *zcitem;
list_node_t *it, *sit;
int r = 0;
size_t i;
if(!object || !cache_size) {
errno = EINVAL;
goto __fail;
}
/* allocate and zero structure */
if(!(cache = malloc(sizeof(ydata_cache_t)))) {
errno = ENOMEM;
goto __fail;
} else memset(cache, 0, sizeof(ydata_cache_t));
/* map a memory buffer */
cache->cache = mmap(NULL, cache_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if(cache->cache == MAP_FAILED) {
errno = ENOMEM;
goto __fail;
}
/* allocate all the locks */
if((r = pthread_rwlock_init(&cache->rwlock, NULL))) {
errno = r;
goto __fail;
}
if((r = pthread_mutex_init(&cache->dirtlock, NULL))) {
pthread_rwlock_destroy(&cache->rwlock);
errno = r;
goto __fail;
}
/* init other values */
cache->cache_size = cache_size;
cache->object_size = dtolen(object);
/* some paranoic check up */
if(!cache->object_size) {
pthread_rwlock_destroy(&cache->rwlock);
pthread_mutex_destroy(&cache->dirtlock);
errno = EINVAL;
goto __fail;
}
/* well, able to continue ... */
cache->objects_amount = cache->cache_size/(cache->object_size + sizeof(uint32_t));
/* init structures */
usrtc_init(&cache->idx_tree, USRTC_SPLAY, cache->objects_amount, __cmp_oids); /* indexing one */
list_init_head(&cache->free_poll);
list_init_head(&cache->dirty_poll);
list_init_head(&cache->pending_poll);
/* mark i.e. format our memory buffer, also it will populate the pages */
for(i = 0, iter = (char *)cache->cache; i < cache->objects_amount;
i++, iter += cache->object_size + sizeof(uint32_t)) {
/* mark with spacer */
*(uint32_t *)iter = YD_CACHE_SPACER_MAGIC;
zcitem = __alloc_ydcitem(i);
if(!zcitem) { errno = ENOMEM; goto __failall; }
list_add2tail(&cache->free_poll, &zcitem->listnode);
}
/* lock the mutex */
pthread_mutex_lock(&cache->dirtlock);
return cache;
__failall:
pthread_rwlock_destroy(&cache->rwlock);
pthread_mutex_destroy(&cache->dirtlock);
__fail:
/* failcase freeing */
if(cache) {
/* memory buffer */
if(cache->cache != MAP_FAILED || cache->cache != NULL) {
/* if we have a buffer, we might have some items already allocated ... */
list_for_each_safe(&cache->free_poll, it, sit) {
zcitem = container_of(it, ydata_cache_item_t, listnode);
list_del(&zcitem->listnode);
free(zcitem);
}
munmap(cache->cache, cache_size);
}
free(cache);
}
return NULL; /* sadly ... */
}
/* allocating is quite simple:
* we're trying to get a free item, if it
* exists, we're moving this the the pending poll,
* otherwise return nil.
*/
void *yd_cache_alloc_item(ydata_cache_t *cache, ydata_cache_item_t **item)
{
char *data = NULL;
list_node_t *listnode;
ydata_cache_item_t *zcitem;
yd_cache_rdlock(cache);
listnode = list_node_first(&cache->free_poll);
if(listnode) {
yd_cache_unlock(cache); /* retake locks */
yd_cache_wrlock(cache);
zcitem = container_of(listnode, ydata_cache_item_t, listnode);
list_del(&zcitem->listnode); /* delete from free poll */
list_add2tail(&cache->pending_poll, &zcitem->listnode); /* add to the pending poll */
/* do the magic */
data = yd_cache_ptrbyidx(cache, zcitem->idx);
/* mark it invalidate - since we don't know what will be in future */
zcitem->attr |= YDC_INVALIDATE;
*item = zcitem;
}
yd_cache_unlock(cache);
/* ok. we'll do some automagically check for the near items safety */
if(data && *(uint32_t *)data != YD_CACHE_SPACER_MAGIC ) { /* currupted - mark near elements invalidated ! */
/* TODO: do it */
}
if(data) {
data += sizeof(uint32_t); /* bypass magic spacer */
return (void *)data;
}
return NULL;
}
void yd_cache_discard_alloc_item(ydata_cache_t *cache, ydata_cache_item_t *item)
{
if(!(item->attr & YDC_INVALIDATE)) return; /* some shit happens */
yd_cache_wrlock(cache);
list_del(&item->listnode); /* delete from panding poll - guess, u can use API ugly and dirty */
list_add2tail(&cache->free_poll, &item->listnode); /* add to the pending poll */
item->attr &= ~YDC_INVALIDATE; /* clear the invalidate bit */
yd_cache_unlock(cache);
return;
}
void yd_cache_confirm_alloc_item(ydata_cache_t *cache, ydata_cache_item_t *item, oid_t oid)
{
if(!(item->attr & YDC_INVALIDATE)) return; /* some shit happens */
yd_cache_wrlock(cache);
list_del(&item->listnode); /* delete from pending poll - guess, u can use API ugly and dirty */
item->attr &= ~YDC_INVALIDATE; /* clear the invalidate bit */
item->attr &= ~YDC_UNUSED; /* clear the use bit */
yd_item_setoid(item, oid);
usrtc_insert(&cache->idx_tree, &item->idxnode, (void *)&item->oid); /* now it will be in the splay tree of the active cached data */
yd_cache_unlock(cache);
return;
}
void *yd_cache_lookup(ydata_cache_t *cache, oid_t oid)
{
usrtc_node_t *node;
ydata_cache_item_t *item;
void *ret = NULL;
yd_cache_rdlock(cache);
node = usrtc_lookup(&cache->idx_tree, &oid);
if(node) {
item = (ydata_cache_item_t *)usrtc_node_getdata(node);
if(!(item->attr & YDC_INVALIDATE)) ret = yd_cache_ptrbyidx(cache, yd_item_idx(item));
else if(item->attr & YDC_INVALIDATE) { /* invalidated item */
yd_cache_unlock(cache); /* retake lock */
yd_cache_wrlock(cache);
usrtc_delete(&cache->idx_tree, &item->idxnode);
if(item->attr & YDC_DIRTY) { list_del(&item->listnode); /* it was dirty - but invalidated, remove it */
cache->dirties--;
}
item->attr = 0; /* clear attributes */
item->attr |= YDC_UNUSED;
list_add2tail(&cache->free_poll, &item->listnode);
ret = NULL;
}
}
yd_cache_unlock(cache);
if(ret && *(uint32_t *)ret != YD_CACHE_SPACER_MAGIC ) { /* currupted - mark near elements invalidated ! */
/* TODO: do it */
}
if(ret) ret += sizeof(uint32_t);
return ret;
}
static inline ydata_qitem_t *__quickitem_alloc(ydata_cache_item_t *zitem)
{
ydata_qitem_t *itm = malloc(sizeof(ydata_qitem_t));
if(itm) {
list_init_node(&itm->node);
itm->item = zitem;
}
return itm;
}
list_head_t *yd_cache_getrslist(ydata_cache_t *cache, uint8_t amount)
{
list_head_t *head = malloc(sizeof(list_head_t));
usrtc_node_t *node;
list_node_t *_i, *_si;
ydata_qitem_t *itm;
ydata_cache_item_t *zitem;
int i;
if(!head) return NULL;
if(!cache || !amount) goto __failcase; /* little bit of paranoic check */
yd_cache_wrlock(cache);
for(i = 0, node = usrtc_last(&cache->idx_tree);
i < amount;
i++, node = usrtc_last(&cache->idx_tree)) {
if(!node) break; /* pfff */
zitem = (ydata_cache_item_t *)usrtc_node_getdata(node);
/* create an quick entry */
if(!(itm = __quickitem_alloc(zitem))) goto __failcase_hasent;
/* delete from the index tree */
usrtc_delete(&cache->idx_tree, &zitem->idxnode);
list_add2tail(head, &itm->node);
}
yd_cache_unlock(cache);
return head;
__failcase_hasent:
if(head) {
list_for_each_safe(head, _i, _si) {
itm = container_of(_i, ydata_qitem_t, node);
zitem = itm->item;
if(zitem->attr & YDC_INVALIDATE) { /* oops, invalidated item found ! */
if(zitem->attr & YDC_DIRTY) {
list_del(&zitem->listnode);
cache->dirties--;
}
list_add2tail(&cache->free_poll, &zitem->listnode);
zitem->attr = 0;
zitem->attr |= YDC_UNUSED;
} else /* return back */
usrtc_insert(&cache->idx_tree, &zitem->idxnode, (void *)&zitem->oid);
list_del(&itm->node);
free(itm);
}
yd_cache_unlock(cache);
}
__failcase:
if(head) free(head);
return NULL;
}
void yd_cache_qitems_pullback(ydata_cache_t *cache, list_head_t *list)
{
list_node_t *iter, *siter;
ydata_qitem_t *itm;
ydata_cache_item_t *zitem;
yd_cache_wrlock(cache);
list_for_each_safe(list, iter, siter) {
itm = container_of(iter, ydata_qitem_t, node);
zitem = itm->item;
if(zitem->attr & YDC_DIRTY) {
list_del(&zitem->listnode);
cache->dirties--;
}
zitem->attr = 0; /* reset flags */
zitem->attr |= YDC_UNUSED;
list_add2tail(&cache->free_poll, &zitem->listnode);
/* free it at all */
list_del(&itm->node);
free(itm);
}
yd_cache_unlock(cache);
free(list);
return;
}
void yd_cache_item_dirtyoid(ydata_cache_t *cache, oid_t oid)
{
usrtc_node_t *node;
ydata_cache_item_t *zitem;
yd_cache_wrlock(cache);
node = usrtc_lookup(&cache->idx_tree, &oid);
if(node) {
zitem = (ydata_cache_item_t *)usrtc_node_getdata(node);
if(!(zitem->attr & YDC_DIRTY)) {
list_add2tail(&cache->dirty_poll, &zitem->listnode);
zitem->attr |= YDC_DIRTY;
cache->dirties++;
/* schedule sync thread, since threshold of dirty items occured */
if(cache->dirties >= YDC_SYNCTHRESHOLD) pthread_mutex_unlock(&cache->dirtlock);
}
}
yd_cache_unlock(cache);
return;
}
void yd_cache_item_invalidateoid(ydata_cache_t *cache, oid_t oid)
{
usrtc_node_t *node;
ydata_cache_item_t *zitem;
yd_cache_wrlock(cache);
node = usrtc_lookup(&cache->idx_tree, &oid);
if(node) {
zitem = (ydata_cache_item_t *)usrtc_node_getdata(node);
if(!(zitem->attr & YDC_INVALIDATE)) zitem->attr |= YDC_INVALIDATE;
}
yd_cache_unlock(cache);
return;
}

@ -0,0 +1,226 @@
/*
* Yet another daemon library especially designed to be used
* with libsxmp based daemons.
*
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
*
* libsxmp is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libsxmp is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#include <ydaemon/dataobject.h>
static long __cmp_uint8(const void *a, const void *b)
{
return *(uint8_t *)a - *(uint8_t *)b;
}
static long __cmp_cstr(const void *a, const void *b)
{
return strcmp((const char *)a, (const char *)b);
}
dataobject_t *dotr_create(const char *name)
{
dataobject_t *dtr = malloc(sizeof(dataobject_t));
if(!dtr) return NULL;
else memset(dtr, 0, sizeof(dataobject_t));
/* init name, indexes etc ... */
strncpy(dtr->name, name, MAX_ITEM_NAME - sizeof(char));
list_init_head(&dtr->description);
usrtc_init(&dtr->id_index, USRTC_REDBLACK, MAX_ITEMS, __cmp_uint8);
usrtc_init(&dtr->name_index, USRTC_REDBLACK, MAX_ITEMS, __cmp_cstr);
return dtr;
}
int dotr_set_item(dataobject_t *dotr, uint8_t type, uint64_t len,
const char *name)
{
dataobject_item_t *ditem = malloc(sizeof(dataobject_item_t));
char *indexes = malloc(2*sizeof(item_idx_t));
item_idx_t *iidx, *nidx;
if(!ditem || !indexes) {
if(ditem) free(ditem);
if(indexes) free(indexes);
return ENOMEM;
} else {
iidx = (item_idx_t *)indexes;
nidx = (item_idx_t *)(indexes + sizeof(item_idx_t));
}
/* init items */
strncpy(ditem->name, name, MAX_ITEM_NAME - sizeof(char));
ditem->id = usrtc_count(&dotr->id_index);
ditem->type = type;
ditem->len = len;
list_init_node(&ditem->node);
iidx->key = &ditem->id;
iidx->dtr = ditem;
usrtc_node_init(&iidx->node, iidx);
nidx->key = ditem->name;
nidx->dtr = ditem;
usrtc_node_init(&nidx->node, nidx);
/* now insert all */
list_add2tail(&dotr->description, &ditem->node);
usrtc_insert(&dotr->id_index, &iidx->node, iidx->key);
usrtc_insert(&dotr->name_index, &nidx->node, nidx->key);
return 0;
}
void dotr_destroy(dataobject_t *dotr)
{
usrtc_t *tree = &dotr->id_index;
usrtc_node_t *node = NULL;
item_idx_t *idx;
list_node_t *iter, *save;
dataobject_item_t *ditem;
if(usrtc_count(tree)) { /* we can free only index, since it was allocated as one memory chunk */
for(node = usrtc_first(tree); node != NULL; node = usrtc_first(tree)) {
idx = (item_idx_t *)usrtc_node_getdata(node);
usrtc_delete(tree, node);
free(idx);
}
/* free description */
list_for_each_safe(&dotr->description, iter, save) {
ditem = container_of(iter, dataobject_item_t, node);
list_del(&ditem->node);
free(ditem);
}
}
free(dotr);
return;
}
uint8_t dotr_item_type(dataobject_t *obj, const char *name, uint64_t *len)
{
usrtc_node_t *node = usrtc_lookup(&obj->name_index, name);
item_idx_t *idxnode;
dataobject_item_t *dtr = NULL;
if(!node) { /* not found ! */
__failed:
*len = 0;
return 0;
}
idxnode = (item_idx_t *)usrtc_node_getdata(node);
if(!idxnode) goto __failed;
else dtr = idxnode->dtr;
if(!dtr) goto __failed;
*len = dtr->len;
return dtr->type;
}
char *dotr_item_nameptr(dataobject_t *obj, const char *name)
{
usrtc_node_t *node = usrtc_lookup(&obj->name_index, name);
item_idx_t *idxnode;
dataobject_item_t *dtr = NULL;
if(!node) /* not found ! */ return NULL;
idxnode = (item_idx_t *)usrtc_node_getdata(node);
if(!idxnode) return NULL;
else dtr = idxnode->dtr;
if(!dtr) return NULL;
return dtr->name;
}
int dotr_item_nameidx(dataobject_t *obj, const char *name)
{
usrtc_node_t *node = usrtc_lookup(&obj->name_index, name);
item_idx_t *idxnode;
dataobject_item_t *dtr = NULL;
if(!node) /* not found ! */ return -1;
idxnode = (item_idx_t *)usrtc_node_getdata(node);
if(!idxnode) return -1;
else dtr = idxnode->dtr;
if(!dtr) return -1;
return dtr->id;
}
static inline int __itemcmp(const dataobject_item_t *s1,
const dataobject_item_t *s2)
{
int r = 0;
if((r = strcmp(s1->name, s2->name))) return r;
if((r = s1->len - s2->len)) return r;
if((r = s1->type - s2->type)) return r;
return 0;
}
int dtocmp(const dataobject_t *src, const dataobject_t *dst)
{
int r = 0;
list_node_t *iter, *siter;
dataobject_item_t *ditem, *sitem;
usrtc_node_t *unode;
item_idx_t *iidx;
/* check name */
if((r = strcmp(src->name, dst->name))) return r;
/* check amount of items */
if((r = usrtc_count((usrtc_t *)&src->id_index) - usrtc_count((usrtc_t *)&dst->id_index)))
return r;
/* check items itself */
list_for_each_safe(&src->description, iter, siter) {
ditem = container_of(iter, dataobject_item_t, node);
if(!(unode = usrtc_lookup((usrtc_t *)&dst->id_index, (const void *)&ditem->id)))
return 100*ditem->id;
iidx = (item_idx_t *)usrtc_node_getdata(unode);
sitem = iidx->dtr;
if((r = __itemcmp(ditem, sitem))) return r*ditem->id;
}
return 0;
}
size_t dtolen(const dataobject_t *src)
{
size_t len = 0;
list_node_t *iter, *siter;
dataobject_item_t *ditem;
list_for_each_safe(&src->description, iter, siter) {
ditem = container_of(iter, dataobject_item_t, node);
len += ditem->len;
}
return len;
}

@ -0,0 +1,249 @@
/*
* Yet another daemon library especially designed to be used
* with libsxmp based daemons.
*
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
*
* libsxmp is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libsxmp is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#include <ydaemon/dataobject.h>
#include <ydaemon/cache.h>
int domx_init(domx_t *dt, dataobject_t *dodesc)
{
if(!dt || !dodesc) return EINVAL;
/* init first values */
dt->dox = dodesc;
dt->name = dodesc->name;
dt->be = dt->cache = NULL;
/* init list for sntls lists */
list_init_head(&dt->sxmp_channels);
return 0;
}
int domx_set_be(domx_t *dt, void *be, const char *key)
{
struct be_ops *f = (struct be_ops *)be;
domx_dsbe_t *dbe;
if(!dt) return EINVAL;
if(!f || f->be_magic != BEMAGIC) return EINVAL;
if(!(dbe = malloc(sizeof(domx_dsbe_t)))) return ENOMEM;
dbe->domx = dt;
dbe->priv = NULL;
dbe->f = f;
dt->be = dbe;
return dbe->f->init(dbe, key);
}
int domx_set_cache(domx_t *dt, size_t size)
{
int r = 0;
/* paranoic check */
if(!dt || !dt->dox) return EINVAL;
if(!(dt->cache = (void *)yd_cache_init(dt->dox, size)))
r = errno;
else r = yd_cache_start_thread(dt);
return r;
}
int domx_set_sxmpchannel(domx_t *dt, const char *instance, int typeid, int access)
{
sxmp_ch_entry_t *sentry = NULL;
if(!dt || !instance) return EINVAL;
if(!(sentry = malloc(sizeof(sxmp_ch_entry_t)))) return ENOMEM;
/* init sntl channel entry */
strncpy(sentry->instance, instance, 31);
sentry->chtid = (uint16_t)typeid;
sentry->filter = (ydm_access_filter_t)access;
list_init_node(&sentry->node);
/* add this entry */
list_add2tail(&dt->sxmp_channels, &sentry->node);
return 0;
}
#define __ITEMSTOFREED 32
int domx_get(domx_t *dt, uint64_t oid, void **refdata)
{
void *data;
ydata_cache_item_t *itm;
ydata_qitem_t *qitem;
domx_dsbe_t *be;
list_head_t *rsitemslist = NULL;
list_node_t *iter, *siter;
int r = 0;
/* paranoic check up */
if(!dt || !dt->cache) return EINVAL;
if(!(be = dt->be)) return EINVAL;
__retry: /* yep, that's here, because anybody can inject our data while we're trying to refresh cache */
data = yd_cache_lookup((ydata_cache_t *)dt->cache, oid);
if(data) *refdata = data;
else { /* cache miss occured */
/* first allocate the required item within the cache */
data = yd_cache_alloc_item((ydata_cache_t *)dt->cache, &itm);
if(data) { /* we have a free entries ! */
if((r = be->f->get(be, oid, data))) yd_cache_discard_alloc_item((ydata_cache_t *)dt->cache, itm);
else {/* all is fine - confirm cache insertion */
yd_cache_confirm_alloc_item((ydata_cache_t *)dt->cache, itm, oid);
*refdata = data;
}
} else { /* we don't have free entries */
if(!(rsitemslist = yd_cache_getrslist((ydata_cache_t *)dt->cache, __ITEMSTOFREED)))
{ r = ENOMEM; goto __fini; }
/* ok, now we're ready to do something - we have unplugged items clean it up then */
list_for_each_safe(rsitemslist, iter, siter) {
qitem = container_of(iter, ydata_qitem_t, node);
itm = qitem->item;
if(itm->attr & YDC_DIRTY) /* if dirty - sync, but we don't care result here - if corrupted - data is totally corrupteed*/
be->f->set(be, itm->oid, (yd_cache_ptrbyidx((ydata_cache_t *)dt->cache, itm->idx)) + sizeof(uint32_t));
}
/* now we need to return there list back to the cache and retry */
yd_cache_qitems_pullback((ydata_cache_t *)dt->cache, rsitemslist);
goto __retry;
}
}
__fini:
return r;
}
int domx_set(domx_t *dt, uint64_t oid, const void *refdata)
{
domx_dsbe_t *be;
void *data;
ydata_cache_t *cc;
int r = 0;
/* paranoic check up */
if(!dt || !dt->cache) return EINVAL;
if(!(be = dt->be)) return EINVAL;
cc = (ydata_cache_t *)dt->cache;
data = yd_cache_lookup(cc, oid);
__again:
if(data) { /* we have those object with this oid in cache */
memcpy(data, refdata, cc->object_size); /* copy object data */
yd_cache_item_dirtyoid(cc, oid); /* just point to the cache - dirty */
} else { /* we're trying to set invalidated data */
r = domx_get(dt, oid, &data);
if(!r) goto __again;
}
return r;
}
int domx_remove(domx_t *dt, uint64_t oid)
{
domx_dsbe_t *be;
void *data;
ydata_cache_t *cc;
/* paranoic check up */
if(!dt || !dt->cache) return EINVAL;
if(!(be = dt->be)) return EINVAL;
cc = (ydata_cache_t *)dt->cache;
data = yd_cache_lookup(cc, oid);
if(data) yd_cache_item_invalidateoid(cc, oid);
return be->f->remove(be, oid);
}
int domx_creat(domx_t *dt, uint64_t *oid, const void *refdata)
{
domx_dsbe_t *be;
oid_t r = 0;
/* paranoic check up */
if(!dt || !dt->cache) return EINVAL;
if(!(be = dt->be)) return EINVAL;
/* here we just create this one and nothing */
r = be->f->creat(be, refdata);
if(!r) return errno;
*oid = r;
return 0;
}
yd_idx_stream_t *domx_idxl_open(domx_t *dt, ydm_access_filter_t afilter,
dataacc_pemctx_t *dacc, yd_filter_t *datafilter)
{
domx_dsbe_t *be;
/* paranoic check up */
if(!dt || !dt->cache) {
__einval:
errno = EINVAL;
return NULL;
}
if(!(be = dt->be)) goto __einval;
return be->f->create_idx_stream(be, afilter, dacc, datafilter);
}
void domx_idxl_close(domx_t *dt, yd_idx_stream_t *idxl)
{
domx_dsbe_t *be;
/* paranoic check up */
if(!dt || !dt->cache) {
__einval:
errno = EINVAL;
return;
}
if(!(be = dt->be) || !idxl) goto __einval;
be->f->destroy_idx_stream(idxl);
}
yd_idx_stream_win_t *domx_idxl_read(domx_t *dt, yd_idx_stream_t *idxl)
{
domx_dsbe_t *be;
/* paranoic check up */
if(!dt || !dt->cache) {
__einval:
errno = EINVAL;
return NULL;
}
if(!(be = dt->be) || !idxl) goto __einval;
return be->f->getportion_idx_stream(idxl);
}

@ -0,0 +1,219 @@
/*
* Yet another daemon library especially designed to be used
* with libsxmp based daemons.
*
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
*
* libsxmp is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libsxmp is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#define DMUX_USE 1
#include <ydaemon/dataobject.h>
yd_filter_t *yd_filter_create(domx_t *refobj)
{
yd_filter_t *f = malloc(sizeof(yd_filter_t));
if(f) {
f->domx = refobj;
list_init_head(&f->filter);
}
return f;
}
void yd_filter_destroy(yd_filter_t *f)
{
list_head_t *lhead;
list_node_t *iter, *siter, *liter, *lsiter;
yd_filter_item_t *filter_item;
yd_inlist_item_t *inl_item;
dataobject_t *obj;
uint64_t len;
uint8_t type;
if(!f) return;
else {
lhead = &f->filter;
obj = f->domx->dox;
}
list_for_each_safe(lhead, iter, siter) {
filter_item = container_of(iter, yd_filter_item_t, node);
type = dotr_item_type(obj, filter_item->name, &len);
if(type && len) {
switch(filter_item->ftype) {
case YDEQUAL:
case YDNOTEQUAL:
if(type == CSTR || type == TBLOB) free(filter_item->cstr);
break;
case YDINLIST:
/* we must destroy the list */
list_for_each_safe(filter_item->inlist, liter, lsiter) {
inl_item = container_of(liter, yd_inlist_item_t, node);
if(type == CSTR || type == TBLOB) free(inl_item->dta);
list_del(&inl_item->node);
free(inl_item);
}
free(filter_item->inlist);
break;
default: break; /* nothing to do */
}
}
/* free struct itself */
list_del(&filter_item->node);
free(filter_item);
}
free(f);
return;
}
static yd_filter_item_t *__alloc_init_fitem(filt_t ftype, char *name)
{
yd_filter_item_t *fitem = NULL;
if(name && (fitem = malloc(sizeof(yd_filter_item_t)))) {
memset(fitem, 0, sizeof(yd_filter_item_t));
fitem->name = name;
fitem->ftype = ftype;
list_init_node(&fitem->node);
}
return fitem;
}
int yd_filter_add_sf(yd_filter_t *f, const char *name, filt_t ftype,
uint64_t vf, uint64_t vc)
{
yd_filter_item_t *fitem;
dataobject_t *obj;
uint64_t len;
uint8_t type;
if(!f || !name) return EINVAL;
/* lookup for existing entry and check it */
obj = f->domx->dox;
type = dotr_item_type(obj, name, &len);
if(!type && !len) return EINVAL;
/* check validity */
if((type < CSTR) && (len > tltable[type].len)) return EINVAL; /* we cannot filter by array here */
if(type > S64) return EINVAL; /* we're adding a simple filter rule */
if(ftype == YDINLIST) return EINVAL; /* we're not working with that in this function */
fitem = __alloc_init_fitem(ftype, dotr_item_nameptr(obj, name));
if(!fitem) return ENOMEM;
fitem->vf = vf;
if(ftype == YDINLIST) fitem->vc = vc;
/* add this entry */
list_add2tail(&f->filter, &fitem->node);
return 0;
}
int yd_filter_add_str(yd_filter_t *f, const char *name, const char *value, int eq)
{
yd_filter_item_t *fitem;
dataobject_t *obj;
filt_t ftype;
uint64_t len;
uint8_t type;
if(!f || !name) return EINVAL;
/* lookup for existing entry and check it */
obj = f->domx->dox;
type = dotr_item_type(obj, name, &len);
if(!type && !len) return EINVAL;
if(type != CSTR) return EINVAL; /* cannot do it, invalid */
if(!eq) ftype = YDNOTEQUAL;
else ftype = YDEQUAL;
fitem = __alloc_init_fitem(ftype, dotr_item_nameptr(obj, name));
if(!fitem) return ENOMEM;
if(!(fitem->cstr = strndup(value, len - sizeof(char)))) {
free(fitem);
return ENOMEM;
}
/* add this entry */
list_add2tail(&f->filter, &fitem->node);
return 0;
}
int yd_filter_add_inlist(yd_filter_t *f, const char *name, int length, void **array)
{
yd_filter_item_t *fitem;
yd_inlist_item_t *list_item;
dataobject_t *obj;
list_node_t *iter, *siter;
uint64_t len;
int i;
uint8_t type;
if(!f || !name) return EINVAL;
if(!length || !array) return EINVAL;
/* lookup for existing entry and check it */
obj = f->domx->dox;
type = dotr_item_type(obj, name, &len);
if(!type && !len) return EINVAL;
fitem = __alloc_init_fitem(YDINLIST, dotr_item_nameptr(obj, name));
if(!fitem) return ENOMEM;
if(!(fitem->inlist = malloc(sizeof(list_head_t)))) {
__enomem:
free(fitem);
return ENOMEM;
}
/* fill the list */
for(i = 0; i < length; i++) {
if(!(list_item = malloc(sizeof(yd_inlist_item_t)))) {
__deep_enomem:
list_for_each_safe(fitem->inlist, iter, siter) {
list_item = container_of(iter, yd_inlist_item_t, node);
if(type == CSTR || type == TBLOB) free(list_item->dta);
list_del(&list_item->node);
free(list_item);
}
/* free the list */
free(fitem->inlist);
goto __enomem;
}
if(type < CSTR) list_item->val = *(uint64_t *)array[i];
else if(!(list_item->dta = strndup((const char *)array[i], len - sizeof(char)))) goto __deep_enomem;
list_init_node(&list_item->node);
list_add2tail(fitem->inlist, &list_item->node);
}
/* add this entry */
list_add2tail(&f->filter, &fitem->node);
return 0;
}

@ -0,0 +1,67 @@
/*
* Yet another daemon library especially designed to be used
* with libsxmp based daemons.
*
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
*
* libsxmp is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libsxmp is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#include <ydaemon/dataobject.h>
yd_idx_stream_t *yd_index_stream_init(void)
{
yd_idx_stream_t *is = malloc(sizeof(yd_idx_stream_t));
if(!is) return NULL;
else memset(is, 0, sizeof(yd_idx_stream_t));
list_init_head(&is->entries_wlist);
return is;
}
static inline void __empty_list(list_head_t *list)
{
list_node_t *iter, *siter;
yd_wlist_node_t *wlistnode;
list_for_each_safe(list, iter, siter){
wlistnode = container_of(iter, yd_wlist_node_t, node);
list_del(&wlistnode->node);
free(wlistnode);
}
return;
}
void yd_index_stream_destroy(yd_idx_stream_t *is)
{
if(!is) return;
else __empty_list(&is->entries_wlist);
free(is);
return;
}
void yd_index_stream_emptylist(yd_idx_stream_t *is)
{
if(!is) return;
else __empty_list(&is->entries_wlist);
return;
}
Loading…
Cancel
Save