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 <freza@NetBSD.org>.
+ * 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
