This commit is contained in:
Troy D. Hanson
2015-08-24 17:54:32 -04:00
parent 6a5e88d6b6
commit 510da4f2c4
12 changed files with 897 additions and 0 deletions

30
qk/README.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}