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 <[email protected]>.
+ * 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 <prop/proplib.h>
+#include "prop_object_impl.h"
+
+#if defined(_KERNEL) || defined(_STANDALONE)
+#include <machine/limits.h>
+#else
+#include <limits.h>
+#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 <key, val> 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 <keyref, objref> 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 <inttypes.h>
#endif

+#include <prop/prop_object.h>
#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 <keyref objref>* while Apple uses
+<keyref* objref*>.
+.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