Move base into lib
This involves reverting dd267e8fc3
,
but that gets lost in the move.
This builds on Ubuntu and Windows at this time.
This commit is contained in:
55
lib/base/Makefile.am
Normal file
55
lib/base/Makefile.am
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
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
|
||||
|
||||
libheimbase_la_LDFLAGS = -version-info 1:0:0
|
||||
|
||||
TESTS = test_base
|
||||
|
||||
if versionscript
|
||||
libheimbase_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
|
||||
endif
|
||||
|
||||
include_HEADERS = heimbase.h
|
||||
|
||||
dist_libheimbase_la_SOURCES = \
|
||||
array.c \
|
||||
baselocl.h \
|
||||
bsearch.c \
|
||||
bool.c \
|
||||
data.c \
|
||||
db.c \
|
||||
dict.c \
|
||||
error.c \
|
||||
heimbase.c \
|
||||
heimbasepriv.h \
|
||||
heimqueue.h \
|
||||
json.c \
|
||||
null.c \
|
||||
number.c \
|
||||
roken_rename.h \
|
||||
string.c
|
||||
|
||||
nodist_libheimbase_la_SOURCES = $(ES)
|
||||
|
||||
# install these?
|
||||
|
||||
libheimbase_la_DEPENDENCIES = version-script.map
|
||||
|
||||
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 .
|
81
lib/base/NTMakefile
Normal file
81
lib/base/NTMakefile
Normal file
@@ -0,0 +1,81 @@
|
||||
########################################################################
|
||||
#
|
||||
# Copyright (c) 2010, Secure Endpoints Inc.
|
||||
# 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.
|
||||
#
|
||||
# - Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# 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 HOLDER 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.
|
||||
#
|
||||
|
||||
RELDIR=lib\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)\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_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
|
439
lib/base/array.c
Normal file
439
lib/base/array.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
struct heim_array_data {
|
||||
size_t len;
|
||||
heim_object_t *val;
|
||||
size_t allocated_len;
|
||||
heim_object_t *allocated;
|
||||
};
|
||||
|
||||
static void
|
||||
array_dealloc(heim_object_t ptr)
|
||||
{
|
||||
heim_array_t array = ptr;
|
||||
size_t n;
|
||||
for (n = 0; n < array->len; n++)
|
||||
heim_release(array->val[n]);
|
||||
free(array->allocated);
|
||||
}
|
||||
|
||||
struct heim_type_data array_object = {
|
||||
HEIM_TID_ARRAY,
|
||||
"dict-object",
|
||||
NULL,
|
||||
array_dealloc,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate an array
|
||||
*
|
||||
* @return A new allocated array, free with heim_release()
|
||||
*/
|
||||
|
||||
heim_array_t
|
||||
heim_array_create(void)
|
||||
{
|
||||
heim_array_t array;
|
||||
|
||||
array = _heim_alloc_object(&array_object, sizeof(*array));
|
||||
if (array == NULL)
|
||||
return NULL;
|
||||
|
||||
array->allocated = NULL;
|
||||
array->allocated_len = 0;
|
||||
array->val = NULL;
|
||||
array->len = 0;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type id of an dict
|
||||
*
|
||||
* @return the type id
|
||||
*/
|
||||
|
||||
heim_tid_t
|
||||
heim_array_get_type_id(void)
|
||||
{
|
||||
return HEIM_TID_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append object to array
|
||||
*
|
||||
* @param array array to add too
|
||||
* @param object the object to add
|
||||
*
|
||||
* @return zero if added, errno otherwise
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
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->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
|
||||
*
|
||||
* @param array array to iterate over
|
||||
* @param ctx context passed to fn
|
||||
* @param fn function to call on each object
|
||||
*/
|
||||
|
||||
void
|
||||
heim_array_iterate_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
|
||||
{
|
||||
size_t n;
|
||||
for (n = 0; n < array->len; n++)
|
||||
fn(array->val[n], ctx);
|
||||
}
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
/**
|
||||
* Iterate over all objects in array
|
||||
*
|
||||
* @param array array to iterate over
|
||||
* @param fn block to call on each object
|
||||
*/
|
||||
|
||||
void
|
||||
heim_array_iterate(heim_array_t array, void (^fn)(heim_object_t))
|
||||
{
|
||||
size_t n;
|
||||
for (n = 0; n < array->len; n++)
|
||||
fn(array->val[n]);
|
||||
}
|
||||
#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
|
||||
*
|
||||
* @param array array to get length of
|
||||
*
|
||||
* @return length of array
|
||||
*/
|
||||
|
||||
size_t
|
||||
heim_array_get_length(heim_array_t array)
|
||||
{
|
||||
return array->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* heim_array_get_length()
|
||||
*
|
||||
* @return a retained copy of the object
|
||||
*/
|
||||
|
||||
heim_object_t
|
||||
heim_array_copy_value(heim_array_t array, size_t idx)
|
||||
{
|
||||
if (idx >= array->len)
|
||||
heim_abort("index too large");
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete value at idx
|
||||
*
|
||||
* @param array the array to modify
|
||||
* @param idx the key to delete
|
||||
*/
|
||||
|
||||
void
|
||||
heim_array_delete_value(heim_array_t array, size_t idx)
|
||||
{
|
||||
heim_object_t obj;
|
||||
if (idx >= array->len)
|
||||
heim_abort("index too large");
|
||||
obj = array->val[idx];
|
||||
|
||||
array->len--;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
/**
|
||||
* Get value at idx
|
||||
*
|
||||
* @param array the array to modify
|
||||
* @param idx the key to delete
|
||||
*/
|
||||
|
||||
void
|
||||
heim_array_filter(heim_array_t array, int (^block)(heim_object_t))
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
while (n < array->len) {
|
||||
if (block(array->val[n])) {
|
||||
heim_array_delete_value(array, n);
|
||||
} else {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __BLOCKS__ */
|
147
lib/base/baselocl.h
Normal file
147
lib/base/baselocl.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2010 - 2011 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "config.h"
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#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"
|
||||
#include "heimbasepriv.h"
|
||||
|
||||
#ifdef HAVE_DISPATCH_DISPATCH_H
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH)
|
||||
|
||||
#define heim_base_atomic_inc(x) __sync_add_and_fetch((x), 1)
|
||||
#define heim_base_atomic_dec(x) __sync_sub_and_fetch((x), 1)
|
||||
#define heim_base_atomic_type unsigned int
|
||||
#define heim_base_atomic_max UINT_MAX
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__sync_swap)
|
||||
#define heim_base_exchange_pointer(t,v) __sync_swap((t), (v))
|
||||
#else
|
||||
#define heim_base_exchange_pointer(t,v) __sync_lock_test_and_set((t), (v))
|
||||
#endif
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
#define heim_base_atomic_inc(x) InterlockedIncrement(x)
|
||||
#define heim_base_atomic_dec(x) InterlockedDecrement(x)
|
||||
#define heim_base_atomic_type LONG
|
||||
#define heim_base_atomic_max MAXLONG
|
||||
|
||||
#define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((t),(v))
|
||||
|
||||
#else
|
||||
|
||||
#define HEIM_BASE_NEED_ATOMIC_MUTEX 1
|
||||
extern HEIMDAL_MUTEX _heim_base_mutex;
|
||||
|
||||
#define heim_base_atomic_type unsigned int
|
||||
|
||||
static inline heim_base_atomic_type
|
||||
heim_base_atomic_inc(heim_base_atomic_type *x)
|
||||
{
|
||||
heim_base_atomic_type t;
|
||||
HEIMDAL_MUTEX_lock(&_heim_base_mutex);
|
||||
t = ++(*x);
|
||||
HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline heim_base_atomic_type
|
||||
heim_base_atomic_dec(heim_base_atomic_type *x)
|
||||
{
|
||||
heim_base_atomic_type t;
|
||||
HEIMDAL_MUTEX_lock(&_heim_base_mutex);
|
||||
t = --(*x);
|
||||
HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
|
||||
return t;
|
||||
}
|
||||
|
||||
#define heim_base_atomic_max UINT_MAX
|
||||
|
||||
#endif
|
||||
|
||||
/* tagged strings/object/XXX */
|
||||
#define heim_base_is_tagged(x) (((uintptr_t)(x)) & 0x3)
|
||||
|
||||
#define heim_base_is_tagged_object(x) ((((uintptr_t)(x)) & 0x3) == 1)
|
||||
#define heim_base_make_tagged_object(x, tid) \
|
||||
((heim_object_t)((((uintptr_t)(x)) << 5) | ((tid) << 2) | 0x1))
|
||||
#define heim_base_tagged_object_tid(x) ((((uintptr_t)(x)) & 0x1f) >> 2)
|
||||
#define heim_base_tagged_object_value(x) (((uintptr_t)(x)) >> 5)
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#undef HEIMDAL_NORETURN_ATTRIBUTE
|
||||
#define HEIMDAL_NORETURN_ATTRIBUTE
|
||||
#undef HEIMDAL_PRINTF_ATTRIBUTE
|
||||
#define HEIMDAL_PRINTF_ATTRIBUTE(x)
|
59
lib/base/bool.c
Normal file
59
lib/base/bool.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
|
||||
struct heim_type_data _heim_bool_object = {
|
||||
HEIM_TID_BOOL,
|
||||
"bool-object",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
heim_bool_t
|
||||
heim_bool_create(int val)
|
||||
{
|
||||
return heim_base_make_tagged_object(!!val, HEIM_TID_BOOL);
|
||||
}
|
||||
|
||||
int
|
||||
heim_bool_val(heim_bool_t ptr)
|
||||
{
|
||||
return heim_base_tagged_object_value(ptr);
|
||||
}
|
886
lib/base/bsearch.c
Normal file
886
lib/base/bsearch.c
Normal file
@@ -0,0 +1,886 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Secure Endpoints Inc.
|
||||
* 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.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 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 HOLDER 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 "baselocl.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_IO_H
|
||||
#include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* This file contains functions for binary searching flat text in memory
|
||||
* and in text files where each line is a [variable length] record.
|
||||
* Each record has a key and an optional value separated from the key by
|
||||
* unquoted whitespace. Whitespace in the key, and leading whitespace
|
||||
* for the value, can be quoted with backslashes (but CR and LF must be
|
||||
* quoted in such a way that they don't appear in the quoted result).
|
||||
*
|
||||
* Binary searching a tree are normally a dead simple algorithm. It
|
||||
* turns out that binary searching flat text with *variable* length
|
||||
* records is... tricky. There's no indexes to record beginning bytes,
|
||||
* thus any index selected during the search is likely to fall in the
|
||||
* middle of a record. When deciding to search a left sub-tree one
|
||||
* might fail to find the last record in that sub-tree on account of the
|
||||
* right boundary falling in the middle of it -- the chosen solution to
|
||||
* this makes left sub-tree searches slightly less efficient than right
|
||||
* sub-tree searches.
|
||||
*
|
||||
* If binary searching flat text in memory is tricky, using block-wise
|
||||
* I/O instead is trickier! But it's necessary in order to support
|
||||
* large files (which we either can't or wouldn't want to read or map
|
||||
* into memory). Each block we read has to be large enough that the
|
||||
* largest record can fit in it. And each block might start and/or end
|
||||
* in the middle of a record. Here it is the right sub-tree searches
|
||||
* that are less efficient than left sub-tree searches.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct bsearch_file_handle {
|
||||
int fd; /* file descriptor */
|
||||
char *cache; /* cache bytes */
|
||||
char *page; /* one double-size page worth of bytes */
|
||||
size_t file_sz; /* file size */
|
||||
size_t cache_sz; /* cache size */
|
||||
size_t page_sz; /* page size */
|
||||
};
|
||||
|
||||
/* Find a new-line */
|
||||
static const char *
|
||||
find_line(const char *buf, size_t i, size_t right)
|
||||
{
|
||||
if (i == 0)
|
||||
return &buf[i];
|
||||
for (; i < right; i++) {
|
||||
if (buf[i] == '\n') {
|
||||
if ((i + 1) < right)
|
||||
return &buf[i + 1];
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common routine for binary searching text in core.
|
||||
*
|
||||
* Perform a binary search of a char array containing a block from a
|
||||
* text 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 be quoted with backslashes.
|
||||
* It's the caller's responsibility to encode/decode keys/values if
|
||||
* quoting is desired; newlines should be encoded such that a newline
|
||||
* does not appear in the result.
|
||||
*
|
||||
* All output arguments are optional.
|
||||
*
|
||||
* Returns 0 if key is found, -1 if not found, or an error code such as
|
||||
* ENOMEM in case of error.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @buf String to search
|
||||
* @sz Size of string to search
|
||||
* @key Key string to search for
|
||||
* @buf_is_start True if the buffer starts with a record, false if it
|
||||
* starts in the middle of a record or if the caller
|
||||
* doesn't know.
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* @value Location to store a copy of the value (caller must free)
|
||||
* @location Record location if found else the location where the
|
||||
* record should be inserted (index into @buf)
|
||||
* @cmp Set to less than or greater than 0 to indicate that a
|
||||
* key not found would have fit in an earlier or later
|
||||
* part of a file. Callers should use this to decide
|
||||
* whether to read a block to the left or to the right and
|
||||
* search that.
|
||||
* @loops Location to store a count of bisections required for
|
||||
* search (useful for confirming logarithmic performance)
|
||||
*/
|
||||
static int
|
||||
bsearch_common(const char *buf, size_t sz, const char *key,
|
||||
int buf_is_start, char **value, size_t *location,
|
||||
int *cmp, size_t *loops)
|
||||
{
|
||||
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 = -1;
|
||||
size_t k;
|
||||
size_t l; /* left side of buffer for binary search */
|
||||
size_t r; /* right side of buffer for binary search */
|
||||
size_t rmax; /* right side of buffer for binary search */
|
||||
size_t i; /* index into buffer, typically in the middle of l and r */
|
||||
size_t loop_count = 0;
|
||||
int ret = -1;
|
||||
|
||||
if (value)
|
||||
*value = NULL;
|
||||
if (cmp)
|
||||
*cmp = 0;
|
||||
if (loops)
|
||||
*loops = 0;
|
||||
|
||||
/* Binary search; file should be sorted */
|
||||
for (l = 0, r = rmax = sz, i = sz >> 1; i >= l && i < rmax; loop_count++) {
|
||||
heim_assert(i < sz, "invalid aname2lname db index");
|
||||
|
||||
/* buf[i] is likely in the middle of a line; find the next line */
|
||||
linep = find_line(buf, i, rmax);
|
||||
k = linep ? linep - buf : i;
|
||||
if (linep == NULL || k >= rmax) {
|
||||
/*
|
||||
* No new line found to the right; search to the left then
|
||||
* but don't change rmax (this isn't optimal, but it's
|
||||
* simple).
|
||||
*/
|
||||
if (i == l)
|
||||
break;
|
||||
r = i;
|
||||
i = l + ((r - l) >> 1);
|
||||
continue;
|
||||
}
|
||||
i = k;
|
||||
heim_assert(i >= l && i < rmax, "invalid aname2lname db index");
|
||||
|
||||
/* Got a line; check it */
|
||||
|
||||
/* Search for and split on unquoted whitespace */
|
||||
val_start = 0;
|
||||
for (key_start = i, key_len = 0, val_len = 0, k = i; k < rmax; k++) {
|
||||
if (buf[k] == '\\') {
|
||||
k++;
|
||||
continue;
|
||||
}
|
||||
if (buf[k] == '\r' || buf[k] == '\n') {
|
||||
/* We now know where the key ends, and there's no value */
|
||||
key_len = k - i;
|
||||
break;
|
||||
}
|
||||
if (!isspace((unsigned char)buf[k]))
|
||||
continue;
|
||||
|
||||
while (k < rmax && isspace((unsigned char)buf[k])) {
|
||||
key_len = k - i;
|
||||
k++;
|
||||
}
|
||||
if (k < rmax)
|
||||
val_start = k;
|
||||
/* Find end of value */
|
||||
for (; k < rmax && buf[k] != '\0'; k++) {
|
||||
if (buf[k] == '\r' || buf[k] == '\n') {
|
||||
val_len = k - val_start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following logic is for dealing with partial buffers,
|
||||
* which we use for block-wise binary searches of large files
|
||||
*/
|
||||
if (key_start == 0 && !buf_is_start) {
|
||||
/*
|
||||
* We're at the beginning of a block that might have started
|
||||
* in the middle of a record whose "key" might well compare
|
||||
* as greater than the key we're looking for, so we don't
|
||||
* bother comparing -- we know key_cmp must be -1 here.
|
||||
*/
|
||||
key_cmp = -1;
|
||||
break;
|
||||
}
|
||||
if ((val_len && buf[val_start + val_len] != '\n') ||
|
||||
(!val_len && buf[key_start + key_len] != '\n')) {
|
||||
/*
|
||||
* We're at the end of a block that ends in the middle of a
|
||||
* record whose "key" might well compare as less than the
|
||||
* key we're looking for, so we don't bother comparing -- we
|
||||
* know key_cmp must be >= 0 but we can't tell. Our caller
|
||||
* will end up reading a double-size block to handle this.
|
||||
*/
|
||||
key_cmp = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
key_cmp = strncmp(key, &buf[key_start], key_len);
|
||||
if (key_cmp == 0 && strlen(key) != key_len)
|
||||
key_cmp = 1;
|
||||
if (key_cmp < 0) {
|
||||
/* search left */
|
||||
r = rmax = (linep - buf);
|
||||
i = l + ((r - l) >> 1);
|
||||
if (location)
|
||||
*location = key_start;
|
||||
} else if (key_cmp > 0) {
|
||||
/* search right */
|
||||
if (l == i)
|
||||
break; /* not found */
|
||||
l = i;
|
||||
i = l + ((r - l) >> 1);
|
||||
if (location)
|
||||
*location = val_start + val_len;
|
||||
} else {
|
||||
/* match! */
|
||||
if (location)
|
||||
*location = key_start;
|
||||
ret = 0;
|
||||
if (val_len && value) {
|
||||
/* Avoid strndup() so we don't need libroken here yet */
|
||||
*value = malloc(val_len + 1);
|
||||
if (!*value)
|
||||
ret = errno;
|
||||
(void) memcpy(*value, &buf[val_start], val_len);
|
||||
(*value)[val_len] = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmp)
|
||||
*cmp = key_cmp;
|
||||
if (loops)
|
||||
*loops = loop_count;
|
||||
|
||||
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
|
||||
* whitespace.
|
||||
*
|
||||
* All output arguments are optional.
|
||||
*
|
||||
* Returns 0 if key is found, -1 if not found, or an error code such as
|
||||
* ENOMEM in case of error.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @buf Char array pointer
|
||||
* @buf_sz Size of buf
|
||||
* @key Key to search for
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* @value Location where to put the value, if any (caller must free)
|
||||
* @location Record location if found else the location where the record
|
||||
* should be inserted (index into @buf)
|
||||
* @loops Location where to put a number of loops (or comparisons)
|
||||
* needed for the search (useful for benchmarking)
|
||||
*/
|
||||
int
|
||||
_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);
|
||||
}
|
||||
|
||||
#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.
|
||||
*
|
||||
* Returns 0 on success, else an error number or -1 if the file is empty.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @fname Name of file to open
|
||||
* @max_sz Maximum size of cache to allocate, in bytes (if zero, default)
|
||||
* @page_sz Page size (must be a power of two, larger than 256, smaller
|
||||
* than 1MB; if zero use default)
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* @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_handle *bfh, size_t *reads)
|
||||
{
|
||||
bsearch_file_handle new_bfh = NULL;
|
||||
struct stat st;
|
||||
size_t i;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
*bfh = NULL;
|
||||
|
||||
if (reads)
|
||||
*reads = 0;
|
||||
|
||||
fd = open(fname, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return errno;
|
||||
|
||||
if (fstat(fd, &st) == -1) {
|
||||
ret = errno;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (st.st_size == 0) {
|
||||
ret = -1; /* no data -> no binary search */
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Validate / default arguments */
|
||||
if (max_sz == 0)
|
||||
max_sz = DEFAULT_MAX_FILE_SIZE;
|
||||
for (i = page_sz; i; i >>= 1) {
|
||||
/* Make sure page_sz is a power of two */
|
||||
if ((i % 2) && (i >> 1)) {
|
||||
page_sz = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (page_sz == 0)
|
||||
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
||||
page_sz = st.st_blksize;
|
||||
#else
|
||||
page_sz = 4096;
|
||||
#endif
|
||||
for (i = page_sz; i; i >>= 1) {
|
||||
/* Make sure page_sz is a power of two */
|
||||
if ((i % 2) && (i >> 1)) {
|
||||
/* Can't happen! Filesystems always use powers of two! */
|
||||
page_sz = 4096;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (page_sz > MAX_BLOCK_SIZE)
|
||||
page_sz = MAX_BLOCK_SIZE;
|
||||
|
||||
new_bfh = calloc(1, sizeof (*new_bfh));
|
||||
if (new_bfh == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
new_bfh->fd = fd;
|
||||
new_bfh->page_sz = page_sz;
|
||||
new_bfh->file_sz = st.st_size;
|
||||
|
||||
if (max_sz >= st.st_size) {
|
||||
/* Whole-file method */
|
||||
new_bfh->cache = malloc(st.st_size + 1);
|
||||
if (new_bfh->cache) {
|
||||
new_bfh->cache[st.st_size] = '\0';
|
||||
new_bfh->cache_sz = st.st_size;
|
||||
ret = read(fd, new_bfh->cache, st.st_size);
|
||||
if (ret < 0) {
|
||||
ret = errno;
|
||||
goto err;
|
||||
}
|
||||
if (ret != st.st_size) {
|
||||
ret = EIO; /* XXX ??? */
|
||||
goto err;
|
||||
}
|
||||
if (reads)
|
||||
*reads = 1;
|
||||
(void) close(fd);
|
||||
new_bfh->fd = -1;
|
||||
*bfh = new_bfh;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Block-size method, or above malloc() failed */
|
||||
new_bfh->page = malloc(new_bfh->page_sz << 1);
|
||||
if (new_bfh->page == NULL) {
|
||||
/* Can't even allocate a single double-size page! */
|
||||
ret = ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
new_bfh->cache_sz = max_sz < st.st_size ? max_sz : st.st_size;
|
||||
new_bfh->cache = malloc(new_bfh->cache_sz);
|
||||
*bfh = new_bfh;
|
||||
|
||||
/*
|
||||
* malloc() may have failed because we were asking for a lot of
|
||||
* memory, but we may still be able to operate without a cache,
|
||||
* so let's not fail.
|
||||
*/
|
||||
if (new_bfh->cache == NULL) {
|
||||
new_bfh->cache_sz = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize cache */
|
||||
for (i = 0; i < new_bfh->cache_sz; i += new_bfh->page_sz)
|
||||
new_bfh->cache[i] = '\0';
|
||||
return 0;
|
||||
|
||||
err:
|
||||
(void) close(fd);
|
||||
if (new_bfh) {
|
||||
free(new_bfh->page);
|
||||
free(new_bfh->cache);
|
||||
free(new_bfh);
|
||||
}
|
||||
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,
|
||||
size_t *page_sz, size_t *max_sz, int *blockwise)
|
||||
{
|
||||
if (page_sz)
|
||||
*page_sz = bfh->page_sz;
|
||||
if (max_sz)
|
||||
*max_sz = bfh->cache_sz;
|
||||
if (blockwise)
|
||||
*blockwise = (bfh->file_sz != bfh->cache_sz);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the given binary file search handle.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @bfh Pointer to variable containing handle to close.
|
||||
*/
|
||||
void
|
||||
_bsearch_file_close(bsearch_file_handle *bfh)
|
||||
{
|
||||
if (!*bfh)
|
||||
return;
|
||||
if ((*bfh)->fd >= 0)
|
||||
(void) close((*bfh)->fd);
|
||||
if ((*bfh)->page)
|
||||
free((*bfh)->page);
|
||||
if ((*bfh)->cache)
|
||||
free((*bfh)->cache);
|
||||
free(*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
|
||||
* smaller than n implies.
|
||||
*
|
||||
* The page may or may not be valid. If the first byte of it is NUL
|
||||
* then it's not valid, else it is.
|
||||
*
|
||||
* Returns 1 if page is in cache and valid, 0 if the cache is too small
|
||||
* or the page is invalid. The page address is output in @buf if the
|
||||
* cache is large enough to contain it regardless of whether the page is
|
||||
* valid.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @bfh Binary search file handle
|
||||
* @level Level in the tree that we want a page for
|
||||
* @page_idx Page number in the given level (0..2^level - 1)
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* @buf Set to address of page if the cache is large enough
|
||||
*/
|
||||
static int
|
||||
get_page_from_cache(bsearch_file_handle bfh, size_t level, size_t page_idx,
|
||||
char **buf)
|
||||
{
|
||||
size_t idx = 0;
|
||||
size_t page_sz;
|
||||
|
||||
page_sz = bfh->page_sz << 1; /* we use double-size pages in the cache */
|
||||
|
||||
*buf = NULL;
|
||||
|
||||
/*
|
||||
* Compute index into cache. The cache is basically an array of
|
||||
* double-size pages. The first (zeroth) double-size page in the
|
||||
* cache will be the middle page of the file -- the root of the
|
||||
* tree. The next two double-size pages will be the left and right
|
||||
* pages of the second level in the tree. The next four double-size
|
||||
* pages will be the four pages at the next level. And so on for as
|
||||
* many pages as fit in the cache.
|
||||
*
|
||||
* The page index is the number of the page at the given level. We
|
||||
* then compute (2^level - 1 + page index) * 2page size, check that
|
||||
* we have that in the cache, check that the page has been read (it
|
||||
* doesn't start with NUL).
|
||||
*/
|
||||
if (level)
|
||||
idx = (1 << level) - 1 + page_idx;
|
||||
if (((idx + 1) * page_sz * 2) > bfh->cache_sz)
|
||||
return 0;
|
||||
|
||||
*buf = &bfh->cache[idx * page_sz * 2];
|
||||
if (bfh->cache[idx * page_sz * 2] == '\0')
|
||||
return 0; /* cache[idx] == NUL -> page not loaded in cache */
|
||||
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
|
||||
* case the number of remaining bytes in the file will be output.
|
||||
*
|
||||
* Returns 0 on success or an errno value otherwise (EIO if reads are
|
||||
* short).
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @bfh Binary search file handle
|
||||
* @level Level in the binary search tree that we're at
|
||||
* @page_idx Page "index" at the @level of the tree that we want
|
||||
* @page Actual page number that we want
|
||||
* want_double Whether we need a page or double page read
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* @buf Page read or cached
|
||||
* @bytes Bytes read (may be less than page or double page size in
|
||||
* the case of the last page, of course)
|
||||
*/
|
||||
static int
|
||||
read_page(bsearch_file_handle bfh, size_t level, size_t page_idx, size_t page,
|
||||
int want_double, const char **buf, size_t *bytes)
|
||||
{
|
||||
int ret;
|
||||
off_t off;
|
||||
size_t expected;
|
||||
size_t wanted;
|
||||
char *page_buf;
|
||||
|
||||
/* Figure out where we're reading and how much */
|
||||
off = page * bfh->page_sz;
|
||||
if (off < 0)
|
||||
return EOVERFLOW;
|
||||
|
||||
wanted = bfh->page_sz << want_double;
|
||||
expected = ((bfh->file_sz - off) > wanted) ? wanted : bfh->file_sz - off;
|
||||
|
||||
if (get_page_from_cache(bfh, level, page_idx, &page_buf)) {
|
||||
*buf = page_buf;
|
||||
*bytes = expected;
|
||||
return 0; /* found in cache */
|
||||
}
|
||||
|
||||
|
||||
*bytes = 0;
|
||||
*buf = NULL;
|
||||
|
||||
/* OK, we have to read a page or double-size page */
|
||||
|
||||
if (page_buf)
|
||||
want_double = 1; /* we'll be caching; we cache double-size pages */
|
||||
else
|
||||
page_buf = bfh->page; /* we won't cache this page */
|
||||
|
||||
wanted = bfh->page_sz << want_double;
|
||||
expected = ((bfh->file_sz - off) > wanted) ? wanted : bfh->file_sz - off;
|
||||
|
||||
#ifdef HAVE_PREAD
|
||||
ret = pread(bfh->fd, page_buf, expected, off);
|
||||
#else
|
||||
if (lseek(bfh->fd, off, SEEK_SET) == (off_t)-1)
|
||||
return errno;
|
||||
ret = read(bfh->fd, page_buf, expected);
|
||||
#endif
|
||||
if (ret < 0)
|
||||
return errno;
|
||||
|
||||
if (ret != expected)
|
||||
return EIO; /* XXX ??? */
|
||||
|
||||
*buf = page_buf;
|
||||
*bytes = expected;
|
||||
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
|
||||
* be quoted with backslashes. It's the caller's responsibility to
|
||||
* encode/decode keys/values if quoting is desired; newlines should be
|
||||
* encoded such that a newline does not appear in the result.
|
||||
*
|
||||
* The search is done with block-wise I/O (i.e., the whole file is not
|
||||
* read into memory).
|
||||
*
|
||||
* All output arguments are optional.
|
||||
*
|
||||
* Returns 0 if key is found, -1 if not found, or an error code such as
|
||||
* ENOMEM in case of error.
|
||||
*
|
||||
* NOTE: We could improve this by not freeing the buffer, instead
|
||||
* requiring that the caller provide it. Further, we could cache
|
||||
* the top N levels of [double-size] pages (2^N - 1 pages), which
|
||||
* should speed up most searches by reducing the number of reads
|
||||
* by N.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @fd File descriptor (file to search)
|
||||
* @page_sz Page size (if zero then the file's st_blksize will be used)
|
||||
* @key Key string to search for
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* @value Location to store a copy of the value (caller must free)
|
||||
* @location Record location if found else the location where the
|
||||
* record should be inserted (index into @buf)
|
||||
* @loops Location to store a count of bisections required for
|
||||
* search (useful for confirming logarithmic performance)
|
||||
* @reads Location to store a count of pages read during search
|
||||
* (useful for confirming logarithmic performance)
|
||||
*/
|
||||
int
|
||||
_bsearch_file(bsearch_file_handle bfh, const char *key,
|
||||
char **value, size_t *location, size_t *loops, size_t *reads)
|
||||
{
|
||||
int ret;
|
||||
const char *buf;
|
||||
size_t buf_sz;
|
||||
size_t page, l, r;
|
||||
size_t my_reads = 0;
|
||||
size_t my_loops_total = 0;
|
||||
size_t my_loops;
|
||||
size_t level; /* level in the tree */
|
||||
size_t page_idx = 0; /* page number in the tree level */
|
||||
size_t buf_location;
|
||||
int cmp;
|
||||
int buf_ends_in_eol = 0;
|
||||
int buf_is_start = 0;
|
||||
|
||||
if (reads)
|
||||
*reads = 0;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Else block-wise binary search */
|
||||
|
||||
if (value)
|
||||
*value = NULL;
|
||||
if (loops)
|
||||
*loops = 0;
|
||||
|
||||
l = 0;
|
||||
r = (bfh->file_sz / bfh->page_sz) + 1;
|
||||
for (level = 0, page = r >> 1; page >= l && page < r ; level++) {
|
||||
ret = read_page(bfh, level, page_idx, page, 0, &buf, &buf_sz);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
my_reads++;
|
||||
if (buf[buf_sz - 1] == '\r' || buf[buf_sz - 1] == '\n')
|
||||
buf_ends_in_eol = 1;
|
||||
else
|
||||
buf_ends_in_eol = 0;
|
||||
|
||||
buf_is_start = page == 0 ? 1 : 0;
|
||||
ret = bsearch_common(buf, (size_t)buf_sz, key, buf_is_start,
|
||||
value, &buf_location, &cmp, &my_loops);
|
||||
if (ret > 0)
|
||||
return ret;
|
||||
/* Found or no we update stats */
|
||||
my_loops_total += my_loops;
|
||||
if (loops)
|
||||
*loops = my_loops_total;
|
||||
if (reads)
|
||||
*reads = my_reads;
|
||||
if (location)
|
||||
*location = page * bfh->page_sz + buf_location;
|
||||
if (ret == 0)
|
||||
return 0; /* found! */
|
||||
/* Not found */
|
||||
if (cmp < 0) {
|
||||
/* Search left */
|
||||
page_idx <<= 1;
|
||||
r = page;
|
||||
page = l + ((r - l) >> 1);
|
||||
continue;
|
||||
} else {
|
||||
/*
|
||||
* Search right, but first search the current and next
|
||||
* blocks in case that the record we're looking for either
|
||||
* straddles the boundary between this and the next record,
|
||||
* or in case the record starts exactly at the next page.
|
||||
*/
|
||||
heim_assert(cmp > 0, "cmp > 0");
|
||||
|
||||
if (!buf_ends_in_eol || page == l || page == (r - 1)) {
|
||||
ret = read_page(bfh, level, page_idx, page, 1, &buf, &buf_sz);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
my_reads++;
|
||||
|
||||
buf_is_start = page == l ? 1 : 0;
|
||||
|
||||
ret = bsearch_common(buf, (size_t)buf_sz, key, buf_is_start,
|
||||
value, &buf_location, &cmp, &my_loops);
|
||||
if (ret > 0)
|
||||
return ret;
|
||||
my_loops_total += my_loops;
|
||||
if (loops)
|
||||
*loops = my_loops_total;
|
||||
if (reads)
|
||||
*reads = my_reads;
|
||||
if (location)
|
||||
*location = page * bfh->page_sz + buf_location;
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Oh well, search right */
|
||||
if (l == page && r == (l + 1))
|
||||
break;
|
||||
page_idx = (page_idx << 1) + 1;
|
||||
l = page;
|
||||
page = l + ((r - l) >> 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
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
|
||||
};
|
165
lib/base/data.c
Normal file
165
lib/base/data.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
#include <string.h>
|
||||
|
||||
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
|
||||
data_cmp(void *a, void *b)
|
||||
{
|
||||
heim_octet_string *osa = a, *osb = b;
|
||||
if (osa->length != osb->length)
|
||||
return osa->length - osb->length;
|
||||
return memcmp(osa->data, osb->data, osa->length);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
data_hash(void *ptr)
|
||||
{
|
||||
heim_octet_string *os = ptr;
|
||||
const unsigned char *s = os->data;
|
||||
|
||||
if (os->length < 4)
|
||||
return os->length;
|
||||
return s[0] | (s[1] << 8) |
|
||||
(s[os->length - 2] << 16) | (s[os->length - 1] << 24);
|
||||
}
|
||||
|
||||
struct heim_type_data _heim_data_object = {
|
||||
HEIM_TID_DATA,
|
||||
"data-object",
|
||||
NULL,
|
||||
data_dealloc,
|
||||
NULL,
|
||||
data_cmp,
|
||||
data_hash,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a data object
|
||||
*
|
||||
* @param string the string to create, must be an utf8 string
|
||||
*
|
||||
* @return string object
|
||||
*/
|
||||
|
||||
heim_data_t
|
||||
heim_data_create(const void *data, size_t length)
|
||||
{
|
||||
heim_octet_string *os;
|
||||
|
||||
os = _heim_alloc_object(&_heim_data_object, sizeof(*os) + length);
|
||||
if (os) {
|
||||
os->data = (uint8_t *)os + sizeof(*os);
|
||||
os->length = length;
|
||||
memcpy(os->data, data, 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 data objects
|
||||
*/
|
||||
|
||||
heim_tid_t
|
||||
heim_data_get_type_id(void)
|
||||
{
|
||||
return HEIM_TID_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data value of the content.
|
||||
*
|
||||
* @param data the data object to get the value from
|
||||
*
|
||||
* @return a heim_octet_string
|
||||
*/
|
||||
|
||||
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
lib/base/db.c
Normal file
1709
lib/base/db.c
Normal file
File diff suppressed because it is too large
Load Diff
303
lib/base/dict.c
Normal file
303
lib/base/dict.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 1997 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
|
||||
struct hashentry {
|
||||
struct hashentry **prev;
|
||||
struct hashentry *next;
|
||||
heim_object_t key;
|
||||
heim_object_t value;
|
||||
};
|
||||
|
||||
struct heim_dict_data {
|
||||
size_t size;
|
||||
struct hashentry **tab;
|
||||
};
|
||||
|
||||
static void
|
||||
dict_dealloc(void *ptr)
|
||||
{
|
||||
heim_dict_t dict = ptr;
|
||||
struct hashentry **h, *g, *i;
|
||||
|
||||
for (h = dict->tab; h < &dict->tab[dict->size]; ++h) {
|
||||
for (g = h[0]; g; g = i) {
|
||||
i = g->next;
|
||||
heim_release(g->key);
|
||||
heim_release(g->value);
|
||||
free(g);
|
||||
}
|
||||
}
|
||||
free(dict->tab);
|
||||
}
|
||||
|
||||
struct heim_type_data dict_object = {
|
||||
HEIM_TID_DICT,
|
||||
"dict-object",
|
||||
NULL,
|
||||
dict_dealloc,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
static size_t
|
||||
isprime(size_t p)
|
||||
{
|
||||
size_t q, i;
|
||||
|
||||
for(i = 2 ; i < p; i++) {
|
||||
q = p / i;
|
||||
|
||||
if (i * q == p)
|
||||
return 0;
|
||||
if (i * i > p)
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t
|
||||
findprime(size_t p)
|
||||
{
|
||||
if (p % 2 == 0)
|
||||
p++;
|
||||
|
||||
while (isprime(p) == 0)
|
||||
p += 2;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate an array
|
||||
*
|
||||
* @return A new allocated array, free with heim_release()
|
||||
*/
|
||||
|
||||
heim_dict_t
|
||||
heim_dict_create(size_t size)
|
||||
{
|
||||
heim_dict_t dict;
|
||||
|
||||
dict = _heim_alloc_object(&dict_object, sizeof(*dict));
|
||||
|
||||
dict->size = findprime(size);
|
||||
if (dict->size == 0) {
|
||||
heim_release(dict);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dict->tab = calloc(dict->size, sizeof(dict->tab[0]));
|
||||
if (dict->tab == NULL) {
|
||||
dict->size = 0;
|
||||
heim_release(dict);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type id of an dict
|
||||
*
|
||||
* @return the type id
|
||||
*/
|
||||
|
||||
heim_tid_t
|
||||
heim_dict_get_type_id(void)
|
||||
{
|
||||
return HEIM_TID_DICT;
|
||||
}
|
||||
|
||||
/* Intern search function */
|
||||
|
||||
static struct hashentry *
|
||||
_search(heim_dict_t dict, heim_object_t ptr)
|
||||
{
|
||||
unsigned long v = heim_get_hash(ptr);
|
||||
struct hashentry *p;
|
||||
|
||||
for (p = dict->tab[v % dict->size]; p != NULL; p = p->next)
|
||||
if (heim_cmp(ptr, p->key) == 0)
|
||||
return p;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for element in hash table
|
||||
*
|
||||
* @value dict the dict to search in
|
||||
* @value key the key to search for
|
||||
*
|
||||
* @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);
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
|
||||
return heim_retain(p->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add key and value to dict
|
||||
*
|
||||
* @value dict the dict to add too
|
||||
* @value key the key to add
|
||||
* @value value the value to add
|
||||
*
|
||||
* @return 0 if added, errno if not
|
||||
*/
|
||||
|
||||
int
|
||||
heim_dict_set_value(heim_dict_t dict, heim_object_t key, heim_object_t value)
|
||||
{
|
||||
struct hashentry **tabptr, *h;
|
||||
|
||||
h = _search(dict, key);
|
||||
if (h) {
|
||||
heim_release(h->value);
|
||||
h->value = heim_retain(value);
|
||||
} else {
|
||||
unsigned long v;
|
||||
|
||||
h = malloc(sizeof(*h));
|
||||
if (h == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
h->key = heim_retain(key);
|
||||
h->value = heim_retain(value);
|
||||
|
||||
v = heim_get_hash(key);
|
||||
|
||||
tabptr = &dict->tab[v % dict->size];
|
||||
h->next = *tabptr;
|
||||
*tabptr = h;
|
||||
h->prev = tabptr;
|
||||
if (h->next)
|
||||
h->next->prev = &h->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete element with key key
|
||||
*
|
||||
* @value dict the dict to delete from
|
||||
* @value key the key to delete
|
||||
*/
|
||||
|
||||
void
|
||||
heim_dict_delete_key(heim_dict_t dict, heim_object_t key)
|
||||
{
|
||||
struct hashentry *h = _search(dict, key);
|
||||
|
||||
if (h == NULL)
|
||||
return;
|
||||
|
||||
heim_release(h->key);
|
||||
heim_release(h->value);
|
||||
|
||||
if ((*(h->prev) = h->next) != NULL)
|
||||
h->next->prev = h->prev;
|
||||
|
||||
free(h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do something for each element
|
||||
*
|
||||
* @value dict the dict to interate over
|
||||
* @value func the function to search for
|
||||
* @value arg argument to func
|
||||
*/
|
||||
|
||||
void
|
||||
heim_dict_iterate_f(heim_dict_t dict, void *arg, heim_dict_iterator_f_t func)
|
||||
{
|
||||
struct hashentry **h, *g;
|
||||
|
||||
for (h = dict->tab; h < &dict->tab[dict->size]; ++h)
|
||||
for (g = *h; g; g = g->next)
|
||||
func(g->key, g->value, arg);
|
||||
}
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
/**
|
||||
* Do something for each element
|
||||
*
|
||||
* @value dict the dict to interate over
|
||||
* @value func the function to search for
|
||||
*/
|
||||
|
||||
void
|
||||
heim_dict_iterate(heim_dict_t dict, void (^func)(heim_object_t, heim_object_t))
|
||||
{
|
||||
struct hashentry **h, *g;
|
||||
|
||||
for (h = dict->tab; h < &dict->tab[dict->size]; ++h)
|
||||
for (g = *h; g; g = g->next)
|
||||
func(g->key, g->value);
|
||||
}
|
||||
#endif
|
164
lib/base/error.c
Normal file
164
lib/base/error.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
|
||||
struct heim_error {
|
||||
int error_code;
|
||||
heim_string_t msg;
|
||||
struct heim_error *next;
|
||||
};
|
||||
|
||||
static void
|
||||
error_dealloc(void *ptr)
|
||||
{
|
||||
struct heim_error *p = ptr;
|
||||
heim_release(p->msg);
|
||||
heim_release(p->next);
|
||||
}
|
||||
|
||||
static int
|
||||
error_cmp(void *a, void *b)
|
||||
{
|
||||
struct heim_error *ap = a, *bp = b;
|
||||
if (ap->error_code == ap->error_code)
|
||||
return ap->error_code - ap->error_code;
|
||||
return heim_cmp(ap->msg, bp->msg);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
error_hash(void *ptr)
|
||||
{
|
||||
struct heim_error *p = ptr;
|
||||
return p->error_code;
|
||||
}
|
||||
|
||||
struct heim_type_data _heim_error_object = {
|
||||
HEIM_TID_ERROR,
|
||||
"error-object",
|
||||
NULL,
|
||||
error_dealloc,
|
||||
NULL,
|
||||
error_cmp,
|
||||
error_hash,
|
||||
NULL
|
||||
};
|
||||
|
||||
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, ...)
|
||||
{
|
||||
heim_error_t e;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
e = heim_error_createv(error_code, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
heim_error_t
|
||||
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 heim_error_enomem();
|
||||
len = vsnprintf(str, 1024, fmt, ap);
|
||||
errno = save_errno;
|
||||
if (len < 0) {
|
||||
free(str);
|
||||
return NULL; /* XXX We should have a special heim_error_t for this */
|
||||
}
|
||||
|
||||
e = _heim_alloc_object(&_heim_error_object, sizeof(struct heim_error));
|
||||
if (e) {
|
||||
e->msg = heim_string_create(str);
|
||||
e->error_code = error_code;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
return top;
|
||||
}
|
983
lib/base/heimbase.c
Normal file
983
lib/base/heimbase.c
Normal file
@@ -0,0 +1,983 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
#include <syslog.h>
|
||||
|
||||
static heim_base_atomic_type tidglobal = HEIM_TID_USER;
|
||||
|
||||
struct heim_base {
|
||||
heim_type_t isa;
|
||||
heim_base_atomic_type ref_cnt;
|
||||
HEIM_TAILQ_ENTRY(heim_base) autorel;
|
||||
heim_auto_release_t autorelpool;
|
||||
uintptr_t isaextra[3];
|
||||
};
|
||||
|
||||
/* specialized version of base */
|
||||
struct heim_base_mem {
|
||||
heim_type_t isa;
|
||||
heim_base_atomic_type ref_cnt;
|
||||
HEIM_TAILQ_ENTRY(heim_base) autorel;
|
||||
heim_auto_release_t autorelpool;
|
||||
const char *name;
|
||||
void (*dealloc)(void *);
|
||||
uintptr_t isaextra[1];
|
||||
};
|
||||
|
||||
#define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1)
|
||||
#define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1))
|
||||
|
||||
#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX
|
||||
HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Auto release structure
|
||||
*/
|
||||
|
||||
struct heim_auto_release {
|
||||
HEIM_TAILQ_HEAD(, heim_base) pool;
|
||||
HEIMDAL_MUTEX pool_mutex;
|
||||
struct heim_auto_release *parent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Retain object (i.e., take a reference)
|
||||
*
|
||||
* @param object to be released, NULL is ok
|
||||
*
|
||||
* @return the same object as passed in
|
||||
*/
|
||||
|
||||
void *
|
||||
heim_retain(void *ptr)
|
||||
{
|
||||
struct heim_base *p = PTR2BASE(ptr);
|
||||
|
||||
if (ptr == NULL || heim_base_is_tagged(ptr))
|
||||
return ptr;
|
||||
|
||||
if (p->ref_cnt == heim_base_atomic_max)
|
||||
return ptr;
|
||||
|
||||
if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0)
|
||||
heim_abort("resurection");
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release object, free if reference count reaches zero
|
||||
*
|
||||
* @param object to be released
|
||||
*/
|
||||
|
||||
void
|
||||
heim_release(void *ptr)
|
||||
{
|
||||
heim_base_atomic_type old;
|
||||
struct heim_base *p = PTR2BASE(ptr);
|
||||
|
||||
if (ptr == NULL || heim_base_is_tagged(ptr))
|
||||
return;
|
||||
|
||||
if (p->ref_cnt == heim_base_atomic_max)
|
||||
return;
|
||||
|
||||
old = heim_base_atomic_dec(&p->ref_cnt) + 1;
|
||||
|
||||
if (old > 1)
|
||||
return;
|
||||
|
||||
if (old == 1) {
|
||||
heim_auto_release_t ar = p->autorelpool;
|
||||
/* remove from autorel pool list */
|
||||
if (ar) {
|
||||
p->autorelpool = NULL;
|
||||
HEIMDAL_MUTEX_lock(&ar->pool_mutex);
|
||||
HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
|
||||
HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
|
||||
}
|
||||
if (p->isa->dealloc)
|
||||
p->isa->dealloc(ptr);
|
||||
free(p);
|
||||
} else
|
||||
heim_abort("over release");
|
||||
}
|
||||
|
||||
/**
|
||||
* If used require wrapped in autorelease pool
|
||||
*/
|
||||
|
||||
heim_string_t
|
||||
heim_description(heim_object_t ptr)
|
||||
{
|
||||
struct heim_base *p = PTR2BASE(ptr);
|
||||
if (p->isa->desc == NULL)
|
||||
return heim_auto_release(heim_string_ref_create(p->isa->name, NULL));
|
||||
return heim_auto_release(p->isa->desc(ptr));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_heim_make_permanent(heim_object_t ptr)
|
||||
{
|
||||
struct heim_base *p = PTR2BASE(ptr);
|
||||
p->ref_cnt = heim_base_atomic_max;
|
||||
}
|
||||
|
||||
|
||||
static heim_type_t tagged_isa[9] = {
|
||||
&_heim_number_object,
|
||||
&_heim_null_object,
|
||||
&_heim_bool_object,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
heim_type_t
|
||||
_heim_get_isa(heim_object_t ptr)
|
||||
{
|
||||
struct heim_base *p;
|
||||
if (heim_base_is_tagged(ptr)) {
|
||||
if (heim_base_is_tagged_object(ptr))
|
||||
return tagged_isa[heim_base_tagged_object_tid(ptr)];
|
||||
heim_abort("not a supported tagged type");
|
||||
}
|
||||
p = PTR2BASE(ptr);
|
||||
return p->isa;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type ID of object
|
||||
*
|
||||
* @param object object to get type id of
|
||||
*
|
||||
* @return type id of object
|
||||
*/
|
||||
|
||||
heim_tid_t
|
||||
heim_get_tid(heim_object_t ptr)
|
||||
{
|
||||
heim_type_t isa = _heim_get_isa(ptr);
|
||||
return isa->tid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hash value of object
|
||||
*
|
||||
* @param object object to get hash value for
|
||||
*
|
||||
* @return a hash value
|
||||
*/
|
||||
|
||||
unsigned long
|
||||
heim_get_hash(heim_object_t ptr)
|
||||
{
|
||||
heim_type_t isa = _heim_get_isa(ptr);
|
||||
if (isa->hash)
|
||||
return isa->hash(ptr);
|
||||
return (unsigned long)ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two objects, returns 0 if equal, can use used for qsort()
|
||||
* and friends.
|
||||
*
|
||||
* @param a first object to compare
|
||||
* @param b first object to compare
|
||||
*
|
||||
* @return 0 if objects are equal
|
||||
*/
|
||||
|
||||
int
|
||||
heim_cmp(heim_object_t a, heim_object_t b)
|
||||
{
|
||||
heim_tid_t ta, tb;
|
||||
heim_type_t isa;
|
||||
|
||||
ta = heim_get_tid(a);
|
||||
tb = heim_get_tid(b);
|
||||
|
||||
if (ta != tb)
|
||||
return ta - tb;
|
||||
|
||||
isa = _heim_get_isa(a);
|
||||
|
||||
if (isa->cmp)
|
||||
return isa->cmp(a, b);
|
||||
|
||||
return (uintptr_t)a - (uintptr_t)b;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private - allocates an memory object
|
||||
*/
|
||||
|
||||
static void
|
||||
memory_dealloc(void *ptr)
|
||||
{
|
||||
struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr);
|
||||
if (p->dealloc)
|
||||
p->dealloc(ptr);
|
||||
}
|
||||
|
||||
struct heim_type_data memory_object = {
|
||||
HEIM_TID_MEMORY,
|
||||
"memory-object",
|
||||
NULL,
|
||||
memory_dealloc,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
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)
|
||||
{
|
||||
/* XXX use posix_memalign */
|
||||
|
||||
struct heim_base_mem *p = calloc(1, size + sizeof(*p));
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
p->isa = &memory_object;
|
||||
p->ref_cnt = 1;
|
||||
p->name = name;
|
||||
p->dealloc = dealloc;
|
||||
return BASE2PTR(p);
|
||||
}
|
||||
|
||||
heim_type_t
|
||||
_heim_create_type(const char *name,
|
||||
heim_type_init init,
|
||||
heim_type_dealloc dealloc,
|
||||
heim_type_copy copy,
|
||||
heim_type_cmp cmp,
|
||||
heim_type_hash hash,
|
||||
heim_type_description desc)
|
||||
{
|
||||
heim_type_t type;
|
||||
|
||||
type = calloc(1, sizeof(*type));
|
||||
if (type == NULL)
|
||||
return NULL;
|
||||
|
||||
type->tid = heim_base_atomic_inc(&tidglobal);
|
||||
type->name = name;
|
||||
type->init = init;
|
||||
type->dealloc = dealloc;
|
||||
type->copy = copy;
|
||||
type->cmp = cmp;
|
||||
type->hash = hash;
|
||||
type->desc = desc;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
heim_object_t
|
||||
_heim_alloc_object(heim_type_t type, size_t size)
|
||||
{
|
||||
/* XXX should use posix_memalign */
|
||||
struct heim_base *p = calloc(1, size + sizeof(*p));
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
p->isa = type;
|
||||
p->ref_cnt = 1;
|
||||
|
||||
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)
|
||||
{
|
||||
return type->tid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call func once and only once
|
||||
*
|
||||
* @param once pointer to a heim_base_once_t
|
||||
* @param ctx context passed to func
|
||||
* @param func function to be called
|
||||
*/
|
||||
|
||||
void
|
||||
heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
|
||||
{
|
||||
#ifdef HAVE_DISPATCH_DISPATCH_H
|
||||
dispatch_once_f(once, ctx, func);
|
||||
#else
|
||||
static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
|
||||
HEIMDAL_MUTEX_lock(&mutex);
|
||||
if (*once == 0) {
|
||||
*once = 1;
|
||||
HEIMDAL_MUTEX_unlock(&mutex);
|
||||
func(ctx);
|
||||
HEIMDAL_MUTEX_lock(&mutex);
|
||||
*once = 2;
|
||||
HEIMDAL_MUTEX_unlock(&mutex);
|
||||
} else if (*once == 2) {
|
||||
HEIMDAL_MUTEX_unlock(&mutex);
|
||||
} else {
|
||||
HEIMDAL_MUTEX_unlock(&mutex);
|
||||
while (1) {
|
||||
struct timeval tv = { 0, 1000 };
|
||||
select(0, NULL, NULL, NULL, &tv);
|
||||
HEIMDAL_MUTEX_lock(&mutex);
|
||||
if (*once == 2)
|
||||
break;
|
||||
HEIMDAL_MUTEX_unlock(&mutex);
|
||||
}
|
||||
HEIMDAL_MUTEX_unlock(&mutex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort and log the failure (using syslog)
|
||||
*/
|
||||
|
||||
void
|
||||
heim_abort(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
heim_abortv(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort and log the failure (using syslog)
|
||||
*/
|
||||
|
||||
void
|
||||
heim_abortv(const char *fmt, va_list ap)
|
||||
{
|
||||
static char str[1024];
|
||||
|
||||
vsnprintf(str, sizeof(str), fmt, ap);
|
||||
syslog(LOG_ERR, "heim_abort: %s", str);
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static int ar_created = 0;
|
||||
static HEIMDAL_thread_key ar_key;
|
||||
|
||||
struct ar_tls {
|
||||
struct heim_auto_release *head;
|
||||
struct heim_auto_release *current;
|
||||
HEIMDAL_MUTEX tls_mutex;
|
||||
};
|
||||
|
||||
static void
|
||||
ar_tls_delete(void *ptr)
|
||||
{
|
||||
struct ar_tls *tls = ptr;
|
||||
if (tls->head)
|
||||
heim_release(tls->head);
|
||||
free(tls);
|
||||
}
|
||||
|
||||
static void
|
||||
init_ar_tls(void *ptr)
|
||||
{
|
||||
int ret;
|
||||
HEIMDAL_key_create(&ar_key, ar_tls_delete, ret);
|
||||
if (ret == 0)
|
||||
ar_created = 1;
|
||||
}
|
||||
|
||||
static struct ar_tls *
|
||||
autorel_tls(void)
|
||||
{
|
||||
static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
|
||||
struct ar_tls *arp;
|
||||
int ret;
|
||||
|
||||
heim_base_once_f(&once, NULL, init_ar_tls);
|
||||
if (!ar_created)
|
||||
return NULL;
|
||||
|
||||
arp = HEIMDAL_getspecific(ar_key);
|
||||
if (arp == NULL) {
|
||||
|
||||
arp = calloc(1, sizeof(*arp));
|
||||
if (arp == NULL)
|
||||
return NULL;
|
||||
HEIMDAL_setspecific(ar_key, arp, ret);
|
||||
if (ret) {
|
||||
free(arp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return arp;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
autorel_dealloc(void *ptr)
|
||||
{
|
||||
heim_auto_release_t ar = ptr;
|
||||
struct ar_tls *tls;
|
||||
|
||||
tls = autorel_tls();
|
||||
if (tls == NULL)
|
||||
heim_abort("autorelease pool released on thread w/o autorelease inited");
|
||||
|
||||
heim_auto_release_drain(ar);
|
||||
|
||||
if (!HEIM_TAILQ_EMPTY(&ar->pool))
|
||||
heim_abort("pool not empty after draining");
|
||||
|
||||
HEIMDAL_MUTEX_lock(&tls->tls_mutex);
|
||||
if (tls->current != ptr)
|
||||
heim_abort("autorelease not releaseing top pool");
|
||||
|
||||
if (tls->current != tls->head)
|
||||
tls->current = ar->parent;
|
||||
HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
autorel_cmp(void *a, void *b)
|
||||
{
|
||||
return (a == b);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
autorel_hash(void *ptr)
|
||||
{
|
||||
return (unsigned long)ptr;
|
||||
}
|
||||
|
||||
|
||||
static struct heim_type_data _heim_autorel_object = {
|
||||
HEIM_TID_AUTORELEASE,
|
||||
"autorelease-pool",
|
||||
NULL,
|
||||
autorel_dealloc,
|
||||
NULL,
|
||||
autorel_cmp,
|
||||
autorel_hash,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
heim_auto_release_create(void)
|
||||
{
|
||||
struct ar_tls *tls = autorel_tls();
|
||||
heim_auto_release_t ar;
|
||||
|
||||
if (tls == NULL)
|
||||
heim_abort("Failed to create/get autorelease head");
|
||||
|
||||
ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release));
|
||||
if (ar) {
|
||||
HEIMDAL_MUTEX_lock(&tls->tls_mutex);
|
||||
if (tls->head == NULL)
|
||||
tls->head = ar;
|
||||
ar->parent = tls->current;
|
||||
tls->current = ar;
|
||||
HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
|
||||
}
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the current object on the thread's auto-release pool
|
||||
*
|
||||
* @param ptr object
|
||||
*/
|
||||
|
||||
heim_object_t
|
||||
heim_auto_release(heim_object_t ptr)
|
||||
{
|
||||
struct heim_base *p = PTR2BASE(ptr);
|
||||
struct ar_tls *tls = autorel_tls();
|
||||
heim_auto_release_t ar;
|
||||
|
||||
if (ptr == NULL || heim_base_is_tagged(ptr))
|
||||
return ptr;
|
||||
|
||||
/* drop from old pool */
|
||||
if ((ar = p->autorelpool) != NULL) {
|
||||
HEIMDAL_MUTEX_lock(&ar->pool_mutex);
|
||||
HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
|
||||
p->autorelpool = NULL;
|
||||
HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
|
||||
}
|
||||
|
||||
if (tls == NULL || (ar = tls->current) == NULL)
|
||||
heim_abort("no auto relase pool in place, would leak");
|
||||
|
||||
HEIMDAL_MUTEX_lock(&ar->pool_mutex);
|
||||
HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel);
|
||||
p->autorelpool = ar;
|
||||
HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all objects on the given auto-release pool
|
||||
*/
|
||||
|
||||
void
|
||||
heim_auto_release_drain(heim_auto_release_t autorel)
|
||||
{
|
||||
heim_object_t obj;
|
||||
|
||||
/* release all elements on the tail queue */
|
||||
|
||||
HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
|
||||
while(!HEIM_TAILQ_EMPTY(&autorel->pool)) {
|
||||
obj = HEIM_TAILQ_FIRST(&autorel->pool);
|
||||
HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
|
||||
heim_release(BASE2PTR(obj));
|
||||
HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
|
||||
}
|
||||
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 = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
389
lib/base/heimbase.h
Normal file
389
lib/base/heimbase.h
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
|
||||
*/
|
||||
|
||||
#ifndef HEIM_BASE_H
|
||||
#define HEIM_BASE_H 1
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <krb5-types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void * heim_object_t;
|
||||
typedef unsigned int heim_tid_t;
|
||||
typedef heim_object_t heim_bool_t;
|
||||
typedef heim_object_t heim_null_t;
|
||||
#define HEIM_BASE_ONCE_INIT 0
|
||||
typedef long heim_base_once_t; /* XXX arch dependant */
|
||||
|
||||
#if !defined(__has_extension)
|
||||
#define __has_extension(x) 0
|
||||
#endif
|
||||
|
||||
#define HEIM_REQUIRE_GNUC(m,n,p) \
|
||||
(((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) >= \
|
||||
(((m) * 10000) + ((n) * 100) + (p)))
|
||||
|
||||
|
||||
#if __has_extension(__builtin_expect) || HEIM_REQUIRE_GNUC(3,0,0)
|
||||
#define heim_builtin_expect(_op,_res) __builtin_expect(_op,_res)
|
||||
#else
|
||||
#define heim_builtin_expect(_op,_res) (_op)
|
||||
#endif
|
||||
|
||||
|
||||
void * heim_retain(heim_object_t);
|
||||
void heim_release(heim_object_t);
|
||||
|
||||
void heim_show(heim_object_t);
|
||||
|
||||
typedef void (*heim_type_dealloc)(void *);
|
||||
|
||||
void *
|
||||
heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc);
|
||||
|
||||
heim_tid_t
|
||||
heim_get_tid(heim_object_t object);
|
||||
|
||||
int
|
||||
heim_cmp(heim_object_t a, heim_object_t b);
|
||||
|
||||
unsigned long
|
||||
heim_get_hash(heim_object_t ptr);
|
||||
|
||||
void
|
||||
heim_base_once_f(heim_base_once_t *, void *, void (*)(void *));
|
||||
|
||||
void
|
||||
heim_abort(const char *fmt, ...)
|
||||
HEIMDAL_NORETURN_ATTRIBUTE
|
||||
HEIMDAL_PRINTF_ATTRIBUTE((printf, 1, 2));
|
||||
|
||||
void
|
||||
heim_abortv(const char *fmt, va_list ap)
|
||||
HEIMDAL_NORETURN_ATTRIBUTE
|
||||
HEIMDAL_PRINTF_ATTRIBUTE((printf, 1, 0));
|
||||
|
||||
#define heim_assert(e,t) \
|
||||
(heim_builtin_expect(!(e), 0) ? heim_abort(t ":" #e) : (void)0)
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
heim_null_t
|
||||
heim_null_create(void);
|
||||
|
||||
heim_bool_t
|
||||
heim_bool_create(int);
|
||||
|
||||
int
|
||||
heim_bool_val(heim_bool_t);
|
||||
|
||||
/*
|
||||
* Array
|
||||
*/
|
||||
|
||||
typedef struct heim_array_data *heim_array_t;
|
||||
|
||||
heim_array_t heim_array_create(void);
|
||||
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));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dict
|
||||
*/
|
||||
|
||||
typedef struct heim_dict_data *heim_dict_t;
|
||||
|
||||
heim_dict_t heim_dict_create(size_t size);
|
||||
heim_tid_t heim_dict_get_type_id(void);
|
||||
|
||||
typedef void (*heim_dict_iterator_f_t)(heim_object_t, heim_object_t, void *);
|
||||
|
||||
int heim_dict_set_value(heim_dict_t, heim_object_t, heim_object_t);
|
||||
void heim_dict_iterate_f(heim_dict_t, void *, heim_dict_iterator_f_t);
|
||||
#ifdef __BLOCKS__
|
||||
void heim_dict_iterate(heim_dict_t, void (^)(heim_object_t, heim_object_t));
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* String
|
||||
*/
|
||||
|
||||
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_string_t heim_string_create_with_format(const char *, ...);
|
||||
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
|
||||
*/
|
||||
|
||||
typedef struct heim_number_data *heim_number_t;
|
||||
|
||||
heim_number_t heim_number_create(int);
|
||||
heim_tid_t heim_number_get_type_id(void);
|
||||
int heim_number_get_int(heim_number_t);
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct heim_auto_release * heim_auto_release_t;
|
||||
|
||||
heim_auto_release_t heim_auto_release_create(void);
|
||||
void heim_auto_release_drain(heim_auto_release_t);
|
||||
heim_object_t heim_auto_release(heim_object_t);
|
||||
|
||||
/*
|
||||
* JSON
|
||||
*/
|
||||
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;
|
||||
|
||||
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 *);
|
||||
|
||||
|
||||
/*
|
||||
* Debug
|
||||
*/
|
||||
|
||||
heim_string_t
|
||||
heim_description(heim_object_t ptr);
|
||||
|
||||
/*
|
||||
* Binary search.
|
||||
*
|
||||
* 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,
|
||||
char **value, size_t *location, size_t *loops);
|
||||
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,
|
||||
size_t *location, size_t *loops, size_t *reads);
|
||||
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);
|
||||
|
||||
#endif /* HEIM_BASE_H */
|
107
lib/base/heimbasepriv.h
Normal file
107
lib/base/heimbasepriv.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
|
||||
*/
|
||||
|
||||
typedef void (*heim_type_init)(void *);
|
||||
typedef heim_object_t (*heim_type_copy)(void *);
|
||||
typedef int (*heim_type_cmp)(void *, void *);
|
||||
typedef unsigned long (*heim_type_hash)(void *);
|
||||
typedef heim_string_t (*heim_type_description)(void *);
|
||||
|
||||
typedef struct heim_type_data *heim_type_t;
|
||||
|
||||
enum {
|
||||
HEIM_TID_NUMBER = 0,
|
||||
HEIM_TID_NULL = 1,
|
||||
HEIM_TID_BOOL = 2,
|
||||
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,
|
||||
HEIM_TID_STRING = 131,
|
||||
HEIM_TID_AUTORELEASE = 132,
|
||||
HEIM_TID_ERROR = 133,
|
||||
HEIM_TID_DATA = 134,
|
||||
HEIM_TID_DB = 135,
|
||||
HEIM_TID_USER = 255
|
||||
|
||||
};
|
||||
|
||||
struct heim_type_data {
|
||||
heim_tid_t tid;
|
||||
const char *name;
|
||||
heim_type_init init;
|
||||
heim_type_dealloc dealloc;
|
||||
heim_type_copy copy;
|
||||
heim_type_cmp cmp;
|
||||
heim_type_hash hash;
|
||||
heim_type_description desc;
|
||||
};
|
||||
|
||||
heim_type_t _heim_get_isa(heim_object_t);
|
||||
|
||||
heim_type_t
|
||||
_heim_create_type(const char *name,
|
||||
heim_type_init init,
|
||||
heim_type_dealloc dealloc,
|
||||
heim_type_copy copy,
|
||||
heim_type_cmp cmp,
|
||||
heim_type_hash hash,
|
||||
heim_type_description desc);
|
||||
|
||||
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;
|
||||
extern struct heim_type_data _heim_number_object;
|
||||
extern struct heim_type_data _heim_string_object;
|
167
lib/base/heimqueue.h
Normal file
167
lib/base/heimqueue.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* $NetBSD: queue.h,v 1.38 2004/04/18 14:12:05 lukem Exp $ */
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _HEIM_QUEUE_H_
|
||||
#define _HEIM_QUEUE_H_
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define HEIM_TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define HEIM_TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
#define HEIM_TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#if defined(_KERNEL) && defined(QUEUEDEBUG)
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD(head, elm, field) \
|
||||
if ((head)->tqh_first && \
|
||||
(head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
|
||||
panic("HEIM_TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL(head, elm, field) \
|
||||
if (*(head)->tqh_last != NULL) \
|
||||
panic("HEIM_TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_OP(elm, field) \
|
||||
if ((elm)->field.tqe_next && \
|
||||
(elm)->field.tqe_next->field.tqe_prev != \
|
||||
&(elm)->field.tqe_next) \
|
||||
panic("HEIM_TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
|
||||
if (*(elm)->field.tqe_prev != (elm)) \
|
||||
panic("HEIM_TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_PREREMOVE(head, elm, field) \
|
||||
if ((elm)->field.tqe_next == NULL && \
|
||||
(head)->tqh_last != &(elm)->field.tqe_next) \
|
||||
panic("HEIM_TAILQ_PREREMOVE head %p elm %p %s:%d", \
|
||||
(head), (elm), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE(elm, field) \
|
||||
(elm)->field.tqe_next = (void *)1L; \
|
||||
(elm)->field.tqe_prev = (void *)1L;
|
||||
#else
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD(head, elm, field)
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL(head, elm, field)
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_OP(elm, field)
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_PREREMOVE(head, elm, field)
|
||||
#define QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE(elm, field)
|
||||
#endif
|
||||
|
||||
#define HEIM_TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define HEIM_TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD((head), (elm), field) \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define HEIM_TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL((head), (elm), field) \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define HEIM_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
QUEUEDEBUG_HEIM_TAILQ_OP((listelm), field) \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define HEIM_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QUEUEDEBUG_HEIM_TAILQ_OP((listelm), field) \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define HEIM_TAILQ_REMOVE(head, elm, field) do { \
|
||||
QUEUEDEBUG_HEIM_TAILQ_PREREMOVE((head), (elm), field) \
|
||||
QUEUEDEBUG_HEIM_TAILQ_OP((elm), field) \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE((elm), field); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define HEIM_TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->tqh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.tqe_next))
|
||||
|
||||
#define HEIM_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
|
||||
(var); \
|
||||
(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
|
||||
|
||||
/*
|
||||
* Tail queue access methods.
|
||||
*/
|
||||
#define HEIM_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
#define HEIM_TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define HEIM_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define HEIM_TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
#define HEIM_TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
|
||||
#endif /* !_HEIM_QUEUE_H_ */
|
811
lib/base/json.c
Normal file
811
lib/base/json.c
Normal file
@@ -0,0 +1,811 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "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;
|
||||
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
|
||||
base2json(heim_object_t, struct twojson *);
|
||||
|
||||
static void
|
||||
indent(struct twojson *j)
|
||||
{
|
||||
size_t i = j->indent;
|
||||
if (j->flags & HEIM_JSON_F_ONE_LINE)
|
||||
return;
|
||||
while (i--)
|
||||
j->out(j->ctx, "\t");
|
||||
}
|
||||
|
||||
static void
|
||||
array2json(heim_object_t value, void *ctx)
|
||||
{
|
||||
struct twojson *j = ctx;
|
||||
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;
|
||||
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) {
|
||||
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);
|
||||
switch (type) {
|
||||
case HEIM_TID_ARRAY:
|
||||
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:
|
||||
indent(j);
|
||||
j->out(j->ctx, "\"");
|
||||
j->out(j->ctx, heim_string_get_utf8(obj));
|
||||
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[32];
|
||||
indent(j);
|
||||
snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
|
||||
j->out(j->ctx, num);
|
||||
break;
|
||||
}
|
||||
case HEIM_TID_NULL:
|
||||
indent(j);
|
||||
j->out(j->ctx, "null");
|
||||
break;
|
||||
case HEIM_TID_BOOL:
|
||||
indent(j);
|
||||
j->out(j->ctx, heim_bool_val(obj) ? "true" : "false");
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
struct parse_ctx {
|
||||
unsigned long lineno;
|
||||
const uint8_t *p;
|
||||
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)
|
||||
{
|
||||
while (ctx->p < ctx->pend) {
|
||||
uint8_t c = *ctx->p;
|
||||
if (c == ' ' || c == '\t' || c == '\r') {
|
||||
|
||||
} else if (c == '\n') {
|
||||
ctx->lineno++;
|
||||
} else
|
||||
return 0;
|
||||
(ctx->p)++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
is_number(uint8_t n)
|
||||
{
|
||||
return ('0' <= n && n <= '9');
|
||||
}
|
||||
|
||||
static heim_number_t
|
||||
parse_number(struct parse_ctx *ctx)
|
||||
{
|
||||
int number = 0, neg = 1;
|
||||
|
||||
if (ctx->p >= ctx->pend)
|
||||
return NULL;
|
||||
|
||||
if (*ctx->p == '-') {
|
||||
if (ctx->p + 1 >= ctx->pend)
|
||||
return NULL;
|
||||
neg = -1;
|
||||
ctx->p += 1;
|
||||
}
|
||||
|
||||
while (ctx->p < ctx->pend) {
|
||||
if (is_number(*ctx->p)) {
|
||||
number = (number * 10) + (*ctx->p - '0');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ctx->p += 1;
|
||||
}
|
||||
|
||||
return heim_number_create(number * neg);
|
||||
}
|
||||
|
||||
static heim_string_t
|
||||
parse_string(struct parse_ctx *ctx)
|
||||
{
|
||||
const uint8_t *start;
|
||||
int quote = 0;
|
||||
|
||||
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) {
|
||||
if (*ctx->p == '\n') {
|
||||
ctx->lineno++;
|
||||
} else if (*ctx->p == '\\') {
|
||||
if (ctx->p + 1 == ctx->pend)
|
||||
goto out;
|
||||
ctx->p++;
|
||||
quote = 1;
|
||||
} else if (*ctx->p == '"') {
|
||||
heim_object_t o;
|
||||
|
||||
if (quote) {
|
||||
char *p0, *p;
|
||||
p = p0 = malloc(ctx->p - start);
|
||||
if (p == NULL)
|
||||
goto out;
|
||||
while (start < ctx->p) {
|
||||
if (*start == '\\') {
|
||||
start++;
|
||||
/* XXX validate quoted char */
|
||||
}
|
||||
*p++ = *start++;
|
||||
}
|
||||
o = heim_string_create_with_bytes(p0, p - p0);
|
||||
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:
|
||||
ctx->error = heim_error_create(EINVAL, "ran out of string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
|
||||
{
|
||||
heim_string_t key;
|
||||
heim_object_t value;
|
||||
|
||||
if (white_spaces(ctx))
|
||||
return -1;
|
||||
|
||||
if (*ctx->p == '}') {
|
||||
ctx->p++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)) {
|
||||
heim_release(key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*ctx->p != ':') {
|
||||
heim_release(key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->p += 1; /* safe because we call white_spaces() next */
|
||||
|
||||
if (white_spaces(ctx)) {
|
||||
heim_release(key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = parse_value(ctx);
|
||||
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;
|
||||
}
|
||||
heim_dict_set_value(dict, key, value);
|
||||
heim_release(key);
|
||||
heim_release(value);
|
||||
|
||||
if (white_spaces(ctx))
|
||||
return -1;
|
||||
|
||||
if (*ctx->p == '}') {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static heim_dict_t
|
||||
parse_dict(struct parse_ctx *ctx)
|
||||
{
|
||||
heim_dict_t dict;
|
||||
size_t count = 0;
|
||||
int ret;
|
||||
|
||||
heim_assert(*ctx->p == '{', "string doesn't start with {");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_item(heim_array_t array, struct parse_ctx *ctx)
|
||||
{
|
||||
heim_object_t value;
|
||||
|
||||
if (white_spaces(ctx))
|
||||
return -1;
|
||||
|
||||
if (*ctx->p == ']') {
|
||||
ctx->p++; /* safe because parse_value() calls white_spaces() first */
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = parse_value(ctx);
|
||||
if (value == NULL &&
|
||||
(ctx->error || (ctx->flags & HEIM_JSON_F_NO_C_NULL)))
|
||||
return -1;
|
||||
|
||||
heim_array_append_value(array, value);
|
||||
heim_release(value);
|
||||
|
||||
if (white_spaces(ctx))
|
||||
return -1;
|
||||
|
||||
if (*ctx->p == ']') {
|
||||
ctx->p++;
|
||||
return 0;
|
||||
} else if (*ctx->p == ',') {
|
||||
ctx->p++;
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static heim_array_t
|
||||
parse_array(struct parse_ctx *ctx)
|
||||
{
|
||||
heim_array_t array = heim_array_create();
|
||||
int ret;
|
||||
|
||||
heim_assert(*ctx->p == '[', "array doesn't start with [");
|
||||
ctx->p += 1;
|
||||
|
||||
while ((ret = parse_item(array, ctx)) > 0)
|
||||
;
|
||||
if (ret < 0) {
|
||||
heim_release(array);
|
||||
return NULL;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
static heim_object_t
|
||||
parse_value(struct parse_ctx *ctx)
|
||||
{
|
||||
size_t len;
|
||||
heim_object_t o;
|
||||
|
||||
if (white_spaces(ctx))
|
||||
return NULL;
|
||||
|
||||
if (*ctx->p == '"') {
|
||||
return parse_string(ctx);
|
||||
} else if (*ctx->p == '{') {
|
||||
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 == '[') {
|
||||
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 ((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) {
|
||||
ctx->p += 4;
|
||||
return heim_bool_create(1);
|
||||
} else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) {
|
||||
ctx->p += 5;
|
||||
return heim_bool_create(0);
|
||||
}
|
||||
|
||||
ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu",
|
||||
(char)*ctx->p,
|
||||
(unsigned long)(ctx->p - ctx->pstart),
|
||||
ctx->lineno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
heim_object_t
|
||||
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), max_depth, flags,
|
||||
error);
|
||||
}
|
||||
|
||||
heim_object_t
|
||||
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);
|
||||
|
||||
if (o == NULL && error) {
|
||||
*error = ctx.error;
|
||||
} else if (ctx.error) {
|
||||
heim_release(ctx.error);
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
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, 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;
|
||||
}
|
53
lib/base/null.c
Normal file
53
lib/base/null.c
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
|
||||
struct heim_type_data _heim_null_object = {
|
||||
HEIM_TID_NULL,
|
||||
"null-object",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
heim_null_t
|
||||
heim_null_create(void)
|
||||
{
|
||||
return heim_base_make_tagged_object(0, HEIM_TID_NULL);
|
||||
}
|
128
lib/base/number.c
Normal file
128
lib/base/number.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
|
||||
static void
|
||||
number_dealloc(void *ptr)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
number_cmp(void *a, void *b)
|
||||
{
|
||||
int na, nb;
|
||||
|
||||
if (heim_base_is_tagged_object(a))
|
||||
na = heim_base_tagged_object_value(a);
|
||||
else
|
||||
na = *(int *)a;
|
||||
|
||||
if (heim_base_is_tagged_object(b))
|
||||
nb = heim_base_tagged_object_value(b);
|
||||
else
|
||||
nb = *(int *)b;
|
||||
|
||||
return na - nb;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
number_hash(void *ptr)
|
||||
{
|
||||
if (heim_base_is_tagged_object(ptr))
|
||||
return heim_base_tagged_object_value(ptr);
|
||||
return (unsigned long)*(int *)ptr;
|
||||
}
|
||||
|
||||
struct heim_type_data _heim_number_object = {
|
||||
HEIM_TID_NUMBER,
|
||||
"number-object",
|
||||
NULL,
|
||||
number_dealloc,
|
||||
NULL,
|
||||
number_cmp,
|
||||
number_hash,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a number object
|
||||
*
|
||||
* @param the number to contain in the object
|
||||
*
|
||||
* @return a number object
|
||||
*/
|
||||
|
||||
heim_number_t
|
||||
heim_number_create(int number)
|
||||
{
|
||||
heim_number_t n;
|
||||
|
||||
if (number < 0xffffff && number >= 0)
|
||||
return heim_base_make_tagged_object(number, HEIM_TID_NUMBER);
|
||||
|
||||
n = _heim_alloc_object(&_heim_number_object, sizeof(int));
|
||||
if (n)
|
||||
*((int *)n) = number;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type ID of number objects
|
||||
*
|
||||
* @return type id of number objects
|
||||
*/
|
||||
|
||||
heim_tid_t
|
||||
heim_number_get_type_id(void)
|
||||
{
|
||||
return HEIM_TID_NUMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int value of the content
|
||||
*
|
||||
* @param number the number object to get the value from
|
||||
*
|
||||
* @return an int
|
||||
*/
|
||||
|
||||
int
|
||||
heim_number_get_int(heim_number_t number)
|
||||
{
|
||||
if (heim_base_is_tagged_object(number))
|
||||
return heim_base_tagged_object_value(number);
|
||||
return *(int *)number;
|
||||
}
|
61
lib/base/roken_rename.h
Normal file
61
lib/base/roken_rename.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 1998 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef __heimbase_roken_rename_h__
|
||||
#define __heimbase_roken_rename_h__
|
||||
|
||||
#ifndef HAVE_VSNPRINTF
|
||||
#define rk_vsnprintf heimbase_vsnprintf
|
||||
#endif
|
||||
#ifndef HAVE_ASPRINTF
|
||||
#define rk_asprintf heimbase_asprintf
|
||||
#endif
|
||||
#ifndef HAVE_ASNPRINTF
|
||||
#define rk_asnprintf heimbase_asnprintf
|
||||
#endif
|
||||
#ifndef HAVE_VASPRINTF
|
||||
#define rk_vasprintf heimbase_vasprintf
|
||||
#endif
|
||||
#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__ */
|
270
lib/base/string.c
Normal file
270
lib/base/string.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
string_hash(void *ptr)
|
||||
{
|
||||
const char *s = ptr;
|
||||
unsigned long n;
|
||||
|
||||
for (n = 0; *s; ++s)
|
||||
n += *s;
|
||||
return n;
|
||||
}
|
||||
|
||||
struct heim_type_data _heim_string_object = {
|
||||
HEIM_TID_STRING,
|
||||
"string-object",
|
||||
NULL,
|
||||
string_dealloc,
|
||||
NULL,
|
||||
string_cmp,
|
||||
string_hash,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a string object
|
||||
*
|
||||
* @param string the string to create, must be an utf8 string
|
||||
*
|
||||
* @return string object
|
||||
*/
|
||||
|
||||
heim_string_t
|
||||
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)
|
||||
{
|
||||
heim_string_t s;
|
||||
|
||||
s = _heim_alloc_object(&_heim_string_object, len + 1);
|
||||
if (s) {
|
||||
memcpy(s, data, len);
|
||||
((char *)s)[len] = '\0';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
string_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string object using a format string
|
||||
*
|
||||
* @param fmt format string
|
||||
* @param ...
|
||||
*
|
||||
* @return string object
|
||||
*/
|
||||
|
||||
heim_string_t
|
||||
heim_string_create_with_format(const char *fmt, ...)
|
||||
{
|
||||
heim_string_t s;
|
||||
char *str = NULL;
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vasprintf(&str, fmt, ap);
|
||||
va_end(ap);
|
||||
if (ret < 0 || str == NULL)
|
||||
return NULL;
|
||||
|
||||
s = heim_string_ref_create(str, string_dealloc);
|
||||
if (s == NULL)
|
||||
free(str);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type ID of string objects
|
||||
*
|
||||
* @return type id of string objects
|
||||
*/
|
||||
|
||||
heim_tid_t
|
||||
heim_string_get_type_id(void)
|
||||
{
|
||||
return HEIM_TID_STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string value of the content.
|
||||
*
|
||||
* @param string the string object to get the value from
|
||||
*
|
||||
* @return a utf8 string
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
init_string(void *ptr)
|
||||
{
|
||||
heim_dict_t *dict = ptr;
|
||||
*dict = heim_dict_create(101);
|
||||
heim_assert(*dict != NULL, "__heim_string_constant");
|
||||
}
|
||||
|
||||
heim_string_t
|
||||
__heim_string_constant(const char *_str)
|
||||
{
|
||||
static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
|
||||
static heim_base_once_t once;
|
||||
static heim_dict_t dict = NULL;
|
||||
heim_string_t s, s2;
|
||||
|
||||
heim_base_once_f(&once, &dict, init_string);
|
||||
s = heim_string_create(_str);
|
||||
|
||||
HEIMDAL_MUTEX_lock(&mutex);
|
||||
s2 = heim_dict_get_value(dict, s);
|
||||
if (s2) {
|
||||
heim_release(s);
|
||||
s = s2;
|
||||
} else {
|
||||
_heim_make_permanent(s);
|
||||
heim_dict_set_value(dict, s, s);
|
||||
}
|
||||
HEIMDAL_MUTEX_unlock(&mutex);
|
||||
|
||||
return s;
|
||||
}
|
908
lib/base/test_base.c
Normal file
908
lib/base/test_base.c
Normal file
@@ -0,0 +1,908 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 "baselocl.h"
|
||||
|
||||
static void
|
||||
memory_free(heim_object_t obj)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
test_memory(void)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = heim_alloc(10, "memory", memory_free);
|
||||
|
||||
heim_retain(ptr);
|
||||
heim_release(ptr);
|
||||
|
||||
heim_retain(ptr);
|
||||
heim_release(ptr);
|
||||
|
||||
heim_release(ptr);
|
||||
|
||||
ptr = heim_alloc(10, "memory", NULL);
|
||||
heim_release(ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_dict(void)
|
||||
{
|
||||
heim_dict_t dict;
|
||||
heim_number_t a1 = heim_number_create(1);
|
||||
heim_string_t a2 = heim_string_create("hejsan");
|
||||
heim_number_t a3 = heim_number_create(3);
|
||||
heim_string_t a4 = heim_string_create("foosan");
|
||||
|
||||
dict = heim_dict_create(10);
|
||||
|
||||
heim_dict_set_value(dict, a1, a2);
|
||||
heim_dict_set_value(dict, a3, a4);
|
||||
|
||||
heim_dict_delete_key(dict, a3);
|
||||
heim_dict_delete_key(dict, a1);
|
||||
|
||||
heim_release(a1);
|
||||
heim_release(a2);
|
||||
heim_release(a3);
|
||||
heim_release(a4);
|
||||
|
||||
heim_release(dict);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_auto_release(void)
|
||||
{
|
||||
heim_auto_release_t ar1, ar2;
|
||||
heim_number_t n1;
|
||||
heim_string_t s1;
|
||||
|
||||
ar1 = heim_auto_release_create();
|
||||
|
||||
s1 = heim_string_create("hejsan");
|
||||
heim_auto_release(s1);
|
||||
|
||||
n1 = heim_number_create(1);
|
||||
heim_auto_release(n1);
|
||||
|
||||
ar2 = heim_auto_release_create();
|
||||
|
||||
n1 = heim_number_create(1);
|
||||
heim_auto_release(n1);
|
||||
|
||||
heim_release(ar2);
|
||||
heim_release(ar1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_string(void)
|
||||
{
|
||||
heim_string_t s1, s2;
|
||||
const char *string = "hejsan";
|
||||
|
||||
s1 = heim_string_create(string);
|
||||
s2 = heim_string_create(string);
|
||||
|
||||
if (heim_cmp(s1, s2) != 0) {
|
||||
printf("the same string is not the same\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
heim_release(s1);
|
||||
heim_release(s2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_error(void)
|
||||
{
|
||||
heim_error_t e;
|
||||
heim_string_t s;
|
||||
|
||||
e = heim_error_create(10, "foo: %s", "bar");
|
||||
heim_assert(heim_error_get_code(e) == 10, "error_code != 10");
|
||||
|
||||
s = heim_error_copy_string(e);
|
||||
heim_assert(strcmp(heim_string_get_utf8(s), "foo: bar") == 0, "msg wrong");
|
||||
|
||||
heim_release(s);
|
||||
heim_release(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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\"", 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\" ]", 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\" }", 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\" } : \"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");
|
||||
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\" } }", 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_dict_get_type_id(), "dict-tid");
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
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_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", 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", 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 ]", 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 ]", 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", (int)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;
|
||||
const void *kptr, *vptr;
|
||||
size_t klen, vlen;
|
||||
|
||||
heim_assert(heim_get_tid(k) == heim_data_get_type_id(), "...");
|
||||
|
||||
kptr = heim_data_get_ptr(k);
|
||||
klen = heim_data_get_length(k);
|
||||
vptr = heim_data_get_ptr(v);
|
||||
vlen = heim_data_get_length(v);
|
||||
|
||||
if (klen == strlen("msg") && !strncmp(kptr, "msg", strlen("msg")) &&
|
||||
vlen == strlen("abc") && !strncmp(vptr, "abc", strlen("abc")))
|
||||
*ret &= ~(1);
|
||||
else if (klen == strlen("msg2") &&
|
||||
!strncmp(kptr, "msg2", strlen("msg2")) &&
|
||||
vlen == strlen("FooBar") && !strncmp(vptr, "FooBar", strlen("FooBar")))
|
||||
*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)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res |= test_memory();
|
||||
res |= test_dict();
|
||||
res |= test_auto_release();
|
||||
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;
|
||||
}
|
90
lib/base/version-script.map
Normal file
90
lib/base/version-script.map
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
HEIMDAL_BASE_1.0 {
|
||||
global:
|
||||
_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;
|
||||
heim_base_once_f;
|
||||
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;
|
||||
heim_dict_get_value;
|
||||
heim_dict_iterate_f;
|
||||
heim_dict_set_value;
|
||||
heim_error_append;
|
||||
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:
|
||||
*;
|
||||
};
|
Reference in New Issue
Block a user