Made ez-blob-print public.
CPPFLAGS=-I.
-CFLAGS=-O2 -fPIC -Wall -mtune=native
+CFLAGS=-Og -g -fPIC -Wall -mtune=native
test_LDLIBS=-lpthread -lrt
ARFLAGS=rvsUc
-VERSION=2.0
+VERSION=2.1.99
SRCS= \
ez-bitset.c \
ez-blob.c \
+ ez-blob-print.c \
ez-port.c \
ez-set.c \
ez-tree.c
$(CC) $(CFLAGS) -o $@ $< libeze.a $(test_LDLIBS)
test-bitset: libeze.a(ez-bitset.o)
-test-blob: libeze.a(ez-blob.o)
+test-blob: libeze.a(ez-blob.o) libeze.a(ez-blob-print.o)
test-port: libeze.a(ez-port.o)
test-set: libeze.a(ez-set.o)
test-tree: libeze.a(ez-tree.o)
--- /dev/null
+/* ez-blob.h: Serialising description and serialiser.
+
+ Copyright (C) 2019 Michael Zucchi
+
+ This program is free software: you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License
+ as published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ez-blob.h"
+#include "ez-list.h"
+
+static void dumphex(const char *data, size_t size, const char *prefix) {
+ for (int i=0;i<size;i+=16) {
+ fputs(prefix, stdout);
+ printf(" %04x:", i);
+ for (int j=0;j<16;j++) {
+ if (i+j<size)
+ printf(" %02x", data[i+j] & 0xff);
+ else
+ fputs(" ", stdout);
+ }
+ fputs(" ", stdout);
+ for (int j=0;j<16;j++) {
+ if (i+j<size)
+ putchar(isprint(data[i+j]) ? data[i+j] : '.');
+ else
+ putchar(' ');
+ }
+ putchar('\n');
+ }
+}
+
+void ez_blob_print(const ez_blob_desc *d, const void *a, int depth) {
+ int e = 1;
+ char x[depth+1];
+
+ memset(x, ' ', depth);
+ x[depth] = 0;
+
+ for (;e && (d->bd_type != EZ_BLOB_END);d++) {
+ const void *u = a + d->bd_offset;
+
+ printf("%s [type=$%02x, offset=$%04x]", x, d->bd_type, d->bd_offset);
+
+ switch (d->bd_type) {
+ case EZ_BLOB_INT8:
+ printf(" .%d = %d\n", d->bd_id, *(uint8_t *)u);
+ break;
+ case EZ_BLOB_INT16:
+ printf(" .%d = %d\n", d->bd_id, *(uint16_t *)u);
+ break;
+ case EZ_BLOB_INT32:
+ printf(" .%d = %d\n", d->bd_id, *(uint32_t *)u);
+ break;
+ case EZ_BLOB_INT64:
+ printf(" .%d = %ld\n", d->bd_id, *(uint64_t *)u);
+ break;
+ case EZ_BLOB_FLOAT32:
+ printf(" .%d = %f\n", d->bd_id, *(float *)u);
+ break;
+ case EZ_BLOB_FLOAT64:
+ printf(" .%d = %f\n", d->bd_id, *(double *)u);
+ break;
+ case EZ_BLOB_STRING:
+ printf(" .%d %p = `%s'\n", d->bd_id, *((char **)u), *((char **)u));
+ break;
+ case EZ_BLOB_BLOB: {
+ const struct ez_blob *ua = u;
+
+ printf(" .%d %p [%zd] = {\n", d->bd_id, ua->data, ua->size);
+ dumphex(ua->data, ua->size, x);
+ printf("%s }\n", x);
+ break; }
+ case EZ_BLOB_POINTER: {
+ void *up = *((void **)u);
+ if (up) {
+ printf(" .%d %p = {\n", d->bd_id, up);
+ ez_blob_print(d->bd_table, up, depth+4);
+ printf("%s }\n", x);
+ } else {
+ printf(" .%d %p\n", d->bd_id, up);
+ }
+ break; }
+ case EZ_BLOB_LIST: {
+ ez_list *l = (void *)u;
+
+ printf(" .%d %p [] = {\n", d->bd_id, l);
+ for (ez_node *w = ez_list_head(l), *n = ez_node_next(w);
+ n;
+ w = n, n=ez_node_next(n)) {
+ printf("%s{\n", x);
+ ez_blob_print(d->bd_table, w, depth+4);
+ printf("%s},\n", x);
+ }
+ printf("%s }\n", x);
+ break; }
+ default:
+ printf("\n");
+ }
+ }
+}
+
#include <assert.h>
#include "ez-blob.h"
+#include "ez-list.h"
/*
This implements a basic 'high performance' serialisation based on the
char *s = ((char **)v)[0];
size += s ? 4 + strlen(s) : 4;
break; }
- case EZ_BLOB_ARRAY:
- size += 4 + ((const struct ez_blob_array *)v)->size;
+ case EZ_BLOB_BLOB:
+ size += 4 + ((const struct ez_blob *)v)->size;
break;
case EZ_BLOB_STRUCT: {
const void *sp = v;
const void *s = ((const void * const *)v)[0];
size += s ? 4 + ez_blob_size(d->bd_table, s) : 4;
break; }
+ case EZ_BLOB_LIST: {
+ ez_list *l = (ez_list *)v;
+
+ size += 4;
+ for (ez_node *w = ez_list_head(l), *n = ez_node_succ(w);
+ n;
+ w = n, n=ez_node_succ(n)) {
+ size += 4 + ez_blob_size(d->bd_table, w);
+ }
+ break; }
default:
break;
}
case EZ_BLOB_STRING:
free(((char **)v)[0]);
break;
- case EZ_BLOB_ARRAY:
- free(((struct ez_blob_array *)v)->data);
+ case EZ_BLOB_BLOB:
+ free(((struct ez_blob *)v)->data);
break;
case EZ_BLOB_STRUCT:
ez_blob_free_raw(d->bd_table, v);
case EZ_BLOB_POINTER:
ez_blob_free(d->bd_table, ((void **)v)[0]);
break;
+ case EZ_BLOB_LIST: {
+ ez_list *l = (ez_list *)v;
+ for (ez_node *w = ez_list_head(l), *n = ez_node_succ(w);
+ n;
+ w = n, n=ez_node_succ(n)) {
+ ez_blob_free(d->bd_table, w);
+ }
+ break; }
default:
break;
}
}
}
-void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void *p) {
- const char *be = b + size;
+void *ez_blob_decode_raw(const ez_blob_desc *d, const ez_blob *blob, void *p) {
+ const char *b = blob->data;
+ const char *be = b + blob->size;
memset(p, 0, d->bd_offset);
goto fail;
memcpy(v, b, psize);
b += psize;
+ } else if (d->bd_type == EZ_BLOB_LIST) {
+ uint32_t count;
+ ez_list *l = v;
+
+ if (b + 4 > be)
+ goto fail;
+ memcpy(&count, b, 4);
+ b += 4;
+
+ ez_list_init(l);
+ for (int i=0;i<count;i++) {
+ uint32_t ss;
+ ez_blob sub;
+ void *s;
+
+ if (b + 4 > be)
+ goto fail;
+ memcpy(&ss, b, 4);
+ b += 4;
+
+ sub.size = ss;
+ sub.data = (void *)b;
+
+ if (b + ss > be)
+ goto fail;
+ if ((s = calloc(d->bd_table->bd_offset, 1)) == NULL)
+ goto fail;
+ if (ez_blob_decode_raw(d->bd_table, &sub, s) == NULL) {
+ free(s);
+ goto fail;
+ }
+ ez_list_addtail(l, s);
+ b+=ss;
+ }
} else if (d->bd_type <= EZ_BLOB_LAST) {
uint32_t ss;
+ ez_blob sub;
if (b + 4 > be)
goto fail;
memcpy(&ss, b, 4);
b += 4;
+ sub.size = ss;
+ sub.data = (char *)b;
+
switch (d->bd_type) {
case EZ_BLOB_STRING: {
char *s = NULL;
}
((char **)v)[0] = s;
break; }
- case EZ_BLOB_ARRAY: {
- struct ez_blob_array *a = v;
+ case EZ_BLOB_BLOB: {
+ struct ez_blob *a = v;
if (b + ss > be)
goto fail;
if (b + ss > be)
goto fail;
- if (ez_blob_decode_raw(d->bd_table, b, ss, v) == NULL)
+ if (ez_blob_decode_raw(d->bd_table, &sub, v) == NULL)
goto fail;
b += ss;
break;
goto fail;
if ((s = calloc(d->bd_table->bd_offset, 1)) == NULL)
goto fail;
- if (ez_blob_decode_raw(d->bd_table, b, ss, s) == NULL) {
+ if (ez_blob_decode_raw(d->bd_table, &sub, s) == NULL) {
free(s);
goto fail;
}
return NULL;
}
-void *ez_blob_decode(const ez_blob_desc *d, const char *b, size_t size) {
+void *ez_blob_decode(const ez_blob_desc *d, const ez_blob *blob) {
void *p = calloc(d->bd_offset, 1);
- if (ez_blob_decode_raw(d, b, size, p) == NULL) {
+ if (ez_blob_decode_raw(d, blob, p) == NULL) {
free(p);
return NULL;
}
return p;
}
-void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t size) {
- char *be = b+size;
+void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob) {
+ char *b = blob->data;
+ char *be = b+blob->size;
+
for (;(d->bd_type != EZ_BLOB_END);d++) {
const void *v = (p + d->bd_offset);
assert(b+psize <= be);
memcpy(b, v, psize);
b += psize;
+ } else if (d->bd_type == EZ_BLOB_LIST) {
+ ez_list *l = (void *)v;
+ uint32_t ss = ez_list_size(l);
+
+ memcpy(b, &ss, 4);
+ b += 4;
+ for (ez_node *w = ez_list_head(l), *n = ez_node_succ(w);
+ n;
+ w = n, n=ez_node_succ(n)) {
+ ez_blob sub;
+
+ ss = ez_blob_size(d->bd_table, w);
+
+ assert(b+4+ss <= be);
+ memcpy(b, &ss, 4);
+ sub.data = b+4;
+ sub.size = ss;
+ ez_blob_encode_raw(d->bd_table, w, &sub);
+ b += 4 + ss;
+ }
} else if (d->bd_type <= EZ_BLOB_LAST) {
uint32_t ss;
+ struct ez_blob sub;
switch (d->bd_type) {
case EZ_BLOB_STRING: {
b += 4;
}
break; }
- case EZ_BLOB_ARRAY: {
- const struct ez_blob_array *a = v;
+ case EZ_BLOB_BLOB: {
+ const struct ez_blob *a = v;
ss = a->size;
assert(b+4+ss <= be);
assert(b+4+ss <= be);
memcpy(b, &ss, 4);
- ez_blob_encode_raw(d->bd_table, v, b+4, ss);
+ sub.data = b+4;
+ sub.size = ss;
+ ez_blob_encode_raw(d->bd_table, v, &sub);
b += 4 + ss;
break;
case EZ_BLOB_POINTER: {
ss = ez_blob_size(d->bd_table, s);
assert(b+4+ss <= be);
memcpy(b, &ss, 4);
- ez_blob_encode_raw(d->bd_table, s, b+4, ss);
+ sub.data = b+4;
+ sub.size = ss;
+ ez_blob_encode_raw(d->bd_table, s, &sub);
b += 4 + ss;
} else {
ss = ~0;
assert(b == be);
}
-/**
- * Encode blob to new buffer.
- *
- * @param p object to serialise.
- * @param sizep size of encoded blob return pointer.
- * @return a newly allocated blob of the correct size.
- */
-char *ez_blob_encode(const ez_blob_desc *d, const void *p, size_t *sizep) {
- size_t size = ez_blob_size(d, p);
- char *b = malloc(size);
-
- ez_blob_encode_raw(d, p, b, size);
-
- *sizep = size;
- return b;
+int ez_blob_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) {
+ blob->size = ez_blob_size(d, p);
+ blob->data = malloc(blob->size);
+
+ if (blob->data) {
+ ez_blob_encode_raw(d, p, blob);
+ return 0;
+ }
+
+ return -1;
}
EZ_BLOB_FLOAT32,
EZ_BLOB_FLOAT64,
EZ_BLOB_STRING, // normall c strring
- EZ_BLOB_ARRAY, // embedded ez_blob_array for arbitrary bytes.
+ EZ_BLOB_BLOB, // embedded ez_blob for arbitrary bytes.
EZ_BLOB_STRUCT, // bd_table points to another descriptor list, object embedded
EZ_BLOB_POINTER, // bd_table points to another descriptor list, object pointer
- EZ_BLOB_LAST = EZ_BLOB_POINTER,
+ EZ_BLOB_LIST, // ez_list nodes.
+ EZ_BLOB_LAST = EZ_BLOB_LIST,
EZ_BLOB_PK = 16, // MUST always be first element in descriptor and occur only once
EZ_BLOB_END
} ez_blob_desc_type;
-typedef struct ez_blob_array {
- unsigned int size;
+/**
+ * This is compatible with lmdb MDB_val
+ */
+typedef struct ez_blob {
+ size_t size;
void *data;
-} ez_blob_array;
+} ez_blob;
#define EZ_BLOB_START(s) { EZ_BLOB_PK, 0, sizeof(s) }
#define EZ_BLOB_INT8(s, id, f) { EZ_BLOB_INT8, id, offsetof(s, f) }
#define EZ_BLOB_FLOAT32(s, id, f) { EZ_BLOB_FLOAT32, id, offsetof(s, f) }
#define EZ_BLOB_FLOAT64(s, id, f) { EZ_BLOB_FLOAT64, id, offsetof(s, f) }
#define EZ_BLOB_STRING(s, id, f) { EZ_BLOB_STRING, id, offsetof(s, f) }
-#define EZ_BLOB_ARRAY(s, id, f) { EZ_BLOB_ARRAY, id, offsetof(s, f) }
+#define EZ_BLOB_BLOB(s, id, f) { EZ_BLOB_BLOB, id, offsetof(s, f) }
#define EZ_BLOB_STRUCT(s, id, f, other) { EZ_BLOB_STRUCT, id, offsetof(s, f), .u.table = other }
#define EZ_BLOB_POINTER(s, id, f, other) { EZ_BLOB_POINTER, id, offsetof(s, f), .u.table = other }
+#define EZ_BLOB_LIST(s, id, f, other) { EZ_BLOB_LIST, id, offsetof(s, f), .u.table = other }
#define EZ_BLOB_END(s) { EZ_BLOB_END }
/**
* @param d descriptor table matching the struct.
* @param p structure.
* @param sizep return pointer for blob size, must not be null.
- * @return the blob, it must be freed with free() when complete.
+ * @param blob return, the data pointer must be freed with free() when complete.
+ * @return non-zero on failure.
*/
-char *ez_blob_encode(const ez_blob_desc *d, const void *p, size_t *sizep);
+int ez_blob_encode(const ez_blob_desc *d, const void *p, ez_blob *blob);
/**
* Marshal a struct to a blob directly
*
* @param d descriptor table matching the struct.
* @param p structure.
- * @param b target memory, must be as large as size
- * @param size size of target memory, must be exactly ez_blob_size().
- * @return the blob, it must be freed with free() when complete.
+ * @param blob available target memory, size must be exactly ez_blob_size().
*/
-void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t size);
+void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob);
/**
* Unmarshall a blob into a struct.
* @param size blob size.
* @return the decoded struct. May be freed using ez_blob_free().
*/
-void *ez_blob_decode(const ez_blob_desc *d, const char *blob, size_t size);
+void *ez_blob_decode(const ez_blob_desc *d, const ez_blob *blob);
/**
* Decode to pre-allocated buffer.
* @param size blob size
* @return p is returned on success. On failure NULL is returned and any internal pointers are freed.
*/
-void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void *p);
+void *ez_blob_decode_raw(const ez_blob_desc *d, const ez_blob *blob, void *p);
/**
* Free struct unmarshalled by ez_blob_decode().
*/
void ez_blob_free_raw(const ez_blob_desc *d, void *p);
+/**
+ * Debug function to dump blob contents.
+ */
+void ez_blob_print(const ez_blob_desc *d, const void *a, int depth);
+
#endif
#include <assert.h>
#include "ez-blob.h"
+#include "ez-list.h"
struct simple {
char b;
struct arrays {
uint32_t count;
char *string;
- ez_blob_array array;
+ ez_blob array;
};
static const ez_blob_desc array_DESC[] = {
EZ_BLOB_START(struct arrays),
EZ_BLOB_STRING(struct arrays, 1, string),
- EZ_BLOB_ARRAY(struct arrays, 2, array),
+ EZ_BLOB_BLOB(struct arrays, 2, array),
EZ_BLOB_END(struct arrays)
};
EZ_BLOB_END(struct tree)
};
+struct listnode {
+ ez_node ln;
+ char *name;
+ ez_blob value;
+};
+
+static const ez_blob_desc listnode_DESC[] = {
+ EZ_BLOB_START(struct listnode),
+ EZ_BLOB_STRING(struct listnode, 1, name),
+ EZ_BLOB_BLOB(struct listnode, 2, value),
+ EZ_BLOB_END(struct listnode)
+};
+
+struct list {
+ char *name;
+ ez_list list;
+};
+
+static const ez_blob_desc list_DESC[] = {
+ EZ_BLOB_START(struct list),
+ EZ_BLOB_STRING(struct list, 1, name),
+ EZ_BLOB_LIST(struct list, 2, list, listnode_DESC),
+ EZ_BLOB_END(struct list)
+};
+
static void dumphex(const char *data, size_t size, const char *prefix) {
for (int i=0;i<size;i+=16) {
fputs(prefix, stdout);
}
}
-static int blob_print(const ez_blob_desc *d, const void *a, int depth) {
- int e = 1;
- char x[depth+1];
-
- memset(x, ' ', depth);
- x[depth] = 0;
-
- for (;e && (d->bd_type != EZ_BLOB_END);d++) {
- const void *u = a + d->bd_offset;
-
- printf("%s [type=$%02x, offset=$%04x]", x, d->bd_type, d->bd_offset);
-
- switch (d->bd_type) {
- case EZ_BLOB_INT8:
- printf(" .%d = %d\n", d->bd_id, *(uint8_t *)u);
- break;
- case EZ_BLOB_INT16:
- printf(" .%d = %d\n", d->bd_id, *(uint16_t *)u);
- break;
- case EZ_BLOB_INT32:
- printf(" .%d = %d\n", d->bd_id, *(uint32_t *)u);
- break;
- case EZ_BLOB_INT64:
- printf(" .%d = %ld\n", d->bd_id, *(uint64_t *)u);
- break;
- case EZ_BLOB_FLOAT32:
- printf(" .%d = %f\n", d->bd_id, *(float *)u);
- break;
- case EZ_BLOB_FLOAT64:
- printf(" .%d = %f\n", d->bd_id, *(double *)u);
- break;
- case EZ_BLOB_STRING:
- printf(" .%d %p = `%s'\n", d->bd_id, *((char **)u), *((char **)u));
- break;
- case EZ_BLOB_ARRAY: {
- const struct ez_blob_array *ua = u;
-
- printf(" .%d %p [%d] = {\n", d->bd_id, ua->data, ua->size);
- dumphex(ua->data, ua->size, x);
- printf("%s }\n", x);
- break; }
- case EZ_BLOB_POINTER: {
- void *up = *((void **)u);
- if (up) {
- printf(" .%d %p = {\n", d->bd_id, up);
- blob_print(d->bd_table, up, depth+4);
- printf("%s }\n", x);
- } else {
- printf(" .%d %p\n", d->bd_id, up);
- }
- break;
- }
- default:
- printf("\n");
- }
- }
-
- return e;
+static void blob_print(const ez_blob_desc *d, const void *a, int depth) {
+ ez_blob_print(d, a, depth);
}
static int blob_equals(const ez_blob_desc *d, const void *a, const void *b) {
case EZ_BLOB_STRING:
e &= strcmp((*(char **)u), (*(char **)v)) == 0;
break;
- case EZ_BLOB_ARRAY: {
- const struct ez_blob_array *ua = u;
- const struct ez_blob_array *va = v;
+ case EZ_BLOB_BLOB: {
+ const struct ez_blob *ua = u;
+ const struct ez_blob *va = v;
e &= ua->size == va->size
&& memcmp(ua->data, va->data, ua->size) == 0;
break; }
+ case EZ_BLOB_LIST: {
+ ez_list *ul = (void *)u;
+ ez_list *vl = (void *)v;
+
+ e &= ez_list_size(ul) == ez_list_size(vl);
+ for (ez_node *uw = ez_list_head(ul), *un = ez_node_succ(uw),
+ *vw = ez_list_head(vl), *vn = ez_node_succ(uw);
+ un && vn;
+ uw = un, un = ez_node_succ(un),
+ vw = vn, vn = ez_node_succ(vn)) {
+ e &= blob_equals(d->bd_table, uw, vw);
+ }
+
+ break; }
}
}
}
static void test_basics(const ez_blob_desc *d, void *src) {
- void *blob, *blob2;
+ ez_blob blob, blob2;
void *t;
- size_t size, size2;
size_t dsize = d->bd_offset;
char tmp[dsize + 64] __attribute__ ((aligned(64)));
-
+
printf("source\n");
blob_print(d, src, 4);
- blob = ez_blob_encode(d, src, &size);
+ ez_blob_encode(d, src, &blob);
+ dumphex(blob.data, blob.size, "serial: ");
// check decode equals
- t = ez_blob_decode(d, blob, size);
+ t = ez_blob_decode(d, &blob);
assert(t != NULL);
assert(blob_equals(d, t, src));
- dumphex(blob, size, "serial: ");
-
printf("decoded\n");
blob_print(d, t, 4);
// check encode-decoded equals source
- blob2 = ez_blob_encode(d, t, &size2);
- assert(size == size2);
- assert(memcmp(blob, blob2, size) == 0);
- free(blob2);
+ ez_blob_encode(d, t, &blob2);
+ assert(blob.size == blob2.size);
+ assert(memcmp(blob.data, blob2.data, blob2.size) == 0);
+ free(blob2.data);
ez_blob_free(d, t);
// check raw decode stays within bounds of struct
memset(tmp, 0xbe, sizeof(tmp));
- t = ez_blob_decode_raw(d, blob, size, &tmp[32]);
+ t = ez_blob_decode_raw(d, &blob, &tmp[32]);
//assert(t != NULL);
//assert(blob_equals(d, tmp+32, src));
dumphex(tmp, sizeof(tmp), "object: ");
assert((tmp[i + 32 + dsize] & 0xff) == 0xbe);
}
ez_blob_free_raw(d, tmp+32);
- free(blob);
+ free(blob.data);
}
static void test_simple(void) {
test_basics(d, &src);
}
-static void test_tree(void) {
+static void test_rawtree(void) {
struct tree src[6] = {
{ &src[1], &src[3], "root" },
{ NULL, &src[2], "left" },
test_basics(d, &src);
}
+static void test_list(void) {
+ struct list list = {
+ .name = "a list",
+ .list = EZ_INIT_LIST(list.list)
+ };
+ struct listnode nodes[3] = {
+ { .name = "node 0", .value.size = 5, .value.data = "data0" },
+ { .name = "node 1", .value.size = 5, .value.data = "data1" },
+ { .name = "node 2", .value.size = 5, .value.data = "data2" },
+ };
+
+ list.name = "a list";
+ // ez_list_init(&list.
+ for (int i=0;i<3;i++)
+ ez_list_addtail(&list.list, &nodes[i]);
+
+ const ez_blob_desc *d = list_DESC;
+
+ test_basics(d, &list);
+}
+
int main(int argc, char **argv) {
test_simple();
test_arrays();
- test_tree();
+ test_rawtree();
+ test_list();
}