Pluggable libheimbase interface for DBs and misc libheimbase enhancements

[Code reviewed by Love Hörnquist Åstrand <lha@kth.se>]

    Added heim_db_*() entry points for dealing with databases, and
    make krb5_aname_to_localname() use it.

    The following enhancements to libheimbase are included:

     - Add heim_data_t and heim_string_t "reference" variants to
       avoid memory copies of potentially large data/strings.

       See heim_data_ref_create() and heim_string_ref_create().

     - Added enhancements to heim_array_t to allow their use for
       queues and stacks, and to improve performance.  See
       heim_array_insert_value().

     - Added XPath-like accessors for heim_object_t.  See
       heim_path_get(), heim_path_copy(), heim_path_create(), and
       heim_path_delete().  These are used extensively in the DB
       framework's generic composition of ACID support and in the
       test_base program

     - Made libheimbase more consistent with Core Foundation naming
       conventions.  See heim_{dict, array}_{get, copy}_value() and
       heim_path_{get, copy}().

     - Added functionality to and fixed bugs in base/json.c:
        - heim_serialize();
        - depth limit for JSON parsing (for DoS protection);
        - pretty-printing;
        - JSON compliance (see below);
        - flag options for parsing and serializing; these are needed
          because of impedance mismatches between heim_object_t and
          JSON (e.g., heim_dict_t allows non-string keys, but JSON
          does not; heimbase supports binary data, while JSON does
          not).

     - Added heim_error_enomem().

     - Enhanced the test_base program to test new functionality and
       to use heim_path*() to better test JSON encoding.  This
       includes some fuzz testing of JSON parsing, and running the
       test under valgrind.

     - Started to add doxygen documentation for libheimbase (but doc
       build for libheimbase is still incomplete).

    Note that there's still some incomplete JSON support:

     - JSON string quoting is not fully implemented;

     - libheimbase lacks support for real numbers, while JSON has
       it -- otherwise libheimbase is a superset of JSON,
       specifically in that any heim_object_t can be a key for an
       associative array.

    The following DB backends are supported natively:

     - "sorted-text", a binary search of sorted (in C locale), flat
       text files;

     - "json", a backend that stores DB contents serialized as JSON
       (this is intended for configuration-like contents).

    The DB framework supports:

     - multiple key/value tables per-DB
     - ACID transactions

    The DB framework also natively implements ACID transactions for
    any DB backends that a) do not provide transactions natively, b)
    do provide lock/unlock/sync methods (even on Windows).  This
    includes autocommit of DB updates outside transactions.

    Future DB enhancements may include:

     - add backends for various DB types (BDB, CDB, MDB, ...);

     - make libhdb use heim_db_t;

     - add a command-line tool for interfacing to databases via
       libheimbase (e.g., to get/set/delete values, create/copy/
       backup DBs, inspect history, check integrity);

     - framework-level transaction logging (with redo and undo
       logging), for generic incremental replication;

     - framework-level DB integrity checking.

       We could store a MAC of the XOR of a hash function applied to
       {key, value} for every entry in the DB, then use this to check
       DB integrity incrementally during incremental replication, as
       well as for the whole DB.
This commit is contained in:
Nicolas Williams
2011-12-29 01:29:26 -06:00
parent df73c96b74
commit f4ba41ebdd
30 changed files with 4211 additions and 259 deletions
+20 -1
View File
@@ -1,6 +1,12 @@
include $(top_srcdir)/Makefile.am.common
if do_roken_rename
ES = base64.c
endif
AM_CPPFLAGS += $(ROKEN_RENAME)
lib_LTLIBRARIES = libheimbase.la
check_PROGRAMS = test_base
@@ -20,6 +26,7 @@ dist_libheimbase_la_SOURCES = \
bsearch.c \
bool.c \
data.c \
db.c \
dict.c \
error.c \
heimbase.c \
@@ -28,10 +35,22 @@ dist_libheimbase_la_SOURCES = \
json.c \
null.c \
number.c \
roken_rename.h \
string.c
nodist_libsl_la_SOURCES = $(ES)
# install these?
libheimbase_la_DEPENDENCIES = version-script.map
test_base_LDADD = libheimbase.la
test_base_LDADD = libheimbase.la $(LIB_roken)
CLEANFILES = base64.c
EXTRA_DIST = NTMakefile version-script.map
base64.c:
$(RM) base64.c
$(LN_S) $(srcdir)/../roken/base64.c .
+28 -2
View File
@@ -31,25 +31,51 @@
RELDIR=base
intcflags=-I$(SRCDIR) -I$(OBJ)
!include ../windows/NTMakefile.w32
INCFILES=$(INCDIR)\heimbase.h
test_binaries = $(OBJ)\test_base.exe
libheimbase_OBJS = \
$(OBJ)\array.obj \
$(OBJ)\bsearch.obj \
$(OBJ)\bool.obj \
$(OBJ)\bsearch.obj \
$(OBJ)\data.obj \
$(OBJ)\db.obj \
$(OBJ)\dict.obj \
$(OBJ)\error.obj \
$(OBJ)\heimbase.obj \
$(OBJ)\json.obj \
$(OBJ)\null.obj \
$(OBJ)\number.obj \
$(OBJ)\string.obj
$(LIBHEIMBASE): $(libheimbase_OBJS)
$(LIBCON)
$(LIBCON_C) -OUT:$@ $(LIBROKEN) @<<
$(libheimbase_OBJS: =
)
<<
test:: test-binaries test-run
test-run:
cd $(OBJ)
test_base.exe
cd $(SRCDIR)
all:: $(INCFILES) $(LIBHEIMBASE)
clean::
-$(RM) $(INCFILES)
test-binaries: $(test_binaries)
$(test_binaries): $$(@R).obj $(LIBHEIMBASE) $(LIBVERS) $(LIBROKEN)
$(EXECONLINK)
$(EXEPREP_NODIST)
$(test_binaries:.exe=.obj): $$(@B).c
$(C2OBJ_C) -Fo$@ -Fd$(@D)\ $** -DBlah
+213 -9
View File
@@ -42,6 +42,8 @@
struct heim_array_data {
size_t len;
heim_object_t *val;
size_t allocated_len;
heim_object_t *allocated;
};
static void
@@ -51,7 +53,7 @@ array_dealloc(heim_object_t ptr)
size_t n;
for (n = 0; n < array->len; n++)
heim_release(array->val[n]);
free(array->val);
free(array->allocated);
}
struct heim_type_data array_object = {
@@ -79,6 +81,8 @@ heim_array_create(void)
if (array == NULL)
return NULL;
array->allocated = NULL;
array->allocated_len = 0;
array->val = NULL;
array->len = 0;
@@ -110,16 +114,134 @@ int
heim_array_append_value(heim_array_t array, heim_object_t object)
{
heim_object_t *ptr;
size_t leading = array->val - array->allocated; /* unused leading slots */
size_t trailing = array->allocated_len - array->len - leading;
size_t new_len;
ptr = realloc(array->val, (array->len + 1) * sizeof(array->val[0]));
if (trailing > 0) {
/* We have pre-allocated space; use it */
array->val[array->len++] = heim_retain(object);
return 0;
}
if (leading > (array->len + 1)) {
/*
* We must have appending to, and deleting at index 0 from this
* array a lot; don't want to grow forever!
*/
(void) memmove(&array->allocated[0], &array->val[0],
array->len * sizeof(array->val[0]));
array->val = array->allocated;
/* We have pre-allocated space; use it */
array->val[array->len++] = heim_retain(object);
return 0;
}
/* Pre-allocate extra .5 times number of used slots */
new_len = leading + array->len + 1 + (array->len >> 1);
ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
if (ptr == NULL)
return ENOMEM;
array->val = ptr;
array->allocated = ptr;
array->allocated_len = new_len;
array->val = &ptr[leading];
array->val[array->len++] = heim_retain(object);
return 0;
}
/*
* Internal function to insert at index 0, taking care to optimize the
* case where we're always inserting at index 0, particularly the case
* where we insert at index 0 and delete from the right end.
*/
static int
heim_array_prepend_value(heim_array_t array, heim_object_t object)
{
heim_object_t *ptr;
size_t leading = array->val - array->allocated; /* unused leading slots */
size_t trailing = array->allocated_len - array->len - leading;
size_t new_len;
if (leading > 0) {
/* We have pre-allocated space; use it */
array->val--;
array->val[0] = heim_retain(object);
array->len++;
return 0;
}
if (trailing > (array->len + 1)) {
/*
* We must have prepending to, and deleting at index
* array->len - 1 from this array a lot; don't want to grow
* forever!
*/
(void) memmove(&array->allocated[array->len], &array->val[0],
array->len * sizeof(array->val[0]));
array->val = &array->allocated[array->len];
/* We have pre-allocated space; use it */
array->val--;
array->val[0] = heim_retain(object);
array->len++;
return 0;
}
/* Pre-allocate extra .5 times number of used slots */
new_len = array->len + 1 + trailing + (array->len >> 1);
ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
if (ptr == NULL)
return ENOMEM;
(void) memmove(&ptr[1], &ptr[0], array->len * sizeof (array->val[0]));
array->allocated = ptr;
array->allocated_len = new_len;
array->val = &ptr[0];
array->val[0] = heim_retain(object);
array->len++;
return 0;
}
/**
* Insert an object at a given index in an array
*
* @param array array to add too
* @param idx index where to add element (-1 == append, -2 next to last, ...)
* @param object the object to add
*
* @return zero if added, errno otherwise
*/
int
heim_array_insert_value(heim_array_t array, size_t idx, heim_object_t object)
{
int ret;
if (idx == 0)
return heim_array_prepend_value(array, object);
else if (idx > array->len)
heim_abort("index too large");
/*
* We cheat: append this element then rotate elements around so we
* have this new element at the desired location, unless we're truly
* appending the new element. This means reusing array growth in
* heim_array_append_value() instead of duplicating that here.
*/
ret = heim_array_append_value(array, object);
if (ret != 0 || idx == (array->len - 1))
return ret;
/*
* Shift to the right by one all the elements after idx, then set
* [idx] to the new object.
*/
(void) memmove(&array->val[idx + 1], &array->val[idx],
(array->len - idx - 1) * sizeof(array->val[0]));
array->val[idx] = heim_retain(object);
return 0;
}
/**
* Iterate over all objects in array
*
@@ -153,6 +275,39 @@ heim_array_iterate(heim_array_t array, void (^fn)(heim_object_t))
}
#endif
/**
* Iterate over all objects in array, backwards
*
* @param array array to iterate over
* @param ctx context passed to fn
* @param fn function to call on each object
*/
void
heim_array_iterate_reverse_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
{
size_t n;
for (n = array->len; n > 0; n--)
fn(array->val[n - 1], ctx);
}
#ifdef __BLOCKS__
/**
* Iterate over all objects in array, backwards
*
* @param array array to iterate over
* @param fn block to call on each object
*/
void
heim_array_iterate_reverse(heim_array_t array, void (^fn)(heim_object_t))
{
size_t n;
for (n = array->len; n > 0; n--)
fn(array->val[n - 1]);
}
#endif
/**
* Get length of array
*
@@ -168,7 +323,25 @@ heim_array_get_length(heim_array_t array)
}
/**
* Copy value of array
* Get value of element at array index
*
* @param array array copy object from
* @param idx index of object, 0 based, must be smaller then
* heim_array_get_length()
*
* @return a not-retained copy of the object
*/
heim_object_t
heim_array_get_value(heim_array_t array, size_t idx)
{
if (idx >= array->len)
heim_abort("index too large");
return array->val[idx];
}
/**
* Get value of element at array index
*
* @param array array copy object from
* @param idx index of object, 0 based, must be smaller then
@@ -178,11 +351,30 @@ heim_array_get_length(heim_array_t array)
*/
heim_object_t
heim_array_get_value(heim_array_t array, size_t idx)
heim_array_copy_value(heim_array_t array, size_t idx)
{
if (idx >= array->len)
heim_abort("index too large");
return array->val[idx];
return heim_retain(array->val[idx]);
}
/**
* Set value at array index
*
* @param array array copy object from
* @param idx index of object, 0 based, must be smaller then
* heim_array_get_length()
* @param value value to set
*
*/
void
heim_array_set_value(heim_array_t array, size_t idx, heim_object_t value)
{
if (idx >= array->len)
heim_abort("index too large");
heim_release(array->val[idx]);
array->val[idx] = heim_retain(value);
}
/**
@@ -202,9 +394,21 @@ heim_array_delete_value(heim_array_t array, size_t idx)
array->len--;
if (idx < array->len)
memmove(&array->val[idx], &array->val[idx + 1],
(array->len - idx) * sizeof(array->val[0]));
/*
* Deleting the first or last elements is cheap, as we leave
* allocated space for opportunistic reuse later; no realloc(), no
* memmove(). All others require a memmove().
*
* If we ever need to optimize deletion of non-last/ non-first
* element we can use a tagged object type to signify "deleted
* value" so we can leave holes in the array, avoid memmove()s on
* delete, and opportunistically re-use those holes on insert.
*/
if (idx == 0)
array->val++;
else if (idx < array->len)
(void) memmove(&array->val[idx], &array->val[idx + 1],
(array->len - idx) * sizeof(array->val[0]));
heim_release(obj);
}
+10
View File
@@ -52,6 +52,16 @@
#include <unistd.h>
#endif
#ifdef LIBINTL
#include <libintl.h>
#define N_(x,y) dgettext(HEIMDAL_TEXTDOMAIN, x)
#else
#define N_(x,y) (x)
#define bindtextdomain(package, localedir)
#endif
#include <roken.h>
#include "heimqueue.h"
#include "heim_threads.h"
#include "heimbase.h"
+108 -19
View File
@@ -33,6 +33,9 @@
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -75,8 +78,8 @@
*
* bsearch_common() contains the common text block binary search code.
*
* __bsearch_text() is the interface for searching in-core text.
* __bsearch_file() is the interface for block-wise searching files.
* _bsearch_text() is the interface for searching in-core text.
* _bsearch_file() is the interface for block-wise searching files.
*/
struct bsearch_file_handle {
@@ -104,7 +107,7 @@ find_line(const char *buf, size_t i, size_t right)
return NULL;
}
/**
/*
* Common routine for binary searching text in core.
*
* Perform a binary search of a char array containing a block from a
@@ -150,7 +153,7 @@ bsearch_common(const char *buf, size_t sz, const char *key,
const char *linep;
size_t key_start, key_len; /* key string in buf */
size_t val_start, val_len; /* value string in buf */
int key_cmp;
int key_cmp = -1;
size_t k;
size_t l; /* left side of buffer for binary search */
size_t r; /* right side of buffer for binary search */
@@ -289,7 +292,7 @@ bsearch_common(const char *buf, size_t sz, const char *key,
return ret;
}
/**
/*
* Binary search a char array containing sorted text records separated
* by new-lines (or CRLF). Each record consists of a key and an
* optional value following the key, separated from the key by unquoted
@@ -315,7 +318,7 @@ bsearch_common(const char *buf, size_t sz, const char *key,
* needed for the search (useful for benchmarking)
*/
int
__bsearch_text(const char *buf, size_t buf_sz, const char *key,
_bsearch_text(const char *buf, size_t buf_sz, const char *key,
char **value, size_t *location, size_t *loops)
{
return bsearch_common(buf, buf_sz, key, 1, value, location, NULL, loops);
@@ -323,7 +326,7 @@ __bsearch_text(const char *buf, size_t buf_sz, const char *key,
#define MAX_BLOCK_SIZE (1024 * 1024)
#define DEFAULT_MAX_FILE_SIZE (1024 * 1024)
/**
/*
* Open a file for binary searching. The file will be read in entirely
* if it is smaller than @max_sz, else a cache of @max_sz bytes will be
* allocated.
@@ -339,14 +342,14 @@ __bsearch_text(const char *buf, size_t buf_sz, const char *key,
*
* Outputs:
*
* @bfh Handle for use with __bsearch_file() and __bsearch_file_close()
* @bfh Handle for use with _bsearch_file() and _bsearch_file_close()
* @reads Number of reads performed
*/
int
__bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
_bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
bsearch_file_handle *bfh, size_t *reads)
{
bsearch_file_handle new_bfh;
bsearch_file_handle new_bfh = NULL;
struct stat st;
size_t i;
int fd;
@@ -469,12 +472,12 @@ err:
return ret;
}
/**
/*
* Indicate whether the given binary search file handle will be searched
* with block-wise method.
*/
void
__bsearch_file_info(bsearch_file_handle bfh,
_bsearch_file_info(bsearch_file_handle bfh,
size_t *page_sz, size_t *max_sz, int *blockwise)
{
if (page_sz)
@@ -485,7 +488,7 @@ __bsearch_file_info(bsearch_file_handle bfh,
*blockwise = (bfh->file_sz != bfh->cache_sz);
}
/**
/*
* Close the given binary file search handle.
*
* Inputs:
@@ -493,7 +496,7 @@ __bsearch_file_info(bsearch_file_handle bfh,
* @bfh Pointer to variable containing handle to close.
*/
void
__bsearch_file_close(bsearch_file_handle *bfh)
_bsearch_file_close(bsearch_file_handle *bfh)
{
if (!*bfh)
return;
@@ -507,7 +510,7 @@ __bsearch_file_close(bsearch_file_handle *bfh)
*bfh = NULL;
}
/**
/*
* Private function to get a page from a cache. The cache is a char
* array of 2^n - 1 double-size page worth of bytes, where n is the
* number of tree levels that the cache stores. The cache can be
@@ -567,7 +570,7 @@ get_page_from_cache(bsearch_file_handle bfh, size_t level, size_t page_idx,
return 1;
}
/**
/*
* Private function to read a page of @page_sz from @fd at offset @off
* into @buf, outputing the number of bytes read, which will be the same
* as @page_sz unless the page being read is the last page, in which
@@ -646,7 +649,7 @@ read_page(bsearch_file_handle bfh, size_t level, size_t page_idx, size_t page,
return 0;
}
/**
/*
* Perform a binary search of a file where each line is a record (LF and
* CRLF supported). Each record consists of a key followed by an
* optional value separated from the key by whitespace. Whitespace can
@@ -685,7 +688,7 @@ read_page(bsearch_file_handle bfh, size_t level, size_t page_idx, size_t page,
* (useful for confirming logarithmic performance)
*/
int
__bsearch_file(bsearch_file_handle bfh, const char *key,
_bsearch_file(bsearch_file_handle bfh, const char *key,
char **value, size_t *location, size_t *loops, size_t *reads)
{
int ret;
@@ -707,7 +710,7 @@ __bsearch_file(bsearch_file_handle bfh, const char *key,
/* If whole file is in memory then search that and we're done */
if (bfh->file_sz == bfh->cache_sz)
return __bsearch_text(bfh->cache, bfh->cache_sz, key, value, location, loops);
return _bsearch_text(bfh->cache, bfh->cache_sz, key, value, location, loops);
/* Else block-wise binary search */
@@ -794,3 +797,89 @@ __bsearch_file(bsearch_file_handle bfh, const char *key,
return -1;
}
static int
stdb_open(void *plug, const char *dbtype, const char *dbname,
heim_dict_t options, void **db, heim_error_t *error)
{
bsearch_file_handle bfh;
char *p;
int ret;
if (error)
*error = NULL;
if (dbname == NULL || *dbname == '\0') {
if (error)
*error = heim_error_create(EINVAL,
N_("DB name required for sorted-text DB "
"plugin", ""));
return EINVAL;
}
p = strrchr(dbname, '.');
if (p == NULL || strcmp(p, ".txt") != 0) {
if (error)
*error = heim_error_create(ENOTSUP,
N_("Text file (name ending in .txt) "
"required for sorted-text DB plugin",
""));
return ENOTSUP;
}
ret = _bsearch_file_open(dbname, 0, 0, &bfh, NULL);
if (ret)
return ret;
*db = bfh;
return 0;
}
static int
stdb_close(void *db, heim_error_t *error)
{
bsearch_file_handle bfh = db;
if (error)
*error = NULL;
_bsearch_file_close(&bfh);
return 0;
}
static heim_data_t
stdb_copy_value(void *db, heim_string_t table, heim_data_t key,
heim_error_t *error)
{
bsearch_file_handle bfh = db;
const char *k;
char *v;
heim_data_t value;
int ret;
if (error)
*error = NULL;
if (table == NULL)
table = HSTR("");
if (table != HSTR(""))
return NULL;
if (heim_get_tid(key) == HEIM_TID_STRING)
k = heim_string_get_utf8((heim_string_t)key);
else
k = (const char *)heim_data_get_ptr(key);
ret = _bsearch_file(bfh, k, &v, NULL, NULL, NULL);
if (ret != 0) {
if (ret > 0 && error)
*error = heim_error_create(ret, "%s", strerror(ret));
return NULL;
}
value = heim_data_create(v, strlen(v));
free(v);
/* XXX Handle ENOMEM */
return value;
}
struct heim_db_type heim_sorted_text_file_dbtype = {
1, stdb_open, NULL, stdb_close, NULL, NULL, NULL, NULL, NULL, NULL,
stdb_copy_value, NULL, NULL, NULL
};
+35 -1
View File
@@ -37,6 +37,19 @@
static void
data_dealloc(void *ptr)
{
heim_data_t d = ptr;
heim_octet_string *os = (heim_octet_string *)d;
heim_data_free_f_t *deallocp;
heim_data_free_f_t dealloc;
if (os->data == NULL)
return;
/* Possible string ref */
deallocp = _heim_get_isaextra(os, 0);
dealloc = *deallocp;
if (dealloc != NULL)
dealloc(os->data);
}
static int
@@ -92,10 +105,28 @@ heim_data_create(const void *data, size_t length)
return (heim_data_t)os;
}
heim_data_t
heim_data_ref_create(const void *data, size_t length,
heim_data_free_f_t dealloc)
{
heim_octet_string *os;
heim_data_free_f_t *deallocp;
os = _heim_alloc_object(&_heim_data_object, sizeof(*os) + length);
if (os) {
os->data = (void *)data;
os->length = length;
deallocp = _heim_get_isaextra(os, 0);
*deallocp = dealloc;
}
return (heim_data_t)os;
}
/**
* Return the type ID of data objects
*
* @return type id of string objects
* @return type id of data objects
*/
heim_tid_t
@@ -115,16 +146,19 @@ heim_data_get_type_id(void)
const heim_octet_string *
heim_data_get_data(heim_data_t data)
{
/* Note that this works for data and data_ref objects */
return (const heim_octet_string *)data;
}
const void *
heim_data_get_ptr(heim_data_t data)
{
/* Note that this works for data and data_ref objects */
return ((const heim_octet_string *)data)->data;
}
size_t heim_data_get_length(heim_data_t data)
{
/* Note that this works for data and data_ref objects */
return ((const heim_octet_string *)data)->length;
}
+1709
View File
File diff suppressed because it is too large Load Diff
+21 -1
View File
@@ -164,11 +164,31 @@ _search(heim_dict_t dict, heim_object_t ptr)
* @value dict the dict to search in
* @value key the key to search for
*
* @return a retained copy of the value for key or NULL if not found
* @return a not-retained copy of the value for key or NULL if not found
*/
heim_object_t
heim_dict_get_value(heim_dict_t dict, heim_object_t key)
{
struct hashentry *p;
p = _search(dict, key);
if (p == NULL)
return NULL;
return p->value;
}
/**
* Search for element in hash table
*
* @value dict the dict to search in
* @value key the key to search for
*
* @return a retained copy of the value for key or NULL if not found
*/
heim_object_t
heim_dict_copy_value(heim_dict_t dict, heim_object_t key)
{
struct hashentry *p;
p = _search(dict, key);
+28 -2
View File
@@ -75,6 +75,13 @@ struct heim_type_data _heim_error_object = {
error_hash
};
heim_error_t
heim_error_enomem(void)
{
/* This is an immediate object; see heim_number_create() */
return (heim_error_t)heim_number_create(ENOMEM);
}
heim_error_t
heim_error_create(int error_code, const char *fmt, ...)
{
@@ -94,14 +101,17 @@ heim_error_createv(int error_code, const char *fmt, va_list ap)
heim_error_t e;
char *str;
int len;
int save_errno = errno;
str = malloc(1024);
errno = save_errno;
if (str == NULL)
return NULL;
return heim_error_enomem();
len = vsnprintf(str, 1024, fmt, ap);
errno = save_errno;
if (len < 0) {
free(str);
return NULL;
return NULL; /* XXX We should have a special heim_error_t for this */
}
e = _heim_alloc_object(&_heim_error_object, sizeof(struct heim_error));
@@ -111,12 +121,18 @@ heim_error_createv(int error_code, const char *fmt, va_list ap)
}
free(str);
errno = save_errno;
return e;
}
heim_string_t
heim_error_copy_string(heim_error_t error)
{
if (heim_get_tid(error) != HEIM_TID_ERROR) {
if (heim_get_tid(error) == heim_number_get_type_id())
return __heim_string_constant(strerror(heim_number_get_int((heim_number_t)error)));
heim_abort("invalid heim_error_t");
}
/* XXX concat all strings */
return heim_retain(error->msg);
}
@@ -124,12 +140,22 @@ heim_error_copy_string(heim_error_t error)
int
heim_error_get_code(heim_error_t error)
{
if (heim_get_tid(error) != HEIM_TID_ERROR) {
if (heim_get_tid(error) == heim_number_get_type_id())
return heim_number_get_int((heim_number_t)error);
heim_abort("invalid heim_error_t");
}
return error->error_code;
}
heim_error_t
heim_error_append(heim_error_t top, heim_error_t append)
{
if (heim_get_tid(top) != HEIM_TID_ERROR) {
if (heim_get_tid(top) == heim_number_get_type_id())
return top;
heim_abort("invalid heim_error_t");
}
if (top->next)
heim_release(top->next);
top->next = heim_retain(append);
+400 -4
View File
@@ -76,7 +76,7 @@ struct heim_auto_release {
/**
* Retain object
* Retain object (i.e., take a reference)
*
* @param object to be released, NULL is ok
*
@@ -100,7 +100,7 @@ heim_retain(void *ptr)
}
/**
* Release object, free is reference count reaches zero
* Release object, free if reference count reaches zero
*
* @param object to be released
*/
@@ -257,6 +257,18 @@ struct heim_type_data memory_object = {
NULL
};
/**
* Allocate memory for an object of anonymous type
*
* @param size size of object to be allocated
* @param name name of ad-hoc type
* @param dealloc destructor function
*
* Objects allocated with this interface do not serialize.
*
* @return allocated object
*/
void *
heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc)
{
@@ -310,6 +322,18 @@ _heim_alloc_object(heim_type_t type, size_t size)
return BASE2PTR(p);
}
void *
_heim_get_isaextra(heim_object_t ptr, size_t idx)
{
struct heim_base *p = (struct heim_base *)PTR2BASE(ptr);
heim_assert(ptr != NULL, "internal error");
if (p->isa == &memory_object)
return NULL;
heim_assert(idx < 3, "invalid private heim_base extra data index");
return &p->isaextra[idx];
}
heim_tid_t
_heim_type_get_tid(heim_type_t type)
{
@@ -489,7 +513,11 @@ static struct heim_type_data _heim_autorel_object = {
};
/**
* Create thread-specific object auto-release pool
*
* Objects placed on the per-thread auto-release pool (with
* heim_auto_release()) can be released in one fell swoop by calling
* heim_auto_release_drain().
*/
heim_auto_release_t
@@ -515,7 +543,9 @@ heim_auto_release_create(void)
}
/**
* Mark the current object as a
* Place the current object on the thread's auto-release pool
*
* @param ptr object
*/
void
@@ -546,7 +576,7 @@ heim_auto_release(heim_object_t ptr)
}
/**
*
* Release all objects on the given auto-release pool
*/
void
@@ -565,3 +595,369 @@ heim_auto_release_drain(heim_auto_release_t autorel)
}
HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
}
/*
* Helper for heim_path_vget() and heim_path_delete(). On success
* outputs the node named by the path and the parent node and key
* (useful for heim_path_delete()).
*/
static heim_object_t
heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key,
heim_error_t *error, va_list ap)
{
heim_object_t path_element;
heim_object_t node, next_node;
heim_tid_t node_type;
*parent = NULL;
*key = NULL;
if (ptr == NULL)
return NULL;
for (node = ptr; node != NULL; ) {
path_element = va_arg(ap, heim_object_t);
if (path_element == NULL) {
*parent = node;
*key = path_element;
return node;
}
node_type = heim_get_tid(node);
switch (node_type) {
case HEIM_TID_ARRAY:
case HEIM_TID_DICT:
case HEIM_TID_DB:
break;
default:
if (node == ptr)
heim_abort("heim_path_get() only operates on container types");
return NULL;
}
if (node_type == HEIM_TID_DICT) {
next_node = heim_dict_get_value(node, path_element);
} else if (node_type == HEIM_TID_DB) {
next_node = _heim_db_get_value(node, NULL, path_element, NULL);
} else if (node_type == HEIM_TID_ARRAY) {
int idx = -1;
if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
idx = heim_number_get_int(path_element);
if (idx < 0) {
if (error)
*error = heim_error_create(EINVAL,
"heim_path_get() path elements "
"for array nodes must be "
"numeric and positive");
return NULL;
}
next_node = heim_array_get_value(node, idx);
} else {
if (error)
*error = heim_error_create(EINVAL,
"heim_path_get() node in path "
"not a container type");
return NULL;
}
node = next_node;
}
return NULL;
}
/**
* Get a node in a heim_object tree by path
*
* @param ptr tree
* @param error error (output)
* @param ap NULL-terminated va_list of heim_object_ts that form a path
*
* @return object (not retained) if found
*
* @addtogroup heimbase
*/
heim_object_t
heim_path_vget(heim_object_t ptr, heim_error_t *error, va_list ap)
{
heim_object_t p, k;
return heim_path_vget2(ptr, &p, &k, error, ap);
}
/**
* Get a node in a tree by path, with retained reference
*
* @param ptr tree
* @param error error (output)
* @param ap NULL-terminated va_list of heim_object_ts that form a path
*
* @return retained object if found
*
* @addtogroup heimbase
*/
heim_object_t
heim_path_vcopy(heim_object_t ptr, heim_error_t *error, va_list ap)
{
heim_object_t p, k;
return heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
}
/**
* Get a node in a tree by path
*
* @param ptr tree
* @param error error (output)
* @param ... NULL-terminated va_list of heim_object_ts that form a path
*
* @return object (not retained) if found
*
* @addtogroup heimbase
*/
heim_object_t
heim_path_get(heim_object_t ptr, heim_error_t *error, ...)
{
heim_object_t o;
heim_object_t p, k;
va_list ap;
if (ptr == NULL)
return NULL;
va_start(ap, error);
o = heim_path_vget2(ptr, &p, &k, error, ap);
va_end(ap);
return o;
}
/**
* Get a node in a tree by path, with retained reference
*
* @param ptr tree
* @param error error (output)
* @param ... NULL-terminated va_list of heim_object_ts that form a path
*
* @return retained object if found
*
* @addtogroup heimbase
*/
heim_object_t
heim_path_copy(heim_object_t ptr, heim_error_t *error, ...)
{
heim_object_t o;
heim_object_t p, k;
va_list ap;
if (ptr == NULL)
return NULL;
va_start(ap, error);
o = heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
va_end(ap);
return o;
}
/**
* Create a path in a heim_object_t tree
*
* @param ptr the tree
* @param size the size of the heim_dict_t nodes to be created
* @param leaf leaf node to be added, if any
* @param error error (output)
* @param ap NULL-terminated of path component objects
*
* Create a path of heim_dict_t interior nodes in a given heim_object_t
* tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
* then the leaf is not deleted).
*
* @return 0 on success, else a system error
*
* @addtogroup heimbase
*/
int
heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
heim_error_t *error, va_list ap)
{
heim_object_t path_element = va_arg(ap, heim_object_t);
heim_object_t next_path_element = NULL;
heim_object_t node = ptr;
heim_object_t next_node = NULL;
heim_tid_t node_type;
int ret;
if (ptr == NULL)
heim_abort("heim_path_vcreate() does not create root nodes");
while (path_element != NULL) {
next_path_element = va_arg(ap, heim_object_t);
node_type = heim_get_tid(node);
if (node_type == HEIM_TID_DICT) {
next_node = heim_dict_get_value(node, path_element);
} else if (node_type == HEIM_TID_ARRAY) {
int idx = -1;
if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
idx = heim_number_get_int(path_element);
if (idx < 0) {
if (error)
*error = heim_error_create(EINVAL,
"heim_path() path elements for "
"array nodes must be numeric "
"and positive");
return EINVAL;
}
if (idx < heim_array_get_length(node))
next_node = heim_array_get_value(node, idx);
else
next_node = NULL;
} else if (node_type == HEIM_TID_DB && next_path_element != NULL) {
if (error)
*error = heim_error_create(EINVAL, "Interior node is a DB");
return EINVAL;
}
if (next_path_element == NULL)
break;
/* Create missing interior node */
if (next_node == NULL) {
next_node = heim_dict_create(size); /* no arrays or DBs, just dicts */
if (next_node == NULL) {
ret = ENOMEM;
goto err;
}
if (node_type == HEIM_TID_DICT) {
ret = heim_dict_set_value(node, path_element, next_node);
} else if (node_type == HEIM_TID_ARRAY &&
heim_number_get_int(path_element) <= heim_array_get_length(node)) {
ret = heim_array_insert_value(node,
heim_number_get_int(path_element),
next_node);
} else {
ret = EINVAL;
if (error)
*error = heim_error_create(ret, "Node in path not a "
"container");
goto err;
}
heim_release(next_node);
if (ret)
goto err;
}
path_element = next_path_element;
node = next_node;
next_node = NULL;
}
if (path_element == NULL)
goto err;
/* Add the leaf */
if (leaf != NULL) {
if (node_type == HEIM_TID_DICT)
ret = heim_dict_set_value(node, path_element, leaf);
else
ret = heim_array_insert_value(node,
heim_number_get_int(path_element),
leaf);
}
return 0;
err:
if (error && !*error) {
if (ret == ENOMEM)
*error = heim_error_enomem();
else
*error = heim_error_create(ret, "Could not set "
"dict value");
}
return ret;
}
/**
* Create a path in a heim_object_t tree
*
* @param ptr the tree
* @param size the size of the heim_dict_t nodes to be created
* @param leaf leaf node to be added, if any
* @param error error (output)
* @param ... NULL-terminated list of path component objects
*
* Create a path of heim_dict_t interior nodes in a given heim_object_t
* tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
* then the leaf is not deleted).
*
* @return 0 on success, else a system error
*
* @addtogroup heimbase
*/
int
heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
heim_error_t *error, ...)
{
va_list ap;
int ret;
va_start(ap, error);
ret = heim_path_vcreate(ptr, size, leaf, error, ap);
va_end(ap);
return ret;
}
/**
* Delete leaf node named by a path in a heim_object_t tree
*
* @param ptr the tree
* @param error error (output)
* @param ap NULL-terminated list of path component objects
*
* @addtogroup heimbase
*/
void
heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap)
{
heim_object_t parent, key, child;
child = heim_path_vget2(ptr, &parent, &key, error, ap);
if (child != NULL) {
if (heim_get_tid(parent) == HEIM_TID_DICT)
heim_dict_delete_key(parent, key);
else if (heim_get_tid(parent) == HEIM_TID_DB)
heim_db_delete_key(parent, NULL, key, error);
else if (heim_get_tid(parent) == HEIM_TID_ARRAY)
heim_array_delete_value(parent, heim_number_get_int(key));
heim_release(child);
}
}
/**
* Delete leaf node named by a path in a heim_object_t tree
*
* @param ptr the tree
* @param error error (output)
* @param ap NULL-terminated list of path component objects
*
* @addtogroup heimbase
*/
void
heim_path_delete(heim_object_t ptr, heim_error_t *error, ...)
{
va_list ap;
va_start(ap, error);
heim_path_vdelete(ptr, error, ap);
va_end(ap);
return;
}
+170 -45
View File
@@ -124,13 +124,19 @@ heim_tid_t heim_array_get_type_id(void);
typedef void (*heim_array_iterator_f_t)(heim_object_t, void *);
int heim_array_append_value(heim_array_t, heim_object_t);
int heim_array_insert_value(heim_array_t, size_t idx, heim_object_t);
void heim_array_iterate_f(heim_array_t, void *, heim_array_iterator_f_t);
void heim_array_iterate_reverse_f(heim_array_t, void *, heim_array_iterator_f_t);
#ifdef __BLOCKS__
void heim_array_iterate(heim_array_t, void (^)(heim_object_t));
void heim_array_iterate_reverse(heim_array_t, void (^)(heim_object_t));
#endif
size_t heim_array_get_length(heim_array_t);
heim_object_t
heim_array_get_value(heim_array_t, size_t);
heim_object_t
heim_array_copy_value(heim_array_t, size_t);
void heim_array_set_value(heim_array_t, size_t, heim_object_t);
void heim_array_delete_value(heim_array_t, size_t);
#ifdef __BLOCKS__
void heim_array_filter(heim_array_t, int (^)(heim_object_t));
@@ -155,6 +161,8 @@ void heim_dict_iterate(heim_dict_t, void (^)(heim_object_t, heim_object_t));
heim_object_t
heim_dict_get_value(heim_dict_t, heim_object_t);
heim_object_t
heim_dict_copy_value(heim_dict_t, heim_object_t);
void heim_dict_delete_key(heim_dict_t, heim_object_t);
/*
@@ -162,15 +170,154 @@ void heim_dict_delete_key(heim_dict_t, heim_object_t);
*/
typedef struct heim_string_data *heim_string_t;
typedef void (*heim_string_free_f_t)(void *);
heim_string_t heim_string_create(const char *);
heim_string_t heim_string_ref_create(const char *, heim_string_free_f_t);
heim_string_t heim_string_create_with_bytes(const void *, size_t);
heim_string_t heim_string_ref_create_with_bytes(const void *, size_t,
heim_string_free_f_t);
heim_tid_t heim_string_get_type_id(void);
const char * heim_string_get_utf8(heim_string_t);
#define HSTR(_str) (__heim_string_constant("" _str ""))
heim_string_t __heim_string_constant(const char *);
/*
* Errors
*/
typedef struct heim_error * heim_error_t;
heim_error_t heim_error_enomem(void);
heim_error_t heim_error_create(int, const char *, ...)
HEIMDAL_PRINTF_ATTRIBUTE((printf, 2, 3));
heim_error_t heim_error_createv(int, const char *, va_list)
HEIMDAL_PRINTF_ATTRIBUTE((printf, 2, 0));
heim_string_t heim_error_copy_string(heim_error_t);
int heim_error_get_code(heim_error_t);
heim_error_t heim_error_append(heim_error_t, heim_error_t);
/*
* Path
*/
heim_object_t heim_path_get(heim_object_t ptr, heim_error_t *error, ...);
heim_object_t heim_path_copy(heim_object_t ptr, heim_error_t *error, ...);
heim_object_t heim_path_vget(heim_object_t ptr, heim_error_t *error,
va_list ap);
heim_object_t heim_path_vcopy(heim_object_t ptr, heim_error_t *error,
va_list ap);
int heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
heim_error_t *error, va_list ap);
int heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
heim_error_t *error, ...);
void heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap);
void heim_path_delete(heim_object_t ptr, heim_error_t *error, ...);
/*
* Data (octet strings)
*/
#ifndef __HEIM_OCTET_STRING__
#define __HEIM_OCTET_STRING__
typedef struct heim_octet_string {
size_t length;
void *data;
} heim_octet_string;
#endif
typedef struct heim_data * heim_data_t;
typedef void (*heim_data_free_f_t)(void *);
heim_data_t heim_data_create(const void *, size_t);
heim_data_t heim_data_ref_create(const void *, size_t, heim_data_free_f_t);
heim_tid_t heim_data_get_type_id(void);
const heim_octet_string *
heim_data_get_data(heim_data_t);
const void * heim_data_get_ptr(heim_data_t);
size_t heim_data_get_length(heim_data_t);
/*
* DB
*/
typedef struct heim_db_data *heim_db_t;
typedef void (*heim_db_iterator_f_t)(heim_data_t, heim_data_t, void *);
typedef int (*heim_db_plug_open_f_t)(void *, const char *, const char *,
heim_dict_t, void **, heim_error_t *);
typedef int (*heim_db_plug_clone_f_t)(void *, void **, heim_error_t *);
typedef int (*heim_db_plug_close_f_t)(void *, heim_error_t *);
typedef int (*heim_db_plug_lock_f_t)(void *, int, heim_error_t *);
typedef int (*heim_db_plug_unlock_f_t)(void *, heim_error_t *);
typedef int (*heim_db_plug_sync_f_t)(void *, heim_error_t *);
typedef int (*heim_db_plug_begin_f_t)(void *, int, heim_error_t *);
typedef int (*heim_db_plug_commit_f_t)(void *, heim_error_t *);
typedef int (*heim_db_plug_rollback_f_t)(void *, heim_error_t *);
typedef heim_data_t (*heim_db_plug_copy_value_f_t)(void *, heim_string_t,
heim_data_t,
heim_error_t *);
typedef int (*heim_db_plug_set_value_f_t)(void *, heim_string_t, heim_data_t,
heim_data_t, heim_error_t *);
typedef int (*heim_db_plug_del_key_f_t)(void *, heim_string_t, heim_data_t,
heim_error_t *);
typedef void (*heim_db_plug_iter_f_t)(void *, heim_string_t, void *,
heim_db_iterator_f_t, heim_error_t *);
struct heim_db_type {
int version;
heim_db_plug_open_f_t openf;
heim_db_plug_clone_f_t clonef;
heim_db_plug_close_f_t closef;
heim_db_plug_lock_f_t lockf;
heim_db_plug_unlock_f_t unlockf;
heim_db_plug_sync_f_t syncf;
heim_db_plug_begin_f_t beginf;
heim_db_plug_commit_f_t commitf;
heim_db_plug_rollback_f_t rollbackf;
heim_db_plug_copy_value_f_t copyf;
heim_db_plug_set_value_f_t setf;
heim_db_plug_del_key_f_t delf;
heim_db_plug_iter_f_t iterf;
};
extern struct heim_db_type heim_sorted_text_file_dbtype;
#define HEIM_DB_TYPE_VERSION_01 1
int heim_db_register(const char *dbtype,
void *data,
struct heim_db_type *plugin);
heim_db_t heim_db_create(const char *dbtype, const char *dbname,
heim_dict_t options, heim_error_t *error);
heim_db_t heim_db_clone(heim_db_t, heim_error_t *);
int heim_db_begin(heim_db_t, int, heim_error_t *);
int heim_db_commit(heim_db_t, heim_error_t *);
int heim_db_rollback(heim_db_t, heim_error_t *);
heim_tid_t heim_db_get_type_id(void);
int heim_db_set_value(heim_db_t, heim_string_t, heim_data_t, heim_data_t,
heim_error_t *);
heim_data_t heim_db_copy_value(heim_db_t, heim_string_t, heim_data_t,
heim_error_t *);
int heim_db_delete_key(heim_db_t, heim_string_t, heim_data_t,
heim_error_t *);
void heim_db_iterate_f(heim_db_t, heim_string_t, void *,
heim_db_iterator_f_t, heim_error_t *);
#ifdef __BLOCKS__
void heim_db_iterate(heim_db_t, heim_string_t,
void (^)(heim_data_t, heim_data_t), heim_error_t *);
#endif
/*
* Number
*/
@@ -191,50 +338,28 @@ heim_auto_release_t heim_auto_release_create(void);
void heim_auto_release_drain(heim_auto_release_t);
void heim_auto_release(heim_object_t);
/*
*
*/
typedef struct heim_error * heim_error_t;
heim_error_t heim_error_create(int, const char *, ...)
HEIMDAL_PRINTF_ATTRIBUTE((printf, 2, 3));
heim_error_t heim_error_createv(int, const char *, va_list)
HEIMDAL_PRINTF_ATTRIBUTE((printf, 2, 0));
heim_string_t heim_error_copy_string(heim_error_t);
int heim_error_get_code(heim_error_t);
heim_error_t heim_error_append(heim_error_t, heim_error_t);
/*
* JSON
*/
heim_object_t heim_json_create(const char *, heim_error_t *);
heim_object_t heim_json_create_with_bytes(const void *, size_t, heim_error_t *);
typedef enum heim_json_flags {
HEIM_JSON_F_NO_C_NULL = 1,
HEIM_JSON_F_STRICT_STRINGS = 2,
HEIM_JSON_F_NO_DATA = 4,
HEIM_JSON_F_NO_DATA_DICT = 8,
HEIM_JSON_F_STRICT_DICT = 16,
HEIM_JSON_F_STRICT = 31,
HEIM_JSON_F_CNULL2JSNULL = 32,
HEIM_JSON_F_TRY_DECODE_DATA = 64,
HEIM_JSON_F_ONE_LINE = 128
} heim_json_flags_t;
/*
*
*/
#ifndef __HEIM_OCTET_STRING__
#define __HEIM_OCTET_STRING__
typedef struct heim_octet_string {
size_t length;
void *data;
} heim_octet_string;
#endif
typedef struct heim_data * heim_data_t;
heim_data_t heim_data_create(const void *, size_t);
heim_tid_t heim_data_get_type_id(void);
const heim_octet_string *
heim_data_get_data(heim_data_t);
const void * heim_data_get_ptr(heim_data_t);
size_t heim_data_get_length(heim_data_t);
heim_object_t heim_json_create(const char *, size_t, heim_json_flags_t,
heim_error_t *);
heim_object_t heim_json_create_with_bytes(const void *, size_t, size_t,
heim_json_flags_t,
heim_error_t *);
heim_string_t heim_serialize(heim_object_t, heim_json_flags_t flags,
heim_error_t *);
/*
@@ -243,14 +368,14 @@ size_t heim_data_get_length(heim_data_t);
* Note: these are private until integrated into the heimbase object system.
*/
typedef struct bsearch_file_handle *bsearch_file_handle;
int __bsearch_text(const char *buf, size_t buf_sz, const char *key,
int _bsearch_text(const char *buf, size_t buf_sz, const char *key,
char **value, size_t *location, size_t *loops);
int __bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
int _bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
bsearch_file_handle *bfh, size_t *reads);
int __bsearch_file(bsearch_file_handle bfh, const char *key, char **value,
int _bsearch_file(bsearch_file_handle bfh, const char *key, char **value,
size_t *location, size_t *loops, size_t *reads);
void __bsearch_file_info(bsearch_file_handle bfh, size_t *page_sz,
void _bsearch_file_info(bsearch_file_handle bfh, size_t *page_sz,
size_t *max_sz, int *blockwise);
void __bsearch_file_close(bsearch_file_handle *bfh);
void _bsearch_file_close(bsearch_file_handle *bfh);
#endif /* HEIM_BASE_H */
+13 -5
View File
@@ -44,11 +44,11 @@ enum {
HEIM_TID_NUMBER = 0,
HEIM_TID_NULL = 1,
HEIM_TID_BOOL = 2,
HEIM_TID_TAGGED_UNUSED2 = 3,
HEIM_TID_TAGGED_UNUSED3 = 4,
HEIM_TID_TAGGED_UNUSED4 = 5,
HEIM_TID_TAGGED_UNUSED5 = 6,
HEIM_TID_TAGGED_UNUSED6 = 7,
HEIM_TID_TAGGED_UNUSED2 = 3, /* reserved for tagged object types */
HEIM_TID_TAGGED_UNUSED3 = 4, /* reserved for tagged object types */
HEIM_TID_TAGGED_UNUSED4 = 5, /* reserved for tagged object types */
HEIM_TID_TAGGED_UNUSED5 = 6, /* reserved for tagged object types */
HEIM_TID_TAGGED_UNUSED6 = 7, /* reserved for tagged object types */
HEIM_TID_MEMORY = 128,
HEIM_TID_ARRAY = 129,
HEIM_TID_DICT = 130,
@@ -56,6 +56,7 @@ enum {
HEIM_TID_AUTORELEASE = 132,
HEIM_TID_ERROR = 133,
HEIM_TID_DATA = 134,
HEIM_TID_DB = 135,
HEIM_TID_USER = 255
};
@@ -83,12 +84,19 @@ _heim_create_type(const char *name,
heim_object_t
_heim_alloc_object(heim_type_t type, size_t size);
void *
_heim_get_isaextra(heim_object_t o, size_t idx);
heim_tid_t
_heim_type_get_tid(heim_type_t type);
void
_heim_make_permanent(heim_object_t ptr);
heim_data_t
_heim_db_get_value(heim_db_t, heim_string_t, heim_data_t, heim_error_t *);
/* tagged tid */
extern struct heim_type_data _heim_null_object;
extern struct heim_type_data _heim_bool_object;
+388 -38
View File
@@ -35,11 +35,34 @@
#include "baselocl.h"
#include <ctype.h>
#include <base64.h>
static heim_base_once_t heim_json_once = HEIM_BASE_ONCE_INIT;
static heim_string_t heim_tid_data_uuid_key = NULL;
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static void
json_init_once(void *arg)
{
heim_tid_data_uuid_key = __heim_string_constant("heimdal-type-data-76d7fca2-d0da-4b20-a126-1a10f8a0eae6");
}
struct twojson {
void *ctx;
size_t indent;
void (*out)(void *, const char *);
size_t indent;
heim_json_flags_t flags;
int ret;
int first;
};
struct strbuf {
char *str;
size_t len;
size_t alloced;
int enomem;
heim_json_flags_t flags;
};
static int
@@ -49,6 +72,8 @@ static void
indent(struct twojson *j)
{
size_t indent = j->indent;
if (j->flags & HEIM_JSON_F_ONE_LINE)
return;
while (indent--)
j->out(j->ctx, "\t");
}
@@ -57,31 +82,56 @@ static void
array2json(heim_object_t value, void *ctx)
{
struct twojson *j = ctx;
indent(j);
base2json(value, j);
j->out(j->ctx, ",\n");
j->indent--;
if (j->ret)
return;
if (j->first) {
j->first = 0;
} else {
j->out(j->ctx, NULL); /* eat previous '\n' if possible */
j->out(j->ctx, ",\n");
}
j->ret = base2json(value, j);
}
static void
dict2json(heim_object_t key, heim_object_t value, void *ctx)
{
struct twojson *j = ctx;
indent(j);
base2json(key, j);
j->out(j->ctx, " = ");
base2json(value, j);
j->out(j->ctx, ",\n");
if (j->ret)
return;
if (j->first) {
j->first = 0;
} else {
j->out(j->ctx, NULL); /* eat previous '\n' if possible */
j->out(j->ctx, ",\n");
}
j->ret = base2json(key, j);
if (j->ret)
return;
j->out(j->ctx, " : \n");
j->indent++;
j->ret = base2json(value, j);
if (j->ret)
return;
j->indent--;
}
static int
base2json(heim_object_t obj, struct twojson *j)
{
heim_tid_t type;
int first = 0;
if (obj == NULL) {
indent(j);
j->out(j->ctx, "<NULL>\n");
if (j->flags & HEIM_JSON_F_CNULL2JSNULL) {
obj = heim_null_create();
} else if (j->flags & HEIM_JSON_F_NO_C_NULL) {
return EINVAL;
} else {
indent(j);
j->out(j->ctx, "<NULL>\n"); /* This is NOT valid JSON! */
return 0;
}
}
type = heim_get_tid(obj);
@@ -90,20 +140,30 @@ base2json(heim_object_t obj, struct twojson *j)
indent(j);
j->out(j->ctx, "[\n");
j->indent++;
first = j->first;
j->first = 1;
heim_array_iterate_f(obj, j, array2json);
j->indent--;
if (!j->first)
j->out(j->ctx, "\n");
indent(j);
j->out(j->ctx, "]\n");
j->first = first;
break;
case HEIM_TID_DICT:
indent(j);
j->out(j->ctx, "{\n");
j->indent++;
first = j->first;
j->first = 1;
heim_dict_iterate_f(obj, j, dict2json);
j->indent--;
if (!j->first)
j->out(j->ctx, "\n");
indent(j);
j->out(j->ctx, "}\n");
j->first = first;
break;
case HEIM_TID_STRING:
@@ -113,10 +173,65 @@ base2json(heim_object_t obj, struct twojson *j)
j->out(j->ctx, "\"");
break;
case HEIM_TID_DATA: {
heim_dict_t d;
heim_string_t v;
const heim_octet_string *data;
char *b64 = NULL;
int ret;
if (j->flags & HEIM_JSON_F_NO_DATA)
return EINVAL; /* JSON doesn't do binary */
data = heim_data_get_data(obj);
ret = base64_encode(data->data, data->length, &b64);
if (ret < 0 || b64 == NULL)
return ENOMEM;
if (j->flags & HEIM_JSON_F_NO_DATA_DICT) {
indent(j);
j->out(j->ctx, "\"");
j->out(j->ctx, b64); /* base64-encode; hope there's no aliasing */
j->out(j->ctx, "\"");
free(b64);
} else {
/*
* JSON has no way to represent binary data, therefore the
* following is a Heimdal-specific convention.
*
* We encode binary data as a dict with a single very magic
* key with a base64-encoded value. The magic key includes
* a uuid, so we're not likely to alias accidentally.
*/
d = heim_dict_create(2);
if (d == NULL) {
free(b64);
return ENOMEM;
}
v = heim_string_ref_create(b64, free);
if (v == NULL) {
free(b64);
heim_release(d);
return ENOMEM;
}
ret = heim_dict_set_value(d, heim_tid_data_uuid_key, v);
heim_release(v);
if (ret) {
heim_release(d);
return ENOMEM;
}
ret = base2json(d, j);
heim_release(d);
if (ret)
return ret;
}
break;
}
case HEIM_TID_NUMBER: {
char num[16];
char num[32];
indent(j);
snprintf(num, sizeof(num), "%d", heim_number_get_int(obj));
snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
j->out(j->ctx, num);
break;
}
@@ -135,14 +250,22 @@ base2json(heim_object_t obj, struct twojson *j)
}
static int
heim_base2json(heim_object_t obj, void *ctx,
heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
void (*out)(void *, const char *))
{
struct twojson j;
if (flags & HEIM_JSON_F_STRICT_STRINGS)
return ENOTSUP; /* Sorry, not yet! */
heim_base_once_f(&heim_json_once, NULL, json_init_once);
j.indent = 0;
j.ctx = ctx;
j.out = out;
j.flags = flags;
j.ret = 0;
j.first = 1;
return base2json(obj, &j);
}
@@ -158,12 +281,18 @@ struct parse_ctx {
const uint8_t *pstart;
const uint8_t *pend;
heim_error_t error;
size_t depth;
heim_json_flags_t flags;
};
static heim_object_t
parse_value(struct parse_ctx *ctx);
/*
* This function eats whitespace, but, critically, it also succeeds
* only if there's anything left to parse.
*/
static int
white_spaces(struct parse_ctx *ctx)
{
@@ -195,6 +324,8 @@ parse_number(struct parse_ctx *ctx)
return NULL;
if (*ctx->p == '-') {
if (ctx->p + 1 >= ctx->pend)
return NULL;
neg = -1;
ctx->p += 1;
}
@@ -217,7 +348,18 @@ parse_string(struct parse_ctx *ctx)
const uint8_t *start;
int quote = 0;
heim_assert(*ctx->p == '"', "string doesnt' start with \"");
if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) {
ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding "
"not yet supported");
return NULL;
}
if (*ctx->p != '"') {
ctx->error = heim_error_create(EINVAL, "Expected a JSON string but "
"found something else at line %lu",
ctx->lineno);
return NULL;
}
start = ++ctx->p;
while (ctx->p < ctx->pend) {
@@ -226,7 +368,7 @@ parse_string(struct parse_ctx *ctx)
} else if (*ctx->p == '\\') {
if (ctx->p + 1 == ctx->pend)
goto out;
ctx->p += 1;
ctx->p++;
quote = 1;
} else if (*ctx->p == '"') {
heim_object_t o;
@@ -239,7 +381,7 @@ parse_string(struct parse_ctx *ctx)
while (start < ctx->p) {
if (*start == '\\') {
start++;
/* XXX validate qouted char */
/* XXX validate quoted char */
}
*p++ = *start++;
}
@@ -247,11 +389,41 @@ parse_string(struct parse_ctx *ctx)
free(p0);
} else {
o = heim_string_create_with_bytes(start, ctx->p - start);
if (o == NULL) {
ctx->error = heim_error_enomem();
return NULL;
}
/* If we can decode as base64, then let's */
if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) {
void *buf;
size_t len;
const char *s;
s = heim_string_get_utf8(o);
len = strlen(s);
if (len >= 4 && strspn(s, base64_chars) >= len - 2) {
buf = malloc(len);
if (buf == NULL) {
heim_release(o);
ctx->error = heim_error_enomem();
return NULL;
}
len = base64_decode(s, buf);
if (len == -1) {
free(buf);
return o;
}
heim_release(o);
o = heim_data_ref_create(buf, len, free);
}
}
}
ctx->p += 1;
return o;
}
}
ctx->p += 1;
}
out:
@@ -268,22 +440,32 @@ parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
if (white_spaces(ctx))
return -1;
if (*ctx->p == '}')
if (*ctx->p == '}') {
ctx->p++;
return 0;
}
key = parse_string(ctx);
if (ctx->flags & HEIM_JSON_F_STRICT_DICT)
/* JSON allows only string keys */
key = parse_string(ctx);
else
/* heim_dict_t allows any heim_object_t as key */
key = parse_value(ctx);
if (key == NULL)
/* Even heim_dict_t does not allow C NULLs as keys though! */
return -1;
if (white_spaces(ctx))
if (white_spaces(ctx)) {
heim_release(key);
return -1;
}
if (*ctx->p != ':') {
heim_release(key);
return -1;
}
ctx->p += 1;
ctx->p += 1; /* safe because we call white_spaces() next */
if (white_spaces(ctx)) {
heim_release(key);
@@ -291,7 +473,10 @@ parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
}
value = parse_value(ctx);
if (value == NULL) {
if (value == NULL &&
(ctx->error != NULL || (ctx->flags & HEIM_JSON_F_NO_C_NULL))) {
if (ctx->error == NULL)
ctx->error = heim_error_create(EINVAL, "Invalid JSON encoding");
heim_release(key);
return -1;
}
@@ -303,8 +488,11 @@ parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
return -1;
if (*ctx->p == '}') {
ctx->p++;
return 0;
/*
* Return 1 but don't consume the '}' so we can count the one
* pair in a one-pair dict
*/
return 1;
} else if (*ctx->p == ',') {
ctx->p++;
return 1;
@@ -315,18 +503,54 @@ parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
static heim_dict_t
parse_dict(struct parse_ctx *ctx)
{
heim_dict_t dict = heim_dict_create(11);
heim_dict_t dict;
size_t count = 0;
int ret;
heim_assert(*ctx->p == '{', "string doesn't start with {");
ctx->p += 1;
dict = heim_dict_create(11);
if (dict == NULL) {
ctx->error = heim_error_enomem();
return NULL;
}
ctx->p += 1; /* safe because parse_pair() calls white_spaces() first */
while ((ret = parse_pair(dict, ctx)) > 0)
;
count++;
if (ret < 0) {
heim_release(dict);
return NULL;
}
if (count == 1 && !(ctx->flags & HEIM_JSON_F_NO_DATA_DICT)) {
heim_object_t v = heim_dict_copy_value(dict, heim_tid_data_uuid_key);
/*
* Binary data encoded as a dict with a single magic key with
* base64-encoded value? Decode as heim_data_t.
*/
if (v != NULL && heim_get_tid(v) == HEIM_TID_STRING) {
void *buf;
size_t len;
buf = malloc(strlen(heim_string_get_utf8(v)));
if (buf == NULL) {
heim_release(dict);
heim_release(v);
ctx->error = heim_error_enomem();
return NULL;
}
len = base64_decode(heim_string_get_utf8(v), buf);
heim_release(v);
if (len == -1) {
free(buf);
return dict; /* assume aliasing accident */
}
heim_release(dict);
return (heim_dict_t)heim_data_ref_create(buf, len, free);
}
}
return dict;
}
@@ -338,11 +562,14 @@ parse_item(heim_array_t array, struct parse_ctx *ctx)
if (white_spaces(ctx))
return -1;
if (*ctx->p == ']')
if (*ctx->p == ']') {
ctx->p++; /* safe because parse_value() calls white_spaces() first */
return 0;
}
value = parse_value(ctx);
if (value == NULL)
if (value == NULL &&
(ctx->error || (ctx->flags & HEIM_JSON_F_NO_C_NULL)))
return -1;
heim_array_append_value(array, value);
@@ -383,6 +610,7 @@ static heim_object_t
parse_value(struct parse_ctx *ctx)
{
size_t len;
heim_object_t o;
if (white_spaces(ctx))
return NULL;
@@ -390,16 +618,32 @@ parse_value(struct parse_ctx *ctx)
if (*ctx->p == '"') {
return parse_string(ctx);
} else if (*ctx->p == '{') {
return parse_dict(ctx);
if (ctx->depth-- == 1) {
ctx->error = heim_error_create(EINVAL, "JSON object too deep");
return NULL;
}
o = parse_dict(ctx);
ctx->depth++;
return o;
} else if (*ctx->p == '[') {
return parse_array(ctx);
if (ctx->depth-- == 1) {
ctx->error = heim_error_create(EINVAL, "JSON object too deep");
return NULL;
}
o = parse_array(ctx);
ctx->depth++;
return o;
} else if (is_number(*ctx->p) || *ctx->p == '-') {
return parse_number(ctx);
}
len = ctx->pend - ctx->p;
if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) {
if ((ctx->flags & HEIM_JSON_F_NO_C_NULL) == 0 &&
len >= 6 && memcmp(ctx->p, "<NULL>", 6) == 0) {
ctx->p += 6;
return heim_null_create();
} else if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) {
ctx->p += 4;
return heim_null_create();
} else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) {
@@ -419,22 +663,29 @@ parse_value(struct parse_ctx *ctx)
heim_object_t
heim_json_create(const char *string, heim_error_t *error)
heim_json_create(const char *string, size_t max_depth, heim_json_flags_t flags,
heim_error_t *error)
{
return heim_json_create_with_bytes(string, strlen(string), error);
return heim_json_create_with_bytes(string, strlen(string), max_depth, flags,
error);
}
heim_object_t
heim_json_create_with_bytes(const void *data, size_t length, heim_error_t *error)
heim_json_create_with_bytes(const void *data, size_t length, size_t max_depth,
heim_json_flags_t flags, heim_error_t *error)
{
struct parse_ctx ctx;
heim_object_t o;
heim_base_once_f(&heim_json_once, NULL, json_init_once);
ctx.lineno = 1;
ctx.p = data;
ctx.pstart = data;
ctx.pend = ((uint8_t *)data) + length;
ctx.error = NULL;
ctx.flags = flags;
ctx.depth = max_depth;
o = parse_value(&ctx);
@@ -451,11 +702,110 @@ heim_json_create_with_bytes(const void *data, size_t length, heim_error_t *error
static void
show_printf(void *ctx, const char *str)
{
if (str == NULL)
return;
fprintf(ctx, "%s", str);
}
/**
* Dump a heimbase object to stderr (useful from the debugger!)
*
* @param obj object to dump using JSON or JSON-like format
*
* @addtogroup heimbase
*/
void
heim_show(heim_object_t obj)
{
heim_base2json(obj, stderr, show_printf);
heim_base2json(obj, stderr, HEIM_JSON_F_NO_DATA_DICT, show_printf);
}
static void
strbuf_add(void *ctx, const char *str)
{
struct strbuf *strbuf = ctx;
size_t len;
if (strbuf->enomem)
return;
if (str == NULL) {
/*
* Eat the last '\n'; this is used when formatting dict pairs
* and array items so that the ',' separating them is never
* preceded by a '\n'.
*/
if (strbuf->len > 0 && strbuf->str[strbuf->len - 1] == '\n')
strbuf->len--;
return;
}
len = strlen(str);
if ((len + 1) > (strbuf->alloced - strbuf->len)) {
size_t new_len = strbuf->alloced + (strbuf->alloced >> 2) + len + 1;
char *s;
s = realloc(strbuf->str, new_len);
if (s == NULL) {
strbuf->enomem = 1;
return;
}
strbuf->str = s;
strbuf->alloced = new_len;
}
/* +1 so we copy the NUL */
(void) memcpy(strbuf->str + strbuf->len, str, len + 1);
strbuf->len += len;
if (strbuf->str[strbuf->len - 1] == '\n' &&
strbuf->flags & HEIM_JSON_F_ONE_LINE)
strbuf->len--;
}
#define STRBUF_INIT_SZ 64
heim_string_t
heim_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_t *error)
{
heim_string_t str;
struct strbuf strbuf;
int ret;
if (error)
*error = NULL;
memset(&strbuf, 0, sizeof (strbuf));
strbuf.str = malloc(STRBUF_INIT_SZ);
if (strbuf.str == NULL) {
if (error)
*error = heim_error_enomem();
return NULL;
}
strbuf.len = 0;
strbuf.alloced = STRBUF_INIT_SZ;
strbuf.str[0] = '\0';
strbuf.flags = flags;
ret = heim_base2json(obj, &strbuf, flags, strbuf_add);
if (ret || strbuf.enomem) {
if (error) {
if (strbuf.enomem || ret == ENOMEM)
*error = heim_error_enomem();
else
*error = heim_error_create(1, "Impossible to JSON-encode "
"object");
}
free(strbuf.str);
return NULL;
}
if (flags & HEIM_JSON_F_ONE_LINE) {
strbuf.flags &= ~HEIM_JSON_F_ONE_LINE;
strbuf_add(&strbuf, "\n");
}
str = heim_string_ref_create(strbuf.str, free);
if (str == NULL) {
if (error)
*error = heim_error_enomem();
free(strbuf.str);
}
return str;
}
+6 -3
View File
@@ -36,9 +36,6 @@
#ifndef __heimbase_roken_rename_h__
#define __heimbase_roken_rename_h__
#ifndef HAVE_SNPRINTF
#define rk_snprintf heimbase_snprintf
#endif
#ifndef HAVE_VSNPRINTF
#define rk_vsnprintf heimbase_vsnprintf
#endif
@@ -54,5 +51,11 @@
#ifndef HAVE_VASNPRINTF
#define rk_vasnprintf heimbase_vasnprintf
#endif
#ifndef HAVE_STRDUP
#define rk_strdup heimbase_strdup
#endif
#ifndef HAVE_STRNDUP
#define rk_strndup heimbase_strndup
#endif
#endif /* __heimbase_roken_rename_h__ */
+71
View File
@@ -39,11 +39,37 @@
static void
string_dealloc(void *ptr)
{
heim_string_t s = ptr;
heim_string_free_f_t *deallocp;
heim_string_free_f_t dealloc;
if (*(const char *)ptr != '\0')
return;
/* Possible string ref */
deallocp = _heim_get_isaextra(s, 0);
dealloc = *deallocp;
if (dealloc != NULL) {
char **strp = _heim_get_isaextra(s, 1);
dealloc(*strp);
}
}
static int
string_cmp(void *a, void *b)
{
if (*(char *)a == '\0') {
char **strp = _heim_get_isaextra(a, 1);
if (*strp != NULL)
a = *strp; /* a is a string ref */
}
if (*(char *)b == '\0') {
char **strp = _heim_get_isaextra(b, 1);
if (*strp != NULL)
b = *strp; /* b is a string ref */
}
return strcmp(a, b);
}
@@ -82,6 +108,43 @@ heim_string_create(const char *string)
return heim_string_create_with_bytes(string, strlen(string));
}
/**
* Create a string object without copying the source.
*
* @param string the string to referenced, must be UTF-8
* @param dealloc the function to use to release the referece to the string
*
* @return string object
*/
heim_string_t
heim_string_ref_create(const char *string, heim_string_free_f_t dealloc)
{
heim_string_t s;
heim_string_free_f_t *deallocp;
s = _heim_alloc_object(&_heim_string_object, 1);
if (s) {
const char **strp;
((char *)s)[0] = '\0';
deallocp = _heim_get_isaextra(s, 0);
*deallocp = dealloc;
strp = _heim_get_isaextra(s, 1);
*strp = string;
}
return s;
}
/**
* Create a string object
*
* @param string the string to create, must be an utf8 string
* @param len the length of the string
*
* @return string object
*/
heim_string_t
heim_string_create_with_bytes(const void *data, size_t len)
{
@@ -118,6 +181,14 @@ heim_string_get_type_id(void)
const char *
heim_string_get_utf8(heim_string_t string)
{
if (*(const char *)string == '\0') {
const char **strp;
/* String ref */
strp = _heim_get_isaextra(string, 1);
if (*strp != NULL)
return *strp;
}
return (const char *)string;
}
+672 -15
View File
@@ -33,12 +33,35 @@
* SUCH DAMAGE.
*/
/*
* This is a test of libheimbase functionality. If you make any changes
* to libheimbase or to this test you should run it under valgrind with
* the following options:
*
* -v --track-fds=yes --num-callers=30 --leak-check=full
*
* and make sure that there are no leaks that don't have
* __heim_string_constant() or heim_db_register() in their stack trace.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN32
#include <sys/file.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#include "heimbase.h"
#include "heimbasepriv.h"
#include "baselocl.h"
static void
memory_free(heim_object_t obj)
@@ -160,72 +183,702 @@ test_error(void)
static int
test_json(void)
{
static char *j[] = {
"{ \"k1\" : \"s1\", \"k2\" : \"s2\" }",
"{ \"k1\" : [\"s1\", \"s2\", \"s3\"], \"k2\" : \"s3\" }",
"{ \"k1\" : {\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"}, \"k5\" : \"s4\" }",
"[ \"v1\", \"v2\", [\"v3\",\"v4\",[\"v 5\",\" v 7 \"]], -123456789, "
"null, true, false, 123456789, \"\"]",
" -1"
};
char *s;
size_t i, k;
heim_object_t o, o2;
heim_string_t k1 = heim_string_create("k1");
o = heim_json_create("\"string\"", NULL);
o = heim_json_create("\"string\"", 10, 0, NULL);
heim_assert(o != NULL, "string");
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string");
heim_release(o);
o = heim_json_create(" \"foo\\\"bar\" ]", NULL);
o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL);
heim_assert(o != NULL, "string");
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string");
heim_release(o);
o = heim_json_create(" { \"key\" : \"value\" }", NULL);
o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL);
heim_assert(o != NULL, "dict");
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
heim_release(o);
o = heim_json_create(" { \"k1\" : \"s1\", \"k2\" : \"s2\" }", NULL);
o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
"{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
heim_assert(o != NULL, "dict");
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
o2 = heim_dict_get_value(o, k1);
heim_release(o);
o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
"{ \"k3\" : \"s4\" } : -1 }", 10,
HEIM_JSON_F_STRICT_DICT, NULL);
heim_assert(o == NULL, "dict");
o = heim_json_create(" { \"k1\" : \"s1\", \"k2\" : \"s2\" }", 10, 0, NULL);
heim_assert(o != NULL, "dict");
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
heim_release(o2);
heim_release(o);
o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", NULL);
o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
heim_assert(o != NULL, "dict");
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
o2 = heim_dict_get_value(o, k1);
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
heim_release(o2);
heim_release(o);
o = heim_json_create("{ \"k1\" : 1 }", NULL);
o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
heim_assert(o != NULL, "array");
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
o2 = heim_dict_get_value(o, k1);
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
heim_release(o2);
heim_release(o);
o = heim_json_create("-10", NULL);
o = heim_json_create("-10", 10, 0, NULL);
heim_assert(o != NULL, "number");
heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
heim_release(o);
o = heim_json_create("99", NULL);
o = heim_json_create("99", 10, 0, NULL);
heim_assert(o != NULL, "number");
heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
heim_release(o);
o = heim_json_create(" [ 1 ]", NULL);
o = heim_json_create(" [ 1 ]", 10, 0, NULL);
heim_assert(o != NULL, "array");
heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
heim_release(o);
o = heim_json_create(" [ -1 ]", NULL);
o = heim_json_create(" [ -1 ]", 10, 0, NULL);
heim_assert(o != NULL, "array");
heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
heim_release(o);
for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
o = heim_json_create(j[i], 10, 0, NULL);
if (o == NULL) {
fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
return 1;
}
heim_release(o);
/* Simple fuzz test */
for (k = strlen(j[i]) - 1; k > 0; k--) {
o = heim_json_create_with_bytes(j[i], k, 10, 0, NULL);
if (o != NULL) {
fprintf(stderr, "Invalid JSON parsed: %.*s\n", k, j[i]);
return EINVAL;
}
}
/* Again, but this time make it so valgrind can find invalid accesses */
for (k = strlen(j[i]) - 1; k > 0; k--) {
s = strndup(j[i], k);
if (s == NULL)
return ENOMEM;
o = heim_json_create(s, 10, 0, NULL);
free(s);
if (o != NULL) {
fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
return EINVAL;
}
}
/* Again, but with no NUL termination */
for (k = strlen(j[i]) - 1; k > 0; k--) {
s = malloc(k);
if (s == NULL)
return ENOMEM;
memcpy(s, j[i], k);
o = heim_json_create_with_bytes(s, k, 10, 0, NULL);
free(s);
if (o != NULL) {
fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
return EINVAL;
}
}
}
heim_release(k1);
return 0;
}
static int
test_path(void)
{
heim_dict_t dict = heim_dict_create(11);
heim_string_t p1 = heim_string_create("abc");
heim_string_t p2a = heim_string_create("def");
heim_string_t p2b = heim_string_create("DEF");
heim_number_t p3 = heim_number_create(0);
heim_string_t p4a = heim_string_create("ghi");
heim_string_t p4b = heim_string_create("GHI");
heim_array_t a = heim_array_create();
heim_number_t l1 = heim_number_create(42);
heim_number_t l2 = heim_number_create(813);
heim_number_t l3 = heim_number_create(1234);
heim_string_t k1 = heim_string_create("k1");
heim_string_t k2 = heim_string_create("k2");
heim_string_t k3 = heim_string_create("k3");
heim_string_t k2_1 = heim_string_create("k2-1");
heim_string_t k2_2 = heim_string_create("k2-2");
heim_string_t k2_3 = heim_string_create("k2-3");
heim_string_t k2_4 = heim_string_create("k2-4");
heim_string_t k2_5 = heim_string_create("k2-5");
heim_string_t k2_5_1 = heim_string_create("k2-5-1");
heim_object_t o;
heim_object_t neg_num;
int ret;
if (!dict || !p1 || !p2a || !p2b || !p4a || !p4b)
return ENOMEM;
ret = heim_path_create(dict, 11, a, NULL, p1, p2a, NULL);
heim_release(a);
if (ret)
return ret;
ret = heim_path_create(dict, 11, l3, NULL, p1, p2b, NULL);
if (ret)
return ret;
o = heim_path_get(dict, NULL, p1, p2b, NULL);
if (o != l3)
return 1;
ret = heim_path_create(dict, 11, NULL, NULL, p1, p2a, p3, NULL);
if (ret)
return ret;
ret = heim_path_create(dict, 11, l1, NULL, p1, p2a, p3, p4a, NULL);
if (ret)
return ret;
ret = heim_path_create(dict, 11, l2, NULL, p1, p2a, p3, p4b, NULL);
if (ret)
return ret;
o = heim_path_get(dict, NULL, p1, p2a, p3, p4a, NULL);
if (o != l1)
return 1;
o = heim_path_get(dict, NULL, p1, p2a, p3, p4b, NULL);
if (o != l2)
return 1;
heim_release(dict);
/* Test that JSON parsing works right by using heim_path_get() */
dict = heim_json_create("{\"k1\":1,"
"\"k2\":{\"k2-1\":21,"
"\"k2-2\":null,"
"\"k2-3\":true,"
"\"k2-4\":false,"
"\"k2-5\":[1,2,3,{\"k2-5-1\":-1},-2]},"
"\"k3\":[true,false,0,42]}", 10, 0, NULL);
heim_assert(dict != NULL, "dict");
o = heim_path_get(dict, NULL, k1, NULL);
if (heim_cmp(o, heim_number_create(1))) return 1;
o = heim_path_get(dict, NULL, k2, NULL);
if (heim_get_tid(o) != heim_dict_get_type_id()) return 1;
o = heim_path_get(dict, NULL, k2, k2_1, NULL);
if (heim_cmp(o, heim_number_create(21))) return 1;
o = heim_path_get(dict, NULL, k2, k2_2, NULL);
if (heim_cmp(o, heim_null_create())) return 1;
o = heim_path_get(dict, NULL, k2, k2_3, NULL);
if (heim_cmp(o, heim_bool_create(1))) return 1;
o = heim_path_get(dict, NULL, k2, k2_4, NULL);
if (heim_cmp(o, heim_bool_create(0))) return 1;
o = heim_path_get(dict, NULL, k2, k2_5, NULL);
if (heim_get_tid(o) != heim_array_get_type_id()) return 1;
o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(0), NULL);
if (heim_cmp(o, heim_number_create(1))) return 1;
o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(1), NULL);
if (heim_cmp(o, heim_number_create(2))) return 1;
o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(3), k2_5_1, NULL);
if (heim_cmp(o, neg_num = heim_number_create(-1))) return 1;
heim_release(neg_num);
o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(4), NULL);
if (heim_cmp(o, neg_num = heim_number_create(-2))) return 1;
heim_release(neg_num);
o = heim_path_get(dict, NULL, k3, heim_number_create(3), NULL);
if (heim_cmp(o, heim_number_create(42))) return 1;
heim_release(dict);
heim_release(p1);
heim_release(p2a);
heim_release(p2b);
heim_release(p4a);
heim_release(p4b);
heim_release(k1);
heim_release(k2);
heim_release(k3);
heim_release(k2_1);
heim_release(k2_2);
heim_release(k2_3);
heim_release(k2_4);
heim_release(k2_5);
heim_release(k2_5_1);
return 0;
}
typedef struct dict_db {
heim_dict_t dict;
int locked;
} *dict_db_t;
static int
dict_db_open(void *plug, const char *dbtype, const char *dbname,
heim_dict_t options, void **db, heim_error_t *error)
{
dict_db_t dictdb;
heim_dict_t contents = NULL;
if (error)
*error = NULL;
if (dbtype && *dbtype && strcmp(dbtype, "dictdb"))
return EINVAL;
if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0)
return EINVAL;
dictdb = heim_alloc(sizeof (*dictdb), "dict_db", NULL);
if (dictdb == NULL)
return ENOMEM;
if (contents != NULL)
dictdb->dict = contents;
else {
dictdb->dict = heim_dict_create(29);
if (dictdb->dict == NULL) {
heim_release(dictdb);
return ENOMEM;
}
}
*db = dictdb;
return 0;
}
static int
dict_db_close(void *db, heim_error_t *error)
{
dict_db_t dictdb = db;
if (error)
*error = NULL;
heim_release(dictdb->dict);
heim_release(dictdb);
return 0;
}
static int
dict_db_lock(void *db, int read_only, heim_error_t *error)
{
dict_db_t dictdb = db;
if (error)
*error = NULL;
if (dictdb->locked)
return EWOULDBLOCK;
dictdb->locked = 1;
return 0;
}
static int
dict_db_unlock(void *db, heim_error_t *error)
{
dict_db_t dictdb = db;
if (error)
*error = NULL;
dictdb->locked = 0;
return 0;
}
static heim_data_t
dict_db_copy_value(void *db, heim_string_t table, heim_data_t key,
heim_error_t *error)
{
dict_db_t dictdb = db;
if (error)
*error = NULL;
return heim_retain(heim_path_get(dictdb->dict, error, table, key, NULL));
}
static int
dict_db_set_value(void *db, heim_string_t table,
heim_data_t key, heim_data_t value, heim_error_t *error)
{
dict_db_t dictdb = db;
if (error)
*error = NULL;
if (table == NULL)
table = HSTR("");
return heim_path_create(dictdb->dict, 29, value, error, table, key, NULL);
}
static int
dict_db_del_key(void *db, heim_string_t table, heim_data_t key,
heim_error_t *error)
{
dict_db_t dictdb = db;
if (error)
*error = NULL;
if (table == NULL)
table = HSTR("");
heim_path_delete(dictdb->dict, error, table, key, NULL);
return 0;
}
struct dict_db_iter_ctx {
heim_db_iterator_f_t iter_f;
void *iter_ctx;
};
static void dict_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
{
struct dict_db_iter_ctx *ctx = arg;
ctx->iter_f((heim_object_t)key, (heim_object_t)value, ctx->iter_ctx);
}
static void
dict_db_iter(void *db, heim_string_t table, void *iter_data,
heim_db_iterator_f_t iter_f, heim_error_t *error)
{
dict_db_t dictdb = db;
struct dict_db_iter_ctx ctx;
heim_dict_t table_dict;
if (error)
*error = NULL;
if (table == NULL)
table = HSTR("");
table_dict = heim_dict_copy_value(dictdb->dict, table);
if (table_dict == NULL)
return;
ctx.iter_ctx = iter_data;
ctx.iter_f = iter_f;
heim_dict_iterate_f(table_dict, &ctx, dict_db_iter_f);
heim_release(table_dict);
}
static void
test_db_iter(heim_data_t k, heim_data_t v, void *arg)
{
int *ret = arg;
heim_assert(heim_get_tid(k) == heim_data_get_type_id(), "...");
if (heim_data_get_length(k) == strlen("msg") && strncmp(heim_data_get_ptr(k), "msg", strlen("msg")) == 0 &&
heim_data_get_length(v) == strlen("abc") && strncmp(heim_data_get_ptr(v), "abc", strlen("abc")) == 0)
*ret &= ~(1);
else if (heim_data_get_length(k) == strlen("msg2") && strncmp(heim_data_get_ptr(k), "msg2", strlen("msg2")) == 0 &&
heim_data_get_length(v) == strlen("FooBar") && strncmp(heim_data_get_ptr(v), "FooBar", strlen("FooBar")) == 0)
*ret &= ~(2);
else
*ret |= 4;
}
static struct heim_db_type dbt = {
1, dict_db_open, NULL, dict_db_close,
dict_db_lock, dict_db_unlock, NULL, NULL, NULL, NULL,
dict_db_copy_value, dict_db_set_value,
dict_db_del_key, dict_db_iter
};
static int
test_db(const char *dbtype, const char *dbname)
{
heim_data_t k1, k2, v, v1, v2, v3;
heim_db_t db;
int ret;
if (dbtype == NULL) {
ret = heim_db_register("dictdb", NULL, &dbt);
heim_assert(!ret, "...");
db = heim_db_create("dictdb", "foo", NULL, NULL);
heim_assert(!db, "...");
db = heim_db_create("foobar", "MEMORY", NULL, NULL);
heim_assert(!db, "...");
db = heim_db_create("dictdb", "MEMORY", NULL, NULL);
heim_assert(db, "...");
} else {
heim_dict_t options;
options = heim_dict_create(11);
if (options == NULL) return ENOMEM;
if (heim_dict_set_value(options, HSTR("journal-filename"),
HSTR("json-journal")))
return ENOMEM;
if (heim_dict_set_value(options, HSTR("create"), heim_null_create()))
return ENOMEM;
if (heim_dict_set_value(options, HSTR("truncate"), heim_null_create()))
return ENOMEM;
db = heim_db_create(dbtype, dbname, options, NULL);
heim_assert(db, "...");
heim_release(options);
}
k1 = heim_data_create("msg", strlen("msg"));
k2 = heim_data_create("msg2", strlen("msg2"));
v1 = heim_data_create("Hello world!", strlen("Hello world!"));
v2 = heim_data_create("FooBar", strlen("FooBar"));
v3 = heim_data_create("abc", strlen("abc"));
ret = heim_db_set_value(db, NULL, k1, v1, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k1, NULL);
heim_assert(v && !heim_cmp(v, v1), "...");
heim_release(v);
ret = heim_db_set_value(db, NULL, k2, v2, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k2, NULL);
heim_assert(v && !heim_cmp(v, v2), "...");
heim_release(v);
ret = heim_db_set_value(db, NULL, k1, v3, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k1, NULL);
heim_assert(v && !heim_cmp(v, v3), "...");
heim_release(v);
ret = 3;
heim_db_iterate_f(db, NULL, &ret, test_db_iter, NULL);
heim_assert(!ret, "...");
ret = heim_db_begin(db, 0, NULL);
heim_assert(!ret, "...");
ret = heim_db_commit(db, NULL);
heim_assert(!ret, "...");
ret = heim_db_begin(db, 0, NULL);
heim_assert(!ret, "...");
ret = heim_db_rollback(db, NULL);
heim_assert(!ret, "...");
ret = heim_db_begin(db, 0, NULL);
heim_assert(!ret, "...");
ret = heim_db_set_value(db, NULL, k1, v1, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k1, NULL);
heim_assert(v && !heim_cmp(v, v1), "...");
heim_release(v);
ret = heim_db_rollback(db, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k1, NULL);
heim_assert(v && !heim_cmp(v, v3), "...");
heim_release(v);
ret = heim_db_begin(db, 0, NULL);
heim_assert(!ret, "...");
ret = heim_db_set_value(db, NULL, k1, v1, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k1, NULL);
heim_assert(v && !heim_cmp(v, v1), "...");
heim_release(v);
ret = heim_db_commit(db, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k1, NULL);
heim_assert(v && !heim_cmp(v, v1), "...");
heim_release(v);
ret = heim_db_begin(db, 0, NULL);
heim_assert(!ret, "...");
ret = heim_db_delete_key(db, NULL, k1, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k1, NULL);
heim_assert(v == NULL, "...");
heim_release(v);
ret = heim_db_rollback(db, NULL);
heim_assert(!ret, "...");
v = heim_db_copy_value(db, NULL, k1, NULL);
heim_assert(v && !heim_cmp(v, v1), "...");
heim_release(v);
if (dbtype != NULL) {
heim_data_t k3 = heim_data_create("value-is-a-dict", strlen("value-is-a-dict"));
heim_dict_t vdict = heim_dict_create(11);
heim_db_t db2;
heim_assert(k3 && vdict, "...");
ret = heim_dict_set_value(vdict, HSTR("vdict-k1"), heim_number_create(11));
heim_assert(!ret, "...");
ret = heim_dict_set_value(vdict, HSTR("vdict-k2"), heim_null_create());
heim_assert(!ret, "...");
ret = heim_dict_set_value(vdict, HSTR("vdict-k3"), HSTR("a value"));
heim_assert(!ret, "...");
ret = heim_db_set_value(db, NULL, k3, (heim_data_t)vdict, NULL);
heim_assert(!ret, "...");
heim_release(vdict);
db2 = heim_db_create(dbtype, dbname, NULL, NULL);
heim_assert(db2, "...");
vdict = (heim_dict_t)heim_db_copy_value(db2, NULL, k3, NULL);
heim_release(db2);
heim_release(k3);
heim_assert(vdict, "...");
heim_assert(heim_get_tid(vdict) == heim_dict_get_type_id(), "...");
v = heim_dict_copy_value(vdict, HSTR("vdict-k1"));
heim_assert(v && !heim_cmp(v, heim_number_create(11)), "...");
heim_release(v);
v = heim_dict_copy_value(vdict, HSTR("vdict-k2"));
heim_assert(v && !heim_cmp(v, heim_null_create()), "...");
heim_release(v);
v = heim_dict_copy_value(vdict, HSTR("vdict-k3"));
heim_assert(v && !heim_cmp(v, HSTR("a value")), "...");
heim_release(v);
heim_release(vdict);
}
heim_release(db);
heim_release(k1);
heim_release(k2);
heim_release(v1);
heim_release(v2);
heim_release(v3);
return 0;
}
struct test_array_iter_ctx {
char buf[256];
};
static void test_array_iter(heim_object_t elt, void *arg)
{
struct test_array_iter_ctx *iter_ctx = arg;
strcat(iter_ctx->buf, heim_string_get_utf8((heim_string_t)elt));
}
static int
test_array()
{
struct test_array_iter_ctx iter_ctx;
heim_string_t s1 = heim_string_create("abc");
heim_string_t s2 = heim_string_create("def");
heim_string_t s3 = heim_string_create("ghi");
heim_string_t s4 = heim_string_create("jkl");
heim_string_t s5 = heim_string_create("mno");
heim_string_t s6 = heim_string_create("pqr");
heim_array_t a = heim_array_create();
if (!s1 || !s2 || !s3 || !s4 || !s5 || !s6 || !a)
return ENOMEM;
heim_array_append_value(a, s4);
heim_array_append_value(a, s5);
heim_array_insert_value(a, 0, s3);
heim_array_insert_value(a, 0, s2);
heim_array_append_value(a, s6);
heim_array_insert_value(a, 0, s1);
iter_ctx.buf[0] = '\0';
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "abcdefghijklmnopqr") != 0)
return 1;
iter_ctx.buf[0] = '\0';
heim_array_delete_value(a, 2);
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "abcdefjklmnopqr") != 0)
return 1;
iter_ctx.buf[0] = '\0';
heim_array_delete_value(a, 2);
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "abcdefmnopqr") != 0)
return 1;
iter_ctx.buf[0] = '\0';
heim_array_delete_value(a, 0);
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "defmnopqr") != 0)
return 1;
iter_ctx.buf[0] = '\0';
heim_array_delete_value(a, 2);
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "defmno") != 0)
return 1;
heim_array_insert_value(a, 0, s1);
iter_ctx.buf[0] = '\0';
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "abcdefmno") != 0)
return 1;
heim_array_insert_value(a, 0, s2);
iter_ctx.buf[0] = '\0';
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "defabcdefmno") != 0)
return 1;
heim_array_append_value(a, s3);
iter_ctx.buf[0] = '\0';
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "defabcdefmnoghi") != 0)
return 1;
heim_array_append_value(a, s6);
iter_ctx.buf[0] = '\0';
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
if (strcmp(iter_ctx.buf, "defabcdefmnoghipqr") != 0)
return 1;
heim_release(s1);
heim_release(s2);
heim_release(s3);
heim_release(s4);
heim_release(s5);
heim_release(s6);
heim_release(a);
return 0;
}
int
main(int argc, char **argv)
@@ -238,6 +891,10 @@ main(int argc, char **argv)
res |= test_string();
res |= test_error();
res |= test_json();
res |= test_path();
res |= test_db(NULL, NULL);
res |= test_db("json", argc > 1 ? argv[1] : "test_db.json");
res |= test_array();
return res ? 1 : 0;
}
+43 -5
View File
@@ -1,21 +1,26 @@
HEIMDAL_BASE_1.0 {
global:
__bsearch_file;
__bsearch_file_close;
__bsearch_file_info;
__bsearch_file_open;
__bsearch_text;
_bsearch_file;
_bsearch_file_close;
_bsearch_file_info;
_bsearch_file_open;
_bsearch_text;
__heim_string_constant;
heim_abort;
heim_abortv;
heim_alloc;
heim_array_append_value;
heim_array_copy_value;
heim_array_create;
heim_array_delete_value;
heim_array_get_length;
heim_array_get_type_id;
heim_array_get_value;
heim_array_iterate_f;
heim_array_iterate_reverse_f;
heim_array_insert_value;
heim_array_set_value;
heim_auto_release;
heim_auto_release_create;
heim_auto_release_drain;
@@ -23,6 +28,25 @@ HEIMDAL_BASE_1.0 {
heim_bool_create;
heim_bool_val;
heim_cmp;
heim_data_create;
heim_data_ref_create;
heim_data_get_data;
heim_data_get_length;
heim_data_get_ptr;
heim_data_get_type_id;
heim_data_ref_get_type_id;
heim_db_begin;
heim_db_clone;
heim_db_commit;
heim_db_copy_value;
heim_db_delete_key;
heim_db_get_type_id;
heim_db_iterate_f;
heim_db_create;
heim_db_register;
heim_db_rollback;
heim_db_set_value;
heim_dict_copy_value;
heim_dict_create;
heim_dict_delete_key;
heim_dict_get_type_id;
@@ -33,20 +57,34 @@ HEIMDAL_BASE_1.0 {
heim_error_copy_string;
heim_error_create;
heim_error_createv;
heim_error_enomem;
heim_error_get_code;
heim_get_hash;
heim_get_tid;
heim_json_create;
heim_json_create_with_bytes;
heim_null_create;
heim_number_create;
heim_number_get_int;
heim_number_get_type_id;
heim_path_create;
heim_path_delete;
heim_path_get;
heim_path_copy;
heim_path_vcreate;
heim_path_vdelete;
heim_path_vget;
heim_path_vcopy;
heim_release;
heim_retain;
heim_serialize;
heim_show;
heim_sorted_text_file_dbtype;
heim_string_create;
heim_string_create_with_bytes;
heim_string_get_type_id;
heim_string_get_utf8;
heim_string_ref_create;
local:
*;
};