mirror of
https://github.com/JHUAPL/kvspool.git
synced 2026-01-09 15:37:56 -05:00
qk
This commit is contained in:
30
qk/README.md
Normal file
30
qk/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
quick key (qk)
|
||||
--------------
|
||||
|
||||
This is a mini-kvspool that lets the application determine what to do with each
|
||||
key-value dictionary that is produced. First create one:
|
||||
|
||||
struct qk *qk = qk_new();
|
||||
|
||||
Set up a callback to be invoked whenever you "end" a dictionary:
|
||||
|
||||
qk->cb = your_callback;
|
||||
|
||||
The callback has this prototype:
|
||||
|
||||
int (*cb)(struct qk *);
|
||||
|
||||
The callback can use `qk->tmp` (a `UT_string`) as a scratch buffer. It can
|
||||
iterate over `qk->keys` and `qk->vals` (both of type `UT_vector` whose elements
|
||||
are `UT_string`). Use this sequence to produce a dictionary:
|
||||
|
||||
qk_start(qk);
|
||||
qk_add(qk, key, val, ...);
|
||||
...
|
||||
qk_end(qk);
|
||||
|
||||
Call `qk_add` multiple times to add several key-value pairs to the dictionary.
|
||||
Note `val` is a printf-style format string that can take additional arguments.
|
||||
At program termination do this:
|
||||
|
||||
qk_free(qk);
|
||||
393
qk/ext/utstring.h
Normal file
393
qk/ext/utstring.h
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* a dynamic string implementation using macros
|
||||
*/
|
||||
#ifndef UTSTRING_H
|
||||
#define UTSTRING_H
|
||||
|
||||
#define UTSTRING_VERSION 1.9.9
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define _UNUSED_ __attribute__ ((__unused__))
|
||||
#else
|
||||
#define _UNUSED_
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#define oom() exit(-1)
|
||||
|
||||
typedef struct {
|
||||
char *d;
|
||||
size_t n; /* allocd size */
|
||||
size_t i; /* index of first unused byte */
|
||||
} UT_string;
|
||||
|
||||
#define utstring_reserve(s,amt) \
|
||||
do { \
|
||||
if (((s)->n - (s)->i) < (size_t)(amt)) { \
|
||||
(s)->d = (char*)realloc((s)->d, (s)->n + amt); \
|
||||
if ((s)->d == NULL) oom(); \
|
||||
(s)->n += amt; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define utstring_init(s) \
|
||||
do { \
|
||||
(s)->n = 0; (s)->i = 0; (s)->d = NULL; \
|
||||
utstring_reserve(s,100); \
|
||||
(s)->d[0] = '\0'; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_done(s) \
|
||||
do { \
|
||||
if ((s)->d != NULL) free((s)->d); \
|
||||
(s)->n = 0; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_free(s) \
|
||||
do { \
|
||||
utstring_done(s); \
|
||||
free(s); \
|
||||
} while(0)
|
||||
|
||||
#define utstring_new(s) \
|
||||
do { \
|
||||
s = (UT_string*)calloc(sizeof(UT_string),1); \
|
||||
if (!s) oom(); \
|
||||
utstring_init(s); \
|
||||
} while(0)
|
||||
|
||||
#define utstring_renew(s) \
|
||||
do { \
|
||||
if (s) { \
|
||||
utstring_clear(s); \
|
||||
} else { \
|
||||
utstring_new(s); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define utstring_clear(s) \
|
||||
do { \
|
||||
(s)->i = 0; \
|
||||
(s)->d[0] = '\0'; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_bincpy(s,b,l) \
|
||||
do { \
|
||||
utstring_reserve((s),(l)+1); \
|
||||
if (l) memcpy(&(s)->d[(s)->i], b, l); \
|
||||
(s)->i += l; \
|
||||
(s)->d[(s)->i]='\0'; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_concat(dst,src) \
|
||||
do { \
|
||||
utstring_reserve((dst),((src)->i)+1); \
|
||||
if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \
|
||||
(dst)->i += (src)->i; \
|
||||
(dst)->d[(dst)->i]='\0'; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_len(s) ((unsigned)((s)->i))
|
||||
|
||||
#define utstring_body(s) ((s)->d)
|
||||
|
||||
_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) {
|
||||
int n;
|
||||
va_list cp;
|
||||
while (1) {
|
||||
#ifdef _WIN32
|
||||
cp = ap;
|
||||
#else
|
||||
va_copy(cp, ap);
|
||||
#endif
|
||||
n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp);
|
||||
va_end(cp);
|
||||
|
||||
if ((n > -1) && ((size_t) n < (s->n-s->i))) {
|
||||
s->i += n;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Else try again with more space. */
|
||||
if (n > -1) utstring_reserve(s,n+1); /* exact */
|
||||
else utstring_reserve(s,(s->n)*2); /* 2x */
|
||||
}
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
/* support printf format checking (2=the format string, 3=start of varargs) */
|
||||
static void utstring_printf(UT_string *s, const char *fmt, ...)
|
||||
__attribute__ (( format( printf, 2, 3) ));
|
||||
#endif
|
||||
_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
utstring_printf_va(s,fmt,ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* begin substring search functions *
|
||||
******************************************************************************/
|
||||
/* Build KMP table from left to right. */
|
||||
_UNUSED_ static void _utstring_BuildTable(
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen,
|
||||
long *P_KMP_Table)
|
||||
{
|
||||
long i, j;
|
||||
|
||||
i = 0;
|
||||
j = i - 1;
|
||||
P_KMP_Table[i] = j;
|
||||
while (i < (long) P_NeedleLen)
|
||||
{
|
||||
while ( (j > -1) && (P_Needle[i] != P_Needle[j]) )
|
||||
{
|
||||
j = P_KMP_Table[j];
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
if (i < (long) P_NeedleLen)
|
||||
{
|
||||
if (P_Needle[i] == P_Needle[j])
|
||||
{
|
||||
P_KMP_Table[i] = P_KMP_Table[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
P_KMP_Table[i] = j;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
P_KMP_Table[i] = j;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Build KMP table from right to left. */
|
||||
_UNUSED_ static void _utstring_BuildTableR(
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen,
|
||||
long *P_KMP_Table)
|
||||
{
|
||||
long i, j;
|
||||
|
||||
i = P_NeedleLen - 1;
|
||||
j = i + 1;
|
||||
P_KMP_Table[i + 1] = j;
|
||||
while (i >= 0)
|
||||
{
|
||||
while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) )
|
||||
{
|
||||
j = P_KMP_Table[j + 1];
|
||||
}
|
||||
i--;
|
||||
j--;
|
||||
if (i >= 0)
|
||||
{
|
||||
if (P_Needle[i] == P_Needle[j])
|
||||
{
|
||||
P_KMP_Table[i + 1] = P_KMP_Table[j + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
P_KMP_Table[i + 1] = j;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
P_KMP_Table[i + 1] = j;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Search data from left to right. ( Multiple search mode. ) */
|
||||
_UNUSED_ static long _utstring_find(
|
||||
const char *P_Haystack,
|
||||
size_t P_HaystackLen,
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen,
|
||||
long *P_KMP_Table)
|
||||
{
|
||||
long i, j;
|
||||
long V_FindPosition = -1;
|
||||
|
||||
/* Search from left to right. */
|
||||
i = j = 0;
|
||||
while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) )
|
||||
{
|
||||
while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) )
|
||||
{
|
||||
i = P_KMP_Table[i];
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
if (i >= (int)P_NeedleLen)
|
||||
{
|
||||
/* Found. */
|
||||
V_FindPosition = j - i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return V_FindPosition;
|
||||
}
|
||||
|
||||
|
||||
/* Search data from right to left. ( Multiple search mode. ) */
|
||||
_UNUSED_ static long _utstring_findR(
|
||||
const char *P_Haystack,
|
||||
size_t P_HaystackLen,
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen,
|
||||
long *P_KMP_Table)
|
||||
{
|
||||
long i, j;
|
||||
long V_FindPosition = -1;
|
||||
|
||||
/* Search from right to left. */
|
||||
j = (P_HaystackLen - 1);
|
||||
i = (P_NeedleLen - 1);
|
||||
while ( (j >= 0) && (j >= i) )
|
||||
{
|
||||
while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) )
|
||||
{
|
||||
i = P_KMP_Table[i + 1];
|
||||
}
|
||||
i--;
|
||||
j--;
|
||||
if (i < 0)
|
||||
{
|
||||
/* Found. */
|
||||
V_FindPosition = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return V_FindPosition;
|
||||
}
|
||||
|
||||
|
||||
/* Search data from left to right. ( One time search mode. ) */
|
||||
_UNUSED_ static long utstring_find(
|
||||
UT_string *s,
|
||||
long P_StartPosition, /* Start from 0. -1 means last position. */
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen)
|
||||
{
|
||||
long V_StartPosition;
|
||||
long V_HaystackLen;
|
||||
long *V_KMP_Table;
|
||||
long V_FindPosition = -1;
|
||||
|
||||
if (P_StartPosition < 0)
|
||||
{
|
||||
V_StartPosition = s->i + P_StartPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
V_StartPosition = P_StartPosition;
|
||||
}
|
||||
V_HaystackLen = s->i - V_StartPosition;
|
||||
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
|
||||
{
|
||||
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
|
||||
if (V_KMP_Table != NULL)
|
||||
{
|
||||
_utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table);
|
||||
|
||||
V_FindPosition = _utstring_find(s->d + V_StartPosition,
|
||||
V_HaystackLen,
|
||||
P_Needle,
|
||||
P_NeedleLen,
|
||||
V_KMP_Table);
|
||||
if (V_FindPosition >= 0)
|
||||
{
|
||||
V_FindPosition += V_StartPosition;
|
||||
}
|
||||
|
||||
free(V_KMP_Table);
|
||||
}
|
||||
}
|
||||
|
||||
return V_FindPosition;
|
||||
}
|
||||
|
||||
|
||||
/* Search data from right to left. ( One time search mode. ) */
|
||||
_UNUSED_ static long utstring_findR(
|
||||
UT_string *s,
|
||||
long P_StartPosition, /* Start from 0. -1 means last position. */
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen)
|
||||
{
|
||||
long V_StartPosition;
|
||||
long V_HaystackLen;
|
||||
long *V_KMP_Table;
|
||||
long V_FindPosition = -1;
|
||||
|
||||
if (P_StartPosition < 0)
|
||||
{
|
||||
V_StartPosition = s->i + P_StartPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
V_StartPosition = P_StartPosition;
|
||||
}
|
||||
V_HaystackLen = V_StartPosition + 1;
|
||||
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
|
||||
{
|
||||
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
|
||||
if (V_KMP_Table != NULL)
|
||||
{
|
||||
_utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table);
|
||||
|
||||
V_FindPosition = _utstring_findR(s->d,
|
||||
V_HaystackLen,
|
||||
P_Needle,
|
||||
P_NeedleLen,
|
||||
V_KMP_Table);
|
||||
|
||||
free(V_KMP_Table);
|
||||
}
|
||||
}
|
||||
|
||||
return V_FindPosition;
|
||||
}
|
||||
/*******************************************************************************
|
||||
* end substring search functions *
|
||||
******************************************************************************/
|
||||
|
||||
#endif /* UTSTRING_H */
|
||||
161
qk/ext/utvector.c
Normal file
161
qk/ext/utvector.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "utvector.h"
|
||||
|
||||
/* utvector
|
||||
*
|
||||
* maintain a contiguous buffer of 'n' elements ('i' occupied)
|
||||
* the 'n' buffers are deep-inited at the time of allocation
|
||||
* the vector leaves popped slots as-is, clearing them on re-use
|
||||
* the memory management helper mm is used to define the size and
|
||||
* deep-init, deep-fini, deep-copy (into inited slots) and deep-clear.
|
||||
* deep-clear prepares a slot for re-use e.g. reset slot state.
|
||||
*
|
||||
*/
|
||||
|
||||
void oom(void) {
|
||||
//fprintf(stderr,"out of memory\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
UT_vector *utvector_new(UT_vector_mm *mm) {
|
||||
UT_vector *v = malloc(sizeof(UT_vector)); if (!v) return NULL;
|
||||
utvector_init(v,mm);
|
||||
return v;
|
||||
}
|
||||
|
||||
unsigned utvector_len(UT_vector *v) {
|
||||
return v->i;
|
||||
}
|
||||
|
||||
void utvector_init(UT_vector *v, UT_vector_mm *mm) {
|
||||
v->mm = *mm; // struct copy
|
||||
v->i = v->n = 0;
|
||||
v->d = NULL;
|
||||
utvector_reserve(v, INITIAL_SIZE); // also inits them, sets v->n
|
||||
}
|
||||
|
||||
void utvector_reserve(UT_vector *v, unsigned num) {
|
||||
if (v->n - v->i >= num) return; // space is big enough, return
|
||||
unsigned n = num - (v->n - v->i); // minimum we need to grow by
|
||||
if (n < (v->n * 2)) n = (v->n * 2); // grow by at least double current size
|
||||
char *d = realloc(v->d, (n + v->n) * v->mm.sz);
|
||||
if (!d) oom();
|
||||
v->d = d;
|
||||
void *b = v->d + (v->n * v->mm.sz); // start of newly allocated area
|
||||
if (v->mm.init) v->mm.init(b, n);
|
||||
else memset(b, 0, n*v->mm.sz);
|
||||
v->n = n + v->n;
|
||||
}
|
||||
|
||||
void utvector_fini(UT_vector *v) {
|
||||
if (v->mm.fini) v->mm.fini(v->d, v->n);
|
||||
free(v->d);
|
||||
v->d = NULL;
|
||||
v->i = v->n = 0;
|
||||
}
|
||||
|
||||
UT_vector * utvector_clone(UT_vector *src) {
|
||||
UT_vector *v = utvector_new(&src->mm);
|
||||
utvector_copy(v, src);
|
||||
return v;
|
||||
}
|
||||
|
||||
void utvector_clear(UT_vector *v) {
|
||||
v->i = 0;
|
||||
}
|
||||
|
||||
void utvector_copy(UT_vector *dst, UT_vector *src) { /* dst, src both inited */
|
||||
assert(dst->mm.sz == src->mm.sz); // double check that its inited
|
||||
utvector_clear(dst);
|
||||
utvector_reserve(dst, src->i);
|
||||
dst->i = src->i;
|
||||
if (dst->mm.clear) dst->mm.clear(dst->d, src->i);
|
||||
if (src->mm.copy) src->mm.copy(dst->d, src->d, src->i);
|
||||
else memcpy(dst->d, src->d, src->mm.sz * src->i);
|
||||
}
|
||||
|
||||
void utvector_free(UT_vector *v) {
|
||||
utvector_fini(v);
|
||||
free(v);
|
||||
}
|
||||
|
||||
void *utvector_extend(UT_vector *v) {
|
||||
utvector_reserve(v,1);
|
||||
void *b = v->d + (v->i * v->mm.sz);
|
||||
if (v->mm.clear) v->mm.clear(b,1);
|
||||
v->i++;
|
||||
return b;
|
||||
}
|
||||
|
||||
void *utvector_next(UT_vector *v, void *cur) {
|
||||
if (cur == NULL) return v->i ? v->d : NULL;
|
||||
assert(cur >= (void*)(v->d)); // user pointer must be inside our data area
|
||||
char *n = (char*)cur + v->mm.sz; // next slot address
|
||||
if (n >= v->d + (v->i * v->mm.sz)) n=NULL; // only if next slot occupied
|
||||
return n;
|
||||
}
|
||||
|
||||
void *utvector_head(UT_vector *v) {
|
||||
if (v->i == 0) return NULL;
|
||||
return v->d;
|
||||
}
|
||||
|
||||
void *utvector_tail(UT_vector *v) {
|
||||
if (v->i == 0) return NULL;
|
||||
return v->d + ((v->i - 1) * v->mm.sz);
|
||||
}
|
||||
|
||||
void *utvector_pop(UT_vector *v) {
|
||||
if (v->i == 0) return NULL;
|
||||
return v->d + (--(v->i) * v->mm.sz);
|
||||
}
|
||||
|
||||
/* shifting is not very efficient. we end up throwing away/fini'ing the
|
||||
* head of the vector, then doing a memmove, then having to init a new slot.
|
||||
* we don't return the shifted item because its been fini'd, and we have
|
||||
* no caller memory to copy it into anyway. a cpy_shift maybe handy */
|
||||
void utvector_shift(UT_vector *v) {
|
||||
assert (v->i);
|
||||
if (v->mm.fini) v->mm.fini(v->d, 1);
|
||||
v->i--;
|
||||
memmove(v->d, v->d + v->mm.sz, (v->n-1)*v->mm.sz);
|
||||
char *b = v->d + ((v->n-1) * v->mm.sz);
|
||||
if (v->mm.init) v->mm.init(b, 1);
|
||||
else memset(b, 0, v->mm.sz);
|
||||
}
|
||||
|
||||
void utvector_push(UT_vector *v, void *e) {
|
||||
void *b = utvector_extend(v);
|
||||
if (v->mm.copy) v->mm.copy(b, e, 1);
|
||||
else memcpy(b, e, v->mm.sz);
|
||||
}
|
||||
|
||||
|
||||
/* a few basic vector types as described via mm that can be passed to utvector_init/new */
|
||||
static UT_vector_mm utvector_int_mm = {.sz = sizeof(int)};
|
||||
UT_vector_mm* utvector_int = &utvector_int_mm;
|
||||
76
qk/ext/utvector.h
Normal file
76
qk/ext/utvector.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* utvector
|
||||
*
|
||||
* maintain a contiguous buffer of 'n' elements ('i' occupied)
|
||||
* the 'n' buffers are deep-inited at the time of allocation
|
||||
* the vector leaves popped slots as-is, clearing them on re-use
|
||||
* the memory management helper mm is used to define the size and
|
||||
* deep-init, deep-fini, deep-copy (into inited slots) and deep-clear.
|
||||
* deep-clear prepares a slot for re-use e.g. reset slot state.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __UTVECTOR_H_
|
||||
#define __UTVECTOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define INITIAL_SIZE 16
|
||||
|
||||
typedef struct _UT_vector_mm {
|
||||
size_t sz;
|
||||
void (*init)(void *buf, unsigned num); //-> utstring-init
|
||||
void (*fini)(void *buf, unsigned num); //-> utstring-done
|
||||
void (*copy)(void *dst, void *src, unsigned num); //-> ustring_concat
|
||||
void (*clear)(void *buf, unsigned num); //-> utstring-clear
|
||||
} UT_vector_mm;
|
||||
|
||||
typedef struct _UT_vector {
|
||||
UT_vector_mm mm;
|
||||
unsigned i,n;/* i: index of next available slot, n: num slots */
|
||||
char *d; /* n slots of size icd->sz*/
|
||||
} UT_vector;
|
||||
|
||||
|
||||
UT_vector *utvector_new(UT_vector_mm *mm);
|
||||
void utvector_init(UT_vector *v, UT_vector_mm *mm);
|
||||
void utvector_reserve(UT_vector *v, unsigned num);
|
||||
void utvector_fini(UT_vector *v);
|
||||
UT_vector * utvector_clone(UT_vector *src);
|
||||
void utvector_clear(UT_vector *v);
|
||||
void utvector_copy(UT_vector *dst, UT_vector *src);
|
||||
void utvector_free(UT_vector *v);
|
||||
void *utvector_extend(UT_vector *v);
|
||||
void *utvector_head(UT_vector *v);
|
||||
void *utvector_tail(UT_vector *v);
|
||||
void *utvector_next(UT_vector *v, void *cur);
|
||||
void *utvector_pop(UT_vector *v);
|
||||
void utvector_shift(UT_vector *v);
|
||||
void utvector_push(UT_vector *v, void *e);
|
||||
unsigned utvector_len(UT_vector *v);
|
||||
|
||||
extern UT_vector_mm* utvector_int;
|
||||
|
||||
#endif /* __UTVECTOR_H_ */
|
||||
77
qk/qk.c
Normal file
77
qk/qk.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "qk.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* plumbing for utvector of string
|
||||
******************************************************************************/
|
||||
void _utstring_init(void *_buf, unsigned num) {
|
||||
UT_string *s = (UT_string*)_buf;
|
||||
while(num--) utstring_init(&s[num]);
|
||||
}
|
||||
void _utstring_fini(void *_buf, unsigned num) {
|
||||
UT_string *s = (UT_string*)_buf;
|
||||
while(num--) utstring_done(&s[num]);
|
||||
}
|
||||
void _utstring_copy(void *_dst, void *_src, unsigned num) {
|
||||
UT_string *dst = (UT_string*)_dst;
|
||||
UT_string *src = (UT_string*)_src;
|
||||
while(num--) utstring_concat( &dst[num], &src[num] );
|
||||
}
|
||||
void _utstring_clear(void *_buf, unsigned num) {
|
||||
UT_string *s = (UT_string*)_buf;
|
||||
while(num--) utstring_clear(&s[num]);
|
||||
}
|
||||
static UT_vector_mm utvector_utstring_mm = {
|
||||
.sz = sizeof(UT_string),
|
||||
.init = _utstring_init,
|
||||
.fini = _utstring_fini,
|
||||
.copy = _utstring_copy,
|
||||
.clear = _utstring_clear,
|
||||
};
|
||||
/*******************************************************************************
|
||||
* end of plumbing
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
struct qk *qk_new(void) {
|
||||
struct qk *qk = malloc(sizeof(*qk));
|
||||
if (qk == NULL) goto done;
|
||||
memset(qk,0,sizeof(*qk));
|
||||
utvector_init(&qk->keys, &utvector_utstring_mm);
|
||||
utvector_init(&qk->vals, &utvector_utstring_mm);
|
||||
utstring_init(&qk->tmp);
|
||||
done:
|
||||
return qk;
|
||||
}
|
||||
|
||||
int qk_start(struct qk *qk) {
|
||||
utvector_clear(&qk->keys);
|
||||
utvector_clear(&qk->vals);
|
||||
utstring_clear(&qk->tmp);
|
||||
}
|
||||
|
||||
int qk_end(struct qk *qk) {
|
||||
if (qk->cb == NULL) return;
|
||||
return qk->cb(qk);
|
||||
}
|
||||
|
||||
int qk_add(struct qk *qk, char *key, char *vfmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap,vfmt);
|
||||
UT_string *k = (UT_string*)utvector_extend(&qk->keys);
|
||||
UT_string *v = (UT_string*)utvector_extend(&qk->vals);
|
||||
utstring_bincpy(k,key,strlen(key));
|
||||
utstring_printf_va(v,vfmt,ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void qk_free(struct qk *qk) {
|
||||
utvector_fini(&qk->keys);
|
||||
utvector_fini(&qk->vals);
|
||||
utstring_done(&qk->tmp);
|
||||
free(qk);
|
||||
}
|
||||
|
||||
27
qk/qk.h
Normal file
27
qk/qk.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef _QK_H_
|
||||
#define _QK_H_
|
||||
|
||||
#include "utvector.h"
|
||||
#include "utstring.h"
|
||||
|
||||
struct qk {
|
||||
UT_vector /* of UT_string */ keys;
|
||||
UT_vector /* of UT_string */ vals;
|
||||
|
||||
/* the callback below is invoked on qk_end. it receives this struct.
|
||||
* the tmp is reserved for the callback to use a scratch space. the
|
||||
* data argument is opaque and is for passing state to the callback. */
|
||||
int (*cb)(struct qk *);
|
||||
UT_string tmp;
|
||||
void *data;
|
||||
|
||||
};
|
||||
|
||||
/* API */
|
||||
struct qk *qk_new(void);
|
||||
int qk_start(struct qk *qk);
|
||||
int qk_add(struct qk *qk, char *key, char *val, ...);
|
||||
int qk_end(struct qk *qk);
|
||||
void qk_free(struct qk *qk);
|
||||
|
||||
#endif // _QK_H_
|
||||
2
qk/tests/README
Normal file
2
qk/tests/README
Normal file
@@ -0,0 +1,2 @@
|
||||
test1: test basic kv_start, kv_add ..., kv_end
|
||||
test2: test multiple kv_add, kv_end
|
||||
21
qk/tests/do_tests
Executable file
21
qk/tests/do_tests
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my @tests;
|
||||
for (glob "test*[0-9]") {
|
||||
push @tests, $_ if -e "$_.ans";
|
||||
}
|
||||
|
||||
my $num_failed=0;
|
||||
|
||||
for my $test (@tests) {
|
||||
`./$test > $test.out`;
|
||||
`diff $test.out $test.ans`;
|
||||
print "$test failed\n" if $?;
|
||||
$num_failed++ if $?;
|
||||
}
|
||||
|
||||
print scalar @tests . " tests conducted, $num_failed failed.\n";
|
||||
exit $num_failed;
|
||||
6
qk/tests/test1.ans
Normal file
6
qk/tests/test1.ans
Normal file
@@ -0,0 +1,6 @@
|
||||
qk_end: 5 kv pairs
|
||||
A -> 1
|
||||
B -> 2a
|
||||
C -> 3b
|
||||
D -> IV
|
||||
E -> 5
|
||||
43
qk/tests/test1.c
Normal file
43
qk/tests/test1.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "qk.h"
|
||||
|
||||
int dump(struct qk *qk) {
|
||||
size_t l = utvector_len(&qk->vals);
|
||||
|
||||
utstring_printf(&qk->tmp, "qk_end: %lu kv pairs\n", l);
|
||||
|
||||
UT_string *k=NULL, *v=NULL;
|
||||
while(l--) {
|
||||
k = (UT_string*)utvector_next(&qk->keys,k);
|
||||
v = (UT_string*)utvector_next(&qk->vals,v);
|
||||
assert(k); assert(v);
|
||||
utstring_printf(&qk->tmp, "%s -> %s\n",
|
||||
utstring_body(k), utstring_body(v));
|
||||
}
|
||||
|
||||
char *out = utstring_body(&qk->tmp);
|
||||
size_t len = utstring_len(&qk->tmp);
|
||||
write(STDOUT_FILENO, out, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct qk *qk = qk_new();
|
||||
|
||||
qk->cb = dump;
|
||||
|
||||
qk_start(qk);
|
||||
qk_add(qk, "A", "%d", 1);
|
||||
qk_add(qk, "B", "%d%c", 2, 'a');
|
||||
qk_add(qk, "C", "%d%c", 3, 'b');
|
||||
qk_add(qk, "D", "%s", "IV");
|
||||
qk_add(qk, "E", "%lu", (long)5);
|
||||
qk_end(qk);
|
||||
|
||||
qk_free(qk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
qk/tests/test2.ans
Normal file
11
qk/tests/test2.ans
Normal file
@@ -0,0 +1,11 @@
|
||||
qk_end: 5 kv pairs
|
||||
a -> 1
|
||||
b -> 2a
|
||||
c -> 3b
|
||||
d -> IV
|
||||
e -> IV
|
||||
qk_end: 4 kv pairs
|
||||
A -> 1
|
||||
B -> 2a
|
||||
C -> 3b
|
||||
D -> IV
|
||||
50
qk/tests/test2.c
Normal file
50
qk/tests/test2.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "qk.h"
|
||||
|
||||
int dump(struct qk *qk) {
|
||||
size_t l = utvector_len(&qk->vals);
|
||||
|
||||
utstring_printf(&qk->tmp, "qk_end: %lu kv pairs\n", l);
|
||||
|
||||
UT_string *k=NULL, *v=NULL;
|
||||
while(l--) {
|
||||
k = (UT_string*)utvector_next(&qk->keys,k);
|
||||
v = (UT_string*)utvector_next(&qk->vals,v);
|
||||
assert(k); assert(v);
|
||||
utstring_printf(&qk->tmp, "%s -> %s\n",
|
||||
utstring_body(k), utstring_body(v));
|
||||
}
|
||||
|
||||
char *out = utstring_body(&qk->tmp);
|
||||
size_t len = utstring_len(&qk->tmp);
|
||||
write(STDOUT_FILENO, out, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct qk *qk = qk_new();
|
||||
|
||||
qk->cb = dump;
|
||||
|
||||
qk_start(qk);
|
||||
qk_add(qk, "a", "%d", 1);
|
||||
qk_add(qk, "b", "%d%c", 2, 'a');
|
||||
qk_add(qk, "c", "%d%c", 3, 'b');
|
||||
qk_add(qk, "d", "%s", "IV");
|
||||
qk_add(qk, "e", "%s", "IV");
|
||||
qk_end(qk);
|
||||
|
||||
qk_start(qk);
|
||||
qk_add(qk, "A", "%d", 1);
|
||||
qk_add(qk, "B", "%d%c", 2, 'a');
|
||||
qk_add(qk, "C", "%d%c", 3, 'b');
|
||||
qk_add(qk, "D", "%s", "IV");
|
||||
qk_end(qk);
|
||||
|
||||
qk_free(qk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user