Index: common/lib/libprop/Makefile.inc =================================================================== RCS file: /cvsroot/src/common/lib/libprop/Makefile.inc,v retrieving revision 1.6 diff -d -p -u -u -r1.6 Makefile.inc --- common/lib/libprop/Makefile.inc 16 Aug 2007 21:44:06 -0000 1.6 +++ common/lib/libprop/Makefile.inc 6 Oct 2007 20:14:28 -0000 @@ -2,7 +2,7 @@ .PATH: ${.PARSEDIR} -SRCS+= prop_array.c prop_bool.c prop_data.c prop_dictionary.c \ +SRCS+= prop_array.c prop_bool.c prop_bplist.c prop_data.c prop_dictionary.c \ prop_dictionary_util.c prop_ingest.c prop_kern.c prop_number.c \ prop_object.c prop_stack.c prop_string.c Index: common/lib/libprop/prop_array.3 =================================================================== RCS file: /cvsroot/src/common/lib/libprop/prop_array.3,v retrieving revision 1.5 diff -d -p -u -u -r1.5 prop_array.3 --- common/lib/libprop/prop_array.3 16 Aug 2007 16:30:59 -0000 1.5 +++ common/lib/libprop/prop_array.3 6 Oct 2007 20:14:28 -0000 @@ -57,6 +57,9 @@ .Nm prop_array_internalize , .Nm prop_array_externalize_to_file , .Nm prop_array_internalize_from_file , +.Nm prop_array_binary_externalize, +.Nm prop_array_binary_internalize, +.Nm prop_array_binary_externalize_to_file, .Nm prop_array_equals .Nd array property collection object .Sh LIBRARY @@ -109,6 +112,15 @@ .Fn prop_array_internalize_from_file "const char *path" .\" .Ft bool +.Fn prop_array_binary_externalize "prop_array_t array" "uint8_t **bufp" \ + "size_t *lenp" +.Ft prop_array_t +.Fn prop_array_binary_internalize "uint8_t *buf" "size_t len" +.Ft bool +.Fn prop_array_binary_externalize_to_file "prop_array_t array" \ + "const char *path" +.\" +.Ft bool .Fn prop_array_equals "prop_array_t array1" "prop_array_t array2" .Sh DESCRIPTION The @@ -241,8 +253,47 @@ and is written atomically. Returns .Dv false if externalizing or writing the array fails for any reason. +.It Fn prop_array_binary_externalize "prop_array_t array" "uint8_t **bufp" \ + "size_t *lenp" +Externalizes an array in binary format. On success +.Dv true +is returned, pointer to buffer containing binary representation is filled to +.Fa bufp +and its size is filled to +.Fa lenp +arguments. The caller is responsible for freeing returned buffer. On failure +.Dv false +is returned and arguments are not modified. +.Pp +In user space, the buffer is allocated using +.Xr malloc 3 . +In the kernel, the buffer is allocated using +.Xr malloc 9 +using the malloc type +.Dv M_TEMP . +.It Fn prop_array_binary_internalize "uint8_t *buf" "size_t len" +Parse the binary representation of a property list in the buffer +.Fa buf +of size +.Fa len +and return the corresponding array. If parsing fails for any reason, +.Dv NULL +is returned. +.It Fn prop_array_binary_externalize_to_file "prop_array_t array" \ + "const char *path" +Externalizes an array in binary format and writes it to the file +specified by +.Fa path . +The file is saved with the mode +.Dv 0666 +as modified by the process's file creation mask +.Pq see Xr umask 3 +and is written atomically. +Returns +.Dv false +if externalizing or writing the array fails for any reason. .It Fn prop_array_internalize_from_file "const char *path" -Reads the XML property list contained in the file specified by +Reads the XML or binary property list contained in the file specified by .Fa path , internalizes it, and returns the corresponding array. .El @@ -259,3 +310,6 @@ The .Nm proplib property container object library first appeared in .Nx 4.0 . +.Pp +Support for binary format first appeared in +.Nx 5.0 . Index: common/lib/libprop/prop_array.c =================================================================== RCS file: /cvsroot/src/common/lib/libprop/prop_array.c,v retrieving revision 1.11 diff -d -p -u -u -r1.11 prop_array.c --- common/lib/libprop/prop_array.c 30 Aug 2007 12:23:54 -0000 1.11 +++ common/lib/libprop/prop_array.c 6 Oct 2007 20:14:28 -0000 @@ -91,6 +91,24 @@ struct _prop_array_iterator { #define EXPAND_STEP 16 +void +_prop_array_rdlock(prop_object_t po) +{ + prop_array_t pa = po; + + _PROP_ASSERT(prop_object_is_array(pa)); + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); +} + +void +_prop_array_unlock(prop_object_t po) +{ + prop_array_t pa = po; + + _PROP_ASSERT(prop_object_is_array(pa)); + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); +} + static int _prop_array_free(prop_stack_t stack, prop_object_t *obj) { @@ -837,7 +855,7 @@ prop_array_internalize(const char *xml) #if !defined(_KERNEL) && !defined(_STANDALONE) /* * prop_array_externalize_to_file -- - * Externalize an array to the specified file. + * Externalize an array to the specified file in XML format. */ bool prop_array_externalize_to_file(prop_array_t array, const char *fname) @@ -860,6 +878,31 @@ prop_array_externalize_to_file(prop_arra } /* + * prop_array_binary_externalize_to_file -- + * Externalize an array to the specified file in bplist format. + */ +bool +prop_array_binary_externalize_to_file(prop_array_t array, const char *fname) +{ + uint8_t *buf; + size_t len; + bool rv; + int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ + + if (! prop_array_binary_externalize(array, &buf, &len)) + return (false); + + rv = _prop_object_externalize_write_file(fname, (char *)buf, len); + if (rv == false) + save_errno = errno; + _PROP_FREE(buf, M_TEMP); + if (rv == false) + errno = save_errno; + + return (rv); +} + +/* * prop_array_internalize_from_file -- * Internalize an array from a file. */ @@ -872,7 +915,10 @@ prop_array_internalize_from_file(const c mf = _prop_object_internalize_map_file(fname); if (mf == NULL) return (NULL); - array = prop_array_internalize(mf->poimf_xml); + array = prop_array_internalize((char *)mf->poimf_data); + if (array == NULL) + array = prop_array_binary_internalize(mf->poimf_data, + mf->poimf_datasize); _prop_object_internalize_unmap_file(mf); return (array); Index: common/lib/libprop/prop_bplist.c =================================================================== RCS file: common/lib/libprop/prop_bplist.c diff -N common/lib/libprop/prop_bplist.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ common/lib/libprop/prop_bplist.c 6 Oct 2007 20:14:30 -0000 @@ -0,0 +1,1661 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2007 Jachym Holecek . + * 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. + * + * 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 HOLDERS 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. + */ + +/* + * XXXfreza TODO: + * o Document public APIs & short note on bplist format + * o Don't allocate internalizer context on the stack + * o Tidy _prop_bp_decode_object() a bit, it's too long + * o Look at using joerg's stack management + * + * XXXfreza CAVEATS: + * o Mutating internalized objects is risky as objects may be arbitrarily + * uniquified, depending on the encoder. + */ + +#include +#include "prop_object_impl.h" + +#if defined(_KERNEL) || defined(_STANDALONE) +#include +#else +#include +#endif + +/* Round 'x' up to the nearest multiple of 'm'. */ +#define _PROP_ROUNDUP(x, m) ((((x) + ((m) - 1))/(m)) * (m)) + +/* General size/count increments. */ +#define BPLIST_INCREMENT_SMALL 16 /* > BPLIST_HEADER_LEN */ +#define BPLIST_INCREMENT_LARGE 128 + +/* Maximum length of encoded integer (marker byte + 64bit value). */ +#define BPLIST_NUMBER_MAXLEN 9 + +/* + * (1) Header structure. Identifies file format and version. + */ + +/* Magic marker & lenght (no terminating NUL). */ +#define BPLIST_MAGIC "bplist" +#define BPLIST_MAGIC_LEN 6 + +/* Version is given in two bytes interpreted as ASCII characters. */ +#define BPLIST_VERSION_LEN 2 +#define BPLIST_VERSION_MAJOR_OFFS 6 +#define BPLIST_VERSION_MINOR_OFFS 7 + +/* Total header length. */ +#define BPLIST_HEADER_LEN (BPLIST_MAGIC_LEN + BPLIST_VERSION_LEN) + +/* + * (2) Object table. Flat array describing all objects in the plist. + * Compound objects (array, dict) refer to their "children" via + * indexes to the offset table. + */ + +/* Object type encoded in higher four bits. */ +#define BPLIST_TYPE_MULTI 0x00 +#define BPLIST_TYPE_UINT 0x10 /* signed int in Apple */ +#define BPLIST_TYPE_REAL 0x20 /* unsupported */ +#define BPLIST_TYPE_DATE 0x30 /* unsupported */ +#define BPLIST_TYPE_DATA 0x40 +#define BPLIST_TYPE_ASCII 0x50 /* ASCII string */ +#define BPLIST_TYPE_UNICODE 0x60 /* unsupported */ +#define BPLIST_TYPE_SINT 0x70 /* reserved in Apple */ +#define BPLIST_TYPE_UID 0x80 /* unsupported */ +#define BPLIST_TYPE_ARRAY 0xa0 +#define BPLIST_TYPE_DICT 0xd0 + +/* Lower four bits for BPLIST_TYPE_MULTI. */ +#define BPLIST_MULTI_NULL 0x00 +#define BPLIST_MULTI_FALSE 0x08 +#define BPLIST_MULTI_TRUE 0x09 +#define BPLIST_MULTI_FILL 0x0f + +/* Where lower bits mean "count", this indicates (u_)integer count follows. */ +#define BPLIST_COUNT_MANY 0x0f + +/* + * (3) Offset table. Array of unsigned integers (width defined by the + * trailer) resolving object references to file offsets at which + * objects are stored. + */ + +/* + * (4) Trailer structure. Defines byte-width of offset table entries, its + * offset in the file, count of offset table entries (ie. object count) + * and index of the toplevel array/dict. + */ + +#define BPLIST_TRAILER_LEN (3*8 + 2) +#define BPLIST_TRAILER_OFFWIDTH_OFFS 0 +#define BPLIST_TRAILER_REFWIDTH_OFFS 1 +#define BPLIST_TRAILER_OBJCNT_OFFS 2 +#define BPLIST_TRAILER_OBJTOP_OFFS 10 +#define BPLIST_TRAILER_OFFTAB_OFFS 18 + +/* + * Internalizer data types. + */ + +typedef struct _prop_bp_intern_frame *_prop_bp_intern_frame_t; +typedef struct _prop_bp_intern *_prop_bp_intern_t; + +/* Internalizer frame, common for arrays/dicts. */ +struct _prop_bp_intern_frame { + prop_object_t if_container; + const char *if_keysym; /* Dictionary only */ + const uint8_t *if_buffer; + u_int if_count; /* Object count */ + u_int if_index; /* Current object */ +}; + +struct _prop_bp_intern { + /* Trailer members. */ + u_int bi_offtab_width; /* size 1 offset 0 */ + u_int bi_objref_width; /* size 1 offset 1 */ + u_int bi_object_count; /* size 8 offset 2 */ + u_int bi_object_first; /* size 8 offset 10 */ + u_int bi_offtab_offset; /* size 8 offset 18 */ + + /* Objects internalized so far, by index. */ + prop_object_t *bi_objects; + + /* Utility members. */ + const uint8_t *bi_offtab; + const uint8_t *bi_buffer; + size_t bi_length; + + /* Manipulated exclusively by _prop_bp_intern_stack_*(). */ + u_int bi_stack_depth; + u_int bi_stack_top; + _prop_bp_intern_frame_t bi_stack; /* array */ +}; + +/* + * Externalizer data types. + */ + +typedef struct _prop_bp_extern *_prop_bp_extern_t; +typedef struct _prop_bp_object *_prop_bp_object_t; +typedef struct _prop_bp_table *_prop_bp_table_t; + +typedef bool (*_prop_bp_table_lookup_t)(_prop_bp_table_t, prop_object_t, + u_int *); + +/* Entry in uniqified object table. */ +struct _prop_bp_object { + prop_object_t bo_object; + u_int bo_index; + u_int bo_offset; +}; + +/* Uniquified object table. */ +struct _prop_bp_table { + _prop_bp_object_t bt_objects; /* array */ + u_int bt_depth; + u_int bt_next; +}; + +/* Uniquified object tables & their count. */ +#define BPLIST_TABLE_COMPOUND 0 /* dict & array */ +#define BPLIST_TABLE_STRING 1 /* string & keysym */ +#define BPLIST_TABLE_NUMBER 2 /* signed & unsigned int */ +#define BPLIST_TABLE_SINGLE 3 /* bool & data */ +#define BPLIST_TABLE_COUNT 4 /* number of tables */ + +struct _prop_bp_extern { + /* Uniquified object tables. */ + struct _prop_bp_table be_tables[BPLIST_TABLE_COUNT]; + + /* Externalized plist. */ + uint8_t *be_extern_buffer; + size_t be_extern_size; + size_t be_extern_next; + + /* Unique object count (and next object index). */ + u_int be_index_next; + + /* Trailer values. */ + u_int be_objref_width; + u_int be_offtab_width; + u_int be_offtab_start; +}; + +/* + * Internalizer stack management. + */ + +static bool +_prop_bp_intern_stack_init(_prop_bp_intern_t ic) +{ + u_int count = BPLIST_INCREMENT_SMALL; + u_int size = (count * sizeof(struct _prop_bp_intern_frame)); + + ic->bi_stack = _PROP_MALLOC(size, M_TEMP); + if (ic->bi_stack == NULL) + return (false); + + ic->bi_stack_depth = count; + ic->bi_stack_top = 0; + + return (true); +} + +static void +_prop_bp_intern_stack_free(_prop_bp_intern_t ic) +{ + /* We're done, WIP objects themselves freed via bi_objects[]. */ + _PROP_FREE(ic->bi_stack, M_TEMP); +} + +static _prop_bp_intern_frame_t +_prop_bp_intern_stack_peek(_prop_bp_intern_t ic) +{ + return (&ic->bi_stack[ic->bi_stack_top]); +} + +static _prop_bp_intern_frame_t +_prop_bp_intern_stack_zerohead(_prop_bp_intern_t ic) +{ + _prop_bp_intern_frame_t bif = _prop_bp_intern_stack_peek(ic); + + return (memset(bif, 0, sizeof(struct _prop_bp_intern_frame))); +} + +static bool +_prop_bp_intern_stack_pop(_prop_bp_intern_t ic) +{ + if (ic->bi_stack_top == 1) + return (true); + + ic->bi_stack_top -= 1; + return (false); +} + +static _prop_bp_intern_frame_t +_prop_bp_intern_stack_push(_prop_bp_intern_t ic) +{ + size_t size; + u_int count; + void *p; + + /* XXXfreza this leaves stack frame 0 wasted. */ + ic->bi_stack_top += 1; + if (ic->bi_stack_top < ic->bi_stack_depth) + return (_prop_bp_intern_stack_zerohead(ic)); + + count = (ic->bi_stack_depth + BPLIST_INCREMENT_SMALL); + size = (count * sizeof(struct _prop_bp_intern_frame)); + p = _PROP_REALLOC(ic->bi_stack, size, M_TEMP); + if (p == NULL) + return (NULL); + + ic->bi_stack_depth = count; + ic->bi_stack = p; + + return (_prop_bp_intern_stack_zerohead(ic)); +} + +/* + * Internalizer utility decoders. + */ + +static const uint8_t * +_prop_bp_object_pointer(_prop_bp_intern_t ic, u_int n) +{ + const uint8_t *ofp = (ic->bi_offtab + n*ic->bi_offtab_width); + u_int offset = 0; + int i; + + for (i = 0; i < ic->bi_offtab_width; i++) + offset = (offset << 8) | ofp[i]; + + if (offset >= (ic->bi_length - BPLIST_TRAILER_LEN)) + return (NULL); + + return (ic->bi_buffer + offset); +} + +static const uint8_t * +_prop_bp_read_int(_prop_bp_intern_t ic, const uint8_t *obj, u_int *valp) +{ + uint8_t hi = (*obj & 0xf0); + uint8_t lo = (*obj & 0x0f); + u_int nbytes; + u_int i; + + /* Check type and calculate number size in bytes. */ + if (hi != BPLIST_TYPE_UINT) + return (NULL); + + nbytes = (1 << lo); + if (nbytes > sizeof(u_int)) + return (NULL); + + /* Careful not to overflow object table. */ + if ((obj + nbytes) > ic->bi_offtab) + return (NULL); + obj += 1; + + /* Shift in big-endian value. */ + for (*valp = 0, i = 0; i < nbytes; i++) + *valp = (*valp << 8) | *obj++; + + return (obj); +} + +static const uint8_t * +_prop_bp_read_index(_prop_bp_intern_t ic, const uint8_t *obj, u_int *valp) +{ + u_int i; + + /* Careful not to overflow object table. */ + if ((obj + ic->bi_objref_width) > ic->bi_offtab) + return (NULL); + + /* Shift in big-endian value. */ + for (*valp = 0, i = 0; i < ic->bi_objref_width; i++) + *valp = (*valp << 8) | *obj++; + + /* Careful not to overflow advised object count. */ + if (*valp >= ic->bi_object_count) + return (NULL); + + return (obj); +} + +/* + * Internalizer object decoder routines. + */ + +/*ARGSUSED*/ +static prop_number_t +_prop_bp_decode_int(_prop_bp_intern_t ic, const uint8_t *obj, bool isuint, + uint8_t lo) +{ + uint64_t val = 0; + u_int nbytes = (1 << lo); + u_int i; + + if (nbytes > 8) + return (NULL); + + for (i = 0; i < nbytes; i++) + val = (val << 8) | *obj++; + + if (isuint) + return (prop_number_create_unsigned_integer(val)); + else + return (prop_number_create_integer((int64_t)val)); +} + +static prop_array_t +_prop_bp_decode_array(_prop_bp_intern_t ic, const uint8_t *obj, uint8_t lo) +{ + _prop_bp_intern_frame_t bif; + prop_array_t pa; + u_int nelts; + + /* Read array size (number of contained objects). */ + if (lo == BPLIST_COUNT_MANY) { + obj = _prop_bp_read_int(ic, obj, &nelts); + if (obj == NULL) + return (NULL); + } else + nelts = lo; + + /* Create the array. */ + pa = prop_array_create_with_capacity(nelts); + if (pa == NULL) + return (NULL); + + /* Initialize stack frame. */ + bif = _prop_bp_intern_stack_push(ic); + if (bif == NULL) { + prop_object_release(pa); + return (NULL); + } + bif->if_container = pa; + bif->if_buffer = obj; + bif->if_count = nelts; + bif->if_index = 0; + + return (pa); +} + +static prop_dictionary_t +_prop_bp_decode_dict(_prop_bp_intern_t ic, const uint8_t *obj, uint8_t lo) +{ + _prop_bp_intern_frame_t bif; + prop_dictionary_t pd; + u_int nelts; + + /* Read dictionary size (number of pairs). */ + if (lo == BPLIST_COUNT_MANY) { + obj = _prop_bp_read_int(ic, obj, &nelts); + if (obj == NULL) + return (NULL); + } else + nelts = lo; + + /* Create the dictionary. */ + pd = prop_dictionary_create_with_capacity(nelts); + if (pd == NULL) + return (NULL); + + /* Initialize stack frame. */ + bif = _prop_bp_intern_stack_push(ic); + if (bif == NULL) { + prop_object_release(pd); + return (NULL); + } + bif->if_container = pd; + bif->if_keysym = NULL; + bif->if_buffer = obj; + bif->if_count = (2 * nelts); + bif->if_index = 0; + + return (pd); +} + +static prop_string_t +_prop_bp_decode_ascii(_prop_bp_intern_t ic, const uint8_t *obj, uint8_t lo) +{ + prop_string_t ps; + u_int size; + char *str; + + /* Read string length. */ + if (lo == BPLIST_COUNT_MANY) { + obj = _prop_bp_read_int(ic, obj, &size); + if (obj == NULL) + return (NULL); + } else + size = lo; + + /* Are we in bounds of the object table? */ + if ((obj + size) > ic->bi_offtab) + return (NULL); + + str = _PROP_MALLOC(size + 1, M_TEMP); + if (str == NULL) + return (NULL); + + str[size] = '\0'; + memcpy(str, obj, size); + + /* XXXfreza this is expensive but we can't donate a buffer to ps */ + ps = prop_string_create_cstring(str); + _PROP_FREE(str, M_TEMP); + + return (ps); +} + +static prop_data_t +_prop_bp_decode_data(_prop_bp_intern_t ic, const uint8_t *obj, uint8_t lo) +{ + u_int size; + + /* Read data length. */ + if (lo == BPLIST_COUNT_MANY) { + obj = _prop_bp_read_int(ic, obj, &size); + if (obj == NULL) + return (NULL); + } else + size = lo; + + /* Are we in bounds of the object table? */ + if ((obj + size) > ic->bi_offtab) + return (NULL); + + return (prop_data_create_data(obj, size)); +} + +static bool +_prop_bp_decode_object(_prop_bp_intern_t ic, u_int idx) +{ + _prop_bp_intern_frame_t bif; + prop_object_t po; + const uint8_t *obj; + uint8_t hi; + uint8_t lo; + + if (! _prop_bp_intern_stack_init(ic)) + return (false); + + nextobj: + /* See if we already have this object, decode otherwise. */ + po = ic->bi_objects[idx]; + if (po != NULL) + goto gotobj; + + /* Get pointer to encoded object and see what we can do. */ + obj = _prop_bp_object_pointer(ic, idx); + if (obj == NULL) + goto fail; + + hi = (*obj & 0xf0); /* Object type tag */ + lo = (*obj & 0x0f); /* Auxiliary data */ + obj += 1; + + switch (hi) { + case BPLIST_TYPE_MULTI: + switch (lo) { + case BPLIST_MULTI_FALSE: + po = prop_bool_create(false); + break; + + case BPLIST_MULTI_TRUE: + po = prop_bool_create(true); + break; + + default: + goto fail; + } + break; + + case BPLIST_TYPE_UINT: + po = _prop_bp_decode_int(ic, obj, true, lo); + break; + + case BPLIST_TYPE_SINT: + po = _prop_bp_decode_int(ic, obj, false, lo); + break; + + case BPLIST_TYPE_ASCII: + po = _prop_bp_decode_ascii(ic, obj, lo); + break; + + case BPLIST_TYPE_DATA: + po = _prop_bp_decode_data(ic, obj, lo); + break; + + case BPLIST_TYPE_ARRAY: + po = _prop_bp_decode_array(ic, obj, lo); + break; + + case BPLIST_TYPE_DICT: + po = _prop_bp_decode_dict(ic, obj, lo); + break; + + default: + goto fail; + } + + /* See if decoding was successful, remember object if so. */ + if (po == NULL) + goto fail; + ic->bi_objects[idx] = po; + + gotobj: + /* Get current compound, if it's a fresh one skip insert step. */ + bif = _prop_bp_intern_stack_peek(ic); + if (bif->if_container == po) + goto newcompound; + + insertobj: + /* Insert object into its container. */ + if (prop_object_type(bif->if_container) == PROP_TYPE_DICTIONARY) { + if (bif->if_keysym == NULL) { + const char *key; + + if (prop_object_type(po) != PROP_TYPE_STRING) + goto fail; + + key = prop_string_cstring_nocopy(po); + bif->if_keysym = key; + } else { + if (! prop_dictionary_set(bif->if_container, + bif->if_keysym, po)) + goto fail; + + bif->if_keysym = NULL; + } + } else + if (prop_object_type(bif->if_container) == PROP_TYPE_ARRAY) { + if (! prop_array_set(bif->if_container, bif->if_index, po)) + goto fail; + } + bif->if_index += 1; + + newcompound: + /* See if we've just finished current compound object. */ + if (bif->if_index == bif->if_count) { + if (_prop_bp_intern_stack_pop(ic)) { + _prop_bp_intern_stack_free(ic); + return (true); + } + + /* Restore previous context and re-run insert path. */ + po = bif->if_container; + bif = _prop_bp_intern_stack_peek(ic); + + goto insertobj; + } + + /* Get index of next object to work on, re-run decoder. */ + obj = _prop_bp_read_index(ic, bif->if_buffer, &idx); + if (obj == NULL) + goto fail; + bif->if_buffer = obj; + + goto nextobj; + + fail: + _prop_bp_intern_stack_free(ic); + return (false); +} + +static prop_object_t +_prop_bp_internalize(const uint8_t *data, size_t len, prop_type_t type) +{ + struct _prop_bp_intern ic; + prop_object_t po; + const uint8_t *trbase; + const uint8_t *obj; + uint64_t big; + uint8_t val; + u_int i; + + /* Is it large enough to make any sense? */ + if (len <= (BPLIST_HEADER_LEN + BPLIST_TRAILER_LEN)) + return (NULL); + + /* Does it match the magic marker? */ + if (memcmp(data, BPLIST_MAGIC, BPLIST_MAGIC_LEN) != 0) + return (NULL); + + /* Can we parse this version? */ + if (data[BPLIST_VERSION_MAJOR_OFFS] != 'Z' || + data[BPLIST_VERSION_MINOR_OFFS] != 'Z') + return (NULL); + + /* Decode the trailer, can't do much without it. */ + trbase = (data + len - BPLIST_TRAILER_LEN); + ic.bi_offtab_width = *(trbase + BPLIST_TRAILER_OFFWIDTH_OFFS); + ic.bi_objref_width = *(trbase + BPLIST_TRAILER_REFWIDTH_OFFS); + + big = be64dec(trbase + BPLIST_TRAILER_OFFTAB_OFFS); + if (big > UINT_MAX) + return (NULL); + ic.bi_offtab_offset = (u_int)big; + + big = be64dec(trbase + BPLIST_TRAILER_OBJCNT_OFFS); + if (big > UINT_MAX) + return (NULL); + ic.bi_object_count = (u_int)big; + + big = be64dec(trbase + BPLIST_TRAILER_OBJTOP_OFFS); + if (big > UINT_MAX) + return (NULL); + ic.bi_object_first = (u_int)big; + +#if 0 + warnx("bi_offtab_width %u", ic.bi_offtab_width); + warnx("bi_objref_width %u", ic.bi_objref_width); + warnx("bi_offtab_offset 0x%x", ic.bi_offtab_offset); + warnx("bi_object_count %u", ic.bi_object_count); + warnx("bi_object_first %u", ic.bi_object_first); +#endif + + /* Sanity check the trailer. */ + if (ic.bi_offtab_width == 0 || ic.bi_offtab_width > sizeof(u_int)) + return (NULL); + + if (ic.bi_objref_width == 0 || ic.bi_objref_width > sizeof(u_int)) + return (NULL); + + if (ic.bi_offtab_offset >= (len - BPLIST_TRAILER_LEN) || + ic.bi_offtab_offset <= BPLIST_HEADER_LEN) + return (NULL); + + if (ic.bi_object_count > (len - ic.bi_offtab_offset - + BPLIST_TRAILER_LEN)/ic.bi_offtab_width) + return (NULL); + + if (ic.bi_object_first >= ic.bi_object_count) + return (NULL); + + /* Setup remaining internalizer context. */ + ic.bi_offtab = (data + ic.bi_offtab_offset); + ic.bi_buffer = data; + ic.bi_length = len; + + /* Check toplevel object type before we waste more time. */ + obj = _prop_bp_object_pointer(&ic, ic.bi_object_first); + if (obj == NULL) + return (NULL); + val = (*obj & 0xf0); + + if ((type == PROP_TYPE_DICTIONARY && val != BPLIST_TYPE_DICT) || + (type == PROP_TYPE_ARRAY && val != BPLIST_TYPE_ARRAY)) + return (NULL); + + /* Initialize object table. */ + ic.bi_objects = _PROP_MALLOC(sizeof(prop_object_t) * + ic.bi_object_count, M_TEMP); + if (ic.bi_objects == NULL) + return (NULL); + memset(ic.bi_objects, 0, sizeof(prop_object_t) * ic.bi_object_count); + + /* Parse toplevel object. */ + if (_prop_bp_decode_object(&ic, ic.bi_object_first)) + po = ic.bi_objects[ic.bi_object_first]; + else + po = NULL; + + /* Release initial object references, except for toplevel one. */ + for (i = 0; i < ic.bi_object_count; i++) + if (ic.bi_objects[i] != NULL && i != ic.bi_object_first) + prop_object_release(ic.bi_objects[i]); + + _PROP_FREE(ic.bi_objects, M_TEMP); + return (po); +} + +/* + * Public internalizer interface. + */ + +prop_dictionary_t +prop_dictionary_binary_internalize(uint8_t *data, size_t len) +{ + return (_prop_bp_internalize(data, len, PROP_TYPE_DICTIONARY)); +} + +prop_array_t +prop_array_binary_internalize(uint8_t *data, size_t len) +{ + return (_prop_bp_internalize(data, len, PROP_TYPE_ARRAY)); +} + +/* + * Externalizer utility routines. + */ + +static uint8_t +_prop_bp_number_logsize(uint64_t val) +{ + /* Calculate log2(number of bytes to represent value). */ + if (val & 0xffffffff00000000ULL) + return (3); + else + if (val & 0x00000000ffff0000ULL) + return (2); + else + if (val & 0x000000000000ff00ULL) + return (1); + + return (0); +} + +static u_int +_prop_bp_number_size(uint64_t val) +{ + if (val <= 0x00000000000000ffULL) + return (1); + else + if (val <= 0x000000000000ffffULL) + return (2); + else + if (val <= 0x0000000000ffffffULL) + return (3); + else + if (val <= 0x00000000ffffffffULL) + return (4); + else + if (val <= 0x000000ffffffffffULL) + return (5); + else + if (val <= 0x0000ffffffffffffULL) + return (6); + else + if (val <= 0x00ffffffffffffffULL) + return (7); + + return (8); +} + +static _prop_bp_extern_t +_prop_bp_extern_init(prop_object_t po) +{ + _prop_bp_extern_t be; + _prop_bp_table_t tab; + size_t size; + u_int i; + + be = _PROP_MALLOC(sizeof(struct _prop_bp_extern), M_TEMP); + if (be == NULL) + return (NULL); + + size = (BPLIST_INCREMENT_SMALL * sizeof(struct _prop_bp_object)); + + /* Initialize object tables. */ + for (i = 0; i < BPLIST_TABLE_COUNT; i++) { + tab = &be->be_tables[i]; + + tab->bt_objects = _PROP_MALLOC(size, M_TEMP); + if (tab->bt_objects == NULL) + goto fail; + + tab->bt_depth = BPLIST_INCREMENT_SMALL; + tab->bt_next = 0; + } + + /* Register toplevel compound (as object 0). */ + tab = &be->be_tables[BPLIST_TABLE_COMPOUND]; + tab->bt_objects[0].bo_object = po; + tab->bt_objects[0].bo_index = 0; + tab->bt_next = 1; + + be->be_index_next = 1; + + /* Initialize externalize buffer. */ + be->be_extern_buffer = _PROP_MALLOC(BPLIST_INCREMENT_LARGE, M_TEMP); + if (be->be_extern_buffer == NULL) + goto fail; + + be->be_extern_size = BPLIST_INCREMENT_LARGE; + be->be_extern_next = 0; + + return (be); + + fail: + /* Note 'i' unsigned. */ + for (; i > 0; i--) { + tab = &be->be_tables[i - 1]; + + _PROP_FREE(tab->bt_objects, M_TEMP); + } + if (be != NULL) + _PROP_FREE(be, M_TEMP); + + return (NULL); +} + +static void +_prop_bp_extern_free(_prop_bp_extern_t be) +{ + u_int i; + + for (i = 0; i < BPLIST_TABLE_COUNT; i++) + _PROP_FREE(be->be_tables[i].bt_objects, M_TEMP); + + /* Externalize buffer managed by caller. */ + _PROP_FREE(be, M_TEMP); +} + +static uint8_t * +_prop_bp_ensure_capacity(_prop_bp_extern_t be, u_int size) +{ + uint8_t *start; + u_int newsize; + void *p; + + retry: + if ((be->be_extern_next + size) < be->be_extern_size) { + start = &be->be_extern_buffer[be->be_extern_next]; + be->be_extern_next += size; + + return (start); + } + + newsize = (be->be_extern_size + size); + newsize = _PROP_ROUNDUP(newsize, BPLIST_INCREMENT_LARGE); + + /* Fail if we've gone insanely large (integer overflow). */ + if (newsize < be->be_extern_size) + return (NULL); + + p = _PROP_REALLOC(be->be_extern_buffer, newsize, M_TEMP); + if (p == NULL) + return (NULL); + + be->be_extern_buffer = p; + be->be_extern_size = newsize; + + /* We'll definitely make it this time. */ + goto retry; +} + +static bool +_prop_bp_table_insert(_prop_bp_table_t tab, prop_object_t po, u_int idx) +{ + _prop_bp_object_t bo; + u_int count; + u_int size; + void *p; + + retry: + if (tab->bt_next < tab->bt_depth) { + bo = &tab->bt_objects[tab->bt_next]; + bo->bo_object = po; + bo->bo_index = idx; + + tab->bt_next += 1; + return (true); + } + + count = (tab->bt_depth + BPLIST_INCREMENT_SMALL); + size = (count * sizeof(struct _prop_bp_object)); + p = _PROP_REALLOC(tab->bt_objects, size, M_TEMP); + if (p == NULL) + return (false); + + tab->bt_objects = p; + tab->bt_depth = count; + + /* We'll definitely make it this time. */ + goto retry; +} + +/* + * Externalizer object utility routines. + */ + +static u_int +_prop_bp_compound_count(prop_object_t po) +{ + if (prop_object_type(po) == PROP_TYPE_DICTIONARY) + return (prop_dictionary_count(po)); + + return (prop_array_count(po)); +} + +static bool +_prop_bp_compound_equals(prop_object_t po1, prop_object_t po2) +{ + if (prop_object_type(po1) != prop_object_type(po2)) + return (false); + + if (_prop_bp_compound_count(po1) == 0 && + _prop_bp_compound_count(po2) == 0) + return (true); + + return (po1 == po2); +} + +static size_t +_prop_bp_string_size(prop_object_t po) +{ + if (prop_object_type(po) == PROP_TYPE_STRING) + return (prop_string_size(po)); + + return (strlen(prop_dictionary_keysym_cstring_nocopy(po))); +} + +static const char * +_prop_bp_string_cstring(prop_object_t po) +{ + if (prop_object_type(po) == PROP_TYPE_STRING) + return (prop_string_cstring_nocopy(po)); + + return (prop_dictionary_keysym_cstring_nocopy(po)); +} + +static bool +_prop_bp_string_equals(prop_object_t po1, prop_object_t po2) +{ + const char *s1 = _prop_bp_string_cstring(po1); + const char *s2 = _prop_bp_string_cstring(po2); + + return (strcmp(s1, s2) == 0); +} + +/* + * Externalizer object table query routines. + */ + +/* XXXfreza all _prop_bp_index_of_${table}() are O(n), optimise! */ + +static bool +_prop_bp_index_of_single(_prop_bp_table_t tab, prop_object_t po, u_int *np) +{ + u_int i; + + /* Don't uniquify data --> rare occurence & unlikely duplicity. */ + if (np == NULL) { + if (prop_object_type(po) != PROP_TYPE_BOOL) + return (false); + } + + /* Booleans are singletons, we treat data as such too. */ + for (i = 0; i < tab->bt_next; i++) + if (tab->bt_objects[i].bo_object == po) { + if (np != NULL) + *np = tab->bt_objects[i].bo_index; + + return (true); + } + + return (false); +} + +static bool +_prop_bp_index_of_number(_prop_bp_table_t tab, prop_object_t po, u_int *np) +{ + u_int i; + + for (i = 0; i < tab->bt_next; i++) + if (prop_number_equals(tab->bt_objects[i].bo_object, po)) { + if (np != NULL) + *np = tab->bt_objects[i].bo_index; + + return (true); + } + + return (false); +} + +static bool +_prop_bp_index_of_string(_prop_bp_table_t tab, prop_object_t po, u_int *np) +{ + u_int i; + + for (i = 0; i < tab->bt_next; i++) + if (_prop_bp_string_equals(tab->bt_objects[i].bo_object, po)) { + if (np != NULL) + *np = tab->bt_objects[i].bo_index; + + return (true); + } + + return (false); +} + +static bool +_prop_bp_index_of_compound(_prop_bp_table_t tab, prop_object_t po, u_int *np) +{ + u_int i; + + /* Only uniquify empty compounds --> comparison too expensive. */ + if (np == NULL) { + if (_prop_bp_compound_count(po) != 0) + return (false); + } + + for (i = 0; i < tab->bt_next; i++) + if (_prop_bp_compound_equals(tab->bt_objects[i].bo_object, + po)) { + if (np != NULL) + *np = tab->bt_objects[i].bo_index; + + return (true); + } + + return (false); +} + +static void +_prop_bp_table_resolve(_prop_bp_extern_t be, prop_type_t type, + _prop_bp_table_lookup_t *lookup, _prop_bp_table_t *table) +{ + switch (type) { + case PROP_TYPE_BOOL: + case PROP_TYPE_DATA: + *lookup = _prop_bp_index_of_single; + *table = &be->be_tables[BPLIST_TABLE_SINGLE]; + break; + + case PROP_TYPE_NUMBER: + *lookup = _prop_bp_index_of_number; + *table = &be->be_tables[BPLIST_TABLE_NUMBER]; + break; + + case PROP_TYPE_DICT_KEYSYM: + case PROP_TYPE_STRING: + *lookup = _prop_bp_index_of_string; + *table = &be->be_tables[BPLIST_TABLE_STRING]; + break; + + case PROP_TYPE_ARRAY: + case PROP_TYPE_DICTIONARY: + *lookup = _prop_bp_index_of_compound; + *table = &be->be_tables[BPLIST_TABLE_COMPOUND]; + break; + + case PROP_TYPE_UNKNOWN: /* gcc */ + break; + } +} + +static u_int +_prop_bp_index_of_object(_prop_bp_extern_t be, prop_object_t po) +{ + _prop_bp_table_lookup_t lookup; + _prop_bp_table_t tab; + u_int idx = UINT_MAX; + + _prop_bp_table_resolve(be, prop_object_type(po), &lookup, &tab); + + /* This is bound to succeed from where it's called. */ + (void)lookup(tab, po, &idx); + + /* But double-check in debug builds. */ + _PROP_ASSERT(idx < UINT_MAX); + + return (idx); +} + +static bool +_prop_bp_uniquify_object(_prop_bp_extern_t be, prop_object_t compound, + prop_object_t po) +{ + _prop_bp_table_lookup_t lookup; + _prop_bp_table_t tab; + prop_type_t type; + + again: + type = prop_object_type(po); + _prop_bp_table_resolve(be, type, &lookup, &tab); + + /* Make sure compound doesn't mutate or die under our hands. */ + if (type == PROP_TYPE_DICTIONARY) + _prop_dictionary_rdlock(po); + else + if (type == PROP_TYPE_ARRAY) + _prop_array_rdlock(po); + + /* If object is already held by appropriate table, we're done. */ + if (lookup(tab, po, NULL)) { + if (prop_object_type(po) == PROP_TYPE_DICT_KEYSYM) + goto keysym; + + return (true); + } + + /* Careful to fail in insane cases. */ + if (be->be_index_next == UINT_MAX) + return (false); + + /* Remember a new unique object. */ + if (! _prop_bp_table_insert(tab, po, be->be_index_next++)) + return (false); + + keysym: + /* If we've handled keysym, go on with object it refers to. */ + if (prop_object_type(po) == PROP_TYPE_DICT_KEYSYM) { + po = prop_dictionary_get_keysym(compound, po); + if (po == NULL) + return (false); + + /* Note that sane dicts mustn't hold keysym elements. */ + goto again; + } + + return (true); +} + +/* + * Externalizer encoder utility routines. + */ + +static uint8_t * +_prop_bp_write_byte(uint8_t *obj, uint8_t val) +{ + *obj = val; + + return (obj + 1); +} + +static uint8_t * +_prop_bp_write_number(uint8_t *obj, u_int val) +{ + uint8_t lo = _prop_bp_number_logsize(val); + int i; + + /* Write object tag. */ + obj = _prop_bp_write_byte(obj, BPLIST_TYPE_UINT | lo); + + /* Write big-endian value. */ + for (i = 8*((1 << lo) - 1); i >= 0; i -= 8) + obj = _prop_bp_write_byte(obj, val >> i); + + return (obj); +} + +static uint8_t * +_prop_bp_write_reference(_prop_bp_extern_t be, uint8_t *obj, u_int val) +{ + int i; + + _PROP_ASSERT(val < be->be_index_next); + + /* Write big-endian value. */ + for (i = 8*(be->be_objref_width - 1); i >= 0; i -= 8) + obj = _prop_bp_write_byte(obj, val >> i); + + return (obj); +} + +static void +_prop_bp_write_offset(_prop_bp_extern_t be, uint8_t *offtab, + _prop_bp_object_t bo) +{ + uint8_t *obj = (offtab + bo->bo_index * be->be_offtab_width); + u_int val = bo->bo_offset; + int i; + + /* Write big-endian value. */ + for (i = 8*(be->be_offtab_width - 1); i >= 0; i -= 8) + obj = _prop_bp_write_byte(obj, val >> i); +} + +static bool +_prop_bp_write_offtab(_prop_bp_extern_t be) +{ + _prop_bp_table_t tab; + uint8_t *offtab; + u_int size = (be->be_offtab_width * be->be_index_next); + u_int i; + u_int j; + + offtab = _prop_bp_ensure_capacity(be, size); + if (offtab == NULL) + return (false); + + for (i = 0; i < BPLIST_TABLE_COUNT; i++) { + tab = &be->be_tables[i]; + + for (j = 0; j < tab->bt_next; j++) + _prop_bp_write_offset(be, offtab, &tab->bt_objects[j]); + } + + return (true); +} + +static uint8_t * +_prop_bp_write_wide(uint8_t *obj, u_int val) +{ + be64enc(obj, val); + + return (obj + 8); +} + +static bool +_prop_bp_write_trailer(_prop_bp_extern_t be) +{ + uint8_t *trailer; + + trailer = _prop_bp_ensure_capacity(be, BPLIST_TRAILER_LEN); + if (trailer == NULL) + return (false); + + /* Write trailer, note we always have toplevel object at index 0. */ + trailer = _prop_bp_write_byte(trailer, be->be_offtab_width); + trailer = _prop_bp_write_byte(trailer, be->be_objref_width); + trailer = _prop_bp_write_wide(trailer, be->be_index_next); + trailer = _prop_bp_write_wide(trailer, 0); + trailer = _prop_bp_write_wide(trailer, be->be_offtab_start); + + return (true); +} + +/* + * Externalize object encoders. + */ + +static uint8_t * +_prop_bp_encode_bool(_prop_bp_extern_t be, prop_bool_t pb) +{ + uint8_t *obj; + + obj = _prop_bp_ensure_capacity(be, 1); + if (obj == NULL) + return (false); + + if (prop_bool_true(pb)) + obj = _prop_bp_write_byte(obj, BPLIST_MULTI_TRUE); + else + obj = _prop_bp_write_byte(obj, BPLIST_MULTI_FALSE); + + return (obj); +} + +static uint8_t * +_prop_bp_encode_number(_prop_bp_extern_t be, prop_number_t pn) +{ + uint64_t big; + uint8_t *obj; + uint8_t tag; + uint8_t low; + int i; + + if (prop_number_unsigned(pn)) { + big = prop_number_unsigned_integer_value(pn); + low = _prop_bp_number_logsize(big); + tag = (low | BPLIST_TYPE_UINT); + } else { + big = (uint64_t)prop_number_integer_value(pn); + low = _prop_bp_number_logsize(big); + tag = (low | BPLIST_TYPE_SINT); + } + + obj = _prop_bp_ensure_capacity(be, BPLIST_NUMBER_MAXLEN); + if (obj == NULL) + return (NULL); + + /* Write object tag. */ + obj = _prop_bp_write_byte(obj, tag); + + /* Write big-endian value. */ + for (i = 8*((1 << low) - 1); i >= 0; i -= 8) + obj = _prop_bp_write_byte(obj, (uint8_t)(big >> i)); + + return (obj); +} + +static uint8_t * +_prop_bp_encode_data(_prop_bp_extern_t be, prop_data_t pd) +{ + uint8_t *obj; + size_t size = prop_data_size(pd); + uint8_t lo; + + if (size < BPLIST_COUNT_MANY) + lo = size; + else + lo = BPLIST_COUNT_MANY; + + obj = _prop_bp_ensure_capacity(be, 1 + BPLIST_NUMBER_MAXLEN + size); + if (obj == NULL) + return (NULL); + + /* Write type tag. */ + obj = _prop_bp_write_byte(obj, BPLIST_TYPE_DATA | lo); + + /* Possibly followed by integer size. */ + if (lo == BPLIST_COUNT_MANY) + obj = _prop_bp_write_number(obj, size); + + /* Write byte array. */ + memcpy(obj, prop_data_data_nocopy(pd), size); + return (obj + size); +} + +static uint8_t * +_prop_bp_encode_string(_prop_bp_extern_t be, prop_object_t po) +{ + uint8_t *obj; + size_t size = _prop_bp_string_size(po); + uint8_t lo; + + if (size < BPLIST_COUNT_MANY) + lo = size; + else + lo = BPLIST_COUNT_MANY; + + obj = _prop_bp_ensure_capacity(be, 1 + BPLIST_NUMBER_MAXLEN + size); + if (obj == NULL) + return (NULL); + + /* Write type tag. */ + obj = _prop_bp_write_byte(obj, BPLIST_TYPE_ASCII | lo); + + /* Possibly followed by integer size. */ + if (lo == BPLIST_COUNT_MANY) + obj = _prop_bp_write_number(obj, size); + + /* Write character array. */ + memcpy(obj, _prop_bp_string_cstring(po), size); + return (obj + size); +} + +static uint8_t * +_prop_bp_encode_array(_prop_bp_extern_t be, prop_array_t pa) +{ + prop_object_iterator_t it; + prop_object_t po; + uint8_t *obj; + uint8_t lo; + u_int count = prop_array_count(pa); + u_int idx; + + if (count < BPLIST_COUNT_MANY) + lo = count; + else + lo = BPLIST_COUNT_MANY; + + obj = _prop_bp_ensure_capacity(be, 1 + BPLIST_NUMBER_MAXLEN + + count * be->be_objref_width); + if (obj == NULL) + return (NULL); + + /* Write type tag. */ + obj = _prop_bp_write_byte(obj, BPLIST_TYPE_ARRAY | lo); + + /* Possibly followed by integer count. */ + if (lo == BPLIST_COUNT_MANY) + obj = _prop_bp_write_number(obj, count); + + /* Write object reference table. */ + it = prop_array_iterator(pa); + if (it == NULL) + return (NULL); + + while ((po = prop_object_iterator_next(it)) != NULL) { + idx = _prop_bp_index_of_object(be, po); + obj = _prop_bp_write_reference(be, obj, idx); + } + + prop_object_iterator_release(it); + return (obj); +} + +static uint8_t * +_prop_bp_encode_dict(_prop_bp_extern_t be, prop_dictionary_t pd) +{ + prop_object_iterator_t it; + prop_object_t po; + prop_object_t qo; + uint8_t *obj; + uint8_t lo; + u_int count = prop_dictionary_count(pd); + u_int idx; + + if (count < BPLIST_COUNT_MANY) + lo = count; + else + lo = BPLIST_COUNT_MANY; + + obj = _prop_bp_ensure_capacity(be, 1 + BPLIST_NUMBER_MAXLEN + + 2 * count * be->be_objref_width); + if (obj == NULL) + return (NULL); + + /* Write type tag. */ + obj = _prop_bp_write_byte(obj, BPLIST_TYPE_DICT | lo); + + /* Possibly followed by integer count. */ + if (lo == BPLIST_COUNT_MANY) + obj = _prop_bp_write_number(obj, count); + + /* Write object reference table. */ + it = prop_dictionary_iterator(pd); + if (it == NULL) + return (NULL); + + /* Write out pairs. */ + while ((po = prop_object_iterator_next(it)) != NULL) { + idx = _prop_bp_index_of_object(be, po); + obj = _prop_bp_write_reference(be, obj, idx); + + qo = prop_dictionary_get_keysym(pd, po); + _PROP_ASSERT(qo != NULL); + + idx = _prop_bp_index_of_object(be, qo); + obj = _prop_bp_write_reference(be, obj, idx); + } + + prop_object_iterator_release(it); + return (obj); +} + +static bool +_prop_bp_encode_object(_prop_bp_extern_t be, _prop_bp_object_t bo) +{ + prop_object_t po = bo->bo_object; + uint8_t *end = NULL; /* gcc */ + + /* Remember offset where object starts. */ + bo->bo_offset = be->be_extern_next; + + switch (prop_object_type(po)) { + case PROP_TYPE_BOOL: + end = _prop_bp_encode_bool(be, po); + break; + + case PROP_TYPE_NUMBER: + end = _prop_bp_encode_number(be, po); + break; + + case PROP_TYPE_DATA: + end = _prop_bp_encode_data(be, po); + break; + + case PROP_TYPE_DICT_KEYSYM: + case PROP_TYPE_STRING: + end = _prop_bp_encode_string(be, po); + break; + + case PROP_TYPE_ARRAY: + end = _prop_bp_encode_array(be, po); + break; + + case PROP_TYPE_DICTIONARY: + end = _prop_bp_encode_dict(be, po); + break; + + case PROP_TYPE_UNKNOWN: /* gcc */ + return (false); + } + + if (end == NULL) + return (false); + + /* Adjust to real encoding length. */ + be->be_extern_next = (end - be->be_extern_buffer); + return (true); +} + +static void +_prop_bp_unlock_compounds(_prop_bp_table_t compounds) +{ + prop_object_t compound; + u_int i; + + /* Unlock all compounds, in reverse order (Note 'i' unsigned). */ + for (i = compounds->bt_next; i > 0; i--) { + compound = compounds->bt_objects[i - 1].bo_object; + + if (prop_object_type(compound) == PROP_TYPE_DICTIONARY) + _prop_dictionary_unlock(compound); + else + _prop_array_unlock(compound); + } +} + +static bool +_prop_bp_externalize(_prop_bp_extern_t be) +{ + prop_object_iterator_t it; + _prop_bp_table_t compounds = &be->be_tables[BPLIST_TABLE_COMPOUND]; + prop_object_t compound; + prop_object_t po; + u_int i; + u_int j; + + /* Build object tables, obtaining readlock on any compound. */ + for (i = 0; i < compounds->bt_next; i++) { + compound = compounds->bt_objects[i].bo_object; + + /* Get iterator for this compound. */ + if (prop_object_type(compound) == PROP_TYPE_DICTIONARY) + it = prop_dictionary_iterator(compound); + else + it = prop_array_iterator(compound); + if (it == NULL) { + _prop_bp_unlock_compounds(compounds); + return (false); + } + + /* Register contents, possibly mutating table's bt_next. */ + while ((po = prop_object_iterator_next(it)) != NULL) + if (! _prop_bp_uniquify_object(be, compound, po)) { + _prop_bp_unlock_compounds(compounds); + return (false); + } + + prop_object_iterator_release(it); + } + + /* Calculate object reference width. */ + be->be_objref_width = _prop_bp_number_size(be->be_index_next); + + /* Encode header. */ + memcpy(be->be_extern_buffer, "bplistZZ", BPLIST_HEADER_LEN); + be->be_extern_next = BPLIST_HEADER_LEN; + + /* Encode object table, fills object offsets. */ + for (i = 0; i < BPLIST_TABLE_COUNT; i++) { + _prop_bp_table_t tab = &be->be_tables[i]; + + for (j = 0; j < tab->bt_next; j++) + if (! _prop_bp_encode_object(be, + &tab->bt_objects[j])) { + _prop_bp_unlock_compounds(compounds); + return (false); + } + } + + /* We won't touch objects any more, release locks. */ + _prop_bp_unlock_compounds(compounds); + + /* Calculate offset table width. */ + be->be_offtab_width = _prop_bp_number_size(be->be_extern_next); + + /* Emit offset table. */ + be->be_offtab_start = be->be_extern_next; + + if (! _prop_bp_write_offtab(be)) + return (false); + + /* Emit trailer structure. */ + if (! _prop_bp_write_trailer(be)) + return (false); + + return (true); +} + +static bool +_prop_bp_externalize_object(prop_object_t po, prop_type_t type, uint8_t **dp, + size_t *lp) +{ + _prop_bp_extern_t be; + bool rv; + + if (prop_object_type(po) != type || dp == NULL || lp == NULL) + return (false); + + be = _prop_bp_extern_init(po); + if (be == NULL) + return (false); + + rv = _prop_bp_externalize(be); + if (rv) { + *dp = be->be_extern_buffer; + *lp = be->be_extern_next; + } else { + _PROP_FREE(be->be_extern_buffer, M_TEMP); + } + + _prop_bp_extern_free(be); + return (rv); +} + +/* + * Public externalizer interface. + */ + +bool +prop_dictionary_binary_externalize(prop_dictionary_t pd, uint8_t **dp, + size_t *lp) +{ + return (_prop_bp_externalize_object(pd, PROP_TYPE_DICTIONARY, dp, lp)); +} + +bool +prop_array_binary_externalize(prop_array_t pa, uint8_t **dp, size_t *lp) +{ + return (_prop_bp_externalize_object(pa, PROP_TYPE_ARRAY, dp, lp)); +} Index: common/lib/libprop/prop_dictionary.3 =================================================================== RCS file: /cvsroot/src/common/lib/libprop/prop_dictionary.3,v retrieving revision 1.8 diff -d -p -u -u -r1.8 prop_dictionary.3 --- common/lib/libprop/prop_dictionary.3 16 Aug 2007 16:31:00 -0000 1.8 +++ common/lib/libprop/prop_dictionary.3 6 Oct 2007 20:14:31 -0000 @@ -60,6 +60,9 @@ .Nm prop_dictionary_internalize , .Nm prop_dictionary_externalize_to_file , .Nm prop_dictionary_internalize_from_file , +.Nm prop_dictionary_binary_externalize, +.Nm prop_dictionary_binary_internalize, +.Nm prop_dictionary_binary_externalize_to_file, .Nm prop_dictionary_equals , .Nm prop_dictionary_keysym_cstring_nocopy , .Nm prop_dictionary_keysym_equals @@ -68,7 +71,6 @@ .Lb libprop .Sh SYNOPSIS .In prop/proplib.h -.\" .Ft prop_dictionary_t .Fn prop_dictionary_create "void" .Ft prop_dictionary_t @@ -136,6 +138,15 @@ .Ft prop_dictionary_t .Fn prop_dictionary_internalize_from_file "const char *path" .\" +.Ft bool +.Fn prop_dictionary_binary_externalize "prop_dictionary_t dict" \ + "uint8_t **bufp" "size_t *lenp" +.Ft prop_dictionary_t +.Fn prop_dictionary_binary_internalize "uint8_t *buf" "size_t len" +.Ft bool +.Fn prop_dictionary_binary_externalize_to_file "prop_dictionary_t dict" \ + "const char *path" +.\" .Sh DESCRIPTION The .Nm prop_dictionary @@ -292,7 +303,7 @@ Returns if parsing fails for any reason. .It Fn prop_dictionary_externalize_to_file "prop_dictionary_t dict" \ "const char *path" -Externalizes a dictionary and writes it to the file specified by +Externalizes a dictionary in XML format and writes it to the file specified by .Fa path . The file is saved with the mode .Dv 0666 @@ -303,9 +314,51 @@ Returns .Dv false if externalizing or writing the dictionary fails for any reason. .It Fn prop_dictionary_internalize_from_file "const char *path" -Reads the XML property list contained in the file specified by +Reads the XML or binary property list contained in the file specified by .Fa path , -internalizes it, and returns the corresponding array. +internalizes it, and returns the corresponding dictionary. +.It Fn prop_dictionary_binary_externalize "prop_dictionary_t dict" \ + "uint8_t **bufp" "size_t *lenp" +Externalizes a dictionary in binary format. On success +.Dv true +is returned, pointer to buffer containing binary representation is filled to +.Fa bufp +and its size is filled to +.Fa lenp +arguments. The caller is responsible for freeing returned buffer. On failure +.Dv false +is returned and arguments are not modified. +.Pp +In user space, the buffer is allocated using +.Xr malloc 3 . +In the kernel, the buffer is allocated using +.Xr malloc 9 +using the malloc type +.Dv M_TEMP . +.It Fn prop_dictionary_binary_externalize_to_file "prop_dictionary_t dict" \ + "const char *path" +Externalizes a dictionary in binary format and writes it to the file +specified by +.Fa path . +The file is saved with the mode +.Dv 0666 +as modified by the process's file creation mask +.Pq see Xr umask 3 +and is written atomically. +Returns +.Dv false +if externalizing or writing the dictionary fails for any reason. +.It Fn prop_dictionary_binary_internalize "uint8_t *buf" "size_t len" +Parse the binary representation of a property list in the buffer +.Fa buf +of size +.Fa len +and return the corresponding dictionary. +Returns +.Dv true +on success, +.Dv false +if parsing fails for any reason. .El .Sh SEE ALSO .Xr prop_array 3 , @@ -320,3 +373,6 @@ The .Nm proplib property container object library first appeared in .Nx 4.0 . +.Pp +Support for binary external representation appeared in +.Nx 5.0 . Index: common/lib/libprop/prop_dictionary.c =================================================================== RCS file: /cvsroot/src/common/lib/libprop/prop_dictionary.c,v retrieving revision 1.20 diff -d -p -u -u -r1.20 prop_dictionary.c --- common/lib/libprop/prop_dictionary.c 30 Aug 2007 12:23:54 -0000 1.20 +++ common/lib/libprop/prop_dictionary.c 6 Oct 2007 20:14:32 -0000 @@ -158,6 +158,24 @@ struct _prop_dictionary_iterator { unsigned int pdi_index; }; +void +_prop_dictionary_rdlock(prop_object_t po) +{ + prop_dictionary_t pd = po; + + _PROP_ASSERT(prop_object_is_dictionary(pd)); + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); +} + +void +_prop_dictionary_unlock(prop_object_t po) +{ + prop_dictionary_t pd = po; + + _PROP_ASSERT(prop_object_is_dictionary(pd)); + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); +} + /* * Dictionary key symbols are immutable, and we are likely to have many * duplicated key symbols. So, to save memory, we unique'ify key symbols @@ -1279,7 +1297,7 @@ prop_dictionary_internalize(const char * #if !defined(_KERNEL) && !defined(_STANDALONE) /* * prop_dictionary_externalize_to_file -- - * Externalize a dictionary to the specified file. + * Externalize a dictionary to the specified file in XML format. */ bool prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname) @@ -1301,6 +1319,33 @@ prop_dictionary_externalize_to_file(prop return (rv); } + +/* + * prop_dictionary_binary_externalize_to_file -- + * Externalize a dictionary to the specified file in bplist format. + */ +bool +prop_dictionary_binary_externalize_to_file(prop_dictionary_t dict, + const char *fname) +{ + uint8_t *buf; + size_t len; + bool rv; + int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ + + if (! prop_dictionary_binary_externalize(dict, &buf, &len)) + return (false); + + rv = _prop_object_externalize_write_file(fname, (char *)buf, len); + if (rv == false) + save_errno = errno; + _PROP_FREE(buf, M_TEMP); + if (rv == false) + errno = save_errno; + + return (rv); +} + /* * prop_dictionary_internalize_from_file -- * Internalize a dictionary from a file. @@ -1314,7 +1359,11 @@ prop_dictionary_internalize_from_file(co mf = _prop_object_internalize_map_file(fname); if (mf == NULL) return (NULL); - dict = prop_dictionary_internalize(mf->poimf_xml); + dict = prop_dictionary_internalize((char *)mf->poimf_data); + if (dict == NULL) { + dict = prop_dictionary_binary_internalize(mf->poimf_data, + mf->poimf_datasize); + } _prop_object_internalize_unmap_file(mf); return (dict); Index: common/lib/libprop/prop_object.c =================================================================== RCS file: /cvsroot/src/common/lib/libprop/prop_object.c,v retrieving revision 1.17 diff -d -p -u -u -r1.17 prop_object.c --- common/lib/libprop/prop_object.c 30 Aug 2007 19:12:32 -0000 1.17 +++ common/lib/libprop/prop_object.c 6 Oct 2007 20:14:33 -0000 @@ -926,6 +926,7 @@ _prop_object_internalize_map_file(const _PROP_FREE(mf, M_TEMP); return (NULL); } + mf->poimf_datasize = (size_t)sb.st_size; mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask; if (mf->poimf_mapsize < sb.st_size) { (void) close(fd); @@ -941,22 +942,22 @@ _prop_object_internalize_map_file(const if ((sb.st_size & pgmask) == 0) need_guard = true; - mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize + mf->poimf_data = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize : mf->poimf_mapsize, PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0); (void) close(fd); - if (mf->poimf_xml == MAP_FAILED) { + if (mf->poimf_data == MAP_FAILED) { _PROP_FREE(mf, M_TEMP); return (NULL); } - (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL); + (void) madvise(mf->poimf_data, mf->poimf_mapsize, MADV_SEQUENTIAL); if (need_guard) { - if (mmap(mf->poimf_xml + mf->poimf_mapsize, + if (mmap(mf->poimf_data + mf->poimf_mapsize, pgsize, PROT_READ, MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, (off_t)0) == MAP_FAILED) { - (void) munmap(mf->poimf_xml, mf->poimf_mapsize); + (void) munmap(mf->poimf_data, mf->poimf_mapsize); _PROP_FREE(mf, M_TEMP); return (NULL); } @@ -975,8 +976,8 @@ _prop_object_internalize_unmap_file( struct _prop_object_internalize_mapped_file *mf) { - (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED); - (void) munmap(mf->poimf_xml, mf->poimf_mapsize); + (void) madvise(mf->poimf_data, mf->poimf_mapsize, MADV_DONTNEED); + (void) munmap(mf->poimf_data, mf->poimf_mapsize); _PROP_FREE(mf, M_TEMP); } #endif /* !_KERNEL && !_STANDALONE */ Index: common/lib/libprop/prop_object_impl.h =================================================================== RCS file: /cvsroot/src/common/lib/libprop/prop_object_impl.h,v retrieving revision 1.18 diff -d -p -u -u -r1.18 prop_object_impl.h --- common/lib/libprop/prop_object_impl.h 30 Aug 2007 12:23:54 -0000 1.18 +++ common/lib/libprop/prop_object_impl.h 6 Oct 2007 20:14:33 -0000 @@ -45,6 +45,7 @@ #include #endif +#include #include "prop_stack.h" struct _prop_object_externalize_context { @@ -159,7 +160,8 @@ bool _prop_object_externalize_write_file const char *, size_t); struct _prop_object_internalize_mapped_file { - char * poimf_xml; + uint8_t *poimf_data; + size_t poimf_datasize; size_t poimf_mapsize; }; @@ -232,6 +234,12 @@ struct _prop_object_iterator { uint32_t pi_version; }; +/* Private APIs published by prop_{array,dictionary}.c */ +void _prop_dictionary_rdlock(prop_object_t); +void _prop_dictionary_unlock(prop_object_t); +void _prop_array_rdlock(prop_object_t); +void _prop_array_unlock(prop_object_t); + #if defined(_KERNEL) /* Index: common/lib/libprop/proplib.3 =================================================================== RCS file: /cvsroot/src/common/lib/libprop/proplib.3,v retrieving revision 1.4 diff -d -p -u -u -r1.4 proplib.3 --- common/lib/libprop/proplib.3 21 Jun 2007 12:02:31 -0000 1.4 +++ common/lib/libprop/proplib.3 6 Oct 2007 20:14:34 -0000 @@ -55,12 +55,14 @@ Structure is provided by the array and d .Pp Property lists can be passed across protection boundaries by translating them to an external representation. -This external representation is an XML document whose format is described -by the following DTD: +This external representation is either an XML document whose format is +described by the following DTD: .Bd -literal -offset indent http://www.apple.com/DTDs/PropertyList-1.0.dtd .Ed .Pp +or a binary format based on Apple bplist encoding. +.Pp Property container objects are reference counted. When an object is created, its reference count is set to 1. Any code that keeps a reference to an object, including the collection @@ -142,3 +144,22 @@ in kernel, standalone, and user space en .Nm parser is not a real XML parser. It is hard-coded to parse only the property list external representation. +.Pp +The binary encoding differs from Apple bplist format in the following +ways: +.Bl -bullet +.It +NetBSD uses type marker 0x10 for unsigned integers while in Apple format, +which doesn't really support unsigned integers, it is used to denote signed +integers. +.It +NetBSD uses type marker 0x70 for signed integers while in Apple format +it is reserved for future use. +.It +NetBSD encodes dictionary contents as * while Apple uses +. +.El +.Pp +Given the above incompatibilities, NetBSD binary plists are marked as +version 'ZZ' of the format. Apple uses version '00' as of the time of +this writing. Index: common/include/prop/prop_array.h =================================================================== RCS file: /cvsroot/src/common/include/prop/prop_array.h,v retrieving revision 1.5 diff -d -p -u -u -r1.5 prop_array.h --- common/include/prop/prop_array.h 16 Aug 2007 16:28:17 -0000 1.5 +++ common/include/prop/prop_array.h 6 Oct 2007 20:14:34 -0000 @@ -69,6 +69,12 @@ bool prop_array_equals(prop_array_t, pr char * prop_array_externalize(prop_array_t); prop_array_t prop_array_internalize(const char *); +bool prop_array_binary_externalize(prop_array_t, uint8_t **, + size_t *); +prop_array_t prop_array_binary_internalize(uint8_t *, size_t); + +bool prop_array_binary_externalize_to_file(prop_array_t, + const char *); bool prop_array_externalize_to_file(prop_array_t, const char *); prop_array_t prop_array_internalize_from_file(const char *); Index: common/include/prop/prop_dictionary.h =================================================================== RCS file: /cvsroot/src/common/include/prop/prop_dictionary.h,v retrieving revision 1.7 diff -d -p -u -u -r1.7 prop_dictionary.h --- common/include/prop/prop_dictionary.h 16 Aug 2007 16:28:17 -0000 1.7 +++ common/include/prop/prop_dictionary.h 6 Oct 2007 20:14:34 -0000 @@ -80,6 +80,12 @@ bool prop_dictionary_equals(prop_dictio char * prop_dictionary_externalize(prop_dictionary_t); prop_dictionary_t prop_dictionary_internalize(const char *); +bool prop_dictionary_binary_externalize(prop_dictionary_t, + uint8_t **, size_t *); +prop_dictionary_t prop_dictionary_binary_internalize(uint8_t *, size_t); + +bool prop_dictionary_binary_externalize_to_file(prop_dictionary_t, + const char *); bool prop_dictionary_externalize_to_file(prop_dictionary_t, const char *); prop_dictionary_t prop_dictionary_internalize_from_file(const char *); Index: lib/libprop/Makefile =================================================================== RCS file: /cvsroot/src/lib/libprop/Makefile,v retrieving revision 1.14 diff -d -p -u -u -r1.14 Makefile --- lib/libprop/Makefile 27 Oct 2006 01:29:37 -0000 1.14 +++ lib/libprop/Makefile 6 Oct 2007 20:14:34 -0000 @@ -73,6 +73,9 @@ MLINKS+= prop_array.3 prop_array_make_im MLINKS+= prop_array.3 prop_array_mutable.3 MLINKS+= prop_array.3 prop_array_remove.3 MLINKS+= prop_array.3 prop_array_set.3 +MLINKS+= prop_array.3 prop_array_binary_externalize.3 +MLINKS+= prop_array.3 prop_array_binary_internalize.3 +MLINKS+= prop_array.3 prop_array_binary_externalize_to_file.3 MLINKS+= prop_bool.3 prop_bool_copy.3 MLINKS+= prop_bool.3 prop_bool_create.3 @@ -111,6 +114,9 @@ MLINKS+= prop_dictionary.3 prop_dictiona MLINKS+= prop_dictionary.3 prop_dictionary_remove_keysym.3 MLINKS+= prop_dictionary.3 prop_dictionary_set.3 MLINKS+= prop_dictionary.3 prop_dictionary_set_keysym.3 +MLINKS+= prop_dictionary.3 prop_dictionary_binary_externalize.3 +MLINKS+= prop_dictionary.3 prop_dictionary_binary_internalize.3 +MLINKS+= prop_dictionary.3 prop_dictionary_binary_externalize_to_file.3 MLINKS+= prop_ingest.3 prop_ingest_context_alloc.3 MLINKS+= prop_ingest.3 prop_ingest_context_error.3