[prev in list] [next in list] [prev in thread] [next in thread] 

List:       rpm-cvs
Subject:    [CVS] RPM: rpm-5_4: rpm/rpmio/ Makefile.am bcon.c bmw.c bson.c bson.h ...
From:       "Jeff Johnson" <jbj () rpm5 ! org>
Date:       2014-09-30 22:31:56
Message-ID: 20140930223156.91329722F3 () rpm5 ! org
[Download RAW message or body]

  RPM Package Manager, CVS Repository
  http://rpm5.org/cvs/
  ____________________________________________________________________________

  Server: rpm5.org                         Name:   Jeff Johnson
  Root:   /v/rpm/cvs                       Email:  jbj@rpm5.org
  Module: rpm                              Date:   01-Oct-2014 00:31:56
  Branch: rpm-5_4                          Handle: 2014093022314201

  Added files:              (Branch: rpm-5_4)
    rpm/rpmio               bcon.c mongoc-counters.defs mongoc.c mongoc.h
  Modified files:           (Branch: rpm-5_4)
    rpm/rpmio               Makefile.am bmw.c bson.c bson.h edon-r.c
                            librpmio.vers md6.c poptIO.c rpmdav.c rpmio.c
                            rpmsp.c rpmtpm.c ttpm.c

  Log:
    - mongo: upgrade to mongo-c-driver-0.92.2.

  Summary:
    Revision    Changes     Path
    1.293.2.40  +5  -4      rpm/rpmio/Makefile.am
    1.1.2.1     +1029 -0    rpm/rpmio/bcon.c
    2.4.4.1     +2  -2      rpm/rpmio/bmw.c
    2.4.4.8     +12949 -1193rpm/rpmio/bson.c
    2.3.4.9     +3366 -773  rpm/rpmio/bson.h
    1.5.4.2     +2  -2      rpm/rpmio/edon-r.c
    2.199.2.47  +477 -202   rpm/rpmio/librpmio.vers
    1.3.4.5     +2  -2      rpm/rpmio/md6.c
    1.1.2.1     +61 -0      rpm/rpmio/mongoc-counters.defs
    1.1.2.1     +17229 -0   rpm/rpmio/mongoc.c
    1.1.2.1     +2639 -0    rpm/rpmio/mongoc.h
    1.94.2.17   +4  -0      rpm/rpmio/poptIO.c
    2.119.2.16  +4  -0      rpm/rpmio/rpmdav.c
    1.230.2.26  +6  -0      rpm/rpmio/rpmio.c
    2.7.2.4     +2  -0      rpm/rpmio/rpmsp.c
    1.5.2.21    +3  -1      rpm/rpmio/rpmtpm.c
    2.6.2.5     +23 -0      rpm/rpmio/ttpm.c
  ____________________________________________________________________________

  patch -p0 <<'@@ .'
  Index: rpm/rpmio/Makefile.am
  ============================================================================
  $ cvs diff -u -r1.293.2.39 -r1.293.2.40 Makefile.am
  --- rpm/rpmio/Makefile.am	29 Sep 2014 18:26:48 -0000	1.293.2.39
  +++ rpm/rpmio/Makefile.am	30 Sep 2014 22:31:42 -0000	1.293.2.40
  @@ -13,7 +13,7 @@
   	rpmjsio.msg rpmtar.c rpmtar.h \
   	tdir.c tfts.c tget.c tgit.c tglob.c thkp.c thtml.c tinv.c tkey.c \
   	tmire.c todbc.c tput.c tpython.c trpmio.c tsexp.c tsvn.c tsw.c tybi.c \
  -	lookup3.c testit.sh
  +	lookup3.c testit.sh mongoc-counters.defs
   
   EXTRA_PROGRAMS = rpmcpio rpmdpkg rpmtar rpmz
   EXTRA_PROGRAMS += bdes thtml tinv tkey tmacro tpw
  @@ -121,10 +121,10 @@
   	rpmbf.h rpmcb.h rpmio.h rpmlog.h rpmiotypes.h rpmmacro.h
   	rpmpgp.h rpmsw.h rpmutil.h
   noinst_HEADERS = \
  -	ar.h bson.h cpio.h crc.h envvar.h fnmatch.h fts.h glob.h iosm.h \
  +	ar.h bcon.h bson.h cpio.h crc.h envvar.h fnmatch.h fts.h glob.h iosm.h \
   	blake2.h blake2-impl.h blake2-rpm.h blake2-kat.h \
   	arirang.h blake.h bmw.h chi.h cubehash.h echo.h edon-r.h fugue.h \
  -	groestl.h hamsi.h jh.h keccak.h lane.h luffa.h md2.h md6.h mongo.h \
  +	groestl.h hamsi.h jh.h keccak.h lane.h luffa.h md2.h md6.h mongoc.h \
   	radiogatun.h \
   	salsa10.h salsa20.h shabal.h shavite3.h simd.h skein.h tib3.h tiger.h \
   	pcrs.h poptIO.h rpmacl.h rpmasn.h rpmaug.h rpmbag.h rpmbc.h rpmbz.h \
  @@ -151,6 +151,7 @@
   	rpmnix.c rpmodbc.c rpmsql.c set.c \
   	ar.c \
   	argv.c \
  +	bcon.c \
   	bson.c \
   	bzdio.c \
   	cipher.c \
  @@ -165,7 +166,7 @@
   	lsyck.c \
   	macro.c \
   	mire.c \
  -	mongo.c \
  +	mongoc.c \
   	mount.c \
   	pcrs.c \
   	poptIO.c \
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/bcon.c
  ============================================================================
  $ cvs diff -u -r0 -r1.1.2.1 bcon.c
  --- /dev/null	2014-10-01 00:31:02.000000000 +0200
  +++ bcon.c	2014-10-01 00:31:45.282504474 +0200
  @@ -0,0 +1,1029 @@
  +/*
  + * @file bcon.c
  + * @brief BCON (BSON C Object Notation) Implementation
  + */
  +
  +/*    Copyright 2009-2013 MongoDB, Inc.
  + *
  + *    Licensed under the Apache License, Version 2.0 (the "License");
  + *    you may not use this file except in compliance with the License.
  + *    You may obtain a copy of the License at
  + *
  + *    http://www.apache.org/licenses/LICENSE-2.0
  + *
  + *    Unless required by applicable law or agreed to in writing, software
  + *    distributed under the License is distributed on an "AS IS" BASIS,
  + *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  + *    See the License for the specific language governing permissions and
  + *    limitations under the License.
  + */
  +
  +#include "system.h"
  +
  +#include "bson.h"
  +#include "bcon.h"
  +
  +#include "debug.h"
  +
  +/* These stack manipulation macros are used to manage append recursion in
  + * bcon_append_ctx_va().  They take care of some awkward dereference rules (the
  + * real bson object isn't in the stack, but accessed by pointer) and add in run
  + * time asserts to make sure we don't blow the stack in either direction */
  +
  +#define STACK_ELE(_delta, _name) (ctx->stack[(_delta) + ctx->n]._name)
  +
  +#define STACK_BSON(_delta) ( \
  +      ((_delta) + ctx->n) == 0 \
  +      ? bson \
  +      : &STACK_ELE (_delta, bson) \
  +      )
  +
  +#define STACK_ITER(_delta) ( \
  +      ((_delta) + ctx->n) == 0 \
  +      ? &root_iter \
  +      : &STACK_ELE (_delta, iter) \
  +      )
  +
  +#define STACK_BSON_PARENT STACK_BSON (-1)
  +#define STACK_BSON_CHILD STACK_BSON (0)
  +
  +#define STACK_ITER_PARENT STACK_ITER (-1)
  +#define STACK_ITER_CHILD STACK_ITER (0)
  +
  +#define STACK_I STACK_ELE (0, i)
  +#define STACK_IS_ARRAY STACK_ELE (0, is_array)
  +
  +#define STACK_PUSH_ARRAY(statement) \
  +   do { \
  +      assert (ctx->n < (BCON_STACK_MAX - 1)); \
  +      ctx->n++; \
  +      STACK_I = 0; \
  +      STACK_IS_ARRAY = 1; \
  +      statement; \
  +   } while (0)
  +
  +#define STACK_PUSH_DOC(statement) \
  +   do { \
  +      assert (ctx->n < (BCON_STACK_MAX - 1)); \
  +      ctx->n++; \
  +      STACK_IS_ARRAY = 0; \
  +      statement; \
  +   } while (0)
  +
  +#define STACK_POP_ARRAY(statement) \
  +   do { \
  +      assert (STACK_IS_ARRAY); \
  +      assert (ctx->n != 0); \
  +      statement; \
  +      ctx->n--; \
  +   } while (0)
  +
  +#define STACK_POP_DOC(statement) \
  +   do { \
  +      assert (!STACK_IS_ARRAY); \
  +      assert (ctx->n != 0); \
  +      statement; \
  +      ctx->n--; \
  +   } while (0)
  +
  +/* This is a landing pad union for all of the types we can process with bcon.
  + * We need actual storage for this to capture the return value of va_arg, which
  + * takes multiple calls to get everything we need for some complex types */
  +typedef union bcon_append {
  +   char   *UTF8;
  +   double  DOUBLE;
  +   bson_t *DOCUMENT;
  +   bson_t *ARRAY;
  +   bson_t *BCON;
  +
  +   struct
  +   {
  +      bson_subtype_t subtype;
  +      uint8_t  *binary;
  +      uint32_t  length;
  +   } BIN;
  +
  +   bson_oid_t    *OID;
  +   bool BOOL;
  +   int64_t   DATE_TIME;
  +
  +   struct
  +   {
  +      char *regex;
  +      char *flags;
  +   } REGEX;
  +
  +   struct
  +   {
  +      char       *collection;
  +      bson_oid_t *oid;
  +   } DBPOINTER;
  +
  +   const char *CODE;
  +
  +   char *SYMBOL;
  +
  +   struct
  +   {
  +      const char *js;
  +      bson_t     *scope;
  +   } CODEWSCOPE;
  +
  +   int32_t INT32;
  +
  +   struct
  +   {
  +      uint32_t timestamp;
  +      uint32_t increment;
  +   } TIMESTAMP;
  +
  +   int64_t       INT64;
  +   const bson_iter_t *ITER;
  +} bcon_append_t;
  +
  +/* same as bcon_append_t.  Some extra symbols and varying types that handle the
  + * differences between bson_append and bson_iter */
  +typedef union bcon_extract {
  +   bson_type_t  TYPE;
  +   bson_iter_t *ITER;
  +   const char  *key;
  +   const char **UTF8;
  +   double      *DOUBLE;
  +   bson_t      *DOCUMENT;
  +   bson_t      *ARRAY;
  +
  +   struct
  +   {
  +      bson_subtype_t      *subtype;
  +      const uint8_t **binary;
  +      uint32_t       *length;
  +   } BIN;
  +
  +   const bson_oid_t **OID;
  +   bool *BOOL;
  +   int64_t      *DATE_TIME;
  +
  +   struct
  +   {
  +      const char **regex;
  +      const char **flags;
  +   } REGEX;
  +
  +   struct
  +   {
  +      const char       **collection;
  +      const bson_oid_t **oid;
  +   } DBPOINTER;
  +
  +   const char **CODE;
  +
  +   const char **SYMBOL;
  +
  +   struct
  +   {
  +      const char **js;
  +      bson_t      *scope;
  +   } CODEWSCOPE;
  +
  +   int32_t *INT32;
  +
  +   struct
  +   {
  +      uint32_t *timestamp;
  +      uint32_t *increment;
  +   } TIMESTAMP;
  +
  +   int64_t *INT64;
  +} bcon_extract_t;
  +
  +static const char *gBconMagic = "BCON_MAGIC";
  +static const char *gBconeMagic = "BCONE_MAGIC";
  +
  +const char *
  +bson_bcon_magic (void)
  +{
  +   return gBconMagic;
  +}
  +
  +
  +const char *
  +bson_bcone_magic (void)
  +{
  +   return gBconeMagic;
  +}
  +
  +static void
  +_noop (void)
  +{
  +}
  +
  +/* appends val to the passed bson object.  Meant to be a super simple dispatch
  + * table */
  +static void
  +_bcon_append_single (bson_t        *bson,
  +                     bcon_type_t    type,
  +                     const char    *key,
  +                     bcon_append_t *val)
  +{
  +   switch ((int)type) {
  +   case BCON_TYPE_UTF8:
  +      bson_append_utf8 (bson, key, -1, val->UTF8, -1);
  +      break;
  +   case BCON_TYPE_DOUBLE:
  +      bson_append_double (bson, key, -1, val->DOUBLE);
  +      break;
  +   case BCON_TYPE_BIN: {
  +         bson_append_binary (bson, key, -1, val->BIN.subtype, val->BIN.binary,
  +                             val->BIN.length);
  +         break;
  +      }
  +   case BCON_TYPE_UNDEFINED:
  +      bson_append_undefined (bson, key, -1);
  +      break;
  +   case BCON_TYPE_OID:
  +      bson_append_oid (bson, key, -1, val->OID);
  +      break;
  +   case BCON_TYPE_BOOL:
  +      bson_append_bool (bson, key, -1, (bool)val->BOOL);
  +      break;
  +   case BCON_TYPE_DATE_TIME:
  +      bson_append_date_time (bson, key, -1, val->DATE_TIME);
  +      break;
  +   case BCON_TYPE_NULL:
  +      bson_append_null (bson, key, -1);
  +      break;
  +   case BCON_TYPE_REGEX: {
  +         bson_append_regex (bson, key, -1, val->REGEX.regex, val->REGEX.flags);
  +         break;
  +      }
  +   case BCON_TYPE_DBPOINTER: {
  +         bson_append_dbpointer (bson, key, -1, val->DBPOINTER.collection,
  +                                val->DBPOINTER.oid);
  +         break;
  +      }
  +   case BCON_TYPE_CODE:
  +      bson_append_code (bson, key, -1, val->CODE);
  +      break;
  +   case BCON_TYPE_SYMBOL:
  +      bson_append_symbol (bson, key, -1, val->SYMBOL, -1);
  +      break;
  +   case BCON_TYPE_CODEWSCOPE:
  +      bson_append_code_with_scope (bson, key, -1, val->CODEWSCOPE.js,
  +                                   val->CODEWSCOPE.scope);
  +      break;
  +   case BCON_TYPE_INT32:
  +      bson_append_int32 (bson, key, -1, val->INT32);
  +      break;
  +   case BCON_TYPE_TIMESTAMP: {
  +         bson_append_timestamp (bson, key, -1, val->TIMESTAMP.timestamp,
  +                                val->TIMESTAMP.increment);
  +         break;
  +      }
  +   case BCON_TYPE_INT64:
  +      bson_append_int64 (bson, key, -1, val->INT64);
  +      break;
  +   case BCON_TYPE_MAXKEY:
  +      bson_append_maxkey (bson, key, -1);
  +      break;
  +   case BCON_TYPE_MINKEY:
  +      bson_append_minkey (bson, key, -1);
  +      break;
  +   case BCON_TYPE_ARRAY: {
  +         bson_append_array (bson, key, -1, val->ARRAY);
  +         break;
  +      }
  +   case BCON_TYPE_DOCUMENT: {
  +         bson_append_document (bson, key, -1, val->DOCUMENT);
  +         break;
  +      }
  +   case BCON_TYPE_ITER:
  +      bson_append_iter (bson, key, -1, val->ITER);
  +      break;
  +   default:
  +      assert (0);
  +      break;
  +   }
  +}
  +
  +#define CHECK_TYPE(_type) \
  +   do { \
  +      if (bson_iter_type (iter) != (_type)) { return false; } \
  +   } while (0)
  +
  +/* extracts the value under the iterator and writes it to val.  returns false
  + * if the iterator type doesn't match the token type.
  + *
  + * There are two magic tokens:
  + *
  + * BCONE_SKIP -
  + *    Let's us verify that a key has a type, without caring about its value.
  + *    This allows for wider declarative BSON verification
  + *
  + * BCONE_ITER -
  + *    Returns the underlying iterator.  This could allow for more complicated,
  + *    procedural verification (if a parameter could have multiple types).
  + * */
  +static bool
  +_bcon_extract_single (const bson_iter_t *iter,
  +                      bcon_type_t        type,
  +                      bcon_extract_t    *val)
  +{
  +   switch ((int)type) {
  +   case BCON_TYPE_UTF8:
  +      CHECK_TYPE (BSON_TYPE_UTF8);
  +      *val->UTF8 = bson_iter_utf8 (iter, NULL);
  +      break;
  +   case BCON_TYPE_DOUBLE:
  +      CHECK_TYPE (BSON_TYPE_DOUBLE);
  +      *val->DOUBLE = bson_iter_double (iter);
  +      break;
  +   case BCON_TYPE_BIN:
  +      CHECK_TYPE (BSON_TYPE_BINARY);
  +      bson_iter_binary (iter, val->BIN.subtype, val->BIN.length,
  +                        val->BIN.binary);
  +      break;
  +   case BCON_TYPE_UNDEFINED:
  +      CHECK_TYPE (BSON_TYPE_UNDEFINED);
  +      break;
  +   case BCON_TYPE_OID:
  +      CHECK_TYPE (BSON_TYPE_OID);
  +      *val->OID = bson_iter_oid (iter);
  +      break;
  +   case BCON_TYPE_BOOL:
  +      CHECK_TYPE (BSON_TYPE_BOOL);
  +      *val->BOOL = bson_iter_bool (iter);
  +      break;
  +   case BCON_TYPE_DATE_TIME:
  +      CHECK_TYPE (BSON_TYPE_DATE_TIME);
  +      *val->DATE_TIME = bson_iter_date_time (iter);
  +      break;
  +   case BCON_TYPE_NULL:
  +      CHECK_TYPE (BSON_TYPE_NULL);
  +      break;
  +   case BCON_TYPE_REGEX:
  +      CHECK_TYPE (BSON_TYPE_REGEX);
  +      *val->REGEX.regex = bson_iter_regex (iter, val->REGEX.flags);
  +
  +      break;
  +   case BCON_TYPE_DBPOINTER:
  +      CHECK_TYPE (BSON_TYPE_DBPOINTER);
  +      bson_iter_dbpointer (iter, NULL, val->DBPOINTER.collection,
  +                           val->DBPOINTER.oid);
  +      break;
  +   case BCON_TYPE_CODE:
  +      CHECK_TYPE (BSON_TYPE_CODE);
  +      *val->CODE = bson_iter_code (iter, NULL);
  +      break;
  +   case BCON_TYPE_SYMBOL:
  +      CHECK_TYPE (BSON_TYPE_SYMBOL);
  +      *val->SYMBOL = bson_iter_symbol (iter, NULL);
  +      break;
  +   case BCON_TYPE_CODEWSCOPE: {
  +         const uint8_t *buf;
  +         uint32_t len;
  +
  +         CHECK_TYPE (BSON_TYPE_CODEWSCOPE);
  +
  +         *val->CODEWSCOPE.js = bson_iter_codewscope (iter, NULL, &len, &buf);
  +
  +         bson_init_static (val->CODEWSCOPE.scope, buf, len);
  +         break;
  +      }
  +   case BCON_TYPE_INT32:
  +      CHECK_TYPE (BSON_TYPE_INT32);
  +      *val->INT32 = bson_iter_int32 (iter);
  +      break;
  +   case BCON_TYPE_TIMESTAMP:
  +      CHECK_TYPE (BSON_TYPE_TIMESTAMP);
  +      bson_iter_timestamp (iter, val->TIMESTAMP.timestamp,
  +                           val->TIMESTAMP.increment);
  +      break;
  +   case BCON_TYPE_INT64:
  +      CHECK_TYPE (BSON_TYPE_INT64);
  +      *val->INT64 = bson_iter_int64 (iter);
  +      break;
  +   case BCON_TYPE_MAXKEY:
  +      CHECK_TYPE (BSON_TYPE_MAXKEY);
  +      break;
  +   case BCON_TYPE_MINKEY:
  +      CHECK_TYPE (BSON_TYPE_MINKEY);
  +      break;
  +   case BCON_TYPE_ARRAY: {
  +         const uint8_t *buf;
  +         uint32_t len;
  +
  +         CHECK_TYPE (BSON_TYPE_ARRAY);
  +
  +         bson_iter_array (iter, &len, &buf);
  +
  +         bson_init_static (val->ARRAY, buf, len);
  +         break;
  +      }
  +   case BCON_TYPE_DOCUMENT: {
  +         const uint8_t *buf;
  +         uint32_t len;
  +
  +         CHECK_TYPE (BSON_TYPE_DOCUMENT);
  +
  +         bson_iter_document (iter, &len, &buf);
  +
  +         bson_init_static (val->DOCUMENT, buf, len);
  +         break;
  +      }
  +   case BCON_TYPE_SKIP:
  +      CHECK_TYPE (val->TYPE);
  +      break;
  +   case BCON_TYPE_ITER:
  +      memcpy (val->ITER, iter, sizeof *iter);
  +      break;
  +   default:
  +      assert (0);
  +      break;
  +   }
  +
  +   return true;
  +}
  +
  +/* Consumes ap, storing output values into u and returning the type of the
  + * captured token.
  + *
  + * The basic workflow goes like this:
  + *
  + * 1. Look at the current arg.  It will be a char *
  + *    a. If it's a NULL, we're done processing.
  + *    b. If it's BCON_MAGIC (a symbol with storage in this module)
  + *       I. The next token is the type
  + *       II. The type specifies how many args to eat and their types
  + *    c. Otherwise it's either recursion related or a raw string
  + *       I. If the first byte is '{', '}', '[', or ']' pass back an
  + *          appropriate recursion token
  + *       II. If not, just call it a UTF8 token and pass that back
  + */
  +bcon_type_t
  +_bcon_append_tokenize (va_list       *ap,
  +                       bcon_append_t *u)
  +{
  +   char *mark;
  +   bcon_type_t type;
  +
  +   mark = va_arg (*ap, char *);
  +
  +   assert (mark != BCONE_MAGIC);
  +
  +   if (mark == NULL) {
  +      type = BCON_TYPE_END;
  +   } else if (mark == BCON_MAGIC) {
  +      type = va_arg (*ap, bcon_type_t);
  +
  +      switch ((int)type) {
  +      case BCON_TYPE_UTF8:
  +         u->UTF8 = va_arg (*ap, char *);
  +         break;
  +      case BCON_TYPE_DOUBLE:
  +         u->DOUBLE = va_arg (*ap, double);
  +         break;
  +      case BCON_TYPE_DOCUMENT:
  +         u->DOCUMENT = va_arg (*ap, bson_t *);
  +         break;
  +      case BCON_TYPE_ARRAY:
  +         u->ARRAY = va_arg (*ap, bson_t *);
  +         break;
  +      case BCON_TYPE_BIN:
  +         u->BIN.subtype = va_arg (*ap, bson_subtype_t);
  +         u->BIN.binary = va_arg (*ap, uint8_t *);
  +         u->BIN.length = va_arg (*ap, uint32_t);
  +         break;
  +      case BCON_TYPE_UNDEFINED:
  +         break;
  +      case BCON_TYPE_OID:
  +         u->OID = va_arg (*ap, bson_oid_t *);
  +         break;
  +      case BCON_TYPE_BOOL:
  +         u->BOOL = va_arg (*ap, int);
  +         break;
  +      case BCON_TYPE_DATE_TIME:
  +         u->DATE_TIME = va_arg (*ap, int64_t);
  +         break;
  +      case BCON_TYPE_NULL:
  +         break;
  +      case BCON_TYPE_REGEX:
  +         u->REGEX.regex = va_arg (*ap, char *);
  +         u->REGEX.flags = va_arg (*ap, char *);
  +         break;
  +      case BCON_TYPE_DBPOINTER:
  +         u->DBPOINTER.collection = va_arg (*ap, char *);
  +         u->DBPOINTER.oid = va_arg (*ap, bson_oid_t *);
  +         break;
  +      case BCON_TYPE_CODE:
  +         u->CODE = va_arg (*ap, char *);
  +         break;
  +      case BCON_TYPE_SYMBOL:
  +         u->SYMBOL = va_arg (*ap, char *);
  +         break;
  +      case BCON_TYPE_CODEWSCOPE:
  +         u->CODEWSCOPE.js = va_arg (*ap, char *);
  +         u->CODEWSCOPE.scope = va_arg (*ap, bson_t *);
  +         break;
  +      case BCON_TYPE_INT32:
  +         u->INT32 = va_arg (*ap, int32_t);
  +         break;
  +      case BCON_TYPE_TIMESTAMP:
  +         u->TIMESTAMP.timestamp = va_arg (*ap, uint32_t);
  +         u->TIMESTAMP.increment = va_arg (*ap, uint32_t);
  +         break;
  +      case BCON_TYPE_INT64:
  +         u->INT64 = va_arg (*ap, int64_t);
  +         break;
  +      case BCON_TYPE_MAXKEY:
  +         break;
  +      case BCON_TYPE_MINKEY:
  +         break;
  +      case BCON_TYPE_BCON:
  +         u->BCON = va_arg (*ap, bson_t *);
  +         break;
  +      case BCON_TYPE_ITER:
  +         u->ITER = va_arg (*ap, const bson_iter_t *);
  +         break;
  +      default:
  +         assert (0);
  +         break;
  +      }
  +   } else {
  +      switch (mark[0]) {
  +      case '{':
  +         type = BCON_TYPE_DOC_START;
  +         break;
  +      case '}':
  +         type = BCON_TYPE_DOC_END;
  +         break;
  +      case '[':
  +         type = BCON_TYPE_ARRAY_START;
  +         break;
  +      case ']':
  +         type = BCON_TYPE_ARRAY_END;
  +         break;
  +
  +      default:
  +         type = BCON_TYPE_UTF8;
  +         u->UTF8 = mark;
  +         break;
  +      }
  +   }
  +
  +   return type;
  +}
  +
  +
  +/* Consumes ap, storing output values into u and returning the type of the
  + * captured token.
  + *
  + * The basic workflow goes like this:
  + *
  + * 1. Look at the current arg.  It will be a char *
  + *    a. If it's a NULL, we're done processing.
  + *    b. If it's BCONE_MAGIC (a symbol with storage in this module)
  + *       I. The next token is the type
  + *       II. The type specifies how many args to eat and their types
  + *    c. Otherwise it's either recursion related or a raw string
  + *       I. If the first byte is '{', '}', '[', or ']' pass back an
  + *          appropriate recursion token
  + *       II. If not, just call it a UTF8 token and pass that back
  + */
  +bcon_type_t
  +_bcon_extract_tokenize (va_list        *ap,
  +                        bcon_extract_t *u)
  +{
  +   char *mark;
  +   bcon_type_t type;
  +
  +   mark = va_arg (*ap, char *);
  +
  +   assert (mark != BCON_MAGIC);
  +
  +   if (mark == NULL) {
  +      type = BCON_TYPE_END;
  +   } else if (mark == BCONE_MAGIC) {
  +      type = va_arg (*ap, bcon_type_t);
  +
  +      switch ((int)type) {
  +      case BCON_TYPE_UTF8:
  +         u->UTF8 = va_arg (*ap, const char **);
  +         break;
  +      case BCON_TYPE_DOUBLE:
  +         u->DOUBLE = va_arg (*ap, double *);
  +         break;
  +      case BCON_TYPE_DOCUMENT:
  +         u->DOCUMENT = va_arg (*ap, bson_t *);
  +         break;
  +      case BCON_TYPE_ARRAY:
  +         u->ARRAY = va_arg (*ap, bson_t *);
  +         break;
  +      case BCON_TYPE_BIN:
  +         u->BIN.subtype = va_arg (*ap, bson_subtype_t *);
  +         u->BIN.binary = va_arg (*ap, const uint8_t * *);
  +         u->BIN.length = va_arg (*ap, uint32_t *);
  +         break;
  +      case BCON_TYPE_UNDEFINED:
  +         break;
  +      case BCON_TYPE_OID:
  +         u->OID = va_arg (*ap, const bson_oid_t * *);
  +         break;
  +      case BCON_TYPE_BOOL:
  +         u->BOOL = va_arg (*ap, bool *);
  +         break;
  +      case BCON_TYPE_DATE_TIME:
  +         u->DATE_TIME = va_arg (*ap, int64_t *);
  +         break;
  +      case BCON_TYPE_NULL:
  +         break;
  +      case BCON_TYPE_REGEX:
  +         u->REGEX.regex = va_arg (*ap, const char **);
  +         u->REGEX.flags = va_arg (*ap, const char **);
  +         break;
  +      case BCON_TYPE_DBPOINTER:
  +         u->DBPOINTER.collection = va_arg (*ap, const char **);
  +         u->DBPOINTER.oid = va_arg (*ap, const bson_oid_t * *);
  +         break;
  +      case BCON_TYPE_CODE:
  +         u->CODE = va_arg (*ap, const char **);
  +         break;
  +      case BCON_TYPE_SYMBOL:
  +         u->SYMBOL = va_arg (*ap, const char **);
  +         break;
  +      case BCON_TYPE_CODEWSCOPE:
  +         u->CODEWSCOPE.js = va_arg (*ap, const char **);
  +         u->CODEWSCOPE.scope = va_arg (*ap, bson_t *);
  +         break;
  +      case BCON_TYPE_INT32:
  +         u->INT32 = va_arg (*ap, int32_t *);
  +         break;
  +      case BCON_TYPE_TIMESTAMP:
  +         u->TIMESTAMP.timestamp = va_arg (*ap, uint32_t *);
  +         u->TIMESTAMP.increment = va_arg (*ap, uint32_t *);
  +         break;
  +      case BCON_TYPE_INT64:
  +         u->INT64 = va_arg (*ap, int64_t *);
  +         break;
  +      case BCON_TYPE_MAXKEY:
  +         break;
  +      case BCON_TYPE_MINKEY:
  +         break;
  +      case BCON_TYPE_SKIP:
  +         u->TYPE = va_arg (*ap, bson_type_t);
  +         break;
  +      case BCON_TYPE_ITER:
  +         u->ITER = va_arg (*ap, bson_iter_t *);
  +         break;
  +      default:
  +         assert (0);
  +         break;
  +      }
  +   } else {
  +      switch (mark[0]) {
  +      case '{':
  +         type = BCON_TYPE_DOC_START;
  +         break;
  +      case '}':
  +         type = BCON_TYPE_DOC_END;
  +         break;
  +      case '[':
  +         type = BCON_TYPE_ARRAY_START;
  +         break;
  +      case ']':
  +         type = BCON_TYPE_ARRAY_END;
  +         break;
  +
  +      default:
  +         type = BCON_TYPE_RAW;
  +         u->key = mark;
  +         break;
  +      }
  +   }
  +
  +   return type;
  +}
  +
  +
  +/* This trivial utility function is useful for concatenating a bson object onto
  + * the end of another, ignoring the keys from the source bson object and
  + * continuing to use and increment the keys from the source.  It's only useful
  + * when called from bcon_append_ctx_va */
  +static void
  +_bson_concat_array (bson_t            *dest,
  +                    const bson_t      *src,
  +                    bcon_append_ctx_t *ctx)
  +{
  +   const char *key;
  +   char i_str[11];
  +   bson_iter_t iter;
  +
  +   bson_iter_init (&iter, src);
  +
  +   STACK_I--;
  +
  +   while (bson_iter_next (&iter)) {
  +      bson_uint32_to_string (STACK_I, &key, i_str, 11);
  +      STACK_I++;
  +
  +      bson_append_iter (dest, key, -1, &iter);
  +   }
  +}
  +
  +
  +/* Append_ctx_va consumes the va_list until NULL is found, appending into bson
  + * as tokens are found.  It can receive or return an in-progress bson object
  + * via the ctx param.  It can also operate on the middle of a va_list, and so
  + * can be wrapped inside of another varargs function.
  + *
  + * Note that passing in a va_list that isn't perferectly formatted for BCON
  + * ingestion will almost certainly result in undefined behavior
  + *
  + * The workflow relies on the passed ctx object, which holds a stack of bson
  + * objects, along with metadata (if the emedded layer is an array, and which
  + * element it is on if so).  We iterate, generating tokens from the va_list,
  + * until we reach an END token.  If any errors occur, we just blow up (the
  + * var_args stuff is already incredibly fragile to mistakes, and we have no way
  + * of introspecting, so just don't screw it up).
  + *
  + * There are also a few STACK_* macros in here which manimpulate ctx that are
  + * defined up top.
  + * */
  +void
  +bcon_append_ctx_va (bson_t            *bson,
  +                    bcon_append_ctx_t *ctx,
  +                    va_list           *ap)
  +{
  +   bcon_type_t type;
  +   const char *key;
  +
  +   char i_str[11];
  +
  +   bcon_append_t u = { 0 };
  +
  +   while (1) {
  +      if (STACK_IS_ARRAY) {
  +         bson_uint32_to_string (STACK_I, &key, i_str, 11);
  +         STACK_I++;
  +      } else {
  +         type = _bcon_append_tokenize (ap, &u);
  +
  +         if (type == BCON_TYPE_END) {
  +            return;
  +         }
  +
  +         if (type == BCON_TYPE_DOC_END) {
  +            STACK_POP_DOC (bson_append_document_end (STACK_BSON_PARENT,
  +                                                     STACK_BSON_CHILD));
  +            continue;
  +         }
  +
  +         if (type == BCON_TYPE_BCON) {
  +            bson_concat (STACK_BSON_CHILD, u.BCON);
  +            continue;
  +         }
  +
  +         assert (type == BCON_TYPE_UTF8);
  +
  +         key = u.UTF8;
  +      }
  +
  +      type = _bcon_append_tokenize (ap, &u);
  +      assert (type != BCON_TYPE_END);
  +
  +      switch ((int)type) {
  +      case BCON_TYPE_BCON:
  +         assert (STACK_IS_ARRAY);
  +         _bson_concat_array (STACK_BSON_CHILD, u.BCON, ctx);
  +
  +         break;
  +      case BCON_TYPE_DOC_START:
  +         STACK_PUSH_DOC (bson_append_document_begin (STACK_BSON_PARENT, key, -1,
  +                                                     STACK_BSON_CHILD));
  +         break;
  +      case BCON_TYPE_DOC_END:
  +         STACK_POP_DOC (bson_append_document_end (STACK_BSON_PARENT,
  +                                                  STACK_BSON_CHILD));
  +         break;
  +      case BCON_TYPE_ARRAY_START:
  +         STACK_PUSH_ARRAY (bson_append_array_begin (STACK_BSON_PARENT, key, -1,
  +                                                    STACK_BSON_CHILD));
  +         break;
  +      case BCON_TYPE_ARRAY_END:
  +         STACK_POP_ARRAY (bson_append_array_end (STACK_BSON_PARENT,
  +                                                 STACK_BSON_CHILD));
  +         break;
  +      default:
  +         _bcon_append_single (STACK_BSON_CHILD, type, key, &u);
  +
  +         break;
  +      }
  +   }
  +}
  +
  +
  +/* extract_ctx_va consumes the va_list until NULL is found, extracting values
  + * as tokens are found.  It can receive or return an in-progress bson object
  + * via the ctx param.  It can also operate on the middle of a va_list, and so
  + * can be wrapped inside of another varargs function.
  + *
  + * Note that passing in a va_list that isn't perferectly formatted for BCON
  + * ingestion will almost certainly result in undefined behavior
  + *
  + * The workflow relies on the passed ctx object, which holds a stack of iterator
  + * objects, along with metadata (if the emedded layer is an array, and which
  + * element it is on if so).  We iterate, generating tokens from the va_list,
  + * until we reach an END token.  If any errors occur, we just blow up (the
  + * var_args stuff is already incredibly fragile to mistakes, and we have no way
  + * of introspecting, so just don't screw it up).
  + *
  + * There are also a few STACK_* macros in here which manimpulate ctx that are
  + * defined up top.
  + *
  + * The function returns true if all tokens could be successfully matched, false
  + * otherwise.
  + * */
  +bool
  +bcon_extract_ctx_va (bson_t             *bson,
  +                     bcon_extract_ctx_t *ctx,
  +                     va_list            *ap)
  +{
  +   bcon_type_t type;
  +   const char *key;
  +   bson_iter_t root_iter;
  +   bson_iter_t current_iter;
  +
  +   char i_str[11];
  +
  +   bcon_extract_t u = { 0 };
  +
  +   bson_iter_init (&root_iter, bson);
  +
  +   while (1) {
  +      if (STACK_IS_ARRAY) {
  +         bson_uint32_to_string (STACK_I, &key, i_str, 11);
  +         STACK_I++;
  +      } else {
  +         type = _bcon_extract_tokenize (ap, &u);
  +
  +         if (type == BCON_TYPE_END) {
  +            return true;
  +         }
  +
  +         if (type == BCON_TYPE_DOC_END) {
  +            STACK_POP_DOC (_noop ());
  +            continue;
  +         }
  +
  +         assert (type == BCON_TYPE_RAW);
  +
  +         key = u.key;
  +      }
  +
  +      type = _bcon_extract_tokenize (ap, &u);
  +      assert (type != BCON_TYPE_END);
  +
  +      if (type == BCON_TYPE_DOC_END) {
  +         STACK_POP_DOC (_noop ());
  +      } else if (type == BCON_TYPE_ARRAY_END) {
  +         STACK_POP_ARRAY (_noop ());
  +      } else {
  +         memcpy (&current_iter, STACK_ITER_CHILD, sizeof current_iter);
  +
  +         if (!bson_iter_find (&current_iter, key)) { return false; }
  +
  +         switch ((int)type) {
  +         case BCON_TYPE_DOC_START:
  +
  +            if (bson_iter_type (&current_iter) !=
  +                BSON_TYPE_DOCUMENT) { return false; }
  +
  +            STACK_PUSH_DOC (bson_iter_recurse (&current_iter,
  +                                               STACK_ITER_CHILD));
  +            break;
  +         case BCON_TYPE_DOC_END:
  +            STACK_POP_DOC (_noop ());
  +            break;
  +         case BCON_TYPE_ARRAY_START:
  +
  +            if (bson_iter_type (&current_iter) !=
  +                BSON_TYPE_ARRAY) { return false; }
  +
  +            STACK_PUSH_ARRAY (bson_iter_recurse (&current_iter,
  +                                                 STACK_ITER_CHILD));
  +            break;
  +         case BCON_TYPE_ARRAY_END:
  +            STACK_POP_ARRAY (_noop ());
  +            break;
  +         default:
  +
  +            if (!_bcon_extract_single (&current_iter, type, &u)) {
  +               return false;
  +            }
  +
  +            break;
  +         }
  +      }
  +   }
  +}
  +
  +void
  +bcon_extract_ctx_init (bcon_extract_ctx_t *ctx)
  +{
  +   ctx->n = 0;
  +   ctx->stack[0].is_array = false;
  +}
  +
  +bool
  +bcon_extract (bson_t *bson,
  +              ...)
  +{
  +   va_list ap;
  +   bcon_extract_ctx_t ctx;
  +   bool r;
  +
  +   bcon_extract_ctx_init (&ctx);
  +
  +   va_start (ap, bson);
  +
  +   r = bcon_extract_ctx_va (bson, &ctx, &ap);
  +
  +   va_end (ap);
  +
  +   return r;
  +}
  +
  +
  +void
  +bcon_append (bson_t *bson,
  +             ...)
  +{
  +   va_list ap;
  +   bcon_append_ctx_t ctx;
  +
  +   bcon_append_ctx_init (&ctx);
  +
  +   va_start (ap, bson);
  +
  +   bcon_append_ctx_va (bson, &ctx, &ap);
  +
  +   va_end (ap);
  +}
  +
  +
  +void
  +bcon_append_ctx (bson_t            *bson,
  +                 bcon_append_ctx_t *ctx,
  +                 ...)
  +{
  +   va_list ap;
  +
  +   va_start (ap, ctx);
  +
  +   bcon_append_ctx_va (bson, ctx, &ap);
  +
  +   va_end (ap);
  +}
  +
  +
  +void
  +bcon_extract_ctx (bson_t             *bson,
  +                  bcon_extract_ctx_t *ctx,
  +                  ...)
  +{
  +   va_list ap;
  +
  +   va_start (ap, ctx);
  +
  +   bcon_extract_ctx_va (bson, ctx, &ap);
  +
  +   va_end (ap);
  +}
  +
  +void
  +bcon_append_ctx_init (bcon_append_ctx_t *ctx)
  +{
  +   ctx->n = 0;
  +   ctx->stack[0].is_array = 0;
  +}
  +
  +
  +bson_t *
  +bcon_new (void *unused,
  +          ...)
  +{
  +   va_list ap;
  +   bcon_append_ctx_t ctx;
  +   bson_t *bson;
  +
  +   bcon_append_ctx_init (&ctx);
  +
  +   bson = bson_new ();
  +
  +   va_start (ap, unused);
  +
  +   bcon_append_ctx_va (bson, &ctx, &ap);
  +
  +   va_end (ap);
  +
  +   return bson;
  +}
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/bmw.c
  ============================================================================
  $ cvs diff -u -r2.4 -r2.4.4.1 bmw.c
  --- rpm/rpmio/bmw.c	23 Jul 2009 23:18:54 -0000	2.4
  +++ rpm/rpmio/bmw.c	30 Sep 2014 22:31:42 -0000	2.4.4.1
  @@ -656,7 +656,7 @@
       case 256:
   	LastByte = (int)sp->unprocessed_bits >> 3;
   	PadOnePosition = 7 - (sp->unprocessed_bits & 0x07);
  -	bmw256(sp)->LastPart[LastByte] = bmw256(sp)->LastPart[LastByte] & (0xff << \
(PadOnePosition + 1) )\  +	bmw256(sp)->LastPart[LastByte] = \
(bmw256(sp)->LastPart[LastByte] & (0xff << (PadOnePosition + 1) ))\  ^ (0x01 << \
PadOnePosition);  data64 = (uint64_t *)bmw256(sp)->LastPart;
   
  @@ -682,7 +682,7 @@
       case 512:
   	LastByte = (int)sp->unprocessed_bits >> 3;
   	PadOnePosition = 7 - (sp->unprocessed_bits & 0x07);
  -	bmw512(sp)->LastPart[LastByte] = bmw512(sp)->LastPart[LastByte] & (0xff << \
(PadOnePosition + 1) )\  +	bmw512(sp)->LastPart[LastByte] = \
(bmw512(sp)->LastPart[LastByte] & (0xff << (PadOnePosition + 1) ))\  ^ (0x01 << \
PadOnePosition);  data64 = (uint64_t *)bmw512(sp)->LastPart;
   
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/bson.c
  ============================================================================
  $ cvs diff -u -r2.4.4.7 -r2.4.4.8 bson.c
  --- rpm/rpmio/bson.c	29 Jun 2013 01:14:14 -0000	2.4.4.7
  +++ rpm/rpmio/bson.c	30 Sep 2014 22:31:42 -0000	2.4.4.8
  @@ -1,1489 +1,13245 @@
   /* bson.c */
   
  -/*    Copyright 2009, 2010 10gen Inc.
  +/*
  + * Copyright 2013 MongoDB, Inc.
  + *
  + * Licensed under the Apache License, Version 2.0 (the "License");
  + * you may not use this file except in compliance with the License.
  + * You may obtain a copy of the License at
    *
  - *    Licensed under the Apache License, Version 2.0 (the "License");
  - *    you may not use this file except in compliance with the License.
  - *    You may obtain a copy of the License at
  - *
  - *    http://www.apache.org/licenses/LICENSE-2.0
  - *
  - *    Unless required by applicable law or agreed to in writing, software
  - *    distributed under the License is distributed on an "AS IS" BASIS,
  - *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  - *    See the License for the specific language governing permissions and
  - *    limitations under the License.
  + *   http://www.apache.org/licenses/LICENSE-2.0
  + *
  + * Unless required by applicable law or agreed to in writing, software
  + * distributed under the License is distributed on an "AS IS" BASIS,
  + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  + * See the License for the specific language governing permissions and
  + * limitations under the License.
    */
   
   #include "system.h"
  +
   #include <stdarg.h>
  +#include <stddef.h>
  +#include <math.h>
   
  -#include "bson.h"
  +#include <bson.h>
   
   #include "debug.h"
   
  -/* big endian is only used for OID generation. little is used everywhere else */
  -
  -/*@-redef@*/
  -union _dbswap {
  -    uint32_t ui;
  -    uint8_t uc[4];
  -};
  -/*@=redef@*/
  +/*==============================================================*/
  +/*
  + * Copyright (c) 2007-2011, Lloyd Hilaiel <lloyd@hilaiel.com>
  + *
  + * Permission to use, copy, modify, and/or distribute this software for any
  + * purpose with or without fee is hereby granted, provided that the above
  + * copyright notice and this permission notice appear in all copies.
  + *
  + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  + */
  +/*==============================================================*/
  +/* --- yajl_common.h */
   
  -void bson_little_endian64(void* outp, const void* inp)
  +#ifdef __cplusplus
  +extern "C" {
  +#endif    
  +
  +#define YAJL_MAX_DEPTH 128
  +
  +/* msft dll export gunk.  To build a DLL on windows, you
  + * must define WIN32, YAJL_SHARED, and YAJL_BUILD.  To use a shared
  + * DLL, you must define YAJL_SHARED and WIN32 */
  +#if (defined(_WIN32) || defined(WIN32)) && defined(YAJL_SHARED)
  +#  ifdef YAJL_BUILD
  +#    define YAJL_API __declspec(dllexport)
  +#  else
  +#    define YAJL_API __declspec(dllimport)
  +#  endif
  +#else
  +#  if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303
  +#    define YAJL_API __attribute__ ((visibility("default")))
  +#  else
  +#    define YAJL_API
  +#  endif
  +#endif 
  +
  +/** pointer to a malloc function, supporting client overriding memory
  + *  allocation routines */
  +typedef void * (*yajl_malloc_func)(void *ctx, size_t sz);
  +
  +/** pointer to a free function, supporting client overriding memory
  + *  allocation routines */
  +typedef void (*yajl_free_func)(void *ctx, void * ptr);
  +
  +/** pointer to a realloc function which can resize an allocation. */
  +typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz);
  +
  +/** A structure which can be passed to yajl_*_alloc routines to allow the
  + *  client to specify memory allocation functions to be used. */
  +typedef struct
   {
  -    union _dbswap _endian;
  -    _endian.ui = 0x11223344; /* XXX static init? */
  -    if (_endian.uc[0] == 0x44)
  -	memcpy(outp, inp, 8);
  -    else {
  -	const char *in = (const char *)inp;
  -	char *out = (char *)outp;
  -	out[0] = in[7];
  -	out[1] = in[6];
  -	out[2] = in[5];
  -	out[3] = in[4];
  -	out[4] = in[3];
  -	out[5] = in[2];
  -	out[6] = in[1];
  -	out[7] = in[0];
  -    }
  +    /** pointer to a function that can allocate uninitialized memory */
  +    yajl_malloc_func malloc;
  +    /** pointer to a function that can resize memory allocations */
  +    yajl_realloc_func realloc;
  +    /** pointer to a function that can free memory allocated using
  +     *  reallocFunction or mallocFunction */
  +    yajl_free_func free;
  +    /** a context pointer that will be passed to above allocation routines */
  +    void * ctx;
  +} yajl_alloc_funcs;
   
  +#ifdef __cplusplus
   }
  +#endif
   
  -void bson_little_endian32(void* outp, const void* inp)
  -{
  -    union _dbswap _endian;
  -    _endian.ui = 0x11223344; /* XXX static init? */
  -    if (_endian.uc[0] == 0x44)
  -	memcpy(outp, inp, 4);
  -    else {
  -	const char *in = (const char *)inp;
  -	char *out = (char *)outp;
  -	out[0] = in[3];
  -	out[1] = in[2];
  -	out[2] = in[1];
  -	out[3] = in[0];
  -    }
  -}
  +/*==============================================================*/
  +/* --- yajl_version.h */
   
  -void bson_big_endian64(void* outp, const void* inp)
  -{
  -    union _dbswap _endian;
  -    _endian.ui = 0x11223344; /* XXX static init? */
  -    if (_endian.uc[0] == 0x11)
  -	memcpy(outp, inp, 8);
  -    else {
  -	const char *in = (const char *)inp;
  -	char *out = (char *)outp;
  -	out[0] = in[7];
  -	out[1] = in[6];
  -	out[2] = in[5];
  -	out[3] = in[4];
  -	out[4] = in[3];
  -	out[5] = in[2];
  -	out[6] = in[1];
  -	out[7] = in[0];
  -    }
  +#define YAJL_MAJOR 2
  +#define YAJL_MINOR 0
  +#define YAJL_MICRO 4
   
  -}
  +#define YAJL_VERSION ((YAJL_MAJOR * 10000) + (YAJL_MINOR * 100) + YAJL_MICRO)
   
  -void bson_big_endian32(void* outp, const void* inp)
  -{
  -    union _dbswap _endian;
  -    _endian.ui = 0x11223344; /* XXX static init? */
  -    if (_endian.uc[0] == 0x11)
  -	memcpy(outp, inp, 4);
  -    else {
  -	const char *in = (const char *)inp;
  -	char *out = (char *)outp;
  -	out[0] = in[3];
  -	out[1] = in[2];
  -	out[2] = in[1];
  -	out[3] = in[0];
  -    }
  +#ifdef __cplusplus
  +extern "C" {
  +#endif
  +
  +extern int YAJL_API yajl_version(void);
  +
  +#ifdef __cplusplus
   }
  +#endif
   
  -static const int initialBufferSize = 128;
  +/*==============================================================*/
  +/* --- yajl_alloc.h */
  +
  +/**
  + * default memory allocation routines for yajl which use malloc/realloc and
  + * free
  + */
  +#define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz))
  +#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr))
  +#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz))
   
  -/* only need one of these */
  -static const int zero = 0;
  +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
   
   /*==============================================================*/
  -/* --- encoding.c */
  +/* --- yajl_buf.h */
   
   /*
  - * Index into the table below with the first byte of a UTF-8 sequence to
  - * get the number of trailing bytes that are supposed to follow it.
  + * Implementation/performance notes.  If this were moved to a header
  + * only implementation using #define's where possible we might be 
  + * able to sqeeze a little performance out of the guy by killing function
  + * call overhead.  YMMV.
    */
  -static const char trailingBytesForUTF8[256] = {
  -    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  -    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  -    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  -    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  -    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  -    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  -    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  -    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
  -};
   
  -/* --------------------------------------------------------------------- */
  +/**
  + * yajl_buf is a buffer with exponential growth.  the buffer ensures that
  + * you are always null padded.
  + */
  +typedef struct yajl_buf_t * yajl_buf;
   
  -/*
  - * Utility routine to tell whether a sequence of bytes is legal UTF-8.
  - * This must be called with the length pre-determined by the first byte.
  - * The length can be set by:
  - *  length = trailingBytesForUTF8[*source]+1;
  - * and the sequence is illegal right away if there aren't that many bytes
  - * available.
  - * If presented with a length > 4, this returns 0.  The Unicode
  - * definition of UTF-8 goes up to 4-byte sequences.
  - */
  -static int isLegalUTF8( const unsigned char *source, int length ) {
  -    unsigned char a;
  -    const unsigned char *srcptr = source + length;
  -    switch ( length ) {
  -    default:
  -        return 0;
  -        /* Everything else falls through when "true"... */
  -    case 4:
  -        if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
  -    case 3:
  -        if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
  -    case 2:
  -        if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0;
  -        switch ( *source ) {
  -            /* no fall-through in this inner switch */
  -        case 0xE0:
  -            if ( a < 0xA0 ) return 0;
  -            break;
  -        case 0xF0:
  -            if ( a < 0x90 ) return 0;
  -            break;
  -        case 0xF4:
  -            if ( a > 0x8F ) return 0;
  -            break;
  -        default:
  -            if ( a < 0x80 ) return 0;
  -        }
  -    case 1:
  -        if ( *source >= 0x80 && *source < 0xC2 ) return 0;
  -        if ( *source > 0xF4 ) return 0;
  -    }
  -    return 1;
  -}
  +/* allocate a new buffer */
  +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc);
   
  -/* If the name is part of a db ref ($ref, $db, or $id), then return true. */
  -static int bson_string_is_db_ref( const unsigned char *string, const size_t length \
                ) {
  -    int result = 0;
  -
  -    if( length >= 4 ) {
  -        if( string[1] == 'r' && string[2] == 'e' && string[3] == 'f' )
  -            result = 1;
  -    }
  -    else if( length >= 3 ) {
  -        if( string[1] == 'i' && string[2] == 'd' )
  -            result = 1;
  -        else if( string[1] == 'd' && string[2] == 'b' )
  -            result = 1;
  -    }
  -
  -    return result;
  -}
  -
  -static int bson_validate_string( bson *b, const unsigned char *string,
  -                                 const size_t length, const char check_utf8, const \
                char check_dot,
  -                                 const char check_dollar ) {
  -
  -    size_t position = 0;
  -    int sequence_length = 1;
  -
  -    if( check_dollar && string[0] == '$' ) {
  -        if( !bson_string_is_db_ref( string, length ) )
  -            b->err |= BSON_FIELD_INIT_DOLLAR;
  -    }
  -
  -    while ( position < length ) {
  -        if ( check_dot && *( string + position ) == '.' ) {
  -            b->err |= BSON_FIELD_HAS_DOT;
  -        }
  -
  -        if ( check_utf8 ) {
  -            sequence_length = trailingBytesForUTF8[*( string + position )] + 1;
  -            if ( ( position + sequence_length ) > length ) {
  -                b->err |= BSON_NOT_UTF8;
  -                return BSON_ERROR;
  -            }
  -            if ( !isLegalUTF8( string + position, sequence_length ) ) {
  -                b->err |= BSON_NOT_UTF8;
  -                return BSON_ERROR;
  -            }
  -        }
  -        position += sequence_length;
  -    }
  -
  -    return BSON_OK;
  -}
  -
  -
  -static int bson_check_string( bson *b, const char *string,
  -                       const size_t length ) {
  -
  -    return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, \
                0 );
  -}
  -
  -static int bson_check_field_name( bson *b, const char *string,
  -                           const size_t length ) {
  -
  -    return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, \
                1 );
  -}
  -
  -/*==============================================================*/
  -/* --- numbers.c */
  -
  -/* all the numbers that fit in a 4 byte string */
  -const char bson_numstrs[1000][4] = {
  -    "0",  "1",  "2",  "3",  "4",  "5",  "6",  "7",  "8",  "9",
  -    "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
  -    "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
  -    "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
  -    "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
  -    "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
  -    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
  -    "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
  -    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
  -    "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
  -
  -    "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
  -    "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
  -    "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
  -    "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
  -    "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
  -    "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
  -    "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
  -    "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
  -    "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
  -    "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
  -
  -    "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
  -    "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
  -    "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
  -    "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
  -    "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
  -    "250", "251", "252", "253", "254", "255", "256", "257", "258", "259",
  -    "260", "261", "262", "263", "264", "265", "266", "267", "268", "269",
  -    "270", "271", "272", "273", "274", "275", "276", "277", "278", "279",
  -    "280", "281", "282", "283", "284", "285", "286", "287", "288", "289",
  -    "290", "291", "292", "293", "294", "295", "296", "297", "298", "299",
  -
  -    "300", "301", "302", "303", "304", "305", "306", "307", "308", "309",
  -    "310", "311", "312", "313", "314", "315", "316", "317", "318", "319",
  -    "320", "321", "322", "323", "324", "325", "326", "327", "328", "329",
  -    "330", "331", "332", "333", "334", "335", "336", "337", "338", "339",
  -    "340", "341", "342", "343", "344", "345", "346", "347", "348", "349",
  -    "350", "351", "352", "353", "354", "355", "356", "357", "358", "359",
  -    "360", "361", "362", "363", "364", "365", "366", "367", "368", "369",
  -    "370", "371", "372", "373", "374", "375", "376", "377", "378", "379",
  -    "380", "381", "382", "383", "384", "385", "386", "387", "388", "389",
  -    "390", "391", "392", "393", "394", "395", "396", "397", "398", "399",
  -
  -    "400", "401", "402", "403", "404", "405", "406", "407", "408", "409",
  -    "410", "411", "412", "413", "414", "415", "416", "417", "418", "419",
  -    "420", "421", "422", "423", "424", "425", "426", "427", "428", "429",
  -    "430", "431", "432", "433", "434", "435", "436", "437", "438", "439",
  -    "440", "441", "442", "443", "444", "445", "446", "447", "448", "449",
  -    "450", "451", "452", "453", "454", "455", "456", "457", "458", "459",
  -    "460", "461", "462", "463", "464", "465", "466", "467", "468", "469",
  -    "470", "471", "472", "473", "474", "475", "476", "477", "478", "479",
  -    "480", "481", "482", "483", "484", "485", "486", "487", "488", "489",
  -    "490", "491", "492", "493", "494", "495", "496", "497", "498", "499",
  -
  -    "500", "501", "502", "503", "504", "505", "506", "507", "508", "509",
  -    "510", "511", "512", "513", "514", "515", "516", "517", "518", "519",
  -    "520", "521", "522", "523", "524", "525", "526", "527", "528", "529",
  -    "530", "531", "532", "533", "534", "535", "536", "537", "538", "539",
  -    "540", "541", "542", "543", "544", "545", "546", "547", "548", "549",
  -    "550", "551", "552", "553", "554", "555", "556", "557", "558", "559",
  -    "560", "561", "562", "563", "564", "565", "566", "567", "568", "569",
  -    "570", "571", "572", "573", "574", "575", "576", "577", "578", "579",
  -    "580", "581", "582", "583", "584", "585", "586", "587", "588", "589",
  -    "590", "591", "592", "593", "594", "595", "596", "597", "598", "599",
  -
  -    "600", "601", "602", "603", "604", "605", "606", "607", "608", "609",
  -    "610", "611", "612", "613", "614", "615", "616", "617", "618", "619",
  -    "620", "621", "622", "623", "624", "625", "626", "627", "628", "629",
  -    "630", "631", "632", "633", "634", "635", "636", "637", "638", "639",
  -    "640", "641", "642", "643", "644", "645", "646", "647", "648", "649",
  -    "650", "651", "652", "653", "654", "655", "656", "657", "658", "659",
  -    "660", "661", "662", "663", "664", "665", "666", "667", "668", "669",
  -    "670", "671", "672", "673", "674", "675", "676", "677", "678", "679",
  -    "680", "681", "682", "683", "684", "685", "686", "687", "688", "689",
  -    "690", "691", "692", "693", "694", "695", "696", "697", "698", "699",
  -
  -    "700", "701", "702", "703", "704", "705", "706", "707", "708", "709",
  -    "710", "711", "712", "713", "714", "715", "716", "717", "718", "719",
  -    "720", "721", "722", "723", "724", "725", "726", "727", "728", "729",
  -    "730", "731", "732", "733", "734", "735", "736", "737", "738", "739",
  -    "740", "741", "742", "743", "744", "745", "746", "747", "748", "749",
  -    "750", "751", "752", "753", "754", "755", "756", "757", "758", "759",
  -    "760", "761", "762", "763", "764", "765", "766", "767", "768", "769",
  -    "770", "771", "772", "773", "774", "775", "776", "777", "778", "779",
  -    "780", "781", "782", "783", "784", "785", "786", "787", "788", "789",
  -    "790", "791", "792", "793", "794", "795", "796", "797", "798", "799",
  -
  -    "800", "801", "802", "803", "804", "805", "806", "807", "808", "809",
  -    "810", "811", "812", "813", "814", "815", "816", "817", "818", "819",
  -    "820", "821", "822", "823", "824", "825", "826", "827", "828", "829",
  -    "830", "831", "832", "833", "834", "835", "836", "837", "838", "839",
  -    "840", "841", "842", "843", "844", "845", "846", "847", "848", "849",
  -    "850", "851", "852", "853", "854", "855", "856", "857", "858", "859",
  -    "860", "861", "862", "863", "864", "865", "866", "867", "868", "869",
  -    "870", "871", "872", "873", "874", "875", "876", "877", "878", "879",
  -    "880", "881", "882", "883", "884", "885", "886", "887", "888", "889",
  -    "890", "891", "892", "893", "894", "895", "896", "897", "898", "899",
  -
  -    "900", "901", "902", "903", "904", "905", "906", "907", "908", "909",
  -    "910", "911", "912", "913", "914", "915", "916", "917", "918", "919",
  -    "920", "921", "922", "923", "924", "925", "926", "927", "928", "929",
  -    "930", "931", "932", "933", "934", "935", "936", "937", "938", "939",
  -    "940", "941", "942", "943", "944", "945", "946", "947", "948", "949",
  -    "950", "951", "952", "953", "954", "955", "956", "957", "958", "959",
  -    "960", "961", "962", "963", "964", "965", "966", "967", "968", "969",
  -    "970", "971", "972", "973", "974", "975", "976", "977", "978", "979",
  -    "980", "981", "982", "983", "984", "985", "986", "987", "988", "989",
  -    "990", "991", "992", "993", "994", "995", "996", "997", "998", "999",
  -};
  +/* free the buffer */
  +void yajl_buf_free(yajl_buf buf);
  +
  +/* append a number of bytes to the buffer */
  +void yajl_buf_append(yajl_buf buf, const void * data, size_t len);
  +
  +/* empty the buffer */
  +void yajl_buf_clear(yajl_buf buf);
  +
  +/* get a pointer to the beginning of the buffer */
  +const unsigned char * yajl_buf_data(yajl_buf buf);
  +
  +/* get the length of the buffer */
  +size_t yajl_buf_len(yajl_buf buf);
  +
  +/* truncate the buffer */
  +void yajl_buf_truncate(yajl_buf buf, size_t len);
   
   /*==============================================================*/
  +/* --- yajl_gen.h */
   
  -/* Static data to use with bson_init_empty( ) and bson_shared_empty( ) */
  -static char bson_shared_empty_data[] = {5,0,0,0,0};
  +/**
  + * Interface to YAJL's JSON generation facilities.
  + */
   
  -/* Custom standard function pointers. */
  -void *( *bson_malloc_func )( size_t ) = malloc;
  -void *( *bson_realloc_func )( void *, size_t ) = realloc;
  -void  ( *bson_free_func )( void * ) = free;
  -#ifdef R_SAFETY_NET
  -bson_printf_func bson_printf;
  -#else
  -bson_printf_func bson_printf = printf;
  +#ifdef __cplusplus
  +extern "C" {
   #endif
  -bson_fprintf_func bson_fprintf = fprintf;
  -bson_sprintf_func bson_sprintf = sprintf;
   
  -static int _bson_errprintf( const char *, ... );
  -bson_printf_func bson_errprintf = _bson_errprintf;
  +    /** generator status codes */
  +    typedef enum {
  +        /** no error */
  +        yajl_gen_status_ok = 0,
  +        /** at a point where a map key is generated, a function other than
  +         *  yajl_gen_string was called */
  +        yajl_gen_keys_must_be_strings,
  +        /** YAJL's maximum generation depth was exceeded.  see
  +         *  YAJL_MAX_DEPTH */
  +        yajl_max_depth_exceeded,
  +        /** A generator function (yajl_gen_XXX) was called while in an error
  +         *  state */
  +        yajl_gen_in_error_state,
  +        /** A complete JSON document has been generated */
  +        yajl_gen_generation_complete,                
  +        /** yajl_gen_double was passed an invalid floating point value
  +         *  (infinity or NaN). */
  +        yajl_gen_invalid_number,
  +        /** A print callback was passed in, so there is no internal
  +         * buffer to get from */
  +        yajl_gen_no_buf,
  +        /** returned from yajl_gen_string() when the yajl_gen_validate_utf8
  +         *  option is enabled and an invalid was passed by client code.
  +         */
  +        yajl_gen_invalid_string
  +    } yajl_gen_status;
  +
  +    /** an opaque handle to a generator */
  +    typedef struct yajl_gen_t * yajl_gen;
  +
  +    /** a callback used for "printing" the results. */
  +    typedef void (*yajl_print_t)(void * ctx,
  +                                 const char * str,
  +                                 size_t len);
  +
  +    /** configuration parameters for the parser, these may be passed to
  +     *  yajl_gen_config() along with option specific argument(s).  In general,
  +     *  all configuration parameters default to *off*. */
  +    typedef enum {
  +        /** generate indented (beautiful) output */
  +        yajl_gen_beautify = 0x01,
  +        /**
  +         * Set an indent string which is used when yajl_gen_beautify
  +         * is enabled.  Maybe something like \\t or some number of
  +         * spaces.  The default is four spaces ' '.
  +         */
  +        yajl_gen_indent_string = 0x02,
  +        /**
  +         * Set a function and context argument that should be used to
  +         * output generated json.  the function should conform to the
  +         * yajl_print_t prototype while the context argument is a
  +         * void * of your choosing.
  +         *
  +         * example:
  +         *   yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr);
  +         */
  +        yajl_gen_print_callback = 0x04,
  +        /**
  +         * Normally the generator does not validate that strings you
  +         * pass to it via yajl_gen_string() are valid UTF8.  Enabling
  +         * this option will cause it to do so.
  +         */
  +        yajl_gen_validate_utf8 = 0x08,
  +        /**
  +         * the forward solidus (slash or '/' in human) is not required to be
  +         * escaped in json text.  By default, YAJL will not escape it in the
  +         * iterest of saving bytes.  Setting this flag will cause YAJL to
  +         * always escape '/' in generated JSON strings.
  +         */
  +        yajl_gen_escape_solidus = 0x10
  +    } yajl_gen_option;
  +
  +    /** allow the modification of generator options subsequent to handle
  +     *  allocation (via yajl_alloc)
  +     *  \returns zero in case of errors, non-zero otherwise
  +     */
  +    YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...);
  +
  +    /** allocate a generator handle
  +     *  \param allocFuncs an optional pointer to a structure which allows
  +     *                    the client to overide the memory allocation
  +     *                    used by yajl.  May be NULL, in which case
  +     *                    malloc/free/realloc will be used.
  +     *
  +     *  \returns an allocated handle on success, NULL on failure (bad params)
  +     */
  +    YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs);
  +
  +    /** free a generator handle */
  +    YAJL_API void yajl_gen_free(yajl_gen handle);
  +
  +    YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long long int \
number);  +    /** generate a floating point number.  number may not be infinity or
  +     *  NaN, as these have no representation in JSON.  In these cases the
  +     *  generator will return 'yajl_gen_invalid_number' */
  +    YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number);
  +    YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand,
  +                                             const char * num,
  +                                             size_t len);
  +    YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand,
  +                                             const unsigned char * str,
  +                                             size_t len);
  +    YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand);
  +    YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean);
  +    YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand);
  +    YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand);
  +    YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand);
  +    YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand);
  +
  +    /** access the null terminated generator buffer.  If incrementally
  +     *  outputing JSON, one should call yajl_gen_clear to clear the
  +     *  buffer.  This allows stream generation. */
  +    YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand,
  +                                              const unsigned char ** buf,
  +                                              size_t * len);
  +
  +    /** clear yajl's output buffer, but maintain all internal generation
  +     *  state.  This function will not "reset" the generator state, and is
  +     *  intended to enable incremental JSON outputing. */
  +    YAJL_API void yajl_gen_clear(yajl_gen hand);
   
  -static void _bson_zero( bson *b );
  -static size_t _bson_position( const bson *b );
  +#ifdef __cplusplus
  +}
  +#endif    
   
  -/* ObjectId fuzz functions. */
  -static int ( *oid_fuzz_func )( void ) = NULL;
  -static int ( *oid_inc_func )( void )  = NULL;
  +/*==============================================================*/
  +/* --- yajl_encode.h */
   
  -/* ----------------------------
  -   READING
  -   ------------------------------ */
  +#ifdef __cplusplus
  +extern "C" {
  +#endif
   
  -MONGO_EXPORT void bson_init_zero(bson* b) {
  -    memset(b, 0, sizeof(bson) - sizeof(b->stack));
  -}
  +void yajl_string_encode(const yajl_print_t printer,
  +                        void * ctx,
  +                        const unsigned char * str,
  +                        size_t length,
  +                        int escape_solidus);
   
  -MONGO_EXPORT bson* bson_alloc( void ) {
  -    return ( bson* )bson_malloc( sizeof( bson ) );
  -}
  +void yajl_string_decode(yajl_buf buf, const unsigned char * str,
  +                        size_t length);
   
  -MONGO_EXPORT void bson_dealloc( bson* b ) {
  -    bson_free( b );
  -}
  +int yajl_string_validate_utf8(const unsigned char * s, size_t len);
   
  -/* When passed a char * of a BSON data block, returns its reported size */
  -static int bson_finished_data_size( const char *data ) {
  -    int i;
  -    bson_little_endian32( &i, data );
  -    return i;
  +#ifdef __cplusplus
   }
  +#endif    
   
  -int bson_init_finished_data( bson *b, char *data, bson_bool_t ownsData ) {
  -    _bson_zero( b );
  -    b->data = data;
  -    b->dataSize = bson_finished_data_size( data );
  -    b->ownsData = ownsData;
  -    b->finished = 1;
  -    return BSON_OK;
  -}
  +/*==============================================================*/
  +/* --- yajl_bytestack.h */
   
  -int bson_init_finished_data_with_copy( bson *b, const char *data ) {
  -    int dataSize = bson_finished_data_size( data );
  -    if ( bson_init_size( b, dataSize ) == BSON_ERROR ) return BSON_ERROR;
  -    memcpy( b->data, data, dataSize );
  -    b->finished = 1;
  -    return BSON_OK;
  -}
  +/*
  + * A header only implementation of a simple stack of bytes, used in YAJL
  + * to maintain parse state.
  + */
   
  -MONGO_EXPORT bson_bool_t bson_init_empty( bson *obj ) {
  -    bson_init_finished_data( obj, bson_shared_empty_data, 0 );
  -    return BSON_OK;
  -}
  +#define YAJL_BS_INC 128
   
  -MONGO_EXPORT const bson *bson_shared_empty( void ) {
  -    static const bson shared_empty = { bson_shared_empty_data, \
                bson_shared_empty_data, 128, 1, 0 };
  -    return &shared_empty;
  +typedef struct yajl_bytestack_t
  +{
  +    unsigned char * stack;
  +    size_t size;
  +    size_t used;
  +    yajl_alloc_funcs * yaf;
  +} yajl_bytestack;
  +
  +/* initialize a bytestack */
  +#define yajl_bs_init(obs, _yaf) {               \
  +        (obs).stack = NULL;                     \
  +        (obs).size = 0;                         \
  +        (obs).used = 0;                         \
  +        (obs).yaf = (_yaf);                     \
  +    }                                           \
  +
  +
  +/* initialize a bytestack */
  +#define yajl_bs_free(obs)                 \
  +    if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack);
  +
  +#define yajl_bs_current(obs)               \
  +    (assert((obs).used > 0), (obs).stack[(obs).used - 1])
  +
  +#define yajl_bs_push(obs, byte) {                       \
  +    if (((obs).size - (obs).used) == 0) {               \
  +        (obs).size += YAJL_BS_INC;                      \
  +        (obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\
  +                                         (void *) (obs).stack, (obs).size);\
  +    }                                                   \
  +    (obs).stack[((obs).used)++] = (byte);               \
   }
   
  -MONGO_EXPORT int bson_copy( bson *out, const bson *in ) {
  -    if ( !out || !in ) return BSON_ERROR;
  -    if ( !in->finished ) return BSON_ERROR;
  -    return bson_init_finished_data_with_copy( out, in->data );
  -}
  +/* removes the top item of the stack, returns nothing */
  +#define yajl_bs_pop(obs) { ((obs).used)--; }
   
  -MONGO_EXPORT int bson_size( const bson *b ) {
  -    int i;
  -    if ( ! b || ! b->data )
  -        return 0;
  -    bson_little_endian32( &i, b->data );
  -    return i;
  -}
  +#define yajl_bs_set(obs, byte)                          \
  +    (obs).stack[((obs).used) - 1] = (byte);
   
  -static size_t _bson_position( const bson *b ) {
  -    return b->cur - b->data;
  -}
  +/*==============================================================*/
  +/* --- yajl_lex.h */
   
  -MONGO_EXPORT size_t bson_buffer_size( const bson *b ) {
  -    return _bson_position(b) + 1;
  -}
  +typedef enum {
  +    yajl_tok_bool,         
  +    yajl_tok_colon,
  +    yajl_tok_comma,     
  +    yajl_tok_eof,
  +    yajl_tok_error,
  +    yajl_tok_left_brace,     
  +    yajl_tok_left_bracket,
  +    yajl_tok_null,         
  +    yajl_tok_right_brace,     
  +    yajl_tok_right_bracket,
  +
  +    /* we differentiate between integers and doubles to allow the
  +     * parser to interpret the number without re-scanning */
  +    yajl_tok_integer, 
  +    yajl_tok_double, 
  +
  +    /* we differentiate between strings which require further processing,
  +     * and strings that do not */
  +    yajl_tok_string,
  +    yajl_tok_string_with_escapes,
  +
  +    /* comment tokens are not currently returned to the parser, ever */
  +    yajl_tok_comment
  +} yajl_tok;
  +
  +typedef struct yajl_lexer_t * yajl_lexer;
  +
  +yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc,
  +                          unsigned int allowComments,
  +                          unsigned int validateUTF8);
   
  +void yajl_lex_free(yajl_lexer lexer);
   
  -MONGO_EXPORT const char *bson_data( const bson *b ) {
  -    return (const char *)b->data;
  -}
  +/**
  + * run/continue a lex. "offset" is an input/output parameter.
  + * It should be initialized to zero for a
  + * new chunk of target text, and upon subsetquent calls with the same
  + * target text should passed with the value of the previous invocation.
  + *
  + * the client may be interested in the value of offset when an error is
  + * returned from the lexer.  This allows the client to render useful
  +n * error messages.
  + *
  + * When you pass the next chunk of data, context should be reinitialized
  + * to zero.
  + * 
  + * Finally, the output buffer is usually just a pointer into the jsonText,
  + * however in cases where the entity being lexed spans multiple chunks,
  + * the lexer will buffer the entity and the data returned will be
  + * a pointer into that buffer.
  + *
  + * This behavior is abstracted from client code except for the performance
  + * implications which require that the client choose a reasonable chunk
  + * size to get adequate performance.
  + */
  +yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
  +                      size_t jsonTextLen, size_t * offset,
  +                      const unsigned char ** outBuf, size_t * outLen);
  +
  +/** have a peek at the next token, but don't move the lexer forward */
  +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText,
  +                       size_t jsonTextLen, size_t offset);
  +
  +
  +typedef enum {
  +    yajl_lex_e_ok = 0,
  +    yajl_lex_string_invalid_utf8,
  +    yajl_lex_string_invalid_escaped_char,
  +    yajl_lex_string_invalid_json_char,
  +    yajl_lex_string_invalid_hex_char,
  +    yajl_lex_invalid_char,
  +    yajl_lex_invalid_string,
  +    yajl_lex_missing_integer_after_decimal,
  +    yajl_lex_missing_integer_after_exponent,
  +    yajl_lex_missing_integer_after_minus,
  +    yajl_lex_unallowed_comment
  +} yajl_lex_error;
  +
  +const char * yajl_lex_error_to_string(yajl_lex_error error);
  +
  +/** allows access to more specific information about the lexical
  + *  error when yajl_lex_lex returns yajl_tok_error. */
  +yajl_lex_error yajl_lex_get_error(yajl_lexer lexer);
  +
  +/** get the current offset into the most recently lexed json string. */
  +size_t yajl_lex_current_offset(yajl_lexer lexer);
  +
  +/** get the number of lines lexed by this lexer instance */
  +size_t yajl_lex_current_line(yajl_lexer lexer);
  +
  +/** get the number of chars lexed by this lexer instance since the last
  + *  \n or \r */
  +size_t yajl_lex_current_char(yajl_lexer lexer);
   
  -static char hexbyte( char hex ) {
  -    if (hex >= '0' && hex <= '9')
  -        return (hex - '0');
  -    else if (hex >= 'A' && hex <= 'F')
  -        return (hex - 'A' + 10);
  -    else if (hex >= 'a' && hex <= 'f')
  -        return (hex - 'a' + 10);
  -    else
  -        return 0x0;
  -}
  +/*==============================================================*/
  +/* --- yajl_tree.h */
   
  -MONGO_EXPORT void bson_oid_from_string( bson_oid_t *oid, const char *str ) {
  -    int i;
  -    for ( i=0; i<12; i++ ) {
  -        oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] );
  -    }
  -}
  +/**
  + *
  + * Parses JSON data and returns the data in tree form.
  + *
  + * \author Florian Forster
  + * \date August 2010
  + *
  + * This interface makes quick parsing and extraction of
  + * smallish JSON docs trivial:
  + *
  + * include example/parse_config.c
  + */
   
  -MONGO_EXPORT void bson_oid_to_string( const bson_oid_t *oid, char *str ) {
  -    static const char hex[16] = \
                {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
  -    int i;
  -    for ( i=0; i<12; i++ ) {
  -        str[2*i]     = hex[( oid->bytes[i] & 0xf0 ) >> 4];
  -        str[2*i + 1] = hex[ oid->bytes[i] & 0x0f      ];
  -    }
  -    str[24] = '\0';
  -}
  +#ifdef __cplusplus
  +extern "C" {
  +#endif
   
  -MONGO_EXPORT void bson_set_oid_fuzz( int ( *func )( void ) ) {
  -    oid_fuzz_func = func;
  -}
  +/** possible data types that a yajl_val_s can hold */
  +typedef enum {
  +    yajl_t_string = 1,
  +    yajl_t_number = 2,
  +    yajl_t_object = 3,
  +    yajl_t_array = 4,
  +    yajl_t_true = 5,
  +    yajl_t_false = 6,
  +    yajl_t_null = 7,
  +    /** The any type isn't valid for yajl_val_s.type, but can be
  +     *  used as an argument to routines like yajl_tree_get().
  +     */
  +    yajl_t_any = 8
  +} yajl_type;
   
  -MONGO_EXPORT void bson_set_oid_inc( int ( *func )( void ) ) {
  -    oid_inc_func = func;
  -}
  +#define YAJL_NUMBER_INT_VALID    0x01
  +#define YAJL_NUMBER_DOUBLE_VALID 0x02
   
  -MONGO_EXPORT void bson_oid_gen( bson_oid_t *oid ) {
  -    static int incr = 0;
  -    static int fuzz = 0;
  -    int i;
  -    time_t t = time( NULL );
  +/** A pointer to a node in the parse tree */
  +typedef struct yajl_val_s * yajl_val;
   
  -    if( oid_inc_func )
  -        i = oid_inc_func();
  -    else
  -        i = incr++;
  +/**
  + * A JSON value representation capable of holding one of the seven
  + * types above. For "string", "number", "object", and "array"
  + * additional data is available in the union.  The "YAJL_IS_*"
  + * and "YAJL_GET_*" macros below allow type checking and convenient
  + * value extraction.
  + */
  +struct yajl_val_s
  +{
  +    /** Type of the value contained. Use the "YAJL_IS_*" macors to check for a
  +     * specific type. */
  +    yajl_type type;
  +    /** Type-specific data. You may use the "YAJL_GET_*" macros to access these
  +     * members. */
  +    union
  +    {
  +        char * string;
  +        struct {
  +            long long i; /*< integer value, if representable. */
  +            double  d;   /*< double value, if representable. */
  +            /** Signals whether the \em i and \em d members are
  +             * valid. See \c YAJL_NUMBER_INT_VALID and
  +             * \c YAJL_NUMBER_DOUBLE_VALID. */
  +            char   *r;   /*< unparsed number in string form. */
  +            unsigned int flags;
  +        } number;
  +        struct {
  +            const char **keys; /*< Array of keys */
  +            yajl_val *values; /*< Array of values. */
  +            size_t len; /*< Number of key-value-pairs. */
  +        } object;
  +        struct {
  +            yajl_val *values; /*< Array of elements. */
  +            size_t len; /*< Number of elements. */
  +        } array;
  +    } u;
  +};
   
  -    if ( !fuzz ) {
  -        if ( oid_fuzz_func )
  -            fuzz = oid_fuzz_func();
  -        else {
  -            srand( ( int )t );
  -            fuzz = rand();
  -        }
  -    }
  +/**
  + * Parse a string.
  + *
  + * Parses an null-terminated string containing JSON data and returns a pointer
  + * to the top-level value (root of the parse tree).
  + *
  + * \param input              Pointer to a null-terminated utf8 string containing
  + *                           JSON data.
  + * \param error_buffer       Pointer to a buffer in which an error message will
  + *                           be stored if \em yajl_tree_parse fails, or
  + *                           \c NULL. The buffer will be initialized before
  + *                           parsing, so its content will be destroyed even if
  + *                           \em yajl_tree_parse succeeds.
  + * \param error_buffer_size  Size of the memory area pointed to by
  + *                           \em error_buffer_size. If \em error_buffer_size is
  + *                           \c NULL, this argument is ignored.
  + *
  + * \returns Pointer to the top-level value or \c NULL on error. The memory
  + * pointed to must be freed using \em yajl_tree_free. In case of an error, a
  + * null terminated message describing the error in more detail is stored in
  + * \em error_buffer if it is not \c NULL.
  + */
  +YAJL_API yajl_val yajl_tree_parse (const char *input,
  +                                   char *error_buffer, size_t error_buffer_size);
   
  -    bson_big_endian32( &oid->ints[0], &t );
  -    oid->ints[1] = fuzz;
  -    bson_big_endian32( &oid->ints[2], &i );
  -}
  +/**
  + * Free a parse tree returned by "yajl_tree_parse".
  + *
  + * \param v Pointer to a JSON value returned by "yajl_tree_parse". Passing NULL
  + * is valid and results in a no-op.
  + */
  +YAJL_API void yajl_tree_free (yajl_val v);
  +
  +/**
  + * Access a nested value inside a tree.
  + *
  + * \param parent the node under which you'd like to extract values.
  + * \param path A null terminated array of strings, each the name of an object key
  + * \param type the yajl_type of the object you seek, or yajl_t_any if any will do.
  + *
  + * \returns a pointer to the found value, or NULL if we came up empty.
  + * 
  + * Future Ideas:  it'd be nice to move path to a string and implement support for
  + * a teeny tiny micro language here, so you can extract array elements, do things
  + * like .first and .last, even .length.  Inspiration from JSONPath and css \
selectors?  + * No it wouldn't be fast, but that's not what this API is about.
  + */
  +YAJL_API yajl_val yajl_tree_get(yajl_val parent, const char ** path, yajl_type \
type);  
  -MONGO_EXPORT time_t bson_oid_generated_time( bson_oid_t *oid ) {
  -    time_t out = 0;
  -    bson_big_endian32( &out, &oid->ints[0] );
  +/* Various convenience macros to check the type of a `yajl_val` */
  +#define YAJL_IS_STRING(v) (((v) != NULL) && ((v)->type == yajl_t_string))
  +#define YAJL_IS_NUMBER(v) (((v) != NULL) && ((v)->type == yajl_t_number))
  +#define YAJL_IS_INTEGER(v) (YAJL_IS_NUMBER(v) && ((v)->u.number.flags & \
YAJL_NUMBER_INT_VALID))  +#define YAJL_IS_DOUBLE(v) (YAJL_IS_NUMBER(v) && \
((v)->u.number.flags & YAJL_NUMBER_DOUBLE_VALID))  +#define YAJL_IS_OBJECT(v) (((v) \
!= NULL) && ((v)->type == yajl_t_object))  +#define YAJL_IS_ARRAY(v)  (((v) != NULL) \
&& ((v)->type == yajl_t_array ))  +#define YAJL_IS_TRUE(v)   (((v) != NULL) && \
((v)->type == yajl_t_true  ))  +#define YAJL_IS_FALSE(v)  (((v) != NULL) && \
((v)->type == yajl_t_false ))  +#define YAJL_IS_NULL(v)   (((v) != NULL) && \
((v)->type == yajl_t_null  ))  +
  +/** Given a yajl_val_string return a ptr to the bare string it contains,
  + *  or NULL if the value is not a string. */
  +#define YAJL_GET_STRING(v) (YAJL_IS_STRING(v) ? (v)->u.string : NULL)
  +
  +/** Get the string representation of a number.  You should check type first,
  + *  perhaps using YAJL_IS_NUMBER */
  +#define YAJL_GET_NUMBER(v) ((v)->u.number.r)
  +
  +/** Get the double representation of a number.  You should check type first,
  + *  perhaps using YAJL_IS_DOUBLE */
  +#define YAJL_GET_DOUBLE(v) ((v)->u.number.d)
  +
  +/** Get the 64bit (long long) integer representation of a number.  You should
  + *  check type first, perhaps using YAJL_IS_INTEGER */
  +#define YAJL_GET_INTEGER(v) ((v)->u.number.i)
   
  -    return out;
  -}
  +/** Get a pointer to a yajl_val_object or NULL if the value is not an object. */
  +#define YAJL_GET_OBJECT(v) (YAJL_IS_OBJECT(v) ? &(v)->u.object : NULL)
   
  -MONGO_EXPORT void bson_print( const bson *b ) {
  -    bson_print_raw( b->data , 0 );
  +/** Get a pointer to a yajl_val_array or NULL if the value is not an object. */
  +#define YAJL_GET_ARRAY(v)  (YAJL_IS_ARRAY(v)  ? &(v)->u.array  : NULL)
  +
  +#ifdef __cplusplus
   }
  +#endif
   
  -MONGO_EXPORT void bson_print_raw( const char *data , int depth ) {
  -    bson_iterator i;
  -    const char *key;
  -    int temp;
  -    bson_timestamp_t ts;
  -    char oidhex[25];
  -    bson scope;
  -    bson_iterator_from_buffer( &i, data );
  +/*==============================================================*/
  +/* --- yajl_parse.h */
   
  -    while ( bson_iterator_next( &i ) ) {
  -        bson_type t = bson_iterator_type( &i );
  -        if ( t == 0 )
  -            break;
  -        key = bson_iterator_key( &i );
  +/**
  + * Interface to YAJL's JSON stream parsing facilities.
  + */
   
  -        for ( temp=0; temp<=depth; temp++ )
  -            bson_printf( "\t" );
  -        bson_printf( "%s : %d \t " , key , t );
  -        switch ( t ) {
  -        case BSON_DOUBLE:
  -            bson_printf( "%f" , bson_iterator_double( &i ) );
  -            break;
  -        case BSON_STRING:
  -            bson_printf( "%s" , bson_iterator_string( &i ) );
  -            break;
  -        case BSON_SYMBOL:
  -            bson_printf( "SYMBOL: %s" , bson_iterator_string( &i ) );
  -            break;
  -        case BSON_OID:
  -            bson_oid_to_string( bson_iterator_oid( &i ), oidhex );
  -            bson_printf( "%s" , oidhex );
  -            break;
  -        case BSON_BOOL:
  -            bson_printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" );
  -            break;
  -        case BSON_DATE:
  -            bson_printf( "%ld" , ( long int )bson_iterator_date( &i ) );
  -            break;
  -        case BSON_BINDATA:
  -            bson_printf( "BSON_BINDATA" );
  -            break;
  -        case BSON_UNDEFINED:
  -            bson_printf( "BSON_UNDEFINED" );
  -            break;
  -        case BSON_NULL:
  -            bson_printf( "BSON_NULL" );
  -            break;
  -        case BSON_REGEX:
  -            bson_printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) );
  -            break;
  -        case BSON_CODE:
  -            bson_printf( "BSON_CODE: %s", bson_iterator_code( &i ) );
  -            break;
  -        case BSON_CODEWSCOPE:
  -            bson_printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) );
  -            bson_iterator_code_scope_init( &i, &scope, 0 );
  -            bson_printf( "\n\t SCOPE: " );
  -            bson_print( &scope );
  -            bson_destroy( &scope );
  -            break;
  -        case BSON_INT:
  -            bson_printf( "%d" , bson_iterator_int( &i ) );
  -            break;
  -        case BSON_LONG:
  -            bson_printf( "%lld" , ( uint64_t )bson_iterator_long( &i ) );
  -            break;
  -        case BSON_TIMESTAMP:
  -            ts = bson_iterator_timestamp( &i );
  -            bson_printf( "i: %d, t: %d", ts.i, ts.t );
  -            break;
  -        case BSON_OBJECT:
  -        case BSON_ARRAY:
  -            bson_printf( "\n" );
  -            bson_print_raw( bson_iterator_value( &i ) , depth + 1 );
  -            break;
  -        default:
  -            bson_errprintf( "can't print type : %d\n" , t );
  -        }
  -        bson_printf( "\n" );
  -    }
  -}
  +#ifdef __cplusplus
  +extern "C" {
  +#endif
   
  -/* ----------------------------
  -   ITERATOR
  -   ------------------------------ */
  +    /** error codes returned from this interface */
  +    typedef enum {
  +        /** no error was encountered */
  +        yajl_status_ok,
  +        /** a client callback returned zero, stopping the parse */
  +        yajl_status_client_canceled,
  +        /** An error occured during the parse.  Call yajl_get_error for
  +         *  more information about the encountered error */
  +        yajl_status_error
  +    } yajl_status;
  +
  +    /** attain a human readable, english, string for an error */
  +    YAJL_API const char * yajl_status_to_string(yajl_status code);
  +
  +    /** an opaque handle to a parser */
  +    typedef struct yajl_handle_t * yajl_handle;
  +
  +    /** yajl is an event driven parser.  this means as json elements are
  +     *  parsed, you are called back to do something with the data.  The
  +     *  functions in this table indicate the various events for which
  +     *  you will be called back.  Each callback accepts a "context"
  +     *  pointer, this is a void * that is passed into the yajl_parse
  +     *  function which the client code may use to pass around context.
  +     *
  +     *  All callbacks return an integer.  If non-zero, the parse will
  +     *  continue.  If zero, the parse will be canceled and
  +     *  yajl_status_client_canceled will be returned from the parse.
  +     *
  +     *  \attention {
  +     *    A note about the handling of numbers:
  +     *
  +     *    yajl will only convert numbers that can be represented in a
  +     *    double or a 64 bit (long long) int.  All other numbers will
  +     *    be passed to the client in string form using the yajl_number
  +     *    callback.  Furthermore, if yajl_number is not NULL, it will
  +     *    always be used to return numbers, that is yajl_integer and
  +     *    yajl_double will be ignored.  If yajl_number is NULL but one
  +     *    of yajl_integer or yajl_double are defined, parsing of a
  +     *    number larger than is representable in a double or 64 bit
  +     *    integer will result in a parse error.
  +     *  }
  +     */
  +    typedef struct {
  +        int (* yajl_null)(void * ctx);
  +        int (* yajl_boolean)(void * ctx, int boolVal);
  +        int (* yajl_integer)(void * ctx, int64_t integerVal);
  +        int (* yajl_double)(void * ctx, double doubleVal);
  +        /** A callback which passes the string representation of the number
  +         *  back to the client.  Will be used for all numbers when present */
  +        int (* yajl_number)(void * ctx, const char * numberVal,
  +                            size_t numberLen);
  +
  +        /** strings are returned as pointers into the JSON text when,
  +         * possible, as a result, they are _not_ null padded */
  +        int (* yajl_string)(void * ctx, const unsigned char * stringVal,
  +                            size_t stringLen);
  +
  +        int (* yajl_start_map)(void * ctx);
  +        int (* yajl_map_key)(void * ctx, const unsigned char * key,
  +                             size_t stringLen);
  +        int (* yajl_end_map)(void * ctx);
  +
  +        int (* yajl_start_array)(void * ctx);
  +        int (* yajl_end_array)(void * ctx);
  +    } yajl_callbacks;
  +
  +    /** allocate a parser handle
  +     *  \param callbacks  a yajl callbacks structure specifying the
  +     *                    functions to call when different JSON entities
  +     *                    are encountered in the input text.  May be NULL,
  +     *                    which is only useful for validation.
  +     *  \param afs        memory allocation functions, may be NULL for to use
  +     *                    C runtime library routines (malloc and friends)
  +     *  \param ctx        a context pointer that will be passed to callbacks.
  +     */
  +    YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks,
  +                                    yajl_alloc_funcs * afs,
  +                                    void * ctx);
  +
  +
  +    /** configuration parameters for the parser, these may be passed to
  +     *  yajl_config() along with option specific argument(s).  In general,
  +     *  all configuration parameters default to *off*. */
  +    typedef enum {
  +        /** Ignore javascript style comments present in
  +         *  JSON input.  Non-standard, but rather fun
  +         *  arguments: toggled off with integer zero, on otherwise.
  +         *
  +         *  example:
  +         *    yajl_config(h, yajl_allow_comments, 1); // turn comment support on
  +         */
  +        yajl_allow_comments = 0x01,
  +        /**
  +         * When set the parser will verify that all strings in JSON input are
  +         * valid UTF8 and will emit a parse error if this is not so.  When set,
  +         * this option makes parsing slightly more expensive (~7% depending
  +         * on processor and compiler in use)
  +         *
  +         * example:
  +         *   yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 \
checking  +         */
  +        yajl_dont_validate_strings     = 0x02,
  +        /**
  +         * By default, upon calls to yajl_complete_parse(), yajl will
  +         * ensure the entire input text was consumed and will raise an error
  +         * otherwise.  Enabling this flag will cause yajl to disable this
  +         * check.  This can be useful when parsing json out of a that contains \
more  +         * than a single JSON document.
  +         */
  +        yajl_allow_trailing_garbage = 0x04,
  +        /**
  +         * Allow multiple values to be parsed by a single handle.  The
  +         * entire text must be valid JSON, and values can be seperated
  +         * by any kind of whitespace.  This flag will change the
  +         * behavior of the parser, and cause it continue parsing after
  +         * a value is parsed, rather than transitioning into a
  +         * complete state.  This option can be useful when parsing multiple
  +         * values from an input stream.
  +         */
  +        yajl_allow_multiple_values = 0x08,
  +        /**
  +         * When yajl_complete_parse() is called the parser will
  +         * check that the top level value was completely consumed.  I.E.,
  +         * if called whilst in the middle of parsing a value
  +         * yajl will enter an error state (premature EOF).  Setting this
  +         * flag suppresses that check and the corresponding error.
  +         */
  +        yajl_allow_partial_values = 0x10
  +    } yajl_option;
  +
  +    /** allow the modification of parser options subsequent to handle
  +     *  allocation (via yajl_alloc)
  +     *  \returns zero in case of errors, non-zero otherwise
  +     */
  +    YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...);
  +
  +    /** free a parser handle */
  +    YAJL_API void yajl_free(yajl_handle handle);
  +
  +    /** Parse some json!
  +     *  \param hand - a handle to the json parser allocated with yajl_alloc
  +     *  \param jsonText - a pointer to the UTF8 json text to be parsed
  +     *  \param jsonTextLength - the length, in bytes, of input text
  +     */
  +    YAJL_API yajl_status yajl_parse(yajl_handle hand,
  +                                    const unsigned char * jsonText,
  +                                    size_t jsonTextLength);
  +
  +    /** Parse any remaining buffered json.
  +     *  Since yajl is a stream-based parser, without an explicit end of
  +     *  input, yajl sometimes can't decide if content at the end of the
  +     *  stream is valid or not.  For example, if "1" has been fed in,
  +     *  yajl can't know whether another digit is next or some character
  +     *  that would terminate the integer token.
  +     *
  +     *  \param hand - a handle to the json parser allocated with yajl_alloc
  +     */
  +    YAJL_API yajl_status yajl_complete_parse(yajl_handle hand);
  +
  +    /** get an error string describing the state of the
  +     *  parse.
  +     *
  +     *  If verbose is non-zero, the message will include the JSON
  +     *  text where the error occured, along with an arrow pointing to
  +     *  the specific char.
  +     *
  +     *  \returns A dynamically allocated string will be returned which should
  +     *  be freed with yajl_free_error
  +     */
  +    YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose,
  +                                            const unsigned char * jsonText,
  +                                            size_t jsonTextLength);
  +
  +    /**
  +     * get the amount of data consumed from the last chunk passed to YAJL.
  +     *
  +     * In the case of a successful parse this can help you understand if
  +     * the entire buffer was consumed (which will allow you to handle
  +     * "junk at end of input").
  +     *
  +     * In the event an error is encountered during parsing, this function
  +     * affords the client a way to get the offset into the most recent
  +     * chunk where the error occured.  0 will be returned if no error
  +     * was encountered.
  +     */
  +    YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand);
   
  -MONGO_EXPORT bson_iterator* bson_iterator_alloc( void ) {
  -    return ( bson_iterator* )bson_malloc( sizeof( bson_iterator ) );
  -}
  +    /** free an error returned from yajl_get_error */
  +    YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str);
   
  -MONGO_EXPORT void bson_iterator_dealloc( bson_iterator* i ) {
  -    bson_free( i );
  +#ifdef __cplusplus
   }
  +#endif
  +
  +/*==============================================================*/
  +/* --- yajl_parser.h */
  +
  +typedef enum {
  +    yajl_state_start = 0,
  +    yajl_state_parse_complete,
  +    yajl_state_parse_error,
  +    yajl_state_lexical_error,
  +    yajl_state_map_start,
  +    yajl_state_map_sep,
  +    yajl_state_map_need_val,
  +    yajl_state_map_got_val,
  +    yajl_state_map_need_key,
  +    yajl_state_array_start,
  +    yajl_state_array_got_val,
  +    yajl_state_array_need_val,
  +    yajl_state_got_value,
  +} yajl_state;
  +
  +struct yajl_handle_t {
  +    const yajl_callbacks * callbacks;
  +    void * ctx;
  +    yajl_lexer lexer;
  +    const char * parseError;
  +    /* the number of bytes consumed from the last client buffer,
  +     * in the case of an error this will be an error offset, in the
  +     * case of an error this can be used as the error offset */
  +    size_t bytesConsumed;
  +    /* temporary storage for decoded strings */
  +    yajl_buf decodeBuf;
  +    /* a stack of states.  access with yajl_state_XXX routines */
  +    yajl_bytestack stateStack;
  +    /* memory allocation routines */
  +    yajl_alloc_funcs alloc;
  +    /* bitfield */
  +    unsigned int flags;
  +};
  +
  +yajl_status
  +yajl_do_parse(yajl_handle handle, const unsigned char * jsonText,
  +              size_t jsonTextLen);
  +
  +yajl_status
  +yajl_do_finish(yajl_handle handle);
  +
  +unsigned char *
  +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
  +                         size_t jsonTextLen, int verbose);
  +
  +/* A little built in integer parsing routine with the same semantics as strtol
  + * that's unaffected by LOCALE. */
  +int64_t
  +yajl_parse_integer(const unsigned char *number, unsigned int length);
   
  -MONGO_EXPORT void bson_iterator_init( bson_iterator *i, const bson *b ) {
  -    i->cur = b->data + 4;
  -    i->first = 1;
  +/*==============================================================*/
  +/* --- yajl_alloc.c */
  +
  +static void * yajl_internal_malloc(void *ctx, size_t sz)
  +{
  +    return malloc(sz);
   }
   
  -MONGO_EXPORT void bson_iterator_from_buffer( bson_iterator *i, const char *buffer \
                ) {
  -    i->cur = buffer + 4;
  -    i->first = 1;
  +static void * yajl_internal_realloc(void *ctx, void * previous,
  +                                    size_t sz)
  +{
  +    return realloc(previous, sz);
   }
   
  -MONGO_EXPORT bson_type bson_find( bson_iterator *it, const bson *obj, const char \
                *name ) {
  -    bson_iterator_init( it, (bson *)obj );
  -    while( bson_iterator_next( it ) ) {
  -        if ( strcmp( name, bson_iterator_key( it ) ) == 0 )
  -            break;
  -    }
  -    return bson_iterator_type( it );
  +static void yajl_internal_free(void *ctx, void * ptr)
  +{
  +    free(ptr);
   }
   
  -MONGO_EXPORT bson_bool_t bson_iterator_more( const bson_iterator *i ) {
  -    return *( i->cur );
  +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf)
  +{
  +    yaf->malloc = yajl_internal_malloc;
  +    yaf->free = yajl_internal_free;
  +    yaf->realloc = yajl_internal_realloc;
  +    yaf->ctx = NULL;
   }
   
  -MONGO_EXPORT bson_type bson_iterator_next( bson_iterator *i ) {
  -    size_t ds;
  +/*==============================================================*/
  +/* --- yajl_buf.c */
   
  -    if ( i->first ) {
  -        i->first = 0;
  -        return ( bson_type )( *i->cur );
  -    }
  +#define YAJL_BUF_INIT_SIZE 2048
   
  -    switch ( bson_iterator_type( i ) ) {
  -    case BSON_EOO:
  -        return BSON_EOO; /* don't advance */
  -    case BSON_UNDEFINED:
  -    case BSON_NULL:
  -    case BSON_MINKEY:
  -    case BSON_MAXKEY:
  -        ds = 0;
  -        break;
  -    case BSON_BOOL:
  -        ds = 1;
  -        break;
  -    case BSON_INT:
  -        ds = 4;
  -        break;
  -    case BSON_LONG:
  -    case BSON_DOUBLE:
  -    case BSON_TIMESTAMP:
  -    case BSON_DATE:
  -        ds = 8;
  -        break;
  -    case BSON_OID:
  -        ds = 12;
  -        break;
  -    case BSON_STRING:
  -    case BSON_SYMBOL:
  -    case BSON_CODE:
  -        ds = 4 + bson_iterator_int_raw( i );
  -        break;
  -    case BSON_BINDATA:
  -        ds = 5 + bson_iterator_int_raw( i );
  -        break;
  -    case BSON_OBJECT:
  -    case BSON_ARRAY:
  -    case BSON_CODEWSCOPE:
  -        ds = bson_iterator_int_raw( i );
  -        break;
  -    case BSON_DBREF:
  -        ds = 4+12 + bson_iterator_int_raw( i );
  -        break;
  -    case BSON_REGEX: {
  -        const char *s = bson_iterator_value( i );
  -        const char *p = s;
  -        p += strlen( p )+1;
  -        p += strlen( p )+1;
  -        ds = p-s;
  -        break;
  -    }
  +struct yajl_buf_t {
  +    size_t len;
  +    size_t used;
  +    unsigned char * data;
  +    yajl_alloc_funcs * alloc;
  +};
   
  -    default: {
  -        char msg[] = "unknown type: 000000000000";
  -        bson_numstr( msg+14, ( unsigned )( i->cur[0] ) );
  -        bson_fatal_msg( 0, msg );
  -        return 0;
  -    }
  +static
  +void yajl_buf_ensure_available(yajl_buf buf, size_t want)
  +{
  +    size_t need;
  +    
  +    assert(buf != NULL);
  +
  +    /* first call */
  +    if (buf->data == NULL) {
  +        buf->len = YAJL_BUF_INIT_SIZE;
  +        buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len);
  +        buf->data[0] = 0;
       }
   
  -    i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds;
  +    need = buf->len;
   
  -    return ( bson_type )( *i->cur );
  -}
  +    while (want >= (need - buf->used)) need <<= 1;
   
  -MONGO_EXPORT bson_type bson_iterator_type( const bson_iterator *i ) {
  -    // problem to convert 0xFF to 255
  -    return ( bson_type )( unsigned char )i->cur[0];
  +    if (need != buf->len) {
  +        buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need);
  +        buf->len = need;
  +    }
   }
   
  -MONGO_EXPORT const char *bson_iterator_key( const bson_iterator *i ) {
  -    return i->cur + 1;
  +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc)
  +{
  +    yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t));
  +    memset((void *) b, 0, sizeof(struct yajl_buf_t));
  +    b->alloc = alloc;
  +    return b;
   }
   
  -MONGO_EXPORT const char *bson_iterator_value( const bson_iterator *i ) {
  -    const char *t = i->cur + 1;
  -    t += strlen( t ) + 1;
  -    return t;
  +void yajl_buf_free(yajl_buf buf)
  +{
  +    assert(buf != NULL);
  +    if (buf->data) YA_FREE(buf->alloc, buf->data);
  +    YA_FREE(buf->alloc, buf);
   }
   
  -/* types */
  -
  -int bson_iterator_int_raw( const bson_iterator *i ) {
  -    int out;
  -    bson_little_endian32( &out, bson_iterator_value( i ) );
  -    return out;
  +void yajl_buf_append(yajl_buf buf, const void * data, size_t len)
  +{
  +    yajl_buf_ensure_available(buf, len);
  +    if (len > 0) {
  +        assert(data != NULL);
  +        memcpy(buf->data + buf->used, data, len);
  +        buf->used += len;
  +        buf->data[buf->used] = 0;
  +    }
   }
   
  -double bson_iterator_double_raw( const bson_iterator *i ) {
  -    double out;
  -    bson_little_endian64( &out, bson_iterator_value( i ) );
  -    return out;
  +void yajl_buf_clear(yajl_buf buf)
  +{
  +    buf->used = 0;
  +    if (buf->data) buf->data[buf->used] = 0;
   }
   
  -int64_t bson_iterator_long_raw( const bson_iterator *i ) {
  -    int64_t out;
  -    bson_little_endian64( &out, bson_iterator_value( i ) );
  -    return out;
  +const unsigned char * yajl_buf_data(yajl_buf buf)
  +{
  +    return buf->data;
   }
   
  -bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) {
  -    return bson_iterator_value( i )[0];
  +size_t yajl_buf_len(yajl_buf buf)
  +{
  +    return buf->used;
   }
   
  -MONGO_EXPORT bson_oid_t *bson_iterator_oid( const bson_iterator *i ) {
  -    return ( bson_oid_t * )bson_iterator_value( i );
  +void
  +yajl_buf_truncate(yajl_buf buf, size_t len)
  +{
  +    assert(len <= buf->used);
  +    buf->used = len;
   }
  +/*==============================================================*/
  +/* --- yajl.c */
   
  -MONGO_EXPORT int bson_iterator_int( const bson_iterator *i ) {
  -    switch ( bson_iterator_type( i ) ) {
  -    case BSON_INT:
  -        return bson_iterator_int_raw( i );
  -    case BSON_LONG:
  -        return ( int )bson_iterator_long_raw( i );
  -    case BSON_DOUBLE:
  -        return ( int )bson_iterator_double_raw( i );
  -    default:
  -        return 0;
  +const char *
  +yajl_status_to_string(yajl_status stat)
  +{
  +    const char * statStr = "unknown";
  +    switch (stat) {
  +        case yajl_status_ok:
  +            statStr = "ok, no error";
  +            break;
  +        case yajl_status_client_canceled:
  +            statStr = "client canceled parse";
  +            break;
  +        case yajl_status_error:
  +            statStr = "parse error";
  +            break;
       }
  +    return statStr;
   }
   
  -MONGO_EXPORT double bson_iterator_double( const bson_iterator *i ) {
  -    switch ( bson_iterator_type( i ) ) {
  -    case BSON_INT:
  -        return bson_iterator_int_raw( i );
  -    case BSON_LONG:
  -        return ( double )bson_iterator_long_raw( i );
  -    case BSON_DOUBLE:
  -        return bson_iterator_double_raw( i );
  -    default:
  -        return 0;
  -    }
  -}
  +yajl_handle
  +yajl_alloc(const yajl_callbacks * callbacks,
  +           yajl_alloc_funcs * afs,
  +           void * ctx)
  +{
  +    yajl_handle hand = NULL;
  +    yajl_alloc_funcs afsBuffer;
   
  -MONGO_EXPORT int64_t bson_iterator_long( const bson_iterator *i ) {
  -    switch ( bson_iterator_type( i ) ) {
  -    case BSON_INT:
  -        return bson_iterator_int_raw( i );
  -    case BSON_LONG:
  -        return bson_iterator_long_raw( i );
  -    case BSON_DOUBLE:
  -        return ( int64_t)bson_iterator_double_raw( i );
  -    default:
  -        return 0;
  +    /* first order of business is to set up memory allocation routines */
  +    if (afs != NULL) {
  +        if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
  +        {
  +            return NULL;
  +        }
  +    } else {
  +        yajl_set_default_alloc_funcs(&afsBuffer);
  +        afs = &afsBuffer;
       }
  -}
  -
  -MONGO_EXPORT bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) {
  -    bson_timestamp_t ts;
  -    bson_little_endian32( &( ts.i ), bson_iterator_value( i ) );
  -    bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 );
  -    return ts;
  -}
   
  +    hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t));
   
  -MONGO_EXPORT int bson_iterator_timestamp_time( const bson_iterator *i ) {
  -    int time;
  -    bson_little_endian32( &time, bson_iterator_value( i ) + 4 );
  -    return time;
  -}
  +    /* copy in pointers to allocation routines */
  +    memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
   
  +    hand->callbacks = callbacks;
  +    hand->ctx = ctx;
  +    hand->lexer = NULL; 
  +    hand->bytesConsumed = 0;
  +    hand->decodeBuf = yajl_buf_alloc(&(hand->alloc));
  +    hand->flags	    = 0;
  +    yajl_bs_init(hand->stateStack, &(hand->alloc));
  +    yajl_bs_push(hand->stateStack, yajl_state_start);
   
  -MONGO_EXPORT int bson_iterator_timestamp_increment( const bson_iterator *i ) {
  -    int increment;
  -    bson_little_endian32( &increment, bson_iterator_value( i ) );
  -    return increment;
  +    return hand;
   }
   
  +int
  +yajl_config(yajl_handle h, yajl_option opt, ...)
  +{
  +    int rv = 1;
  +    va_list ap;
  +    va_start(ap, opt);
   
  -MONGO_EXPORT bson_bool_t bson_iterator_bool( const bson_iterator *i ) {
  -    switch ( bson_iterator_type( i ) ) {
  -    case BSON_BOOL:
  -        return bson_iterator_bool_raw( i );
  -    case BSON_INT:
  -        return bson_iterator_int_raw( i ) != 0;
  -    case BSON_LONG:
  -        return bson_iterator_long_raw( i ) != 0;
  -    case BSON_DOUBLE:
  -        return bson_iterator_double_raw( i ) != 0;
  -    case BSON_EOO:
  -    case BSON_NULL:
  -        return 0;
  -    default:
  -        return 1;
  +    switch(opt) {
  +        case yajl_allow_comments:
  +        case yajl_dont_validate_strings:
  +        case yajl_allow_trailing_garbage:
  +        case yajl_allow_multiple_values:
  +        case yajl_allow_partial_values:
  +            if (va_arg(ap, int)) h->flags |= opt;
  +            else h->flags &= ~opt;
  +            break;
  +        default:
  +            rv = 0;
       }
  -}
  +    va_end(ap);
   
  -MONGO_EXPORT const char *bson_iterator_string( const bson_iterator *i ) {
  -    switch ( bson_iterator_type( i ) ) {
  -    case BSON_STRING:
  -    case BSON_SYMBOL:
  -        return bson_iterator_value( i ) + 4;
  -    default:
  -        return "";
  -    }
  +    return rv;
   }
   
  -int bson_iterator_string_len( const bson_iterator *i ) {
  -    return bson_iterator_int_raw( i );
  +void
  +yajl_free(yajl_handle handle)
  +{
  +    yajl_bs_free(handle->stateStack);
  +    yajl_buf_free(handle->decodeBuf);
  +    if (handle->lexer) {
  +        yajl_lex_free(handle->lexer);
  +        handle->lexer = NULL;
  +    }
  +    YA_FREE(&(handle->alloc), handle);
   }
   
  -MONGO_EXPORT const char *bson_iterator_code( const bson_iterator *i ) {
  -    switch ( bson_iterator_type( i ) ) {
  -    case BSON_STRING:
  -    case BSON_CODE:
  -        return bson_iterator_value( i ) + 4;
  -    case BSON_CODEWSCOPE:
  -        return bson_iterator_value( i ) + 8;
  -    default:
  -        return NULL;
  -    }
  -}
  +yajl_status
  +yajl_parse(yajl_handle hand, const unsigned char * jsonText,
  +           size_t jsonTextLen)
  +{
  +    yajl_status status;
   
  -MONGO_EXPORT void bson_iterator_code_scope_init( const bson_iterator *i, bson \
                *scope, bson_bool_t copyData ) {
  -    if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) {
  -        int codeLen = bson_finished_data_size( bson_iterator_value( i )+4 );
  -        const char * scopeData = bson_iterator_value( i )+8+codeLen;
  -        if( copyData )
  -            bson_init_finished_data_with_copy( scope, scopeData );
  -        else
  -            bson_init_finished_data( scope, (char *)scopeData, 0 );
  +    /* lazy allocation of the lexer */
  +    if (hand->lexer == NULL) {
  +        hand->lexer = yajl_lex_alloc(&(hand->alloc),
  +                                     hand->flags & yajl_allow_comments,
  +                                     !(hand->flags & yajl_dont_validate_strings));
       }
  -    else {
  -        bson_init_empty( scope );
  -    }
  -}
   
  -MONGO_EXPORT bson_date_t bson_iterator_date( const bson_iterator *i ) {
  -    return bson_iterator_long_raw( i );
  +    status = yajl_do_parse(hand, jsonText, jsonTextLen);
  +    return status;
   }
   
  -MONGO_EXPORT time_t bson_iterator_time_t( const bson_iterator *i ) {
  -    return bson_iterator_date( i ) / 1000;
  -}
   
  -MONGO_EXPORT int bson_iterator_bin_len( const bson_iterator *i ) {
  -    return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
  -           ? bson_iterator_int_raw( i ) - 4
  -           : bson_iterator_int_raw( i );
  +yajl_status
  +yajl_complete_parse(yajl_handle hand)
  +{
  +    /* The lexer is lazy allocated in the first call to parse.  if parse is
  +     * never called, then no data was provided to parse at all.  This is a
  +     * "premature EOF" error unless yajl_allow_partial_values is specified.
  +     * allocating the lexer now is the simplest possible way to handle this
  +     * case while preserving all the other semantics of the parser
  +     * (multiple values, partial values, etc). */
  +    if (hand->lexer == NULL) {
  +        hand->lexer = yajl_lex_alloc(&(hand->alloc),
  +                                     hand->flags & yajl_allow_comments,
  +                                     !(hand->flags & yajl_dont_validate_strings));
  +    }
  +
  +    return yajl_do_finish(hand);
  +}
  +
  +unsigned char *
  +yajl_get_error(yajl_handle hand, int verbose,
  +               const unsigned char * jsonText, size_t jsonTextLen)
  +{
  +    return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose);
   }
   
  -MONGO_EXPORT char bson_iterator_bin_type( const bson_iterator *i ) {
  -    return bson_iterator_value( i )[4];
  +size_t
  +yajl_get_bytes_consumed(yajl_handle hand)
  +{
  +    if (!hand) return 0;
  +    else return hand->bytesConsumed;
   }
   
  -MONGO_EXPORT const char *bson_iterator_bin_data( const bson_iterator *i ) {
  -    return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
  -           ? bson_iterator_value( i ) + 9
  -           : bson_iterator_value( i ) + 5;
  -}
   
  -MONGO_EXPORT const char *bson_iterator_regex( const bson_iterator *i ) {
  -    return bson_iterator_value( i );
  +void
  +yajl_free_error(yajl_handle hand, unsigned char * str)
  +{
  +    /* use memory allocation functions if set */
  +    YA_FREE(&(hand->alloc), str);
   }
   
  -MONGO_EXPORT const char *bson_iterator_regex_opts( const bson_iterator *i ) {
  -    const char *p = bson_iterator_value( i );
  -    return p + strlen( p ) + 1;
  +/* XXX: add utility routines to parse from file */
  +/*==============================================================*/
  +/* --- yajl_encode.c */
   
  +static void CharToHex(unsigned char c, char * hexBuf)
  +{
  +    const char * hexchar = "0123456789ABCDEF";
  +    hexBuf[0] = hexchar[c >> 4];
  +    hexBuf[1] = hexchar[c & 0x0F];
   }
   
  -MONGO_EXPORT void bson_iterator_subobject_init( const bson_iterator *i, bson *sub, \
                bson_bool_t copyData ) {
  -    const char *data = bson_iterator_value( i );
  -    if( copyData )
  -        bson_init_finished_data_with_copy( sub, data );
  -    else
  -        bson_init_finished_data( sub, (char *)data, 0 );
  +void
  +yajl_string_encode(const yajl_print_t print,
  +                   void * ctx,
  +                   const unsigned char * str,
  +                   size_t len,
  +                   int escape_solidus)
  +{
  +    size_t beg = 0;
  +    size_t end = 0;
  +    char hexBuf[7];
  +    hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0';
  +    hexBuf[6] = 0;
  +
  +    while (end < len) {
  +        const char * escaped = NULL;
  +        switch (str[end]) {
  +            case '\r': escaped = "\\r"; break;
  +            case '\n': escaped = "\\n"; break;
  +            case '\\': escaped = "\\\\"; break;
  +            /* it is not required to escape a solidus in JSON:
  +             * read sec. 2.5: http://www.ietf.org/rfc/rfc4627.txt
  +             * specifically, this production from the grammar:
  +             *   unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
  +             */
  +            case '/': if (escape_solidus) escaped = "\\/"; break;
  +            case '"': escaped = "\\\""; break;
  +            case '\f': escaped = "\\f"; break;
  +            case '\b': escaped = "\\b"; break;
  +            case '\t': escaped = "\\t"; break;
  +            default:
  +                if ((unsigned char) str[end] < 32) {
  +                    CharToHex(str[end], hexBuf + 4);
  +                    escaped = hexBuf;
  +                }
  +                break;
  +        }
  +        if (escaped != NULL) {
  +            print(ctx, (const char *) (str + beg), end - beg);
  +            print(ctx, escaped, (unsigned int)strlen(escaped));
  +            beg = ++end;
  +        } else {
  +            ++end;
  +        }
  +    }
  +    print(ctx, (const char *) (str + beg), end - beg);
   }
   
  -MONGO_EXPORT void bson_iterator_subiterator( const bson_iterator *i, bson_iterator \
                *sub ) {
  -    bson_iterator_from_buffer( sub, bson_iterator_value( i ) );
  +static void hexToDigit(unsigned int * val, const unsigned char * hex)
  +{
  +    unsigned int i;
  +    for (i=0;i<4;i++) {
  +        unsigned char c = hex[i];
  +        if (c >= 'A') c = (c & ~0x20) - 7;
  +        c -= '0';
  +        assert(!(c & 0xF0));
  +        *val = (*val << 4) | c;
  +    }
   }
   
  -/* ----------------------------
  -   BUILDING
  -   ------------------------------ */
  -
  -static void _bson_zero( bson *b ) {
  -    memset( b, 0, sizeof( bson ) );
  +static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf) 
  +{
  +    if (codepoint < 0x80) {
  +        utf8Buf[0] = (char) codepoint;
  +        utf8Buf[1] = 0;
  +    } else if (codepoint < 0x0800) {
  +        utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0);
  +        utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80);
  +        utf8Buf[2] = 0;
  +    } else if (codepoint < 0x10000) {
  +        utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0);
  +        utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80);
  +        utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80);
  +        utf8Buf[3] = 0;
  +    } else if (codepoint < 0x200000) {
  +        utf8Buf[0] =(char)((codepoint >> 18) | 0xF0);
  +        utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80);
  +        utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80);
  +        utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80);
  +        utf8Buf[4] = 0;
  +    } else {
  +        utf8Buf[0] = '?';
  +        utf8Buf[1] = 0;
  +    }
   }
   
  -MONGO_EXPORT int bson_init( bson *b ) {
  -    return bson_init_size( b, initialBufferSize );
  -}
  +void yajl_string_decode(yajl_buf buf, const unsigned char * str,
  +                        size_t len)
  +{
  +    size_t beg = 0;
  +    size_t end = 0;    
   
  -int bson_init_size( bson *b, int size ) {
  -    _bson_zero( b );
  -    if( size != 0 )
  -    {
  -        char * data = (char *) bson_malloc( size );
  -        if (data == NULL) return BSON_ERROR;
  -        b->data = data;
  -        b->dataSize = size;
  +    while (end < len) {
  +        if (str[end] == '\\') {
  +            char utf8Buf[5];
  +            const char * unescaped = "?";
  +            yajl_buf_append(buf, str + beg, end - beg);
  +            switch (str[++end]) {
  +                case 'r': unescaped = "\r"; break;
  +                case 'n': unescaped = "\n"; break;
  +                case '\\': unescaped = "\\"; break;
  +                case '/': unescaped = "/"; break;
  +                case '"': unescaped = "\""; break;
  +                case 'f': unescaped = "\f"; break;
  +                case 'b': unescaped = "\b"; break;
  +                case 't': unescaped = "\t"; break;
  +                case 'u': {
  +                    unsigned int codepoint = 0;
  +                    hexToDigit(&codepoint, str + ++end);
  +                    end+=3;
  +                    /* check if this is a surrogate */
  +                    if ((codepoint & 0xFC00) == 0xD800) {
  +                        end++;
  +                        if (str[end] == '\\' && str[end + 1] == 'u') {
  +                            unsigned int surrogate = 0;
  +                            hexToDigit(&surrogate, str + end + 2);
  +                            codepoint =
  +                                (((codepoint & 0x3F) << 10) | 
  +                                 ((((codepoint >> 6) & 0xF) + 1) << 16) | 
  +                                 (surrogate & 0x3FF));
  +                            end += 5;
  +                        } else {
  +                            unescaped = "?";
  +                            break;
  +                        }
  +                    }
  +                    
  +                    Utf32toUtf8(codepoint, utf8Buf);
  +                    unescaped = utf8Buf;
  +
  +                    if (codepoint == 0) {
  +                        yajl_buf_append(buf, unescaped, 1);
  +                        beg = ++end;
  +                        continue;
  +                    }
  +
  +                    break;
  +                }
  +                default:
  +                    assert("this should never happen" == NULL);
  +            }
  +            yajl_buf_append(buf, unescaped, (unsigned int)strlen(unescaped));
  +            beg = ++end;
  +        } else {
  +            end++;
  +        }
       }
  -    b->ownsData = 1;
  -    b->cur = b->data + 4;
  -    return BSON_OK;
  +    yajl_buf_append(buf, str + beg, end - beg);
   }
   
  -int bson_init_unfinished_data( bson *b, char *data, int dataSize, bson_bool_t \
                ownsData ) {
  -    _bson_zero( b );
  -    b->data = data;
  -    b->dataSize = dataSize;
  -    b->ownsData = ownsData;
  -    return BSON_OK;
  -}
  +#define ADV_PTR s++; if (!(len--)) return 0;
   
  -static int _bson_append_grow_stack( bson * b ) {
  -    if ( !b->stackPtr ) {
  -        // If this is an empty bson structure, initially use the struct-local \
                (fixed-size) stack
  -        b->stackPtr = b->stack;
  -        b->stackSize = sizeof( b->stack ) / sizeof( size_t );
  -    }
  -    else if ( b->stackPtr == b->stack ) {
  -        // Once we require additional capacity, set up a dynamically resized stack
  -        size_t *new_stack = ( size_t * ) bson_malloc( 2 * sizeof( b->stack ) );
  -        if ( new_stack ) {
  -            b->stackPtr = new_stack;
  -            b->stackSize = 2 * sizeof( b->stack ) / sizeof( size_t );
  -            memcpy( b->stackPtr, b->stack, sizeof( b->stack ) );
  +int yajl_string_validate_utf8(const unsigned char * s, size_t len)
  +{
  +    if (!len) return 1;
  +    if (!s) return 0;
  +    
  +    while (len--) {
  +        /* single byte */
  +        if (*s <= 0x7f) {
  +            /* noop */
           }
  -        else {
  -            return BSON_ERROR;
  +        /* two byte */ 
  +        else if ((*s >> 5) == 0x6) {
  +            ADV_PTR;
  +            if (!((*s >> 6) == 0x2)) return 0;
           }
  -    }
  -    else {
  -        // Double the capacity of the dynamically-resized stack
  -        size_t *new_stack = ( size_t * ) bson_realloc( b->stackPtr, ( b->stackSize \
                * 2 ) * sizeof( size_t ) );
  -        if ( new_stack ) {
  -            b->stackPtr = new_stack;
  -            b->stackSize *= 2;
  +        /* three byte */
  +        else if ((*s >> 4) == 0x0e) {
  +            ADV_PTR;
  +            if (!((*s >> 6) == 0x2)) return 0;
  +            ADV_PTR;
  +            if (!((*s >> 6) == 0x2)) return 0;
           }
  -        else {
  -            return BSON_ERROR;
  +        /* four byte */        
  +        else if ((*s >> 3) == 0x1e) {
  +            ADV_PTR;
  +            if (!((*s >> 6) == 0x2)) return 0;
  +            ADV_PTR;
  +            if (!((*s >> 6) == 0x2)) return 0;
  +            ADV_PTR;
  +            if (!((*s >> 6) == 0x2)) return 0;
  +        } else {
  +            return 0;
           }
  +        
  +        s++;
       }
  -    return BSON_OK;
  +    
  +    return 1;
   }
  +/*==============================================================*/
  +/* --- yajl_gen.c */
   
  -static void bson_append_byte( bson *b, char c ) {
  -    b->cur[0] = c;
  -    b->cur++;
  -}
  +typedef enum {
  +    yajl_gen_start,
  +    yajl_gen_map_start,
  +    yajl_gen_map_key,
  +    yajl_gen_map_val,
  +    yajl_gen_array_start,
  +    yajl_gen_in_array,
  +    yajl_gen_complete,
  +    yajl_gen_error
  +} yajl_gen_state;
   
  -static void bson_append( bson *b, const void *data, size_t len ) {
  -    memcpy( b->cur , data , len );
  -    b->cur += len;
  -}
  +struct yajl_gen_t
  +{
  +    unsigned int flags;
  +    unsigned int depth;
  +    const char * indentString;
  +    yajl_gen_state state[YAJL_MAX_DEPTH];
  +    yajl_print_t print;
  +    void * ctx; /* yajl_buf */
  +    /* memory allocation routines */
  +    yajl_alloc_funcs alloc;
  +};
   
  -static void bson_append32( bson *b, const void *data ) {
  -    bson_little_endian32( b->cur, data );
  -    b->cur += 4;
  -}
  +int
  +yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...)
  +{
  +    int rv = 1;
  +    va_list ap;
  +    va_start(ap, opt);
   
  -static void bson_append32_as_int( bson *b, int data ) {
  -    bson_little_endian32( b->cur, &data );
  -    b->cur += 4;
  -}
  +    switch(opt) {
  +        case yajl_gen_beautify:
  +        case yajl_gen_validate_utf8:
  +        case yajl_gen_escape_solidus:
  +            if (va_arg(ap, int)) g->flags |= opt;
  +            else g->flags &= ~opt;
  +            break;
  +        case yajl_gen_indent_string: {
  +            const char *indent = va_arg(ap, const char *);
  +            g->indentString = indent;
  +            for (; *indent; indent++) {
  +                if (*indent != '\n'
  +                    && *indent != '\v'
  +                    && *indent != '\f'
  +                    && *indent != '\t'
  +                    && *indent != '\r'
  +                    && *indent != ' ')
  +                {
  +                    g->indentString = NULL;
  +                    rv = 0;
  +                }
  +            }
  +            break;
  +        }
  +        case yajl_gen_print_callback:
  +            yajl_buf_free(g->ctx);
  +            g->print = va_arg(ap, const yajl_print_t);
  +            g->ctx = va_arg(ap, void *);
  +            break;
  +        default:
  +            rv = 0;
  +    }
   
  -static void bson_append64( bson *b, const void *data ) {
  -    bson_little_endian64( b->cur, data );
  -    b->cur += 8;
  +    va_end(ap);
  +
  +    return rv;
   }
   
  -int bson_ensure_space( bson *b, const size_t bytesNeeded ) {
  -    size_t pos = _bson_position(b);
  -    char *orig = b->data;
  -    int new_size;
   
  -    if ( pos + bytesNeeded <= (size_t) b->dataSize )
  -        return BSON_OK;
   
  -    new_size = (int) ( 1.5 * ( b->dataSize + bytesNeeded ) );
  +yajl_gen
  +yajl_gen_alloc(const yajl_alloc_funcs * afs)
  +{
  +    yajl_gen g = NULL;
  +    yajl_alloc_funcs afsBuffer;
   
  -    if( new_size < b->dataSize ) {
  -        if( ( b->dataSize + bytesNeeded ) < INT_MAX )
  -            new_size = INT_MAX;
  -        else {
  -            b->err = BSON_SIZE_OVERFLOW;
  -            return BSON_ERROR;
  +    /* first order of business is to set up memory allocation routines */
  +    if (afs != NULL) {
  +        if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
  +        {
  +            return NULL;
           }
  +    } else {
  +        yajl_set_default_alloc_funcs(&afsBuffer);
  +        afs = &afsBuffer;
       }
   
  -    if ( ! b->ownsData ) {
  -        b->err = BSON_DOES_NOT_OWN_DATA;
  -        return BSON_ERROR;
  -    }
  +    g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
  +    if (!g) return NULL;
   
  -    b->data = bson_realloc( b->data, new_size );
  -    if ( !b->data )
  -        bson_fatal_msg( !!b->data, "realloc() failed" );
  +    memset((void *) g, 0, sizeof(struct yajl_gen_t));
  +    /* copy in pointers to allocation routines */
  +    memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
   
  -    b->dataSize = new_size;
  -    b->cur += b->data - orig;
  +    g->print = (yajl_print_t)&yajl_buf_append;
  +    g->ctx = yajl_buf_alloc(&(g->alloc));
  +    g->indentString = "    ";
   
  -    return BSON_OK;
  +    return g;
   }
   
  -MONGO_EXPORT int bson_finish( bson *b ) {
  -    int i;
  +void
  +yajl_gen_free(yajl_gen g)
  +{
  +    if (g->print == (yajl_print_t)&yajl_buf_append) \
yajl_buf_free((yajl_buf)g->ctx);  +    YA_FREE(&(g->alloc), g);
  +}
   
  -    if( b->err & BSON_NOT_UTF8 )
  -        return BSON_ERROR;
  +#define INSERT_SEP \
  +    if (g->state[g->depth] == yajl_gen_map_key ||               \
  +        g->state[g->depth] == yajl_gen_in_array) {              \
  +        g->print(g->ctx, ",", 1);                               \
  +        if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);             \
\  +    } else if (g->state[g->depth] == yajl_gen_map_val) {        \
  +        g->print(g->ctx, ":", 1);                               \
  +        if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1);              \
\  +   }
  +
  +#define INSERT_WHITESPACE                                               \
  +    if ((g->flags & yajl_gen_beautify)) {                                          \
\  +        if (g->state[g->depth] != yajl_gen_map_val) {                   \
  +            unsigned int _i;                                            \
  +            for (_i=0;_i<g->depth;_i++)                                 \
  +                g->print(g->ctx,                                        \
  +                         g->indentString,                               \
  +                         (unsigned int)strlen(g->indentString));        \
  +        }                                                               \
  +    }
  +
  +#define ENSURE_NOT_KEY \
  +    if (g->state[g->depth] == yajl_gen_map_key ||       \
  +        g->state[g->depth] == yajl_gen_map_start)  {    \
  +        return yajl_gen_keys_must_be_strings;           \
  +    }                                                   \
  +
  +/* check that we're not complete, or in error state.  in a valid state
  + * to be generating */
  +#define ENSURE_VALID_STATE \
  +    if (g->state[g->depth] == yajl_gen_error) {   \
  +        return yajl_gen_in_error_state;\
  +    } else if (g->state[g->depth] == yajl_gen_complete) {   \
  +        return yajl_gen_generation_complete;                \
  +    }
  +
  +#define INCREMENT_DEPTH \
  +    if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded;
  +
  +#define DECREMENT_DEPTH \
  +  if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_gen_invalid_string;
  +
  +#define APPENDED_ATOM \
  +    switch (g->state[g->depth]) {                   \
  +        case yajl_gen_start:                        \
  +            g->state[g->depth] = yajl_gen_complete; \
  +            break;                                  \
  +        case yajl_gen_map_start:                    \
  +        case yajl_gen_map_key:                      \
  +            g->state[g->depth] = yajl_gen_map_val;  \
  +            break;                                  \
  +        case yajl_gen_array_start:                  \
  +            g->state[g->depth] = yajl_gen_in_array; \
  +            break;                                  \
  +        case yajl_gen_map_val:                      \
  +            g->state[g->depth] = yajl_gen_map_key;  \
  +            break;                                  \
  +        default:                                    \
  +            break;                                  \
  +    }                                               \
  +
  +#define FINAL_NEWLINE                                        \
  +    if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \
\  +        g->print(g->ctx, "\n", 1);
   
  -    if ( ! b->finished ) {
  -        bson_fatal_msg(!b->stackPos, "Subobject not finished before \
                bson_finish().");
  -        if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
  -        bson_append_byte( b, 0 );
  -        if ( _bson_position(b) >= INT32_MAX ) {
  -            b->err = BSON_SIZE_OVERFLOW;
  -            return BSON_ERROR;
  -        }
  -        i = ( int ) _bson_position(b);
  -        bson_little_endian32( b->data, &i );
  -        b->finished = 1;
  -    }
  +yajl_gen_status
  +yajl_gen_integer(yajl_gen g, long long int number)
  +{
  +    char i[32];
  +    ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
  +    sprintf(i, "%lld", number);
  +    g->print(g->ctx, i, (unsigned int)strlen(i));
  +    APPENDED_ATOM;
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
  +}
  +
  +#if defined(_WIN32) || defined(WIN32)
  +# include <float.h>
  +# ifndef isnan
  +#  define isnan _isnan
  +# endif
  +# ifndef isinf
  +#  define isinf !_finite
  +# endif
  +#endif
   
  -    return BSON_OK;
  +yajl_gen_status
  +yajl_gen_double(yajl_gen g, double number)
  +{
  +    char i[32];
  +    ENSURE_VALID_STATE; ENSURE_NOT_KEY;
  +    if (isnan(number) || isinf(number)) return yajl_gen_invalid_number;
  +    INSERT_SEP; INSERT_WHITESPACE;
  +    sprintf(i, "%.20g", number);
  +    if (strspn(i, "0123456789-") == strlen(i)) {
  +        strcat(i, ".0");
  +    }
  +    g->print(g->ctx, i, (unsigned int)strlen(i));
  +    APPENDED_ATOM;
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
   }
   
  -MONGO_EXPORT void bson_destroy( bson *b ) {
  -    if ( b ) {
  -        if ( b->ownsData && b->data != NULL ) {
  -            bson_free( b->data );
  -        }
  -        b->data = NULL;
  -        b->dataSize = 0;
  -        b->ownsData = 0;
  -        if ( b->stackPtr && b->stackPtr != b->stack ) {
  -            bson_free( b->stackPtr );
  -            b->stackPtr = NULL;
  +yajl_gen_status
  +yajl_gen_number(yajl_gen g, const char * s, size_t l)
  +{
  +    ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
  +    g->print(g->ctx, s, l);
  +    APPENDED_ATOM;
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
  +}
  +
  +yajl_gen_status
  +yajl_gen_string(yajl_gen g, const unsigned char * str,
  +                size_t len)
  +{
  +    // if validation is enabled, check that the string is valid utf8
  +    // XXX: This checking could be done a little faster, in the same pass as
  +    // the string encoding
  +    if (g->flags & yajl_gen_validate_utf8) {
  +        if (!yajl_string_validate_utf8(str, len)) {
  +            return yajl_gen_invalid_string;
           }
  -        b->stackSize = 0;
  -        b->stackPos = 0;
  -        b->err = 0;
  -        b->cur = 0;
  -        b->finished = 1;
       }
  +    ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
  +    g->print(g->ctx, "\"", 1);
  +    yajl_string_encode(g->print, g->ctx, str, len, g->flags & \
yajl_gen_escape_solidus);  +    g->print(g->ctx, "\"", 1);
  +    APPENDED_ATOM;
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
   }
   
  -static int bson_append_estart( bson *b, int type, const char *name, const size_t \
                dataSize ) {
  -    const size_t len = strlen( name ) + 1;
  +yajl_gen_status
  +yajl_gen_null(yajl_gen g)
  +{
  +    ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
  +    g->print(g->ctx, "null", strlen("null"));
  +    APPENDED_ATOM;
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
  +}
   
  -    if ( b->finished ) {
  -        b->err |= BSON_ALREADY_FINISHED;
  -        return BSON_ERROR;
  -    }
  +yajl_gen_status
  +yajl_gen_bool(yajl_gen g, int boolean)
  +{
  +    const char * val = boolean ? "true" : "false";
   
  -    if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) {
  -        return BSON_ERROR;
  -    }
  +	ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
  +    g->print(g->ctx, val, (unsigned int)strlen(val));
  +    APPENDED_ATOM;
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
  +}
   
  -    if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) \
                {
  -        bson_builder_error( b );
  -        return BSON_ERROR;
  -    }
  +yajl_gen_status
  +yajl_gen_map_open(yajl_gen g)
  +{
  +    ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
  +    INCREMENT_DEPTH;
   
  -    bson_append_byte( b, ( char )type );
  -    bson_append( b, name, len );
  -    return BSON_OK;
  +    g->state[g->depth] = yajl_gen_map_start;
  +    g->print(g->ctx, "{", 1);
  +    if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
   }
   
  -/* ----------------------------
  -   BUILDING TYPES
  -   ------------------------------ */
  +yajl_gen_status
  +yajl_gen_map_close(yajl_gen g)
  +{
  +    ENSURE_VALID_STATE;
  +    DECREMENT_DEPTH;
   
  -MONGO_EXPORT int bson_append_int( bson *b, const char *name, const int i ) {
  -    if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    bson_append32( b , &i );
  -    return BSON_OK;
  +    if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
  +    APPENDED_ATOM;
  +    INSERT_WHITESPACE;
  +    g->print(g->ctx, "}", 1);
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
   }
   
  -MONGO_EXPORT int bson_append_long( bson *b, const char *name, const int64_t i ) {
  -    if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    bson_append64( b , &i );
  -    return BSON_OK;
  +yajl_gen_status
  +yajl_gen_array_open(yajl_gen g)
  +{
  +    ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
  +    INCREMENT_DEPTH;
  +    g->state[g->depth] = yajl_gen_array_start;
  +    g->print(g->ctx, "[", 1);
  +    if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
   }
   
  -MONGO_EXPORT int bson_append_double( bson *b, const char *name, const double d ) {
  -    if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    bson_append64( b , &d );
  -    return BSON_OK;
  +yajl_gen_status
  +yajl_gen_array_close(yajl_gen g)
  +{
  +    ENSURE_VALID_STATE;
  +    DECREMENT_DEPTH;
  +    if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
  +    APPENDED_ATOM;
  +    INSERT_WHITESPACE;
  +    g->print(g->ctx, "]", 1);
  +    FINAL_NEWLINE;
  +    return yajl_gen_status_ok;
   }
   
  -MONGO_EXPORT int bson_append_bool( bson *b, const char *name, const bson_bool_t i \
                ) {
  -    if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    bson_append_byte( b , i != 0 );
  -    return BSON_OK;
  +yajl_gen_status
  +yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf,
  +                 size_t * len)
  +{
  +    if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf;
  +    *buf = yajl_buf_data((yajl_buf)g->ctx);
  +    *len = yajl_buf_len((yajl_buf)g->ctx);
  +    return yajl_gen_status_ok;
   }
   
  -MONGO_EXPORT int bson_append_null( bson *b, const char *name ) {
  -    if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    return BSON_OK;
  +void
  +yajl_gen_clear(yajl_gen g)
  +{
  +    if (g->print == (yajl_print_t)&yajl_buf_append) \
yajl_buf_clear((yajl_buf)g->ctx);  }
  +/*==============================================================*/
  +/* --- yajl_lex.c */
   
  -MONGO_EXPORT int bson_append_undefined( bson *b, const char *name ) {
  -    if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    return BSON_OK;
  +#ifdef YAJL_LEXER_DEBUG
  +static const char *
  +tokToStr(yajl_tok tok) 
  +{
  +    switch (tok) {
  +        case yajl_tok_bool: return "bool";
  +        case yajl_tok_colon: return "colon";
  +        case yajl_tok_comma: return "comma";
  +        case yajl_tok_eof: return "eof";
  +        case yajl_tok_error: return "error";
  +        case yajl_tok_left_brace: return "brace";
  +        case yajl_tok_left_bracket: return "bracket";
  +        case yajl_tok_null: return "null";
  +        case yajl_tok_integer: return "integer";
  +        case yajl_tok_double: return "double";
  +        case yajl_tok_right_brace: return "brace";
  +        case yajl_tok_right_bracket: return "bracket";
  +        case yajl_tok_string: return "string";
  +        case yajl_tok_string_with_escapes: return "string_with_escapes";
  +    }
  +    return "unknown";
   }
  +#endif
   
  -MONGO_EXPORT int bson_append_maxkey( bson *b, const char *name ) {
  -    if ( bson_append_estart( b, BSON_MAXKEY, name, 0 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    return BSON_OK;
  -}
  +/* Impact of the stream parsing feature on the lexer:
  + *
  + * YAJL support stream parsing.  That is, the ability to parse the first
  + * bits of a chunk of JSON before the last bits are available (still on
  + * the network or disk).  This makes the lexer more complex.  The
  + * responsibility of the lexer is to handle transparently the case where
  + * a chunk boundary falls in the middle of a token.  This is
  + * accomplished is via a buffer and a character reading abstraction. 
  + *
  + * Overview of implementation
  + *
  + * When we lex to end of input string before end of token is hit, we
  + * copy all of the input text composing the token into our lexBuf.
  + * 
  + * Every time we read a character, we do so through the readChar function.
  + * readChar's responsibility is to handle pulling all chars from the buffer
  + * before pulling chars from input text
  + */
   
  -MONGO_EXPORT int bson_append_minkey( bson *b, const char *name ) {
  -    if ( bson_append_estart( b, BSON_MINKEY, name, 0 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    return BSON_OK;
  -}
  +struct yajl_lexer_t {
  +    /* the overal line and char offset into the data */
  +    size_t lineOff;
  +    size_t charOff;
   
  -static int bson_append_string_base( bson *b, const char *name,
  -                                    const char *value, size_t len, bson_type type \
) {  +    /* error */
  +    yajl_lex_error error;
   
  -    size_t sl = len + 1;
  -    if ( sl > INT32_MAX ) {
  -        b->err = BSON_SIZE_OVERFLOW;
  -        /* string too long */
  -        return BSON_ERROR;
  -    }
  -    if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) {
  -        return BSON_ERROR;
  -    }
  -    bson_append32_as_int( b , ( int )sl );
  -    bson_append( b , value , sl - 1 );
  -    bson_append( b , "\0" , 1 );
  -    return BSON_OK;
  -}
  +    /* a input buffer to handle the case where a token is spread over
  +     * multiple chunks */ 
  +    yajl_buf buf;
   
  -MONGO_EXPORT int bson_append_string( bson *b, const char *name, const char *value \
                ) {
  -    return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING \
                );
  -}
  +    /* in the case where we have data in the lexBuf, bufOff holds
  +     * the current offset into the lexBuf. */
  +    size_t bufOff;
  +
  +    /* are we using the lex buf? */
  +    unsigned int bufInUse;
   
  -MONGO_EXPORT int bson_append_symbol( bson *b, const char *name, const char *value \
                ) {
  -    return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL \
);  +    /* shall we allow comments? */
  +    unsigned int allowComments;
  +
  +    /* shall we validate utf8 inside strings? */
  +    unsigned int validateUTF8;
  +
  +    yajl_alloc_funcs * alloc;
  +};
  +
  +#define readChar(lxr, txt, off)                      \
  +    (((lxr)->bufInUse && yajl_buf_len((lxr)->buf) && lxr->bufOff < \
yajl_buf_len((lxr)->buf)) ? \  +     (*((const unsigned char *) \
yajl_buf_data((lxr)->buf) + ((lxr)->bufOff)++)) : \  +     ((txt)[(*(off))++]))
  +
  +#define unreadChar(lxr, off) ((*(off) > 0) ? (*(off))-- : ((lxr)->bufOff--))
  +
  +yajl_lexer
  +yajl_lex_alloc(yajl_alloc_funcs * alloc,
  +               unsigned int allowComments, unsigned int validateUTF8)
  +{
  +    yajl_lexer lxr = (yajl_lexer) YA_MALLOC(alloc, sizeof(struct yajl_lexer_t));
  +    memset((void *) lxr, 0, sizeof(struct yajl_lexer_t));
  +    lxr->buf = yajl_buf_alloc(alloc);
  +    lxr->allowComments = allowComments;
  +    lxr->validateUTF8 = validateUTF8;
  +    lxr->alloc = alloc;
  +    return lxr;
   }
   
  -MONGO_EXPORT int bson_append_code( bson *b, const char *name, const char *value ) \
                {
  -    return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE );
  +void
  +yajl_lex_free(yajl_lexer lxr)
  +{
  +    yajl_buf_free(lxr->buf);
  +    YA_FREE(lxr->alloc, lxr);
  +    return;
   }
   
  -MONGO_EXPORT int bson_append_string_n( bson *b, const char *name, const char \
                *value, size_t len ) {
  -    return bson_append_string_base( b, name, value, len, BSON_STRING );
  +/* a lookup table which lets us quickly determine three things:
  + * VEC - valid escaped control char
  + * note.  the solidus '/' may be escaped or not.
  + * IJC - invalid json char
  + * VHC - valid hex char
  + * NFP - needs further processing (from a string scanning perspective)
  + * NUC - needs utf8 checking when enabled (from a string scanning perspective)
  + */
  +#define VEC 0x01
  +#define IJC 0x02
  +#define VHC 0x04
  +#define NFP 0x08
  +#define NUC 0x10
  +
  +static const char charLookupTable[256] =
  +{
  +/*00*/ IJC    , IJC    , IJC    , IJC    , IJC    , IJC    , IJC    , IJC    ,
  +/*08*/ IJC    , IJC    , IJC    , IJC    , IJC    , IJC    , IJC    , IJC    ,
  +/*10*/ IJC    , IJC    , IJC    , IJC    , IJC    , IJC    , IJC    , IJC    ,
  +/*18*/ IJC    , IJC    , IJC    , IJC    , IJC    , IJC    , IJC    , IJC    ,
  +
  +/*20*/ 0      , 0      , NFP|VEC|IJC, 0      , 0      , 0      , 0      , 0      ,
  +/*28*/ 0      , 0      , 0      , 0      , 0      , 0      , 0      , VEC    ,
  +/*30*/ VHC    , VHC    , VHC    , VHC    , VHC    , VHC    , VHC    , VHC    ,
  +/*38*/ VHC    , VHC    , 0      , 0      , 0      , 0      , 0      , 0      ,
  +
  +/*40*/ 0      , VHC    , VHC    , VHC    , VHC    , VHC    , VHC    , 0      ,
  +/*48*/ 0      , 0      , 0      , 0      , 0      , 0      , 0      , 0      ,
  +/*50*/ 0      , 0      , 0      , 0      , 0      , 0      , 0      , 0      ,
  +/*58*/ 0      , 0      , 0      , 0      , NFP|VEC|IJC, 0      , 0      , 0      ,
  +
  +/*60*/ 0      , VHC    , VEC|VHC, VHC    , VHC    , VHC    , VEC|VHC, 0      ,
  +/*68*/ 0      , 0      , 0      , 0      , 0      , 0      , VEC    , 0      ,
  +/*70*/ 0      , 0      , VEC    , 0      , VEC    , 0      , 0      , 0      ,
  +/*78*/ 0      , 0      , 0      , 0      , 0      , 0      , 0      , 0      ,
  +
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    ,
  +       NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC    , NUC
  +};
  +
  +/** process a variable length utf8 encoded codepoint.
  + *
  + *  returns:
  + *    yajl_tok_string - if valid utf8 char was parsed and offset was
  + *                      advanced
  + *    yajl_tok_eof - if end of input was hit before validation could
  + *                   complete
  + *    yajl_tok_error - if invalid utf8 was encountered
  + * 
  + *  NOTE: on error the offset will point to the first char of the
  + *  invalid utf8 */
  +#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; }
  +
  +static yajl_tok
  +yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText,
  +                   size_t jsonTextLen, size_t * offset,
  +                   unsigned char curChar)
  +{
  +    if (curChar <= 0x7f) {
  +        /* single byte */
  +        return yajl_tok_string;
  +    } else if ((curChar >> 5) == 0x6) {
  +        /* two byte */ 
  +        UTF8_CHECK_EOF;
  +        curChar = readChar(lexer, jsonText, offset);
  +        if ((curChar >> 6) == 0x2) return yajl_tok_string;
  +    } else if ((curChar >> 4) == 0x0e) {
  +        /* three byte */
  +        UTF8_CHECK_EOF;
  +        curChar = readChar(lexer, jsonText, offset);
  +        if ((curChar >> 6) == 0x2) {
  +            UTF8_CHECK_EOF;
  +            curChar = readChar(lexer, jsonText, offset);
  +            if ((curChar >> 6) == 0x2) return yajl_tok_string;
  +        }
  +    } else if ((curChar >> 3) == 0x1e) {
  +        /* four byte */
  +        UTF8_CHECK_EOF;
  +        curChar = readChar(lexer, jsonText, offset);
  +        if ((curChar >> 6) == 0x2) {
  +            UTF8_CHECK_EOF;
  +            curChar = readChar(lexer, jsonText, offset);
  +            if ((curChar >> 6) == 0x2) {
  +                UTF8_CHECK_EOF;
  +                curChar = readChar(lexer, jsonText, offset);
  +                if ((curChar >> 6) == 0x2) return yajl_tok_string;
  +            }
  +        }
  +    } 
  +
  +    return yajl_tok_error;
   }
   
  -MONGO_EXPORT int bson_append_symbol_n( bson *b, const char *name, const char \
                *value, size_t len ) {
  -    return bson_append_string_base( b, name, value, len, BSON_SYMBOL );
  +/* lex a string.  input is the lexer, pointer to beginning of
  + * json text, and start of string (offset).
  + * a token is returned which has the following meanings:
  + * yajl_tok_string: lex of string was successful.  offset points to
  + *                  terminating '"'.
  + * yajl_tok_eof: end of text was encountered before we could complete
  + *               the lex.
  + * yajl_tok_error: embedded in the string were unallowable chars.  offset
  + *               points to the offending char
  + */
  +#define STR_CHECK_EOF \
  +if (*offset >= jsonTextLen) { \
  +   tok = yajl_tok_eof; \
  +   goto finish_string_lex; \
   }
   
  -MONGO_EXPORT int bson_append_code_n( bson *b, const char *name, const char *value, \
                size_t len ) {
  -    return bson_append_string_base( b, name, value, len, BSON_CODE );
  +/** scan a string for interesting characters that might need further
  + *  review.  return the number of chars that are uninteresting and can
  + *  be skipped.
  + * (lth) hi world, any thoughts on how to make this routine faster? */
  +static size_t
  +yajl_string_scan(const unsigned char * buf, size_t len, int utf8check)
  +{
  +    unsigned char mask = IJC|NFP|(utf8check ? NUC : 0);
  +    size_t skip = 0;
  +    while (skip < len && !(charLookupTable[*buf] & mask))
  +    {
  +        skip++;
  +        buf++;
  +    }
  +    return skip;
   }
   
  -MONGO_EXPORT int bson_append_code_w_scope_n( bson *b, const char *name,
  -        const char *code, size_t len, const bson *scope ) {
  +static yajl_tok
  +yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText,
  +                size_t jsonTextLen, size_t * offset)
  +{
  +    yajl_tok tok = yajl_tok_error;
  +    int hasEscapes = 0;
  +
  +    for (;;) {
  +        unsigned char curChar;
  +
  +        /* now jump into a faster scanning routine to skip as much
  +         * of the buffers as possible */
  +        {
  +            const unsigned char * p;
  +            size_t len;
  +            
  +            if ((lexer->bufInUse && yajl_buf_len(lexer->buf) &&
  +                 lexer->bufOff < yajl_buf_len(lexer->buf)))
  +            {
  +                p = ((const unsigned char *) yajl_buf_data(lexer->buf) +
  +                     (lexer->bufOff));
  +                len = yajl_buf_len(lexer->buf) - lexer->bufOff;
  +                lexer->bufOff += yajl_string_scan(p, len, lexer->validateUTF8);
  +            }                
  +            else if (*offset < jsonTextLen) 
  +            {
  +                p = jsonText + *offset;
  +                len = jsonTextLen - *offset;
  +                *offset += yajl_string_scan(p, len, lexer->validateUTF8);
  +            }
  +        }
  +
  +        STR_CHECK_EOF;
  +
  +        curChar = readChar(lexer, jsonText, offset);
   
  -    size_t sl, size;
  -    if ( !scope ) return BSON_ERROR;
  -    sl = len + 1;
  -    size = 4 + 4 + sl + bson_size( scope );
  -    if ( size > (size_t)INT32_MAX ) {
  -        b->err = BSON_SIZE_OVERFLOW;
  -        return BSON_ERROR;
  +        /* quote terminates */
  +        if (curChar == '"') {
  +            tok = yajl_tok_string;
  +            break;
  +        }
  +        /* backslash escapes a set of control chars, */
  +        else if (curChar == '\\') {
  +            hasEscapes = 1;
  +            STR_CHECK_EOF;
  +
  +            /* special case \u */
  +            curChar = readChar(lexer, jsonText, offset);
  +            if (curChar == 'u') {
  +                unsigned int i = 0;
  +
  +                for (i=0;i<4;i++) {
  +                    STR_CHECK_EOF;                
  +                    curChar = readChar(lexer, jsonText, offset);                
  +                    if (!(charLookupTable[curChar] & VHC)) {
  +                        /* back up to offending char */
  +                        unreadChar(lexer, offset);
  +                        lexer->error = yajl_lex_string_invalid_hex_char;
  +                        goto finish_string_lex;
  +                    }
  +                }
  +            } else if (!(charLookupTable[curChar] & VEC)) {
  +                /* back up to offending char */
  +                unreadChar(lexer, offset);
  +                lexer->error = yajl_lex_string_invalid_escaped_char;
  +                goto finish_string_lex;                
  +            } 
  +        }
  +        /* when not validating UTF8 it's a simple table lookup to determine
  +         * if the present character is invalid */
  +        else if(charLookupTable[curChar] & IJC) {
  +            /* back up to offending char */
  +            unreadChar(lexer, offset);
  +            lexer->error = yajl_lex_string_invalid_json_char;
  +            goto finish_string_lex;                
  +        }
  +        /* when in validate UTF8 mode we need to do some extra work */
  +        else if (lexer->validateUTF8) {
  +            yajl_tok t = yajl_lex_utf8_char(lexer, jsonText, jsonTextLen,
  +                                            offset, curChar);
  +            
  +            if (t == yajl_tok_eof) {
  +                tok = yajl_tok_eof;
  +                goto finish_string_lex;
  +            } else if (t == yajl_tok_error) {
  +                lexer->error = yajl_lex_string_invalid_utf8;
  +                goto finish_string_lex;
  +            } 
  +        }
  +        /* accept it, and move on */ 
       }
  -    if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    bson_append32_as_int( b, ( int )size );
  -    bson_append32( b, &sl );
  -    bson_append( b, code, sl );
  -    bson_append( b, scope->data, bson_size( scope ) );
  -    return BSON_OK;
  -}
  +  finish_string_lex:
  +    /* tell our buddy, the parser, wether he needs to process this string
  +     * again */
  +    if (hasEscapes && tok == yajl_tok_string) {
  +        tok = yajl_tok_string_with_escapes;
  +    } 
   
  -MONGO_EXPORT int bson_append_code_w_scope( bson *b, const char *name, const char \
                *code, const bson *scope ) {
  -    return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope );
  +    return tok;
   }
   
  -MONGO_EXPORT int bson_append_binary( bson *b, const char *name, char type, const \
                char *str, size_t len ) {
  -    if ( type == BSON_BIN_BINARY_OLD ) {
  -        size_t subtwolen = len + 4;
  -        if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR \
                )
  -            return BSON_ERROR;
  -        bson_append32_as_int( b, ( int )subtwolen );
  -        bson_append_byte( b, type );
  -        bson_append32_as_int( b, ( int )len );
  -        bson_append( b, str, len );
  +#define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof;
  +
  +static yajl_tok
  +yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText,
  +                size_t jsonTextLen, size_t * offset)
  +{
  +    /** XXX: numbers are the only entities in json that we must lex
  +     *       _beyond_ in order to know that they are complete.  There
  +     *       is an ambiguous case for integers at EOF. */
  +
  +    unsigned char c;
  +
  +    yajl_tok tok = yajl_tok_integer;
  +
  +    RETURN_IF_EOF;    
  +    c = readChar(lexer, jsonText, offset);
  +
  +    /* optional leading minus */
  +    if (c == '-') {
  +        RETURN_IF_EOF;    
  +        c = readChar(lexer, jsonText, offset); 
  +    }
  +
  +    /* a single zero, or a series of integers */
  +    if (c == '0') {
  +        RETURN_IF_EOF;    
  +        c = readChar(lexer, jsonText, offset); 
  +    } else if (c >= '1' && c <= '9') {
  +        do {
  +            RETURN_IF_EOF;    
  +            c = readChar(lexer, jsonText, offset); 
  +        } while (c >= '0' && c <= '9');
  +    } else {
  +        unreadChar(lexer, offset);
  +        lexer->error = yajl_lex_missing_integer_after_minus;
  +        return yajl_tok_error;
  +    }
  +
  +    /* optional fraction (indicates this is floating point) */
  +    if (c == '.') {
  +        int numRd = 0;
  +        
  +        RETURN_IF_EOF;
  +        c = readChar(lexer, jsonText, offset); 
  +
  +        while (c >= '0' && c <= '9') {
  +            numRd++;
  +            RETURN_IF_EOF;
  +            c = readChar(lexer, jsonText, offset); 
  +        } 
  +
  +        if (!numRd) {
  +            unreadChar(lexer, offset);
  +            lexer->error = yajl_lex_missing_integer_after_decimal;
  +            return yajl_tok_error;
  +        }
  +        tok = yajl_tok_double;
       }
  -    else {
  -        if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR )
  -            return BSON_ERROR;
  -        bson_append32_as_int( b, ( int )len );
  -        bson_append_byte( b, type );
  -        bson_append( b, str, len );
  +
  +    /* optional exponent (indicates this is floating point) */
  +    if (c == 'e' || c == 'E') {
  +        RETURN_IF_EOF;
  +        c = readChar(lexer, jsonText, offset); 
  +
  +        /* optional sign */
  +        if (c == '+' || c == '-') {
  +            RETURN_IF_EOF;
  +            c = readChar(lexer, jsonText, offset); 
  +        }
  +
  +        if (c >= '0' && c <= '9') {
  +            do {
  +                RETURN_IF_EOF;
  +                c = readChar(lexer, jsonText, offset); 
  +            } while (c >= '0' && c <= '9');
  +        } else {
  +            unreadChar(lexer, offset);
  +            lexer->error = yajl_lex_missing_integer_after_exponent;
  +            return yajl_tok_error;
  +        }
  +        tok = yajl_tok_double;
       }
  -    return BSON_OK;
  +    
  +    /* we always go "one too far" */
  +    unreadChar(lexer, offset);
  +    
  +    return tok;
   }
   
  -MONGO_EXPORT int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid \
                ) {
  -    if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    bson_append( b , oid , 12 );
  -    return BSON_OK;
  -}
  +static yajl_tok
  +yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText,
  +                 size_t jsonTextLen, size_t * offset)
  +{
  +    unsigned char c;
   
  -MONGO_EXPORT int bson_append_new_oid( bson *b, const char *name ) {
  -    bson_oid_t oid;
  -    bson_oid_gen( &oid );
  -    return bson_append_oid( b, name, &oid );
  -}
  +    yajl_tok tok = yajl_tok_comment;
   
  -MONGO_EXPORT int bson_append_regex( bson *b, const char *name, const char \
                *pattern, const char *opts ) {
  -    const size_t plen = strlen( pattern )+1;
  -    const size_t olen = strlen( opts )+1;
  -    if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR )
  -        return BSON_ERROR;
  -    bson_append( b , pattern , plen );
  -    bson_append( b , opts , olen );
  -    return BSON_OK;
  -}
  +    RETURN_IF_EOF;    
  +    c = readChar(lexer, jsonText, offset);
   
  -MONGO_EXPORT int bson_append_bson( bson *b, const char *name, const bson *bson ) {
  -    if ( !bson ) return BSON_ERROR;
  -    if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == \
                BSON_ERROR )
  -        return BSON_ERROR;
  -    bson_append( b , bson->data , bson_size( bson ) );
  -    return BSON_OK;
  +    /* either slash or star expected */
  +    if (c == '/') {
  +        /* now we throw away until end of line */
  +        do {
  +            RETURN_IF_EOF;    
  +            c = readChar(lexer, jsonText, offset); 
  +        } while (c != '\n');
  +    } else if (c == '*') {
  +        /* now we throw away until end of comment */        
  +        for (;;) {
  +            RETURN_IF_EOF;    
  +            c = readChar(lexer, jsonText, offset); 
  +            if (c == '*') {
  +                RETURN_IF_EOF;    
  +                c = readChar(lexer, jsonText, offset);                 
  +                if (c == '/') {
  +                    break;
  +                } else {
  +                    unreadChar(lexer, offset);
  +                }
  +            }
  +        }
  +    } else {
  +        lexer->error = yajl_lex_invalid_char;
  +        tok = yajl_tok_error;
  +    }
  +    
  +    return tok;
   }
   
  -MONGO_EXPORT int bson_append_element( bson *b, const char *name_or_null, const \
                bson_iterator *elem ) {
  -    bson_iterator next = *elem;
  -    size_t size;
  +yajl_tok
  +yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
  +             size_t jsonTextLen, size_t * offset,
  +             const unsigned char ** outBuf, size_t * outLen)
  +{
  +    yajl_tok tok = yajl_tok_error;
  +    unsigned char c;
  +    size_t startOffset = *offset;
  +
  +    *outBuf = NULL;
  +    *outLen = 0;
  +
  +    for (;;) {
  +        assert(*offset <= jsonTextLen);
  +
  +        if (*offset >= jsonTextLen) {
  +            tok = yajl_tok_eof;
  +            goto lexed;
  +        }
  +
  +        c = readChar(lexer, jsonText, offset);
  +
  +        switch (c) {
  +            case '{':
  +                tok = yajl_tok_left_bracket;
  +                goto lexed;
  +            case '}':
  +                tok = yajl_tok_right_bracket;
  +                goto lexed;
  +            case '[':
  +                tok = yajl_tok_left_brace;
  +                goto lexed;
  +            case ']':
  +                tok = yajl_tok_right_brace;
  +                goto lexed;
  +            case ',':
  +                tok = yajl_tok_comma;
  +                goto lexed;
  +            case ':':
  +                tok = yajl_tok_colon;
  +                goto lexed;
  +            case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
  +                startOffset++;
  +                break;
  +            case 't': {
  +                const char * want = "rue";
  +                do {
  +                    if (*offset >= jsonTextLen) {
  +                        tok = yajl_tok_eof;
  +                        goto lexed;
  +                    }
  +                    c = readChar(lexer, jsonText, offset);
  +                    if (c != *want) {
  +                        unreadChar(lexer, offset);
  +                        lexer->error = yajl_lex_invalid_string;
  +                        tok = yajl_tok_error;
  +                        goto lexed;
  +                    }
  +                } while (*(++want));
  +                tok = yajl_tok_bool;
  +                goto lexed;
  +            }
  +            case 'f': {
  +                const char * want = "alse";
  +                do {
  +                    if (*offset >= jsonTextLen) {
  +                        tok = yajl_tok_eof;
  +                        goto lexed;
  +                    }
  +                    c = readChar(lexer, jsonText, offset);
  +                    if (c != *want) {
  +                        unreadChar(lexer, offset);
  +                        lexer->error = yajl_lex_invalid_string;
  +                        tok = yajl_tok_error;
  +                        goto lexed;
  +                    }
  +                } while (*(++want));
  +                tok = yajl_tok_bool;
  +                goto lexed;
  +            }
  +            case 'n': {
  +                const char * want = "ull";
  +                do {
  +                    if (*offset >= jsonTextLen) {
  +                        tok = yajl_tok_eof;
  +                        goto lexed;
  +                    }
  +                    c = readChar(lexer, jsonText, offset);
  +                    if (c != *want) {
  +                        unreadChar(lexer, offset);
  +                        lexer->error = yajl_lex_invalid_string;
  +                        tok = yajl_tok_error;
  +                        goto lexed;
  +                    }
  +                } while (*(++want));
  +                tok = yajl_tok_null;
  +                goto lexed;
  +            }
  +            case '"': {
  +                tok = yajl_lex_string(lexer, (const unsigned char *) jsonText,
  +                                      jsonTextLen, offset);
  +                goto lexed;
  +            }
  +            case '-':
  +            case '0': case '1': case '2': case '3': case '4': 
  +            case '5': case '6': case '7': case '8': case '9': {
  +                /* integer parsing wants to start from the beginning */
  +                unreadChar(lexer, offset);
  +                tok = yajl_lex_number(lexer, (const unsigned char *) jsonText,
  +                                      jsonTextLen, offset);
  +                goto lexed;
  +            }
  +            case '/':
  +                /* hey, look, a probable comment!  If comments are disabled
  +                 * it's an error. */
  +                if (!lexer->allowComments) {
  +                    unreadChar(lexer, offset);
  +                    lexer->error = yajl_lex_unallowed_comment;
  +                    tok = yajl_tok_error;
  +                    goto lexed;
  +                }
  +                /* if comments are enabled, then we should try to lex
  +                 * the thing.  possible outcomes are
  +                 * - successful lex (tok_comment, which means continue),
  +                 * - malformed comment opening (slash not followed by
  +                 *   '*' or '/') (tok_error)
  +                 * - eof hit. (tok_eof) */
  +                tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText,
  +                                       jsonTextLen, offset);
  +                if (tok == yajl_tok_comment) {
  +                    /* "error" is silly, but that's the initial
  +                     * state of tok.  guilty until proven innocent. */  
  +                    tok = yajl_tok_error;
  +                    yajl_buf_clear(lexer->buf);
  +                    lexer->bufInUse = 0;
  +                    startOffset = *offset; 
  +                    break;
  +                }
  +                /* hit error or eof, bail */
  +                goto lexed;
  +            default:
  +                lexer->error = yajl_lex_invalid_char;
  +                tok = yajl_tok_error;
  +                goto lexed;
  +        }
  +    }
  +
   
  -    bson_iterator_next( &next );
  -    size = next.cur - elem->cur;
  +  lexed:
  +    /* need to append to buffer if the buffer is in use or
  +     * if it's an EOF token */
  +    if (tok == yajl_tok_eof || lexer->bufInUse) {
  +        if (!lexer->bufInUse) yajl_buf_clear(lexer->buf);
  +        lexer->bufInUse = 1;
  +        yajl_buf_append(lexer->buf, jsonText + startOffset, *offset - \
startOffset);  +        lexer->bufOff = 0;
  +        
  +        if (tok != yajl_tok_eof) {
  +            *outBuf = yajl_buf_data(lexer->buf);
  +            *outLen = yajl_buf_len(lexer->buf);
  +            lexer->bufInUse = 0;
  +        }
  +    } else if (tok != yajl_tok_error) {
  +        *outBuf = jsonText + startOffset;
  +        *outLen = *offset - startOffset;
  +    }
   
  -    if ( name_or_null == NULL ) {
  -        if( bson_ensure_space( b, size ) == BSON_ERROR )
  -            return BSON_ERROR;
  -        bson_append( b, elem->cur, size );
  +    /* special case for strings. skip the quotes. */
  +    if (tok == yajl_tok_string || tok == yajl_tok_string_with_escapes)
  +    {
  +        assert(*outLen >= 2);
  +        (*outBuf)++;
  +        *outLen -= 2; 
       }
  -    else {
  -        size_t data_size = size - 2 - strlen( bson_iterator_key( elem ) );
  -        if ( bson_append_estart( b, elem->cur[0], name_or_null, data_size ) == \
                BSON_ERROR )
  -            return BSON_ERROR;
  -        bson_append( b, bson_iterator_value( elem ), data_size );
  +
  +
  +#ifdef YAJL_LEXER_DEBUG
  +    if (tok == yajl_tok_error) {
  +        printf("lexical error: %s\n",
  +               yajl_lex_error_to_string(yajl_lex_get_error(lexer)));
  +    } else if (tok == yajl_tok_eof) {
  +        printf("EOF hit\n");
  +    } else {
  +        printf("lexed %s: '", tokToStr(tok));
  +        fwrite(*outBuf, 1, *outLen, stdout);
  +        printf("'\n");
       }
  +#endif
   
  -    return BSON_OK;
  +    return tok;
   }
   
  -MONGO_EXPORT int bson_append_timestamp( bson *b, const char *name, \
                bson_timestamp_t *ts ) {
  -    if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return \
BSON_ERROR;  +const char *
  +yajl_lex_error_to_string(yajl_lex_error error)
  +{
  +    switch (error) {
  +        case yajl_lex_e_ok:
  +            return "ok, no error";
  +        case yajl_lex_string_invalid_utf8:
  +            return "invalid bytes in UTF8 string.";
  +        case yajl_lex_string_invalid_escaped_char:
  +            return "inside a string, '\\' occurs before a character "
  +                   "which it may not.";
  +        case yajl_lex_string_invalid_json_char:            
  +            return "invalid character inside string.";
  +        case yajl_lex_string_invalid_hex_char:
  +            return "invalid (non-hex) character occurs after '\\u' inside "
  +                   "string.";
  +        case yajl_lex_invalid_char:
  +            return "invalid char in json text.";
  +        case yajl_lex_invalid_string:
  +            return "invalid string in json text.";
  +        case yajl_lex_missing_integer_after_exponent:
  +            return "malformed number, a digit is required after the exponent.";
  +        case yajl_lex_missing_integer_after_decimal:
  +            return "malformed number, a digit is required after the "
  +                   "decimal point.";
  +        case yajl_lex_missing_integer_after_minus:
  +            return "malformed number, a digit is required after the "
  +                   "minus sign.";
  +        case yajl_lex_unallowed_comment:
  +            return "probable comment found in input text, comments are "
  +                   "not enabled.";
  +    }
  +    return "unknown error code";
  +}
   
  -    bson_append32( b , &( ts->i ) );
  -    bson_append32( b , &( ts->t ) );
   
  -    return BSON_OK;
  +/** allows access to more specific information about the lexical
  + *  error when yajl_lex_lex returns yajl_tok_error. */
  +yajl_lex_error
  +yajl_lex_get_error(yajl_lexer lexer)
  +{
  +    if (lexer == NULL) return (yajl_lex_error) -1;
  +    return lexer->error;
   }
   
  -MONGO_EXPORT int bson_append_timestamp2( bson *b, const char *name, int time, int \
                increment ) {
  -    if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return \
BSON_ERROR;  +size_t yajl_lex_current_line(yajl_lexer lexer)
  +{
  +    return lexer->lineOff;
  +}
   
  -    bson_append32( b , &increment );
  -    bson_append32( b , &time );
  -    return BSON_OK;
  +size_t yajl_lex_current_char(yajl_lexer lexer)
  +{
  +    return lexer->charOff;
   }
   
  -MONGO_EXPORT int bson_append_date( bson *b, const char *name, bson_date_t millis ) \
                {
  -    if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return \
                BSON_ERROR;
  -    bson_append64( b , &millis );
  -    return BSON_OK;
  +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText,
  +                       size_t jsonTextLen, size_t offset)
  +{
  +    const unsigned char * outBuf;
  +    size_t outLen;
  +    size_t bufLen = yajl_buf_len(lexer->buf);
  +    size_t bufOff = lexer->bufOff;
  +    unsigned int bufInUse = lexer->bufInUse;
  +    yajl_tok tok;
  +    
  +    tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset,
  +                       &outBuf, &outLen);
  +
  +    lexer->bufOff = bufOff;
  +    lexer->bufInUse = bufInUse;
  +    yajl_buf_truncate(lexer->buf, bufLen);
  +    
  +    return tok;
   }
  +/*==============================================================*/
  +/* --- yajl_parser.c */
  +
  +#define MAX_VALUE_TO_MULTIPLY ((INT64_MAX / 10) + (INT64_MAX % 10))
  +
  + /* same semantics as strtol */
  +int64_t
  +yajl_parse_integer(const unsigned char *number, unsigned int length)
  +{
  +    int64_t ret  = 0;
  +    long sign = 1;
  +    const unsigned char *pos = number;
  +    if (*pos == '-') { pos++; sign = -1; }
  +    if (*pos == '+') { pos++; }
  +
  +    while (pos < number + length) {
  +        if ( ret > MAX_VALUE_TO_MULTIPLY ) {
  +            errno = ERANGE;
  +            return sign == 1 ? INT64_MAX : INT64_MIN;
  +        }
  +        ret *= 10;
  +        if (INT64_MAX - ret < (*pos - '0')) {
  +            errno = ERANGE;
  +            return sign == 1 ? INT64_MAX : INT64_MIN;
  +        }
  +        if (*pos < '0' || *pos > '9') {
  +            errno = ERANGE;
  +            return sign == 1 ? INT64_MAX : INT64_MIN;
  +        }
  +        ret += (*pos++ - '0');
  +    }
   
  -MONGO_EXPORT int bson_append_time_t( bson *b, const char *name, time_t secs ) {
  -    return bson_append_date( b, name, ( bson_date_t )secs * 1000 );
  +    return sign * ret;
   }
   
  -MONGO_EXPORT int bson_append_start_object( bson *b, const char *name ) {
  -    if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return \
                BSON_ERROR;
  -    if ( b->stackPos >= b->stackSize && _bson_append_grow_stack( b ) == BSON_ERROR \
                ) return BSON_ERROR;
  -    b->stackPtr[ b->stackPos++ ] = _bson_position(b);
  -    bson_append32( b , &zero );
  -    return BSON_OK;
  +unsigned char *
  +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
  +                         size_t jsonTextLen, int verbose)
  +{
  +    size_t offset = hand->bytesConsumed;
  +    unsigned char * str;
  +    const char * errorType = NULL;
  +    const char * errorText = NULL;
  +    char text[72];
  +    const char * arrow = "                     (right here) ------^\n";
  +
  +    if (yajl_bs_current(hand->stateStack) == yajl_state_parse_error) {
  +        errorType = "parse";
  +        errorText = hand->parseError;
  +    } else if (yajl_bs_current(hand->stateStack) == yajl_state_lexical_error) {
  +        errorType = "lexical";
  +        errorText = yajl_lex_error_to_string(yajl_lex_get_error(hand->lexer));
  +    } else {
  +        errorType = "unknown";
  +    }
  +
  +    {
  +        size_t memneeded = 0;
  +        memneeded += strlen(errorType);
  +        memneeded += strlen(" error");
  +        if (errorText != NULL) {
  +            memneeded += strlen(": ");
  +            memneeded += strlen(errorText);
  +        }
  +        str = (unsigned char *) YA_MALLOC(&(hand->alloc), memneeded + 2);
  +        if (!str) return NULL;
  +        str[0] = 0;
  +        strcat((char *) str, errorType);
  +        strcat((char *) str, " error");
  +        if (errorText != NULL) {
  +            strcat((char *) str, ": ");
  +            strcat((char *) str, errorText);
  +        }
  +        strcat((char *) str, "\n");
  +    }
  +
  +    /* now we append as many spaces as needed to make sure the error
  +     * falls at char 41, if verbose was specified */
  +    if (verbose) {
  +        size_t start, end, i;
  +        size_t spacesNeeded;
  +
  +        spacesNeeded = (offset < 30 ? 40 - offset : 10);
  +        start = (offset >= 30 ? offset - 30 : 0);
  +        end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30);
  +
  +        for (i=0;i<spacesNeeded;i++) text[i] = ' ';
  +
  +        for (;start < end;start++, i++) {
  +            if (jsonText[start] != '\n' && jsonText[start] != '\r')
  +            {
  +                text[i] = jsonText[start];
  +            }
  +            else
  +            {
  +                text[i] = ' ';
  +            }
  +        }
  +        assert(i <= 71);
  +        text[i++] = '\n';
  +        text[i] = 0;
  +        {
  +            char * newStr = (char *)
  +                YA_MALLOC(&(hand->alloc), (unsigned int)(strlen((char *) str) +
  +                                                         strlen((char *) text) +
  +                                                         strlen(arrow) + 1));
  +            if (newStr) {
  +                newStr[0] = 0;
  +                strcat((char *) newStr, (char *) str);
  +                strcat((char *) newStr, text);
  +                strcat((char *) newStr, arrow);
  +            }
  +            YA_FREE(&(hand->alloc), str);
  +            str = (unsigned char *) newStr;
  +        }
  +    }
  +    return str;
   }
   
  -MONGO_EXPORT int bson_append_start_array( bson *b, const char *name ) {
  -    if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return \
                BSON_ERROR;
  -    if ( b->stackPos >= b->stackSize && _bson_append_grow_stack( b ) == BSON_ERROR \
                ) return BSON_ERROR;
  -    b->stackPtr[ b->stackPos++ ] = _bson_position(b);
  -    bson_append32( b , &zero );
  -    return BSON_OK;
  +/* check for client cancelation */
  +#define _CC_CHK(x)                                                \
  +    if (!(x)) {                                                   \
  +        yajl_bs_set(hand->stateStack, yajl_state_parse_error);    \
  +        hand->parseError =                                        \
  +            "client cancelled parse via callback return value";   \
  +        return yajl_status_client_canceled;                       \
  +    }
  +
  +
  +yajl_status
  +yajl_do_finish(yajl_handle hand)
  +{
  +    yajl_status stat;
  +    stat = yajl_do_parse(hand,(const unsigned char *) " ",1);
  +
  +    if (stat != yajl_status_ok) return stat;
  +
  +    switch(yajl_bs_current(hand->stateStack))
  +    {
  +        case yajl_state_parse_error:
  +        case yajl_state_lexical_error:
  +            return yajl_status_error;
  +        case yajl_state_got_value:
  +        case yajl_state_parse_complete:
  +            return yajl_status_ok;
  +        default:
  +            if (!(hand->flags & yajl_allow_partial_values))
  +            {
  +                yajl_bs_set(hand->stateStack, yajl_state_parse_error);
  +                hand->parseError = "premature EOF";
  +                return yajl_status_error;
  +            }
  +            return yajl_status_ok;
  +    }
   }
   
  -MONGO_EXPORT int bson_append_finish_object( bson *b ) {
  -    char *start;
  -    int i;
  -    if (!b) return BSON_ERROR;
  -    if (!b->stackPos) { b->err = BSON_NOT_IN_SUBOBJECT; return BSON_ERROR; }
  -    if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
  -    bson_append_byte( b , 0 );
  +yajl_status
  +yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
  +              size_t jsonTextLen)
  +{
  +    yajl_tok tok;
  +    const unsigned char * buf;
  +    size_t bufLen;
  +    size_t * offset = &(hand->bytesConsumed);
  +
  +    *offset = 0;
  +
  +  around_again:
  +    switch (yajl_bs_current(hand->stateStack)) {
  +        case yajl_state_parse_complete:
  +            if (hand->flags & yajl_allow_multiple_values) {
  +                yajl_bs_set(hand->stateStack, yajl_state_got_value);
  +                goto around_again;
  +            }
  +            if (!(hand->flags & yajl_allow_trailing_garbage)) {
  +                if (*offset != jsonTextLen) {
  +                    tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
  +                                       offset, &buf, &bufLen);
  +                    if (tok != yajl_tok_eof) {
  +                        yajl_bs_set(hand->stateStack, yajl_state_parse_error);
  +                        hand->parseError = "trailing garbage";
  +                    }
  +                    goto around_again;
  +                }
  +            }
  +            return yajl_status_ok;
  +        case yajl_state_lexical_error:
  +        case yajl_state_parse_error:
  +            return yajl_status_error;
  +        case yajl_state_start:
  +        case yajl_state_got_value:
  +        case yajl_state_map_need_val:
  +        case yajl_state_array_need_val:
  +        case yajl_state_array_start:  {
  +            /* for arrays and maps, we advance the state for this
  +             * depth, then push the state of the next depth.
  +             * If an error occurs during the parsing of the nesting
  +             * enitity, the state at this level will not matter.
  +             * a state that needs pushing will be anything other
  +             * than state_start */
  +
  +            yajl_state stateToPush = yajl_state_start;
  +
  +            tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
  +                               offset, &buf, &bufLen);
  +
  +            switch (tok) {
  +                case yajl_tok_eof:
  +                    return yajl_status_ok;
  +                case yajl_tok_error:
  +                    yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
  +                    goto around_again;
  +                case yajl_tok_string:
  +                    if (hand->callbacks && hand->callbacks->yajl_string) {
  +                        _CC_CHK(hand->callbacks->yajl_string(hand->ctx,
  +                                                             buf, bufLen));
  +                    }
  +                    break;
  +                case yajl_tok_string_with_escapes:
  +                    if (hand->callbacks && hand->callbacks->yajl_string) {
  +                        yajl_buf_clear(hand->decodeBuf);
  +                        yajl_string_decode(hand->decodeBuf, buf, bufLen);
  +                        _CC_CHK(hand->callbacks->yajl_string(
  +                                    hand->ctx, yajl_buf_data(hand->decodeBuf),
  +                                    yajl_buf_len(hand->decodeBuf)));
  +                    }
  +                    break;
  +                case yajl_tok_bool:
  +                    if (hand->callbacks && hand->callbacks->yajl_boolean) {
  +                        _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx,
  +                                                              *buf == 't'));
  +                    }
  +                    break;
  +                case yajl_tok_null:
  +                    if (hand->callbacks && hand->callbacks->yajl_null) {
  +                        _CC_CHK(hand->callbacks->yajl_null(hand->ctx));
  +                    }
  +                    break;
  +                case yajl_tok_left_bracket:
  +                    if (hand->callbacks && hand->callbacks->yajl_start_map) {
  +                        _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx));
  +                    }
  +                    stateToPush = yajl_state_map_start;
  +                    break;
  +                case yajl_tok_left_brace:
  +                    if (hand->callbacks && hand->callbacks->yajl_start_array) {
  +                        _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx));
  +                    }
  +                    stateToPush = yajl_state_array_start;
  +                    break;
  +                case yajl_tok_integer:
  +                    if (hand->callbacks) {
  +                        if (hand->callbacks->yajl_number) {
  +                            _CC_CHK(hand->callbacks->yajl_number(
  +                                        hand->ctx,(const char *) buf, bufLen));
  +                        } else if (hand->callbacks->yajl_integer) {
  +                            int64_t i = 0;
  +                            errno = 0;
  +                            i = yajl_parse_integer(buf, bufLen);
  +                            if ((i == INT64_MIN || i == INT64_MAX) &&
  +                                errno == ERANGE)
  +                            {
  +                                yajl_bs_set(hand->stateStack,
  +                                            yajl_state_parse_error);
  +                                hand->parseError = "integer overflow" ;
  +                                /* try to restore error offset */
  +                                if (*offset >= bufLen) *offset -= bufLen;
  +                                else *offset = 0;
  +                                goto around_again;
  +                            }
  +                            _CC_CHK(hand->callbacks->yajl_integer(hand->ctx,
  +                                                                  i));
  +                        }
  +                    }
  +                    break;
  +                case yajl_tok_double:
  +                    if (hand->callbacks) {
  +                        if (hand->callbacks->yajl_number) {
  +                            _CC_CHK(hand->callbacks->yajl_number(
  +                                        hand->ctx, (const char *) buf, bufLen));
  +                        } else if (hand->callbacks->yajl_double) {
  +                            double d = 0.0;
  +                            yajl_buf_clear(hand->decodeBuf);
  +                            yajl_buf_append(hand->decodeBuf, buf, bufLen);
  +                            buf = yajl_buf_data(hand->decodeBuf);
  +                            errno = 0;
  +                            d = strtod((char *) buf, NULL);
  +                            if ((d == HUGE_VAL || d == -HUGE_VAL) &&
  +                                errno == ERANGE)
  +                            {
  +                                yajl_bs_set(hand->stateStack,
  +                                            yajl_state_parse_error);
  +                                hand->parseError = "numeric (floating point) "
  +                                    "overflow";
  +                                /* try to restore error offset */
  +                                if (*offset >= bufLen) *offset -= bufLen;
  +                                else *offset = 0;
  +                                goto around_again;
  +                            }
  +                            _CC_CHK(hand->callbacks->yajl_double(hand->ctx,
  +                                                                 d));
  +                        }
  +                    }
  +                    break;
  +                case yajl_tok_right_brace: {
  +                    if (yajl_bs_current(hand->stateStack) ==
  +                        yajl_state_array_start)
  +                    {
  +                        if (hand->callbacks &&
  +                            hand->callbacks->yajl_end_array)
  +                        {
  +                            _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx));
  +                        }
  +                        yajl_bs_pop(hand->stateStack);
  +                        goto around_again;
  +                    }
  +                    /* intentional fall-through */
  +                }
  +                case yajl_tok_colon:
  +                case yajl_tok_comma:
  +                case yajl_tok_right_bracket:
  +                    yajl_bs_set(hand->stateStack, yajl_state_parse_error);
  +                    hand->parseError =
  +                        "unallowed token at this point in JSON text";
  +                    goto around_again;
  +                default:
  +                    yajl_bs_set(hand->stateStack, yajl_state_parse_error);
  +                    hand->parseError = "invalid token, internal error";
  +                    goto around_again;
  +            }
  +            /* got a value.  transition depends on the state we're in. */
  +            {
  +                yajl_state s = yajl_bs_current(hand->stateStack);
  +                if (s == yajl_state_start || s == yajl_state_got_value) {
  +                    yajl_bs_set(hand->stateStack, yajl_state_parse_complete);
  +                } else if (s == yajl_state_map_need_val) {
  +                    yajl_bs_set(hand->stateStack, yajl_state_map_got_val);
  +                } else {
  +                    yajl_bs_set(hand->stateStack, yajl_state_array_got_val);
  +                }
  +            }
  +            if (stateToPush != yajl_state_start) {
  +                yajl_bs_push(hand->stateStack, stateToPush);
  +            }
   
  -    start = b->data + b->stackPtr[ --b->stackPos ];
  -    if ( b->cur - start >= INT32_MAX ) {
  -        b->err = BSON_SIZE_OVERFLOW;
  -        return BSON_ERROR;
  +            goto around_again;
  +        }
  +        case yajl_state_map_start:
  +        case yajl_state_map_need_key: {
  +            /* only difference between these two states is that in
  +             * start '}' is valid, whereas in need_key, we've parsed
  +             * a comma, and a string key _must_ follow */
  +            tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
  +                               offset, &buf, &bufLen);
  +            switch (tok) {
  +                case yajl_tok_eof:
  +                    return yajl_status_ok;
  +                case yajl_tok_error:
  +                    yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
  +                    goto around_again;
  +                case yajl_tok_string_with_escapes:
  +                    if (hand->callbacks && hand->callbacks->yajl_map_key) {
  +                        yajl_buf_clear(hand->decodeBuf);
  +                        yajl_string_decode(hand->decodeBuf, buf, bufLen);
  +                        buf = yajl_buf_data(hand->decodeBuf);
  +                        bufLen = yajl_buf_len(hand->decodeBuf);
  +                    }
  +                    /* intentional fall-through */
  +                case yajl_tok_string:
  +                    if (hand->callbacks && hand->callbacks->yajl_map_key) {
  +                        _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf,
  +                                                              bufLen));
  +                    }
  +                    yajl_bs_set(hand->stateStack, yajl_state_map_sep);
  +                    goto around_again;
  +                case yajl_tok_right_bracket:
  +                    if (yajl_bs_current(hand->stateStack) ==
  +                        yajl_state_map_start)
  +                    {
  +                        if (hand->callbacks && hand->callbacks->yajl_end_map) {
  +                            _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx));
  +                        }
  +                        yajl_bs_pop(hand->stateStack);
  +                        goto around_again;
  +                    }
  +                default:
  +                    yajl_bs_set(hand->stateStack, yajl_state_parse_error);
  +                    hand->parseError =
  +                        "invalid object key (must be a string)";
  +                    goto around_again;
  +            }
  +        }
  +        case yajl_state_map_sep: {
  +            tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
  +                               offset, &buf, &bufLen);
  +            switch (tok) {
  +                case yajl_tok_colon:
  +                    yajl_bs_set(hand->stateStack, yajl_state_map_need_val);
  +                    goto around_again;
  +                case yajl_tok_eof:
  +                    return yajl_status_ok;
  +                case yajl_tok_error:
  +                    yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
  +                    goto around_again;
  +                default:
  +                    yajl_bs_set(hand->stateStack, yajl_state_parse_error);
  +                    hand->parseError = "object key and value must "
  +                        "be separated by a colon (':')";
  +                    goto around_again;
  +            }
  +        }
  +        case yajl_state_map_got_val: {
  +            tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
  +                               offset, &buf, &bufLen);
  +            switch (tok) {
  +                case yajl_tok_right_bracket:
  +                    if (hand->callbacks && hand->callbacks->yajl_end_map) {
  +                        _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx));
  +                    }
  +                    yajl_bs_pop(hand->stateStack);
  +                    goto around_again;
  +                case yajl_tok_comma:
  +                    yajl_bs_set(hand->stateStack, yajl_state_map_need_key);
  +                    goto around_again;
  +                case yajl_tok_eof:
  +                    return yajl_status_ok;
  +                case yajl_tok_error:
  +                    yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
  +                    goto around_again;
  +                default:
  +                    yajl_bs_set(hand->stateStack, yajl_state_parse_error);
  +                    hand->parseError = "after key and value, inside map, "
  +                                       "I expect ',' or '}'";
  +                    /* try to restore error offset */
  +                    if (*offset >= bufLen) *offset -= bufLen;
  +                    else *offset = 0;
  +                    goto around_again;
  +            }
  +        }
  +        case yajl_state_array_got_val: {
  +            tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
  +                               offset, &buf, &bufLen);
  +            switch (tok) {
  +                case yajl_tok_right_brace:
  +                    if (hand->callbacks && hand->callbacks->yajl_end_array) {
  +                        _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx));
  +                    }
  +                    yajl_bs_pop(hand->stateStack);
  +                    goto around_again;
  +                case yajl_tok_comma:
  +                    yajl_bs_set(hand->stateStack, yajl_state_array_need_val);
  +                    goto around_again;
  +                case yajl_tok_eof:
  +                    return yajl_status_ok;
  +                case yajl_tok_error:
  +                    yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
  +                    goto around_again;
  +                default:
  +                    yajl_bs_set(hand->stateStack, yajl_state_parse_error);
  +                    hand->parseError =
  +                        "after array element, I expect ',' or ']'";
  +                    goto around_again;
  +            }
  +        }
       }
  -    i = ( int )( b->cur - start );
  -    bson_little_endian32( start, &i );
   
  -    return BSON_OK;
  +    abort();
  +    return yajl_status_error;
   }
   
  -MONGO_EXPORT double bson_int64_to_double( int64_t i64 ) {
  -    return (double)i64;
  +/*==============================================================*/
  +/* --- yajl_tree.c */
  +
  +#if defined(_WIN32) || defined(WIN32)
  +#define snprintf sprintf_s
  +#endif
  +
  +#define STATUS_CONTINUE 1
  +#define STATUS_ABORT    0
  +
  +struct stack_elem_s;
  +typedef struct stack_elem_s stack_elem_t;
  +struct stack_elem_s
  +{
  +    char * key;
  +    yajl_val value;
  +    stack_elem_t *next;
  +};
  +
  +struct context_s
  +{
  +    stack_elem_t *stack;
  +    yajl_val root;
  +    char *errbuf;
  +    size_t errbuf_size;
  +};
  +typedef struct context_s context_t;
  +
  +#define RETURN_ERROR(ctx,retval,...) {                                  \
  +        if ((ctx)->errbuf != NULL)                                      \
  +            snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__);  \
  +        return (retval);                                                \
  +    }
  +
  +static yajl_val value_alloc (yajl_type type)
  +{
  +    yajl_val v;
  +
  +    v = malloc (sizeof (*v));
  +    if (v == NULL) return (NULL);
  +    memset (v, 0, sizeof (*v));
  +    v->type = type;
  +
  +    return (v);
   }
   
  -MONGO_EXPORT int bson_append_finish_array( bson *b ) {
  -    return bson_append_finish_object( b );
  +static void yajl_object_free (yajl_val v)
  +{
  +    size_t i;
  +
  +    if (!YAJL_IS_OBJECT(v)) return;
  +
  +    for (i = 0; i < v->u.object.len; i++)
  +    {
  +        free((char *) v->u.object.keys[i]);
  +        v->u.object.keys[i] = NULL;
  +        yajl_tree_free (v->u.object.values[i]);
  +        v->u.object.values[i] = NULL;
  +    }
  +
  +    free((void*) v->u.object.keys);
  +    free(v->u.object.values);
  +    free(v);
   }
   
  -/* Error handling and allocators. */
  +static void yajl_array_free (yajl_val v)
  +{
  +    size_t i;
  +
  +    if (!YAJL_IS_ARRAY(v)) return;
   
  -static bson_err_handler err_handler = NULL;
  +    for (i = 0; i < v->u.array.len; i++)
  +    {
  +        yajl_tree_free (v->u.array.values[i]);
  +        v->u.array.values[i] = NULL;
  +    }
   
  -MONGO_EXPORT bson_err_handler set_bson_err_handler( bson_err_handler func ) {
  -    bson_err_handler old = err_handler;
  -    err_handler = func;
  -    return old;
  +    free(v->u.array.values);
  +    free(v);
   }
   
  -MONGO_EXPORT void bson_free( void *ptr ) {
  -    bson_free_func( ptr );
  +/*
  + * Parsing nested objects and arrays is implemented using a stack. When a new
  + * object or array starts (a curly or a square opening bracket is read), an
  + * appropriate value is pushed on the stack. When the end of the object is
  + * reached (an appropriate closing bracket has been read), the value is popped
  + * off the stack and added to the enclosing object using "context_add_value".
  + */
  +static int context_push(context_t *ctx, yajl_val v)
  +{
  +    stack_elem_t *stack;
  +
  +    stack = malloc (sizeof (*stack));
  +    if (stack == NULL)
  +        RETURN_ERROR (ctx, ENOMEM, "Out of memory");
  +    memset (stack, 0, sizeof (*stack));
  +
  +    assert ((ctx->stack == NULL)
  +            || YAJL_IS_OBJECT (v)
  +            || YAJL_IS_ARRAY (v));
  +
  +    stack->value = v;
  +    stack->next = ctx->stack;
  +    ctx->stack = stack;
  +
  +    return (0);
   }
   
  -MONGO_EXPORT void *bson_malloc( size_t size ) {
  -    void *p;
  -    p = bson_malloc_func( size );
  -    bson_fatal_msg( !!p, "malloc() failed" );
  -    return p;
  +static yajl_val context_pop(context_t *ctx)
  +{
  +    stack_elem_t *stack;
  +    yajl_val v;
  +
  +    if (ctx->stack == NULL)
  +        RETURN_ERROR (ctx, NULL, "context_pop: "
  +                      "Bottom of stack reached prematurely");
  +
  +    stack = ctx->stack;
  +    ctx->stack = stack->next;
  +
  +    v = stack->value;
  +
  +    free (stack);
  +
  +    return (v);
   }
   
  -void *bson_realloc( void *ptr, size_t size ) {
  -    void *p;
  -    p = bson_realloc_func( ptr, size );
  -    bson_fatal_msg( !!p, "realloc() failed" );
  -    return p;
  +static int object_add_keyval(context_t *ctx,
  +                             yajl_val obj, char *key, yajl_val value)
  +{
  +    const char **tmpk;
  +    yajl_val *tmpv;
  +
  +    /* We're checking for NULL in "context_add_value" or its callers. */
  +    assert (ctx != NULL);
  +    assert (obj != NULL);
  +    assert (key != NULL);
  +    assert (value != NULL);
  +
  +    /* We're assuring that "obj" is an object in "context_add_value". */
  +    assert(YAJL_IS_OBJECT(obj));
  +
  +    tmpk = realloc((void *) obj->u.object.keys, sizeof(*(obj->u.object.keys)) * \
(obj->u.object.len + 1));  +    if (tmpk == NULL)
  +        RETURN_ERROR(ctx, ENOMEM, "Out of memory");
  +    obj->u.object.keys = tmpk;
  +
  +    tmpv = realloc(obj->u.object.values, sizeof (*obj->u.object.values) * \
(obj->u.object.len + 1));  +    if (tmpv == NULL)
  +        RETURN_ERROR(ctx, ENOMEM, "Out of memory");
  +    obj->u.object.values = tmpv;
  +
  +    obj->u.object.keys[obj->u.object.len] = key;
  +    obj->u.object.values[obj->u.object.len] = value;
  +    obj->u.object.len++;
  +
  +    return (0);
   }
   
  -int _bson_errprintf( const char *format, ... ) {
  -    va_list ap;
  -    int ret = 0;
  -    va_start( ap, format );
  -#ifndef R_SAFETY_NET
  -    ret = vfprintf( stderr, format, ap );
  -#endif
  -    va_end( ap );
  +static int array_add_value (context_t *ctx,
  +                            yajl_val array, yajl_val value)
  +{
  +    yajl_val *tmp;
  +
  +    /* We're checking for NULL pointers in "context_add_value" or its
  +     * callers. */
  +    assert (ctx != NULL);
  +    assert (array != NULL);
  +    assert (value != NULL);
  +
  +    /* "context_add_value" will only call us with array values. */
  +    assert(YAJL_IS_ARRAY(array));
  +    
  +    tmp = realloc(array->u.array.values,
  +                  sizeof(*(array->u.array.values)) * (array->u.array.len + 1));
  +    if (tmp == NULL)
  +        RETURN_ERROR(ctx, ENOMEM, "Out of memory");
  +    array->u.array.values = tmp;
  +    array->u.array.values[array->u.array.len] = value;
  +    array->u.array.len++;
   
  -    return ret;
  +    return 0;
   }
   
  -/**
  - * This method is invoked when a non-fatal bson error is encountered.
  - * Calls the error handler if available.
  - *
  - *  @param
  +/*
  + * Add a value to the value on top of the stack or the "root" member in the
  + * context if the end of the parsing process is reached.
    */
  -void bson_builder_error( bson *b ) {
  -    if( err_handler )
  -        err_handler( "BSON error." );
  +static int context_add_value (context_t *ctx, yajl_val v)
  +{
  +    /* We're checking for NULL values in all the calling functions. */
  +    assert (ctx != NULL);
  +    assert (v != NULL);
  +
  +    /*
  +     * There are three valid states in which this function may be called:
  +     *   - There is no value on the stack => This is the only value. This is the
  +     *     last step done when parsing a document. We assign the value to the
  +     *     "root" member and return.
  +     *   - The value on the stack is an object. In this case store the key on the
  +     *     stack or, if the key has already been read, add key and value to the
  +     *     object.
  +     *   - The value on the stack is an array. In this case simply add the value
  +     *     and return.
  +     */
  +    if (ctx->stack == NULL)
  +    {
  +        assert (ctx->root == NULL);
  +        ctx->root = v;
  +        return (0);
  +    }
  +    else if (YAJL_IS_OBJECT (ctx->stack->value))
  +    {
  +        if (ctx->stack->key == NULL)
  +        {
  +            if (!YAJL_IS_STRING (v))
  +                RETURN_ERROR (ctx, EINVAL, "context_add_value: "
  +                              "Object key is not a string (%#04x)",
  +                              v->type);
  +
  +            ctx->stack->key = v->u.string;
  +            v->u.string = NULL;
  +            free(v);
  +            return (0);
  +        }
  +        else /* if (ctx->key != NULL) */
  +        {
  +            char * key;
  +
  +            key = ctx->stack->key;
  +            ctx->stack->key = NULL;
  +            return (object_add_keyval (ctx, ctx->stack->value, key, v));
  +        }
  +    }
  +    else if (YAJL_IS_ARRAY (ctx->stack->value))
  +    {
  +        return (array_add_value (ctx, ctx->stack->value, v));
  +    }
  +    else
  +    {
  +        RETURN_ERROR (ctx, EINVAL, "context_add_value: Cannot add value to "
  +                      "a value of type %#04x (not a composite type)",
  +                      ctx->stack->value->type);
  +    }
   }
   
  -void bson_fatal( int ok ) {
  -    bson_fatal_msg( ok, "" );
  +static int handle_string (void *ctx,
  +                          const unsigned char *string, size_t string_length)
  +{
  +    yajl_val v;
  +
  +    v = value_alloc (yajl_t_string);
  +    if (v == NULL)
  +        RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
  +
  +    v->u.string = malloc (string_length + 1);
  +    if (v->u.string == NULL)
  +    {
  +        free (v);
  +        RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
  +    }
  +    memcpy(v->u.string, string, string_length);
  +    v->u.string[string_length] = 0;
  +
  +    return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
   }
   
  -void bson_fatal_msg( int ok , const char *msg ) {
  -    if ( ok )
  -        return;
  +static int handle_number (void *ctx, const char *string, size_t string_length)
  +{
  +    yajl_val v;
  +    char *endptr;
  +
  +    v = value_alloc(yajl_t_number);
  +    if (v == NULL)
  +        RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory");
   
  -    if ( err_handler ) {
  -        err_handler( msg );
  +    v->u.number.r = malloc(string_length + 1);
  +    if (v->u.number.r == NULL)
  +    {
  +        free(v);
  +        RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory");
       }
  -#ifndef R_SAFETY_NET
  -    bson_errprintf( "error: %s\n" , msg );
  -    exit( -5 );
  -#endif
  +    memcpy(v->u.number.r, string, string_length);
  +    v->u.number.r[string_length] = 0;
  +
  +    v->u.number.flags = 0;
  +
  +    errno = 0;
  +    v->u.number.i = yajl_parse_integer((const unsigned char *) v->u.number.r,
  +                                       strlen(v->u.number.r));
  +    if (errno == 0)
  +        v->u.number.flags |= YAJL_NUMBER_INT_VALID;
  +
  +    endptr = NULL;
  +    errno = 0;
  +    v->u.number.d = strtod(v->u.number.r, &endptr);
  +    if ((errno == 0) && (endptr != NULL) && (*endptr == 0))
  +        v->u.number.flags |= YAJL_NUMBER_DOUBLE_VALID;
  +
  +    return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
   }
   
  +static int handle_start_map (void *ctx)
  +{
  +    yajl_val v;
   
  -/* Efficiently copy an integer to a string. */
  -extern const char bson_numstrs[1000][4];
  +    v = value_alloc(yajl_t_object);
  +    if (v == NULL)
  +        RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
   
  -void bson_numstr( char *str, int i ) {
  -    if( i < 1000 )
  -        memcpy( str, bson_numstrs[i], 4 );
  -    else
  -        bson_sprintf( str,"%d", i );
  +    v->u.object.keys = NULL;
  +    v->u.object.values = NULL;
  +    v->u.object.len = 0;
  +
  +    return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
  +}
  +
  +static int handle_end_map (void *ctx)
  +{
  +    yajl_val v;
  +
  +    v = context_pop (ctx);
  +    if (v == NULL)
  +        return (STATUS_ABORT);
  +
  +    return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
   }
   
  -MONGO_EXPORT void bson_swap_endian64( void *outp, const void *inp ) {
  -    const char *in = ( const char * )inp;
  -    char *out = ( char * )outp;
  +static int handle_start_array (void *ctx)
  +{
  +    yajl_val v;
  +
  +    v = value_alloc(yajl_t_array);
  +    if (v == NULL)
  +        RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
   
  -    out[0] = in[7];
  -    out[1] = in[6];
  -    out[2] = in[5];
  -    out[3] = in[4];
  -    out[4] = in[3];
  -    out[5] = in[2];
  -    out[6] = in[1];
  -    out[7] = in[0];
  +    v->u.array.values = NULL;
  +    v->u.array.len = 0;
   
  +    return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
   }
   
  -MONGO_EXPORT void bson_swap_endian32( void *outp, const void *inp ) {
  -    const char *in = ( const char * )inp;
  -    char *out = ( char * )outp;
  +static int handle_end_array (void *ctx)
  +{
  +    yajl_val v;
  +
  +    v = context_pop (ctx);
  +    if (v == NULL)
  +        return (STATUS_ABORT);
  +
  +    return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
  +}
  +
  +static int handle_boolean (void *ctx, int boolean_value)
  +{
  +    yajl_val v;
  +
  +    v = value_alloc (boolean_value ? yajl_t_true : yajl_t_false);
  +    if (v == NULL)
  +        RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
  +
  +    return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
  +}
  +
  +static int handle_null (void *ctx)
  +{
  +    yajl_val v;
  +
  +    v = value_alloc (yajl_t_null);
  +    if (v == NULL)
  +        RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
  +
  +    return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
  +}
  +
  +/*
  + * Public functions
  + */
  +yajl_val yajl_tree_parse (const char *input,
  +                          char *error_buffer, size_t error_buffer_size)
  +{
  +    static const yajl_callbacks callbacks =
  +        {
  +            /* null        = */ handle_null,
  +            /* boolean     = */ handle_boolean,
  +            /* integer     = */ NULL,
  +            /* double      = */ NULL,
  +            /* number      = */ handle_number,
  +            /* string      = */ handle_string,
  +            /* start map   = */ handle_start_map,
  +            /* map key     = */ handle_string,
  +            /* end map     = */ handle_end_map,
  +            /* start array = */ handle_start_array,
  +            /* end array   = */ handle_end_array
  +        };
  +
  +    yajl_handle handle;
  +    yajl_status status;
  +    char * internal_err_str;
  +	context_t ctx = { NULL, NULL, NULL, 0 };
  +
  +	ctx.errbuf = error_buffer;
  +	ctx.errbuf_size = error_buffer_size;
  +
  +    if (error_buffer != NULL)
  +        memset (error_buffer, 0, error_buffer_size);
  +
  +    handle = yajl_alloc (&callbacks, NULL, &ctx);
  +    yajl_config(handle, yajl_allow_comments, 1);
  +
  +    status = yajl_parse(handle,
  +                        (unsigned char *) input,
  +                        strlen (input));
  +    status = yajl_complete_parse (handle);
  +    if (status != yajl_status_ok) {
  +        if (error_buffer != NULL && error_buffer_size > 0) {
  +               internal_err_str = (char *) yajl_get_error(handle, 1,
  +                     (const unsigned char *) input,
  +                     strlen(input));
  +             snprintf(error_buffer, error_buffer_size, "%s", internal_err_str);
  +             YA_FREE(&(handle->alloc), internal_err_str);
  +        }
  +        yajl_free (handle);
  +        return NULL;
  +    }
  +
  +    yajl_free (handle);
  +    return (ctx.root);
  +}
  +
  +yajl_val yajl_tree_get(yajl_val n, const char ** path, yajl_type type)
  +{
  +    if (!path) return NULL;
  +    while (n && *path) {
  +        unsigned int i;
  +        int len;
  +
  +        if (n->type != yajl_t_object) return NULL;
  +        len = n->u.object.len;
  +        for (i = 0; i < len; i++) {
  +            if (!strcmp(*path, n->u.object.keys[i])) {
  +                n = n->u.object.values[i];
  +                break;
  +            }
  +        }
  +        if (i == len) return NULL;
  +        path++;
  +    }
  +    if (n && type != yajl_t_any && type != n->type) n = NULL;
  +    return n;
  +}
  +
  +void yajl_tree_free (yajl_val v)
  +{
  +    if (v == NULL) return;
  +
  +    if (YAJL_IS_STRING(v))
  +    {
  +        free(v->u.string);
  +        free(v);
  +    }
  +    else if (YAJL_IS_NUMBER(v))
  +    {
  +        free(v->u.number.r);
  +        free(v);
  +    }
  +    else if (YAJL_GET_OBJECT(v))
  +    {
  +        yajl_object_free(v);
  +    }
  +    else if (YAJL_GET_ARRAY(v))
  +    {
  +        yajl_array_free(v);
  +    }
  +    else /* if (yajl_t_true or yajl_t_false or yajl_t_null) */
  +    {
  +        free(v);
  +    }
  +}
  +/*==============================================================*/
  +/* --- yajl_version.c */
  +
  +int yajl_version(void)
  +{
  +	return YAJL_VERSION;
  +}
  +/*==============================================================*/
  +/*==============================================================*/
  +/* --- bson.c */
  +
  +#ifndef BSON_MAX_RECURSION
  +# define BSON_MAX_RECURSION 100
  +#endif
  +
  +
  +/*
  + * Structures.
  + */
  +typedef struct
  +{
  +   bson_validate_flags_t flags;
  +   ssize_t               err_offset;
  +} bson_validate_state_t;
  +
  +
  +typedef struct
  +{
  +   uint32_t       count;
  +   bool           keys;
  +   uint32_t       depth;
  +   bson_string_t *str;
  +} bson_json_state_t;
  +
  +
  +/*
  + * Forward declarations.
  + */
  +static bool _bson_as_json_visit_array    (const bson_iter_t *iter,
  +                                          const char        *key,
  +                                          const bson_t      *v_array,
  +                                          void              *data);
  +static bool _bson_as_json_visit_document (const bson_iter_t *iter,
  +                                          const char        *key,
  +                                          const bson_t      *v_document,
  +                                          void              *data);
  +
  +
  +/*
  + * Globals.
  + */
  +static const uint8_t gZero;
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_impl_inline_grow --
  + *
  + *       Document growth implementation for documents that currently
  + *       contain stack based buffers. The document may be switched to
  + *       a malloc based buffer.
  + *
  + * Returns:
  + *       true if successful; otherwise false indicating INT_MAX overflow.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_bson_impl_inline_grow (bson_impl_inline_t *impl, /* IN */
  +                        uint32_t            size) /* IN */
  +{
  +   bson_impl_alloc_t *alloc = (bson_impl_alloc_t *)impl;
  +   uint8_t *data;
  +   size_t req;
  +
  +   BSON_ASSERT (impl);
  +   BSON_ASSERT (!(impl->flags & BSON_FLAG_RDONLY));
  +   BSON_ASSERT (!(impl->flags & BSON_FLAG_CHILD));
  +
  +   if ((impl->len + size) <= sizeof impl->data) {
  +      return true;
  +   }
  +
  +   req = bson_next_power_of_two (impl->len + size);
  +
  +   if (req <= INT32_MAX) {
  +      data = bson_malloc (req);
  +
  +      memcpy (data, impl->data, impl->len);
  +      alloc->flags &= ~BSON_FLAG_INLINE;
  +      alloc->parent = NULL;
  +      alloc->depth = 0;
  +      alloc->buf = &alloc->alloc;
  +      alloc->buflen = &alloc->alloclen;
  +      alloc->offset = 0;
  +      alloc->alloc = data;
  +      alloc->alloclen = req;
  +      alloc->realloc = bson_realloc;
  +
  +      return true;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_impl_alloc_grow --
  + *
  + *       Document growth implementation for documents containing malloc
  + *       based buffers.
  + *
  + * Returns:
  + *       true if successful; otherwise false indicating INT_MAX overflow.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_bson_impl_alloc_grow (bson_impl_alloc_t *impl, /* IN */
  +                       uint32_t           size) /* IN */
  +{
  +   uint32_t req;
  +
  +   BSON_ASSERT (impl);
  +
  +   /*
  +    * Determine how many bytes we need for this document in the buffer
  +    * including necessary trailing bytes for parent documents.
  +    */
  +   req = (uint32_t)(impl->offset + impl->len + size + impl->depth);
  +
  +   if (req <= *impl->buflen) {
  +      return true;
  +   }
  +
  +   req = bson_next_power_of_two (req);
  +
  +   if ((int32_t)req <= INT32_MAX && impl->realloc) {
  +      *impl->buf = impl->realloc (*impl->buf, req);
  +      *impl->buflen = req;
  +      return true;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_grow --
  + *
  + *       Grows the bson_t structure to be large enough to contain @size
  + *       bytes.
  + *
  + * Returns:
  + *       true if successful, false if the size would overflow.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_bson_grow (bson_t   *bson, /* IN */
  +            uint32_t  size) /* IN */
  +{
  +   BSON_ASSERT (bson);
  +   BSON_ASSERT (!(bson->flags & BSON_FLAG_RDONLY));
  +
  +   if ((bson->flags & BSON_FLAG_INLINE)) {
  +      return _bson_impl_inline_grow ((bson_impl_inline_t *)bson, size);
  +   }
  +
  +   return _bson_impl_alloc_grow ((bson_impl_alloc_t *)bson, size);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_data --
  + *
  + *       A helper function to return the contents of the bson document
  + *       taking into account the polymorphic nature of bson_t.
  + *
  + * Returns:
  + *       A buffer which should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE uint8_t *
  +_bson_data (const bson_t *bson) /* IN */
  +{
  +   if ((bson->flags & BSON_FLAG_INLINE)) {
  +      return ((bson_impl_inline_t *)bson)->data;
  +   } else {
  +      bson_impl_alloc_t *impl = (bson_impl_alloc_t *)bson;
  +      return (*impl->buf) + impl->offset;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_encode_length --
  + *
  + *       Helper to encode the length of the bson_t in the first 4 bytes
  + *       of the bson document. Little endian format is used as specified
  + *       by bsonspec.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE void
  +_bson_encode_length (bson_t *bson) /* IN */
  +{
  +#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN
  +   memcpy (_bson_data (bson), &bson->len, 4);
  +#else
  +   uint32_t length_le = BSON_UINT32_TO_LE (bson->len);
  +   memcpy (_bson_data (bson), &length_le, 4);
  +#endif
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_append_va --
  + *
  + *       Appends the length,buffer pairs to the bson_t. @n_bytes is an
  + *       optimization to perform one array growth rather than many small
  + *       growths.
  + *
  + *       @bson: A bson_t
  + *       @n_bytes: The number of bytes to append to the document.
  + *       @n_pairs: The number of length,buffer pairs.
  + *       @first_len: Length of first buffer.
  + *       @first_data: First buffer.
  + *       @args: va_list of additional tuples.
  + *
  + * Returns:
  + *       true if the bytes were appended successfully.
  + *       false if it bson would overflow INT_MAX.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE bool
  +_bson_append_va (bson_t        *bson,        /* IN */
  +                 uint32_t       n_bytes,     /* IN */
  +                 uint32_t       n_pairs,     /* IN */
  +                 uint32_t       first_len,   /* IN */
  +                 const uint8_t *first_data,  /* IN */
  +                 va_list        args)        /* IN */
  +{
  +   const uint8_t *data;
  +   uint32_t data_len;
  +   uint8_t *buf;
  +
  +   BSON_ASSERT (bson);
  +   BSON_ASSERT (!(bson->flags & BSON_FLAG_IN_CHILD));
  +   BSON_ASSERT (!(bson->flags & BSON_FLAG_RDONLY));
  +   BSON_ASSERT (n_pairs);
  +   BSON_ASSERT (first_len);
  +   BSON_ASSERT (first_data);
  +
  +   if (BSON_UNLIKELY (!_bson_grow (bson, n_bytes))) {
  +      return false;
  +   }
  +
  +   data = first_data;
  +   data_len = first_len;
  +
  +   buf = _bson_data (bson) + bson->len - 1;
  +
  +   do {
  +      n_pairs--;
  +      memcpy (buf, data, data_len);
  +      bson->len += data_len;
  +      buf += data_len;
  +
  +      if (n_pairs) {
  +         data_len = va_arg (args, uint32_t);
  +         data = va_arg (args, const uint8_t *);
  +      }
  +   } while (n_pairs);
  +
  +   _bson_encode_length (bson);
  +
  +   *buf = '\0';
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_append --
  + *
  + *       Variadic function to append length,buffer pairs to a bson_t. If the
  + *       append would cause the bson_t to overflow a 32-bit length, it will
  + *       return false and no append will have occurred.
  + *
  + * Parameters:
  + *       @bson: A bson_t.
  + *       @n_pairs: Number of length,buffer pairs.
  + *       @n_bytes: the total number of bytes being appended.
  + *       @first_len: Length of first buffer.
  + *       @first_data: First buffer.
  + *
  + * Returns:
  + *       true if successful; otherwise false indicating INT_MAX overflow.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_bson_append (bson_t        *bson,        /* IN */
  +              uint32_t       n_pairs,     /* IN */
  +              uint32_t       n_bytes,     /* IN */
  +              uint32_t       first_len,   /* IN */
  +              const uint8_t *first_data,  /* IN */
  +              ...)
  +{
  +   va_list args;
  +   bool ok;
  +
  +   BSON_ASSERT (bson);
  +   BSON_ASSERT (n_pairs);
  +   BSON_ASSERT (first_len);
  +   BSON_ASSERT (first_data);
  +
  +   /*
  +    * Check to see if this append would overflow 32-bit signed integer. I know
  +    * what you're thinking. BSON uses a signed 32-bit length field? Yeah. It
  +    * does.
  +    */
  +   if (BSON_UNLIKELY (n_bytes > (BSON_MAX_SIZE - bson->len))) {
  +      return false;
  +   }
  +
  +   va_start (args, first_data);
  +   ok = _bson_append_va (bson, n_bytes, n_pairs, first_len, first_data, args);
  +   va_end (args);
  +
  +   return ok;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_append_bson_begin --
  + *
  + *       Begin appending a subdocument or subarray to the document using
  + *       the key provided by @key.
  + *
  + *       If @key_length is < 0, then strlen() will be called on @key
  + *       to determine the length.
  + *
  + *       @key_type MUST be either BSON_TYPE_DOCUMENT or BSON_TYPE_ARRAY.
  + *
  + * Returns:
  + *       true if successful; otherwise false indiciating INT_MAX overflow.
  + *
  + * Side effects:
  + *       @child is initialized if true is returned.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_bson_append_bson_begin (bson_t      *bson,        /* IN */
  +                         const char  *key,         /* IN */
  +                         int          key_length,  /* IN */
  +                         bson_type_t  child_type,  /* IN */
  +                         bson_t      *child)       /* OUT */
  +{
  +   const uint8_t type = child_type;
  +   const uint8_t empty[5] = { 5 };
  +   bson_impl_alloc_t *aparent = (bson_impl_alloc_t *)bson;
  +   bson_impl_alloc_t *achild = (bson_impl_alloc_t *)child;
  +
  +   BSON_ASSERT (bson);
  +   BSON_ASSERT (!(bson->flags & BSON_FLAG_RDONLY));
  +   BSON_ASSERT (!(bson->flags & BSON_FLAG_IN_CHILD));
  +   BSON_ASSERT (key);
  +   BSON_ASSERT ((child_type == BSON_TYPE_DOCUMENT) ||
  +                (child_type == BSON_TYPE_ARRAY));
  +   BSON_ASSERT (child);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   /*
  +    * If the parent is an inline bson_t, then we need to convert
  +    * it to a heap allocated buffer. This makes extending buffers
  +    * of child bson documents much simpler logic, as they can just
  +    * realloc the *buf pointer.
  +    */
  +   if ((bson->flags & BSON_FLAG_INLINE)) {
  +      BSON_ASSERT (bson->len <= 120);
  +      _bson_grow (bson, 128 - bson->len);
  +      BSON_ASSERT (!(bson->flags & BSON_FLAG_INLINE));
  +   }
  +
  +   /*
  +    * Append the type and key for the field.
  +    */
  +   if (!_bson_append (bson, 4,
  +                      (1 + key_length + 1 + 5),
  +                      1, &type,
  +                      key_length, key,
  +                      1, &gZero,
  +                      5, empty)) {
  +      return false;
  +   }
  +
  +   /*
  +    * Mark the document as working on a child document so that no
  +    * further modifications can happen until the caller has called
  +    * bson_append_{document,array}_end().
  +    */
  +   bson->flags |= BSON_FLAG_IN_CHILD;
  +
  +   /*
  +    * Initialize the child bson_t structure and point it at the parents
  +    * buffers. This allows us to realloc directly from the child without
  +    * walking up to the parent bson_t.
  +    */
  +   achild->flags = (BSON_FLAG_CHILD | BSON_FLAG_NO_FREE | BSON_FLAG_STATIC);
  +
  +   if ((bson->flags & BSON_FLAG_CHILD)) {
  +      achild->depth = ((bson_impl_alloc_t *)bson)->depth + 1;
  +   } else {
  +      achild->depth = 1;
  +   }
  +
  +   achild->parent = bson;
  +   achild->buf = aparent->buf;
  +   achild->buflen = aparent->buflen;
  +   achild->offset = aparent->offset + aparent->len - 1 - 5;
  +   achild->len = 5;
  +   achild->alloc = NULL;
  +   achild->alloclen = 0;
  +   achild->realloc = aparent->realloc;
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_append_bson_end --
  + *
  + *       Complete a call to _bson_append_bson_begin.
  + *
  + * Returns:
  + *       true if successful; otherwise false indiciating INT_MAX overflow.
  + *
  + * Side effects:
  + *       @child is destroyed and no longer valid after calling this
  + *       function.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_bson_append_bson_end (bson_t *bson,   /* IN */
  +                       bson_t *child)  /* IN */
  +{
  +   BSON_ASSERT (bson);
  +   BSON_ASSERT ((bson->flags & BSON_FLAG_IN_CHILD));
  +   BSON_ASSERT (!(child->flags & BSON_FLAG_IN_CHILD));
  +
  +   /*
  +    * Unmark the IN_CHILD flag.
  +    */
  +   bson->flags &= ~BSON_FLAG_IN_CHILD;
  +
  +   /*
  +    * Now that we are done building the sub-document, add the size to the
  +    * parent, not including the default 5 byte empty document already added.
  +    */
  +   bson->len = (bson->len + child->len - 5);
  +
  +   /*
  +    * Ensure we have a \0 byte at the end and proper length encoded at
  +    * the beginning of the document.
  +    */
  +   _bson_data (bson)[bson->len - 1] = '\0';
  +   _bson_encode_length (bson);
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_array_begin --
  + *
  + *       Start appending a new array.
  + *
  + *       Use @child to append to the data area for the given field.
  + *
  + *       It is a programming error to call any other bson function on
  + *       @bson until bson_append_array_end() has been called. It is
  + *       valid to call bson_append*() functions on @child.
  + *
  + *       This function is useful to allow building nested documents using
  + *       a single buffer owned by the top-level bson document.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @child is invalid.
  + *
  + * Side effects:
  + *       @child is initialized if true is returned.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_array_begin (bson_t     *bson,         /* IN */
  +                         const char *key,          /* IN */
  +                         int         key_length,   /* IN */
  +                         bson_t     *child)        /* IN */
  +{
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (child, false);
  +
  +   return _bson_append_bson_begin (bson, key, key_length, BSON_TYPE_ARRAY,
  +                                   child);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_array_end --
  + *
  + *       Complete a call to bson_append_array_begin().
  + *
  + *       It is safe to append other fields to @bson after calling this
  + *       function.
  + *
  + * Returns:
  + *       true if successful; otherwise false indiciating INT_MAX overflow.
  + *
  + * Side effects:
  + *       @child is invalid after calling this function.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_array_end (bson_t *bson,   /* IN */
  +                       bson_t *child)  /* IN */
  +{
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (child, false);
  +
  +   return _bson_append_bson_end (bson, child);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_document_begin --
  + *
  + *       Start appending a new document.
  + *
  + *       Use @child to append to the data area for the given field.
  + *
  + *       It is a programming error to call any other bson function on
  + *       @bson until bson_append_document_end() has been called. It is
  + *       valid to call bson_append*() functions on @child.
  + *
  + *       This function is useful to allow building nested documents using
  + *       a single buffer owned by the top-level bson document.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @child is invalid.
  + *
  + * Side effects:
  + *       @child is initialized if true is returned.
  + *
  + *--------------------------------------------------------------------------
  + */
  +bool
  +bson_append_document_begin (bson_t     *bson,         /* IN */
  +                            const char *key,          /* IN */
  +                            int         key_length,   /* IN */
  +                            bson_t     *child)        /* IN */
  +{
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (child, false);
  +
  +   return _bson_append_bson_begin (bson, key, key_length, BSON_TYPE_DOCUMENT,
  +                                   child);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_document_end --
  + *
  + *       Complete a call to bson_append_document_begin().
  + *
  + *       It is safe to append new fields to @bson after calling this
  + *       function, if true is returned.
  + *
  + * Returns:
  + *       true if successful; otherwise false indicating INT_MAX overflow.
  + *
  + * Side effects:
  + *       @child is destroyed and invalid after calling this function.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_document_end (bson_t *bson,   /* IN */
  +                          bson_t *child)  /* IN */
  +{
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (child, false);
  +
  +   return _bson_append_bson_end (bson, child);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_array --
  + *
  + *       Append an array to @bson.
  + *
  + *       Generally, bson_append_array_begin() will result in faster code
  + *       since few buffers need to be malloced.
  + *
  + * Returns:
  + *       true if successful; otherwise false indiciating INT_MAX overflow.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_array (bson_t       *bson,       /* IN */
  +                   const char   *key,        /* IN */
  +                   int           key_length, /* IN */
  +                   const bson_t *array)      /* IN */
  +{
  +   static const uint8_t type = BSON_TYPE_ARRAY;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (array, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + array->len),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        array->len, _bson_data (array));
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_binary --
  + *
  + *       Append binary data to @bson. The field will have the
  + *       BSON_TYPE_BINARY type.
  + *
  + * Parameters:
  + *       @subtype: the BSON Binary Subtype. See bsonspec.org for more
  + *                 information.
  + *       @binary: a pointer to the raw binary data.
  + *       @length: the size of @binary in bytes.
  + *
  + * Returns:
  + *       true if successful; otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_binary (bson_t         *bson,       /* IN */
  +                    const char     *key,        /* IN */
  +                    int             key_length, /* IN */
  +                    bson_subtype_t  subtype,    /* IN */
  +                    const uint8_t  *binary,     /* IN */
  +                    uint32_t        length)     /* IN */
  +{
  +   static const uint8_t type = BSON_TYPE_BINARY;
  +   uint32_t length_le;
  +   uint32_t deprecated_length_le;
  +   uint8_t subtype8 = 0;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (binary, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   subtype8 = subtype;
  +
  +   if (subtype == BSON_SUBTYPE_BINARY_DEPRECATED) {
  +      length_le = BSON_UINT32_TO_LE (length + 4);
  +      deprecated_length_le = BSON_UINT32_TO_LE (length);
  +
  +      return _bson_append (bson, 7,
  +                           (1 + key_length + 1 + 4 + 1 + 4 + length),
  +                           1, &type,
  +                           key_length, key,
  +                           1, &gZero,
  +                           4, &length_le,
  +                           1, &subtype8,
  +                           4, &deprecated_length_le,
  +                           length, binary);
  +   } else {
  +      length_le = BSON_UINT32_TO_LE (length);
  +
  +      return _bson_append (bson, 6,
  +                           (1 + key_length + 1 + 4 + 1 + length),
  +                           1, &type,
  +                           key_length, key,
  +                           1, &gZero,
  +                           4, &length_le,
  +                           1, &subtype8,
  +                           length, binary);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_bool --
  + *
  + *       Append a new field to @bson with the name @key. The value is
  + *       a boolean indicated by @value.
  + *
  + * Returns:
  + *       true if succesful; otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_bool (bson_t     *bson,       /* IN */
  +                  const char *key,        /* IN */
  +                  int         key_length, /* IN */
  +                  bool        value)      /* IN */
  +{
  +   static const uint8_t type = BSON_TYPE_BOOL;
  +   uint8_t byte = !!value;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + 1),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        1, &byte);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_code --
  + *
  + *       Append a new field to @bson containing javascript code.
  + *
  + *       @javascript MUST be a zero terminated UTF-8 string. It MUST NOT
  + *       containing embedded \0 characters.
  + *
  + * Returns:
  + *       true if successful; otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + * See also:
  + *       bson_append_code_with_scope().
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_code (bson_t     *bson,       /* IN */
  +                  const char *key,        /* IN */
  +                  int         key_length, /* IN */
  +                  const char *javascript) /* IN */
  +{
  +   static const uint8_t type = BSON_TYPE_CODE;
  +   uint32_t length;
  +   uint32_t length_le;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (javascript, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   length = (int)strlen (javascript) + 1;
  +   length_le = BSON_UINT32_TO_LE (length);
  +
  +   return _bson_append (bson, 5,
  +                        (1 + key_length + 1 + 4 + length),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        4, &length_le,
  +                        length, javascript);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_code_with_scope --
  + *
  + *       Append a new field to @bson containing javascript code with
  + *       supplied scope.
  + *
  + * Returns:
  + *       true if successful; otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_code_with_scope (bson_t       *bson,         /* IN */
  +                             const char   *key,          /* IN */
  +                             int           key_length,   /* IN */
  +                             const char   *javascript,   /* IN */
  +                             const bson_t *scope)        /* IN */
  +{
  +   static const uint8_t type = BSON_TYPE_CODEWSCOPE;
  +   uint32_t codews_length_le;
  +   uint32_t codews_length;
  +   uint32_t js_length_le;
  +   uint32_t js_length;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (javascript, false);
  +
  +   if (bson_empty0 (scope)) {
  +      return bson_append_code (bson, key, key_length, javascript);
  +   }
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   js_length = (int)strlen (javascript) + 1;
  +   js_length_le = BSON_UINT32_TO_LE (js_length);
  +
  +   codews_length = 4 + 4 + js_length + scope->len;
  +   codews_length_le = BSON_UINT32_TO_LE (codews_length);
  +
  +   return _bson_append (bson, 7,
  +                        (1 + key_length + 1 + 4 + 4 + js_length + scope->len),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        4, &codews_length_le,
  +                        4, &js_length_le,
  +                        js_length, javascript,
  +                        scope->len, _bson_data (scope));
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_dbpointer --
  + *
  + *       This BSON data type is DEPRECATED.
  + *
  + *       Append a BSON dbpointer field to @bson.
  + *
  + * Returns:
  + *       true if successful; otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_dbpointer (bson_t           *bson,       /* IN */
  +                       const char       *key,        /* IN */
  +                       int               key_length, /* IN */
  +                       const char       *collection, /* IN */
  +                       const bson_oid_t *oid)
  +{
  +   static const uint8_t type = BSON_TYPE_DBPOINTER;
  +   uint32_t length;
  +   uint32_t length_le;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (collection, false);
  +   bson_return_val_if_fail (oid, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   length = (int)strlen (collection) + 1;
  +   length_le = BSON_UINT32_TO_LE (length);
  +
  +   return _bson_append (bson, 6,
  +                        (1 + key_length + 1 + 4 + length + 12),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        4, &length_le,
  +                        length, collection,
  +                        12, oid);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_append_document --
  + *
  + *       Append a new field to @bson containing a BSON document.
  + *
  + *       In general, using bson_append_document_begin() results in faster
  + *       code and less memory fragmentation.
  + *
  + * Returns:
  + *       true if successful; otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + * See also:
  + *       bson_append_document_begin().
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_append_document (bson_t       *bson,       /* IN */
  +                      const char   *key,        /* IN */
  +                      int           key_length, /* IN */
  +                      const bson_t *value)      /* IN */
  +{
  +   static const uint8_t type = BSON_TYPE_DOCUMENT;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (value, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + value->len),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        value->len, _bson_data (value));
  +}
  +
  +
  +bool
  +bson_append_double (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length,
  +                    double      value)
  +{
  +   static const uint8_t type = BSON_TYPE_DOUBLE;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +#if BSON_BYTE_ORDER == BSON_BIG_ENDIAN
  +   value = BSON_DOUBLE_TO_LE (value);
  +#endif
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + 8),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        8, &value);
  +}
  +
  +
  +bool
  +bson_append_int32 (bson_t      *bson,
  +                   const char  *key,
  +                   int          key_length,
  +                   int32_t value)
  +{
  +   static const uint8_t type = BSON_TYPE_INT32;
  +   uint32_t value_le;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   value_le = BSON_UINT32_TO_LE (value);
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + 4),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        4, &value_le);
  +}
  +
  +
  +bool
  +bson_append_int64 (bson_t      *bson,
  +                   const char  *key,
  +                   int          key_length,
  +                   int64_t value)
  +{
  +   static const uint8_t type = BSON_TYPE_INT64;
  +   uint64_t value_le;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   value_le = BSON_UINT64_TO_LE (value);
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + 8),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        8, &value_le);
  +}
  +
  +
  +bool
  +bson_append_iter (bson_t            *bson,
  +                  const char        *key,
  +                  int                key_length,
  +                  const bson_iter_t *iter)
  +{
  +   bool ret = false;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (iter, false);
  +
  +   if (!key) {
  +      key = bson_iter_key (iter);
  +      key_length = -1;
  +   }
  +
  +   switch (bson_iter_type_unsafe (iter)) {
  +   case BSON_TYPE_EOD:
  +      return false;
  +   case BSON_TYPE_DOUBLE:
  +      ret = bson_append_double (bson, key, key_length, bson_iter_double (iter));
  +      break;
  +   case BSON_TYPE_UTF8:
  +      {
  +         uint32_t len = 0;
  +         const char *str;
  +
  +         str = bson_iter_utf8 (iter, &len);
  +         ret = bson_append_utf8 (bson, key, key_length, str, len);
  +      }
  +      break;
  +   case BSON_TYPE_DOCUMENT:
  +      {
  +         const uint8_t *buf = NULL;
  +         uint32_t len = 0;
  +         bson_t doc;
  +
  +         bson_iter_document (iter, &len, &buf);
  +
  +         if (bson_init_static (&doc, buf, len)) {
  +            ret = bson_append_document (bson, key, key_length, &doc);
  +            bson_destroy (&doc);
  +         }
  +      }
  +      break;
  +   case BSON_TYPE_ARRAY:
  +      {
  +         const uint8_t *buf = NULL;
  +         uint32_t len = 0;
  +         bson_t doc;
  +
  +         bson_iter_array (iter, &len, &buf);
  +
  +         if (bson_init_static (&doc, buf, len)) {
  +            ret = bson_append_array (bson, key, key_length, &doc);
  +            bson_destroy (&doc);
  +         }
  +      }
  +      break;
  +   case BSON_TYPE_BINARY:
  +      {
  +         const uint8_t *binary = NULL;
  +         bson_subtype_t subtype = BSON_SUBTYPE_BINARY;
  +         uint32_t len = 0;
  +
  +         bson_iter_binary (iter, &subtype, &len, &binary);
  +         ret = bson_append_binary (bson, key, key_length,
  +                                   subtype, binary, len);
  +      }
  +      break;
  +   case BSON_TYPE_UNDEFINED:
  +      ret = bson_append_undefined (bson, key, key_length);
  +      break;
  +   case BSON_TYPE_OID:
  +      ret = bson_append_oid (bson, key, key_length, bson_iter_oid (iter));
  +      break;
  +   case BSON_TYPE_BOOL:
  +      ret = bson_append_bool (bson, key, key_length, bson_iter_bool (iter));
  +      break;
  +   case BSON_TYPE_DATE_TIME:
  +      ret = bson_append_date_time (bson, key, key_length,
  +                                   bson_iter_date_time (iter));
  +      break;
  +   case BSON_TYPE_NULL:
  +      ret = bson_append_undefined (bson, key, key_length);
  +      break;
  +   case BSON_TYPE_REGEX:
  +      {
  +         const char *regex;
  +         const char *options;
  +
  +         regex = bson_iter_regex (iter, &options);
  +         ret = bson_append_regex (bson, key, key_length, regex, options);
  +      }
  +      break;
  +   case BSON_TYPE_DBPOINTER:
  +      {
  +         const bson_oid_t *oid;
  +         uint32_t len;
  +         const char *collection;
  +
  +         bson_iter_dbpointer (iter, &len, &collection, &oid);
  +         ret = bson_append_dbpointer (bson, key, key_length, collection, oid);
  +      }
  +      break;
  +   case BSON_TYPE_CODE:
  +      {
  +         uint32_t len;
  +         const char *code;
  +
  +         code = bson_iter_code (iter, &len);
  +         ret = bson_append_code (bson, key, key_length, code);
  +      }
  +      break;
  +   case BSON_TYPE_SYMBOL:
  +      {
  +         uint32_t len;
  +         const char *symbol;
  +
  +         symbol = bson_iter_symbol (iter, &len);
  +         ret = bson_append_symbol (bson, key, key_length, symbol, len);
  +      }
  +      break;
  +   case BSON_TYPE_CODEWSCOPE:
  +      {
  +         const uint8_t *scope = NULL;
  +         uint32_t scope_len = 0;
  +         uint32_t len = 0;
  +         const char *javascript = NULL;
  +         bson_t doc;
  +
  +         javascript = bson_iter_codewscope (iter, &len, &scope_len, &scope);
  +
  +         if (bson_init_static (&doc, scope, scope_len)) {
  +            ret = bson_append_code_with_scope (bson, key, key_length,
  +                                               javascript, &doc);
  +            bson_destroy (&doc);
  +         }
  +      }
  +      break;
  +   case BSON_TYPE_INT32:
  +      ret = bson_append_int32 (bson, key, key_length, bson_iter_int32 (iter));
  +      break;
  +   case BSON_TYPE_TIMESTAMP:
  +      {
  +         uint32_t ts;
  +         uint32_t inc;
  +
  +         bson_iter_timestamp (iter, &ts, &inc);
  +         ret = bson_append_timestamp (bson, key, key_length, ts, inc);
  +      }
  +      break;
  +   case BSON_TYPE_INT64:
  +      ret = bson_append_int64 (bson, key, key_length, bson_iter_int64 (iter));
  +      break;
  +   case BSON_TYPE_MAXKEY:
  +      ret = bson_append_maxkey (bson, key, key_length);
  +      break;
  +   case BSON_TYPE_MINKEY:
  +      ret = bson_append_minkey (bson, key, key_length);
  +      break;
  +   default:
  +      break;
  +   }
  +
  +   return ret;
  +}
  +
  +
  +bool
  +bson_append_maxkey (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length)
  +{
  +   static const uint8_t type = BSON_TYPE_MAXKEY;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 3,
  +                        (1 + key_length + 1),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero);
  +}
  +
  +
  +bool
  +bson_append_minkey (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length)
  +{
  +   static const uint8_t type = BSON_TYPE_MINKEY;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 3,
  +                        (1 + key_length + 1),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero);
  +}
  +
  +
  +bool
  +bson_append_null (bson_t     *bson,
  +                  const char *key,
  +                  int         key_length)
  +{
  +   static const uint8_t type = BSON_TYPE_NULL;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 3,
  +                        (1 + key_length + 1),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero);
  +}
  +
  +
  +bool
  +bson_append_oid (bson_t           *bson,
  +                 const char       *key,
  +                 int               key_length,
  +                 const bson_oid_t *value)
  +{
  +   static const uint8_t type = BSON_TYPE_OID;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (value, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + 12),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        12, value);
  +}
  +
  +
  +bool
  +bson_append_regex (bson_t     *bson,
  +                   const char *key,
  +                   int         key_length,
  +                   const char *regex,
  +                   const char *options)
  +{
  +   static const uint8_t type = BSON_TYPE_REGEX;
  +   uint32_t regex_len;
  +   uint32_t options_len;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   if (!regex) {
  +      regex = "";
  +   }
  +
  +   if (!options) {
  +      options = "";
  +   }
  +
  +   regex_len = (int)strlen (regex) + 1;
  +   options_len = (int)strlen (options) + 1;
  +
  +   return _bson_append (bson, 5,
  +                        (1 + key_length + 1 + regex_len + options_len),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        regex_len, regex,
  +                        options_len, options);
  +}
  +
  +
  +bool
  +bson_append_utf8 (bson_t     *bson,
  +                  const char *key,
  +                  int         key_length,
  +                  const char *value,
  +                  int         length)
  +{
  +   static const uint8_t type = BSON_TYPE_UTF8;
  +   uint32_t length_le;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (BSON_UNLIKELY (!value)) {
  +      return bson_append_null (bson, key, key_length);
  +   }
  +
  +   if (BSON_UNLIKELY (key_length < 0)) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   if (BSON_UNLIKELY (length < 0)) {
  +      length = (int)strlen (value);
  +   }
  +
  +   length_le = BSON_UINT32_TO_LE (length + 1);
  +
  +   return _bson_append (bson, 6,
  +                        (1 + key_length + 1 + 4 + length + 1),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        4, &length_le,
  +                        length, value,
  +                        1, &gZero);
  +}
  +
  +
  +bool
  +bson_append_symbol (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length,
  +                    const char *value,
  +                    int         length)
  +{
  +   static const uint8_t type = BSON_TYPE_SYMBOL;
  +   uint32_t length_le;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (!value) {
  +      return bson_append_null (bson, key, key_length);
  +   }
  +
  +   if (key_length < 0) {
  +      key_length = (int)strlen (key);
  +   }
  +
  +   if (length < 0) {
  +      length =(int)strlen (value);
  +   }
  +
  +   length_le = BSON_UINT32_TO_LE (length + 1);
  +
  +   return _bson_append (bson, 6,
  +                        (1 + key_length + 1 + 4 + length + 1),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        4, &length_le,
  +                        length, value,
  +                        1, &gZero);
  +}
  +
  +
  +bool
  +bson_append_time_t (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length,
  +                    time_t      value)
  +{
  +#ifdef BSON_OS_WIN32
  +   struct timeval tv = { (long)value, 0 };
  +#else
  +   struct timeval tv = { value, 0 };
  +#endif
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   return bson_append_timeval (bson, key, key_length, &tv);
  +}
  +
  +
  +bool
  +bson_append_timestamp (bson_t       *bson,
  +                       const char   *key,
  +                       int           key_length,
  +                       uint32_t timestamp,
  +                       uint32_t increment)
  +{
  +   static const uint8_t type = BSON_TYPE_TIMESTAMP;
  +   uint64_t value;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length =(int)strlen (key);
  +   }
  +
  +   value = ((((uint64_t)timestamp) << 32) | ((uint64_t)increment));
  +   value = BSON_UINT64_TO_LE (value);
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + 8),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        8, &value);
  +}
  +
  +
  +bool
  +bson_append_now_utc (bson_t     *bson,
  +                     const char *key,
  +                     int         key_length)
  +{
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (key_length >= -1, false);
  +
  +   return bson_append_time_t (bson, key, key_length, time (NULL));
  +}
  +
  +
  +bool
  +bson_append_date_time (bson_t      *bson,
  +                       const char  *key,
  +                       int          key_length,
  +                       int64_t value)
  +{
  +   static const uint8_t type = BSON_TYPE_DATE_TIME;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (value, false);
  +
  +   if (key_length < 0) {
  +      key_length =(int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 4,
  +                        (1 + key_length + 1 + 8),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero,
  +                        8, &value);
  +}
  +
  +
  +bool
  +bson_append_timeval (bson_t         *bson,
  +                     const char     *key,
  +                     int             key_length,
  +                     struct timeval *value)
  +{
  +   uint64_t unix_msec;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +   bson_return_val_if_fail (value, false);
  +
  +   unix_msec = BSON_UINT64_TO_LE ((((uint64_t)value->tv_sec) * 1000UL) +
  +                                  (value->tv_usec / 1000UL));
  +   return bson_append_date_time (bson, key, key_length, unix_msec);
  +}
  +
  +
  +bool
  +bson_append_undefined (bson_t     *bson,
  +                       const char *key,
  +                       int         key_length)
  +{
  +   static const uint8_t type = BSON_TYPE_UNDEFINED;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (key_length < 0) {
  +      key_length =(int)strlen (key);
  +   }
  +
  +   return _bson_append (bson, 3,
  +                        (1 + key_length + 1),
  +                        1, &type,
  +                        key_length, key,
  +                        1, &gZero);
  +}
  +
  +
  +void
  +bson_init (bson_t *bson)
  +{
  +   bson_impl_inline_t *impl = (bson_impl_inline_t *)bson;
  +
  +   bson_return_if_fail (bson);
  +
  +   impl->flags = BSON_FLAG_INLINE | BSON_FLAG_STATIC;
  +   impl->len = 5;
  +   impl->data[0] = 5;
  +   impl->data[1] = 0;
  +   impl->data[2] = 0;
  +   impl->data[3] = 0;
  +   impl->data[4] = 0;
  +}
  +
  +
  +void
  +bson_reinit (bson_t *bson)
  +{
  +   uint8_t *data;
  +
  +   bson_return_if_fail (bson);
  +
  +   data = _bson_data (bson);
  +
  +   bson->len = 5;
  +
  +   data [0] = 5;
  +   data [1] = 0;
  +   data [2] = 0;
  +   data [3] = 0;
  +   data [4] = 0;
  +}
  +
  +
  +bool
  +bson_init_static (bson_t             *bson,
  +                  const uint8_t *data,
  +                  uint32_t       length)
  +{
  +   bson_impl_alloc_t *impl = (bson_impl_alloc_t *)bson;
  +   uint32_t len_le;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (data, false);
  +
  +   if ((length < 5) || (length > INT_MAX)) {
  +      return false;
  +   }
  +
  +   memcpy (&len_le, data, 4);
  +
  +   if (BSON_UINT32_FROM_LE (len_le) != length) {
  +      return false;
  +   }
  +
  +   if (data[length - 1]) {
  +      return false;
  +   }
  +
  +   impl->flags = BSON_FLAG_STATIC | BSON_FLAG_RDONLY;
  +   impl->len = length;
  +   impl->parent = NULL;
  +   impl->depth = 0;
  +   impl->buf = &impl->alloc;
  +   impl->buflen = &impl->alloclen;
  +   impl->offset = 0;
  +   impl->alloc = (uint8_t *)data;
  +   impl->alloclen = length;
  +   impl->realloc = NULL;
  +
  +   return true;
  +}
  +
  +
  +bson_t *
  +bson_new (void)
  +{
  +   bson_impl_inline_t *impl;
  +   bson_t *bson;
  +
  +   bson = bson_malloc (sizeof *bson);
  +
  +   impl = (bson_impl_inline_t *)bson;
  +   impl->flags = BSON_FLAG_INLINE;
  +   impl->len = 5;
  +   impl->data[0] = 5;
  +   impl->data[1] = 0;
  +   impl->data[2] = 0;
  +   impl->data[3] = 0;
  +   impl->data[4] = 0;
  +
  +   return bson;
  +}
  +
  +
  +bson_t *
  +bson_sized_new (size_t size)
  +{
  +   bson_impl_alloc_t *impl_a;
  +   bson_impl_inline_t *impl_i;
  +   bson_t *b;
  +
  +   bson_return_val_if_fail (size <= INT32_MAX, NULL);
  +
  +   b = bson_malloc (sizeof *b);
  +   impl_a = (bson_impl_alloc_t *)b;
  +   impl_i = (bson_impl_inline_t *)b;
  +
  +   if (size <= sizeof impl_i->data) {
  +      bson_init (b);
  +      b->flags &= ~BSON_FLAG_STATIC;
  +   } else {
  +      impl_a->flags = BSON_FLAG_NONE;
  +      impl_a->len = 5;
  +      impl_a->parent = NULL;
  +      impl_a->depth = 0;
  +      impl_a->buf = &impl_a->alloc;
  +      impl_a->buflen = &impl_a->alloclen;
  +      impl_a->offset = 0;
  +      impl_a->alloclen = MAX (5, size);
  +      impl_a->alloc = bson_malloc (impl_a->alloclen);
  +      impl_a->alloc[0] = 5;
  +      impl_a->alloc[1] = 0;
  +      impl_a->alloc[2] = 0;
  +      impl_a->alloc[3] = 0;
  +      impl_a->alloc[4] = 0;
  +      impl_a->realloc = bson_realloc;
  +   }
  +
  +   return b;
  +}
  +
  +
  +bson_t *
  +bson_new_from_data (const uint8_t *data,
  +                    uint32_t       length)
  +{
  +   uint32_t len_le;
  +   bson_t *bson;
  +
  +   bson_return_val_if_fail (data, NULL);
  +
  +   if (length < 5) {
  +      return NULL;
  +   }
  +
  +   if (data[length - 1]) {
  +      return NULL;
  +   }
  +
  +   memcpy (&len_le, data, 4);
  +
  +   if (length != BSON_UINT32_FROM_LE (len_le)) {
  +      return NULL;
  +   }
  +
  +   bson = bson_sized_new (length);
  +   memcpy (_bson_data (bson), data, length);
  +   bson->len = length;
  +
  +   return bson;
  +}
  +
  +
  +bson_t *
  +bson_copy (const bson_t *bson)
  +{
  +   const uint8_t *data;
  +
  +   bson_return_val_if_fail (bson, NULL);
  +
  +   data = _bson_data (bson);
  +   return bson_new_from_data (data, bson->len);
  +}
  +
  +
  +void
  +bson_copy_to (const bson_t *src,
  +              bson_t       *dst)
  +{
  +   const uint8_t *data;
  +   bson_impl_alloc_t *adst;
  +   uint32_t len;
  +
  +   bson_return_if_fail (src);
  +   bson_return_if_fail (dst);
  +
  +   if ((src->flags & BSON_FLAG_INLINE)) {
  +      memcpy (dst, src, sizeof *dst);
  +      dst->flags = (BSON_FLAG_STATIC | BSON_FLAG_INLINE);
  +      return;
  +   }
  +
  +   data = _bson_data (src);
  +   len = bson_next_power_of_two (src->len);
  +
  +   adst = (bson_impl_alloc_t *)dst;
  +   adst->flags = BSON_FLAG_STATIC;
  +   adst->len = src->len;
  +   adst->parent = NULL;
  +   adst->depth = 0;
  +   adst->buf = &adst->alloc;
  +   adst->buflen = &adst->alloclen;
  +   adst->offset = 0;
  +   adst->alloc = bson_malloc (len);
  +   adst->alloclen = len;
  +   adst->realloc = bson_realloc;
  +   memcpy (adst->alloc, data, src->len);
  +}
  +
  +
  +static bool
  +should_ignore (const char *first_exclude,
  +               va_list     args,
  +               const char *name)
  +{
  +   bool ret = false;
  +   const char *exclude = first_exclude;
  +   va_list args_copy;
  +
  +   va_copy (args_copy, args);
  +
  +   do {
  +      if (!strcmp (name, exclude)) {
  +         ret = true;
  +         break;
  +      }
  +   } while ((exclude = va_arg (args_copy, const char *)));
  +
  +   va_end (args_copy);
  +
  +   return ret;
  +}
  +
  +
  +static void
  +_bson_copy_to_excluding_va (const bson_t *src,
  +                            bson_t       *dst,
  +                            const char   *first_exclude,
  +                            va_list       args)
  +{
  +   bson_iter_t iter;
  +
  +   bson_init (dst);
  +
  +   if (bson_iter_init (&iter, src)) {
  +      while (bson_iter_next (&iter)) {
  +         if (!should_ignore (first_exclude, args, bson_iter_key (&iter))) {
  +            if (!bson_append_iter (dst, NULL, 0, &iter)) {
  +               /*
  +                * This should not be able to happen since we are copying
  +                * from within a valid bson_t.
  +                */
  +               BSON_ASSERT (false);
  +               return;
  +            }
  +         }
  +      }
  +   }
  +}
  +
  +
  +void
  +bson_copy_to_excluding (const bson_t *src,
  +                        bson_t       *dst,
  +                        const char   *first_exclude,
  +                        ...)
  +{
  +   va_list args;
  +
  +   bson_return_if_fail (src);
  +   bson_return_if_fail (dst);
  +   bson_return_if_fail (first_exclude);
  +
  +   va_start (args, first_exclude);
  +   _bson_copy_to_excluding_va (src, dst, first_exclude, args);
  +   va_end (args);
  +}
  +
  +
  +void
  +bson_destroy (bson_t *bson)
  +{
  +   BSON_ASSERT (bson);
  +
  +   if (!(bson->flags &
  +         (BSON_FLAG_RDONLY | BSON_FLAG_INLINE | BSON_FLAG_NO_FREE))) {
  +      bson_free (*((bson_impl_alloc_t *)bson)->buf);
  +   }
  +
  +   if (!(bson->flags & BSON_FLAG_STATIC)) {
  +      bson_free (bson);
  +   }
  +}
  +
  +
  +const uint8_t *
  +bson_get_data (const bson_t *bson)
  +{
  +   bson_return_val_if_fail (bson, NULL);
  +
  +   return _bson_data (bson);
  +}
  +
  +
  +uint32_t
  +bson_count_keys (const bson_t *bson)
  +{
  +   uint32_t count = 0;
  +   bson_iter_t iter;
  +
  +   bson_return_val_if_fail (bson, 0);
  +
  +   if (bson_iter_init (&iter, bson)) {
  +      while (bson_iter_next (&iter)) {
  +         count++;
  +      }
  +   }
  +
  +   return count;
  +}
  +
  +
  +bool
  +bson_has_field (const bson_t *bson,
  +                const char   *key)
  +{
  +   bson_iter_t iter;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   return bson_iter_init_find (&iter, bson, key);
  +}
  +
  +
  +int
  +bson_compare (const bson_t *bson,
  +              const bson_t *other)
  +{
  +   uint32_t len;
  +   int ret;
  +
  +   if (bson->len != other->len) {
  +      len = MIN (bson->len, other->len);
  +
  +      if (!(ret = memcmp (_bson_data (bson), _bson_data (other), len))) {
  +         ret = bson->len - other->len;
  +      }
  +   } else {
  +      ret = memcmp (_bson_data (bson), _bson_data (other), bson->len);
  +   }
  +
  +   return ret;
  +}
  +
  +
  +bool
  +bson_equal (const bson_t *bson,
  +            const bson_t *other)
  +{
  +   return !bson_compare (bson, other);
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_utf8 (const bson_iter_t *iter,
  +                          const char        *key,
  +                          size_t             v_utf8_len,
  +                          const char        *v_utf8,
  +                          void              *data)
  +{
  +   bson_json_state_t *state = data;
  +   char *escaped;
  +
  +   escaped = bson_utf8_escape_for_json (v_utf8, v_utf8_len);
  +   bson_string_append (state->str, "\"");
  +   bson_string_append (state->str, escaped);
  +   bson_string_append (state->str, "\"");
  +   bson_free (escaped);
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_int32 (const bson_iter_t *iter,
  +                           const char        *key,
  +                           int32_t       v_int32,
  +                           void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append_printf (state->str, "%" PRId32, v_int32);
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_int64 (const bson_iter_t *iter,
  +                           const char        *key,
  +                           int64_t       v_int64,
  +                           void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append_printf (state->str, "%" PRIi64, v_int64);
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_double (const bson_iter_t *iter,
  +                            const char        *key,
  +                            double             v_double,
  +                            void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append_printf (state->str, "%lf", v_double);
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_undefined (const bson_iter_t *iter,
  +                               const char        *key,
  +                               void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "{ \"$undefined\" : true }");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_null (const bson_iter_t *iter,
  +                          const char        *key,
  +                          void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "null");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_oid (const bson_iter_t *iter,
  +                         const char        *key,
  +                         const bson_oid_t  *oid,
  +                         void              *data)
  +{
  +   bson_json_state_t *state = data;
  +   char str[25];
  +
  +   bson_return_val_if_fail (oid, false);
  +
  +   bson_oid_to_string (oid, str);
  +   bson_string_append (state->str, "{ \"$oid\" : \"");
  +   bson_string_append (state->str, str);
  +   bson_string_append (state->str, "\" }");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_binary (const bson_iter_t  *iter,
  +                            const char         *key,
  +                            bson_subtype_t      v_subtype,
  +                            size_t              v_binary_len,
  +                            const uint8_t *v_binary,
  +                            void               *data)
  +{
  +   bson_json_state_t *state = data;
  +   size_t b64_len;
  +   char *b64;
  +
  +   b64_len = (v_binary_len / 3 + 1) * 4 + 1;
  +   b64 = bson_malloc0 (b64_len);
  +   b64_ntop (v_binary, v_binary_len, b64, b64_len);
  +
  +   bson_string_append (state->str, "{ \"$type\" : \"");
  +   bson_string_append_printf (state->str, "%02x", v_subtype);
  +   bson_string_append (state->str, "\", \"$binary\" : \"");
  +   bson_string_append (state->str, b64);
  +   bson_string_append (state->str, "\" }");
  +   bson_free (b64);
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_bool (const bson_iter_t *iter,
  +                          const char        *key,
  +                          bool        v_bool,
  +                          void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, v_bool ? "true" : "false");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_date_time (const bson_iter_t *iter,
  +                               const char        *key,
  +                               int64_t       msec_since_epoch,
  +                               void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "{ \"$date\" : ");
  +   bson_string_append_printf (state->str, "%" PRIi64, msec_since_epoch);
  +   bson_string_append (state->str, " }");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_regex (const bson_iter_t *iter,
  +                           const char        *key,
  +                           const char        *v_regex,
  +                           const char        *v_options,
  +                           void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "{ \"$regex\" : \"");
  +   bson_string_append (state->str, v_regex);
  +   bson_string_append (state->str, "\", \"$options\" : \"");
  +   bson_string_append (state->str, v_options);
  +   bson_string_append (state->str, "\" }");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_timestamp (const bson_iter_t *iter,
  +                               const char        *key,
  +                               uint32_t      v_timestamp,
  +                               uint32_t      v_increment,
  +                               void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "{ \"$timestamp\" : { \"t\" : ");
  +   bson_string_append_printf (state->str, "%u", v_timestamp);
  +   bson_string_append (state->str, ", \"i\" : ");
  +   bson_string_append_printf (state->str, "%u", v_increment);
  +   bson_string_append (state->str, " } }");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_dbpointer (const bson_iter_t *iter,
  +                               const char        *key,
  +                               size_t             v_collection_len,
  +                               const char        *v_collection,
  +                               const bson_oid_t  *v_oid,
  +                               void              *data)
  +{
  +   bson_json_state_t *state = data;
  +   char str[25];
  +
  +   bson_string_append (state->str, "{ \"$ref\" : \"");
  +   bson_string_append (state->str, v_collection);
  +   bson_string_append (state->str, "\"");
  +
  +   if (v_oid) {
  +      bson_oid_to_string (v_oid, str);
  +      bson_string_append (state->str, ", \"$id\" : \"");
  +      bson_string_append (state->str, str);
  +      bson_string_append (state->str, "\"");
  +   }
  +
  +   bson_string_append (state->str, " }");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_minkey (const bson_iter_t *iter,
  +                            const char        *key,
  +                            void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "{ \"$minKey\" : 1 }");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_maxkey (const bson_iter_t *iter,
  +                            const char        *key,
  +                            void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "{ \"$maxKey\" : 1 }");
  +
  +   return false;
  +}
  +
  +
  +
  +
  +static bool
  +_bson_as_json_visit_before (const bson_iter_t *iter,
  +                            const char        *key,
  +                            void              *data)
  +{
  +   bson_json_state_t *state = data;
  +   char *escaped;
  +
  +   if (state->count) {
  +      bson_string_append (state->str, ", ");
  +   }
  +
  +   if (state->keys) {
  +      escaped = bson_utf8_escape_for_json (key, -1);
  +      bson_string_append (state->str, "\"");
  +      bson_string_append (state->str, escaped);
  +      bson_string_append (state->str, "\" : ");
  +      bson_free (escaped);
  +   }
  +
  +   state->count++;
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_code (const bson_iter_t *iter,
  +                          const char        *key,
  +                          size_t             v_code_len,
  +                          const char        *v_code,
  +                          void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "\"");
  +   bson_string_append (state->str, v_code);
  +   bson_string_append (state->str, "\"");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_symbol (const bson_iter_t *iter,
  +                            const char        *key,
  +                            size_t             v_symbol_len,
  +                            const char        *v_symbol,
  +                            void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "\"");
  +   bson_string_append (state->str, v_symbol);
  +   bson_string_append (state->str, "\"");
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_codewscope (const bson_iter_t *iter,
  +                                const char        *key,
  +                                size_t             v_code_len,
  +                                const char        *v_code,
  +                                const bson_t      *v_scope,
  +                                void              *data)
  +{
  +   bson_json_state_t *state = data;
  +
  +   bson_string_append (state->str, "\"");
  +   bson_string_append (state->str, v_code);
  +   bson_string_append (state->str, "\"");
  +
  +   return false;
  +}
  +
  +
  +static const bson_visitor_t bson_as_json_visitors = {
  +   _bson_as_json_visit_before,
  +   NULL, /* visit_after */
  +   NULL, /* visit_corrupt */
  +   _bson_as_json_visit_double,
  +   _bson_as_json_visit_utf8,
  +   _bson_as_json_visit_document,
  +   _bson_as_json_visit_array,
  +   _bson_as_json_visit_binary,
  +   _bson_as_json_visit_undefined,
  +   _bson_as_json_visit_oid,
  +   _bson_as_json_visit_bool,
  +   _bson_as_json_visit_date_time,
  +   _bson_as_json_visit_null,
  +   _bson_as_json_visit_regex,
  +   _bson_as_json_visit_dbpointer,
  +   _bson_as_json_visit_code,
  +   _bson_as_json_visit_symbol,
  +   _bson_as_json_visit_codewscope,
  +   _bson_as_json_visit_int32,
  +   _bson_as_json_visit_timestamp,
  +   _bson_as_json_visit_int64,
  +   _bson_as_json_visit_maxkey,
  +   _bson_as_json_visit_minkey,
  +};
  +
  +
  +static bool
  +_bson_as_json_visit_document (const bson_iter_t *iter,
  +                              const char        *key,
  +                              const bson_t      *v_document,
  +                              void              *data)
  +{
  +   bson_json_state_t *state = data;
  +   bson_json_state_t child_state = { 0, true };
  +   bson_iter_t child;
  +
  +   if (state->depth >= BSON_MAX_RECURSION) {
  +      bson_string_append (state->str, "{ ... }");
  +      return false;
  +   }
  +
  +   if (bson_iter_init (&child, v_document)) {
  +      child_state.str = bson_string_new ("{ ");
  +      child_state.depth = state->depth + 1;
  +      bson_iter_visit_all (&child, &bson_as_json_visitors, &child_state);
  +      bson_string_append (child_state.str, " }");
  +      bson_string_append (state->str, child_state.str->str);
  +      bson_string_free (child_state.str, true);
  +   }
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_as_json_visit_array (const bson_iter_t *iter,
  +                           const char        *key,
  +                           const bson_t      *v_array,
  +                           void              *data)
  +{
  +   bson_json_state_t *state = data;
  +   bson_json_state_t child_state = { 0, false };
  +   bson_iter_t child;
  +
  +   if (state->depth >= BSON_MAX_RECURSION) {
  +      bson_string_append (state->str, "{ ... }");
  +      return false;
  +   }
  +
  +   if (bson_iter_init (&child, v_array)) {
  +      child_state.str = bson_string_new ("[ ");
  +      child_state.depth = state->depth + 1;
  +      bson_iter_visit_all (&child, &bson_as_json_visitors, &child_state);
  +      bson_string_append (child_state.str, " ]");
  +      bson_string_append (state->str, child_state.str->str);
  +      bson_string_free (child_state.str, true);
  +   }
  +
  +   return false;
  +}
  +
  +
  +char *
  +bson_as_json (const bson_t *bson,
  +              size_t       *length)
  +{
  +   bson_json_state_t state;
  +   bson_iter_t iter;
  +
  +   bson_return_val_if_fail (bson, NULL);
  +
  +   if (length) {
  +      *length = 0;
  +   }
  +
  +   if (bson_empty0 (bson)) {
  +      if (length) {
  +         *length = 2;
  +      }
  +
  +      return bson_strdup ("{ }");
  +   }
  +
  +   if (!bson_iter_init (&iter, bson)) {
  +      return NULL;
  +   }
  +
  +   state.count = 0;
  +   state.keys = true;
  +   state.str = bson_string_new ("{ ");
  +   state.depth = 0;
  +   bson_iter_visit_all (&iter, &bson_as_json_visitors, &state);
  +
  +   if (iter.err_off) {
  +      bson_string_free (state.str, true);
  +      if (length) {
  +         *length = 0;
  +      }
  +      return NULL;
  +   }
  +
  +   bson_string_append (state.str, " }");
  +
  +   if (length) {
  +      *length = state.str->len;
  +   }
  +
  +   return bson_string_free (state.str, false);
  +}
  +
  +
  +static bool
  +_bson_iter_validate_utf8 (const bson_iter_t *iter,
  +                          const char        *key,
  +                          size_t             v_utf8_len,
  +                          const char        *v_utf8,
  +                          void              *data)
  +{
  +   bson_validate_state_t *state = data;
  +   bool allow_null;
  +
  +   if ((state->flags & BSON_VALIDATE_UTF8)) {
  +      allow_null = !!(state->flags & BSON_VALIDATE_UTF8_ALLOW_NULL);
  +
  +      if (!bson_utf8_validate (v_utf8, v_utf8_len, allow_null)) {
  +         state->err_offset = iter->off;
  +         return true;
  +      }
  +   }
  +
  +   return false;
  +}
  +
  +
  +static void
  +_bson_iter_validate_corrupt (const bson_iter_t *iter,
  +                             void              *data)
  +{
  +   bson_validate_state_t *state = data;
  +
  +   state->err_offset = iter->err_off;
  +}
  +
  +
  +static bool
  +_bson_iter_validate_before (const bson_iter_t *iter,
  +                            const char        *key,
  +                            void              *data)
  +{
  +   bson_validate_state_t *state = data;
  +
  +   if ((state->flags & BSON_VALIDATE_DOLLAR_KEYS)) {
  +      if (key[0] == '$') {
  +         state->err_offset = iter->off;
  +         return true;
  +      }
  +   }
  +
  +   if ((state->flags & BSON_VALIDATE_DOT_KEYS)) {
  +      if (strstr (key, ".")) {
  +         state->err_offset = iter->off;
  +         return true;
  +      }
  +   }
  +
  +   return false;
  +}
  +
  +
  +static bool
  +_bson_iter_validate_codewscope (const bson_iter_t *iter,
  +                                const char        *key,
  +                                size_t             v_code_len,
  +                                const char        *v_code,
  +                                const bson_t      *v_scope,
  +                                void              *data)
  +{
  +   bson_validate_state_t *state = data;
  +   size_t offset;
  +
  +   if (!bson_validate (v_scope, state->flags, &offset)) {
  +      state->err_offset = iter->off + offset;
  +      return false;
  +   }
  +
  +   return true;
  +}
  +
  +
  +static bool
  +_bson_iter_validate_document (const bson_iter_t *iter,
  +                              const char        *key,
  +                              const bson_t      *v_document,
  +                              void              *data);
  +
  +
  +static const bson_visitor_t bson_validate_funcs = {
  +   _bson_iter_validate_before,
  +   NULL, /* visit_after */
  +   _bson_iter_validate_corrupt,
  +   NULL, /* visit_double */
  +   _bson_iter_validate_utf8,
  +   _bson_iter_validate_document,
  +   _bson_iter_validate_document, /* visit_array */
  +   NULL, /* visit_binary */
  +   NULL, /* visit_undefined */
  +   NULL, /* visit_oid */
  +   NULL, /* visit_bool */
  +   NULL, /* visit_date_time */
  +   NULL, /* visit_null */
  +   NULL, /* visit_regex */
  +   NULL, /* visit_dbpoint */
  +   NULL, /* visit_code */
  +   NULL, /* visit_symbol */
  +   _bson_iter_validate_codewscope,
  +};
  +
  +
  +static bool
  +_bson_iter_validate_document (const bson_iter_t *iter,
  +                              const char        *key,
  +                              const bson_t      *v_document,
  +                              void              *data)
  +{
  +   bson_validate_state_t *state = data;
  +   bson_iter_t child;
  +
  +   if (!bson_iter_init (&child, v_document)) {
  +      state->err_offset = iter->off;
  +      return true;
  +   }
  +
  +   bson_iter_visit_all (&child, &bson_validate_funcs, state);
  +
  +   return false;
  +}
  +
  +
  +bool
  +bson_validate (const bson_t         *bson,
  +               bson_validate_flags_t flags,
  +               size_t               *offset)
  +{
  +   bson_validate_state_t state = { flags, -1 };
  +   bson_iter_t iter;
  +
  +   if (!bson_iter_init (&iter, bson)) {
  +      state.err_offset = 0;
  +      goto failure;
  +   }
  +
  +   _bson_iter_validate_document (&iter, NULL, bson, &state);
  +
  +failure:
  +
  +   if (offset) {
  +      *offset = state.err_offset;
  +   }
  +
  +   return state.err_offset < 0;
  +}
  +
  +
  +bool
  +bson_concat (bson_t       *dst,
  +             const bson_t *src)
  +{
  +   BSON_ASSERT (dst);
  +   BSON_ASSERT (src);
  +
  +   if (!bson_empty (src)) {
  +      return _bson_append (dst, 1, src->len - 5,
  +                           src->len - 5, _bson_data (src) + 4);
  +   }
  +
  +   return true;
  +}
  +
  +/*==============================================================*/
  +/* --- bson-clock.c */
  +
  +#ifdef __APPLE__
  +# include <mach/clock.h>
  +# include <mach/mach.h>
  +# include <mach/mach_time.h>
  +#endif
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_gettimeofday --
  + *
  + *       A wrapper around gettimeofday() with fallback support for Windows.
  + *
  + * Returns:
  + *       0 if successful.
  + *
  + * Side effects:
  + *       @tv and @tz are set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int
  +bson_gettimeofday (struct timeval  *tv, /* OUT */
  +                   struct timezone *tz) /* OUT */
  +{
  +#if defined(_WIN32)
  +# if defined(_MSC_VER)
  +#  define DELTA_EPOCH_IN_MICROSEC 11644473600000000Ui64
  +# else
  +#  define DELTA_EPOCH_IN_MICROSEC 11644473600000000ULL
  +# endif
  +  FILETIME ft;
  +  uint64_t tmp = 0;
  +
  +   /*
  +    * The const value is shamelessy stolen from
  +    * http://www.boost.org/doc/libs/1_55_0/boost/chrono/detail/inlined/win/chrono.hpp
  +    *
  +    * File times are the number of 100 nanosecond intervals elapsed since
  +    * 12:00 am Jan 1, 1601 UTC.  I haven't check the math particularly hard
  +    *
  +    * ...  good luck
  +    */
  +
  +   if (tv) {
  +      GetSystemTimeAsFileTime (&ft);
  +
  +      /* pull out of the filetime into a 64 bit uint */
  +      tmp |= ft.dwHighDateTime;
  +      tmp <<= 32;
  +      tmp |= ft.dwLowDateTime;
  +
  +      /* convert from 100's of nanosecs to microsecs */
  +      tmp /= 10;
  +
  +      /* adjust to unix epoch */
  +      tmp -= DELTA_EPOCH_IN_MICROSEC;
  +
  +      tv->tv_sec = (long)(tmp / 1000000UL);
  +      tv->tv_usec = (long)(tmp % 1000000UL);
  +   }
  +
  +   BSON_ASSERT (NULL == tz);
  +
  +   return 0;
  +#else
  +   return gettimeofday (tv, tz);
  +#endif
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_get_monotonic_time --
  + *
  + *       Returns the monotonic system time, if available. A best effort is
  + *       made to use the monotonic clock. However, some systems may not
  + *       support such a feature.
  + *
  + * Returns:
  + *       The monotonic clock in microseconds.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int64_t
  +bson_get_monotonic_time (void)
  +{
  +#if defined(BSON_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
  +   struct timespec ts;
  +   clock_gettime (CLOCK_MONOTONIC, &ts);
  +   return ((ts.tv_sec * 1000000UL) + (ts.tv_nsec / 1000UL));
  +#elif defined(__APPLE__)
  +   static mach_timebase_info_data_t info = { 0 };
  +   static double ratio = 0.0;
  +
  +   if (!info.denom) {
  +      mach_timebase_info (&info);
  +      ratio = info.numer / info.denom;
  +   }
  +
  +   return mach_absolute_time () * ratio;
  +#elif defined(_WIN32)
  +   /* Despite it's name, this is in milliseconds! */
  +   int64_t ticks = GetTickCount64 ();
  +   return (ticks * 1000L);
  +#else
  +# warning "Monotonic clock is not yet supported on your platform."
  +   struct timeval tv;
  +
  +   bson_gettimeofday (&tv, NULL);
  +   return (tv.tv_sec * 1000000UL) + tv.tv_usec;
  +#endif
  +}
  +
  +/*==============================================================*/
  +/* --- bson-context.c */
  +
  +#if defined(__linux__)
  +#include <sys/syscall.h>
  +#endif
  +
  +#ifndef HOST_NAME_MAX
  +#define HOST_NAME_MAX 256
  +#endif
  +
  +/*
  + * Globals.
  + */
  +static bson_context_t *gContextDefault;
  +
  +
  +#if defined(__linux__)
  +static uint16_t
  +gettid (void)
  +{
  +   return syscall (SYS_gettid);
  +}
  +#endif
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_get_oid_host --
  + *
  + *       Retrieves the first three bytes of MD5(hostname) and assigns them
  + *       to the host portion of oid.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is modified.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_get_oid_host (bson_context_t *context,  /* IN */
  +                            bson_oid_t     *oid)      /* OUT */
  +{
  +   uint8_t *bytes = (uint8_t *)oid;
  +   uint8_t digest[16];
  +   bson_md5_t md5;
  +   char hostname[HOST_NAME_MAX];
  +
  +   BSON_ASSERT (context);
  +   BSON_ASSERT (oid);
  +
  +   gethostname (hostname, sizeof hostname);
  +   hostname[HOST_NAME_MAX - 1] = '\0';
  +
  +   bson_md5_init (&md5);
  +   bson_md5_append (&md5, (const uint8_t *)hostname, (uint32_t)strlen (hostname));
  +   bson_md5_finish (&md5, &digest[0]);
  +
  +   bytes[4] = digest[0];
  +   bytes[5] = digest[1];
  +   bytes[6] = digest[2];
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_get_oid_host_cached --
  + *
  + *       Fetch the cached copy of the MD5(hostname).
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is modified.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_get_oid_host_cached (bson_context_t *context, /* IN */
  +                                   bson_oid_t     *oid)     /* OUT */
  +{
  +   BSON_ASSERT (context);
  +   BSON_ASSERT (oid);
  +
  +   oid->bytes[4] = context->md5[0];
  +   oid->bytes[5] = context->md5[1];
  +   oid->bytes[6] = context->md5[2];
  +}
  +
  +
  +static BSON_INLINE uint16_t
  +_bson_getpid (void)
  +{
  +   uint16_t pid;
  +#ifdef BSON_OS_WIN32
  +   DWORD real_pid;
  +
  +   real_pid = GetCurrentProcessId ();
  +   pid = (real_pid & 0xFFFF) ^ ((real_pid >> 16) & 0xFFFF);
  +#else
  +   pid = getpid ();
  +#endif
  +
  +   return pid;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_get_oid_pid --
  + *
  + *       Initialize the pid field of @oid.
  + *
  + *       The pid field is 2 bytes, big-endian for memcmp().
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is modified.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_get_oid_pid (bson_context_t *context, /* IN */
  +                           bson_oid_t     *oid)     /* OUT */
  +{
  +   uint16_t pid = _bson_getpid ();
  +   uint8_t *bytes = (uint8_t *)&pid;
  +
  +   BSON_ASSERT (context);
  +   BSON_ASSERT (oid);
  +
  +   pid = BSON_UINT16_TO_BE (pid);
  +
  +   oid->bytes[7] = bytes[0];
  +   oid->bytes[8] = bytes[1];
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_get_oid_pid_cached --
  + *
  + *       Fetch the cached copy of the current pid.
  + *       This helps avoid multiple calls to getpid() which is slower
  + *       on some systems.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is modified.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_get_oid_pid_cached (bson_context_t *context, /* IN */
  +                                  bson_oid_t     *oid)     /* OUT */
  +{
  +   oid->bytes[7] = context->pidbe[0];
  +   oid->bytes[8] = context->pidbe[1];
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_get_oid_seq32 --
  + *
  + *       32-bit sequence generator, non-thread-safe version.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is modified.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_get_oid_seq32 (bson_context_t *context, /* IN */
  +                             bson_oid_t     *oid)     /* OUT */
  +{
  +   uint32_t seq = context->seq32++;
  +
  +   seq = BSON_UINT32_TO_BE (seq);
  +   memcpy (&oid->bytes[9], ((uint8_t *)&seq) + 1, 3);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_get_oid_seq32_threadsafe --
  + *
  + *       Thread-safe version of 32-bit sequence generator.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is modified.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_get_oid_seq32_threadsafe (bson_context_t *context, /* IN */
  +                                        bson_oid_t     *oid)     /* OUT */
  +{
  +#if defined WITH_OID32_PT
  +   uint32_t seq;
  +   bson_mutex_lock (&context->_m32);
  +   seq = context->seq32++;
  +   bson_mutex_unlock (&context->_m32);
  +#else
  +   uint32_t seq = bson_atomic_int_add (&context->seq32, 1);
  +#endif
  +
  +   seq = BSON_UINT32_TO_BE (seq);
  +   memcpy (&oid->bytes[9], ((uint8_t *)&seq) + 1, 3);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_get_oid_seq64 --
  + *
  + *       64-bit oid sequence generator, non-thread-safe version.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is modified.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_get_oid_seq64 (bson_context_t *context, /* IN */
  +                             bson_oid_t     *oid)     /* OUT */
  +{
  +   uint64_t seq;
  +
  +   BSON_ASSERT (context);
  +   BSON_ASSERT (oid);
  +
  +   seq = BSON_UINT64_TO_BE (context->seq64++);
  +   memcpy (&oid->bytes[4], &seq, 8);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_get_oid_seq64_threadsafe --
  + *
  + *       Thread-safe 64-bit sequence generator.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is modified.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_get_oid_seq64_threadsafe (bson_context_t *context, /* IN */
  +                                        bson_oid_t     *oid)     /* OUT */
  +{
  +#if defined WITH_OID64_PT
  +   uint64_t seq;
  +   bson_mutex_lock (&context->_m64);
  +   seq = context->seq64++;
  +   bson_mutex_unlock (&context->_m64);
  +#elif defined BSON_OS_WIN32
  +   uint64_t seq = InterlockedIncrement64 ((int64_t *)&context->seq64);
  +#else
  +   uint64_t seq = __sync_fetch_and_add_8 (&context->seq64, 1);
  +#endif
  +
  +   seq = BSON_UINT64_TO_BE (seq);
  +   memcpy (&oid->bytes[4], &seq, 8);
  +}
  +
  +
  +/**
  + * bson_context_new:
  + * @flags: A #bson_context_flags_t.
  + *
  + * Returns: (transfer full): A newly allocated bson_context_t that should be
  + *   freed with bson_context_destroy().
  + */
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_context_new --
  + *
  + *       Initializes a new context with the flags specified.
  + *
  + *       In most cases, you want to call this with @flags set to
  + *       BSON_CONTEXT_NONE.
  + *
  + *       If you are running on Linux, %BSON_CONTEXT_USE_TASK_ID can result
  + *       in a healthy speedup for multi-threaded scenarios.
  + *
  + *       If you absolutely must have a single context for your application
  + *       and use more than one thread, then %BSON_CONTEXT_THREAD_SAFE should
  + *       be bitwise-or'd with your flags. This requires synchronization
  + *       between threads.
  + *
  + *       If you expect your hostname to change often, you may consider
  + *       specifying %BSON_CONTEXT_DISABLE_HOST_CACHE so that gethostname()
  + *       is called for every OID generated. This is much slower.
  + *
  + *       If you expect your pid to change without notice, such as from an
  + *       unexpected call to fork(), then specify
  + *       %BSON_CONTEXT_DISABLE_PID_CACHE.
  + *
  + * Returns:
  + *       A newly allocated bson_context_t that should be freed with
  + *       bson_context_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_context_t *
  +bson_context_new (bson_context_flags_t flags) /* IN */
  +{
  +   bson_context_t *context;
  +   struct timeval tv;
  +   uint16_t pid;
  +   unsigned int seed[3];
  +   unsigned int real_seed;
  +   bson_oid_t oid;
  +
  +   context = bson_malloc0 (sizeof *context);
  +
  +   context->flags = flags;
  +   context->oid_get_host = _bson_context_get_oid_host_cached;
  +   context->oid_get_pid = _bson_context_get_oid_pid_cached;
  +   context->oid_get_seq32 = _bson_context_get_oid_seq32;
  +   context->oid_get_seq64 = _bson_context_get_oid_seq64;
  +
  +   /*
  +    * Generate a seed for our the random starting position of our increment
  +    * bytes. We mask off the last nibble so that the last digit of the OID will
  +    * start at zero. Just to be nice.
  +    *
  +    * The seed itself is made up of the current time in seconds, milliseconds,
  +    * and pid xored together. I welcome better solutions if at all necessary.
  +    */
  +   bson_gettimeofday (&tv, NULL);
  +   seed[0] = tv.tv_sec;
  +   seed[1] = tv.tv_usec;
  +   seed[2] = _bson_getpid ();
  +   real_seed = seed[0] ^ seed[1] ^ seed[2];
  +
  +#ifdef BSON_OS_WIN32
  +   /* ms's runtime is multithreaded by default, so no rand_r */
  +   srand(real_seed);
  +   context->seq32 = rand() & 0x007FFFF0;
  +#else
  +   context->seq32 = rand_r (&real_seed) & 0x007FFFF0;
  +#endif
  +
  +   if ((flags & BSON_CONTEXT_DISABLE_HOST_CACHE)) {
  +      context->oid_get_host = _bson_context_get_oid_host;
  +   } else {
  +      _bson_context_get_oid_host (context, &oid);
  +      context->md5[0] = oid.bytes[4];
  +      context->md5[1] = oid.bytes[5];
  +      context->md5[2] = oid.bytes[6];
  +   }
  +
  +   if ((flags & BSON_CONTEXT_THREAD_SAFE)) {
  +#if defined WITH_OID32_PT
  +      bson_mutex_init (&context->_m32, NULL);
  +#endif
  +#if defined WITH_OID64_PT
  +      bson_mutex_init (&context->_m64, NULL);
  +#endif
  +      context->oid_get_seq32 = _bson_context_get_oid_seq32_threadsafe;
  +      context->oid_get_seq64 = _bson_context_get_oid_seq64_threadsafe;
  +   }
  +
  +   if ((flags & BSON_CONTEXT_DISABLE_PID_CACHE)) {
  +      context->oid_get_pid = _bson_context_get_oid_pid;
  +   } else {
  +      pid = BSON_UINT16_TO_BE (_bson_getpid());
  +#if defined(__linux__)
  +
  +      if ((flags & BSON_CONTEXT_USE_TASK_ID)) {
  +         int32_t tid;
  +
  +         if ((tid = gettid ())) {
  +            pid = BSON_UINT16_TO_BE (tid);
  +         }
  +      }
  +
  +#endif
  +      memcpy (&context->pidbe[0], &pid, 2);
  +   }
  +
  +   return context;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_context_destroy --
  + *
  + *       Cleans up a bson_context_t and releases any associated resources.
  + *       This should be called when you are done using @context.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_context_destroy (bson_context_t *context)  /* IN */
  +{
  +#if defined WITH_OID32_PT
  +   bson_mutex_destroy (&context->_m32);
  +#endif
  +#if defined WITH_OID64_PT
  +   bson_mutex_destroy (&context->_m64);
  +#endif
  +   memset (context, 0, sizeof *context);
  +   bson_free (context);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_context_destroy_default --
  + *
  + *       Cleanup the default context on exit.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_context_destroy_default (void)
  +{
  +   if (gContextDefault) {
  +      bson_context_destroy (gContextDefault);
  +      gContextDefault = NULL;
  +   }
  +}
  +
  +
  +static
  +BSON_ONCE_FUN(_bson_context_init_default)
  +{
  +   gContextDefault = bson_context_new ((BSON_CONTEXT_THREAD_SAFE |
  +                                        BSON_CONTEXT_DISABLE_PID_CACHE));
  +   atexit (_bson_context_destroy_default);
  +   BSON_ONCE_RETURN;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_context_get_default --
  + *
  + *       Fetches the default, thread-safe implementation of #bson_context_t.
  + *       If you need faster generation, it is recommended you create your
  + *       own #bson_context_t with bson_context_new().
  + *
  + * Returns:
  + *       A shared instance to the default #bson_context_t. This should not
  + *       be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_context_t *
  +bson_context_get_default (void)
  +{
  +   static bson_once_t once = BSON_ONCE_INIT;
  +
  +   bson_once (&once, _bson_context_init_default);
  +
  +   return gContextDefault;
  +}
  +
  +/*==============================================================*/
  +/* --- bson-error.c */
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_set_error --
  + *
  + *       Initializes @error using the parameters specified.
  + *
  + *       @domain is an application specific error domain which should
  + *       describe which module initiated the error. Think of this as the
  + *       exception type.
  + *
  + *       @code is the @domain specific error code.
  + *
  + *       @format is used to generate the format string. It uses vsnprintf()
  + *       internally so the format should match what you would use there.
  + *
  + * Parameters:
  + *       @error: A #bson_error_t.
  + *       @domain: The error domain.
  + *       @code: The error code.
  + *       @format: A printf style format string.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @error is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_set_error (bson_error_t *error,  /* OUT */
  +                uint32_t      domain, /* IN */
  +                uint32_t      code,   /* IN */
  +                const char   *format, /* IN */
  +                ...)                  /* IN */
  +{
  +   va_list args;
  +
  +   if (error) {
  +      error->domain = domain;
  +      error->code = code;
  +
  +      va_start (args, format);
  +      bson_vsnprintf (error->message, sizeof error->message, format, args);
  +      va_end (args);
  +
  +      error->message[sizeof error->message - 1] = '\0';
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_strerror_r --
  + *
  + *       This is a reentrant safe macro for strerror.
  + *
  + *       The resulting string is stored in @buf and @buf is returned.
  + *
  + * Returns:
  + *       A pointer to a static string or @buf.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +bson_strerror_r (int     err_code,  /* IN */
  +                 char   *buf,       /* IN */
  +                 size_t  buflen)    /* IN */
  +{
  +   static const char *unknown_msg = "Unknown error";
  +   char *ret = NULL;
  +
  +#if defined(__GNUC__) && defined(_GNU_SOURCE)
  +   ret = strerror_r (err_code, buf, buflen);
  +#elif defined(_WIN32)
  +   if (strerror_s (buf, buflen, err_code) != 0) {
  +      ret = buf;
  +   }
  +#else /* XSI strerror_r */
  +   if (strerror_r (err_code, buf, buflen) != 0) {
  +      ret = buf;
  +   }
  +#endif
  +
  +   if (!ret) {
  +      memcpy (buf, unknown_msg, MIN (buflen, strlen (unknown_msg)));
  +      buf [buflen - 1] = '\0';
  +      ret = buf;
  +   }
  +
  +   return buf;
  +}
  +
  +/*==============================================================*/
  +/* --- bson-iter.c */
  +
  +#define ITER_TYPE(i) ((bson_type_t) *((i)->raw + (i)->type))
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_init --
  + *
  + *       Initializes @iter to be used to iterate @bson.
  + *
  + * Returns:
  + *       true if bson_iter_t was initialized. otherwise false.
  + *
  + * Side effects:
  + *       @iter is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_init (bson_iter_t  *iter, /* OUT */
  +                const bson_t *bson) /* IN */
  +{
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (bson, false);
  +
  +   if (BSON_UNLIKELY (bson->len < 5)) {
  +      return false;
  +   }
  +
  +   iter->raw = bson_get_data (bson);
  +   iter->len = bson->len;
  +   iter->off = 0;
  +   iter->type = 0;
  +   iter->key = 0;
  +   iter->d1 = 0;
  +   iter->d2 = 0;
  +   iter->d3 = 0;
  +   iter->d4 = 0;
  +   iter->next_off = 4;
  +   iter->err_off = 0;
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_recurse --
  + *
  + *       Creates a new sub-iter looking at the document or array that @iter
  + *       is currently pointing at.
  + *
  + * Returns:
  + *       true if successful and @child was initialized.
  + *
  + * Side effects:
  + *       @child is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_recurse (const bson_iter_t *iter,  /* IN */
  +                   bson_iter_t       *child) /* OUT */
  +{
  +   const uint8_t *data = NULL;
  +   uint32_t len = 0;
  +
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (child, false);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_DOCUMENT) {
  +      bson_iter_document (iter, &len, &data);
  +   } else if (ITER_TYPE (iter) == BSON_TYPE_ARRAY) {
  +      bson_iter_array (iter, &len, &data);
  +   } else {
  +      return false;
  +   }
  +
  +   child->raw = data;
  +   child->len = len;
  +   child->off = 0;
  +   child->type = 0;
  +   child->key = 0;
  +   child->d1 = 0;
  +   child->d2 = 0;
  +   child->d3 = 0;
  +   child->d4 = 0;
  +   child->next_off = 4;
  +   child->err_off = 0;
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_init_find --
  + *
  + *       Initializes a #bson_iter_t and moves the iter to the first field
  + *       matching @key.
  + *
  + * Returns:
  + *       true if the field named @key was found; otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_init_find (bson_iter_t  *iter, /* INOUT */
  +                     const bson_t *bson, /* IN */
  +                     const char   *key)  /* IN */
  +{
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   return bson_iter_init (iter, bson) && bson_iter_find (iter, key);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_init_find_case --
  + *
  + *       A case-insensitive version of bson_iter_init_find().
  + *
  + * Returns:
  + *       true if the field was found and @iter is observing that field.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_init_find_case (bson_iter_t  *iter, /* INOUT */
  +                          const bson_t *bson, /* IN */
  +                          const char   *key)  /* IN */
  +{
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   return bson_iter_init (iter, bson) && bson_iter_find_case (iter, key);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_iter_find_with_len --
  + *
  + *       Internal helper for finding an exact key.
  + *
  + * Returns:
  + *       true if the field @key was found.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_bson_iter_find_with_len (bson_iter_t *iter,   /* INOUT */
  +                          const char  *key,    /* IN */
  +                          int          keylen) /* IN */
  +{
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   if (keylen < 0) {
  +      keylen = (int)strlen (key);
  +   }
  +
  +   while (bson_iter_next (iter)) {
  +      if (!strncmp (key, bson_iter_key (iter), keylen)) {
  +         return true;
  +      }
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_find --
  + *
  + *       Searches through @iter starting from the current position for a key
  + *       matching @key. This is a case-sensitive search meaning "KEY" and
  + *       "key" would NOT match.
  + *
  + * Returns:
  + *       true if @key is found.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_find (bson_iter_t *iter, /* INOUT */
  +                const char  *key)  /* IN */
  +{
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   return _bson_iter_find_with_len (iter, key, -1);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_find_case --
  + *
  + *       Searches through @iter starting from the current position for a key
  + *       matching @key. This is a case-insensitive search meaning "KEY" and
  + *       "key" would match.
  + *
  + * Returns:
  + *       true if @key is found.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_find_case (bson_iter_t *iter, /* INOUT */
  +                     const char  *key)  /* IN */
  +{
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (key, false);
  +
  +   while (bson_iter_next (iter)) {
  +#ifdef BSON_OS_WIN32
  +      if (!_stricmp(key, bson_iter_key (iter))) {
  +#else
  +      if (!strcasecmp (key, bson_iter_key (iter))) {
  +#endif
  +         return true;
  +      }
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_find_descendant --
  + *
  + *       Locates a descendant using the "parent.child.key" notation. This
  + *       operates similar to bson_iter_find() except that it can recurse
  + *       into children documents using the dot notation.
  + *
  + * Returns:
  + *       true if the descendant was found and @descendant was initialized.
  + *
  + * Side effects:
  + *       @descendant may be initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_find_descendant (bson_iter_t *iter,       /* INOUT */
  +                           const char  *dotkey,     /* IN */
  +                           bson_iter_t *descendant) /* OUT */
  +{
  +   bson_iter_t tmp;
  +   const char *dot;
  +   size_t sublen;
  +
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (dotkey, false);
  +   bson_return_val_if_fail (descendant, false);
  +
  +   if ((dot = strchr (dotkey, '.'))) {
  +      sublen = dot - dotkey;
  +   } else {
  +      sublen = strlen (dotkey);
  +   }
  +
  +   if (_bson_iter_find_with_len (iter, dotkey, (int)sublen)) {
  +      if (!dot) {
  +         *descendant = *iter;
  +         return true;
  +      }
  +
  +      if (BSON_ITER_HOLDS_DOCUMENT (iter) || BSON_ITER_HOLDS_ARRAY (iter)) {
  +         if (bson_iter_recurse (iter, &tmp)) {
  +            return bson_iter_find_descendant (&tmp, dot + 1, descendant);
  +         }
  +      }
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_key --
  + *
  + *       Retrieves the key of the current field. The resulting key is valid
  + *       while @iter is valid.
  + *
  + * Returns:
  + *       A string that should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const char *
  +bson_iter_key (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, NULL);
  +
  +   return bson_iter_key_unsafe (iter);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_type --
  + *
  + *       Retrieves the type of the current field.  It may be useful to check
  + *       the type using the BSON_ITER_HOLDS_*() macros.
  + *
  + * Returns:
  + *       A bson_type_t.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_type_t
  +bson_iter_type (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, BSON_TYPE_EOD);
  +   bson_return_val_if_fail (iter->raw, BSON_TYPE_EOD);
  +   bson_return_val_if_fail (iter->len, BSON_TYPE_EOD);
  +
  +   return bson_iter_type_unsafe (iter);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_next --
  + *
  + *       Advances @iter to the next field of the underlying BSON document.
  + *       If all fields have been exhausted, then %false is returned.
  + *
  + *       It is a programming error to use @iter after this function has
  + *       returned false.
  + *
  + * Returns:
  + *       true if the iter was advanced to the next record.
  + *       otherwise false and @iter should be considered invalid.
  + *
  + * Side effects:
  + *       @iter may be invalidated.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_next (bson_iter_t *iter) /* INOUT */
  +{
  +   const uint8_t *data;
  +   uint32_t o;
  +   unsigned int len;
  +
  +   bson_return_val_if_fail (iter, false);
  +
  +   if (!iter->raw) {
  +      return false;
  +   }
  +
  +   data = iter->raw;
  +   len = iter->len;
  +
  +   iter->off = iter->next_off;
  +   iter->type = iter->off;
  +   iter->key = iter->off + 1;
  +   iter->d1 = 0;
  +   iter->d2 = 0;
  +   iter->d3 = 0;
  +   iter->d4 = 0;
  +
  +   for (o = iter->off + 1; o < len; o++) {
  +      if (!data [o]) {
  +         iter->d1 = ++o;
  +         goto fill_data_fields;
  +      }
  +   }
  +
  +   goto mark_invalid;
  +
  +fill_data_fields:
  +
  +   switch (ITER_TYPE (iter)) {
  +   case BSON_TYPE_DATE_TIME:
  +   case BSON_TYPE_DOUBLE:
  +   case BSON_TYPE_INT64:
  +   case BSON_TYPE_TIMESTAMP:
  +      iter->next_off = o + 8;
  +      break;
  +   case BSON_TYPE_CODE:
  +   case BSON_TYPE_SYMBOL:
  +   case BSON_TYPE_UTF8:
  +      {
  +         uint32_t l;
  +
  +         if ((o + 4) >= len) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         iter->d2 = o + 4;
  +         memcpy (&l, iter->raw + iter->d1, 4);
  +         l = BSON_UINT32_FROM_LE (l);
  +
  +         if (l > (len - (o + 4))) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         iter->next_off = o + 4 + l;
  +
  +         /*
  +          * Make sure the string length includes the NUL byte.
  +          */
  +         if (BSON_UNLIKELY ((l == 0) || (iter->next_off >= len))) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         /*
  +          * Make sure the last byte is a NUL byte.
  +          */
  +         if (BSON_UNLIKELY ((iter->raw + iter->d2)[l - 1] != '\0')) {
  +            iter->err_off = o + 4 + l - 1;
  +            goto mark_invalid;
  +         }
  +      }
  +      break;
  +   case BSON_TYPE_BINARY:
  +      {
  +         bson_subtype_t subtype;
  +         uint32_t l;
  +
  +         if (o >= (len - 4)) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         iter->d2 = o + 4;
  +         iter->d3 = o + 5;
  +
  +         memcpy (&l, iter->raw + iter->d1, 4);
  +         l = BSON_UINT32_FROM_LE (l);
  +
  +         if (l >= (len - o)) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         subtype = *(iter->raw + iter->d2);
  +
  +         if (subtype == BSON_SUBTYPE_BINARY_DEPRECATED) {
  +            if (l < 4) {
  +               iter->err_off = o;
  +               goto mark_invalid;
  +            }
  +         }
  +
  +         iter->next_off = o + 5 + l;
  +      }
  +      break;
  +   case BSON_TYPE_ARRAY:
  +   case BSON_TYPE_DOCUMENT:
  +      {
  +         uint32_t l;
  +
  +         if (o >= (len - 4)) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         memcpy (&l, iter->raw + iter->d1, 4);
  +         l = BSON_UINT32_FROM_LE (l);
  +
  +         if ((l > len) || (l > (len - o))) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         iter->next_off = o + l;
  +      }
  +      break;
  +   case BSON_TYPE_OID:
  +      iter->next_off = o + 12;
  +      break;
  +   case BSON_TYPE_BOOL:
  +      iter->next_off = o + 1;
  +      break;
  +   case BSON_TYPE_REGEX:
  +      {
  +         bool eor = false;
  +         bool eoo = false;
  +
  +         for (; o < len; o++) {
  +            if (!data [o]) {
  +               iter->d2 = ++o;
  +               eor = true;
  +               break;
  +            }
  +         }
  +
  +         if (!eor) {
  +            iter->err_off = iter->next_off;
  +            goto mark_invalid;
  +         }
  +
  +         for (; o < len; o++) {
  +            if (!data [o]) {
  +               eoo = true;
  +               break;
  +            }
  +         }
  +
  +         if (!eoo) {
  +            iter->err_off = iter->next_off;
  +            goto mark_invalid;
  +         }
  +
  +         iter->next_off = o + 1;
  +      }
  +      break;
  +   case BSON_TYPE_DBPOINTER:
  +      {
  +         uint32_t l;
  +
  +         if (o >= (len - 4)) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         iter->d2 = o + 4;
  +         memcpy (&l, iter->raw + iter->d1, 4);
  +         l = BSON_UINT32_FROM_LE (l);
  +
  +         if ((l > len) || (l > (len - o))) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         iter->d3 = o + 4 + l;
  +         iter->next_off = o + 4 + l + 12;
  +      }
  +      break;
  +   case BSON_TYPE_CODEWSCOPE:
  +      {
  +         uint32_t l;
  +         uint32_t doclen;
  +
  +         if ((len < 19) || (o >= (len - 14))) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         iter->d2 = o + 4;
  +         iter->d3 = o + 8;
  +
  +         memcpy (&l, iter->raw + iter->d1, 4);
  +         l = BSON_UINT32_FROM_LE (l);
  +
  +         if ((l < 14) || (l >= (len - o))) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         iter->next_off = o + l;
  +
  +         if (iter->next_off >= len) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         memcpy (&l, iter->raw + iter->d2, 4);
  +         l = BSON_UINT32_FROM_LE (l);
  +
  +         if (l >= (len - o - 4 - 4)) {
  +            iter->err_off = o;
  +            goto mark_invalid;
  +         }
  +
  +         if ((o + 4 + 4 + l + 4) >= iter->next_off) {
  +            iter->err_off = o + 4;
  +            goto mark_invalid;
  +         }
  +
  +         iter->d4 = o + 4 + 4 + l;
  +         memcpy (&doclen, iter->raw + iter->d4, 4);
  +         doclen = BSON_UINT32_FROM_LE (doclen);
  +
  +         if ((o + 4 + 4 + l + doclen) != iter->next_off) {
  +            iter->err_off = o + 4 + 4 + l;
  +            goto mark_invalid;
  +         }
  +      }
  +      break;
  +   case BSON_TYPE_INT32:
  +      iter->next_off = o + 4;
  +      break;
  +   case BSON_TYPE_MAXKEY:
  +   case BSON_TYPE_MINKEY:
  +   case BSON_TYPE_NULL:
  +   case BSON_TYPE_UNDEFINED:
  +      iter->d1 = -1;
  +      iter->next_off = o;
  +      break;
  +   case BSON_TYPE_EOD:
  +   default:
  +      iter->err_off = o;
  +      goto mark_invalid;
  +   }
  +
  +   /*
  +    * Check to see if any of the field locations would overflow the
  +    * current BSON buffer. If so, set the error location to the offset
  +    * of where the field starts.
  +    */
  +   if (iter->next_off >= len) {
  +      iter->err_off = o;
  +      goto mark_invalid;
  +   }
  +
  +   iter->err_off = 0;
  +
  +   return true;
  +
  +mark_invalid:
  +   iter->raw = NULL;
  +   iter->len = 0;
  +   iter->next_off = 0;
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_binary --
  + *
  + *       Retrieves the BSON_TYPE_BINARY field. The subtype is stored in
  + *       @subtype.  The length of @binary in bytes is stored in @binary_len.
  + *
  + *       @binary should not be modified or freed and is only valid while
  + *       @iter is on the current field.
  + *
  + * Parameters:
  + *       @iter: A bson_iter_t
  + *       @subtype: A location for the binary subtype.
  + *       @binary_len: A location for the length of @binary.
  + *       @binary: A location for a pointer to the binary data.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_binary (const bson_iter_t  *iter,        /* IN */
  +                  bson_subtype_t     *subtype,     /* OUT */
  +                  uint32_t           *binary_len,  /* OUT */
  +                  const uint8_t     **binary)      /* OUT */
  +{
  +   bson_subtype_t backup;
  +
  +   bson_return_if_fail (iter);
  +   bson_return_if_fail (!binary || binary_len);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_BINARY) {
  +      if (!subtype) {
  +         subtype = &backup;
  +      }
  +
  +      *subtype = (bson_subtype_t) *(iter->raw + iter->d2);
  +
  +      if (binary) {
  +         memcpy (binary_len, (iter->raw + iter->d1), 4);
  +         *binary_len = BSON_UINT32_FROM_LE (*binary_len);
  +         *binary = iter->raw + iter->d3;
  +
  +         if (*subtype == BSON_SUBTYPE_BINARY_DEPRECATED) {
  +            *binary_len -= sizeof (int32_t);
  +            *binary += sizeof (int32_t);
  +         }
  +      }
  +
  +      return;
  +   }
  +
  +   if (binary) {
  +      *binary = NULL;
  +   }
  +
  +   if (binary_len) {
  +      *binary_len = 0;
  +   }
  +
  +   if (subtype) {
  +      *subtype = BSON_SUBTYPE_BINARY;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_bool --
  + *
  + *       Retrieves the current field of type BSON_TYPE_BOOL.
  + *
  + * Returns:
  + *       true or false, dependent on bson document.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_bool (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, 0);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_BOOL) {
  +      return bson_iter_bool_unsafe (iter);
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_as_bool --
  + *
  + *       If @iter is on a boolean field, returns the boolean. If it is on a
  + *       non-boolean field such as int32, int64, or double, it will convert
  + *       the value to a boolean.
  + *
  + *       Zero is false, and non-zero is true.
  + *
  + * Returns:
  + *       true or false, dependent on field type.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_as_bool (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, 0);
  +
  +   switch ((int)ITER_TYPE (iter)) {
  +   case BSON_TYPE_BOOL:
  +      return bson_iter_bool (iter);
  +   case BSON_TYPE_DOUBLE:
  +      return !(bson_iter_double (iter) == 0.0);
  +   case BSON_TYPE_INT64:
  +      return !(bson_iter_int64 (iter) == 0);
  +   case BSON_TYPE_INT32:
  +      return !(bson_iter_int32 (iter) == 0);
  +   case BSON_TYPE_UTF8:
  +      return true;
  +   case BSON_TYPE_NULL:
  +   case BSON_TYPE_UNDEFINED:
  +      return false;
  +   default:
  +      return true;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_double --
  + *
  + *       Retrieves the current field of type BSON_TYPE_DOUBLE.
  + *
  + * Returns:
  + *       A double.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +double
  +bson_iter_double (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, 0);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_DOUBLE) {
  +      return bson_iter_double_unsafe (iter);
  +   }
  +
  +   return 0;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_int32 --
  + *
  + *       Retrieves the value of the field of type BSON_TYPE_INT32.
  + *
  + * Returns:
  + *       A 32-bit signed integer.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int32_t
  +bson_iter_int32 (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, 0);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_INT32) {
  +      return bson_iter_int32_unsafe (iter);
  +   }
  +
  +   return 0;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_int64 --
  + *
  + *       Retrieves a 64-bit signed integer for the current BSON_TYPE_INT64
  + *       field.
  + *
  + * Returns:
  + *       A 64-bit signed integer.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int64_t
  +bson_iter_int64 (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, 0);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_INT64) {
  +      return bson_iter_int64_unsafe (iter);
  +   }
  +
  +   return 0;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_as_int64 --
  + *
  + *       If @iter is not an int64 field, it will try to convert the value to
  + *       an int64. Such field types include:
  + *
  + *        - bool
  + *        - double
  + *        - int32
  + *
  + * Returns:
  + *       An int64_t.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int64_t
  +bson_iter_as_int64 (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, 0);
  +
  +   switch ((int)ITER_TYPE (iter)) {
  +   case BSON_TYPE_BOOL:
  +      return (int64_t)bson_iter_bool (iter);
  +   case BSON_TYPE_DOUBLE:
  +      return (int64_t)bson_iter_double (iter);
  +   case BSON_TYPE_INT64:
  +      return bson_iter_int64 (iter);
  +   case BSON_TYPE_INT32:
  +      return (int64_t)bson_iter_int32 (iter);
  +   default:
  +      return 0;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_oid --
  + *
  + *       Retrieves the current field of type %BSON_TYPE_OID. The result is
  + *       valid while @iter is valid.
  + *
  + * Returns:
  + *       A bson_oid_t that should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const bson_oid_t *
  +bson_iter_oid (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, NULL);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_OID) {
  +      return bson_iter_oid_unsafe (iter);
  +   }
  +
  +   return NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_regex --
  + *
  + *       Fetches the current field from the iter which should be of type
  + *       BSON_TYPE_REGEX.
  + *
  + * Returns:
  + *       Regex from @iter. This should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const char *
  +bson_iter_regex (const bson_iter_t *iter,    /* IN */
  +                 const char       **options) /* IN */
  +{
  +   const char *ret = NULL;
  +   const char *ret_options = NULL;
  +
  +   bson_return_val_if_fail (iter, NULL);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_REGEX) {
  +      ret = (const char *)(iter->raw + iter->d1);
  +      ret_options = (const char *)(iter->raw + iter->d2);
  +   }
  +
  +   if (options) {
  +      *options = ret_options;
  +   }
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_utf8 --
  + *
  + *       Retrieves the current field of type %BSON_TYPE_UTF8 as a UTF-8
  + *       encoded string.
  + *
  + * Parameters:
  + *       @iter: A bson_iter_t.
  + *       @length: A location for the length of the string.
  + *
  + * Returns:
  + *       A string that should not be modified or freed.
  + *
  + * Side effects:
  + *       @length will be set to the result strings length if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const char *
  +bson_iter_utf8 (const bson_iter_t *iter,   /* IN */
  +                uint32_t          *length) /* OUT */
  +{
  +   bson_return_val_if_fail (iter, NULL);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_UTF8) {
  +      if (length) {
  +         *length = bson_iter_utf8_len_unsafe (iter);
  +      }
  +
  +      return (const char *)(iter->raw + iter->d2);
  +   }
  +
  +   if (length) {
  +      *length = 0;
  +   }
  +
  +   return NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_dup_utf8 --
  + *
  + *       Copies the current UTF-8 element into a newly allocated string. The
  + *       string should be freed using bson_free() when the caller is
  + *       finished with it.
  + *
  + * Returns:
  + *       A newly allocated char* that should be freed with bson_free().
  + *
  + * Side effects:
  + *       @length will be set to the result strings length if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +bson_iter_dup_utf8 (const bson_iter_t *iter,   /* IN */
  +                    uint32_t          *length) /* OUT */
  +{
  +   uint32_t local_length = 0;
  +   const char *str;
  +   char *ret = NULL;
  +
  +   bson_return_val_if_fail (iter, NULL);
  +
  +   if ((str = bson_iter_utf8 (iter, &local_length))) {
  +      ret = bson_malloc0 (local_length + 1);
  +      memcpy (ret, str, local_length);
  +      ret[local_length] = '\0';
  +   }
  +
  +   if (length) {
  +      *length = local_length;
  +   }
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_code --
  + *
  + *       Retrieves the current field of type %BSON_TYPE_CODE. The length of
  + *       the resulting string is stored in @length.
  + *
  + * Parameters:
  + *       @iter: A bson_iter_t.
  + *       @length: A location for the code length.
  + *
  + * Returns:
  + *       A NUL-terminated string containing the code which should not be
  + *       modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const char *
  +bson_iter_code (const bson_iter_t *iter,   /* IN */
  +                uint32_t          *length) /* OUT */
  +{
  +   bson_return_val_if_fail (iter, NULL);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_CODE) {
  +      if (length) {
  +         *length = bson_iter_utf8_len_unsafe (iter);
  +      }
  +
  +      return (const char *)(iter->raw + iter->d2);
  +   }
  +
  +   if (length) {
  +      *length = 0;
  +   }
  +
  +   return NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_codewscope --
  + *
  + *       Similar to bson_iter_code() but with a scope associated encoded as
  + *       a BSON document. @scope should not be modified or freed. It is
  + *       valid while @iter is valid.
  + *
  + * Parameters:
  + *       @iter: A #bson_iter_t.
  + *       @length: A location for the length of resulting string.
  + *       @scope_len: A location for the length of @scope.
  + *       @scope: A location for the scope encoded as BSON.
  + *
  + * Returns:
  + *       A NUL-terminated string that should not be modified or freed.
  + *
  + * Side effects:
  + *       @length is set to the resulting string length in bytes.
  + *       @scope_len is set to the length of @scope in bytes.
  + *       @scope is set to the scope documents buffer which can be
  + *       turned into a bson document with bson_init_static().
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const char *
  +bson_iter_codewscope (const bson_iter_t  *iter,      /* IN */
  +                      uint32_t           *length,    /* OUT */
  +                      uint32_t           *scope_len, /* OUT */
  +                      const uint8_t     **scope)     /* OUT */
  +{
  +   uint32_t len;
  +
  +   bson_return_val_if_fail (iter, NULL);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_CODEWSCOPE) {
  +      if (length) {
  +         memcpy (&len, iter->raw + iter->d2, 4);
  +         *length = BSON_UINT32_FROM_LE (len) - 1;
  +      }
  +
  +      memcpy (&len, iter->raw + iter->d4, 4);
  +      *scope_len = BSON_UINT32_FROM_LE (len);
  +      *scope = iter->raw + iter->d4;
  +      return (const char *)(iter->raw + iter->d3);
  +   }
  +
  +   if (length) {
  +      *length = 0;
  +   }
  +
  +   if (scope_len) {
  +      *scope_len = 0;
  +   }
  +
  +   if (scope) {
  +      *scope = NULL;
  +   }
  +
  +   return NULL;
  +}
  +
  +
  +/**
  + * bson_iter_dbpointer:
  + *
  + *
  + *
  + */
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_dbpointer --
  + *
  + *       Retrieves a BSON_TYPE_DBPOINTER field. @collection_len will be set
  + *       to the length of the collection name. The collection name will be
  + *       placed into @collection. The oid will be placed into @oid.
  + *
  + *       @collection and @oid should not be modified.
  + *
  + * Parameters:
  + *       @iter: A #bson_iter_t.
  + *       @collection_len: A location for the length of @collection.
  + *       @collection: A location for the collection name.
  + *       @oid: A location for the oid.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @collection_len is set to the length of @collection in bytes
  + *       excluding the null byte.
  + *       @collection is set to the collection name, including a terminating
  + *       null byte.
  + *       @oid is initialized with the oid.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_dbpointer (const bson_iter_t  *iter,           /* IN */
  +                     uint32_t           *collection_len, /* OUT */
  +                     const char        **collection,     /* OUT */
  +                     const bson_oid_t  **oid)            /* OUT */
  +{
  +   bson_return_if_fail (iter);
  +
  +   if (collection) {
  +      *collection = NULL;
  +   }
  +
  +   if (oid) {
  +      *oid = NULL;
  +   }
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_DBPOINTER) {
  +      if (collection_len) {
  +         memcpy (collection_len, (iter->raw + iter->d1), 4);
  +         *collection_len = BSON_UINT32_FROM_LE (*collection_len);
  +
  +         if ((*collection_len) > 0) {
  +            (*collection_len)--;
  +         }
  +      }
  +
  +      if (collection) {
  +         *collection = (const char *)(iter->raw + iter->d2);
  +      }
  +
  +      if (oid) {
  +         *oid = (const bson_oid_t *)(iter->raw + iter->d3);
  +      }
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_symbol --
  + *
  + *       Retrieves the symbol of the current field of type BSON_TYPE_SYMBOL.
  + *
  + * Parameters:
  + *       @iter: A bson_iter_t.
  + *       @length: A location for the length of the symbol.
  + *
  + * Returns:
  + *       A string containing the symbol as UTF-8. The value should not be
  + *       modified or freed.
  + *
  + * Side effects:
  + *       @length is set to the resulting strings length in bytes,
  + *       excluding the null byte.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const char *
  +bson_iter_symbol (const bson_iter_t *iter,   /* IN */
  +                  uint32_t          *length) /* OUT */
  +{
  +   const char *ret = NULL;
  +   uint32_t ret_length = 0;
  +
  +   bson_return_val_if_fail (iter, NULL);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_SYMBOL) {
  +      ret = (const char *)(iter->raw + iter->d2);
  +      ret_length = bson_iter_utf8_len_unsafe (iter);
  +   }
  +
  +   if (length) {
  +      *length = ret_length;
  +   }
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_date_time --
  + *
  + *       Fetches the number of milliseconds elapsed since the UNIX epoch.
  + *       This value can be negative as times before 1970 are valid.
  + *
  + * Returns:
  + *       A signed 64-bit integer containing the number of milliseconds.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int64_t
  +bson_iter_date_time (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, 0);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_DATE_TIME) {
  +      return bson_iter_int64_unsafe (iter);
  +   }
  +
  +   return 0;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_time_t --
  + *
  + *       Retrieves the current field of type BSON_TYPE_DATE_TIME as a
  + *       time_t.
  + *
  + * Returns:
  + *       A #time_t of the number of seconds since UNIX epoch in UTC.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +time_t
  +bson_iter_time_t (const bson_iter_t *iter) /* IN */
  +{
  +   bson_return_val_if_fail (iter, 0);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_DATE_TIME) {
  +      return bson_iter_time_t_unsafe (iter);
  +   }
  +
  +   return 0;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_timestamp --
  + *
  + *       Fetches the current field if it is a BSON_TYPE_TIMESTAMP.
  + *
  + * Parameters:
  + *       @iter: A #bson_iter_t.
  + *       @timestamp: a location for the timestamp.
  + *       @increment: A location for the increment.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @timestamp is initialized.
  + *       @increment is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_timestamp (const bson_iter_t *iter,      /* IN */
  +                     uint32_t          *timestamp, /* OUT */
  +                     uint32_t          *increment) /* OUT */
  +{
  +   uint64_t encoded;
  +   uint32_t ret_timestamp = 0;
  +   uint32_t ret_increment = 0;
  +
  +   bson_return_if_fail (iter);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_TIMESTAMP) {
  +      memcpy (&encoded, iter->raw + iter->d1, 8);
  +      encoded = BSON_UINT64_FROM_LE (encoded);
  +      ret_timestamp = (encoded >> 32) & 0xFFFFFFFF;
  +      ret_increment = encoded & 0xFFFFFFFF;
  +   }
  +
  +   if (timestamp) {
  +      *timestamp = ret_timestamp;
  +   }
  +
  +   if (increment) {
  +      *increment = ret_increment;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_timeval --
  + *
  + *       Retrieves the current field of type BSON_TYPE_DATE_TIME and stores
  + *       it into the struct timeval provided. tv->tv_sec is set to the
  + *       number of seconds since the UNIX epoch in UTC.
  + *
  + *       Since BSON_TYPE_DATE_TIME does not support fractions of a second,
  + *       tv->tv_usec will always be set to zero.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @tv is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_timeval (const bson_iter_t *iter,  /* IN */
  +                   struct timeval    *tv)    /* OUT */
  +{
  +   bson_return_if_fail (iter);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_DATE_TIME) {
  +      bson_iter_timeval_unsafe (iter, tv);
  +      return;
  +   }
  +
  +   memset (tv, 0, sizeof *tv);
  +}
  +
  +
  +/**
  + * bson_iter_document:
  + * @iter: a bson_iter_t.
  + * @document_len: A location for the document length.
  + * @document: A location for a pointer to the document buffer.
  + *
  + */
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_document --
  + *
  + *       Retrieves the data to the document BSON structure and stores the
  + *       length of the document buffer in @document_len and the document
  + *       buffer in @document.
  + *
  + *       If you would like to iterate over the child contents, you might
  + *       consider creating a bson_t on the stack such as the following. It
  + *       allows you to call functions taking a const bson_t* only.
  + *
  + *          bson_t b;
  + *          uint32_t len;
  + *          const uint8_t *data;
  + *
  + *          bson_iter_document(iter, &len, &data);
  + *
  + *          if (bson_init_static (&b, data, len)) {
  + *             ...
  + *          }
  + *
  + *       There is no need to cleanup the bson_t structure as no data can be
  + *       modified in the process of its use (as it is static/const).
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @document_len is initialized.
  + *       @document is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_document (const bson_iter_t  *iter,         /* IN */
  +                    uint32_t           *document_len, /* OUT */
  +                    const uint8_t     **document)     /* OUT */
  +{
  +   bson_return_if_fail (iter);
  +   bson_return_if_fail (document_len);
  +   bson_return_if_fail (document);
  +
  +   *document = NULL;
  +   *document_len = 0;
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_DOCUMENT) {
  +      memcpy (document_len, (iter->raw + iter->d1), 4);
  +      *document_len = BSON_UINT32_FROM_LE (*document_len);
  +      *document = (iter->raw + iter->d1);
  +   }
  +}
  +
  +
  +/**
  + * bson_iter_array:
  + * @iter: a #bson_iter_t.
  + * @array_len: A location for the array length.
  + * @array: A location for a pointer to the array buffer.
  + */
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_array --
  + *
  + *       Retrieves the data to the array BSON structure and stores the
  + *       length of the array buffer in @array_len and the array buffer in
  + *       @array.
  + *
  + *       If you would like to iterate over the child contents, you might
  + *       consider creating a bson_t on the stack such as the following. It
  + *       allows you to call functions taking a const bson_t* only.
  + *
  + *          bson_t b;
  + *          uint32_t len;
  + *          const uint8_t *data;
  + *
  + *          bson_iter_array (iter, &len, &data);
  + *
  + *          if (bson_init_static (&b, data, len)) {
  + *             ...
  + *          }
  + *
  + *       There is no need to cleanup the #bson_t structure as no data can be
  + *       modified in the process of its use.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @array_len is initialized.
  + *       @array is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_array (const bson_iter_t  *iter,      /* IN */
  +                 uint32_t           *array_len, /* OUT */
  +                 const uint8_t     **array)     /* OUT */
  +{
  +   bson_return_if_fail (iter);
  +   bson_return_if_fail (array_len);
  +   bson_return_if_fail (array);
  +
  +   *array = NULL;
  +   *array_len = 0;
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_ARRAY) {
  +      memcpy (array_len, (iter->raw + iter->d1), 4);
  +      *array_len = BSON_UINT32_FROM_LE (*array_len);
  +      *array = (iter->raw + iter->d1);
  +   }
  +}
  +
  +
  +#define VISIT_FIELD(name) visitor->visit_##name && visitor->visit_##name
  +#define VISIT_AFTER VISIT_FIELD (after)
  +#define VISIT_BEFORE VISIT_FIELD (before)
  +#define VISIT_CORRUPT if (visitor->visit_corrupt) visitor->visit_corrupt
  +#define VISIT_DOUBLE VISIT_FIELD (double)
  +#define VISIT_UTF8 VISIT_FIELD (utf8)
  +#define VISIT_DOCUMENT VISIT_FIELD (document)
  +#define VISIT_ARRAY VISIT_FIELD (array)
  +#define VISIT_BINARY VISIT_FIELD (binary)
  +#define VISIT_UNDEFINED VISIT_FIELD (undefined)
  +#define VISIT_OID VISIT_FIELD (oid)
  +#define VISIT_BOOL VISIT_FIELD (bool)
  +#define VISIT_DATE_TIME VISIT_FIELD (date_time)
  +#define VISIT_NULL VISIT_FIELD (null)
  +#define VISIT_REGEX VISIT_FIELD (regex)
  +#define VISIT_DBPOINTER VISIT_FIELD (dbpointer)
  +#define VISIT_CODE VISIT_FIELD (code)
  +#define VISIT_SYMBOL VISIT_FIELD (symbol)
  +#define VISIT_CODEWSCOPE VISIT_FIELD (codewscope)
  +#define VISIT_INT32 VISIT_FIELD (int32)
  +#define VISIT_TIMESTAMP VISIT_FIELD (timestamp)
  +#define VISIT_INT64 VISIT_FIELD (int64)
  +#define VISIT_MAXKEY VISIT_FIELD (maxkey)
  +#define VISIT_MINKEY VISIT_FIELD (minkey)
  +
  +
  +/**
  + * bson_iter_visit_all:
  + * @iter: A #bson_iter_t.
  + * @visitor: A #bson_visitor_t containing the visitors.
  + * @data: User data for @visitor data parameters.
  + *
  + *
  + * Returns: true if the visitor was pre-maturely ended; otherwise false.
  + */
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_visit_all --
  + *
  + *       Visits all fields forward from the current position of @iter. For
  + *       each field found a function in @visitor will be called. Typically
  + *       you will use this immediately after initializing a bson_iter_t.
  + *
  + *          bson_iter_init (&iter, b);
  + *          bson_iter_visit_all (&iter, my_visitor, NULL);
  + *
  + *       @iter will no longer be valid after this function has executed and
  + *       will need to be reinitialized if intending to reuse.
  + *
  + * Returns:
  + *       true if successfully visited all fields or callback requested
  + *       early termination, otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_iter_visit_all (bson_iter_t          *iter,    /* INOUT */
  +                     const bson_visitor_t *visitor, /* IN */
  +                     void                 *data)    /* IN */
  +{
  +   const char *key;
  +
  +   bson_return_val_if_fail (iter, false);
  +   bson_return_val_if_fail (visitor, false);
  +
  +   while (bson_iter_next (iter)) {
  +      key = bson_iter_key_unsafe (iter);
  +
  +      if (*key && !bson_utf8_validate (key, strlen (key), false)) {
  +         iter->err_off = iter->off;
  +         return true;
  +      }
  +
  +      if (VISIT_BEFORE (iter, key, data)) {
  +         return true;
  +      }
  +
  +      switch (bson_iter_type (iter)) {
  +      case BSON_TYPE_DOUBLE:
  +
  +         if (VISIT_DOUBLE (iter, key, bson_iter_double (iter), data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_UTF8:
  +         {
  +            uint32_t utf8_len;
  +            const char *utf8;
  +
  +            utf8 = bson_iter_utf8 (iter, &utf8_len);
  +
  +            if (!bson_utf8_validate (utf8, utf8_len, true)) {
  +               iter->err_off = iter->off;
  +               return true;
  +            }
  +
  +            if (VISIT_UTF8 (iter, key, utf8_len, utf8, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_DOCUMENT:
  +         {
  +            const uint8_t *docbuf = NULL;
  +            uint32_t doclen = 0;
  +            bson_t b;
  +
  +            bson_iter_document (iter, &doclen, &docbuf);
  +
  +            if (bson_init_static (&b, docbuf, doclen) &&
  +                VISIT_DOCUMENT (iter, key, &b, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_ARRAY:
  +         {
  +            const uint8_t *docbuf = NULL;
  +            uint32_t doclen = 0;
  +            bson_t b;
  +
  +            bson_iter_array (iter, &doclen, &docbuf);
  +
  +            if (bson_init_static (&b, docbuf, doclen)
  +                && VISIT_ARRAY (iter, key, &b, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_BINARY:
  +         {
  +            const uint8_t *binary = NULL;
  +            bson_subtype_t subtype = BSON_SUBTYPE_BINARY;
  +            uint32_t binary_len = 0;
  +
  +            bson_iter_binary (iter, &subtype, &binary_len, &binary);
  +
  +            if (VISIT_BINARY (iter, key, subtype, binary_len, binary, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_UNDEFINED:
  +
  +         if (VISIT_UNDEFINED (iter, key, data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_OID:
  +
  +         if (VISIT_OID (iter, key, bson_iter_oid (iter), data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_BOOL:
  +
  +         if (VISIT_BOOL (iter, key, bson_iter_bool (iter), data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_DATE_TIME:
  +
  +         if (VISIT_DATE_TIME (iter, key, bson_iter_date_time (iter), data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_NULL:
  +
  +         if (VISIT_NULL (iter, key, data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_REGEX:
  +         {
  +            const char *regex = NULL;
  +            const char *options = NULL;
  +            regex = bson_iter_regex (iter, &options);
  +
  +            if (VISIT_REGEX (iter, key, regex, options, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_DBPOINTER:
  +         {
  +            uint32_t collection_len = 0;
  +            const char *collection = NULL;
  +            const bson_oid_t *oid = NULL;
  +
  +            bson_iter_dbpointer (iter, &collection_len, &collection, &oid);
  +
  +            if (VISIT_DBPOINTER (iter, key, collection_len, collection, oid,
  +                                 data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_CODE:
  +         {
  +            uint32_t code_len;
  +            const char *code;
  +
  +            code = bson_iter_code (iter, &code_len);
  +
  +            if (VISIT_CODE (iter, key, code_len, code, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_SYMBOL:
  +         {
  +            uint32_t symbol_len;
  +            const char *symbol;
  +
  +            symbol = bson_iter_symbol (iter, &symbol_len);
  +
  +            if (VISIT_SYMBOL (iter, key, symbol_len, symbol, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_CODEWSCOPE:
  +         {
  +            uint32_t length = 0;
  +            const char *code;
  +            const uint8_t *docbuf = NULL;
  +            uint32_t doclen = 0;
  +            bson_t b;
  +
  +            code = bson_iter_codewscope (iter, &length, &doclen, &docbuf);
  +
  +            if (bson_init_static (&b, docbuf, doclen) &&
  +                VISIT_CODEWSCOPE (iter, key, length, code, &b, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_INT32:
  +
  +         if (VISIT_INT32 (iter, key, bson_iter_int32 (iter), data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_TIMESTAMP:
  +         {
  +            uint32_t timestamp;
  +            uint32_t increment;
  +            bson_iter_timestamp (iter, &timestamp, &increment);
  +
  +            if (VISIT_TIMESTAMP (iter, key, timestamp, increment, data)) {
  +               return true;
  +            }
  +         }
  +         break;
  +      case BSON_TYPE_INT64:
  +
  +         if (VISIT_INT64 (iter, key, bson_iter_int64 (iter), data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_MAXKEY:
  +
  +         if (VISIT_MAXKEY (iter, bson_iter_key_unsafe (iter), data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_MINKEY:
  +
  +         if (VISIT_MINKEY (iter, bson_iter_key_unsafe (iter), data)) {
  +            return true;
  +         }
  +
  +         break;
  +      case BSON_TYPE_EOD:
  +      default:
  +         break;
  +      }
  +
  +      if (VISIT_AFTER (iter, bson_iter_key_unsafe (iter), data)) {
  +         return true;
  +      }
  +   }
  +
  +   if (iter->err_off) {
  +      VISIT_CORRUPT (iter, data);
  +   }
  +
  +#undef VISIT_FIELD
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_overwrite_bool --
  + *
  + *       Overwrites the current BSON_TYPE_BOOLEAN field with a new value.
  + *       This is performed in-place and therefore no keys are moved.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_overwrite_bool (bson_iter_t *iter,  /* IN */
  +                          bool         value) /* IN */
  +{
  +   bson_return_if_fail (iter);
  +   bson_return_if_fail (value == 1 || value == 0);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_BOOL) {
  +      memcpy ((void *)(iter->raw + iter->d1), &value, 1);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_overwrite_int32 --
  + *
  + *       Overwrites the current BSON_TYPE_INT32 field with a new value.
  + *       This is performed in-place and therefore no keys are moved.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_overwrite_int32 (bson_iter_t *iter,  /* IN */
  +                           int32_t      value) /* IN */
  +{
  +   bson_return_if_fail (iter);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_INT32) {
  +#if BSON_BYTE_ORDER != BSON_LITTLE_ENDIAN
  +      value = BSON_UINT32_TO_LE (value);
  +#endif
  +      memcpy ((void *)(iter->raw + iter->d1), &value, 4);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_overwrite_int64 --
  + *
  + *       Overwrites the current BSON_TYPE_INT64 field with a new value.
  + *       This is performed in-place and therefore no keys are moved.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_overwrite_int64 (bson_iter_t *iter,   /* IN */
  +                           int64_t      value)  /* IN */
  +{
  +   bson_return_if_fail (iter);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_INT64) {
  +#if BSON_BYTE_ORDER != BSON_LITTLE_ENDIAN
  +      value = BSON_UINT64_TO_LE (value);
  +#endif
  +      memcpy ((void *)(iter->raw + iter->d1), &value, 8);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_iter_overwrite_double --
  + *
  + *       Overwrites the current BSON_TYPE_DOUBLE field with a new value.
  + *       This is performed in-place and therefore no keys are moved.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_iter_overwrite_double (bson_iter_t *iter,  /* IN */
  +                            double       value) /* IN */
  +{
  +   bson_return_if_fail (iter);
  +
  +   if (ITER_TYPE (iter) == BSON_TYPE_DOUBLE) {
  +      value = BSON_DOUBLE_TO_LE (value);
  +      memcpy ((void *)(iter->raw + iter->d1), &value, 8);
  +   }
  +}
  +/*==============================================================*/
  +/* --- bson-json.c */
  +
  +#ifdef	FIXME
  +#include "b64_pton.h"
  +
  +#include <yajl/yajl_parser.h>
  +#include <yajl/yajl_bytestack.h>
  +#endif
  +
  +#define STACK_MAX 100
  +#define BSON_JSON_DEFAULT_BUF_SIZE (1 << 14)
  +
  +
  +typedef enum
  +{
  +   BSON_JSON_REGULAR,
  +   BSON_JSON_DONE,
  +   BSON_JSON_ERROR,
  +   BSON_JSON_IN_START_MAP,
  +   BSON_JSON_IN_BSON_TYPE,
  +   BSON_JSON_IN_BSON_TYPE_TIMESTAMP_STARTMAP,
  +   BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES,
  +   BSON_JSON_IN_BSON_TYPE_TIMESTAMP_ENDMAP,
  +} bson_json_read_state_t;
  +
  +
  +typedef enum
  +{
  +   BSON_JSON_LF_REGEX,
  +   BSON_JSON_LF_OPTIONS,
  +   BSON_JSON_LF_OID,
  +   BSON_JSON_LF_BINARY,
  +   BSON_JSON_LF_TYPE,
  +   BSON_JSON_LF_DATE,
  +   BSON_JSON_LF_TIMESTAMP_T,
  +   BSON_JSON_LF_TIMESTAMP_I,
  +   BSON_JSON_LF_REF,
  +   BSON_JSON_LF_ID,
  +   BSON_JSON_LF_UNDEFINED,
  +   BSON_JSON_LF_MINKEY,
  +   BSON_JSON_LF_MAXKEY,
  +} bson_json_read_bson_state_t;
  +
  +
  +typedef struct
  +{
  +   uint8_t *buf;
  +   size_t   n_bytes;
  +   size_t   len;
  +} bson_json_buf_t;
  +
  +
  +typedef struct
  +{
  +   int    i;
  +   bool   is_array;
  +   bson_t bson;
  +} bson_json_stack_frame_t;
  +
  +
  +typedef union
  +{
  +   struct {
  +      bool has_regex;
  +      bool has_options;
  +   } regex;
  +   struct {
  +      bool       has_oid;
  +      bson_oid_t oid;
  +   } oid;
  +   struct {
  +      bool           has_binary;
  +      bool           has_subtype;
  +      bson_subtype_t type;
  +   } binary;
  +   struct {
  +      bool    has_date;
  +      int64_t date;
  +   } date;
  +   struct {
  +      bool     has_t;
  +      bool     has_i;
  +      uint32_t t;
  +      uint32_t i;
  +   } timestamp;
  +   struct {
  +      bool       has_ref;
  +      bool       has_id;
  +      bson_oid_t id;
  +   } ref;
  +   struct {
  +      bool has_undefined;
  +   } undefined;
  +   struct {
  +      bool has_minkey;
  +   } minkey;
  +   struct {
  +      bool has_maxkey;
  +   } maxkey;
  +} bson_json_bson_data_t;
  +
  +
  +typedef struct
  +{
  +   bson_t                      *bson;
  +   bson_json_stack_frame_t      stack[STACK_MAX];
  +   int                          n;
  +   const char                  *key;
  +   bson_json_buf_t              key_buf;
  +   bson_json_read_state_t       read_state;
  +   bson_json_read_bson_state_t  bson_state;
  +   bson_type_t                  bson_type;
  +   bson_json_buf_t              bson_type_buf [3];
  +   bson_json_bson_data_t        bson_type_data;
  +   bool                         known_bson_type;
  +} bson_json_reader_bson_t;
  +
  +
  +typedef struct
  +{
  +   void                 *data;
  +   bson_json_reader_cb   cb;
  +   bson_json_destroy_cb  dcb;
  +   uint8_t              *buf;
  +   size_t                buf_size;
  +   size_t                bytes_read;
  +   size_t                bytes_parsed;
  +   bool                  all_whitespace;
  +} bson_json_reader_producer_t;
  +
  +
  +struct _bson_json_reader_t
  +{
  +   bson_json_reader_producer_t  producer;
  +   bson_json_reader_bson_t      bson;
  +   yajl_handle                  yh;
  +   bson_error_t                *error;
  +};
  +
  +
  +typedef struct
  +{
  +   int fd;
  +   bool do_close;
  +} bson_json_reader_handle_fd_t;
  +
  +
  +#define STACK_ELE(_delta, _name) (bson->stack[(_delta) + bson->n]._name)
  +#define STACK_BSON(_delta) \
  +      (((_delta) + bson->n) == 0 ? bson->bson : &STACK_ELE (_delta, bson))
  +#define STACK_BSON_PARENT STACK_BSON (-1)
  +#define STACK_BSON_CHILD STACK_BSON (0)
  +#define STACK_I STACK_ELE (0, i)
  +#define STACK_IS_ARRAY STACK_ELE (0, is_array)
  +#define STACK_PUSH_ARRAY(statement) \
  +   do { \
  +      if (bson->n >= (STACK_MAX - 1)) { return 0; } \
  +      if (bson->n == -1) { return 0; } \
  +      bson->n++; \
  +      STACK_I = 0; \
  +      STACK_IS_ARRAY = 1; \
  +      statement; \
  +   } while (0)
  +#define STACK_PUSH_DOC(statement) \
  +   do { \
  +      if (bson->n >= (STACK_MAX - 1)) { return 0; } \
  +      bson->n++; \
  +      if (bson->n != 0) { \
  +         STACK_IS_ARRAY = 0; \
  +         statement; \
  +      } \
  +   } while (0)
  +#define STACK_POP_ARRAY(statement) \
  +   do { \
  +      if (!STACK_IS_ARRAY) { return 0; } \
  +      if (bson->n <= 0) { return 0; } \
  +      statement; \
  +      bson->n--; \
  +   } while (0)
  +#define STACK_POP_DOC(statement) \
  +   do { \
  +      if (STACK_IS_ARRAY) { return 0; } \
  +      if (bson->n < 0) { return 0; } \
  +      if (bson->n > 0) { \
  +         statement; \
  +      } \
  +      bson->n--; \
  +   } while (0)
  +#define BASIC_YAJL_CB_PREAMBLE \
  +   const char *key; \
  +   size_t len; \
  +   bson_json_reader_t *reader = (bson_json_reader_t *)_ctx; \
  +   bson_json_reader_bson_t *bson = &reader->bson; \
  +   _bson_json_read_fixup_key (bson); \
  +   key = bson->key; \
  +   len = bson->key_buf.len;
  +#define BASIC_YAJL_CB_BAIL_IF_NOT_NORMAL(_type) \
  +   if (bson->read_state != BSON_JSON_REGULAR) { \
  +      _bson_json_read_set_error (reader, "Invalid read of %s in state %d", \
  +                                 (_type), bson->read_state); \
  +      return 0; \
  +   }
  +#define HANDLE_OPTION(_key, _type, _state) \
  +   (len == strlen (_key) && strncmp ((const char *)val, (_key), len) == 0) { \
  +      if (bson->known_bson_type && bson->bson_type != (_type)) { \
  +         _bson_json_read_set_error (reader, \
  +                                    "Invalid key %s.  Looking for values for %d", \
\  +                                    (_key), bson->bson_type); \
  +         return 0; \
  +      } \
  +      bson->bson_type = (_type); \
  +      bson->bson_state = (_state); \
  +   }
  +
  +
  +static bool
  +_bson_json_all_whitespace (const char *utf8)
  +{
  +   bool all_whitespace = true;
  +
  +   for (; *utf8; utf8 = bson_utf8_next_char (utf8)) {
  +      if (!isspace (bson_utf8_get_char (utf8))) {
  +         all_whitespace = false;
  +         break;
  +      }
  +   }
  +
  +   return all_whitespace;
  +}
  +
  +static void
  +_bson_json_read_set_error (bson_json_reader_t *reader,
  +                           const char         *fmt,
  +                           ...)
  +   BSON_GNUC_PRINTF (2, 3);
  +
  +
  +static void
  +_bson_json_read_set_error (bson_json_reader_t *reader, /* IN */
  +                           const char         *fmt,    /* IN */
  +                           ...)
  +{
  +   va_list ap;
  +
  +   BSON_ASSERT (reader);
  +   BSON_ASSERT (fmt);
  +
  +   if (reader->error) {
  +      reader->error->domain = BSON_ERROR_JSON;
  +      reader->error->code = BSON_JSON_ERROR_READ_INVALID_PARAM;
  +      va_start (ap, fmt);
  +      bson_vsnprintf (reader->error->message, sizeof reader->error->message,
  +                      fmt, ap);
  +      va_end (ap);
  +      reader->error->message [sizeof reader->error->message - 1] = '\0';
  +   }
  +
  +   reader->bson.read_state = BSON_JSON_ERROR;
  +}
  +
  +
  +static void
  +_bson_json_buf_ensure (bson_json_buf_t *buf, /* IN */
  +                       size_t           len) /* IN */
  +{
  +   BSON_ASSERT (buf);
  +
  +   if (buf->n_bytes < len) {
  +      bson_free (buf->buf);
  +
  +      buf->n_bytes = bson_next_power_of_two (len);
  +      buf->buf = bson_malloc (buf->n_bytes);
  +   }
  +}
  +
  +
  +static void
  +_bson_json_read_fixup_key (bson_json_reader_bson_t *bson) /* IN */
  +{
  +   BSON_ASSERT (bson);
  +
  +   if (bson->n > 0 && STACK_IS_ARRAY) {
  +      _bson_json_buf_ensure (&bson->key_buf, 12);
  +      bson->key_buf.len = bson_uint32_to_string (STACK_I, &bson->key,
  +                                                 (char *)bson->key_buf.buf, 12);
  +      STACK_I++;
  +   }
  +}
  +
  +
  +static void
  +_bson_json_buf_set (bson_json_buf_t *buf,            /* IN */
  +                    const void       *from,          /* IN */
  +                    size_t            len,           /* IN */
  +                    bool              trailing_null) /* IN */
  +{
  +   if (trailing_null) {
  +      _bson_json_buf_ensure (buf, len + 1);
  +   } else {
  +      _bson_json_buf_ensure (buf, len);
  +   }
  +
  +   memcpy (buf->buf, from, len);
  +
  +   if (trailing_null) {
  +      buf->buf[len] = '\0';
  +   }
  +
  +   buf->len = len;
  +}
  +
  +
  +static int
  +_bson_json_read_null (void *_ctx)
  +{
  +   BASIC_YAJL_CB_PREAMBLE;
  +   BASIC_YAJL_CB_BAIL_IF_NOT_NORMAL ("null");
  +
  +   bson_append_null (STACK_BSON_CHILD, key, len);
  +
  +   return 1;
  +}
  +
  +
  +static int
  +_bson_json_read_boolean (void *_ctx, /* IN */
  +                         int   val)  /* IN */
  +{
  +   BASIC_YAJL_CB_PREAMBLE;
  +
  +   if (bson->read_state == BSON_JSON_IN_BSON_TYPE && bson->bson_state ==
  +       BSON_JSON_LF_UNDEFINED) {
  +      bson->bson_type_data.undefined.has_undefined = true;
  +      return 1;
  +   }
  +
  +   BASIC_YAJL_CB_BAIL_IF_NOT_NORMAL ("boolean");
  +
  +   bson_append_bool (STACK_BSON_CHILD, key, len, val);
  +
  +   return 1;
  +}
  +
  +
  +static int
  +_bson_json_read_integer (void    *_ctx, /* IN */
  +                         int64_t  val)  /* IN */
  +{
  +   bson_json_read_state_t rs;
  +   bson_json_read_bson_state_t bs;
  +
  +   BASIC_YAJL_CB_PREAMBLE;
  +
  +   rs = bson->read_state;
  +   bs = bson->bson_state;
  +
  +   if (rs == BSON_JSON_REGULAR) {
  +      if (val <= INT32_MAX) {
  +         bson_append_int32 (STACK_BSON_CHILD, key, len, (int)val);
  +      } else {
  +         bson_append_int64 (STACK_BSON_CHILD, key, len, val);
  +      }
  +   } else if (rs == BSON_JSON_IN_BSON_TYPE || rs ==
  +              BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) {
  +      switch (bs) {
  +      case BSON_JSON_LF_DATE:
  +         bson->bson_type_data.date.has_date = true;
  +         bson->bson_type_data.date.date = val;
  +         break;
  +      case BSON_JSON_LF_TIMESTAMP_T:
  +         bson->bson_type_data.timestamp.has_t = true;
  +         bson->bson_type_data.timestamp.t = (uint32_t)val;
  +         break;
  +      case BSON_JSON_LF_TIMESTAMP_I:
  +         bson->bson_type_data.timestamp.has_i = true;
  +         bson->bson_type_data.timestamp.i = (uint32_t)val;
  +         break;
  +      case BSON_JSON_LF_MINKEY:
  +         bson->bson_type_data.minkey.has_minkey = true;
  +         break;
  +      case BSON_JSON_LF_MAXKEY:
  +         bson->bson_type_data.maxkey.has_maxkey = true;
  +         break;
  +      case BSON_JSON_LF_REGEX:
  +      case BSON_JSON_LF_OPTIONS:
  +      case BSON_JSON_LF_OID:
  +      case BSON_JSON_LF_BINARY:
  +      case BSON_JSON_LF_TYPE:
  +      case BSON_JSON_LF_REF:
  +      case BSON_JSON_LF_ID:
  +      case BSON_JSON_LF_UNDEFINED:
  +      default:
  +         _bson_json_read_set_error (reader,
  +                                    "Invalid special type for integer read %d",
  +                                    bs);
  +         return 0;
  +      }
  +   } else {
  +      _bson_json_read_set_error (reader, "Invalid state for integer read %d",
  +                                 rs);
  +      return 0;
  +   }
  +
  +   return 1;
  +}
  +
  +
  +static int
  +_bson_json_read_double (void   *_ctx, /* IN */
  +                        double  val)  /* IN */
  +{
  +   BASIC_YAJL_CB_PREAMBLE;
  +   BASIC_YAJL_CB_BAIL_IF_NOT_NORMAL ("double");
  +
  +   bson_append_double (STACK_BSON_CHILD, key, len, val);
  +
  +   return 1;
  +}
  +
  +
  +static int
  +_bson_json_read_string (void                *_ctx, /* IN */
  +                        const unsigned char *val,  /* IN */
  +                        size_t               vlen) /* IN */
  +{
  +   bson_json_read_state_t rs;
  +   bson_json_read_bson_state_t bs;
  +
  +   BASIC_YAJL_CB_PREAMBLE;
  +
  +   rs = bson->read_state;
  +   bs = bson->bson_state;
  +
  +   if (rs == BSON_JSON_REGULAR) {
  +      bson_append_utf8 (STACK_BSON_CHILD, key, len, (const char *)val, vlen);
  +   } else if (rs == BSON_JSON_IN_BSON_TYPE || rs ==
  +              BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) {
  +      const char *val_w_null;
  +      _bson_json_buf_set (&bson->bson_type_buf[2], val, vlen, true);
  +      val_w_null = (const char *)bson->bson_type_buf[2].buf;
  +
  +      switch (bs) {
  +      case BSON_JSON_LF_REGEX:
  +         bson->bson_type_data.regex.has_regex = true;
  +         _bson_json_buf_set (&bson->bson_type_buf[0], val, vlen, true);
  +         break;
  +      case BSON_JSON_LF_OPTIONS:
  +         bson->bson_type_data.regex.has_options = true;
  +         _bson_json_buf_set (&bson->bson_type_buf[1], val, vlen, true);
  +         break;
  +      case BSON_JSON_LF_OID:
  +
  +         if (vlen != 24) {
  +            goto BAD_PARSE;
  +         }
  +
  +         bson->bson_type_data.oid.has_oid = true;
  +         bson_oid_init_from_string (&bson->bson_type_data.oid.oid, val_w_null);
  +         break;
  +      case BSON_JSON_LF_TYPE:
  +         bson->bson_type_data.binary.has_subtype = true;
  +
  +#ifdef _WIN32
  +# define SSCANF sscanf_s
  +#else
  +# define SSCANF sscanf
  +#endif
  +
  +         if (SSCANF (val_w_null, "%02x",
  +                     &bson->bson_type_data.binary.type) != 1) {
  +            goto BAD_PARSE;
  +         }
  +
  +#undef SSCANF
  +
  +         break;
  +      case BSON_JSON_LF_BINARY: {
  +            /* TODO: error handling for pton */
  +            int binary_len;
  +            bson->bson_type_data.binary.has_binary = true;
  +            binary_len = b64_pton (val_w_null, NULL, 0);
  +            _bson_json_buf_ensure (&bson->bson_type_buf[0], binary_len + 1);
  +            b64_pton ((char *)bson->bson_type_buf[2].buf,
  +                      bson->bson_type_buf[0].buf, binary_len + 1);
  +            bson->bson_type_buf[0].len = binary_len;
  +            break;
  +         }
  +      case BSON_JSON_LF_REF:
  +         bson->bson_type_data.ref.has_ref = true;
  +         _bson_json_buf_set (&bson->bson_type_buf[0], val, vlen, true);
  +         break;
  +      case BSON_JSON_LF_ID:
  +
  +         if (vlen != 24) {
  +            goto BAD_PARSE;
  +         }
  +
  +         bson->bson_type_data.ref.has_id = true;
  +         bson_oid_init_from_string (&bson->bson_type_data.ref.id, val_w_null);
  +         break;
  +      case BSON_JSON_LF_DATE:
  +      case BSON_JSON_LF_TIMESTAMP_T:
  +      case BSON_JSON_LF_TIMESTAMP_I:
  +      case BSON_JSON_LF_UNDEFINED:
  +      case BSON_JSON_LF_MINKEY:
  +      case BSON_JSON_LF_MAXKEY:
  +      default:
  +         goto BAD_PARSE;
  +      }
  +
  +      return 1;
  +
  +   BAD_PARSE:
  +      _bson_json_read_set_error (reader,
  +                                 "Invalid input string %s, looking for %d",
  +                                 val_w_null, bs);
  +      return 0;
  +   } else {
  +      _bson_json_read_set_error (reader, "Invalid state to look for string %d",
  +                                 rs);
  +      return 0;
  +   }
  +
  +   return 1;
  +}
  +
  +
  +static int
  +_bson_json_read_start_map (void *_ctx) /* IN */
  +{
  +   BASIC_YAJL_CB_PREAMBLE;
  +
  +   if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_STARTMAP) {
  +      bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES;
  +   } else {
  +      bson->read_state = BSON_JSON_IN_START_MAP;
  +   }
  +
  +   /* silence some warnings */
  +   (void)len;
  +   (void)key;
  +
  +   return 1;
  +}
  +
  +
  +static bool
  +_is_known_key (const char *key)
  +{
  +   bool ret;
  +
  +#define IS_KEY(k) (0 == strncmp (k, key, strlen(k) - 1))
  +
  +   /*
  +    * For the LULZ, yajl includes the end " character as part of the key name.
  +    */
  +   ret = (IS_KEY ("$regex") ||
  +          IS_KEY ("$options") ||
  +          IS_KEY ("$oid") ||
  +          IS_KEY ("$binary") ||
  +          IS_KEY ("$type") ||
  +          IS_KEY ("$date") ||
  +          IS_KEY ("$ref") ||
  +          IS_KEY ("$id") ||
  +          IS_KEY ("$undefined") ||
  +          IS_KEY ("$maxKey") ||
  +          IS_KEY ("$minKey") ||
  +          IS_KEY ("$timestamp"));
  +
  +#undef IS_KEY
  +
  +   return ret;
  +}
  +
  +
  +static int
  +_bson_json_read_map_key (void          *_ctx, /* IN */
  +                         const uint8_t *val,  /* IN */
  +                         size_t         len)  /* IN */
  +{
  +   bson_json_reader_t *reader = (bson_json_reader_t *)_ctx;
  +   bson_json_reader_bson_t *bson = &reader->bson;
  +
  +   if (bson->read_state == BSON_JSON_IN_START_MAP) {
  +      if (len > 0 && val[0] == '$' && _is_known_key ((const char *)val)) {
  +         bson->read_state = BSON_JSON_IN_BSON_TYPE;
  +         bson->bson_type = 0;
  +         memset (&bson->bson_type_data, 0, sizeof bson->bson_type_data);
  +      } else {
  +         bson->read_state = BSON_JSON_REGULAR;
  +         STACK_PUSH_DOC (bson_append_document_begin (STACK_BSON_PARENT,
  +                                                     bson->key,
  +                                                     bson->key_buf.len,
  +                                                     STACK_BSON_CHILD));
  +      }
  +   }
  +
  +   if (bson->read_state == BSON_JSON_IN_BSON_TYPE) {
  +      if HANDLE_OPTION ("$regex", BSON_TYPE_REGEX, BSON_JSON_LF_REGEX) else
  +      if HANDLE_OPTION ("$options", BSON_TYPE_REGEX, BSON_JSON_LF_OPTIONS) else
  +      if HANDLE_OPTION ("$oid", BSON_TYPE_OID, BSON_JSON_LF_OID) else
  +      if HANDLE_OPTION ("$binary", BSON_TYPE_BINARY, BSON_JSON_LF_BINARY) else
  +      if HANDLE_OPTION ("$type", BSON_TYPE_BINARY, BSON_JSON_LF_TYPE) else
  +      if HANDLE_OPTION ("$date", BSON_TYPE_DATE_TIME, BSON_JSON_LF_DATE) else
  +      if HANDLE_OPTION ("$ref", BSON_TYPE_DBPOINTER, BSON_JSON_LF_REF) else
  +      if HANDLE_OPTION ("$id", BSON_TYPE_DBPOINTER, BSON_JSON_LF_ID) else
  +      if HANDLE_OPTION ("$undefined", BSON_TYPE_UNDEFINED,
  +                        BSON_JSON_LF_UNDEFINED) else
  +      if HANDLE_OPTION ("$minKey", BSON_TYPE_MINKEY, BSON_JSON_LF_MINKEY) else
  +      if HANDLE_OPTION ("$maxKey", BSON_TYPE_MAXKEY, BSON_JSON_LF_MAXKEY) else
  +      if (len == strlen ("$timestamp") &&
  +          memcmp (val, "$timestamp", len) == 0) {
  +         bson->bson_type = BSON_TYPE_TIMESTAMP;
  +         bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_STARTMAP;
  +      } else {
  +         _bson_json_read_set_error (reader,
  +                                    "Invalid key %s.  Looking for values for %d",
  +                                    val, bson->bson_type);
  +         return 0;
  +      }
  +   } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) {
  +      if HANDLE_OPTION ("t", BSON_TYPE_TIMESTAMP, BSON_JSON_LF_TIMESTAMP_T) else
  +      if HANDLE_OPTION ("i", BSON_TYPE_TIMESTAMP,
  +                        BSON_JSON_LF_TIMESTAMP_I) else {
  +         _bson_json_read_set_error (reader,
  +                                    "Invalid key %s.  Looking for values for %d",
  +                                    val, bson->bson_type);
  +         return 0;
  +      }
  +   } else {
  +      _bson_json_buf_set (&bson->key_buf, val, len, true);
  +      bson->key = (const char *)bson->key_buf.buf;
  +   }
  +
  +   return 1;
  +}
  +
  +
  +static int
  +_bson_json_read_append_binary (bson_json_reader_t      *reader, /* IN */
  +                               bson_json_reader_bson_t *bson)   /* IN */
  +{
  +   if (!bson->bson_type_data.binary.has_binary) {
  +      _bson_json_read_set_error (reader,
  +                                 "Missing $binary after $type in \
BSON_TYPE_BINARY");  +   } else if (!bson->bson_type_data.binary.has_subtype) {
  +      _bson_json_read_set_error (reader,
  +                                 "Missing $type after $binary in \
BSON_TYPE_BINARY");  +   } else {
  +      return bson_append_binary (STACK_BSON_CHILD, bson->key, bson->key_buf.len,
  +                                 bson->bson_type_data.binary.type,
  +                                 bson->bson_type_buf[0].buf,
  +                                 bson->bson_type_buf[0].len);
  +   }
  +
  +   return 0;
  +}
  +
  +
  +static int
  +_bson_json_read_append_regex (bson_json_reader_t      *reader, /* IN */
  +                              bson_json_reader_bson_t *bson)   /* IN */
  +{
  +   char *regex = NULL;
  +   char *options = NULL;
  +
  +   if (!bson->bson_type_data.regex.has_regex) {
  +      _bson_json_read_set_error (reader,
  +                                 "Missing $regex after $options in \
BSON_TYPE_REGEX");  +      return 0;
  +   }
  +
  +   regex = (char *)bson->bson_type_buf[0].buf;
  +
  +   if (bson->bson_type_data.regex.has_options) {
  +      options = (char *)bson->bson_type_buf[1].buf;
  +   }
  +
  +   return bson_append_regex (STACK_BSON_CHILD, bson->key, bson->key_buf.len,
  +                             regex, options);
  +}
  +
  +
  +static int
  +_bson_json_read_append_oid (bson_json_reader_t      *reader, /* IN */
  +                            bson_json_reader_bson_t *bson)   /* IN */
  +{
  +   return bson_append_oid (STACK_BSON_CHILD, bson->key, bson->key_buf.len,
  +                           &bson->bson_type_data.oid.oid);
  +}
  +
  +
  +static int
  +_bson_json_read_append_date_time (bson_json_reader_t      *reader, /* IN */
  +                                  bson_json_reader_bson_t *bson)   /* IN */
  +{
  +   return bson_append_date_time (STACK_BSON_CHILD, bson->key, bson->key_buf.len,
  +                                 bson->bson_type_data.date.date);
  +}
  +
  +
  +static int
  +_bson_json_read_append_timestamp (bson_json_reader_t      *reader, /* IN */
  +                                  bson_json_reader_bson_t *bson)   /* IN */
  +{
  +   if (!bson->bson_type_data.timestamp.has_t) {
  +      _bson_json_read_set_error (reader,
  +                                 "Missing t after $timestamp in \
BSON_TYPE_TIMESTAMP");  +      return 0;
  +   }
  +
  +   if (!bson->bson_type_data.timestamp.has_i) {
  +      _bson_json_read_set_error (reader,
  +                                 "Missing i after $timestamp in \
BSON_TYPE_TIMESTAMP");  +      return 0;
  +   }
  +
  +   return bson_append_timestamp (STACK_BSON_CHILD, bson->key, bson->key_buf.len,
  +                                 bson->bson_type_data.timestamp.t,
  +                                 bson->bson_type_data.timestamp.i);
  +}
  +
  +
  +static int
  +_bson_json_read_append_dbpointer (bson_json_reader_t      *reader, /* IN */
  +                                  bson_json_reader_bson_t *bson)   /* IN */
  +{
  +   char *ref;
  +
  +   if (!bson->bson_type_data.ref.has_ref) {
  +      _bson_json_read_set_error (reader,
  +                                 "Missing $ref after $id in BSON_TYPE_DBPOINTER");
  +      return 0;
  +   }
  +
  +   if (!bson->bson_type_data.ref.has_id) {
  +      _bson_json_read_set_error (reader,
  +                                 "Missing $id after $ref in BSON_TYPE_DBPOINTER");
  +      return 0;
  +   }
  +
  +   ref = (char *)bson->bson_type_buf[0].buf;
  +
  +   return bson_append_dbpointer (STACK_BSON_CHILD, bson->key, bson->key_buf.len,
  +                                 ref, &bson->bson_type_data.ref.id);
  +}
  +
  +
  +static int
  +_bson_json_read_end_map (void *_ctx) /* IN */
  +{
  +   bson_json_reader_t *reader = (bson_json_reader_t *)_ctx;
  +   bson_json_reader_bson_t *bson = &reader->bson;
  +
  +   if (bson->read_state == BSON_JSON_IN_START_MAP) {
  +      bson->read_state = BSON_JSON_REGULAR;
  +      STACK_PUSH_DOC (bson_append_document_begin (STACK_BSON_PARENT, bson->key,
  +                                                  bson->key_buf.len,
  +                                                  STACK_BSON_CHILD));
  +   }
  +
  +   if (bson->read_state == BSON_JSON_IN_BSON_TYPE) {
  +      bson->read_state = BSON_JSON_REGULAR;
  +      switch (bson->bson_type) {
  +      case BSON_TYPE_REGEX:
  +         return _bson_json_read_append_regex (reader, bson);
  +      case BSON_TYPE_OID:
  +         return _bson_json_read_append_oid (reader, bson);
  +      case BSON_TYPE_BINARY:
  +         return _bson_json_read_append_binary (reader, bson);
  +      case BSON_TYPE_DATE_TIME:
  +         return _bson_json_read_append_date_time (reader, bson);
  +      case BSON_TYPE_DBPOINTER:
  +         return _bson_json_read_append_dbpointer (reader, bson);
  +      case BSON_TYPE_UNDEFINED:
  +         return bson_append_undefined (STACK_BSON_CHILD, bson->key,
  +                                       bson->key_buf.len);
  +      case BSON_TYPE_MINKEY:
  +         return bson_append_minkey (STACK_BSON_CHILD, bson->key,
  +                                    bson->key_buf.len);
  +      case BSON_TYPE_MAXKEY:
  +         return bson_append_maxkey (STACK_BSON_CHILD, bson->key,
  +                                    bson->key_buf.len);
  +      case BSON_TYPE_EOD:
  +      case BSON_TYPE_DOUBLE:
  +      case BSON_TYPE_UTF8:
  +      case BSON_TYPE_DOCUMENT:
  +      case BSON_TYPE_ARRAY:
  +      case BSON_TYPE_BOOL:
  +      case BSON_TYPE_NULL:
  +      case BSON_TYPE_CODE:
  +      case BSON_TYPE_SYMBOL:
  +      case BSON_TYPE_CODEWSCOPE:
  +      case BSON_TYPE_INT32:
  +      case BSON_TYPE_TIMESTAMP:
  +      case BSON_TYPE_INT64:
  +      default:
  +         _bson_json_read_set_error (reader, "Unknown type %d", bson->bson_type);
  +         return 0;
  +         break;
  +      }
  +   } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) {
  +      bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_ENDMAP;
  +
  +      return _bson_json_read_append_timestamp (reader, bson);
  +   } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_ENDMAP) {
  +      bson->read_state = BSON_JSON_REGULAR;
  +   } else if (bson->read_state == BSON_JSON_REGULAR) {
  +      STACK_POP_DOC (bson_append_document_end (STACK_BSON_PARENT,
  +                                               STACK_BSON_CHILD));
  +
  +      if (bson->n == -1) {
  +         bson->read_state = BSON_JSON_DONE;
  +         return 0;
  +      }
  +   } else {
  +      _bson_json_read_set_error (reader, "Invalid state %d", bson->read_state);
  +      return 0;
  +   }
  +
  +   return 1;
  +}
  +
  +
  +static int
  +_bson_json_read_start_array (void *_ctx) /* IN */
  +{
  +   BASIC_YAJL_CB_PREAMBLE;
  +   BASIC_YAJL_CB_BAIL_IF_NOT_NORMAL ("[");
  +
  +   STACK_PUSH_ARRAY (bson_append_array_begin (STACK_BSON_PARENT, key, len,
  +                                              STACK_BSON_CHILD));
  +
  +   return 1;
  +}
  +
  +
  +static int
  +_bson_json_read_end_array (void *_ctx) /* IN */
  +{
  +   bson_json_reader_t *reader = (bson_json_reader_t *)_ctx;
  +   bson_json_reader_bson_t *bson = &reader->bson;
  +
  +   BASIC_YAJL_CB_BAIL_IF_NOT_NORMAL ("]");
  +
  +   STACK_POP_ARRAY (bson_append_array_end (STACK_BSON_PARENT,
  +                                           STACK_BSON_CHILD));
  +
  +   return 1;
  +}
  +
  +
  +static yajl_callbacks read_cbs = {
  +   _bson_json_read_null,
  +   _bson_json_read_boolean,
  +   _bson_json_read_integer,
  +   _bson_json_read_double,
  +   NULL,
  +   _bson_json_read_string,
  +   _bson_json_read_start_map,
  +   _bson_json_read_map_key,
  +   _bson_json_read_end_map,
  +   _bson_json_read_start_array,
  +   _bson_json_read_end_array
  +};
  +
  +
  +static int
  +_bson_json_read_parse_error (bson_json_reader_t *reader, /* IN */
  +                             yajl_status         ys,     /* IN */
  +                             bson_error_t       *error)  /* OUT */
  +{
  +   unsigned char *str;
  +   int r;
  +   yajl_handle yh = reader->yh;
  +   bson_json_reader_bson_t *bson = &reader->bson;
  +   bson_json_reader_producer_t *p = &reader->producer;
  +
  +   if (ys == yajl_status_client_canceled) {
  +      if (bson->read_state == BSON_JSON_DONE) {
  +         r = 1;
  +      } else {
  +         r = -1;
  +      }
  +   } else if (p->all_whitespace) {
  +      r = 0;
  +   } else {
  +      if (error) {
  +         str = yajl_get_error (yh, 1, p->buf + p->bytes_parsed,
  +                               p->bytes_read - p->bytes_parsed);
  +         bson_set_error (error,
  +                         BSON_ERROR_JSON,
  +                         BSON_JSON_ERROR_READ_CORRUPT_JS,
  +                         "%s", str);
  +         yajl_free_error (yh, str);
  +      }
  +
  +      r = -1;
  +   }
  +
  +   p->bytes_parsed += yajl_get_bytes_consumed (yh);
  +
  +   yh->stateStack.used = 0;
  +   yajl_bs_push (yh->stateStack, yajl_state_start);
  +
  +   return r;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_json_reader_read --
  + *
  + *       Read the next json document from @reader and write its value
  + *       into @bson. @bson will be allocated as part of this process.
  + *
  + *       @bson MUST be initialized before calling this function as it
  + *       will not be initialized automatically. The reasoning for this
  + *       is so that you can chain together bson_json_reader_t with
  + *       other components like bson_writer_t.
  + *
  + * Returns:
  + *       1 if successful and data was read.
  + *       0 if successful and no data was read.
  + *       -1 if there was an error and @error is set.
  + *
  + * Side effects:
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int
  +bson_json_reader_read (bson_json_reader_t *reader, /* IN */
  +                       bson_t             *bson,   /* IN */
  +                       bson_error_t       *error)  /* OUT */
  +{
  +   bson_json_reader_producer_t *p;
  +   yajl_status ys;
  +   yajl_handle yh;
  +   ssize_t r;
  +   bool read_something = false;
  +   int ret = 0;
  +
  +   bson_return_val_if_fail (reader, -1);
  +   bson_return_val_if_fail (bson, -1);
  +
  +   p = &reader->producer;
  +   yh = reader->yh;
  +
  +   reader->bson.bson = bson;
  +   reader->bson.n = -1;
  +   reader->bson.read_state = BSON_JSON_REGULAR;
  +   reader->error = error;
  +   reader->producer.all_whitespace = true;
  +
  +   for (;; ) {
  +      if (!read_something &&
  +          p->bytes_parsed &&
  +          (p->bytes_read > p->bytes_parsed)) {
  +         r = p->bytes_read - p->bytes_parsed;
  +      } else {
  +         r = p->cb (p->data, p->buf, p->buf_size - 1);
  +
  +         if (r > 0) {
  +            p->bytes_read = r;
  +            p->bytes_parsed = 0;
  +            p->buf[r] = '\0';
  +         }
  +      }
  +
  +      if (r < 0) {
  +         if (error) {
  +            bson_set_error (error,
  +                            BSON_ERROR_JSON,
  +                            BSON_JSON_ERROR_READ_CB_FAILURE,
  +                            "reader cb failed");
  +         }
  +         ret = -1;
  +         goto cleanup;
  +      } else if (r == 0) {
  +         break;
  +      } else {
  +         read_something = true;
  +
  +         if (p->all_whitespace) {
  +            p->all_whitespace = _bson_json_all_whitespace (
  +               (char *)(p->buf + p->bytes_parsed));
  +         }
  +
  +         ys = yajl_parse (yh, p->buf + p->bytes_parsed, r);
  +
  +         if (ys != yajl_status_ok) {
  +            ret = _bson_json_read_parse_error (reader, ys, error);
  +            goto cleanup;
  +         }
  +      }
  +   }
  +
  +   if (read_something) {
  +      ys = yajl_complete_parse (yh);
  +
  +      if (ys != yajl_status_ok) {
  +         ret = _bson_json_read_parse_error (reader, ys, error);
  +         goto cleanup;
  +      }
  +   }
  +
  +cleanup:
  +
  +   return ret;
  +}
  +
  +
  +bson_json_reader_t *
  +bson_json_reader_new (void                 *data,           /* IN */
  +                      bson_json_reader_cb   cb,             /* IN */
  +                      bson_json_destroy_cb  dcb,            /* IN */
  +                      bool                  allow_multiple, /* IN */
  +                      size_t                buf_size)       /* IN */
  +{
  +   bson_json_reader_t *r;
  +   bson_json_reader_producer_t *p;
  +
  +   r = bson_malloc0 (sizeof *r);
  +
  +   p = &r->producer;
  +
  +   p->data = data;
  +   p->cb = cb;
  +   p->dcb = dcb;
  +   p->buf = bson_malloc (buf_size);
  +   p->buf_size = buf_size ? buf_size : BSON_JSON_DEFAULT_BUF_SIZE;
  +
  +   r->yh = yajl_alloc (&read_cbs, NULL, r);
  +
  +   yajl_config (r->yh,
  +                yajl_dont_validate_strings |
  +                (allow_multiple ?  yajl_allow_multiple_values : 0)
  +                , 1);
  +
  +   return r;
  +}
  +
  +
  +void
  +bson_json_reader_destroy (bson_json_reader_t *reader) /* IN */
  +{
  +   int i;
  +   bson_json_reader_producer_t *p = &reader->producer;
  +   bson_json_reader_bson_t *b = &reader->bson;
  +
  +   if (reader->producer.dcb) {
  +      reader->producer.dcb (reader->producer.data);
  +   }
  +
  +   bson_free (p->buf);
  +   bson_free (b->key_buf.buf);
  +
  +   for (i = 0; i < 3; i++) {
  +      bson_free (b->bson_type_buf[i].buf);
  +   }
  +
  +   yajl_free (reader->yh);
  +
  +   bson_free (reader);
  +}
  +
  +
  +typedef struct
  +{
  +   const uint8_t *data;
  +   size_t         len;
  +   size_t         bytes_parsed;
  +} bson_json_data_reader_t;
  +
  +
  +static ssize_t
  +_bson_json_data_reader_cb (void    *_ctx,
  +                           uint8_t *buf,
  +                           size_t   len)
  +{
  +   size_t bytes;
  +   bson_json_data_reader_t *ctx = (bson_json_data_reader_t *)_ctx;
  +
  +   if (!ctx->data) {
  +      return -1;
  +   }
  +
  +   bytes = MIN (len, ctx->len - ctx->bytes_parsed);
  +
  +   memcpy (buf, ctx->data + ctx->bytes_parsed, bytes);
  +
  +   ctx->bytes_parsed += bytes;
  +
  +   return bytes;
  +}
  +
  +
  +bson_json_reader_t *
  +bson_json_data_reader_new (bool   allow_multiple, /* IN */
  +                           size_t size)           /* IN */
  +{
  +   bson_json_data_reader_t *dr = bson_malloc0 (sizeof *dr);
  +
  +   return bson_json_reader_new (dr, &_bson_json_data_reader_cb, &free,
  +                                allow_multiple, size);
  +}
  +
  +
  +void
  +bson_json_data_reader_ingest (bson_json_reader_t *reader, /* IN */
  +                              const uint8_t      *data,   /* IN */
  +                              size_t              len)    /* IN */
  +{
  +   bson_json_data_reader_t *ctx =
  +      (bson_json_data_reader_t *)reader->producer.data;
  +
  +   ctx->data = data;
  +   ctx->len = len;
  +   ctx->bytes_parsed = 0;
  +}
  +
  +
  +bson_t *
  +bson_new_from_json (const uint8_t *data,  /* IN */
  +                    size_t         len,   /* IN */
  +                    bson_error_t  *error) /* OUT */
  +{
  +   bson_json_reader_t *reader;
  +   bson_t *bson;
  +   int r;
  +
  +   bson_return_val_if_fail (data, NULL);
  +
  +   bson = bson_new ();
  +   reader = bson_json_data_reader_new (false, BSON_JSON_DEFAULT_BUF_SIZE);
  +   bson_json_data_reader_ingest (reader, data, len);
  +   r = bson_json_reader_read (reader, bson, error);
  +   bson_json_reader_destroy (reader);
  +
  +   if (r != 1) {
  +      bson_destroy (bson);
  +      return NULL;
  +   }
  +
  +   return bson;
  +}
  +
  +
  +bool
  +bson_init_from_json (bson_t       *bson,  /* OUT */
  +                     const char   *data,  /* IN */
  +                     ssize_t       len,   /* IN */
  +                     bson_error_t *error) /* OUT */
  +{
  +   bson_json_reader_t *reader;
  +   int r;
  +
  +   bson_return_val_if_fail (bson, false);
  +   bson_return_val_if_fail (data, false);
  +
  +   if (len < 0) {
  +      len = strlen (data);
  +   }
  +
  +   bson_init (bson);
  +
  +   reader = bson_json_data_reader_new (false, BSON_JSON_DEFAULT_BUF_SIZE);
  +   bson_json_data_reader_ingest (reader, (const uint8_t *)data, len);
  +   r = bson_json_reader_read (reader, bson, error);
  +   bson_json_reader_destroy (reader);
  +
  +   if (r != 1) {
  +      bson_destroy (bson);
  +      return false;
  +   }
  +
  +   return true;
  +}
  +
  +
  +static void
  +_bson_json_reader_handle_fd_destroy (void *handle) /* IN */
  +{
  +   bson_json_reader_handle_fd_t *fd = handle;
  +
  +   if (fd) {
  +      if ((fd->fd != -1) && fd->do_close) {
  +#ifdef _WIN32
  +		 _close(fd->fd);
  +#else
  +         close (fd->fd);
  +#endif
  +      }
  +      bson_free (fd);
  +   }
  +}
  +
  +
  +static ssize_t
  +_bson_json_reader_handle_fd_read (void    *handle, /* IN */
  +                                  uint8_t *buf,    /* IN */
  +                                  size_t   len)   /* IN */
  +{
  +   bson_json_reader_handle_fd_t *fd = handle;
  +   ssize_t ret = -1;
  +
  +   if (fd && (fd->fd != -1)) {
  +   again:
  +#ifdef BSON_OS_WIN32
  +      ret = _read (fd->fd, buf, (unsigned int)len);
  +#else
  +      ret = read (fd->fd, buf, len);
  +#endif
  +      if ((ret == -1) && (errno == EAGAIN)) {
  +         goto again;
  +      }
  +   }
  +
  +   return ret;
  +}
  +
  +
  +bson_json_reader_t *
  +bson_json_reader_new_from_fd (int fd,                /* IN */
  +                              bool close_on_destroy) /* IN */
  +{
  +   bson_json_reader_handle_fd_t *handle;
  +
  +   bson_return_val_if_fail (fd != -1, NULL);
  +
  +   handle = bson_malloc0 (sizeof *handle);
  +   handle->fd = fd;
  +   handle->do_close = close_on_destroy;
  +
  +   return bson_json_reader_new (handle,
  +                                _bson_json_reader_handle_fd_read,
  +                                _bson_json_reader_handle_fd_destroy,
  +                                true,
  +                                BSON_JSON_DEFAULT_BUF_SIZE);
  +}
  +
  +
  +bson_json_reader_t *
  +bson_json_reader_new_from_file (const char   *path,  /* IN */
  +                                bson_error_t *error) /* OUT */
  +{
  +   char errmsg[32];
  +   int fd = -1;
  +
  +   bson_return_val_if_fail (path, NULL);
  +
  +#ifdef BSON_OS_WIN32
  +   fd = _sopen_s (&fd, path, (_O_RDONLY | _O_BINARY), _SH_DENYNO, _S_IREAD);
  +#else
  +   fd = open (path, O_RDONLY);
  +#endif
  +
  +   if (fd == -1) {
  +      bson_strerror_r (errno, errmsg, sizeof errmsg);
  +      bson_set_error (error,
  +                      BSON_ERROR_READER,
  +                      BSON_ERROR_READER_BADFD,
  +                      "%s", errmsg);
  +      return NULL;
  +   }
  +
  +   return bson_json_reader_new_from_fd (fd, true);
  +}
  +/*==============================================================*/
  +/* --- bson-keys.c */
  +
  +static const char * gUint32Strs[] = {
  +   "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
  +   "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23",
  +   "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34",
  +   "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45",
  +   "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56",
  +   "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67",
  +   "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78",
  +   "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
  +   "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100",
  +   "101", "102", "103", "104", "105", "106", "107", "108", "109", "110",
  +   "111", "112", "113", "114", "115", "116", "117", "118", "119", "120",
  +   "121", "122", "123", "124", "125", "126", "127", "128", "129", "130",
  +   "131", "132", "133", "134", "135", "136", "137", "138", "139", "140",
  +   "141", "142", "143", "144", "145", "146", "147", "148", "149", "150",
  +   "151", "152", "153", "154", "155", "156", "157", "158", "159", "160",
  +   "161", "162", "163", "164", "165", "166", "167", "168", "169", "170",
  +   "171", "172", "173", "174", "175", "176", "177", "178", "179", "180",
  +   "181", "182", "183", "184", "185", "186", "187", "188", "189", "190",
  +   "191", "192", "193", "194", "195", "196", "197", "198", "199", "200",
  +   "201", "202", "203", "204", "205", "206", "207", "208", "209", "210",
  +   "211", "212", "213", "214", "215", "216", "217", "218", "219", "220",
  +   "221", "222", "223", "224", "225", "226", "227", "228", "229", "230",
  +   "231", "232", "233", "234", "235", "236", "237", "238", "239", "240",
  +   "241", "242", "243", "244", "245", "246", "247", "248", "249", "250",
  +   "251", "252", "253", "254", "255", "256", "257", "258", "259", "260",
  +   "261", "262", "263", "264", "265", "266", "267", "268", "269", "270",
  +   "271", "272", "273", "274", "275", "276", "277", "278", "279", "280",
  +   "281", "282", "283", "284", "285", "286", "287", "288", "289", "290",
  +   "291", "292", "293", "294", "295", "296", "297", "298", "299", "300",
  +   "301", "302", "303", "304", "305", "306", "307", "308", "309", "310",
  +   "311", "312", "313", "314", "315", "316", "317", "318", "319", "320",
  +   "321", "322", "323", "324", "325", "326", "327", "328", "329", "330",
  +   "331", "332", "333", "334", "335", "336", "337", "338", "339", "340",
  +   "341", "342", "343", "344", "345", "346", "347", "348", "349", "350",
  +   "351", "352", "353", "354", "355", "356", "357", "358", "359", "360",
  +   "361", "362", "363", "364", "365", "366", "367", "368", "369", "370",
  +   "371", "372", "373", "374", "375", "376", "377", "378", "379", "380",
  +   "381", "382", "383", "384", "385", "386", "387", "388", "389", "390",
  +   "391", "392", "393", "394", "395", "396", "397", "398", "399", "400",
  +   "401", "402", "403", "404", "405", "406", "407", "408", "409", "410",
  +   "411", "412", "413", "414", "415", "416", "417", "418", "419", "420",
  +   "421", "422", "423", "424", "425", "426", "427", "428", "429", "430",
  +   "431", "432", "433", "434", "435", "436", "437", "438", "439", "440",
  +   "441", "442", "443", "444", "445", "446", "447", "448", "449", "450",
  +   "451", "452", "453", "454", "455", "456", "457", "458", "459", "460",
  +   "461", "462", "463", "464", "465", "466", "467", "468", "469", "470",
  +   "471", "472", "473", "474", "475", "476", "477", "478", "479", "480",
  +   "481", "482", "483", "484", "485", "486", "487", "488", "489", "490",
  +   "491", "492", "493", "494", "495", "496", "497", "498", "499", "500",
  +   "501", "502", "503", "504", "505", "506", "507", "508", "509", "510",
  +   "511", "512", "513", "514", "515", "516", "517", "518", "519", "520",
  +   "521", "522", "523", "524", "525", "526", "527", "528", "529", "530",
  +   "531", "532", "533", "534", "535", "536", "537", "538", "539", "540",
  +   "541", "542", "543", "544", "545", "546", "547", "548", "549", "550",
  +   "551", "552", "553", "554", "555", "556", "557", "558", "559", "560",
  +   "561", "562", "563", "564", "565", "566", "567", "568", "569", "570",
  +   "571", "572", "573", "574", "575", "576", "577", "578", "579", "580",
  +   "581", "582", "583", "584", "585", "586", "587", "588", "589", "590",
  +   "591", "592", "593", "594", "595", "596", "597", "598", "599", "600",
  +   "601", "602", "603", "604", "605", "606", "607", "608", "609", "610",
  +   "611", "612", "613", "614", "615", "616", "617", "618", "619", "620",
  +   "621", "622", "623", "624", "625", "626", "627", "628", "629", "630",
  +   "631", "632", "633", "634", "635", "636", "637", "638", "639", "640",
  +   "641", "642", "643", "644", "645", "646", "647", "648", "649", "650",
  +   "651", "652", "653", "654", "655", "656", "657", "658", "659", "660",
  +   "661", "662", "663", "664", "665", "666", "667", "668", "669", "670",
  +   "671", "672", "673", "674", "675", "676", "677", "678", "679", "680",
  +   "681", "682", "683", "684", "685", "686", "687", "688", "689", "690",
  +   "691", "692", "693", "694", "695", "696", "697", "698", "699", "700",
  +   "701", "702", "703", "704", "705", "706", "707", "708", "709", "710",
  +   "711", "712", "713", "714", "715", "716", "717", "718", "719", "720",
  +   "721", "722", "723", "724", "725", "726", "727", "728", "729", "730",
  +   "731", "732", "733", "734", "735", "736", "737", "738", "739", "740",
  +   "741", "742", "743", "744", "745", "746", "747", "748", "749", "750",
  +   "751", "752", "753", "754", "755", "756", "757", "758", "759", "760",
  +   "761", "762", "763", "764", "765", "766", "767", "768", "769", "770",
  +   "771", "772", "773", "774", "775", "776", "777", "778", "779", "780",
  +   "781", "782", "783", "784", "785", "786", "787", "788", "789", "790",
  +   "791", "792", "793", "794", "795", "796", "797", "798", "799", "800",
  +   "801", "802", "803", "804", "805", "806", "807", "808", "809", "810",
  +   "811", "812", "813", "814", "815", "816", "817", "818", "819", "820",
  +   "821", "822", "823", "824", "825", "826", "827", "828", "829", "830",
  +   "831", "832", "833", "834", "835", "836", "837", "838", "839", "840",
  +   "841", "842", "843", "844", "845", "846", "847", "848", "849", "850",
  +   "851", "852", "853", "854", "855", "856", "857", "858", "859", "860",
  +   "861", "862", "863", "864", "865", "866", "867", "868", "869", "870",
  +   "871", "872", "873", "874", "875", "876", "877", "878", "879", "880",
  +   "881", "882", "883", "884", "885", "886", "887", "888", "889", "890",
  +   "891", "892", "893", "894", "895", "896", "897", "898", "899", "900",
  +   "901", "902", "903", "904", "905", "906", "907", "908", "909", "910",
  +   "911", "912", "913", "914", "915", "916", "917", "918", "919", "920",
  +   "921", "922", "923", "924", "925", "926", "927", "928", "929", "930",
  +   "931", "932", "933", "934", "935", "936", "937", "938", "939", "940",
  +   "941", "942", "943", "944", "945", "946", "947", "948", "949", "950",
  +   "951", "952", "953", "954", "955", "956", "957", "958", "959", "960",
  +   "961", "962", "963", "964", "965", "966", "967", "968", "969", "970",
  +   "971", "972", "973", "974", "975", "976", "977", "978", "979", "980",
  +   "981", "982", "983", "984", "985", "986", "987", "988", "989", "990",
  +   "991", "992", "993", "994", "995", "996", "997", "998", "999"
  +};
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_uint32_to_string --
  + *
  + *       Converts @value to a string.
  + *
  + *       If @value is from 0 to 1000, it will use a constant string in the
  + *       data section of the library.
  + *
  + *       If not, a string will be formatted using @str and snprintf(). This
  + *       is much slower, of course and therefore we try to optimize it out.
  + *
  + *       @strptr will always be set. It will either point to @str or a
  + *       constant string. You will want to use this as your key.
  + *
  + * Parameters:
  + *       @value: A #uint32_t to convert to string.
  + *       @strptr: (out): A pointer to the resulting string.
  + *       @str: (out): Storage for a string made with snprintf.
  + *       @size: Size of @str.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +size_t
  +bson_uint32_to_string (uint32_t     value,  /* IN */
  +                       const char **strptr, /* OUT */
  +                       char        *str,    /* IN */
  +                       size_t       size)   /* IN */
  +{
  +   size_t i;
  +
  +   if (value < 1000) {
  +      *strptr = gUint32Strs[value];
  +
  +      if (value < 10) {
  +         return 1;
  +      } else if (value < 100) {
  +         return 2;
  +      } else {
  +         return 3;
  +      }
  +   } else {
  +      i = size;
  +      str[i] = '\0';
  +
  +      while (value) {
  +         i--;
  +         str[i] = (value % 10) + '0';
  +         value /= 10;
  +      }
  +
  +      *strptr = str + i;
  +
  +      return size - i;
  +   }
  +}
  +/*==============================================================*/
  +/* --- bson-md5.c */
  +
  +#undef BYTE_ORDER   /* 1 = big-endian, -1 = little-endian, 0 = unknown */
  +#if BSON_BYTE_ORDER == BSON_BIG_ENDIAN
  +#  define BYTE_ORDER 1
  +#else
  +#  define BYTE_ORDER -1
  +#endif
  +
  +#define T_MASK ((uint32_t)~0)
  +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
  +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
  +#define T3    0x242070db
  +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
  +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
  +#define T6    0x4787c62a
  +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
  +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
  +#define T9    0x698098d8
  +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
  +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
  +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
  +#define T13    0x6b901122
  +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
  +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
  +#define T16    0x49b40821
  +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
  +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
  +#define T19    0x265e5a51
  +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
  +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
  +#define T22    0x02441453
  +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
  +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
  +#define T25    0x21e1cde6
  +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
  +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
  +#define T28    0x455a14ed
  +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
  +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
  +#define T31    0x676f02d9
  +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
  +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
  +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
  +#define T35    0x6d9d6122
  +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
  +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
  +#define T38    0x4bdecfa9
  +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
  +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
  +#define T41    0x289b7ec6
  +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
  +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
  +#define T44    0x04881d05
  +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
  +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
  +#define T47    0x1fa27cf8
  +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
  +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
  +#define T50    0x432aff97
  +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
  +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
  +#define T53    0x655b59c3
  +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
  +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
  +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
  +#define T57    0x6fa87e4f
  +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
  +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
  +#define T60    0x4e0811a1
  +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
  +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
  +#define T63    0x2ad7d2bb
  +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
  +
  +
  +static void
  +bson_md5_process (bson_md5_t     *md5,
  +                  const uint8_t *data)
  +{
  +   uint32_t a = md5->abcd[0];
  +   uint32_t b = md5->abcd[1];
  +   uint32_t c = md5->abcd[2];
  +   uint32_t d = md5->abcd[3];
  +   uint32_t t;
  +
  +#if BYTE_ORDER > 0
  +    /* Define storage only for big-endian CPUs. */
  +    uint32_t X[16];
  +#else
  +    /* Define storage for little-endian or both types of CPUs. */
  +    uint32_t xbuf[16];
  +    const uint32_t *X;
  +#endif
  +
  +    {
  +#if BYTE_ORDER == 0
  +        /*
  +         * Determine dynamically whether this is a big-endian or
  +         * little-endian machine, since we can use a more efficient
  +         * algorithm on the latter.
  +         */
  +        static const int w = 1;
  +
  +        if (*((const uint8_t *)&w)) /* dynamic little-endian */
  +#endif
  +#if BYTE_ORDER <= 0     /* little-endian */
  +        {
  +            /*
  +             * On little-endian machines, we can process properly aligned
  +             * data without copying it.
  +             */
  +            if (!((data - (const uint8_t *)0) & 3)) {
  +                /* data are properly aligned */
  +#ifdef __clang__
  +#pragma clang diagnostic push
  +#pragma clang diagnostic ignored "-Wcast-align"
  +#endif
  +                X = (const uint32_t *)data;
  +#ifdef __clang__
  +#pragma clang diagnostic pop
  +#endif
  +            }
  +            else {
  +                /* not aligned */
  +                memcpy(xbuf, data, 64);
  +                X = xbuf;
  +            }
  +        }
  +#endif
  +#if BYTE_ORDER == 0
  +        else            /* dynamic big-endian */
  +#endif
  +#if BYTE_ORDER >= 0     /* big-endian */
  +        {
  +            /*
  +             * On big-endian machines, we must arrange the bytes in the
  +             * right order.
  +             */
  +            const uint8_t *xp = data;
  +            int i;
  +
  +#  if BYTE_ORDER == 0
  +            X = xbuf;       /* (dynamic only) */
  +#  else
  +#    define xbuf X      /* (static only) */
  +#  endif
  +            for (i = 0; i < 16; ++i, xp += 4)
  +                xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
  +        }
  +#endif
  +    }
  +
  +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
  +
  +    /* Round 1. */
  +    /* Let [abcd k s i] denote the operation
  +       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
  +#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
  +#define SET(a, b, c, d, k, s, Ti)\
  +    t = a + F(b,c,d) + X[k] + Ti;\
  +    a = ROTATE_LEFT(t, s) + b
  +    /* Do the following 16 operations. */
  +    SET(a, b, c, d,  0,  7,  T1);
  +    SET(d, a, b, c,  1, 12,  T2);
  +    SET(c, d, a, b,  2, 17,  T3);
  +    SET(b, c, d, a,  3, 22,  T4);
  +    SET(a, b, c, d,  4,  7,  T5);
  +    SET(d, a, b, c,  5, 12,  T6);
  +    SET(c, d, a, b,  6, 17,  T7);
  +    SET(b, c, d, a,  7, 22,  T8);
  +    SET(a, b, c, d,  8,  7,  T9);
  +    SET(d, a, b, c,  9, 12, T10);
  +    SET(c, d, a, b, 10, 17, T11);
  +    SET(b, c, d, a, 11, 22, T12);
  +    SET(a, b, c, d, 12,  7, T13);
  +    SET(d, a, b, c, 13, 12, T14);
  +    SET(c, d, a, b, 14, 17, T15);
  +    SET(b, c, d, a, 15, 22, T16);
  +#undef SET
  +
  +    /* Round 2. */
  +    /* Let [abcd k s i] denote the operation
  +         a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
  +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
  +#define SET(a, b, c, d, k, s, Ti)\
  +    t = a + G(b,c,d) + X[k] + Ti;\
  +    a = ROTATE_LEFT(t, s) + b
  +    /* Do the following 16 operations. */
  +    SET(a, b, c, d,  1,  5, T17);
  +    SET(d, a, b, c,  6,  9, T18);
  +    SET(c, d, a, b, 11, 14, T19);
  +    SET(b, c, d, a,  0, 20, T20);
  +    SET(a, b, c, d,  5,  5, T21);
  +    SET(d, a, b, c, 10,  9, T22);
  +    SET(c, d, a, b, 15, 14, T23);
  +    SET(b, c, d, a,  4, 20, T24);
  +    SET(a, b, c, d,  9,  5, T25);
  +    SET(d, a, b, c, 14,  9, T26);
  +    SET(c, d, a, b,  3, 14, T27);
  +    SET(b, c, d, a,  8, 20, T28);
  +    SET(a, b, c, d, 13,  5, T29);
  +    SET(d, a, b, c,  2,  9, T30);
  +    SET(c, d, a, b,  7, 14, T31);
  +    SET(b, c, d, a, 12, 20, T32);
  +#undef SET
  +
  +    /* Round 3. */
  +    /* Let [abcd k s t] denote the operation
  +         a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
  +#define H(x, y, z) ((x) ^ (y) ^ (z))
  +#define SET(a, b, c, d, k, s, Ti)\
  +    t = a + H(b,c,d) + X[k] + Ti;\
  +    a = ROTATE_LEFT(t, s) + b
  +    /* Do the following 16 operations. */
  +    SET(a, b, c, d,  5,  4, T33);
  +    SET(d, a, b, c,  8, 11, T34);
  +    SET(c, d, a, b, 11, 16, T35);
  +    SET(b, c, d, a, 14, 23, T36);
  +    SET(a, b, c, d,  1,  4, T37);
  +    SET(d, a, b, c,  4, 11, T38);
  +    SET(c, d, a, b,  7, 16, T39);
  +    SET(b, c, d, a, 10, 23, T40);
  +    SET(a, b, c, d, 13,  4, T41);
  +    SET(d, a, b, c,  0, 11, T42);
  +    SET(c, d, a, b,  3, 16, T43);
  +    SET(b, c, d, a,  6, 23, T44);
  +    SET(a, b, c, d,  9,  4, T45);
  +    SET(d, a, b, c, 12, 11, T46);
  +    SET(c, d, a, b, 15, 16, T47);
  +    SET(b, c, d, a,  2, 23, T48);
  +#undef SET
  +
  +    /* Round 4. */
  +    /* Let [abcd k s t] denote the operation
  +         a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
  +#define I(x, y, z) ((y) ^ ((x) | ~(z)))
  +#define SET(a, b, c, d, k, s, Ti)\
  +    t = a + I(b,c,d) + X[k] + Ti;\
  +    a = ROTATE_LEFT(t, s) + b
  +    /* Do the following 16 operations. */
  +    SET(a, b, c, d,  0,  6, T49);
  +    SET(d, a, b, c,  7, 10, T50);
  +    SET(c, d, a, b, 14, 15, T51);
  +    SET(b, c, d, a,  5, 21, T52);
  +    SET(a, b, c, d, 12,  6, T53);
  +    SET(d, a, b, c,  3, 10, T54);
  +    SET(c, d, a, b, 10, 15, T55);
  +    SET(b, c, d, a,  1, 21, T56);
  +    SET(a, b, c, d,  8,  6, T57);
  +    SET(d, a, b, c, 15, 10, T58);
  +    SET(c, d, a, b,  6, 15, T59);
  +    SET(b, c, d, a, 13, 21, T60);
  +    SET(a, b, c, d,  4,  6, T61);
  +    SET(d, a, b, c, 11, 10, T62);
  +    SET(c, d, a, b,  2, 15, T63);
  +    SET(b, c, d, a,  9, 21, T64);
  +#undef SET
  +
  +    /* Then perform the following additions. (That is increment each
  +       of the four registers by the value it had before this block
  +       was started.) */
  +    md5->abcd[0] += a;
  +    md5->abcd[1] += b;
  +    md5->abcd[2] += c;
  +    md5->abcd[3] += d;
  +}
  +
  +void
  +bson_md5_init (bson_md5_t *pms)
  +{
  +    pms->count[0] = pms->count[1] = 0;
  +    pms->abcd[0] = 0x67452301;
  +    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
  +    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
  +    pms->abcd[3] = 0x10325476;
  +}
  +
  +void
  +bson_md5_append (bson_md5_t         *pms,
  +                 const uint8_t *data,
  +                 uint32_t       nbytes)
  +{
  +    const uint8_t *p = data;
  +    int left = nbytes;
  +    int offset = (pms->count[0] >> 3) & 63;
  +    uint32_t nbits = (uint32_t)(nbytes << 3);
  +
  +    if (nbytes <= 0)
  +        return;
  +
  +    /* Update the message length. */
  +    pms->count[1] += nbytes >> 29;
  +    pms->count[0] += nbits;
  +    if (pms->count[0] < nbits)
  +        pms->count[1]++;
  +
  +    /* Process an initial partial block. */
  +    if (offset) {
  +        int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
  +
  +        memcpy(pms->buf + offset, p, copy);
  +        if (offset + copy < 64)
  +            return;
  +        p += copy;
  +        left -= copy;
  +        bson_md5_process(pms, pms->buf);
  +    }
  +
  +    /* Process full blocks. */
  +    for (; left >= 64; p += 64, left -= 64)
  +        bson_md5_process(pms, p);
  +
  +    /* Process a final partial block. */
  +    if (left)
  +        memcpy(pms->buf, p, left);
  +}
  +
  +void
  +bson_md5_finish (bson_md5_t   *pms,
  +                 uint8_t  digest[16])
  +{
  +    static const uint8_t pad[64] = {
  +        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  +        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  +        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  +        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  +    };
  +    uint8_t data[8];
  +    int i;
  +
  +    /* Save the length before padding. */
  +    for (i = 0; i < 8; ++i)
  +        data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3));
  +    /* Pad to 56 bytes mod 64. */
  +    bson_md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
  +    /* Append the length. */
  +    bson_md5_append(pms, data, 8);
  +    for (i = 0; i < 16; ++i)
  +        digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
  +}
  +/*==============================================================*/
  +/* --- bson-memory.c */
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_malloc --
  + *
  + *       Allocates @num_bytes of memory and returns a pointer to it.  If
  + *       malloc failed to allocate the memory, abort() is called.
  + *
  + *       Libbson does not try to handle OOM conditions as it is beyond the
  + *       scope of this library to handle so appropriately.
  + *
  + * Parameters:
  + *       @num_bytes: The number of bytes to allocate.
  + *
  + * Returns:
  + *       A pointer if successful; otherwise abort() is called and this
  + *       function will never return.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void *
  +bson_malloc (size_t num_bytes) /* IN */
  +{
  +   void *mem;
  +
  +   if (!(mem = malloc (num_bytes))) {
  +      abort ();
  +   }
  +
  +   return mem;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_malloc0 --
  + *
  + *       Like bson_malloc() except the memory is zeroed first. This is
  + *       similar to calloc() except that abort() is called in case of
  + *       failure to allocate memory.
  + *
  + * Parameters:
  + *       @num_bytes: The number of bytes to allocate.
  + *
  + * Returns:
  + *       A pointer if successful; otherwise abort() is called and this
  + *       function will never return.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void *
  +bson_malloc0 (size_t num_bytes) /* IN */
  +{
  +   void *mem = NULL;
  +
  +   if (BSON_LIKELY (num_bytes)) {
  +      if (BSON_UNLIKELY (!(mem = calloc (1, num_bytes)))) {
  +         abort ();
  +      }
  +   }
  +
  +   return mem;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_realloc --
  + *
  + *       This function behaves similar to realloc() except that if there is
  + *       a failure abort() is called.
  + *
  + * Parameters:
  + *       @mem: The memory to realloc, or NULL.
  + *       @num_bytes: The size of the new allocation or 0 to free.
  + *
  + * Returns:
  + *       The new allocation if successful; otherwise abort() is called and
  + *       this function never returns.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void *
  +bson_realloc (void   *mem,        /* IN */
  +              size_t  num_bytes)  /* IN */
  +{
  +   mem = realloc (mem, num_bytes);
  +
  +   if (BSON_UNLIKELY (!mem)) {
  +      if (!num_bytes) {
  +         return mem;
  +      }
  +
  +      abort ();
  +   }
  +
  +   return mem;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_free --
  + *
  + *       Frees @mem using the underlying allocator.
  + *
  + *       Currently, this only calls free() directly, but that is subject to
  + *       change.
  + *
  + * Parameters:
  + *       @mem: An allocation to free.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_free (void *mem) /* IN */
  +{
  +   free (mem);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_zero_free --
  + *
  + *       Frees @mem using the underlying allocator. @size bytes of @mem will
  + *       be zeroed before freeing the memory. This is useful in scenarios
  + *       where @mem contains passwords or other sensitive information.
  + *
  + * Parameters:
  + *       @mem: An allocation to free.
  + *       @size: The number of bytes in @mem.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_zero_free (void  *mem,  /* IN */
  +                size_t size) /* IN */
  +{
  +   if (BSON_LIKELY (mem)) {
  +      memset (mem, 0, size);
  +      bson_free (mem);
  +   }
  +}
  +/*==============================================================*/
  +/* --- bson-oid.c */
  +
  +/*
  + * This table contains an array of two character pairs for every possible
  + * uint8_t. It is used as a lookup table when encoding a bson_oid_t
  + * to hex formatted ASCII. Performing two characters at a time roughly
  + * reduces the number of operations by one-half.
  + */
  +static const uint16_t gHexCharPairs[] = {
  +#if BSON_BYTE_ORDER == BSON_BIG_ENDIAN
  +   12336, 12337, 12338, 12339, 12340, 12341, 12342, 12343, 12344, 12345,
  +   12385, 12386, 12387, 12388, 12389, 12390, 12592, 12593, 12594, 12595,
  +   12596, 12597, 12598, 12599, 12600, 12601, 12641, 12642, 12643, 12644,
  +   12645, 12646, 12848, 12849, 12850, 12851, 12852, 12853, 12854, 12855,
  +   12856, 12857, 12897, 12898, 12899, 12900, 12901, 12902, 13104, 13105,
  +   13106, 13107, 13108, 13109, 13110, 13111, 13112, 13113, 13153, 13154,
  +   13155, 13156, 13157, 13158, 13360, 13361, 13362, 13363, 13364, 13365,
  +   13366, 13367, 13368, 13369, 13409, 13410, 13411, 13412, 13413, 13414,
  +   13616, 13617, 13618, 13619, 13620, 13621, 13622, 13623, 13624, 13625,
  +   13665, 13666, 13667, 13668, 13669, 13670, 13872, 13873, 13874, 13875,
  +   13876, 13877, 13878, 13879, 13880, 13881, 13921, 13922, 13923, 13924,
  +   13925, 13926, 14128, 14129, 14130, 14131, 14132, 14133, 14134, 14135,
  +   14136, 14137, 14177, 14178, 14179, 14180, 14181, 14182, 14384, 14385,
  +   14386, 14387, 14388, 14389, 14390, 14391, 14392, 14393, 14433, 14434,
  +   14435, 14436, 14437, 14438, 14640, 14641, 14642, 14643, 14644, 14645,
  +   14646, 14647, 14648, 14649, 14689, 14690, 14691, 14692, 14693, 14694,
  +   24880, 24881, 24882, 24883, 24884, 24885, 24886, 24887, 24888, 24889,
  +   24929, 24930, 24931, 24932, 24933, 24934, 25136, 25137, 25138, 25139,
  +   25140, 25141, 25142, 25143, 25144, 25145, 25185, 25186, 25187, 25188,
  +   25189, 25190, 25392, 25393, 25394, 25395, 25396, 25397, 25398, 25399,
  +   25400, 25401, 25441, 25442, 25443, 25444, 25445, 25446, 25648, 25649,
  +   25650, 25651, 25652, 25653, 25654, 25655, 25656, 25657, 25697, 25698,
  +   25699, 25700, 25701, 25702, 25904, 25905, 25906, 25907, 25908, 25909,
  +   25910, 25911, 25912, 25913, 25953, 25954, 25955, 25956, 25957, 25958,
  +   26160, 26161, 26162, 26163, 26164, 26165, 26166, 26167, 26168, 26169,
  +   26209, 26210, 26211, 26212, 26213, 26214
  +#else
  +   12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640,
  +   24880, 25136, 25392, 25648, 25904, 26160, 12337, 12593, 12849, 13105,
  +   13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649,
  +   25905, 26161, 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130,
  +   14386, 14642, 24882, 25138, 25394, 25650, 25906, 26162, 12339, 12595,
  +   12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139,
  +   25395, 25651, 25907, 26163, 12340, 12596, 12852, 13108, 13364, 13620,
  +   13876, 14132, 14388, 14644, 24884, 25140, 25396, 25652, 25908, 26164,
  +   12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645,
  +   24885, 25141, 25397, 25653, 25909, 26165, 12342, 12598, 12854, 13110,
  +   13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654,
  +   25910, 26166, 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135,
  +   14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167, 12344, 12600,
  +   12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144,
  +   25400, 25656, 25912, 26168, 12345, 12601, 12857, 13113, 13369, 13625,
  +   13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169,
  +   12385, 12641, 12897, 13153, 13409, 13665, 13921, 14177, 14433, 14689,
  +   24929, 25185, 25441, 25697, 25953, 26209, 12386, 12642, 12898, 13154,
  +   13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698,
  +   25954, 26210, 12387, 12643, 12899, 13155, 13411, 13667, 13923, 14179,
  +   14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, 12388, 12644,
  +   12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188,
  +   25444, 25700, 25956, 26212, 12389, 12645, 12901, 13157, 13413, 13669,
  +   13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213,
  +   12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694,
  +   24934, 25190, 25446, 25702, 25958, 26214
  +#endif
  +};
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_init_sequence --
  + *
  + *       Initializes @oid with the next oid in the sequence. The first 4
  + *       bytes contain the current time and the following 8 contain a 64-bit
  + *       integer in big-endian format.
  + *
  + *       The bson_oid_t generated by this function is not guaranteed to be
  + *       globally unique. Only unique within this context. It is however,
  + *       guaranteed to be sequential.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_oid_init_sequence (bson_oid_t     *oid,     /* OUT */
  +                        bson_context_t *context) /* IN */
  +{
  +   uint32_t now = (uint32_t)(time (NULL));
  +
  +   if (!context) {
  +      context = bson_context_get_default ();
  +   }
  +
  +   now = BSON_UINT32_TO_BE (now);
  +
  +   memcpy (&oid->bytes[0], &now, 4);
  +   context->oid_get_seq64 (context, oid);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_init --
  + *
  + *       Generates bytes for a new bson_oid_t and stores them in @oid. The
  + *       bytes will be generated according to the specification and includes
  + *       the current time, first 3 bytes of MD5(hostname), pid (or tid), and
  + *       monotonic counter.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_oid_init (bson_oid_t     *oid,     /* OUT */
  +               bson_context_t *context) /* IN */
  +{
  +   uint32_t now = (uint32_t)(time (NULL));
  +
  +   bson_return_if_fail (oid);
  +
  +   if (!context) {
  +      context = bson_context_get_default ();
  +   }
  +
  +   now = BSON_UINT32_TO_BE (now);
  +   memcpy (&oid->bytes[0], &now, 4);
  +
  +   context->oid_get_host (context, oid);
  +   context->oid_get_pid (context, oid);
  +   context->oid_get_seq32 (context, oid);
  +}
  +
  +
  +/**
  + * bson_oid_init_from_data:
  + * @oid: A bson_oid_t to initialize.
  + * @bytes: A 12-byte buffer to copy into @oid.
  + *
  + */
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_init_from_data --
  + *
  + *       Initializes an @oid from @data. @data MUST be a buffer of at least
  + *       12 bytes. This method is analagous to memcpy()'ing data into @oid.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_oid_init_from_data (bson_oid_t    *oid,  /* OUT */
  +                         const uint8_t *data) /* IN */
  +{
  +   bson_return_if_fail (oid);
  +   bson_return_if_fail (data);
  +
  +   memcpy (oid, data, 12);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_init_from_string --
  + *
  + *       Parses @str containing hex formatted bytes of an object id and
  + *       places the bytes in @oid.
  + *
  + * Parameters:
  + *       @oid: A bson_oid_t
  + *       @str: A string containing at least 24 characters.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @oid is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_oid_init_from_string (bson_oid_t *oid, /* OUT */
  +                           const char *str) /* IN */
  +{
  +   bson_return_if_fail (oid);
  +   bson_return_if_fail (str);
  +
  +   bson_oid_init_from_string_unsafe (oid, str);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_get_time_t --
  + *
  + *       Fetches the time for which @oid was created.
  + *
  + * Returns:
  + *       A time_t.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +time_t
  +bson_oid_get_time_t (const bson_oid_t *oid) /* IN */
  +{
  +   bson_return_val_if_fail (oid, 0);
  +
  +   return bson_oid_get_time_t_unsafe (oid);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_to_string --
  + *
  + *       Formats a bson_oid_t into a string. @str must contain enough bytes
  + *       for the resulting string which is 25 bytes with a terminating
  + *       NUL-byte.
  + *
  + * Parameters:
  + *       @oid: A bson_oid_t.
  + *       @str: A location to store the resulting string.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_oid_to_string
  +   (const bson_oid_t *oid,                                   /* IN */
  +    char              str[BSON_ENSURE_ARRAY_PARAM_SIZE(25)]) /* OUT */
  +{
  +   uint16_t *dst;
  +   uint8_t *id = (uint8_t *)oid;
  +
  +   bson_return_if_fail (oid);
  +   bson_return_if_fail (str);
  +
  +   dst = (uint16_t *)(void *)str;
  +   dst[0] = gHexCharPairs[id[0]];
  +   dst[1] = gHexCharPairs[id[1]];
  +   dst[2] = gHexCharPairs[id[2]];
  +   dst[3] = gHexCharPairs[id[3]];
  +   dst[4] = gHexCharPairs[id[4]];
  +   dst[5] = gHexCharPairs[id[5]];
  +   dst[6] = gHexCharPairs[id[6]];
  +   dst[7] = gHexCharPairs[id[7]];
  +   dst[8] = gHexCharPairs[id[8]];
  +   dst[9] = gHexCharPairs[id[9]];
  +   dst[10] = gHexCharPairs[id[10]];
  +   dst[11] = gHexCharPairs[id[11]];
  +   str[24] = '\0';
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_hash --
  + *
  + *       Hashes the bytes of the provided bson_oid_t using DJB hash.  This
  + *       allows bson_oid_t to be used as keys in a hash table.
  + *
  + * Returns:
  + *       A hash value corresponding to @oid.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +uint32_t
  +bson_oid_hash (const bson_oid_t *oid) /* IN */
  +{
  +   bson_return_val_if_fail (oid, 5381);
  +
  +   return bson_oid_hash_unsafe (oid);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_compare --
  + *
  + *       A qsort() style compare function that will return less than zero if
  + *       @oid1 is less than @oid2, zero if they are the same, and greater
  + *       than zero if @oid2 is greater than @oid1.
  + *
  + * Returns:
  + *       A qsort() style compare integer.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int
  +bson_oid_compare (const bson_oid_t *oid1, /* IN */
  +                  const bson_oid_t *oid2) /* IN */
  +{
  +   bson_return_val_if_fail (oid1, 0);
  +   bson_return_val_if_fail (oid2, 0);
  +
  +   return bson_oid_compare_unsafe (oid1, oid2);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_equal --
  + *
  + *       Compares for equality of @oid1 and @oid2. If they are equal, then
  + *       true is returned, otherwise false.
  + *
  + * Returns:
  + *       A boolean indicating the equality of @oid1 and @oid2.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_oid_equal (const bson_oid_t *oid1, /* IN */
  +                const bson_oid_t *oid2) /* IN */
  +{
  +   bson_return_val_if_fail (oid1, false);
  +   bson_return_val_if_fail (oid2, false);
  +
  +   return bson_oid_equal_unsafe (oid1, oid2);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_copy --
  + *
  + *       Copies the contents of @src to @dst.
  + *
  + * Parameters:
  + *       @src: A bson_oid_t to copy from.
  + *       @dst: A bson_oid_t to copy to.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @dst will contain a copy of the data in @src.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_oid_copy (const bson_oid_t *src, /* IN */
  +               bson_oid_t       *dst) /* OUT */
  +{
  +   bson_return_if_fail (src);
  +   bson_return_if_fail (dst);
  +
  +   bson_oid_copy_unsafe (src, dst);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_oid_is_valid --
  + *
  + *       Validates that @str is a valid OID string. @length MUST be 12, but
  + *       is provided as a parameter to simplify calling code.
  + *
  + * Parameters:
  + *       @str: A string to validate.
  + *       @length: The length of @str.
  + *
  + * Returns:
  + *       true if @str can be passed to bson_oid_init_from_string().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_oid_is_valid (const char *str,    /* IN */
  +                   size_t      length) /* IN */
  +{
  +   size_t i;
  +
  +   bson_return_val_if_fail (str, false);
  +
  +   if (length == 24) {
  +      for (i = 0; i < length; i++) {
  +         switch (str[i]) {
  +         case '0': case '1': case '2': case '3': case '4': case '5': case '6':
  +         case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd':
  +         case 'e': case 'f':
  +            break;
  +         default:
  +            return false;
  +         }
  +      }
  +      return true;
  +   }
  +
  +   return false;
  +}
  +/*==============================================================*/
  +/* --- bson-reader.c */
  +
  +typedef enum
  +{
  +   BSON_READER_HANDLE = 1,
  +   BSON_READER_DATA = 2,
  +} bson_reader_type_t;
  +
  +
  +typedef struct
  +{
  +   bson_reader_type_t         type;
  +   void                      *handle;
  +   bool                       done   : 1;
  +   bool                       failed : 1;
  +   size_t                     end;
  +   size_t                     len;
  +   size_t                     offset;
  +   size_t                     bytes_read;
  +   bson_t                     inline_bson;
  +   uint8_t                   *data;
  +   bson_reader_read_func_t    read_func;
  +   bson_reader_destroy_func_t destroy_func;
  +} bson_reader_handle_t;
  +
  +
  +typedef struct
  +{
  +   int fd;
  +   bool do_close;
  +} bson_reader_handle_fd_t;
  +
  +
  +typedef struct
  +{
  +   bson_reader_type_t type;
  +   const uint8_t     *data;
  +   size_t             length;
  +   size_t             offset;
  +   bson_t             inline_bson;
  +} bson_reader_data_t;
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_reader_handle_fill_buffer --
  + *
  + *       Attempt to read as much as possible until the underlying buffer
  + *       in @reader is filled or we have reached end-of-stream or
  + *       read failure.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_reader_handle_fill_buffer (bson_reader_handle_t *reader) /* IN */
  +{
  +   ssize_t ret;
  +
  +   BSON_ASSERT (reader);
  +
  +   /*
  +    * Handle first read specially.
  +    */
  +   if ((!reader->done) && (!reader->offset) && (!reader->end)) {
  +      ret = reader->read_func (reader->handle, &reader->data[0], reader->len);
  +
  +      if (ret <= 0) {
  +         reader->done = true;
  +         return;
  +      }
  +      reader->bytes_read += ret;
  +
  +      reader->end = ret;
  +      return;
  +   }
  +
  +   /*
  +    * Move valid data to head.
  +    */
  +   memmove (&reader->data[0],
  +            &reader->data[reader->offset],
  +            reader->end - reader->offset);
  +   reader->end = reader->end - reader->offset;
  +   reader->offset = 0;
  +
  +   /*
  +    * Read in data to fill the buffer.
  +    */
  +   ret = reader->read_func (reader->handle,
  +                            &reader->data[reader->end],
  +                            reader->len - reader->end);
  +
  +   if (ret <= 0) {
  +      reader->done = true;
  +      reader->failed = (ret < 0);
  +   } else {
  +      reader->bytes_read += ret;
  +      reader->end += ret;
  +   }
  +
  +   bson_return_if_fail (reader->offset == 0);
  +   bson_return_if_fail (reader->end <= reader->len);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_new_from_handle --
  + *
  + *       Allocates and initializes a new bson_reader_t using the opaque
  + *       handle provided.
  + *
  + * Parameters:
  + *       @handle: an opaque handle to use to read data.
  + *       @rf: a function to perform reads on @handle.
  + *       @df: a function to release @handle, or NULL.
  + *
  + * Returns:
  + *       A newly allocated bson_reader_t if successful, otherwise NULL.
  + *       Free the successful result with bson_reader_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_reader_t *
  +bson_reader_new_from_handle (void                       *handle,
  +                             bson_reader_read_func_t     rf,
  +                             bson_reader_destroy_func_t  df)
  +{
  +   bson_reader_handle_t *real;
  +
  +   bson_return_val_if_fail (handle, NULL);
  +   bson_return_val_if_fail (rf, NULL);
  +
  +   real = bson_malloc0 (sizeof *real);
  +   real->type = BSON_READER_HANDLE;
  +   real->data = bson_malloc0 (1024);
  +   real->handle = handle;
  +   real->len = 1024;
  +   real->offset = 0;
  +
  +   bson_reader_set_read_func ((bson_reader_t *)real, rf);
  +
  +   if (df) {
  +      bson_reader_set_destroy_func ((bson_reader_t *)real, df);
  +   }
  +
  +   _bson_reader_handle_fill_buffer (real);
  +
  +   return (bson_reader_t *)real;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_reader_handle_fd_destroy --
  + *
  + *       Cleanup allocations associated with state created in
  + *       bson_reader_new_from_fd().
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_reader_handle_fd_destroy (void *handle) /* IN */
  +{
  +   bson_reader_handle_fd_t *fd = handle;
  +
  +   if (fd) {
  +      if ((fd->fd != -1) && fd->do_close) {
  +#ifdef _WIN32
  +         _close (fd->fd);
  +#else
  +         close (fd->fd);
  +#endif
  +      }
  +      bson_free (fd);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_reader_handle_fd_read --
  + *
  + *       Perform read on opaque handle created in
  + *       bson_reader_new_from_fd().
  + *
  + *       The underlying file descriptor is read from the current position
  + *       using the bson_reader_handle_fd_t allocated.
  + *
  + * Returns:
  + *       -1 on failure.
  + *       0 on end of stream.
  + *       Greater than zero on success.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static ssize_t
  +_bson_reader_handle_fd_read (void   *handle, /* IN */
  +                             void   *buf,    /* IN */
  +                             size_t  len)    /* IN */
  +{
  +   bson_reader_handle_fd_t *fd = handle;
  +   ssize_t ret = -1;
  +
  +   if (fd && (fd->fd != -1)) {
  +   again:
  +#ifdef BSON_OS_WIN32
  +      ret = _read (fd->fd, buf, (unsigned int)len);
  +#else
  +      ret = read (fd->fd, buf, len);
  +#endif
  +      if ((ret == -1) && (errno == EAGAIN)) {
  +         goto again;
  +      }
  +   }
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_new_from_fd --
  + *
  + *       Create a new bson_reader_t using the file-descriptor provided.
  + *
  + * Parameters:
  + *       @fd: a libc style file-descriptor.
  + *       @close_on_destroy: if close() should be called on @fd when
  + *          bson_reader_destroy() is called.
  + *
  + * Returns:
  + *       A newly allocated bson_reader_t on success; otherwise NULL.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_reader_t *
  +bson_reader_new_from_fd (int  fd,               /* IN */
  +                         bool close_on_destroy) /* IN */
  +{
  +   bson_reader_handle_fd_t *handle;
  +
  +   bson_return_val_if_fail (fd != -1, NULL);
  +
  +   handle = bson_malloc0 (sizeof *handle);
  +   handle->fd = fd;
  +   handle->do_close = close_on_destroy;
  +
  +   return bson_reader_new_from_handle (handle,
  +                                       _bson_reader_handle_fd_read,
  +                                       _bson_reader_handle_fd_destroy);
  +}
  +
  +
  +/**
  + * bson_reader_set_read_func:
  + * @reader: A bson_reader_t.
  + *
  + * Note that @reader must be initialized by bson_reader_init_from_handle(), or \
data  + * will be destroyed.
  + */
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_set_read_func --
  + *
  + *       Set the read func to be provided for @reader.
  + *
  + *       You probably want to use bson_reader_new_from_handle() or
  + *       bson_reader_new_from_fd() instead.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_reader_set_read_func (bson_reader_t          *reader, /* IN */
  +                           bson_reader_read_func_t func)   /* IN */
  +{
  +   bson_reader_handle_t *real = (bson_reader_handle_t *)reader;
  +
  +   bson_return_if_fail (reader->type == BSON_READER_HANDLE);
  +
  +   real->read_func = func;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_set_destroy_func --
  + *
  + *       Set the function to cleanup state when @reader is destroyed.
  + *
  + *       You probably want bson_reader_new_from_fd() or
  + *       bson_reader_new_from_handle() instead.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_reader_set_destroy_func (bson_reader_t             *reader, /* IN */
  +                              bson_reader_destroy_func_t func)   /* IN */
  +{
  +   bson_reader_handle_t *real = (bson_reader_handle_t *)reader;
  +
  +   bson_return_if_fail (reader->type == BSON_READER_HANDLE);
  +
  +   real->destroy_func = func;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_reader_handle_grow_buffer --
  + *
  + *       Grow the buffer to the next power of two.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_reader_handle_grow_buffer (bson_reader_handle_t *reader) /* IN */
  +{
  +   size_t size;
  +
  +   bson_return_if_fail (reader);
  +
  +   size = reader->len * 2;
  +   reader->data = bson_realloc (reader->data, size);
  +   reader->len = size;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_reader_handle_tell --
  + *
  + *       Tell the current position within the underlying file-descriptor.
  + *
  + * Returns:
  + *       An off_t containing the current offset.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static off_t
  +_bson_reader_handle_tell (bson_reader_handle_t *reader) /* IN */
  +{
  +   off_t off;
  +
  +   bson_return_val_if_fail (reader, -1);
  +
  +   off = (off_t)reader->bytes_read;
  +   off -= (off_t)reader->end;
  +   off += (off_t)reader->offset;
  +
  +   return off;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_reader_handle_read --
  + *
  + *       Read the next chunk of data from the underlying file descriptor
  + *       and return a bson_t which should not be modified.
  + *
  + *       There was a failure if NULL is returned and @reached_eof is
  + *       not set to true.
  + *
  + * Returns:
  + *       NULL on failure or end of stream.
  + *
  + * Side effects:
  + *       @reached_eof is set if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static const bson_t *
  +_bson_reader_handle_read (bson_reader_handle_t *reader,      /* IN */
  +                          bool                 *reached_eof) /* IN */
  +{
  +   uint32_t blen;
  +
  +   bson_return_val_if_fail (reader, NULL);
  +
  +   while (!reader->done) {
  +      if ((reader->end - reader->offset) < 4) {
  +         _bson_reader_handle_fill_buffer (reader);
  +         continue;
  +      }
  +
  +      memcpy (&blen, &reader->data[reader->offset], sizeof blen);
  +      blen = BSON_UINT32_FROM_LE (blen);
  +
  +      if (blen > (reader->end - reader->offset)) {
  +         if (blen > reader->len) {
  +            _bson_reader_handle_grow_buffer (reader);
  +         }
  +
  +         _bson_reader_handle_fill_buffer (reader);
  +         continue;
  +      }
  +
  +      if (!bson_init_static (&reader->inline_bson,
  +                             &reader->data[reader->offset],
  +                             blen)) {
  +         return NULL;
  +      }
  +
  +      reader->offset += blen;
  +
  +      return &reader->inline_bson;
  +   }
  +
  +   if (reached_eof) {
  +      *reached_eof = reader->done && !reader->failed;
  +   }
  +
  +   return NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_new_from_data --
  + *
  + *       Allocates and initializes a new bson_reader_t that will the memory
  + *       provided as a stream of BSON documents.
  + *
  + * Parameters:
  + *       @data: A buffer to read BSON documents from.
  + *       @length: The length of @data.
  + *
  + * Returns:
  + *       A newly allocated bson_reader_t that should be freed with
  + *       bson_reader_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_reader_t *
  +bson_reader_new_from_data (const uint8_t *data,   /* IN */
  +                           size_t         length) /* IN */
  +{
  +   bson_reader_data_t *real;
  +
  +   bson_return_val_if_fail (data, NULL);
  +
  +   real = bson_malloc0 (sizeof *real);
  +   real->type = BSON_READER_DATA;
  +   real->data = data;
  +   real->length = length;
  +   real->offset = 0;
  +
  +   return (bson_reader_t *)real;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_reader_data_read --
  + *
  + *       Read the next document from the underlying buffer.
  + *
  + * Returns:
  + *       NULL on failure or end of stream.
  + *       a bson_t which should not be modified.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static const bson_t *
  +_bson_reader_data_read (bson_reader_data_t *reader,      /* IN */
  +                        bool               *reached_eof) /* IN */
  +{
  +   uint32_t blen;
  +
  +   bson_return_val_if_fail (reader, NULL);
  +
  +   if (reached_eof) {
  +      *reached_eof = false;
  +   }
  +
  +   if ((reader->offset + 4) < reader->length) {
  +      memcpy (&blen, &reader->data[reader->offset], sizeof blen);
  +      blen = BSON_UINT32_FROM_LE (blen);
  +
  +      if ((blen + reader->offset) <= reader->length) {
  +         if (!bson_init_static (&reader->inline_bson,
  +                                &reader->data[reader->offset],
  +                                blen)) {
  +            if (reached_eof) {
  +               *reached_eof = false;
  +            }
  +
  +            return NULL;
  +         }
  +
  +         reader->offset += blen;
  +
  +         if (reached_eof) {
  +            *reached_eof = (reader->offset == reader->length);
  +         }
  +
  +         return &reader->inline_bson;
  +      }
  +   }
  +
  +   if (reached_eof) {
  +      *reached_eof = (reader->offset == reader->length);
  +   }
  +
  +   return NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_reader_data_tell --
  + *
  + *       Tell the current position in the underlying buffer.
  + *
  + * Returns:
  + *       An off_t of the current offset.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static off_t
  +_bson_reader_data_tell (bson_reader_data_t *reader) /* IN */
  +{
  +   bson_return_val_if_fail (reader, -1);
  +
  +   return (off_t)reader->offset;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_destroy --
  + *
  + *       Release a bson_reader_t created with bson_reader_new_from_data(),
  + *       bson_reader_new_from_fd(), or bson_reader_new_from_handle().
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_reader_destroy (bson_reader_t *reader) /* IN */
  +{
  +   bson_return_if_fail (reader);
  +
  +   switch (reader->type) {
  +   case 0:
  +      break;
  +   case BSON_READER_HANDLE:
  +      {
  +         bson_reader_handle_t *handle = (bson_reader_handle_t *)reader;
  +
  +         if (handle->destroy_func) {
  +
  +            handle->destroy_func(handle->handle);
  +         }
  +
  +         bson_free (handle->data);
  +      }
  +      break;
  +   case BSON_READER_DATA:
  +      break;
  +   default:
  +      fprintf (stderr, "No such reader type: %02x\n", reader->type);
  +      break;
  +   }
  +
  +   reader->type = 0;
  +
  +   bson_free (reader);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_read --
  + *
  + *       Reads the next bson_t in the underlying memory or storage.  The
  + *       resulting bson_t should not be modified or freed. You may copy it
  + *       and iterate over it.  Functions that take a const bson_t* are safe
  + *       to use.
  + *
  + *       This structure does not survive calls to bson_reader_read() or
  + *       bson_reader_destroy() as it uses memory allocated by the reader or
  + *       underlying storage/memory.
  + *
  + *       If NULL is returned then @reached_eof will be set to true if the
  + *       end of the file or buffer was reached. This indicates if there was
  + *       an error parsing the document stream.
  + *
  + * Returns:
  + *       A const bson_t that should not be modified or freed.
  + *       NULL on failure or end of stream.
  + *
  + * Side effects:
  + *       @reached_eof is set if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const bson_t *
  +bson_reader_read (bson_reader_t *reader,      /* IN */
  +                  bool          *reached_eof) /* OUT */
  +{
  +   bson_return_val_if_fail (reader, NULL);
  +
  +   switch (reader->type) {
  +   case BSON_READER_HANDLE:
  +      return _bson_reader_handle_read ((bson_reader_handle_t *)reader, \
reached_eof);  +
  +   case BSON_READER_DATA:
  +      return _bson_reader_data_read ((bson_reader_data_t *)reader, reached_eof);
  +
  +   default:
  +      fprintf (stderr, "No such reader type: %02x\n", reader->type);
  +      break;
  +   }
  +
  +   return NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_tell --
  + *
  + *       Return the current position in the underlying reader. This will
  + *       always be at the beginning of a bson document or end of file.
  + *
  + * Returns:
  + *       An off_t containing the current offset.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +off_t
  +bson_reader_tell (bson_reader_t *reader) /* IN */
  +{
  +   bson_return_val_if_fail (reader, -1);
  +
  +   switch (reader->type) {
  +   case BSON_READER_HANDLE:
  +      return _bson_reader_handle_tell ((bson_reader_handle_t *)reader);
  +
  +   case BSON_READER_DATA:
  +      return _bson_reader_data_tell ((bson_reader_data_t *)reader);
  +
  +   default:
  +      fprintf (stderr, "No such reader type: %02x\n", reader->type);
  +      return -1;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_reader_new_from_file --
  + *
  + *       A convenience function to open a file containing sequential
  + *       bson documents and read them using bson_reader_t.
  + *
  + * Returns:
  + *       A new bson_reader_t if successful, otherwise NULL and
  + *       @error is set. Free the non-NULL result with
  + *       bson_reader_destroy().
  + *
  + * Side effects:
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_reader_t *
  +bson_reader_new_from_file (const char   *path,  /* IN */
  +                           bson_error_t *error) /* OUT */
  +{
  +   char errmsg[32];
  +   int fd;
  +
  +   bson_return_val_if_fail (path, NULL);
  +
  +#ifdef BSON_OS_WIN32
  +   if (_sopen_s (&fd, path, (_O_RDONLY | _O_BINARY), _SH_DENYNO, 0) != 0) {
  +      fd = -1;
  +   }
  +#else
  +   fd = open (path, O_RDONLY);
  +#endif
  +
  +   if (fd == -1) {
  +      bson_strerror_r (errno, errmsg, sizeof errmsg);
  +      bson_set_error (error,
  +                      BSON_ERROR_READER,
  +                      BSON_ERROR_READER_BADFD,
  +                      "%s", errmsg);
  +      return NULL;
  +   }
  +
  +   return bson_reader_new_from_fd (fd, true);
  +}
  +/*==============================================================*/
  +/* --- bson-string.c */
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_string_new --
  + *
  + *       Create a new bson_string_t.
  + *
  + *       bson_string_t is a power-of-2 allocation growing string. Every
  + *       time data is appended the next power of two size is chosen for
  + *       the allocation. Pretty standard stuff.
  + *
  + *       It is UTF-8 aware through the use of bson_string_append_unichar().
  + *       The proper UTF-8 character sequence will be used.
  + *
  + * Parameters:
  + *       @str: a string to copy or NULL.
  + *
  + * Returns:
  + *       A newly allocated bson_string_t that should be freed with
  + *       bson_string_free().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_string_t *
  +bson_string_new (const char *str) /* IN */
  +{
  +   bson_string_t *ret;
  +
  +   ret = bson_malloc0 (sizeof *ret);
  +   ret->len = str ? (int)strlen (str) : 0;
  +   ret->alloc = ret->len + 1;
  +
  +   if (!bson_is_power_of_two (ret->alloc)) {
  +      ret->alloc = bson_next_power_of_two (ret->alloc);
  +   }
  +
  +   BSON_ASSERT (ret->alloc >= 1);
  +
  +   ret->str = bson_malloc (ret->alloc);
  +
  +   if (str) {
  +      memcpy (ret->str, str, ret->len);
  +   }
  +   ret->str [ret->len] = '\0';
  +
  +   ret->str [ret->len] = '\0';
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_string_free --
  + *
  + *       Free the bson_string_t @string and related allocations.
  + *
  + *       If @free_segment is false, then the strings buffer will be
  + *       returned and is not freed. Otherwise, NULL is returned.
  + *
  + * Returns:
  + *       The string->str if free_segment is false.
  + *       Otherwise NULL.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +bson_string_free (bson_string_t *string,       /* IN */
  +                  bool           free_segment) /* IN */
  +{
  +   char *ret = NULL;
  +
  +   bson_return_val_if_fail (string, NULL);
  +
  +   if (!free_segment) {
  +      ret = string->str;
  +   } else {
  +      bson_free (string->str);
  +   }
  +
  +   bson_free (string);
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_string_append --
  + *
  + *       Append the UTF-8 string @str to @string.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_string_append (bson_string_t *string, /* IN */
  +                    const char    *str)    /* IN */
  +{
  +   uint32_t len;
  +
  +   bson_return_if_fail (string);
  +   bson_return_if_fail (str);
  +
  +   len = (uint32_t)strlen (str);
  +
  +   if ((string->alloc - string->len - 1) < len) {
  +      string->alloc += len;
  +      if (!bson_is_power_of_two (string->alloc)) {
  +         string->alloc = bson_next_power_of_two (string->alloc);
  +      }
  +      string->str = bson_realloc (string->str, string->alloc);
  +   }
  +
  +   memcpy (string->str + string->len, str, len);
  +   string->len += len;
  +   string->str [string->len] = '\0';
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_string_append_c --
  + *
  + *       Append the ASCII character @c to @string.
  + *
  + *       Do not use this if you are working with UTF-8 sequences,
  + *       use bson_string_append_unichar().
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_string_append_c (bson_string_t *string, /* IN */
  +                      char           c)      /* IN */
  +{
  +   char cc[2];
  +
  +   BSON_ASSERT (string);
  +
  +   if (BSON_UNLIKELY (string->alloc == (string->len + 1))) {
  +      cc [0] = c;
  +      cc [1] = '\0';
  +      bson_string_append (string, cc);
  +      return;
  +   }
  +
  +   string->str [string->len++] = c;
  +   string->str [string->len] = '\0';
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_string_append_unichar --
  + *
  + *       Append the bson_unichar_t @unichar to the string @string.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_string_append_unichar (bson_string_t  *string,  /* IN */
  +                            bson_unichar_t  unichar) /* IN */
  +{
  +   uint32_t len;
  +   char str [8];
  +
  +   BSON_ASSERT (string);
  +   BSON_ASSERT (unichar);
  +
  +   bson_utf8_from_unichar (unichar, str, &len);
  +
  +   if (len <= 6) {
  +      str [len] = '\0';
  +      bson_string_append (string, str);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_string_append_printf --
  + *
  + *       Format a string according to @format and append it to @string.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_string_append_printf (bson_string_t *string,
  +                           const char    *format,
  +                           ...)
  +{
  +   va_list args;
  +   char *ret;
  +
  +   BSON_ASSERT (string);
  +   BSON_ASSERT (format);
  +
  +   va_start (args, format);
  +   ret = bson_strdupv_printf (format, args);
  +   va_end (args);
  +   bson_string_append (string, ret);
  +   bson_free (ret);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_string_truncate --
  + *
  + *       Truncate the string @string to @len bytes.
  + *
  + *       The underlying memory will be released via realloc() down to
  + *       the minimum required size specified by @len.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_string_truncate (bson_string_t *string, /* IN */
  +                      uint32_t       len)    /* IN */
  +{
  +   uint32_t alloc;
  +
  +   bson_return_if_fail (string);
  +   bson_return_if_fail (len < INT_MAX);
  +
  +   alloc = len + 1;
  +
  +   if (alloc < 16) {
  +      alloc = 16;
  +   }
  +
  +   if (!bson_is_power_of_two (alloc)) {
  +      alloc = bson_next_power_of_two (alloc);
  +   }
  +
  +   string->str = bson_realloc (string->str, alloc);
  +   string->alloc = alloc;
  +   string->len = len;
  +
  +   string->str [string->len] = '\0';
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_strdup --
  + *
  + *       Portable strdup().
  + *
  + * Returns:
  + *       A newly allocated string that should be freed with bson_free().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +bson_strdup (const char *str) /* IN */
  +{
  +   long len;
  +   char *out;
  +
  +   if (!str) {
  +      return NULL;
  +   }
  +
  +   len = (long)strlen (str);
  +   out = bson_malloc (len + 1);
  +
  +   if (!out) {
  +      return NULL;
  +   }
  +
  +   memcpy (out, str, len + 1);
  +
  +   return out;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_strdupv_printf --
  + *
  + *       Like bson_strdup_printf() but takes a va_list.
  + *
  + * Returns:
  + *       A newly allocated string that should be freed with bson_free().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +bson_strdupv_printf (const char *format, /* IN */
  +                     va_list     args)   /* IN */
  +{
  +   va_list my_args;
  +   char *buf;
  +   int len = 32;
  +   int n;
  +
  +   bson_return_val_if_fail (format, NULL);
  +
  +   buf = bson_malloc0 (len);
  +
  +   while (true) {
  +      va_copy (my_args, args);
  +      n = bson_vsnprintf (buf, len, format, my_args);
  +      va_end (my_args);
  +
  +      if (n > -1 && n < len) {
  +         return buf;
  +      }
  +
  +      if (n > -1) {
  +         len = n + 1;
  +      } else {
  +         len *= 2;
  +      }
  +
  +      buf = bson_realloc (buf, len);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_strdup_printf --
  + *
  + *       Convenience function that formats a string according to @format
  + *       and returns a copy of it.
  + *
  + * Returns:
  + *       A newly created string that should be freed with bson_free().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +bson_strdup_printf (const char *format, /* IN */
  +                    ...)                /* IN */
  +{
  +   va_list args;
  +   char *ret;
  +
  +   bson_return_val_if_fail (format, NULL);
  +
  +   va_start (args, format);
  +   ret = bson_strdupv_printf (format, args);
  +   va_end (args);
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_strndup --
  + *
  + *       A portable strndup().
  + *
  + * Returns:
  + *       A newly allocated string that should be freed with bson_free().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +bson_strndup (const char *str,     /* IN */
  +              size_t      n_bytes) /* IN */
  +{
  +   char *ret;
  +
  +   bson_return_val_if_fail (str, NULL);
  +
  +   ret = bson_malloc (n_bytes + 1);
  +   memcpy (ret, str, n_bytes);
  +   ret[n_bytes] = '\0';
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_strfreev --
  + *
  + *       Frees each string in a NULL terminated array of strings.
  + *       This also frees the underlying array.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_strfreev (char **str) /* IN */
  +{
  +   int i;
  +
  +   if (str) {
  +      for (i = 0; str [i]; i++)
  +         bson_free (str [i]);
  +      bson_free (str);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_strnlen --
  + *
  + *       A portable strnlen().
  + *
  + * Returns:
  + *       The length of @s up to @maxlen.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +size_t
  +bson_strnlen (const char *s,      /* IN */
  +              size_t      maxlen) /* IN */
  +{
  +#ifdef HAVE_STRNLEN
  +   return strnlen (s, maxlen);
  +#else
  +   size_t i;
  +
  +   for (i = 0; i < maxlen; i++) {
  +      if (s [i] == '\0') {
  +         return i + 1;
  +      }
  +   }
  +
  +   return maxlen;
  +#endif
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_strncpy --
  + *
  + *       A portable strncpy.
  + *
  + *       Copies @src into @dst, which must be @size bytes or larger.
  + *       The result is guaranteed to be \0 terminated.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_strncpy (char       *dst,  /* IN */
  +              const char *src,  /* IN */
  +              size_t      size) /* IN */
  +{
  +#ifdef _MSC_VER
  +   strcpy_s (dst, size, src);
  +#else
  +   strncpy (dst, src, size);
  +   dst[size - 1] = '\0';
  +#endif
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_vsnprintf --
  + *
  + *       A portable vsnprintf.
  + *
  + *       If more than @size bytes are required (exluding the null byte),
  + *       then @size bytes will be written to @string and the return value
  + *       is the number of bytes required.
  + *
  + *       This function will always return a NULL terminated string.
  + *
  + * Returns:
  + *       The number of bytes required for @format excluding the null byte.
  + *
  + * Side effects:
  + *       @str is initialized with the formatted string.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int
  +bson_vsnprintf (char       *str,    /* IN */
  +                size_t      size,   /* IN */
  +                const char *format, /* IN */
  +                va_list     ap)     /* IN */
  +{
  +#ifdef BSON_OS_WIN32
  +   int r = -1;
  +
  +   BSON_ASSERT (str);
  +
  +   if (size != 0) {
  +       r = _vsnprintf_s (str, size, _TRUNCATE, format, ap);
  +   }
  +
  +   if (r == -1) {
  +      r = _vscprintf (format, ap);
  +   }
  +
  +   str [size - 1] = '\0';
  +
  +   return r;
  +#else
  +   int r;
  +
  +   r = vsnprintf (str, size, format, ap);
  +   str [size - 1] = '\0';
  +   return r;
  +#endif
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_snprintf --
  + *
  + *       A portable snprintf.
  + *
  + *       If @format requires more than @size bytes, then @size bytes are
  + *       written and the result is the number of bytes required (excluding
  + *       the null byte).
  + *
  + *       This function will always return a NULL terminated string.
  + *
  + * Returns:
  + *       The number of bytes required for @format.
  + *
  + * Side effects:
  + *       @str is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int
  +bson_snprintf (char       *str,    /* IN */
  +               size_t      size,   /* IN */
  +               const char *format, /* IN */
  +               ...)
  +{
  +   int r;
  +   va_list ap;
  +
  +   BSON_ASSERT (str);
  +
  +   va_start (ap, format);
  +   r = bson_vsnprintf (str, size, format, ap);
  +   va_end (ap);
  +
  +   return r;
  +}
  +/*==============================================================*/
  +/* --- bson-utf8.c */
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_utf8_get_sequence --
  + *
  + *       Determine the sequence length of the first UTF-8 character in
  + *       @utf8. The sequence length is stored in @seq_length and the mask
  + *       for the first character is stored in @first_mask.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @seq_length is set.
  + *       @first_mask is set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE void
  +_bson_utf8_get_sequence (const char *utf8,       /* IN */
  +                         uint8_t    *seq_length, /* OUT */
  +                         uint8_t    *first_mask) /* OUT */
  +{
  +   unsigned char c = *(const unsigned char *)utf8;
  +   uint8_t m;
  +   uint8_t n;
  +
  +   /*
  +    * See the following[1] for a description of what the given multi-byte
  +    * sequences will be based on the bits set of the first byte. We also need
  +    * to mask the first byte based on that.  All subsequent bytes are masked
  +    * against 0x3F.
  +    *
  +    * [1] http://www.joelonsoftware.com/articles/Unicode.html
  +    */
  +
  +   if ((c & 0x80) == 0) {
  +      n = 1;
  +      m = 0x7F;
  +   } else if ((c & 0xE0) == 0xC0) {
  +      n = 2;
  +      m = 0x1F;
  +   } else if ((c & 0xF0) == 0xE0) {
  +      n = 3;
  +      m = 0x0F;
  +   } else if ((c & 0xF8) == 0xF0) {
  +      n = 4;
  +      m = 0x07;
  +   } else if ((c & 0xFC) == 0xF8) {
  +      n = 5;
  +      m = 0x03;
  +   } else if ((c & 0xFE) == 0xFC) {
  +      n = 6;
  +      m = 0x01;
  +   } else {
  +      n = 0;
  +      m = 0;
  +   }
  +
  +   *seq_length = n;
  +   *first_mask = m;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_utf8_validate --
  + *
  + *       Validates that @utf8 is a valid UTF-8 string.
  + *
  + *       If @allow_null is true, then \0 is allowed within @utf8_len bytes
  + *       of @utf8.  Generally, this is bad practice since the main point of
  + *       UTF-8 strings is that they can be used with strlen() and friends.
  + *       However, some languages such as Python can send UTF-8 encoded
  + *       strings with NUL's in them.
  + *
  + * Parameters:
  + *       @utf8: A UTF-8 encoded string.
  + *       @utf8_len: The length of @utf8 in bytes.
  + *       @allow_null: If \0 is allowed within @utf8, exclusing trailing \0.
  + *
  + * Returns:
  + *       true if @utf8 is valid UTF-8. otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_utf8_validate (const char *utf8,       /* IN */
  +                    size_t      utf8_len,   /* IN */
  +                    bool        allow_null) /* IN */
  +{
  +   uint8_t first_mask;
  +   uint8_t seq_length;
  +   unsigned i;
  +   unsigned j;
  +
  +   bson_return_val_if_fail (utf8, false);
  +
  +   for (i = 0; i < utf8_len; i += seq_length) {
  +      _bson_utf8_get_sequence (&utf8[i], &seq_length, &first_mask);
  +
  +      if (!seq_length) {
  +         return false;
  +      }
  +
  +      for (j = i + 1; j < (i + seq_length); j++) {
  +         if ((utf8[j] & 0xC0) != 0x80) {
  +            return false;
  +         }
  +      }
  +
  +      if (!allow_null) {
  +         for (j = 0; j < seq_length; j++) {
  +            if (((i + j) > utf8_len) || !utf8[i + j]) {
  +               return false;
  +            }
  +         }
  +      }
  +   }
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_utf8_escape_for_json --
  + *
  + *       Allocates a new string matching @utf8 except that special
  + *       characters in JSON will be escaped. The resulting string is also
  + *       UTF-8 encoded.
  + *
  + *       Both " and \ characters will be escaped. Additionally, if a NUL
  + *       byte is found before @utf8_len bytes, it will be converted to the
  + *       two byte UTF-8 sequence.
  + *
  + * Parameters:
  + *       @utf8: A UTF-8 encoded string.
  + *       @utf8_len: The length of @utf8 in bytes or -1 if NUL terminated.
  + *
  + * Returns:
  + *       A newly allocated string that should be freed with bson_free().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +bson_utf8_escape_for_json (const char *utf8,     /* IN */
  +                           ssize_t     utf8_len) /* IN */
  +{
  +   bson_unichar_t c;
  +   bson_string_t *str;
  +   const char *end;
  +
  +   bson_return_val_if_fail (utf8, NULL);
  +
  +   str = bson_string_new (NULL);
  +
  +   if (utf8_len < 0) {
  +      utf8_len = strlen (utf8);
  +   }
  +
  +   end = utf8 + utf8_len;
  +
  +   for (; utf8 < end; utf8 = bson_utf8_next_char (utf8)) {
  +      c = bson_utf8_get_char (utf8);
  +
  +      switch (c) {
  +      case '\\':
  +      case '"':
  +      case '/':
  +         bson_string_append_c (str, '\\');
  +         bson_string_append_unichar (str, c);
  +         break;
  +      case '\b':
  +         bson_string_append (str, "\\b");
  +         break;
  +      case '\f':
  +         bson_string_append (str, "\\f");
  +         break;
  +      case '\n':
  +         bson_string_append (str, "\\n");
  +         break;
  +      case '\r':
  +         bson_string_append (str, "\\r");
  +         break;
  +      case '\t':
  +         bson_string_append (str, "\\t");
  +         break;
  +      default:
  +         if (c < ' ') {
  +            bson_string_append_printf (str, "\\u%04u", (unsigned)c);
  +         } else {
  +            bson_string_append_unichar (str, c);
  +         }
  +         break;
  +      }
  +   }
  +
  +   return bson_string_free (str, false);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_utf8_get_char --
  + *
  + *       Fetches the next UTF-8 character from the UTF-8 sequence.
  + *
  + * Parameters:
  + *       @utf8: A string containing validated UTF-8.
  + *
  + * Returns:
  + *       A 32-bit bson_unichar_t reprsenting the multi-byte sequence.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_unichar_t
  +bson_utf8_get_char (const char *utf8) /* IN */
  +{
  +   bson_unichar_t c;
  +   uint8_t mask;
  +   uint8_t num;
  +   int i;
  +
  +   bson_return_val_if_fail (utf8, -1);
  +
  +   _bson_utf8_get_sequence (utf8, &num, &mask);
  +   c = (*utf8) & mask;
  +
  +   for (i = 1; i < num; i++) {
  +      c = (c << 6) | (utf8[i] & 0x3F);
  +   }
  +
  +   return c;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_utf8_next_char --
  + *
  + *       Returns an incremented pointer to the beginning of the next
  + *       multi-byte sequence in @utf8.
  + *
  + * Parameters:
  + *       @utf8: A string containing validated UTF-8.
  + *
  + * Returns:
  + *       An incremented pointer in @utf8.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const char *
  +bson_utf8_next_char (const char *utf8) /* IN */
  +{
  +   uint8_t mask;
  +   uint8_t num;
  +
  +   bson_return_val_if_fail (utf8, NULL);
  +
  +   _bson_utf8_get_sequence (utf8, &num, &mask);
  +
  +   return utf8 + num;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_utf8_from_unichar --
  + *
  + *       Converts the unichar to a sequence of utf8 bytes and stores those
  + *       in @utf8. The number of bytes in the sequence are stored in @len.
  + *
  + * Parameters:
  + *       @unichar: A bson_unichar_t.
  + *       @utf8: A location for the multi-byte sequence.
  + *       @len: A location for number of bytes stored in @utf8.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @utf8 is set.
  + *       @len is set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_utf8_from_unichar (
  +      bson_unichar_t  unichar,                               /* IN */
  +      char            utf8[BSON_ENSURE_ARRAY_PARAM_SIZE(6)], /* OUT */
  +      uint32_t       *len)                                   /* OUT */
  +{
  +   bson_return_if_fail (utf8);
  +   bson_return_if_fail (len);
  +
  +   if (unichar <= 0x7F) {
  +      utf8[0] = unichar;
  +      *len = 1;
  +   } else if (unichar <= 0x7FF) {
  +      *len = 2;
  +      utf8[0] = 0xC0 | ((unichar >> 6) & 0x3F);
  +      utf8[1] = 0x80 | ((unichar) & 0x3F);
  +   } else if (unichar <= 0xFFFF) {
  +      *len = 3;
  +      utf8[0] = 0xE0 | ((unichar >> 12) & 0xF);
  +      utf8[1] = 0x80 | ((unichar >> 6) & 0x3F);
  +      utf8[2] = 0x80 | ((unichar) & 0x3F);
  +   } else if (unichar <= 0x1FFFFF) {
  +      *len = 4;
  +      utf8[0] = 0xF0 | ((unichar >> 18) & 0x7);
  +      utf8[1] = 0x80 | ((unichar >> 12) & 0x3F);
  +      utf8[2] = 0x80 | ((unichar >> 6) & 0x3F);
  +      utf8[3] = 0x80 | ((unichar) & 0x3F);
  +   } else if (unichar <= 0x3FFFFFF) {
  +      *len = 5;
  +      utf8[0] = 0xF8 | ((unichar >> 24) & 0x3);
  +      utf8[1] = 0x80 | ((unichar >> 18) & 0x3F);
  +      utf8[2] = 0x80 | ((unichar >> 12) & 0x3F);
  +      utf8[3] = 0x80 | ((unichar >> 6) & 0x3F);
  +      utf8[4] = 0x80 | ((unichar) & 0x3F);
  +   } else if (unichar <= 0x7FFFFFFF) {
  +      *len = 6;
  +      utf8[0] = 0xFC | ((unichar >> 31) & 0x1);
  +      utf8[1] = 0x80 | ((unichar >> 25) & 0x3F);
  +      utf8[2] = 0x80 | ((unichar >> 19) & 0x3F);
  +      utf8[3] = 0x80 | ((unichar >> 13) & 0x3F);
  +      utf8[4] = 0x80 | ((unichar >> 7) & 0x3F);
  +      utf8[5] = 0x80 | ((unichar) & 0x1);
  +   } else {
  +      *len = 0;
  +   }
  +}
  +/*==============================================================*/
  +/* --- bson-writer.c */
  +
  +struct _bson_writer_t
  +{
  +   bool                ready;
  +   uint8_t           **buf;
  +   size_t             *buflen;
  +   size_t              offset;
  +   bson_realloc_func   realloc_func;
  +   bson_t              b;
  +};
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_writer_new --
  + *
  + *       Creates a new instance of bson_writer_t using the buffer, length,
  + *       offset, and realloc() function supplied.
  + *
  + *       The caller is expected to clean up the structure when finished
  + *       using bson_writer_destroy().
  + *
  + * Parameters:
  + *       @buf: (inout): A pointer to a target buffer.
  + *       @buflen: (inout): A pointer to the buffer length.
  + *       @offset: The offset in the target buffer to start from.
  + *       @realloc_func: A realloc() style function or NULL.
  + *
  + * Returns:
  + *       A newly allocated bson_writer_t that should be freed with
  + *       bson_writer_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bson_writer_t *
  +bson_writer_new (uint8_t           **buf,          /* IN */
  +                 size_t             *buflen,       /* IN */
  +                 size_t              offset,       /* IN */
  +                 bson_realloc_func   realloc_func) /* IN */
  +{
  +   bson_writer_t *writer;
  +
  +   writer = bson_malloc0 (sizeof *writer);
  +   writer->buf = buf;
  +   writer->buflen = buflen;
  +   writer->offset = offset;
  +   writer->realloc_func = realloc_func;
  +   writer->ready = true;
  +
  +   return writer;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_writer_destroy --
  + *
  + *       Cleanup after @writer and release any allocated memory. Note that
  + *       the buffer supplied to bson_writer_new() is NOT freed from this
  + *       method.  The caller is responsible for that.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_writer_destroy (bson_writer_t *writer) /* IN */
  +{
  +   bson_free (writer);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_writer_get_length --
  + *
  + *       Fetches the current length of the content written by the buffer
  + *       (including the initial offset). This includes a partly written
  + *       document currently being written.
  + *
  + *       This is useful if you want to check to see if you've passed a given
  + *       memory boundry that cannot be sent in a packet. See
  + *       bson_writer_rollback() to abort the current document being written.
  + *
  + * Returns:
  + *       The number of bytes written plus initial offset.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +size_t
  +bson_writer_get_length (bson_writer_t *writer) /* IN */
  +{
  +   return writer->offset + writer->b.len;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_writer_begin --
  + *
  + *       Begins writing a new document. The caller may use the bson
  + *       structure to write out a new BSON document. When completed, the
  + *       caller must call either bson_writer_end() or
  + *       bson_writer_rollback().
  + *
  + * Parameters:
  + *       @writer: A bson_writer_t.
  + *       @bson: (out): A location for a bson_t*.
  + *
  + * Returns:
  + *       true if the underlying realloc was successful; otherwise false.
  + *
  + * Side effects:
  + *       @bson is initialized if true is returned.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +bson_writer_begin (bson_writer_t  *writer, /* IN */
  +                   bson_t        **bson)   /* OUT */
  +{
  +   bson_impl_alloc_t *b;
  +   bool grown = false;
  +
  +   bson_return_val_if_fail (writer, false);
  +   bson_return_val_if_fail (writer->ready, false);
  +   bson_return_val_if_fail (bson, false);
  +
  +   writer->ready = false;
  +
  +   memset (&writer->b, 0, sizeof (bson_t));
  +
  +   b = (bson_impl_alloc_t *)&writer->b;
  +   b->flags = BSON_FLAG_STATIC | BSON_FLAG_NO_FREE;
  +   b->len = 5;
  +   b->parent = NULL;
  +   b->buf = writer->buf;
  +   b->buflen = writer->buflen;
  +   b->offset = writer->offset;
  +   b->alloc = NULL;
  +   b->alloclen = 0;
  +   b->realloc = writer->realloc_func;
  +
  +   while ((writer->offset + writer->b.len) > *writer->buflen) {
  +      if (!writer->realloc_func) {
  +         memset (&writer->b, 0, sizeof (bson_t));
  +         writer->ready = true;
  +         return false;
  +      }
  +      grown = true;
  +
  +      if (!*writer->buflen) {
  +         *writer->buflen = 64;
  +      } else {
  +         (*writer->buflen) *= 2;
  +      }
  +   }
  +
  +   if (grown) {
  +      *writer->buf = writer->realloc_func (*writer->buf, *writer->buflen);
  +   }
  +
  +   memset ((*writer->buf) + writer->offset + 1, 0, 5);
  +   (*writer->buf)[writer->offset] = 5;
  +
  +   *bson = &writer->b;
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_writer_end --
  + *
  + *       Complete writing of a bson_writer_t to the buffer supplied.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_writer_end (bson_writer_t *writer) /* IN */
  +{
  +   bson_return_if_fail (writer);
  +   bson_return_if_fail (!writer->ready);
  +
  +   writer->offset += writer->b.len;
  +   memset (&writer->b, 0, sizeof (bson_t));
  +   writer->ready = true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_writer_rollback --
  + *
  + *       Abort the appending of the current bson_t to the memory region
  + *       managed by @writer.  This is useful if you detected that you went
  + *       past a particular memory limit.  For example, MongoDB has 48MB
  + *       message limits.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +bson_writer_rollback (bson_writer_t *writer) /* IN */
  +{
  +   bson_return_if_fail (writer);
  +
  +   if (writer->b.len) {
  +      memset (&writer->b, 0, sizeof (bson_t));
  +   }
   
  -    out[0] = in[3];
  -    out[1] = in[2];
  -    out[2] = in[1];
  -    out[3] = in[0];
  +   writer->ready = true;
   }
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/bson.h
  ============================================================================
  $ cvs diff -u -r2.3.4.8 -r2.3.4.9 bson.h
  --- rpm/rpmio/bson.h	27 Sep 2014 15:51:21 -0000	2.3.4.8
  +++ rpm/rpmio/bson.h	30 Sep 2014 22:31:43 -0000	2.3.4.9
  @@ -3,1177 +3,3770 @@
    * @brief BSON Declarations
    */
   
  -/*    Copyright 2009-2012 10gen Inc.
  +/*
  + * Copyright 2013 MongoDB, Inc.
    *
  - *    Licensed under the Apache License, Version 2.0 (the "License");
  - *    you may not use this file except in compliance with the License.
  - *    You may obtain a copy of the License at
  - *
  - *    http://www.apache.org/licenses/LICENSE-2.0
  - *
  - *    Unless required by applicable law or agreed to in writing, software
  - *    distributed under the License is distributed on an "AS IS" BASIS,
  - *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  - *    See the License for the specific language governing permissions and
  - *    limitations under the License.
  + * Licensed under the Apache License, Version 2.0 (the "License");
  + * you may not use this file except in compliance with the License.
  + * You may obtain a copy of the License at
  + *
  + *   http://www.apache.org/licenses/LICENSE-2.0
  + *
  + * Unless required by applicable law or agreed to in writing, software
  + * distributed under the License is distributed on an "AS IS" BASIS,
  + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  + * See the License for the specific language governing permissions and
  + * limitations under the License.
    */
   
   #ifndef H_BSON
   #define H_BSON
   
   #include <stdint.h>
  +# include <inttypes.h>
   #include <stdio.h>
   #include <time.h>
   
   #include <rpmutil.h>
   
  -#ifdef __GNUC__
  -#define MONGO_INLINE static __inline__
  -#define MONGO_EXPORT
  -#elif defined(__sun) && defined(__SUNPRO_C)
  -/* Solaris with Sun/Oracle Studio */
  -#define MONGO_INLINE static inline
  -#define MONGO_EXPORT
  +/*==============================================================*/
  +/* --- bson-config.h */
  +
  +/*
  + * Define to 1234 for Little Endian, 4321 for Big Endian.
  + */
  +#define BSON_BYTE_ORDER 1234
  +
  +
  +/*
  + * Define to 1 if you have stdbool.h
  + */
  +#define BSON_HAVE_STDBOOL_H 1
  +#if BSON_HAVE_STDBOOL_H != 1
  +# undef BSON_HAVE_STDBOOL_H
  +#endif
  +
  +
  +/*
  + * Define to 1 for POSIX-like systems, 2 for Windows.
  + */
  +#define BSON_OS 1
  +
  +
  +/*
  + * Define to 1 if your system requires {} around PTHREAD_ONCE_INIT.
  + * This is typically just Solaris 8-10.
  + */
  +#define BSON_PTHREAD_ONCE_INIT_NEEDS_BRACES 0
  +#if BSON_PTHREAD_ONCE_INIT_NEEDS_BRACES != 1
  +# undef BSON_PTHREAD_ONCE_INIT_NEEDS_BRACES
  +#endif
  +
  +
  +/*
  + * Define to 1 if you have clock_gettime() available.
  + */
  +#define BSON_HAVE_CLOCK_GETTIME 1
  +#if BSON_HAVE_CLOCK_GETTIME != 1
  +# undef BSON_HAVE_CLOCK_GETTIME
  +#endif
  +
  +
  +/*
  + * Define to 1 if you have strnlen available on your platform.
  + */
  +#define BSON_HAVE_STRNLEN 1
  +#if BSON_HAVE_STRNLEN != 1
  +# undef BSON_HAVE_STRNLEN
  +#endif
  +
  +
  +/*
  + * Define to 1 if you have strnlen available on your platform.
  + */
  +#define BSON_HAVE_SNPRINTF 1
  +#if BSON_HAVE_SNPRINTF != 1
  +# undef BSON_HAVE_SNPRINTF
  +#endif
  +
  +
  +/*
  + * Define to 1 if 32-bit atomics are not available and pthreads should be
  + * used to emulate them.
  + */
  +#define BSON_WITH_OID32_PT 0
  +#if BSON_WITH_OID32_PT != 1
  +# undef BSON_WITH_OID32_PT
  +#endif
  +
  +
  +/*
  + * Define to 1 if 64-bit atomics are not available and pthreads should be
  + * used to emulate them.
  + */
  +#define BSON_WITH_OID64_PT 0
  +#if BSON_WITH_OID64_PT != 1
  +# undef BSON_WITH_OID64_PT
  +#endif
  +
  +/*==============================================================*/
  +/* --- bson-macros.h */
  +
  +#ifdef __cplusplus
  +#  include <algorithm>
  +#endif
  +
  +#if BSON_OS == 1
  +# define BSON_OS_UNIX
  +#elif BSON_OS == 2
  +# define BSON_OS_WIN32
   #else
  -#define MONGO_INLINE static
  -#ifdef MONGO_STATIC_BUILD
  -#define MONGO_EXPORT
  -#elif defined(MONGO_DLL_BUILD)
  -#define MONGO_EXPORT __declspec(dllexport)
  +# error "Unknown operating system."
  +#endif
  +
  +
  +#ifdef __cplusplus
  +#  define BSON_BEGIN_DECLS extern "C" {
  +#  define BSON_END_DECLS   }
   #else
  -#define MONGO_EXPORT __declspec(dllimport)
  +#  define BSON_BEGIN_DECLS
  +#  define BSON_END_DECLS
   #endif
  +
  +#ifdef _MSC_VER
  +#  ifdef BSON_COMPILATION
  +#    define BSON_API __declspec(dllexport)
  +#  else
  +#    define BSON_API __declspec(dllimport)
  +#  endif
  +#else
  +#  define BSON_API
   #endif
   
  -#ifdef __cplusplus
  -#define MONGO_EXTERN_C_START extern "C" {
  -#define MONGO_EXTERN_C_END }
  +
  +#ifndef MIN
  +#  ifdef __cplusplus
  +#    define MIN(a, b) ( (std::min)(a, b) )
  +#  elif defined(_MSC_VER)
  +#    define MIN(a, b) ((a) < (b) ? (a) : (b))
  +#  else
  +#    define MIN(a, b) ({     \
  +                          __typeof__ (a)_a = (a); \
  +                          __typeof__ (b)_b = (b); \
  +                          _a < _b ? _a : _b;   \
  +                       })
  +#  endif
  +#endif
  +
  +
  +#ifndef MAX
  +#  ifdef __cplusplus
  +#    define MAX(a, b) ( (std::max)(a, b) )
  +#  elif defined(_MSC_VER)
  +#    define MAX(a, b) ((a) > (b) ? (a) : (b))
  +#  else
  +#    define MAX(a, b) ({     \
  +                          __typeof__ (a)_a = (a); \
  +                          __typeof__ (b)_b = (b); \
  +                          _a > _b ? _a : _b;   \
  +                       })
  +#  endif
  +#endif
  +
  +
  +#ifndef ABS
  +#  define ABS(a) (((a) < 0) ? ((a) * -1) : (a))
  +#endif
  +
  +
  +#if defined(_MSC_VER)
  +#  define BSON_ALIGNED_BEGIN(_N) __declspec (align (_N))
  +#  define BSON_ALIGNED_END(_N)
   #else
  -#define MONGO_EXTERN_C_START
  -#define MONGO_EXTERN_C_END
  +#  define BSON_ALIGNED_BEGIN(_N)
  +#  define BSON_ALIGNED_END(_N) __attribute__((aligned (_N)))
   #endif
   
  -MONGO_EXTERN_C_START
   
  -#define BSON_OK 0
  -#define BSON_ERROR -1
  +#define bson_str_empty(s)  (!s[0])
  +#define bson_str_empty0(s) (!s || !s[0])
   
  -enum bson_error_t {
  -    BSON_SIZE_OVERFLOW =     (1 << 0),  /**< Trying to create a BSON object larger \
                than INT_MAX. */
  -    BSON_ALREADY_FINISHED =  (1 << 4),  /**< Trying to modify a finished BSON \
                object. */
  -    BSON_NOT_IN_SUBOBJECT =  (1 << 5),  /**< Trying bson_append_finish_object() \
                and not in sub */
  -    BSON_DOES_NOT_OWN_DATA = (1 << 6)   /**< Trying to expand a BSON object which \
                does not own its data block. */
  -};
   
  -enum bson_validity_t {
  -    BSON_VALID =             0,         /**< BSON is valid and UTF-8 compliant. */
  -    BSON_NOT_UTF8 =          (1 << 1),  /**< A key or a string is not valid UTF-8. \
                */
  -    BSON_FIELD_HAS_DOT =     (1 << 2),  /**< Warning: key contains '.' character. \
                */
  -    BSON_FIELD_INIT_DOLLAR = (1 << 3)   /**< Warning: key starts with '$' \
                character. */
  -};
  +#ifndef BSON_DISABLE_ASSERT
  +#  define BSON_ASSERT(s) assert ((s))
  +#else
  +#  define BSON_ASSERT(s)
  +#endif
   
  -enum bson_binary_subtype_t {
  -    BSON_BIN_BINARY = 0,
  -    BSON_BIN_FUNC = 1,
  -    BSON_BIN_BINARY_OLD = 2,
  -    BSON_BIN_UUID = 3,
  -    BSON_BIN_MD5 = 5,
  -    BSON_BIN_USER = 128
  -};
   
  -typedef enum {
  -    BSON_EOO = 0,
  -    BSON_DOUBLE = 1,
  -    BSON_STRING = 2,
  -    BSON_OBJECT = 3,
  -    BSON_ARRAY = 4,
  -    BSON_BINDATA = 5,
  -    BSON_UNDEFINED = 6,
  -    BSON_OID = 7,
  -    BSON_BOOL = 8,
  -    BSON_DATE = 9,
  -    BSON_NULL = 10,
  -    BSON_REGEX = 11,
  -    BSON_DBREF = 12, /**< Deprecated. */
  -    BSON_CODE = 13,
  -    BSON_SYMBOL = 14,
  -    BSON_CODEWSCOPE = 15,
  -    BSON_INT = 16,
  -    BSON_TIMESTAMP = 17,
  -    BSON_LONG = 18,
  -    BSON_MAXKEY = 127,
  -    BSON_MINKEY = 255
  -} bson_type;
  -
  -typedef int bson_bool_t;
  -
  -typedef struct {
  -    const char *cur;
  -    bson_bool_t first;
  -} bson_iterator;
  -
  -typedef struct {
  -    char *data;           /**< Pointer to a block of data in this BSON object. */
  -    char *cur;            /**< Pointer to the current position. */
  -    int dataSize;         /**< The number of bytes allocated to char *data. */
  -    bson_bool_t finished; /**< When finished, the BSON object can no longer be \
                modified. */
  -    bson_bool_t ownsData; /**< Whether destroying this object will deallocate its \
                data block */
  -    int err;              /**< Bitfield representing errors or warnings on this \
                buffer */
  -    int stackSize;        /**< Number of elements in the current stack */
  -    int stackPos;         /**< Index of current stack position. */
  -    size_t* stackPtr;     /**< Pointer to the current stack */
  -    size_t stack[32];     /**< A stack used to keep track of nested BSON elements.
  -                               Must be at end of bson struct so _bson_zero does \
                not clear. */
  -} bson;
  -
  -#pragma pack(1)
  -typedef union {
  -    char bytes[12];
  -    int ints[3];
  +#define BSON_STATIC_ASSERT(s) BSON_STATIC_ASSERT_ (s, __LINE__)
  +#define BSON_STATIC_ASSERT_JOIN(a, b) BSON_STATIC_ASSERT_JOIN2 (a, b)
  +#define BSON_STATIC_ASSERT_JOIN2(a, b) a##b
  +#define BSON_STATIC_ASSERT_(s, l) \
  +   typedef char BSON_STATIC_ASSERT_JOIN (static_assert_test_, \
  +                                         __LINE__)[(s) ? 1 : -1]
  +
  +
  +#if defined(__GNUC__)
  +#  define BSON_GNUC_CONST __attribute__((const))
  +#  define BSON_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
  +#else
  +#  define BSON_GNUC_CONST
  +#  define BSON_GNUC_WARN_UNUSED_RESULT
  +#endif
  +
  +
  +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(_WIN32)
  +#  define BSON_GNUC_NULL_TERMINATED __attribute__((sentinel))
  +#  define BSON_GNUC_INTERNAL __attribute__((visibility ("hidden")))
  +#else
  +#  define BSON_GNUC_NULL_TERMINATED
  +#  define BSON_GNUC_INTERNAL
  +#endif
  +
  +
  +#if defined(__GNUC__)
  +#  define BSON_LIKELY(x)    __builtin_expect (!!(x), 1)
  +#  define BSON_UNLIKELY(x)  __builtin_expect (!!(x), 0)
  +#else
  +#  define BSON_LIKELY(v)   v
  +#  define BSON_UNLIKELY(v) v
  +#endif
  +
  +
  +#if defined(__clang__)
  +# define BSON_GNUC_PRINTF(f, v) __attribute__((format (printf, f, v)))
  +#elif defined(__GNUC__)
  +#  define GCC_VERSION (__GNUC__ * 10000 \
  +                       + __GNUC_MINOR__ * 100 \
  +                       + __GNUC_PATCHLEVEL__)
  +#  if GCC_VERSION > 40400
  +#    define BSON_GNUC_PRINTF(f, v) __attribute__((format (gnu_printf, f, v)))
  +#  else
  +#    define BSON_GNUC_PRINTF(f, v)
  +#  endif /* GCC_VERSION > 40400 */
  +#else
  +#  define BSON_GNUC_PRINTF(f, v)
  +#endif /* __GNUC__ */
  +
  +
  +#if defined(__LP64__) || defined(_LP64)
  +#  define BSON_WORD_SIZE 64
  +#else
  +#  define BSON_WORD_SIZE 32
  +#endif
  +
  +
  +#if defined(_MSC_VER)
  +#  define BSON_INLINE __inline
  +#else
  +#  define BSON_INLINE __inline__
  +#endif
  +
  +
  +#ifndef BSON_DISABLE_CHECKS
  +#  define bson_return_if_fail(test) \
  +   do { \
  +      if (!(test)) { \
  +         fprintf (stderr, "%s(): precondition failed: %s\n", \
  +                  __FUNCTION__, #test); \
  +         return; \
  +      } \
  +   } while (0)
  +#else
  +#  define bson_return_if_fail(test)
  +#endif
  +
  +
  +#ifndef BSON_DISABLE_CHECKS
  +#  define bson_return_val_if_fail(test, val) \
  +   do { \
  +      if (!(test)) { \
  +         fprintf (stderr, "%s(): precondition failed: %s\n", \
  +                  __FUNCTION__, #test); \
  +         return (val); \
  +      } \
  +   } while (0)
  +#else
  +#  define bson_return_val_if_fail(test, val)
  +#endif
  +
  +
  +#ifdef _MSC_VER
  +#define BSON_ENSURE_ARRAY_PARAM_SIZE(_n)
  +#define BSON_TYPEOF decltype
  +#else
  +#define BSON_ENSURE_ARRAY_PARAM_SIZE(_n) static (_n)
  +#define BSON_TYPEOF typeof
  +#endif
  +
  +/*==============================================================*/
  +/* --- bson-stdint.h */
  +
  +/* generated using a gnu compiler version gcc (GCC) 4.4.7 20120313 (Red Hat \
4.4.7-4) Copyright (C) 2010 Free Software Foundation, Inc. This is free software; see \
the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY \
or FITNESS FOR A PARTICULAR PURPOSE. */  +
  +#include <stdint.h>
  +
  +
  +/* system headers have good uint64_t */
  +#ifndef _HAVE_UINT64_T
  +#define _HAVE_UINT64_T
  +#endif
  +
  +/*==============================================================*/
  +/* --- bson-compat.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +#ifdef _MSC_VER
  +# include "bson-stdint-win32.h"
  +# ifndef __cplusplus
  +   /* benign redefinition of type */
  +#  pragma warning (disable :4142)
  +    typedef SSIZE_T ssize_t;
  +    typedef SIZE_T size_t;
  +#  pragma warning (default :4142)
  +# endif
  +# define PRIi32 "d"
  +# define PRId32 "d"
  +# define PRIu32 "u"
  +# define PRIi64 "I64i"
  +# define PRId64 "I64i"
  +# define PRIu64 "I64u"
  +#endif
  +
  +
  +#ifdef BSON_HAVE_STDBOOL_H
  +# include <stdbool.h>
  +#elif !defined(__bool_true_false_are_defined)
  +# ifndef __cplusplus
  +   typedef signed char bool;
  +#  define false 0
  +#  define true 1
  +# endif
  +# define __bool_true_false_are_defined 1
  +#endif
  +
  +
  +#if defined(__GNUC__)
  +# if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
  +#  define bson_sync_synchronize() __sync_synchronize()
  +# elif defined(__i386__ ) || defined( __i486__ ) || defined( __i586__ ) || \
  +          defined( __i686__ ) || defined( __x86_64__ )
  +#  define bson_sync_synchronize() asm volatile("mfence":::"memory")
  +# else
  +#  define bson_sync_synchronize() asm volatile("sync":::"memory")
  +# endif
  +#elif defined(_MSC_VER)
  +# define bson_sync_synchronize() MemoryBarrier()
  +#endif
  +
  +
  +#if !defined(va_copy) && defined(_MSC_VER)
  +# define va_copy(dst,src) ((dst) = (src))
  +#endif
  +
  +
  +#if !defined(va_copy) && defined(__GNUC__) && __GNUC__ < 3
  +# define va_copy(dst,src) __va_copy(dst, src)
  +#endif
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-types.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * bson_unichar_t --
  + *
  + *       bson_unichar_t provides an unsigned 32-bit type for containing
  + *       unicode characters. When iterating UTF-8 sequences, this should
  + *       be used to avoid losing the high-bits of non-ascii characters.
  + *
  + * See also:
  + *       bson_string_append_unichar()
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +typedef uint32_t bson_unichar_t;
  +
  +
  +/**
  + * bson_context_flags_t:
  + *
  + * This enumeration is used to configure a bson_context_t.
  + *
  + * %BSON_CONTEXT_NONE: Use default options.
  + * %BSON_CONTEXT_THREAD_SAFE: Context will be called from multiple threads.
  + * %BSON_CONTEXT_DISABLE_PID_CACHE: Call getpid() instead of caching the
  + *   result of getpid() when initializing the context.
  + * %BSON_CONTEXT_DISABLE_HOST_CACHE: Call gethostname() instead of caching the
  + *   result of gethostname() when initializing the context.
  + */
  +typedef enum
  +{
  +   BSON_CONTEXT_NONE = 0,
  +   BSON_CONTEXT_THREAD_SAFE = (1 << 0),
  +   BSON_CONTEXT_DISABLE_HOST_CACHE = (1 << 1),
  +   BSON_CONTEXT_DISABLE_PID_CACHE = (1 << 2),
  +#if defined(__linux__)
  +   BSON_CONTEXT_USE_TASK_ID = (1 << 3),
  +#endif
  +} bson_context_flags_t;
  +
  +
  +/**
  + * bson_context_t:
  + *
  + * This structure manages context for the bson library. It handles
  + * configuration for thread-safety and other performance related requirements.
  + * Consumers will create a context and may use multiple under a variety of
  + * situations.
  + *
  + * If your program calls fork(), you should initialize a new bson_context_t
  + * using bson_context_init().
  + *
  + * If you are using threading, it is suggested that you use a bson_context_t
  + * per thread for best performance. Alternatively, you can initialize the
  + * bson_context_t with BSON_CONTEXT_THREAD_SAFE, although a performance penalty
  + * will be incurred.
  + *
  + * Many functions will require that you provide a bson_context_t such as OID
  + * generation.
  + *
  + * This structure is oqaque in that you cannot see the contents of the
  + * structure. However, it is stack allocatable in that enough padding is
  + * provided in _bson_context_t to hold the structure.
  + */
  +typedef struct _bson_context_t bson_context_t;
  +
  +
  +/**
  + * bson_t:
  + *
  + * This structure manages a buffer whose contents are a properly formatted
  + * BSON document. You may perform various transforms on the BSON documents.
  + * Additionally, it can be iterated over using bson_iter_t.
  + *
  + * See bson_iter_init() for iterating the contents of a bson_t.
  + *
  + * When building a bson_t structure using the various append functions,
  + * memory allocations may occur. That is performed using power of two
  + * allocations and realloc().
  + *
  + * See http://bsonspec.org for the BSON document spec.
  + *
  + * This structure is meant to fit in two sequential 64-byte cachelines.
  + */
  +BSON_ALIGNED_BEGIN (128)
  +typedef struct
  +{
  +   uint32_t flags;        /* Internal flags for the bson_t. */
  +   uint32_t len;          /* Length of BSON data. */
  +   uint8_t padding[120];  /* Padding for stack allocation. */
  +} bson_t
  +BSON_ALIGNED_END (128);
  +
  +
  +/**
  + * BSON_INITIALIZER:
  + *
  + * This macro can be used to initialize a #bson_t structure on the stack
  + * without calling bson_init().
  + *
  + * |[
  + * bson_t b = BSON_INITIALIZER;
  + * ]|
  + */
  +#define BSON_INITIALIZER { 3, 5, { 5 } }
  +
  +
  +BSON_STATIC_ASSERT (sizeof (bson_t) == 128);
  +
  +
  +/**
  + * bson_oid_t:
  + *
  + * This structure contains the binary form of a BSON Object Id as specified
  + * on http://bsonspec.org. If you would like the bson_oid_t in string form
  + * see bson_oid_to_string() or bson_oid_to_string_r().
  + */
  +typedef struct
  +{
  +   uint8_t bytes[12];
   } bson_oid_t;
  -#pragma pack()
   
  -typedef int64_t bson_date_t; /* milliseconds since epoch UTC */
   
  -typedef struct {
  -    int i; /* increment */
  -    int t; /* time in seconds */
  -} bson_timestamp_t;
  +BSON_STATIC_ASSERT (sizeof (bson_oid_t) == 12);
  +
  +
  +/**
  + * bson_validate_flags_t:
  + *
  + * This enumeration is used for validation of BSON documents. It allows
  + * selective control on what you wish to validate.
  + *
  + * %BSON_VALIDATE_NONE: No additional validation occurs.
  + * %BSON_VALIDATE_UTF8: Check that strings are valid UTF-8.
  + * %BSON_VALIDATE_DOLLAR_KEYS: Check that keys do not start with $.
  + * %BSON_VALIDATE_DOT_KEYS: Check that keys do not contain a period.
  + * %BSON_VALIDATE_UTF8_ALLOW_NULL: Allow NUL bytes in UTF-8 text.
  + */
  +typedef enum
  +{
  +   BSON_VALIDATE_NONE = 0,
  +   BSON_VALIDATE_UTF8 = (1 << 0),
  +   BSON_VALIDATE_DOLLAR_KEYS = (1 << 1),
  +   BSON_VALIDATE_DOT_KEYS = (1 << 2),
  +   BSON_VALIDATE_UTF8_ALLOW_NULL = (1 << 3),
  +} bson_validate_flags_t;
  +
  +
  +/**
  + * bson_type_t:
  + *
  + * This enumeration contains all of the possible types within a BSON document.
  + * Use bson_iter_type() to fetch the type of a field while iterating over it.
  + */
  +typedef enum
  +{
  +   BSON_TYPE_EOD = 0x00,
  +   BSON_TYPE_DOUBLE = 0x01,
  +   BSON_TYPE_UTF8 = 0x02,
  +   BSON_TYPE_DOCUMENT = 0x03,
  +   BSON_TYPE_ARRAY = 0x04,
  +   BSON_TYPE_BINARY = 0x05,
  +   BSON_TYPE_UNDEFINED = 0x06,
  +   BSON_TYPE_OID = 0x07,
  +   BSON_TYPE_BOOL = 0x08,
  +   BSON_TYPE_DATE_TIME = 0x09,
  +   BSON_TYPE_NULL = 0x0A,
  +   BSON_TYPE_REGEX = 0x0B,
  +   BSON_TYPE_DBPOINTER = 0x0C,
  +   BSON_TYPE_CODE = 0x0D,
  +   BSON_TYPE_SYMBOL = 0x0E,
  +   BSON_TYPE_CODEWSCOPE = 0x0F,
  +   BSON_TYPE_INT32 = 0x10,
  +   BSON_TYPE_TIMESTAMP = 0x11,
  +   BSON_TYPE_INT64 = 0x12,
  +   BSON_TYPE_MAXKEY = 0x7F,
  +   BSON_TYPE_MINKEY = 0xFF,
  +} bson_type_t;
  +
  +
  +/**
  + * bson_subtype_t:
  + *
  + * This enumeration contains the various subtypes that may be used in a binary
  + * field. See http://bsonspec.org for more information.
  + */
  +typedef enum
  +{
  +   BSON_SUBTYPE_BINARY = 0x00,
  +   BSON_SUBTYPE_FUNCTION = 0x01,
  +   BSON_SUBTYPE_BINARY_DEPRECATED = 0x02,
  +   BSON_SUBTYPE_UUID_DEPRECATED = 0x03,
  +   BSON_SUBTYPE_UUID = 0x04,
  +   BSON_SUBTYPE_MD5 = 0x05,
  +   BSON_SUBTYPE_USER = 0x80,
  +} bson_subtype_t;
  +
  +
  +/**
  + * bson_iter_t:
  + *
  + * This structure manages iteration over a bson_t structure. It keeps track
  + * of the location of the current key and value within the buffer. Using the
  + * various functions to get the value of the iter will read from these
  + * locations.
  + *
  + * This structure is safe to discard on the stack. No cleanup is necessary
  + * after using it.
  + */
  +typedef struct
  +{
  +   const uint8_t *raw;      /* The raw buffer being iterated. */
  +   uint32_t       len;      /* The length of raw. */
  +   uint32_t       off;      /* The offset within the buffer. */
  +   uint32_t       type;     /* The offset of the type byte. */
  +   uint32_t       key;      /* The offset of the key byte. */
  +   uint32_t       d1;       /* The offset of the first data byte. */
  +   uint32_t       d2;       /* The offset of the second data byte. */
  +   uint32_t       d3;       /* The offset of the third data byte. */
  +   uint32_t       d4;       /* The offset of the fourth data byte. */
  +   uint32_t       next_off; /* The offset of the next field. */
  +   uint32_t       err_off;  /* The offset of the error. */
  +   char           padding[16];
  +} bson_iter_t;
  +
  +
  +/**
  + * bson_reader_t:
  + *
  + * This structure is used to iterate over a sequence of BSON documents. It
  + * allows for them to be iterated with the possibility of no additional
  + * memory allocations under certain circumstances such as reading from an
  + * incoming mongo packet.
  + */
  +BSON_ALIGNED_BEGIN (128)
  +typedef struct
  +{
  +   uint32_t type;
  +   /*< private >*/
  +} bson_reader_t
  +BSON_ALIGNED_END (128);
  +
  +
  +/**
  + * bson_visitor_t:
  + *
  + * This structure contains a series of pointers that can be executed for
  + * each field of a BSON document based on the field type.
  + *
  + * For example, if an int32 field is found, visit_int32 will be called.
  + *
  + * When visiting each field using bson_iter_visit_all(), you may provide a
  + * data pointer that will be provided with each callback. This might be useful
  + * if you are marshaling to another language.
  + *
  + * You may pre-maturely stop the visitation of fields by returning true in your
  + * visitor. Returning false will continue visitation to further fields.
  + */
  +typedef struct
  +{
  +   bool (*visit_before)(const bson_iter_t *iter,
  +                               const char        *key,
  +                               void              *data);
  +   bool (*visit_after)(const bson_iter_t *iter,
  +                              const char        *key,
  +                              void              *data);
  +   void (*visit_corrupt)(const bson_iter_t *iter,
  +                         void              *data);
  +   bool (*visit_double)(const bson_iter_t *iter,
  +                               const char        *key,
  +                               double             v_double,
  +                               void              *data);
  +   bool (*visit_utf8)(const bson_iter_t *iter,
  +                             const char        *key,
  +                             size_t             v_utf8_len,
  +                             const char        *v_utf8,
  +                             void              *data);
  +   bool (*visit_document)(const bson_iter_t *iter,
  +                                 const char        *key,
  +                                 const bson_t      *v_document,
  +                                 void              *data);
  +   bool (*visit_array)(const bson_iter_t *iter,
  +                              const char        *key,
  +                              const bson_t      *v_array,
  +                              void              *data);
  +   bool (*visit_binary)(const bson_iter_t  *iter,
  +                               const char         *key,
  +                               bson_subtype_t      v_subtype,
  +                               size_t              v_binary_len,
  +                               const uint8_t *v_binary,
  +                               void               *data);
  +   bool (*visit_undefined)(const bson_iter_t *iter,
  +                                  const char        *key,
  +                                  void              *data);
  +   bool (*visit_oid)(const bson_iter_t *iter,
  +                            const char        *key,
  +                            const bson_oid_t  *v_oid,
  +                            void              *data);
  +   bool (*visit_bool)(const bson_iter_t *iter,
  +                             const char        *key,
  +                             bool        v_bool,
  +                             void              *data);
  +   bool (*visit_date_time)(const bson_iter_t *iter,
  +                                  const char        *key,
  +                                  int64_t       msec_since_epoch,
  +                                  void              *data);
  +   bool (*visit_null)(const bson_iter_t *iter,
  +                             const char        *key,
  +                             void              *data);
  +   bool (*visit_regex)(const bson_iter_t *iter,
  +                              const char        *key,
  +                              const char        *v_regex,
  +                              const char        *v_options,
  +                              void              *data);
  +   bool (*visit_dbpointer)(const bson_iter_t *iter,
  +                                  const char        *key,
  +                                  size_t             v_collection_len,
  +                                  const char        *v_collection,
  +                                  const bson_oid_t  *v_oid,
  +                                  void              *data);
  +   bool (*visit_code)(const bson_iter_t *iter,
  +                             const char        *key,
  +                             size_t             v_code_len,
  +                             const char        *v_code,
  +                             void              *data);
  +   bool (*visit_symbol)(const bson_iter_t *iter,
  +                               const char        *key,
  +                               size_t             v_symbol_len,
  +                               const char        *v_symbol,
  +                               void              *data);
  +   bool (*visit_codewscope)(const bson_iter_t *iter,
  +                                   const char        *key,
  +                                   size_t             v_code_len,
  +                                   const char        *v_code,
  +                                   const bson_t      *v_scope,
  +                                   void              *data);
  +   bool (*visit_int32)(const bson_iter_t *iter,
  +                              const char        *key,
  +                              int32_t       v_int32,
  +                              void              *data);
  +   bool (*visit_timestamp)(const bson_iter_t *iter,
  +                                  const char        *key,
  +                                  uint32_t      v_timestamp,
  +                                  uint32_t      v_increment,
  +                                  void              *data);
  +   bool (*visit_int64)(const bson_iter_t *iter,
  +                              const char        *key,
  +                              int64_t       v_int64,
  +                              void              *data);
  +   bool (*visit_maxkey)(const bson_iter_t *iter,
  +                               const char        *key,
  +                               void              *data);
  +   bool (*visit_minkey)(const bson_iter_t *iter,
  +                               const char        *key,
  +                               void              *data);
  +
  +   void *padding[9];
  +} bson_visitor_t;
  +
  +
  +typedef struct
  +{
  +   uint32_t domain;
  +   uint32_t code;
  +   char          message[504];
  +} bson_error_t;
  +
  +
  +BSON_STATIC_ASSERT (sizeof (bson_error_t) == 512);
  +
  +
  +/**
  + * bson_next_power_of_two:
  + * @v: A 32-bit unsigned integer of required bytes.
  + *
  + * Determines the next larger power of two for the value of @v
  + * in a constant number of operations.
  + *
  + * It is up to the caller to guarantee this will not overflow.
  + *
  + * Returns: The next power of 2 from @v.
  + */
  +static BSON_INLINE uint32_t
  +bson_next_power_of_two (uint32_t v)
  +{
  +   v--;
  +   v |= v >> 1;
  +   v |= v >> 2;
  +   v |= v >> 4;
  +   v |= v >> 8;
  +   v |= v >> 16;
  +   v++;
  +
  +   return v;
  +}
  +
  +
  +static BSON_INLINE bool
  +bson_is_power_of_two (uint32_t v)
  +{
  +   return ((v != 0) && ((v & (v - 1)) == 0));
  +}
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-atomic.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +#if defined(__GNUC__)
  +# define bson_atomic_int_add(p, v)   (__sync_add_and_fetch(p, v))
  +# define bson_atomic_int64_add(p, v) (__sync_add_and_fetch_8(p, v))
  +# define bson_memory_barrier         __sync_synchronize
  +#elif defined(_MSC_VER) || defined(_WIN32)
  +# define bson_atomic_int_add(p, v)   (InterlockedExchangeAdd((long int *)(p), v))
  +# define bson_atomic_int64_add(p, v) (InterlockedExchangeAdd64(p, v))
  +# define bson_memory_barrier         MemoryBarrier
  +#endif
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-clock.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +int64_t bson_get_monotonic_time (void);
  +int     bson_gettimeofday       (struct timeval  *tv,
  +                                 struct timezone *tz);
  +
   
  -extern void bson_little_endian64(void* outp, const void* inp);
  -extern void bson_little_endian32(void* outp, const void* inp);
  -extern void bson_big_endian64(void* outp, const void* inp);
  -extern void bson_big_endian32(void* outp, const void* inp);
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-context.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +bson_context_t *bson_context_new         (bson_context_flags_t flags);
  +void            bson_context_destroy     (bson_context_t *context);
  +bson_context_t *bson_context_get_default (void) BSON_GNUC_CONST;
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-endian.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +#define BSON_BIG_ENDIAN    4321
  +#define BSON_LITTLE_ENDIAN 1234
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * __bson_uint16_swap_slow --
  + *
  + *       Fallback endianness conversion for 16-bit integers.
  + *
  + * Returns:
  + *       The endian swapped version.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE uint16_t
  +__bson_uint16_swap_slow (uint16_t v) /* IN */
  +{
  +   return ((v & 0xFF) << 8) | ((v & 0xFF00) >> 8);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * __bson_uint32_swap_slow --
  + *
  + *       Fallback endianness conversion for 32-bit integers.
  + *
  + * Returns:
  + *       The endian swapped version.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE uint32_t
  +__bson_uint32_swap_slow (uint32_t v) /* IN */
  +{
  +   uint32_t ret;
  +   const char *src = (const char *)&v;
  +   char *dst = (char *)&ret;
  +
  +   dst[0] = src[3];
  +   dst[1] = src[2];
  +   dst[2] = src[1];
  +   dst[3] = src[0];
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * __bson_uint64_swap_slow --
  + *
  + *       Fallback endianness conversion for 64-bit integers.
  + *
  + * Returns:
  + *       The endian swapped version.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE uint64_t
  +__bson_uint64_swap_slow (uint64_t v) /* IN */
  +{
  +   uint64_t ret;
  +   const char *src = (const char *)&v;
  +   char *dst = (char *)&ret;
  +
  +   dst[0] = src[7];
  +   dst[1] = src[6];
  +   dst[2] = src[5];
  +   dst[3] = src[4];
  +   dst[4] = src[3];
  +   dst[5] = src[2];
  +   dst[6] = src[1];
  +   dst[7] = src[0];
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * __bson_double_swap_slow --
  + *
  + *       Fallback endianness conversion for double floating point.
  + *
  + * Returns:
  + *       The endian swapped version.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE double
  +__bson_double_swap_slow (double v) /* IN */
  +{
  +   double ret;
  +   const char *src = (const char *)&v;
  +   char *dst = (char *)&ret;
  +
  +   dst[0] = src[7];
  +   dst[1] = src[6];
  +   dst[2] = src[5];
  +   dst[3] = src[4];
  +   dst[4] = src[3];
  +   dst[5] = src[2];
  +   dst[6] = src[1];
  +   dst[7] = src[0];
  +
  +   return ret;
  +}
  +
  +
  +#if defined (__GNUC__) && (__GNUC__ >= 4)
  +# if __GNUC__ >= 4 && defined (__GNUC_MINOR__) && __GNUC_MINOR__ >= 3
  +#  define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32 ((uint32_t)v)
  +#  define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64 ((uint64_t)v)
  +# endif
  +# if __GNUC__ >= 4 && defined (__GNUC_MINOR__) && __GNUC_MINOR__ >= 8
  +#  define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16 ((uint32_t)v)
  +# endif
  +#endif
  +
  +
  +#ifndef BSON_UINT16_SWAP_LE_BE
  +# define BSON_UINT16_SWAP_LE_BE(v) __bson_uint16_swap_slow (v)
  +#endif
  +
  +
  +#ifndef BSON_UINT32_SWAP_LE_BE
  +# define BSON_UINT32_SWAP_LE_BE(v) __bson_uint32_swap_slow (v)
  +#endif
  +
  +
  +#ifndef BSON_UINT64_SWAP_LE_BE
  +# define BSON_UINT64_SWAP_LE_BE(v) __bson_uint64_swap_slow (v)
  +#endif
  +
  +
  +#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN
  +# define BSON_UINT16_FROM_LE(v)  ((uint16_t)v)
  +# define BSON_UINT16_TO_LE(v)    ((uint16_t)v)
  +# define BSON_UINT16_FROM_BE(v)  BSON_UINT16_SWAP_LE_BE (v)
  +# define BSON_UINT16_TO_BE(v)    BSON_UINT16_SWAP_LE_BE (v)
  +# define BSON_UINT32_FROM_LE(v)  ((uint32_t)v)
  +# define BSON_UINT32_TO_LE(v)    ((uint32_t)v)
  +# define BSON_UINT32_FROM_BE(v)  BSON_UINT32_SWAP_LE_BE (v)
  +# define BSON_UINT32_TO_BE(v)    BSON_UINT32_SWAP_LE_BE (v)
  +# define BSON_UINT64_FROM_LE(v)  ((uint64_t)v)
  +# define BSON_UINT64_TO_LE(v)    ((uint64_t)v)
  +# define BSON_UINT64_FROM_BE(v)  BSON_UINT64_SWAP_LE_BE (v)
  +# define BSON_UINT64_TO_BE(v)    BSON_UINT64_SWAP_LE_BE (v)
  +# define BSON_DOUBLE_FROM_LE(v)  ((double)v)
  +# define BSON_DOUBLE_TO_LE(v)    ((double)v)
  +#elif BSON_BYTE_ORDER == BSON_BIG_ENDIAN
  +# define BSON_UINT16_FROM_LE(v)  BSON_UINT16_SWAP_LE_BE (v)
  +# define BSON_UINT16_TO_LE(v)    BSON_UINT16_SWAP_LE_BE (v)
  +# define BSON_UINT16_FROM_BE(v)  ((uint16_t)v)
  +# define BSON_UINT16_TO_BE(v)    ((uint16_t)v)
  +# define BSON_UINT32_FROM_LE(v)  BSON_UINT32_SWAP_LE_BE (v)
  +# define BSON_UINT32_TO_LE(v)    BSON_UINT32_SWAP_LE_BE (v)
  +# define BSON_UINT32_FROM_BE(v)  ((uint32_t)v)
  +# define BSON_UINT32_TO_BE(v)    ((uint32_t)v)
  +# define BSON_UINT64_FROM_LE(v)  BSON_UINT64_SWAP_LE_BE (v)
  +# define BSON_UINT64_TO_LE(v)    BSON_UINT64_SWAP_LE_BE (v)
  +# define BSON_UINT64_FROM_BE(v)  ((uint64_t)v)
  +# define BSON_UINT64_TO_BE(v)    ((uint64_t)v)
  +# define BSON_DOUBLE_FROM_LE(v)  (__bson_double_swap_slow (v))
  +# define BSON_DOUBLE_TO_LE(v)    (__bson_double_swap_slow (v))
  +#else
  +# error "The endianness of target architecture is unknown."
  +#endif
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-error.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +#define BSON_ERROR_JSON   1
  +#define BSON_ERROR_READER 2
  +
  +
  +void  bson_set_error  (bson_error_t *error,
  +                       uint32_t      domain,
  +                       uint32_t      code,
  +                       const char   *format,
  +                       ...) BSON_GNUC_PRINTF (4, 5);
  +char *bson_strerror_r (int           err_code,
  +                       char         *buf,
  +                       size_t        buflen);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-iter.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +#define BSON_ITER_HOLDS_DOUBLE(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_DOUBLE)
  +
  +#define BSON_ITER_HOLDS_UTF8(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_UTF8)
  +
  +#define BSON_ITER_HOLDS_DOCUMENT(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_DOCUMENT)
  +
  +#define BSON_ITER_HOLDS_ARRAY(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_ARRAY)
  +
  +#define BSON_ITER_HOLDS_BINARY(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_BINARY)
  +
  +#define BSON_ITER_HOLDS_UNDEFINED(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_UNDEFINED)
  +
  +#define BSON_ITER_HOLDS_OID(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_OID)
  +
  +#define BSON_ITER_HOLDS_BOOL(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_BOOL)
  +
  +#define BSON_ITER_HOLDS_DATE_TIME(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_DATE_TIME)
  +
  +#define BSON_ITER_HOLDS_NULL(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_NULL)
  +
  +#define BSON_ITER_HOLDS_REGEX(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_REGEX)
  +
  +#define BSON_ITER_HOLDS_DBPOINTER(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_DBPOINTER)
  +
  +#define BSON_ITER_HOLDS_CODE(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_CODE)
  +
  +#define BSON_ITER_HOLDS_SYMBOL(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_SYMBOL)
  +
  +#define BSON_ITER_HOLDS_CODEWSCOPE(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_CODEWSCOPE)
  +
  +#define BSON_ITER_HOLDS_INT32(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_INT32)
  +
  +#define BSON_ITER_HOLDS_TIMESTAMP(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_TIMESTAMP)
  +
  +#define BSON_ITER_HOLDS_INT64(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_INT64)
  +
  +#define BSON_ITER_HOLDS_MAXKEY(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_MAXKEY)
  +
  +#define BSON_ITER_HOLDS_MINKEY(iter) \
  +   (bson_iter_type ((iter)) == BSON_TYPE_MINKEY)
  +
  +
  +/**
  + * bson_iter_utf8_len_unsafe:
  + * @iter: a bson_iter_t.
  + *
  + * Returns the length of a string currently pointed to by @iter. This performs
  + * no validation so the is responsible for knowing the BSON is valid. Calling
  + * bson_validate() is one way to do this ahead of time.
  + */
  +static BSON_INLINE uint32_t
  +bson_iter_utf8_len_unsafe (const bson_iter_t *iter)
  +{
  +   int32_t val;
  +
  +   memcpy (&val, iter->raw + iter->d1, 4);
  +   val = BSON_UINT32_FROM_LE (val);
  +   return MAX (0, val - 1);
  +}
  +
  +
  +void
  +bson_iter_array (const bson_iter_t   *iter,
  +                 uint32_t       *array_len,
  +                 const uint8_t **array);
  +
  +
  +void
  +bson_iter_binary (const bson_iter_t   *iter,
  +                  bson_subtype_t      *subtype,
  +                  uint32_t       *binary_len,
  +                  const uint8_t **binary);
  +
  +
  +const char *
  +bson_iter_code (const bson_iter_t *iter,
  +                uint32_t     *length);
  +
  +
  +/**
  + * bson_iter_code_unsafe:
  + * @iter: A bson_iter_t.
  + * @length: A location for the length of the resulting string.
  + *
  + * Like bson_iter_code() but performs no integrity checks.
  + *
  + * Returns: A string that should not be modified or freed.
  + */
  +static BSON_INLINE const char *
  +bson_iter_code_unsafe (const bson_iter_t *iter,
  +                       uint32_t     *length)
  +{
  +   *length = bson_iter_utf8_len_unsafe (iter);
  +   return (const char *)(iter->raw + iter->d2);
  +}
  +
  +
  +const char *
  +bson_iter_codewscope (const bson_iter_t   *iter,
  +                      uint32_t       *length,
  +                      uint32_t       *scope_len,
  +                      const uint8_t **scope);
  +
  +
  +void
  +bson_iter_dbpointer (const bson_iter_t *iter,
  +                     uint32_t     *collection_len,
  +                     const char       **collection,
  +                     const bson_oid_t **oid);
  +
  +
  +void
  +bson_iter_document (const bson_iter_t   *iter,
  +                    uint32_t       *document_len,
  +                    const uint8_t **document);
  +
  +
  +double
  +bson_iter_double (const bson_iter_t *iter);
  +
  +
  +/**
  + * bson_iter_double_unsafe:
  + * @iter: A bson_iter_t.
  + *
  + * Similar to bson_iter_double() but does not perform an integrity checking.
  + *
  + * Returns: A double.
  + */
  +static BSON_INLINE double
  +bson_iter_double_unsafe (const bson_iter_t *iter)
  +{
  +   double val;
  +
  +   memcpy (&val, iter->raw + iter->d1, 8);
  +   return BSON_DOUBLE_FROM_LE (val);
  +}
  +
  +
  +bool
  +bson_iter_init (bson_iter_t  *iter,
  +                const bson_t *bson);
  +
  +
  +bool
  +bson_iter_init_find (bson_iter_t  *iter,
  +                     const bson_t *bson,
  +                     const char   *key);
  +
  +
  +bool
  +bson_iter_init_find_case (bson_iter_t  *iter,
  +                          const bson_t *bson,
  +                          const char   *key);
  +
  +
  +int32_t
  +bson_iter_int32 (const bson_iter_t *iter);
  +
  +
  +/**
  + * bson_iter_int32_unsafe:
  + * @iter: A bson_iter_t.
  + *
  + * Similar to bson_iter_int32() but with no integrity checking.
  + *
  + * Returns: A 32-bit signed integer.
  + */
  +static BSON_INLINE int32_t
  +bson_iter_int32_unsafe (const bson_iter_t *iter)
  +{
  +   int32_t val;
  +
  +   memcpy (&val, iter->raw + iter->d1, 4);
  +   return BSON_UINT32_FROM_LE (val);
  +}
  +
  +
  +int64_t
  +bson_iter_int64 (const bson_iter_t *iter);
  +
  +
  +int64_t
  +bson_iter_as_int64 (const bson_iter_t *iter);
  +
  +
  +/**
  + * bson_iter_int64_unsafe:
  + * @iter: a bson_iter_t.
  + *
  + * Similar to bson_iter_int64() but without integrity checking.
  + *
  + * Returns: A 64-bit signed integer.
  + */
  +static BSON_INLINE int64_t
  +bson_iter_int64_unsafe (const bson_iter_t *iter)
  +{
  +   int64_t val;
  +
  +   memcpy (&val, iter->raw + iter->d1, 8);
  +   return BSON_UINT64_FROM_LE (val);
  +}
  +
  +
  +bool
  +bson_iter_find (bson_iter_t *iter,
  +                const char  *key);
  +
  +
  +bool
  +bson_iter_find_case (bson_iter_t *iter,
  +                     const char  *key);
  +
  +
  +bool
  +bson_iter_find_descendant (bson_iter_t *iter,
  +                           const char  *dotkey,
  +                           bson_iter_t *descendant);
  +
  +
  +bool
  +bson_iter_next (bson_iter_t *iter);
  +
  +
  +const bson_oid_t *
  +bson_iter_oid (const bson_iter_t *iter);
   
  -/* ----------------------------
  -   READING
  -   ------------------------------ */
   
   /**
  - * Zero a bson struct.  All fields are set to zero except the stack.
  + * bson_iter_oid_unsafe:
  + * @iter: A #bson_iter_t.
    *
  - * @note Mainly used internally, but can be called for safety
  - *       purposes so that a later call to bson_destroy() doesn't flip out.
  - *       It is safe to call this function on a NULL pointer in which case
  - *       there is no effect.
  - * @param b the BSON object to zero.
  + * Similar to bson_iter_oid() but performs no integrity checks.
    *
  + * Returns: A #bson_oid_t that should not be modified or freed.
    */
  -MONGO_EXPORT void bson_init_zero( bson *b );
  +static BSON_INLINE const bson_oid_t *
  +bson_iter_oid_unsafe (const bson_iter_t *iter)
  +{
  +   return (const bson_oid_t *)(iter->raw + iter->d1);
  +}
  +
  +
  +const char *
  +bson_iter_key (const bson_iter_t *iter);
  +
  +
  +/**
  + * bson_iter_key_unsafe:
  + * @iter: A bson_iter_t.
  + *
  + * Similar to bson_iter_key() but performs no integrity checking.
  + *
  + * Returns: A string that should not be modified or freed.
  + */
  +static BSON_INLINE const char *
  +bson_iter_key_unsafe (const bson_iter_t *iter)
  +{
  +   return (const char *)(iter->raw + iter->key);
  +}
  +
  +
  +const char *
  +bson_iter_utf8 (const bson_iter_t *iter,
  +                uint32_t     *length);
  +
  +
  +/**
  + * bson_iter_utf8_unsafe:
  + *
  + * Similar to bson_iter_utf8() but performs no integrity checking.
  + *
  + * Returns: A string that should not be modified or freed.
  + */
  +static BSON_INLINE const char *
  +bson_iter_utf8_unsafe (const bson_iter_t *iter,
  +                       uint32_t     *length)
  +{
  +   *length = bson_iter_utf8_len_unsafe (iter);
  +   return (const char *)(iter->raw + iter->d2);
  +}
  +
  +
  +char *
  +bson_iter_dup_utf8 (const bson_iter_t *iter,
  +                    uint32_t     *length);
  +
  +
  +int64_t
  +bson_iter_date_time (const bson_iter_t *iter);
  +
  +
  +time_t
  +bson_iter_time_t (const bson_iter_t *iter);
  +
  +
  +/**
  + * bson_iter_time_t_unsafe:
  + * @iter: A bson_iter_t.
  + *
  + * Similar to bson_iter_time_t() but performs no integrity checking.
  + *
  + * Returns: A time_t containing the number of seconds since UNIX epoch
  + *          in UTC.
  + */
  +static BSON_INLINE time_t
  +bson_iter_time_t_unsafe (const bson_iter_t *iter)
  +{
  +   return (time_t)(bson_iter_int64_unsafe (iter) / 1000UL);
  +}
  +
  +
  +void
  +bson_iter_timeval (const bson_iter_t *iter,
  +                   struct timeval    *tv);
  +
  +
  +/**
  + * bson_iter_timeval_unsafe:
  + * @iter: A bson_iter_t.
  + * @tv: A struct timeval.
  + *
  + * Similar to bson_iter_timeval() but performs no integrity checking.
  + */
  +static BSON_INLINE void
  +bson_iter_timeval_unsafe (const bson_iter_t *iter,
  +                          struct timeval    *tv)
  +{
  +#ifdef BSON_OS_WIN32
  +   tv->tv_sec = (long)bson_iter_int64_unsafe (iter);
  +#else
  +   tv->tv_sec = bson_iter_int64_unsafe (iter);
  +#endif
  +   tv->tv_usec = 0;
  +}
  +
  +
  +void
  +bson_iter_timestamp (const bson_iter_t *iter,
  +                     uint32_t     *timestamp,
  +                     uint32_t     *increment);
  +
  +
  +bool
  +bson_iter_bool (const bson_iter_t *iter);
  +
  +
  +/**
  + * bson_iter_bool_unsafe:
  + * @iter: A bson_iter_t.
  + *
  + * Similar to bson_iter_bool() but performs no integrity checking.
  + *
  + * Returns: true or false.
  + */
  +static BSON_INLINE bool
  +bson_iter_bool_unsafe (const bson_iter_t *iter)
  +{
  +   char val;
  +
  +   memcpy (&val, iter->raw + iter->d1, 1);
  +   return !!val;
  +}
  +
  +
  +bool
  +bson_iter_as_bool (const bson_iter_t *iter);
  +
  +
  +const char *
  +bson_iter_regex (const bson_iter_t *iter,
  +                 const char       **options);
  +
  +
  +const char *
  +bson_iter_symbol (const bson_iter_t *iter,
  +                  uint32_t     *length);
  +
  +
  +bson_type_t
  +bson_iter_type (const bson_iter_t *iter);
  +
  +
  +/**
  + * bson_iter_type_unsafe:
  + * @iter: A bson_iter_t.
  + *
  + * Similar to bson_iter_type() but performs no integrity checking.
  + *
  + * Returns: A bson_type_t.
  + */
  +static BSON_INLINE bson_type_t
  +bson_iter_type_unsafe (const bson_iter_t *iter)
  +{
  +   return (bson_type_t) (iter->raw + iter->type) [0];
  +}
  +
  +
  +bool
  +bson_iter_recurse (const bson_iter_t *iter,
  +                   bson_iter_t       *child);
  +
  +
  +void
  +bson_iter_overwrite_int32 (bson_iter_t *iter,
  +                           int32_t value);
  +
  +
  +void
  +bson_iter_overwrite_int64 (bson_iter_t *iter,
  +                           int64_t value);
  +
  +
  +void
  +bson_iter_overwrite_double (bson_iter_t *iter,
  +                            double       value);
  +
  +
  +void
  +bson_iter_overwrite_bool (bson_iter_t *iter,
  +                          bool  value);
  +
  +
  +bool
  +bson_iter_visit_all (bson_iter_t          *iter,
  +                     const bson_visitor_t *visitor,
  +                     void                 *data);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-json.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +typedef struct _bson_json_reader_t bson_json_reader_t;
  +
  +
  +typedef enum
  +{
  +   BSON_JSON_ERROR_READ_CORRUPT_JS = 1,
  +   BSON_JSON_ERROR_READ_INVALID_PARAM,
  +   BSON_JSON_ERROR_READ_CB_FAILURE,
  +} bson_json_error_code_t;
  +
  +
  +typedef ssize_t (*bson_json_reader_cb) (void    *handle,
  +                                        uint8_t *buf,
  +                                        size_t   count);
  +typedef void    (*bson_json_destroy_cb)(void    *handle);
  +
  +
  +bson_json_reader_t  *bson_json_reader_new          (void                 *data,
  +                                                    bson_json_reader_cb   cb,
  +                                                    bson_json_destroy_cb  dcb,
  +                                                    bool                  \
allow_multiple,  +                                                    size_t          \
buf_size);  +bson_json_reader_t *bson_json_reader_new_from_fd   (int                  \
fd,  +                                                    bool                  \
close_on_destroy);  +bson_json_reader_t *bson_json_reader_new_from_file (const char   \
*filename,  +                                                    bson_error_t         \
*error);  +void                bson_json_reader_destroy       (bson_json_reader_t   \
*reader);  +int                 bson_json_reader_read          (bson_json_reader_t   \
*reader,  +                                                    bson_t               \
*bson,  +                                                    bson_error_t         \
*error);  +bson_json_reader_t *bson_json_data_reader_new      (bool                  \
allow_multiple,  +                                                    size_t          \
size);  +void                bson_json_data_reader_ingest   (bson_json_reader_t   \
*reader,  +                                                    const uint8_t        \
*data,  +                                                    size_t                \
len);  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-keys.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +size_t bson_uint32_to_string (uint32_t     value,
  +                              const char **strptr,
  +                              char        *str,
  +                              size_t       size);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-md5.h */
  +
  +/*
  +  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
  +
  +  This software is provided 'as-is', without any express or implied
  +  warranty.  In no event will the authors be held liable for any damages
  +  arising from the use of this software.
  +
  +  Permission is granted to anyone to use this software for any purpose,
  +  including commercial applications, and to alter it and redistribute it
  +  freely, subject to the following restrictions:
  +
  +  1. The origin of this software must not be misrepresented; you must not
  +     claim that you wrote the original software. If you use this software
  +     in a product, an acknowledgment in the product documentation would be
  +     appreciated but is not required.
  +  2. Altered source versions must be plainly marked as such, and must not be
  +     misrepresented as being the original software.
  +  3. This notice may not be removed or altered from any source distribution.
  +
  +  L. Peter Deutsch
  +  ghost@aladdin.com
  +
  + */
  +/* $Id: bson.h,v 2.3.4.9 2014/09/30 22:31:43 jbj Exp $ */
  +/*
  +  Independent implementation of MD5 (RFC 1321).
  +
  +  This code implements the MD5 Algorithm defined in RFC 1321, whose
  +  text is available at
  +    http://www.ietf.org/rfc/rfc1321.txt
  +  The code is derived from the text of the RFC, including the test suite
  +  (section A.5) but excluding the rest of Appendix A.  It does not include
  +  any code or documentation that is identified in the RFC as being
  +  copyrighted.
  +
  +  The original and principal author of md5.h is L. Peter Deutsch
  +  <ghost@aladdin.com>.  Other authors are noted in the change history
  +  that follows (in reverse chronological order):
  +
  +  2002-04-13 lpd Removed support for non-ANSI compilers; removed
  +    references to Ghostscript; clarified derivation from RFC 1321;
  +    now handles byte order either statically or dynamically.
  +  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
  +  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
  +    added conditionalization for C++ compilation from Martin
  +    Purschke <purschke@bnl.gov>.
  +  1999-05-03 lpd Original version.
  + */
  +
  +
  +/*
  + * The following MD5 implementation has been modified to use types as
  + * specified in libbson.
  + */
  +
  +
  +BSON_BEGIN_DECLS
  +
  +
  +typedef struct
  +{
  +   uint32_t count[2]; /* message length in bits, lsw first */
  +   uint32_t abcd[4];  /* digest buffer */
  +   uint8_t  buf[64];  /* accumulate block */
  +} bson_md5_t;
  +
  +
  +void bson_md5_init   (bson_md5_t         *pms);
  +void bson_md5_append (bson_md5_t         *pms,
  +                      const uint8_t *data,
  +                      uint32_t       nbytes);
  +void bson_md5_finish (bson_md5_t         *pms,
  +                      uint8_t        digest[16]);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-memory.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +typedef void *(*bson_realloc_func) (void  *mem,
  +                                    size_t num_bytes);
  +
  +
  +void *bson_malloc    (size_t  num_bytes);
  +void *bson_malloc0   (size_t  num_bytes);
  +void *bson_realloc   (void   *mem,
  +                      size_t  num_bytes);
  +void  bson_free      (void   *mem);
  +void  bson_zero_free (void   *mem,
  +                      size_t  size);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-oid.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +int      bson_oid_compare          (const bson_oid_t *oid1,
  +                                    const bson_oid_t *oid2);
  +void     bson_oid_copy             (const bson_oid_t *src,
  +                                    bson_oid_t       *dst);
  +bool     bson_oid_equal            (const bson_oid_t *oid1,
  +                                    const bson_oid_t *oid2);
  +bool     bson_oid_is_valid         (const char       *str,
  +                                    size_t            length);
  +time_t   bson_oid_get_time_t       (const bson_oid_t *oid);
  +uint32_t bson_oid_hash             (const bson_oid_t *oid);
  +void     bson_oid_init             (bson_oid_t       *oid,
  +                                    bson_context_t   *context);
  +void     bson_oid_init_from_data   (bson_oid_t       *oid,
  +                                    const uint8_t    *data);
  +void     bson_oid_init_from_string (bson_oid_t       *oid,
  +                                    const char       *str);
  +void     bson_oid_init_sequence    (bson_oid_t       *oid,
  +                                    bson_context_t   *context);
  +void     bson_oid_to_string        (const bson_oid_t *oid,
  +                                    char              str[25]);
  +
   
   /**
  - * Allocate memory for a new BSON object.
  + * bson_oid_compare_unsafe:
  + * @oid1: A bson_oid_t.
  + * @oid2: A bson_oid_t.
    *
  - * @note After using this function, you must initialize the object
  - * using bson_init_finished_data( ), bson_init_empty( ), bson_init( ),
  - * or one of the other init functions.
  + * Performs a qsort() style comparison between @oid1 and @oid2.
    *
  - * @return a new BSON object.
  - */
  -MONGO_EXPORT bson* bson_alloc( void );
  -
  -/**
  - * Deallocate a BSON object.
  + * This function is meant to be as fast as possible and therefore performs
  + * no argument validation. That is the callers responsibility.
    *
  - * @note You must call bson_destroy( ) before calling this function.
  + * Returns: An integer < 0 if @oid1 is less than @oid2. Zero if they are equal.
  + *          An integer > 0 if @oid1 is greater than @oid2.
    */
  -MONGO_EXPORT void bson_dealloc( bson* b );
  +static BSON_INLINE int
  +bson_oid_compare_unsafe (const bson_oid_t *oid1,
  +                         const bson_oid_t *oid2)
  +{
  +   return memcmp (oid1, oid2, sizeof *oid1);
  +}
  +
   
   /**
  - * Initialize a BSON object for reading and set its data
  - * pointer to the provided char*.
  + * bson_oid_equal_unsafe:
  + * @oid1: A bson_oid_t.
  + * @oid2: A bson_oid_t.
    *
  - * @note When done using the bson object, you must pass
  - *      the object to bson_destroy( ).
  + * Checks the equality of @oid1 and @oid2.
    *
  - * @param b the BSON object to initialize.
  - * @param data the finalized raw BSON data.
  - * @param ownsData when true, bson_destroy() will free the data block.
  + * This function is meant to be as fast as possible and therefore performs
  + * no checks for argument validity. That is the callers responsibility.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if @oid1 and @oid2 are equal; otherwise false.
    */
  -int bson_init_finished_data( bson *b, char *data, bson_bool_t ownsData );
  +static BSON_INLINE bool
  +bson_oid_equal_unsafe (const bson_oid_t *oid1,
  +                       const bson_oid_t *oid2)
  +{
  +   return !memcmp (oid1, oid2, sizeof *oid1);
  +}
   
   /**
  - * Initialize a BSON object for reading and copy finalized
  - * BSON data from the provided char*.
  + * bson_oid_hash_unsafe:
  + * @oid: A bson_oid_t.
    *
  - * @note When done using the bson object, you must pass
  - *      the object to bson_destroy( ).
  + * This function performs a DJB style hash upon the bytes contained in @oid.
  + * The result is a hash key suitable for use in a hashtable.
    *
  - * @param b the BSON object to initialize.
  - * @param data the finalized raw BSON data to copy.
  + * This function is meant to be as fast as possible and therefore performs no
  + * validation of arguments. The caller is responsible to ensure they are
  + * passing valid arguments.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: A uint32_t containing a hash code.
    */
  -int bson_init_finished_data_with_copy( bson *b, const char *data );
  +static BSON_INLINE uint32_t
  +bson_oid_hash_unsafe (const bson_oid_t *oid)
  +{
  +   uint32_t hash = 5381;
  +   uint32_t i;
   
  -/**
  - * Size of a BSON object.
  - *
  - * @param b the BSON object.
  - *
  - * @return the size.
  - */
  -MONGO_EXPORT int bson_size( const bson *b );
  +   for (i = 0; i < sizeof oid->bytes; i++) {
  +      hash = ((hash << 5) + hash) + oid->bytes[i];
  +   }
   
  -/**
  - * Minimum finished size of an unfinished BSON object given current contents.
  - *
  - * @param b the BSON object.
  - *
  - * @return the BSON object's minimum finished size
  - */
  -MONGO_EXPORT size_t bson_buffer_size( const bson *b )
  -	RPM_GNUC_PURE;
  +   return hash;
  +}
   
  -/**
  - * Print a string representation of a BSON object.
  - *
  - * @param b the BSON object to print.
  - */
  -MONGO_EXPORT void bson_print( const bson *b );
   
   /**
  - * Return a pointer to the raw buffer stored by this bson object.
  + * bson_oid_copy_unsafe:
  + * @src: A bson_oid_t to copy from.
  + * @dst: A bson_oid_t to copy into.
    *
  - * @param b a BSON object
  + * Copies the contents of @src into @dst. This function is meant to be as
  + * fast as possible and therefore performs no argument checking. It is the
  + * callers responsibility to ensure they are passing valid data into the
  + * function.
    */
  -MONGO_EXPORT const char *bson_data( const bson *b )
  -	RPM_GNUC_PURE;
  +static BSON_INLINE void
  +bson_oid_copy_unsafe (const bson_oid_t *src,
  +                      bson_oid_t       *dst)
  +{
  +   memcpy (dst, src, sizeof *src);
  +}
  +
   
   /**
  - * Returns true if bson_data(b) {b->data} is not null; else, false.
  + * bson_oid_parse_hex_char:
  + * @hex: A character to parse to its integer value.
  + *
  + * This function contains a jump table to return the integer value for a
  + * character containing a hexidecimal value (0-9, a-f, A-F). If the character
  + * is not a hexidecimal character then zero is returned.
    *
  - * @note Convenience function for determining if bson data was returned by a \
                function.
  - *       Check required after calls to mongo_create_index(), \
                mongo_create_simple_index(),
  - *       mongo_cmd_get_last_error() and mongo_cmd_get_prev_error().
  - * @param b the bson struct to inspect.
  + * Returns: An integer between 0 and 15.
    */
  +static BSON_INLINE uint8_t
  +bson_oid_parse_hex_char (char hex)
  +{
  +   switch (hex) {
  +   case '0':
  +      return 0;
  +   case '1':
  +      return 1;
  +   case '2':
  +      return 2;
  +   case '3':
  +      return 3;
  +   case '4':
  +      return 4;
  +   case '5':
  +      return 5;
  +   case '6':
  +      return 6;
  +   case '7':
  +      return 7;
  +   case '8':
  +      return 8;
  +   case '9':
  +      return 9;
  +   case 'a':
  +   case 'A':
  +      return 0xa;
  +   case 'b':
  +   case 'B':
  +      return 0xb;
  +   case 'c':
  +   case 'C':
  +      return 0xc;
  +   case 'd':
  +   case 'D':
  +      return 0xd;
  +   case 'e':
  +   case 'E':
  +      return 0xe;
  +   case 'f':
  +   case 'F':
  +      return 0xf;
  +   default:
  +      return 0;
  +   }
  +}
   
  -MONGO_EXPORT int bson_has_data( const bson *b );
   
   /**
  - * Print a string representation of a BSON object.
  + * bson_oid_init_from_string_unsafe:
  + * @oid: A bson_oid_t to store the result.
  + * @str: A 24-character hexidecimal encoded string.
    *
  - * @param bson the raw data to print.
  - * @param depth the depth to recurse the object.x
  + * Parses a string containing 24 hexidecimal encoded bytes into a bson_oid_t.
  + * This function is meant to be as fast as possible and inlined into your
  + * code. For that purpose, the function does not perform any sort of bounds
  + * checking and it is the callers responsibility to ensure they are passing
  + * valid input to the function.
    */
  -MONGO_EXPORT void bson_print_raw( const char *bson , int depth );
  +static BSON_INLINE void
  +bson_oid_init_from_string_unsafe (bson_oid_t *oid,
  +                                  const char *str)
  +{
  +   int i;
  +
  +   for (i = 0; i < 12; i++) {
  +      oid->bytes[i] = ((bson_oid_parse_hex_char (str[2 * i]) << 4) |
  +                       (bson_oid_parse_hex_char (str[2 * i + 1])));
  +   }
  +}
  +
   
   /**
  - * Advance a bson_iterator to the named field.
  + * bson_oid_get_time_t_unsafe:
  + * @oid: A bson_oid_t.
    *
  - * @param it the bson_iterator to use.
  - * @param obj the BSON object to use.
  - * @param name the name of the field to find.
  + * Fetches the time @oid was generated.
    *
  - * @return the type of the found object or BSON_EOO if it is not found.
  + * Returns: A time_t containing the UNIX timestamp of generation.
    */
  -MONGO_EXPORT bson_type bson_find( bson_iterator *it, const bson *obj, const char \
*name );  +static BSON_INLINE time_t
  +bson_oid_get_time_t_unsafe (const bson_oid_t *oid)
  +{
  +   uint32_t t;
   
  +   memcpy (&t, oid, 4);
  +   return BSON_UINT32_FROM_BE (t);
  +}
   
  -MONGO_EXPORT bson_iterator* bson_iterator_alloc( void );
  -MONGO_EXPORT void bson_iterator_dealloc(bson_iterator*);
  -/**
  - * Initialize a bson_iterator.
  - *
  - * @param i the bson_iterator to initialize.
  - * @param b the BSON object to associate with the iterator.
  - */
  -MONGO_EXPORT void bson_iterator_init( bson_iterator *i , const bson *b );
   
  -/**
  - * Initialize a bson iterator from a const char* buffer. Note
  - * that this is mostly used internally.
  - *
  - * @param i the bson_iterator to initialize.
  - * @param buffer the buffer to point to.
  - */
  -MONGO_EXPORT void bson_iterator_from_buffer( bson_iterator *i, const char *buffer \
);  +BSON_END_DECLS
   
  -/* more returns true for eoo. best to loop with bson_iterator_next(&it) */
  -/**
  - * Check to see if the bson_iterator has more data.
  +/*==============================================================*/
  +/* --- bson-reader.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +#define BSON_ERROR_READER_BADFD 1
  +
  +
  +/*
  + *--------------------------------------------------------------------------
    *
  - * @param i the iterator.
  + * bson_reader_read_func_t --
    *
  - * @return  returns true if there is more data.
  - */
  -MONGO_EXPORT bson_bool_t bson_iterator_more( const bson_iterator *i )
  -	RPM_GNUC_PURE;
  -
  -/**
  - * Point the iterator at the next BSON object.
  + *       This function is a callback used by bson_reader_t to read the
  + *       next chunk of data from the underlying opaque file descriptor.
    *
  - * @param i the bson_iterator.
  + *       This function is meant to operate similar to the read() function
  + *       as part of libc on UNIX-like systems.
    *
  - * @return the type of the next BSON object.
  - */
  -MONGO_EXPORT bson_type bson_iterator_next( bson_iterator *i );
  -
  -/**
  - * Get the type of the BSON object currently pointed to by the iterator.
  + * Parameters:
  + *       @handle: The handle to read from.
  + *       @buf: The buffer to read into.
  + *       @count: The number of bytes to read.
    *
  - * @param i the bson_iterator
  + * Returns:
  + *       0 for end of stream.
  + *       -1 for read failure.
  + *       Greater than zero for number of bytes read into @buf.
    *
  - * @return  the type of the current BSON object.
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
    */
  -MONGO_EXPORT bson_type bson_iterator_type( const bson_iterator *i )
  -	RPM_GNUC_PURE;
   
  -/**
  - * Get the key of the BSON object currently pointed to by the iterator.
  +typedef ssize_t (*bson_reader_read_func_t) (void  *handle, /* IN */
  +                                            void  *buf,    /* IN */
  +                                            size_t count); /* IN */
  +
  +
  +/*
  + *--------------------------------------------------------------------------
    *
  - * @param i the bson_iterator
  + * bson_reader_destroy_func_t --
    *
  - * @return the key of the current BSON object.
  - */
  -MONGO_EXPORT const char *bson_iterator_key( const bson_iterator *i )
  -	RPM_GNUC_PURE;
  -
  -/**
  - * Get the value of the BSON object currently pointed to by the iterator.
  + *       Destroy callback to release any resources associated with the
  + *       opaque handle.
    *
  - * @param i the bson_iterator
  + * Parameters:
  + *       @handle: the handle provided to bson_reader_new_from_handle().
    *
  - * @return  the value of the current BSON object.
  - */
  -MONGO_EXPORT const char *bson_iterator_value( const bson_iterator *i )
  -	RPM_GNUC_PURE;
  -
  -/* these convert to the right type (return 0 if non-numeric) */
  -/**
  - * Get the double value of the BSON object currently pointed to by the
  - * iterator.
  + * Returns:
  + *       None.
    *
  - * @param i the bson_iterator
  + * Side effects:
  + *       None.
    *
  - * @return  the value of the current BSON object.
  + *--------------------------------------------------------------------------
    */
  -MONGO_EXPORT double bson_iterator_double( const bson_iterator *i );
  +
  +typedef void (*bson_reader_destroy_func_t) (void *handle); /* IN */
  +
  +
  +bson_reader_t *bson_reader_new_from_handle  (void                       *handle,
  +                                             bson_reader_read_func_t     rf,
  +                                             bson_reader_destroy_func_t  df);
  +bson_reader_t *bson_reader_new_from_fd      (int                         fd,
  +                                             bool                        \
close_on_destroy);  +bson_reader_t *bson_reader_new_from_file    (const char          \
*path,  +                                             bson_error_t               \
*error);  +bson_reader_t *bson_reader_new_from_data    (const uint8_t              \
*data,  +                                             size_t                      \
length);  +void           bson_reader_destroy          (bson_reader_t              \
*reader);  +void           bson_reader_set_read_func    (bson_reader_t              \
*reader,  +                                             bson_reader_read_func_t     \
func);  +void           bson_reader_set_destroy_func (bson_reader_t              \
*reader,  +                                             bson_reader_destroy_func_t  \
func);  +const bson_t  *bson_reader_read             (bson_reader_t              \
*reader,  +                                             bool                       \
*reached_eof);  +off_t          bson_reader_tell             (bson_reader_t           \
*reader);  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-string.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +typedef struct
  +{
  +   char     *str;
  +   uint32_t  len;
  +   uint32_t  alloc;
  +} bson_string_t;
  +
  +
  +bson_string_t *bson_string_new            (const char      *str);
  +char          *bson_string_free           (bson_string_t   *string,
  +                                           bool             free_segment);
  +void           bson_string_append         (bson_string_t   *string,
  +                                           const char      *str);
  +void           bson_string_append_c       (bson_string_t   *string,
  +                                           char             str);
  +void           bson_string_append_unichar (bson_string_t   *string,
  +                                           bson_unichar_t   unichar);
  +void           bson_string_append_printf  (bson_string_t   *string,
  +                                           const char      *format,
  +                                           ...) BSON_GNUC_PRINTF (2, 3);
  +void           bson_string_truncate       (bson_string_t  *string,
  +                                           uint32_t        len);
  +char          *bson_strdup                (const char     *str);
  +char          *bson_strdup_printf         (const char     *format,
  +                                           ...) BSON_GNUC_PRINTF (1, 2);
  +char          *bson_strdupv_printf        (const char     *format,
  +                                           va_list         args) BSON_GNUC_PRINTF \
(1, 0);  +char          *bson_strndup               (const char     *str,
  +                                           size_t          n_bytes);
  +void           bson_strncpy               (char           *dst,
  +                                           const char     *src,
  +                                           size_t          size);
  +int            bson_vsnprintf             (char           *str,
  +                                           size_t          size,
  +                                           const char     *format,
  +                                           va_list         ap) BSON_GNUC_PRINTF \
(3, 0);  +int            bson_snprintf              (char           *str,
  +                                           size_t          size,
  +                                           const char     *format,
  +                                           ...) BSON_GNUC_PRINTF (3, 4);
  +void           bson_strfreev              (char          **strv);
  +size_t         bson_strnlen               (const char     *s,
  +                                           size_t          maxlen);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-utf8.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +bool            bson_utf8_validate        (const char     *utf8,
  +                                           size_t          utf8_len,
  +                                           bool            allow_null);
  +char           *bson_utf8_escape_for_json (const char     *utf8,
  +                                           ssize_t         utf8_len);
  +bson_unichar_t  bson_utf8_get_char        (const char     *utf8);
  +const char     *bson_utf8_next_char       (const char     *utf8);
  +void            bson_utf8_from_unichar    (bson_unichar_t  unichar,
  +                                           char            utf8[6],
  +                                           uint32_t       *len);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-version.h */
   
   /**
  - * Get the int value of the BSON object currently pointed to by the iterator.
  + * BSON_MAJOR_VERSION:
    *
  - * @param i the bson_iterator
  - *
  - * @return  the value of the current BSON object.
  + * BSON major version component (e.g. 1 if %BSON_VERSION is 1.2.3)
    */
  -MONGO_EXPORT int bson_iterator_int( const bson_iterator *i );
  +#define BSON_MAJOR_VERSION (0)
  +
   
   /**
  - * Get the long value of the BSON object currently pointed to by the iterator.
  + * BSON_MINOR_VERSION:
    *
  - * @param i the bson_iterator
  - *
  - * @return the value of the current BSON object.
  + * BSON minor version component (e.g. 2 if %BSON_VERSION is 1.2.3)
    */
  -MONGO_EXPORT int64_t bson_iterator_long( const bson_iterator *i );
  +#define BSON_MINOR_VERSION (6)
  +
   
  -/* return the bson timestamp as a whole or in parts */
   /**
  - * Get the timestamp value of the BSON object currently pointed to by
  - * the iterator.
  + * BSON_MICRO_VERSION:
    *
  - * @param i the bson_iterator
  - *
  - * @return the value of the current BSON object.
  + * BSON micro version component (e.g. 3 if %BSON_VERSION is 1.2.3)
    */
  -MONGO_EXPORT bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i );
  -MONGO_EXPORT int bson_iterator_timestamp_time( const bson_iterator *i );
  -MONGO_EXPORT int bson_iterator_timestamp_increment( const bson_iterator *i );
  +#define BSON_MICRO_VERSION (4)
  +
   
   /**
  - * Get the boolean value of the BSON object currently pointed to by
  - * the iterator.
  + * BSON_VERSION:
    *
  - * @param i the bson_iterator
  - *
  - * @return the value of the current BSON object.
  + * BSON version.
    */
  -/* false: boolean false, 0 in any type, or null */
  -/* true: anything else (even empty strings and objects) */
  -MONGO_EXPORT bson_bool_t bson_iterator_bool( const bson_iterator *i );
  +#define BSON_VERSION (0.6.4)
  +
   
   /**
  - * Get the double value of the BSON object currently pointed to by the
  - * iterator. Assumes the correct type is used.
  + * BSON_VERSION_S:
    *
  - * @param i the bson_iterator
  - *
  - * @return the value of the current BSON object.
  + * BSON version, encoded as a string, useful for printing and
  + * concatenation.
    */
  -/* these assume you are using the right type */
  -double bson_iterator_double_raw( const bson_iterator *i );
  +#define BSON_VERSION_S "0.6.4"
  +
   
   /**
  - * Get the int value of the BSON object currently pointed to by the
  - * iterator. Assumes the correct type is used.
  + * BSON_VERSION_HEX:
    *
  - * @param i the bson_iterator
  - *
  - * @return the value of the current BSON object.
  + * BSON version, encoded as an hexadecimal number, useful for
  + * integer comparisons.
    */
  -int bson_iterator_int_raw( const bson_iterator *i );
  +#define BSON_VERSION_HEX (BSON_MAJOR_VERSION << 24 | \
  +                          BSON_MINOR_VERSION << 16 | \
  +                          BSON_MICRO_VERSION << 8)
  +
   
   /**
  - * Get the long value of the BSON object currently pointed to by the
  - * iterator. Assumes the correct type is used.
  + * BSON_CHECK_VERSION:
  + * @major: required major version
  + * @minor: required minor version
  + * @micro: required micro version
    *
  - * @param i the bson_iterator
  - *
  - * @return the value of the current BSON object.
  + * Compile-time version checking. Evaluates to %TRUE if the version
  + * of BSON is greater than the required one.
    */
  -int64_t bson_iterator_long_raw( const bson_iterator *i );
  +#define BSON_CHECK_VERSION(major,minor,micro)   \
  +        (BSON_MAJOR_VERSION > (major) || \
  +         (BSON_MAJOR_VERSION == (major) && BSON_MINOR_VERSION > (minor)) || \
  +         (BSON_MAJOR_VERSION == (major) && BSON_MINOR_VERSION == (minor) && \
  +          BSON_MICRO_VERSION >= (micro)))
  +
  +/*==============================================================*/
  +/* --- bson-writer.h */
  +
  +BSON_BEGIN_DECLS
  +
   
   /**
  - * Get the bson_bool_t value of the BSON object currently pointed to by the
  - * iterator. Assumes the correct type is used.
  + * bson_writer_t:
    *
  - * @param i the bson_iterator
  + * The bson_writer_t structure is a helper for writing a series of BSON
  + * documents to a single malloc() buffer. You can provide a realloc() style
  + * function to grow the buffer as you go.
    *
  - * @return the value of the current BSON object.
  + * This is useful if you want to build a series of BSON documents right into
  + * the target buffer for an outgoing packet. The offset parameter allows you to
  + * start at an offset of the target buffer.
    */
  -bson_bool_t bson_iterator_bool_raw( const bson_iterator *i )
  -	RPM_GNUC_PURE;
  +typedef struct _bson_writer_t bson_writer_t;
   
  -/**
  - * Get the bson_oid_t value of the BSON object currently pointed to by the
  - * iterator.
  +
  +bson_writer_t *bson_writer_new        (uint8_t           **buf,
  +                                       size_t             *buflen,
  +                                       size_t              offset,
  +                                       bson_realloc_func   realloc_func);
  +void           bson_writer_destroy    (bson_writer_t      *writer);
  +size_t         bson_writer_get_length (bson_writer_t      *writer);
  +bool           bson_writer_begin      (bson_writer_t      *writer,
  +                                       bson_t            **bson);
  +void           bson_writer_end        (bson_writer_t      *writer);
  +void           bson_writer_rollback   (bson_writer_t      *writer);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- b64_ntop.h */
  +
  +/*
  + * Copyright (c) 1996, 1998 by Internet Software Consortium.
    *
  - * @param i the bson_iterator
  + * Permission to use, copy, modify, and distribute this software for any
  + * purpose with or without fee is hereby granted, provided that the above
  + * copyright notice and this permission notice appear in all copies.
    *
  - * @return the value of the current BSON object.
  + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
  + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
  + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
  + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  + * SOFTWARE.
    */
  -MONGO_EXPORT bson_oid_t *bson_iterator_oid( const bson_iterator *i )
  -	RPM_GNUC_PURE;
   
  -/**
  - * Get the string value of the BSON object currently pointed to by the
  - * iterator.
  +/*
  + * Portions Copyright (c) 1995 by International Business Machines, Inc.
  + *
  + * International Business Machines, Inc. (hereinafter called IBM) grants
  + * permission under its copyrights to use, copy, modify, and distribute this
  + * Software with or without fee, provided that the above copyright notice and
  + * all paragraphs of this notice appear in all copies, and that the name of IBM
  + * not be used in connection with the marketing of any product incorporating
  + * the Software or modifications thereof, without specific, written prior
  + * permission.
    *
  - * @param i the bson_iterator
  + * To the extent it has a right to do so, IBM grants an immunity from suit
  + * under its patents, if any, for the use, sale or manufacture of products to
  + * the extent that such products are used for performing Domain Name System
  + * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
  + * granted for any product per se or for any other function of any product.
    *
  - * @return  the value of the current BSON object.
  + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
  + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  + * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
  + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
  + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
  + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
    */
  -/* these can also be used with bson_code and bson_symbol*/
  -MONGO_EXPORT const char *bson_iterator_string( const bson_iterator *i )
  -	RPM_GNUC_PURE;
   
  -/**
  - * Get the string length of the BSON object currently pointed to by the
  - * iterator.
  +#define Assert(Cond) if (!(Cond)) abort ()
  +
  +static const char Base64[] =
  +   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  +static const char Pad64 = '=';
  +
  +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
  + * The following encoding technique is taken from RFC 1521 by Borenstein
  + * and Freed.  It is reproduced here in a slightly edited form for
  + * convenience.
    *
  - * @param i the bson_iterator
  + * A 65-character subset of US-ASCII is used, enabling 6 bits to be
  + * represented per printable character. (The extra 65th character, "=",
  + * is used to signify a special processing function.)
    *
  - * @return the length of the current BSON object.
  - */
  -int bson_iterator_string_len( const bson_iterator *i );
  -
  -/**
  - * Get the code value of the BSON object currently pointed to by the
  - * iterator. Works with bson_code, bson_codewscope, and BSON_STRING
  - * returns NULL for everything else.
  + * The encoding process represents 24-bit groups of input bits as output
  + * strings of 4 encoded characters. Proceeding from left to right, a
  + * 24-bit input group is formed by concatenating 3 8-bit input groups.
  + * These 24 bits are then treated as 4 concatenated 6-bit groups, each
  + * of which is translated into a single digit in the base64 alphabet.
  + *
  + * Each 6-bit group is used as an index into an array of 64 printable
  + * characters. The character referenced by the index is placed in the
  + * output string.
  + *
  + *                       Table 1: The Base64 Alphabet
    *
  - * @param i the bson_iterator
  + *    Value Encoding  Value Encoding  Value Encoding  Value Encoding
  + *        0 A            17 R            34 i            51 z
  + *        1 B            18 S            35 j            52 0
  + *        2 C            19 T            36 k            53 1
  + *        3 D            20 U            37 l            54 2
  + *        4 E            21 V            38 m            55 3
  + *        5 F            22 W            39 n            56 4
  + *        6 G            23 X            40 o            57 5
  + *        7 H            24 Y            41 p            58 6
  + *        8 I            25 Z            42 q            59 7
  + *        9 J            26 a            43 r            60 8
  + *       10 K            27 b            44 s            61 9
  + *       11 L            28 c            45 t            62 +
  + *       12 M            29 d            46 u            63 /
  + *       13 N            30 e            47 v
  + *       14 O            31 f            48 w         (pad) =
  + *       15 P            32 g            49 x
  + *       16 Q            33 h            50 y
    *
  - * @return the code value of the current BSON object.
  + * Special processing is performed if fewer than 24 bits are available
  + * at the end of the data being encoded.  A full encoding quantum is
  + * always completed at the end of a quantity.  When fewer than 24 input
  + * bits are available in an input group, zero bits are added (on the
  + * right) to form an integral number of 6-bit groups.  Padding at the
  + * end of the data is performed using the '=' character.
  + *
  + * Since all base64 input is an integral number of octets, only the
  + * following cases can arise:
  + *
  + *     (1) the final quantum of encoding input is an integral
  + *         multiple of 24 bits; here, the final unit of encoded
  + *    output will be an integral multiple of 4 characters
  + *    with no "=" padding,
  + *     (2) the final quantum of encoding input is exactly 8 bits;
  + *         here, the final unit of encoded output will be two
  + *    characters followed by two "=" padding characters, or
  + *     (3) the final quantum of encoding input is exactly 16 bits;
  + *         here, the final unit of encoded output will be three
  + *    characters followed by one "=" padding character.
    */
  -/* works with bson_code, bson_codewscope, and BSON_STRING */
  -/* returns NULL for everything else */
  -MONGO_EXPORT const char *bson_iterator_code( const bson_iterator *i )
  -	RPM_GNUC_PURE;
   
  -/**
  - * Get the code scope value of the BSON object currently pointed to
  - * by the iterator. Calls bson_init_empty on scope if current object is
  - * not BSON_CODEWSCOPE.
  +static int
  +b64_ntop (uint8_t const *src,
  +          size_t         srclength,
  +          char          *target,
  +          size_t         targsize)
  +{
  +   size_t datalength = 0;
  +   uint8_t input[3];
  +   uint8_t output[4];
  +   size_t i;
  +
  +   while (2 < srclength) {
  +      input[0] = *src++;
  +      input[1] = *src++;
  +      input[2] = *src++;
  +      srclength -= 3;
  +
  +      output[0] = input[0] >> 2;
  +      output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
  +      output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
  +      output[3] = input[2] & 0x3f;
  +      Assert (output[0] < 64);
  +      Assert (output[1] < 64);
  +      Assert (output[2] < 64);
  +      Assert (output[3] < 64);
  +
  +      if (datalength + 4 > targsize) {
  +         return -1;
  +      }
  +      target[datalength++] = Base64[output[0]];
  +      target[datalength++] = Base64[output[1]];
  +      target[datalength++] = Base64[output[2]];
  +      target[datalength++] = Base64[output[3]];
  +   }
  +
  +   /* Now we worry about padding. */
  +   if (0 != srclength) {
  +      /* Get what's left. */
  +      input[0] = input[1] = input[2] = '\0';
  +
  +      for (i = 0; i < srclength; i++) {
  +         input[i] = *src++;
  +      }
  +      output[0] = input[0] >> 2;
  +      output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
  +      output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
  +      Assert (output[0] < 64);
  +      Assert (output[1] < 64);
  +      Assert (output[2] < 64);
  +
  +      if (datalength + 4 > targsize) {
  +         return -1;
  +      }
  +      target[datalength++] = Base64[output[0]];
  +      target[datalength++] = Base64[output[1]];
  +
  +      if (srclength == 1) {
  +         target[datalength++] = Pad64;
  +      } else{
  +         target[datalength++] = Base64[output[2]];
  +      }
  +      target[datalength++] = Pad64;
  +   }
  +
  +   if (datalength >= targsize) {
  +      return -1;
  +   }
  +   target[datalength] = '\0'; /* Returned value doesn't count \0. */
  +   return (int)datalength;
  +}
  +
  +/*==============================================================*/
  +/* --- b64_pton.h */
  +
  +/*
  + * Copyright (c) 1996, 1998 by Internet Software Consortium.
    *
  - * @note When copyData is false, the scope becomes invalid when the
  - *       iterator's data buffer is deallocated. For either value of
  - *       copyData, you must pass the scope object to bson_destroy
  - *       when you are done using it.
  + * Permission to use, copy, modify, and distribute this software for any
  + * purpose with or without fee is hereby granted, provided that the above
  + * copyright notice and this permission notice appear in all copies.
    *
  - * @param i the bson_iterator.
  - * @param scope an uninitialized BSON object to receive the scope.
  - * @param copyData when true, makes a copy of the scope data which will remain
  - *   valid when the iterator's data buffer is deallocated.
  + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
  + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
  + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
  + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  + * SOFTWARE.
    */
  -MONGO_EXPORT void bson_iterator_code_scope_init( const bson_iterator *i, bson \
*scope, bson_bool_t copyData );  
  -/**
  - * Get the date value of the BSON object currently pointed to by the
  - * iterator.
  +/*
  + * Portions Copyright (c) 1995 by International Business Machines, Inc.
    *
  - * @param i the bson_iterator
  + * International Business Machines, Inc. (hereinafter called IBM) grants
  + * permission under its copyrights to use, copy, modify, and distribute this
  + * Software with or without fee, provided that the above copyright notice and
  + * all paragraphs of this notice appear in all copies, and that the name of IBM
  + * not be used in connection with the marketing of any product incorporating
  + * the Software or modifications thereof, without specific, written prior
  + * permission.
    *
  - * @return the date value of the current BSON object.
  + * To the extent it has a right to do so, IBM grants an immunity from suit
  + * under its patents, if any, for the use, sale or manufacture of products to
  + * the extent that such products are used for performing Domain Name System
  + * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
  + * granted for any product per se or for any other function of any product.
  + *
  + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
  + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  + * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
  + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
  + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
  + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
    */
  -/* both of these only work with bson_date */
  -MONGO_EXPORT bson_date_t bson_iterator_date( const bson_iterator *i );
  +
  +#ifdef	DYING
  +#define Assert(Cond) if (!(Cond)) abort()
  +
  +static const char Base64[] =
  +	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  +static const char Pad64 = '=';
  +#endif
  +
  +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
  +   The following encoding technique is taken from RFC 1521 by Borenstein
  +   and Freed.  It is reproduced here in a slightly edited form for
  +   convenience.
  +
  +   A 65-character subset of US-ASCII is used, enabling 6 bits to be
  +   represented per printable character. (The extra 65th character, "=",
  +   is used to signify a special processing function.)
  +
  +   The encoding process represents 24-bit groups of input bits as output
  +   strings of 4 encoded characters. Proceeding from left to right, a
  +   24-bit input group is formed by concatenating 3 8-bit input groups.
  +   These 24 bits are then treated as 4 concatenated 6-bit groups, each
  +   of which is translated into a single digit in the base64 alphabet.
  +
  +   Each 6-bit group is used as an index into an array of 64 printable
  +   characters. The character referenced by the index is placed in the
  +   output string.
  +
  +                         Table 1: The Base64 Alphabet
  +
  +      Value Encoding  Value Encoding  Value Encoding  Value Encoding
  +          0 A            17 R            34 i            51 z
  +          1 B            18 S            35 j            52 0
  +          2 C            19 T            36 k            53 1
  +          3 D            20 U            37 l            54 2
  +          4 E            21 V            38 m            55 3
  +          5 F            22 W            39 n            56 4
  +          6 G            23 X            40 o            57 5
  +          7 H            24 Y            41 p            58 6
  +          8 I            25 Z            42 q            59 7
  +          9 J            26 a            43 r            60 8
  +         10 K            27 b            44 s            61 9
  +         11 L            28 c            45 t            62 +
  +         12 M            29 d            46 u            63 /
  +         13 N            30 e            47 v
  +         14 O            31 f            48 w         (pad) =
  +         15 P            32 g            49 x
  +         16 Q            33 h            50 y
  +
  +   Special processing is performed if fewer than 24 bits are available
  +   at the end of the data being encoded.  A full encoding quantum is
  +   always completed at the end of a quantity.  When fewer than 24 input
  +   bits are available in an input group, zero bits are added (on the
  +   right) to form an integral number of 6-bit groups.  Padding at the
  +   end of the data is performed using the '=' character.
  +
  +   Since all base64 input is an integral number of octets, only the
  +   following cases can arise:
  +
  +       (1) the final quantum of encoding input is an integral
  +           multiple of 24 bits; here, the final unit of encoded
  +	   output will be an integral multiple of 4 characters
  +	   with no "=" padding,
  +       (2) the final quantum of encoding input is exactly 8 bits;
  +           here, the final unit of encoded output will be two
  +	   characters followed by two "=" padding characters, or
  +       (3) the final quantum of encoding input is exactly 16 bits;
  +           here, the final unit of encoded output will be three
  +	   characters followed by one "=" padding character.
  +   */
  +
  +/* skips all whitespace anywhere.
  +   converts characters, four at a time, starting at (or after)
  +   src from base - 64 numbers into three 8 bit bytes in the target area.
  +   it returns the number of data bytes stored at the target, or -1 on error.
  + */
  +
  +static int b64rmap_initialized = 0;
  +static uint8_t b64rmap[256];
  +
  +static const uint8_t b64rmap_special = 0xf0;
  +static const uint8_t b64rmap_end = 0xfd;
  +static const uint8_t b64rmap_space = 0xfe;
  +static const uint8_t b64rmap_invalid = 0xff;
  +
  +/**
  + * Initializing the reverse map is not thread safe.
  + * Which is fine for NSD. For now...
  + **/
  +static void
  +b64_initialize_rmap ()
  +{
  +	int i;
  +	unsigned char ch;
  +
  +	/* Null: end of string, stop parsing */
  +	b64rmap[0] = b64rmap_end;
  +
  +	for (i = 1; i < 256; ++i) {
  +		ch = (unsigned char)i;
  +		/* Whitespaces */
  +		if (isspace(ch))
  +			b64rmap[i] = b64rmap_space;
  +		/* Padding: stop parsing */
  +		else if (ch == Pad64)
  +			b64rmap[i] = b64rmap_end;
  +		/* Non-base64 char */
  +		else
  +			b64rmap[i] = b64rmap_invalid;
  +	}
  +
  +	/* Fill reverse mapping for base64 chars */
  +	for (i = 0; Base64[i] != '\0'; ++i)
  +		b64rmap[(uint8_t)Base64[i]] = i;
  +
  +	b64rmap_initialized = 1;
  +}
  +
  +static int
  +b64_pton_do(char const *src, uint8_t *target, size_t targsize)
  +{
  +	int tarindex, state, ch;
  +	uint8_t ofs;
  +
  +	state = 0;
  +	tarindex = 0;
  +
  +	while (1)
  +	{
  +		ch = *src++;
  +		ofs = b64rmap[ch];
  +
  +		if (ofs >= b64rmap_special) {
  +			/* Ignore whitespaces */
  +			if (ofs == b64rmap_space)
  +				continue;
  +			/* End of base64 characters */
  +			if (ofs == b64rmap_end)
  +				break;
  +			/* A non-base64 character. */
  +			return (-1);
  +		}
  +
  +		switch (state) {
  +		case 0:
  +			if ((size_t)tarindex >= targsize)
  +				return (-1);
  +			target[tarindex] = ofs << 2;
  +			state = 1;
  +			break;
  +		case 1:
  +			if ((size_t)tarindex + 1 >= targsize)
  +				return (-1);
  +			target[tarindex]   |=  ofs >> 4;
  +			target[tarindex+1]  = (ofs & 0x0f)
  +						<< 4 ;
  +			tarindex++;
  +			state = 2;
  +			break;
  +		case 2:
  +			if ((size_t)tarindex + 1 >= targsize)
  +				return (-1);
  +			target[tarindex]   |=  ofs >> 2;
  +			target[tarindex+1]  = (ofs & 0x03)
  +						<< 6;
  +			tarindex++;
  +			state = 3;
  +			break;
  +		case 3:
  +			if ((size_t)tarindex >= targsize)
  +				return (-1);
  +			target[tarindex] |= ofs;
  +			tarindex++;
  +			state = 0;
  +			break;
  +		default:
  +			abort();
  +		}
  +	}
  +
  +	/*
  +	 * We are done decoding Base-64 chars.  Let's see if we ended
  +	 * on a byte boundary, and/or with erroneous trailing characters.
  +	 */
  +
  +	if (ch == Pad64) {		/* We got a pad char. */
  +		ch = *src++;		/* Skip it, get next. */
  +		switch (state) {
  +		case 0:		/* Invalid = in first position */
  +		case 1:		/* Invalid = in second position */
  +			return (-1);
  +
  +		case 2:		/* Valid, means one byte of info */
  +			/* Skip any number of spaces. */
  +			for ((void)NULL; ch != '\0'; ch = *src++)
  +				if (b64rmap[ch] != b64rmap_space)
  +					break;
  +			/* Make sure there is another trailing = sign. */
  +			if (ch != Pad64)
  +				return (-1);
  +			ch = *src++;		/* Skip the = */
  +			/* Fall through to "single trailing =" case. */
  +			/* FALLTHROUGH */
  +
  +		case 3:		/* Valid, means two bytes of info */
  +			/*
  +			 * We know this char is an =.  Is there anything but
  +			 * whitespace after it?
  +			 */
  +			for ((void)NULL; ch != '\0'; ch = *src++)
  +				if (b64rmap[ch] != b64rmap_space)
  +					return (-1);
  +
  +			/*
  +			 * Now make sure for cases 2 and 3 that the "extra"
  +			 * bits that slopped past the last full byte were
  +			 * zeros.  If we don't check them, they become a
  +			 * subliminal channel.
  +			 */
  +			if (target[tarindex] != 0)
  +				return (-1);
  +		default:
  +			break;
  +		}
  +	} else {
  +		/*
  +		 * We ended by seeing the end of the string.  Make sure we
  +		 * have no partial bytes lying around.
  +		 */
  +		if (state != 0)
  +			return (-1);
  +	}
  +
  +	return (tarindex);
  +}
  +
  +
  +static int
  +b64_pton_len(char const *src)
  +{
  +	int tarindex, state, ch;
  +	uint8_t ofs;
  +
  +	state = 0;
  +	tarindex = 0;
  +
  +	while (1)
  +	{
  +		ch = *src++;
  +		ofs = b64rmap[ch];
  +
  +		if (ofs >= b64rmap_special) {
  +			/* Ignore whitespaces */
  +			if (ofs == b64rmap_space)
  +				continue;
  +			/* End of base64 characters */
  +			if (ofs == b64rmap_end)
  +				break;
  +			/* A non-base64 character. */
  +			return (-1);
  +		}
  +
  +		switch (state) {
  +		case 0:
  +			state = 1;
  +			break;
  +		case 1:
  +			tarindex++;
  +			state = 2;
  +			break;
  +		case 2:
  +			tarindex++;
  +			state = 3;
  +			break;
  +		case 3:
  +			tarindex++;
  +			state = 0;
  +			break;
  +		default:
  +			abort();
  +		}
  +	}
  +
  +	/*
  +	 * We are done decoding Base-64 chars.  Let's see if we ended
  +	 * on a byte boundary, and/or with erroneous trailing characters.
  +	 */
  +
  +	if (ch == Pad64) {		/* We got a pad char. */
  +		ch = *src++;		/* Skip it, get next. */
  +		switch (state) {
  +		case 0:		/* Invalid = in first position */
  +		case 1:		/* Invalid = in second position */
  +			return (-1);
  +
  +		case 2:		/* Valid, means one byte of info */
  +			/* Skip any number of spaces. */
  +			for ((void)NULL; ch != '\0'; ch = *src++)
  +				if (b64rmap[ch] != b64rmap_space)
  +					break;
  +			/* Make sure there is another trailing = sign. */
  +			if (ch != Pad64)
  +				return (-1);
  +			ch = *src++;		/* Skip the = */
  +			/* Fall through to "single trailing =" case. */
  +			/* FALLTHROUGH */
  +
  +		case 3:		/* Valid, means two bytes of info */
  +			/*
  +			 * We know this char is an =.  Is there anything but
  +			 * whitespace after it?
  +			 */
  +			for ((void)NULL; ch != '\0'; ch = *src++)
  +				if (b64rmap[ch] != b64rmap_space)
  +					return (-1);
  +
  +		default:
  +			break;
  +		}
  +	} else {
  +		/*
  +		 * We ended by seeing the end of the string.  Make sure we
  +		 * have no partial bytes lying around.
  +		 */
  +		if (state != 0)
  +			return (-1);
  +	}
  +
  +	return (tarindex);
  +}
  +
  +
  +static int
  +b64_pton(char const *src, uint8_t *target, size_t targsize)
  +{
  +	if (!b64rmap_initialized)
  +		b64_initialize_rmap ();
  +
  +	if (target)
  +		return b64_pton_do (src, target, targsize);
  +	else
  +		return b64_pton_len (src);
  +}
  +
  +/*==============================================================*/
  +/* --- bson-private.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +typedef enum
  +{
  +   BSON_FLAG_NONE = 0,
  +   BSON_FLAG_INLINE = (1 << 0),
  +   BSON_FLAG_STATIC = (1 << 1),
  +   BSON_FLAG_RDONLY = (1 << 2),
  +   BSON_FLAG_CHILD = (1 << 3),
  +   BSON_FLAG_IN_CHILD = (1 << 4),
  +   BSON_FLAG_NO_FREE = (1 << 5),
  +} bson_flags_t;
  +
  +
  +BSON_ALIGNED_BEGIN (128)
  +typedef struct
  +{
  +   bson_flags_t flags;
  +   uint32_t len;
  +   uint8_t data[120];
  +} bson_impl_inline_t
  +BSON_ALIGNED_END (128);
  +
  +
  +BSON_STATIC_ASSERT (sizeof (bson_impl_inline_t) == 128);
  +
  +
  +BSON_ALIGNED_BEGIN (128)
  +typedef struct
  +{
  +   bson_flags_t flags;           /* flags describing the bson_t */
  +   uint32_t len;            /* length of bson document in bytes */
  +   bson_t *parent;               /* parent bson if a child */
  +   uint32_t depth;          /* Subdocument depth. */
  +   uint8_t **buf;           /* pointer to buffer pointer */
  +   size_t *buflen;               /* pointer to buffer length */
  +   size_t offset;                /* our offset inside *buf  */
  +   uint8_t *alloc;          /* buffer that we own. */
  +   size_t alloclen;              /* length of buffer that we own. */
  +   bson_realloc_func realloc;    /* our realloc implementation */
  +} bson_impl_alloc_t
  +BSON_ALIGNED_END (128);
  +
  +
  +BSON_STATIC_ASSERT (sizeof (bson_impl_alloc_t) <= 128);
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-context-private.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +struct _bson_context_t
  +{
  +   bson_context_flags_t flags : 7;
  +   bool                 pidbe_once : 1;
  +   uint8_t              pidbe[2];
  +   uint8_t              md5[3];
  +   uint32_t             seq32;
  +   uint64_t             seq64;
  +#if defined WITH_OID32_PT
  +   bson_mutex_t         _m32;
  +#endif
  +#if defined WITH_OID64_PT
  +   bson_mutex_t        _m64;
  +#endif
  +
  +   void (*oid_get_host)  (bson_context_t *context,
  +                          bson_oid_t     *oid);
  +   void (*oid_get_pid)   (bson_context_t *context,
  +                          bson_oid_t     *oid);
  +   void (*oid_get_seq32) (bson_context_t *context,
  +                          bson_oid_t     *oid);
  +   void (*oid_get_seq64) (bson_context_t *context,
  +                          bson_oid_t     *oid);
  +};
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson-thread-private.h */
  +
  +BSON_BEGIN_DECLS
  +
  +
  +#if defined(BSON_OS_UNIX)
  +#  include <pthread.h>
  +#  define bson_mutex_t                    pthread_mutex_t
  +#  define bson_mutex_init(_n)             pthread_mutex_init((_n), NULL)
  +#  define bson_mutex_lock                 pthread_mutex_lock
  +#  define bson_mutex_unlock               pthread_mutex_unlock
  +#  define bson_mutex_destroy              pthread_mutex_destroy
  +#  define bson_thread_t                   pthread_t
  +#  define bson_thread_create(_t,_f,_d)    pthread_create((_t), NULL, (_f), (_d))
  +#  define bson_thread_join(_n)            pthread_join((_n), NULL)
  +#  define bson_once_t                     pthread_once_t
  +#  define bson_once                       pthread_once
  +#  define BSON_ONCE_FUN(n)                void n(void)
  +#  define BSON_ONCE_RETURN                return
  +#  ifdef _PTHREAD_ONCE_INIT_NEEDS_BRACES
  +#    define BSON_ONCE_INIT                {PTHREAD_ONCE_INIT}
  +#  else
  +#    define BSON_ONCE_INIT                PTHREAD_ONCE_INIT
  +#  endif
  +#else
  +#  define bson_mutex_t                    CRITICAL_SECTION
  +#  define bson_mutex_init                 InitializeCriticalSection
  +#  define bson_mutex_lock                 EnterCriticalSection
  +#  define bson_mutex_unlock               LeaveCriticalSection
  +#  define bson_mutex_destroy              DeleteCriticalSection
  +#  define bson_thread_t                   HANDLE
  +#  define bson_thread_create(_t,_f,_d)    (!(*(_t) = \
CreateThread(NULL,0,(void*)_f,_d,0,NULL)))  +#  define bson_thread_join(_n)           \
WaitForSingleObject((_n), INFINITE)  +#  define bson_once_t                     \
INIT_ONCE  +#  define BSON_ONCE_INIT                  INIT_ONCE_STATIC_INIT
  +#  define bson_once(o, c)                 InitOnceExecuteOnce(o, c, NULL, NULL)
  +#  define BSON_ONCE_FUN(n)                BOOL CALLBACK n(PINIT_ONCE _ignored_a, \
PVOID _ignored_b, PVOID *_ignored_c)  +#  define BSON_ONCE_RETURN                \
return true  +#endif
  +
  +
  +BSON_END_DECLS
  +
  +/*==============================================================*/
  +/* --- bson.h */
   
   /**
  - * Get the time value of the BSON object currently pointed to by the
  - * iterator.
  + * bson_empty:
  + * @b: a bson_t.
    *
  - * @param i the bson_iterator
  - *
  - * @return the time value of the current BSON object.
  + * Checks to see if @b is an empty BSON document. An empty BSON document is
  + * a 5 byte document which contains the length (4 bytes) and a single NUL
  + * byte indicating end of fields.
    */
  -MONGO_EXPORT time_t bson_iterator_time_t( const bson_iterator *i );
  +#define bson_empty(b) (((b)->len == 5) || !bson_get_data ((b))[4])
  +
   
   /**
  - * Get the length of the BSON binary object currently pointed to by the
  - * iterator.
  + * bson_empty0:
    *
  - * @param i the bson_iterator
  - *
  - * @return the length of the current BSON binary object.
  + * Like bson_empty() but treats NULL the same as an empty bson_t document.
    */
  -MONGO_EXPORT int bson_iterator_bin_len( const bson_iterator *i );
  +#define bson_empty0(b) (!(b) || bson_empty (b))
  +
   
   /**
  - * Get the type of the BSON binary object currently pointed to by the
  - * iterator.
  + * bson_clear:
    *
  - * @param i the bson_iterator
  + * Easily free a bson document and set it to NULL. Use like:
    *
  - * @return the type of the current BSON binary object.
  + * bson_t *doc = bson_new();
  + * bson_clear (&doc);
  + * assert (doc == NULL);
    */
  -MONGO_EXPORT char bson_iterator_bin_type( const bson_iterator *i )
  -	RPM_GNUC_PURE;
  +#define bson_clear(bptr) \
  +   do { \
  +      if (*(bptr)) { \
  +         bson_destroy (*(bptr)); \
  +         *(bptr) = NULL; \
  +      } \
  +   } while (0)
  +
   
   /**
  - * Get the value of the BSON binary object currently pointed to by the
  - * iterator.
  - *
  - * @param i the bson_iterator
  + * BSON_MAX_SIZE:
    *
  - * @return the value of the current BSON binary object.
  + * The maximum size in bytes of a BSON document.
    */
  -MONGO_EXPORT const char *bson_iterator_bin_data( const bson_iterator *i )
  -	RPM_GNUC_PURE;
  +#define BSON_MAX_SIZE ((size_t)((1U << 31) - 1))
  +
  +
  +#define BSON_APPEND_ARRAY(b,key,val) \
  +      bson_append_array (b, key, (int)strlen (key), val)
  +
  +#define BSON_APPEND_BINARY(b,key,subtype,val,len) \
  +      bson_append_binary (b, key, (int) strlen (key), subtype, val, len)
  +
  +#define BSON_APPEND_BOOL(b,key,val) \
  +      bson_append_bool (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_CODE(b,key,val) \
  +      bson_append_code (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_CODE_WITH_SCOPE(b,key,val,scope) \
  +      bson_append_code_with_scope (b, key, (int) strlen (key), val, scope)
  +
  +#define BSON_APPEND_DOUBLE(b,key,val) \
  +      bson_append_double (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_DOCUMENT(b,key,val) \
  +      bson_append_document (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_INT32(b,key,val) \
  +      bson_append_int32 (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_INT64(b,key,val) \
  +      bson_append_int64 (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_MINKEY(b,key) \
  +      bson_append_minkey (b, key, (int) strlen (key))
  +
  +#define BSON_APPEND_MAXKEY(b,key) \
  +      bson_append_maxkey (b, key, (int) strlen (key))
  +
  +#define BSON_APPEND_NULL(b,key) \
  +      bson_append_null (b, key, (int) strlen (key))
  +
  +#define BSON_APPEND_OID(b,key,val) \
  +      bson_append_oid (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_REGEX(b,key,val,opt) \
  +      bson_append_regex (b, key, (int) strlen (key), val, opt)
  +
  +#define BSON_APPEND_UTF8(b,key,val) \
  +      bson_append_utf8 (b, key, (int) strlen (key), val, (int) strlen (val))
  +
  +#define BSON_APPEND_SYMBOL(b,key,val) \
  +      bson_append_symbol (b, key, (int) strlen (key), val, (int) strlen (val))
  +
  +#define BSON_APPEND_TIME_T(b,key,val) \
  +      bson_append_time_t (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_TIMEVAL(b,key,val) \
  +      bson_append_timeval (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_DATE_TIME(b,key,val) \
  +      bson_append_date_time (b, key, (int) strlen (key), val)
  +
  +#define BSON_APPEND_TIMESTAMP(b,key,val,inc) \
  +      bson_append_timestamp (b, key, (int) strlen (key), val, inc)
  +
  +#define BSON_APPEND_UNDEFINED(b,key) \
  +      bson_append_undefined (b, key, (int) strlen (key))
  +
   
   /**
  - * Get the value of the BSON regex object currently pointed to by the
  - * iterator.
  + * bson_new:
    *
  - * @param i the bson_iterator
  + * Allocates a new bson_t structure. Call the various bson_append_*()
  + * functions to add fields to the bson. You can iterate the bson_t at any
  + * time using a bson_iter_t and bson_iter_init().
    *
  - * @return the value of the current BSON regex object.
  + * Returns: A newly allocated bson_t that should be freed with bson_destroy().
    */
  -MONGO_EXPORT const char *bson_iterator_regex( const bson_iterator *i )
  -	RPM_GNUC_PURE;
  +bson_t *
  +bson_new (void);
  +
  +
  +bson_t *
  +bson_new_from_json (const uint8_t *data,
  +                    size_t         len,
  +                    bson_error_t  *error);
  +
  +
  +bool
  +bson_init_from_json (bson_t        *bson,
  +                     const char    *data,
  +                     ssize_t        len,
  +                     bson_error_t  *error);
  +
   
   /**
  - * Get the options of the BSON regex object currently pointed to by the
  - * iterator.
  + * bson_init_static:
  + * @b: A pointer to a bson_t.
  + * @data: The data buffer to use.
  + * @length: The length of @data.
    *
  - * @param i the bson_iterator.
  + * Initializes a bson_t using @data and @length. This is ideal if you would
  + * like to use a stack allocation for your bson and do not need to grow the
  + * buffer. @data must be valid for the life of @b.
    *
  - * @return the options of the current BSON regex object.
  + * Returns: true if initialized successfully; otherwise false.
    */
  -MONGO_EXPORT const char *bson_iterator_regex_opts( const bson_iterator *i )
  -	RPM_GNUC_PURE;
  +bool
  +bson_init_static (bson_t             *b,
  +                  const uint8_t *data,
  +                  uint32_t       length);
  +
   
  -/* these work with BSON_OBJECT and BSON_ARRAY */
   /**
  - * Get the BSON subobject currently pointed to by the
  - * iterator.
  + * bson_init:
  + * @b: A pointer to a bson_t.
    *
  - * @note When copyData is 0, the subobject becomes invalid when its parent's
  - *       data buffer is deallocated. For either value of copyData, you must
  - *       pass the subobject to bson_destroy when you are done using it.
  + * Initializes a bson_t for use. This function is useful to those that want a
  + * stack allocated bson_t. The usefulness of a stack allocated bson_t is
  + * marginal as the target buffer for content will still require heap
  + * allocations. It can help reduce heap fragmentation on allocators that do
  + * not employ SLAB/magazine semantics.
    *
  - * @param i the bson_iterator.
  - * @param sub an unitialized BSON object which will become the new subobject.
  + * You must call bson_destroy() with @b to release resources when you are done
  + * using @b.
    */
  -MONGO_EXPORT void bson_iterator_subobject_init( const bson_iterator *i, bson *sub, \
bson_bool_t copyData );  +void
  +bson_init (bson_t *b);
  +
   
   /**
  - * Get a bson_iterator that on the BSON subobject.
  + * bson_reinit:
  + * @b: (inout): A bson_t.
    *
  - * @param i the bson_iterator.
  - * @param sub the iterator to point at the BSON subobject.
  + * This is equivalent to calling bson_destroy() and bson_init() on a #bson_t.
  + * However, it will try to persist the existing malloc'd buffer if one exists.
  + * This is useful in cases where you want to reduce malloc overhead while
  + * building many documents.
    */
  -MONGO_EXPORT void bson_iterator_subiterator( const bson_iterator *i, bson_iterator \
*sub );  +void
  +bson_reinit (bson_t *b);
  +
   
  -/* str must be at least 24 hex chars + null byte */
   /**
  - * Create a bson_oid_t from a string.
  + * bson_new_from_data:
  + * @data: A buffer containing a serialized bson document.
  + * @length: The length of the document in bytes.
  + *
  + * Creates a new bson_t structure using the data provided. @data should contain
  + * at least @length bytes that can be copied into the new bson_t structure.
    *
  - * @param oid the bson_oid_t destination.
  - * @param str a null terminated string comprised of at least 24 hex chars.
  + * Returns: A newly allocate bson_t that should be freed with bson_destroy().
  + *   If the first four bytes (little-endian) of data do not match @length,
  + *   then NULL will be returned.
    */
  -MONGO_EXPORT void bson_oid_from_string( bson_oid_t *oid, const char *str );
  +bson_t *
  +bson_new_from_data (const uint8_t *data,
  +                    uint32_t       length);
  +
   
   /**
  - * Create a string representation of the bson_oid_t.
  + * bson_sized_new:
  + * @size: A size_t containing the number of bytes to allocate.
  + *
  + * This will allocate a new bson_t with enough bytes to hold a buffer
  + * sized @size. @size must be smaller than INT_MAX bytes.
    *
  - * @param oid the bson_oid_t source.
  - * @param str the string representation destination.
  + * Returns: A newly allocated bson_t that should be freed with bson_destroy().
    */
  -MONGO_EXPORT void bson_oid_to_string( const bson_oid_t *oid, char *str );
  +bson_t *
  +bson_sized_new (size_t size);
  +
   
   /**
  - * Create a bson_oid object.
  + * bson_copy:
  + * @bson: A bson_t.
  + *
  + * Copies @bson into a newly allocated bson_t. You must call bson_destroy()
  + * when you are done with the resulting value to free its resources.
    *
  - * @param oid the destination for the newly created bson_oid_t.
  + * Returns: A newly allocated bson_t that should be free'd with bson_destroy()
    */
  -MONGO_EXPORT void bson_oid_gen( bson_oid_t *oid );
  +bson_t *
  +bson_copy (const bson_t *bson);
  +
   
   /**
  - * Set a function to be used to generate the second four bytes
  - * of an object id.
  + * bson_copy_to:
  + * @src: The source bson_t.
  + * @dst: The destination bson_t.
    *
  - * @param func a pointer to a function that returns an int.
  + * Initializes @dst and copies the content from @src into @dst.
    */
  -MONGO_EXPORT void bson_set_oid_fuzz( int ( *func )( void ) );
  +void
  +bson_copy_to (const bson_t *src,
  +              bson_t       *dst);
  +
   
   /**
  - * Set a function to be used to generate the incrementing part
  - * of an object id (last four bytes). If you need thread-safety
  - * in generating object ids, you should set this function.
  + * bson_copy_to_excluding:
  + * @src: A bson_t.
  + * @dst: A bson_t to initialize and copy into.
  + * @first_exclude: First field name to exclude.
    *
  - * @param func a pointer to a function that returns an int.
  + * Copies @src into @dst excluding any field that is provided.
  + * This is handy for situations when you need to remove one or
  + * more fields in a bson_t.
    */
  -MONGO_EXPORT void bson_set_oid_inc( int ( *func )( void ) );
  +void
  +bson_copy_to_excluding (const bson_t *src,
  +                        bson_t       *dst,
  +                        const char   *first_exclude,
  +                        ...) BSON_GNUC_NULL_TERMINATED;
  +
   
   /**
  - * Get the time a bson_oid_t was created.
  + * bson_destroy:
  + * @bson: A bson_t.
    *
  - * @param oid the bson_oid_t.
  + * Frees the resources associated with @bson.
    */
  -MONGO_EXPORT time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time \
the OID was created */  +void
  +bson_destroy (bson_t *bson);
   
  -/* ----------------------------
  -   BUILDING
  -   ------------------------------ */
   
   /**
  - * Initialize a BSON object for building and allocate a data buffer.
  - *
  - * @note You must initialize each new bson object using this,
  - *  bson_init_finished_data( ), or one of the other init functions.
  - *  When done using the BSON object, you must pass it to bson_destroy( ).
  + * bson_get_data:
  + * @bson: A bson_t.
    *
  - * @param b the BSON object to initialize.
  + * Fetched the data buffer for @bson of @bson->len bytes in length.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: A buffer that should not be modified or freed.
    */
  -MONGO_EXPORT int bson_init( bson *b );
  +const uint8_t *
  +bson_get_data (const bson_t *bson);
  +
   
   /**
  - * Initialize a BSON object for building and allocate a data buffer
  - * of a given size.
  - *
  - * @note When done using the bson object, you must pass it
  - *  to bson_destroy( ).
  + * bson_count_keys:
  + * @bson: A bson_t.
    *
  - * @param b the BSON object to initialize.
  - * @param size the initial size of the buffer.
  - *
  - * @return BSON_OK or BSON_ERROR.
  + * Counts the number of elements found in @bson.
    */
  -int bson_init_size( bson *b, int size );
  +uint32_t
  +bson_count_keys (const bson_t *bson);
  +
   
   /**
  - * Initialize a BSON object for building, using the provided char*
  - * of the given size. When ownsData is true, the BSON object may
  - * reallocate the data block as needed, and bson_destroy will free
  - * it.
  + * bson_has_field:
  + * @bson: A bson_t.
  + * @key: The key to lookup.
    *
  - * See also bson_init_finished_data( )
  + * Checks to see if @bson contains a field named @key.
    *
  - * @note When done using the BSON object, you must pass
  - *      it to bson_destroy( ). 
  + * This function is case-sensitive.
    *
  - * @param b the BSON object to initialize.
  - * @param data the raw BSON data.
  - * @param dataSize no. of octets
  - * @param ownsData when true, bson_ensure_space() may reallocate the block and
  - *   bson_destroy() will free it
  - *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if @key exists in @bson; otherwise false.
    */
  -int bson_init_unfinished_data( bson *b, char *data, int dataSize, bson_bool_t \
ownsData );  +bool
  +bson_has_field (const bson_t *bson,
  +                const char   *key);
  +
   
   /**
  - * Grow a bson object.
  + * bson_compare:
  + * @bson: A bson_t.
  + * @other: A bson_t.
    *
  - * @param b the bson to grow.
  - * @param bytesNeeded the additional number of bytes needed.
  + * Compares @bson to @other in a qsort() style comparison.
  + * See qsort() for information on how this function works.
    *
  - * @return BSON_OK or BSON_ERROR with the bson error object set.
  - *   Exits if allocation fails.
  + * Returns: Less than zero, zero, or greater than zero.
    */
  -int bson_ensure_space( bson *b, const size_t bytesNeeded );
  +int
  +bson_compare (const bson_t *bson,
  +              const bson_t *other);
   
  -/**
  - * Finalize a bson object.
  +/*
  + * bson_compare:
  + * @bson: A bson_t.
  + * @other: A bson_t.
    *
  - * @param b the bson object to finalize.
  + * Checks to see if @bson and @other are equal.
    *
  - * @return the standard error code. To deallocate memory,
  - *   call bson_destroy on the bson object.
  + * Returns: true if equal; otherwise false.
    */
  -MONGO_EXPORT int bson_finish( bson *b );
  +bool
  +bson_equal (const bson_t *bson,
  +            const bson_t *other);
  +
   
   /**
  - * Destroy a bson object and deallocate its data buffer.
  + * bson_validate:
  + * @bson: A bson_t.
  + * @offset: A location for the error offset.
    *
  - * @param b the bson object to destroy.
  + * Validates a BSON document by walking through the document and inspecting
  + * the fields for valid content.
    *
  + * Returns: true if @bson is valid; otherwise false and @offset is set.
    */
  -MONGO_EXPORT void bson_destroy( bson *b );
  +bool
  +bson_validate (const bson_t         *bson,
  +               bson_validate_flags_t flags,
  +               size_t               *offset);
  +
   
   /**
  - * Initialize a BSON object to an emoty object with a shared, static data
  - * buffer.
  + * bson_as_json:
  + * @bson: A bson_t.
  + * @length: A location for the string length, or NULL.
    *
  - * @note You must NOT modify this object's data. It is safe though not
  - * required to call bson_destroy( ) on this object.
  + * Creates a new string containing @bson in extended JSON format. The caller
  + * is responsible for freeing the resulting string. If @length is non-NULL,
  + * then the length of the resulting string will be placed in @length.
    *
  - * @param obj the BSON object to initialize.
  + * See http://docs.mongodb.org/manual/reference/mongodb-extended-json/ for
  + * more information on extended JSON.
    *
  - * @return BSON_OK
  + * Returns: A newly allocated string that should be freed with bson_free().
    */
  -MONGO_EXPORT bson_bool_t bson_init_empty( bson *obj );
  +char *
  +bson_as_json (const bson_t *bson,
  +              size_t       *length);
  +
   
   /**
  - * Return a pointer to an empty, shared, static BSON object.
  + * bson_append_array:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @array: A bson_t containing the array.
    *
  - * @note This object is owned by the driver. You must NOT modify it
  - * and must NOT call bson_destroy( ) on it.
  + * Appends a BSON array to @bson. BSON arrays are like documents where the
  + * key is the string version of the index. For example, the first item of the
  + * array would have the key "0". The second item would have the index "1".
    *
  - * @return the shared initialized BSON object.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT const bson *bson_shared_empty( void )
  -	RPM_GNUC_CONST;
  +bool
  +bson_append_array (bson_t       *bson,
  +                   const char   *key,
  +                   int           key_length,
  +                   const bson_t *array);
   
  -/**
  - * Make a complete copy of the a BSON object.
  - * The source bson object must be in a finished
  - * state; otherwise, the copy will fail.
  - *
  - * @param out the copy destination BSON object.
  - * @param in the copy source BSON object.
  - */
  -MONGO_EXPORT int bson_copy( bson *out, const bson *in ); /* puts data in new \
buffer. NOOP if out==NULL */  
   /**
  - * Append a previously created bson_oid_t to a bson object.
  + * bson_append_binary:
  + * @bson: A bson_t to append.
  + * @key: The key for the field.
  + * @subtype: The bson_subtype_t of the binary.
  + * @binary: The binary buffer to append.
  + * @length: The length of @binary.
    *
  - * @param b the bson to append to.
  - * @param name the key for the bson_oid_t.
  - * @param oid the bson_oid_t to append.
  + * Appends a binary buffer to the BSON document.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid \
);  +bool
  +bson_append_binary (bson_t             *bson,
  +                    const char         *key,
  +                    int                 key_length,
  +                    bson_subtype_t      subtype,
  +                    const uint8_t *binary,
  +                    uint32_t       length);
   
  -/**
  - * Append a bson_oid_t to a bson.
  - *
  - * @param b the bson to append to.
  - * @param name the key for the bson_oid_t.
  - *
  - * @return BSON_OK or BSON_ERROR.
  - */
  -MONGO_EXPORT int bson_append_new_oid( bson *b, const char *name );
   
   /**
  - * Append an int to a bson.
  + * bson_append_bool:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @value: The boolean value.
    *
  - * @param b the bson to append to.
  - * @param name the key for the int.
  - * @param i the int to append.
  + * Appends a new field to @bson of type BSON_TYPE_BOOL.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_int( bson *b, const char *name, const int i );
  +bool
  +bson_append_bool (bson_t     *bson,
  +                  const char *key,
  +                  int         key_length,
  +                  bool value);
   
  -/**
  - * Append an long to a bson.
  - *
  - * @param b the bson to append to.
  - * @param name the key for the long.
  - * @param i the long to append.
  - *
  - * @return BSON_OK or BSON_ERROR.
  - */
  -MONGO_EXPORT int bson_append_long( bson *b, const char *name, const int64_t i );
   
   /**
  - * Append an double to a bson.
  + * bson_append_code:
  + * @bson: A bson_t.
  + * @key: The key for the document.
  + * @javascript: JavaScript code to be executed.
    *
  - * @param b the bson to append to.
  - * @param name the key for the double.
  - * @param d the double to append.
  + * Appends a field of type BSON_TYPE_CODE to the BSON document. @javascript
  + * should contain a script in javascript to be executed.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_double( bson *b, const char *name, const double d );
  +bool
  +bson_append_code (bson_t     *bson,
  +                  const char *key,
  +                  int         key_length,
  +                  const char *javascript);
   
  -/**
  - * Append a string to a bson.
  - *
  - * @param b the bson to append to.
  - * @param name the key for the string.
  - * @param str the string to append.
  - *
  - * @return BSON_OK or BSON_ERROR.
  -*/
  -MONGO_EXPORT int bson_append_string( bson *b, const char *name, const char *str );
   
   /**
  - * Append len bytes of a string to a bson.
  + * bson_append_code_with_scope:
  + * @bson: A bson_t.
  + * @key: The key for the document.
  + * @javascript: JavaScript code to be executed.
  + * @scope: A bson_t containing the scope for @javascript.
    *
  - * @param b the bson to append to.
  - * @param name the key for the string.
  - * @param str the string to append.
  - * @param len the number of bytes from str to append.
  + * Appends a field of type BSON_TYPE_CODEWSCOPE to the BSON document.
  + * @javascript should contain a script in javascript to be executed.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_string_n( bson *b, const char *name, const char *str, \
size_t len );  +bool
  +bson_append_code_with_scope (bson_t       *bson,
  +                             const char   *key,
  +                             int           key_length,
  +                             const char   *javascript,
  +                             const bson_t *scope);
   
  -/**
  - * Append a symbol to a bson.
  - *
  - * @param b the bson to append to.
  - * @param name the key for the symbol.
  - * @param str the symbol to append.
  - *
  - * @return BSON_OK or BSON_ERROR.
  - */
  -MONGO_EXPORT int bson_append_symbol( bson *b, const char *name, const char *str );
   
   /**
  - * Append len bytes of a symbol to a bson.
  + * bson_append_dbpointer:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @collection: The collection name.
  + * @oid: The oid to the reference.
    *
  - * @param b the bson to append to.
  - * @param name the key for the symbol.
  - * @param str the symbol to append.
  - * @param len the number of bytes from str to append.
  + * Appends a new field of type BSON_TYPE_DBPOINTER. This datum type is
  + * deprecated in the BSON spec and should not be used in new code.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_symbol_n( bson *b, const char *name, const char *str, \
size_t len );  +bool
  +bson_append_dbpointer (bson_t           *bson,
  +                       const char       *key,
  +                       int               key_length,
  +                       const char       *collection,
  +                       const bson_oid_t *oid);
  +
   
   /**
  - * Append code to a bson.
  + * bson_append_double:
  + * @bson: A bson_t.
  + * @key: The key for the field.
    *
  - * @param b the bson to append to.
  - * @param name the key for the code.
  - * @param str the code to append.
  + * Appends a new field to @bson of the type BSON_TYPE_DOUBLE.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_code( bson *b, const char *name, const char *str );
  +bool
  +bson_append_double (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length,
  +                    double      value);
  +
   
   /**
  - * Append len bytes of code to a bson.
  + * bson_append_document:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @value: A bson_t containing the subdocument.
    *
  - * @param b the bson to append to.
  - * @param name the key for the code.
  - * @param str the code to append.
  - * @param len the number of bytes from str to append.
  + * Appends a new field to @bson of the type BSON_TYPE_DOCUMENT.
  + * The documents contents will be copied into @bson.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_code_n( bson *b, const char *name, const char *str, \
size_t len );  +bool
  +bson_append_document (bson_t       *bson,
  +                      const char   *key,
  +                      int           key_length,
  +                      const bson_t *value);
  +
   
   /**
  - * Append code to a bson with scope.
  + * bson_append_document_begin:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @key_length: The length of @key in bytes not including NUL or -1
  + *    if @key_length is NUL terminated.
  + * @child: A location to an uninitialized bson_t.
    *
  - * @param b the bson to append to.
  - * @param name the key for the code.
  - * @param code the string to append.
  - * @param scope a BSON object containing the scope.
  + * Appends a new field named @key to @bson. The field is, however,
  + * incomplete.  @child will be initialized so that you may add fields to the
  + * child document.  Child will use a memory buffer owned by @bson and
  + * therefore grow the parent buffer as additional space is used. This allows
  + * a single malloc'd buffer to be used when building documents which can help
  + * reduce memory fragmentation.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_code_w_scope( bson *b, const char *name, const char \
*code, const bson *scope );  +bool
  +bson_append_document_begin (bson_t     *bson,
  +                            const char *key,
  +                            int         key_length,
  +                            bson_t     *child);
  +
   
   /**
  - * Append len bytes of code to a bson with scope.
  + * bson_append_document_end:
  + * @bson: A bson_t.
  + * @child: A bson_t supplied to bson_append_document_begin().
    *
  - * @param b the bson to append to.
  - * @param name the key for the code.
  - * @param code the string to append.
  - * @param size the number of bytes from str to append.
  - * @param scope a BSON object containing the scope.
  + * Finishes the appending of a document to a @bson. @child is considered
  + * disposed after this call and should not be used any further.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_code_w_scope_n( bson *b, const char *name, const char \
*code, size_t size, const bson *scope );  +bool
  +bson_append_document_end (bson_t *bson,
  +                          bson_t *child);
  +
   
   /**
  - * Append binary data to a bson.
  + * bson_append_array_begin:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @key_length: The length of @key in bytes not including NUL or -1
  + *    if @key_length is NUL terminated.
  + * @child: A location to an uninitialized bson_t.
  + *
  + * Appends a new field named @key to @bson. The field is, however,
  + * incomplete. @child will be initialized so that you may add fields to the
  + * child array. Child will use a memory buffer owned by @bson and
  + * therefore grow the parent buffer as additional space is used. This allows
  + * a single malloc'd buffer to be used when building arrays which can help
  + * reduce memory fragmentation.
    *
  - * @param b the bson to append to.
  - * @param name the key for the data.
  - * @param type the binary data type.
  - * @param str the binary data.
  - * @param len the length of the data.
  + * The type of @child will be BSON_TYPE_ARRAY and therefore the keys inside
  + * of it MUST be "0", "1", etc.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_binary( bson *b, const char *name, char type, const \
char *str, size_t len );  +bool
  +bson_append_array_begin (bson_t     *bson,
  +                         const char *key,
  +                         int         key_length,
  +                         bson_t     *child);
  +
   
   /**
  - * Append a bson_bool_t to a bson.
  + * bson_append_array_end:
  + * @bson: A bson_t.
  + * @child: A bson_t supplied to bson_append_array_begin().
    *
  - * @param b the bson to append to.
  - * @param name the key for the boolean value.
  - * @param v the bson_bool_t to append.
  + * Finishes the appending of a array to a @bson. @child is considered
  + * disposed after this call and should not be used any further.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_bool( bson *b, const char *name, const bson_bool_t v \
);  +bool
  +bson_append_array_end (bson_t *bson,
  +                       bson_t *child);
  +
   
   /**
  - * Append a null value to a bson.
  + * bson_append_int32:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @value: The int32_t 32-bit integer value.
    *
  - * @param b the bson to append to.
  - * @param name the key for the null value.
  + * Appends a new field of type BSON_TYPE_INT32 to @bson.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_null( bson *b, const char *name );
  +bool
  +bson_append_int32 (bson_t      *bson,
  +                   const char  *key,
  +                   int          key_length,
  +                   int32_t value);
  +
   
   /**
  - * Append an undefined value to a bson.
  + * bson_append_int64:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @value: The int64_t 64-bit integer value.
    *
  - * @param b the bson to append to.
  - * @param name the key for the undefined value.
  + * Appends a new field of type BSON_TYPE_INT64 to @bson.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_undefined( bson *b, const char *name );
  +bool
  +bson_append_int64 (bson_t      *bson,
  +                   const char  *key,
  +                   int          key_length,
  +                   int64_t value);
  +
   
   /**
  - * Append a maxkey value to a bson.
  + * bson_append_iter:
  + * @bson: A bson_t to append to.
  + * @key: The key name or %NULL to take current key from @iter.
  + * @key_length: The key length or -1 to use strlen().
  + * @iter: The iter located on the position of the element to append.
    *
  - * @param b the bson to append to.
  - * @param name the key for the maxkey value.
  + * Appends a new field to @bson that is equivalent to the field currently
  + * pointed to by @iter.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_maxkey( bson *b, const char *name );
  +bool
  +bson_append_iter (bson_t            *bson,
  +                  const char        *key,
  +                  int                key_length,
  +                  const bson_iter_t *iter);
  +
   
   /**
  - * Append a minkey value to a bson.
  + * bson_append_minkey:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + *
  + * Appends a new field of type BSON_TYPE_MINKEY to @bson. This is a special
  + * type that compares lower than all other possible BSON element values.
    *
  - * @param b the bson to append to.
  - * @param name the key for the minkey value.
  + * See http://bsonspec.org for more information on this type.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_minkey( bson *b, const char *name );
  +bool
  +bson_append_minkey (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length);
  +
   
   /**
  - * Append a regex value to a bson.
  + * bson_append_maxkey:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + *
  + * Appends a new field of type BSON_TYPE_MAXKEY to @bson. This is a special
  + * type that compares higher than all other possible BSON element values.
    *
  - * @param b the bson to append to.
  - * @param name the key for the regex value.
  - * @param pattern the regex pattern to append.
  - * @param opts the regex options.
  + * See http://bsonspec.org for more information on this type.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_regex( bson *b, const char *name, const char \
*pattern, const char *opts );  +bool
  +bson_append_maxkey (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length);
  +
   
   /**
  - * Append bson data to a bson.
  + * bson_append_null:
  + * @bson: A bson_t.
  + * @key: The key for the field.
    *
  - * @param b the bson to append to.
  - * @param name the key for the bson data.
  - * @param bson the bson object to append.
  + * Appends a new field to @bson with NULL for the value.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_bson( bson *b, const char *name, const bson *bson );
  +bool
  +bson_append_null (bson_t     *bson,
  +                  const char *key,
  +                  int         key_length);
  +
   
   /**
  - * Append a BSON element to a bson from the current point of an iterator.
  + * bson_append_oid:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @oid: bson_oid_t.
    *
  - * @param b the bson to append to.
  - * @param name_or_null the key for the BSON element, or NULL.
  - * @param elem the bson_iterator.
  + * Appends a new field to the @bson of type BSON_TYPE_OID using the contents of
  + * @oid.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_element( bson *b, const char *name_or_null, const \
bson_iterator *elem );  +bool
  +bson_append_oid (bson_t           *bson,
  +                 const char       *key,
  +                 int               key_length,
  +                 const bson_oid_t *oid);
  +
   
   /**
  - * Append a bson_timestamp_t value to a bson.
  + * bson_append_regex:
  + * @bson: A bson_t.
  + * @key: The key of the field.
  + * @regex: The regex to append to the bson.
  + * @options: Options for @regex.
    *
  - * @param b the bson to append to.
  - * @param name the key for the timestampe value.
  - * @param ts the bson_timestamp_t value to append.
  + * Appends a new field to @bson of type BSON_TYPE_REGEX. @regex should
  + * be the regex string. @options should contain the options for the regex.
    *
  - * @return BSON_OK or BSON_ERROR.
  - */
  -MONGO_EXPORT int bson_append_timestamp( bson *b, const char *name, \
                bson_timestamp_t *ts );
  -MONGO_EXPORT int bson_append_timestamp2( bson *b, const char *name, int time, int \
                increment );
  -
  -/* these both append a bson_date */
  -/**
  - * Append a bson_date_t value to a bson.
  + * Valid options for @options are:
    *
  - * @param b the bson to append to.
  - * @param name the key for the date value.
  - * @param millis the bson_date_t to append.
  + *   'i' for case-insensitive.
  + *   'm' for multiple matching.
  + *   'x' for verbose mode.
  + *   'l' to make \w and \W locale dependent.
  + *   's' for dotall mode ('.' matches everything)
  + *   'u' to make \w and \W match unicode.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * For more information on what comprimises a BSON regex, see bsonspec.org.
  + *
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_date( bson *b, const char *name, bson_date_t millis \
);  +bool
  +bson_append_regex (bson_t     *bson,
  +                   const char *key,
  +                   int         key_length,
  +                   const char *regex,
  +                   const char *options);
  +
   
   /**
  - * Append a time_t value to a bson.
  + * bson_append_utf8:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @value: A UTF-8 encoded string.
  + * @length: The length of @value or -1 if it is NUL terminated.
    *
  - * @param b the bson to append to.
  - * @param name the key for the date value.
  - * @param secs the time_t to append.
  + * Appends a new field to @bson using @key as the key and @value as the UTF-8
  + * encoded value.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * It is the callers responsibility to ensure @value is valid UTF-8. You can
  + * use bson_utf8_validate() to perform this check.
  + *
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_time_t( bson *b, const char *name, time_t secs );
  +bool
  +bson_append_utf8 (bson_t     *bson,
  +                  const char *key,
  +                  int         key_length,
  +                  const char *value,
  +                  int         length);
  +
   
   /**
  - * Start appending a new object to a bson.
  + * bson_append_symbol:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @value: The symbol as a string.
  + * @length: The length of @value or -1 if NUL-terminated.
    *
  - * @param b the bson to append to.
  - * @param name the name of the new object.
  + * Appends a new field to @bson of type BSON_TYPE_SYMBOL. This BSON type is
  + * deprecated and should not be used in new code.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * See http://bsonspec.org for more information on this type.
  + *
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_start_object( bson *b, const char *name );
  +bool
  +bson_append_symbol (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length,
  +                    const char *value,
  +                    int         length);
  +
   
   /**
  - * Start appending a new array to a bson.
  + * bson_append_time_t:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @value: A time_t.
    *
  - * @param b the bson to append to.
  - * @param name the name of the new array.
  + * Appends a BSON_TYPE_DATE_TIME field to @bson using the time_t @value for the
  + * number of seconds since UNIX epoch in UTC.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_start_array( bson *b, const char *name );
  +bool
  +bson_append_time_t (bson_t     *bson,
  +                    const char *key,
  +                    int         key_length,
  +                    time_t      value);
  +
   
   /**
  - * Finish appending a new object or array to a bson.
  + * bson_append_timeval:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @value: A struct timeval containing the date and time.
    *
  - * @param b the bson to append to.
  + * Appends a BSON_TYPE_DATE_TIME field to @bson using the struct timeval
  + * provided. The time is persisted in milliseconds since the UNIX epoch in UTC.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT int bson_append_finish_object( bson *b );
  +bool
  +bson_append_timeval (bson_t         *bson,
  +                     const char     *key,
  +                     int             key_length,
  +                     struct timeval *value);
  +
   
   /**
  - * Finish appending a new object or array to a bson. This
  - * is simply an alias for bson_append_finish_object.
  + * bson_append_date_time:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @key_length: The length of @key in bytes or -1 if \0 terminated.
  + * @value: The number of milliseconds elapsed since UNIX epoch.
    *
  - * @param b the bson to append to.
  + * Appends a new field to @bson of type BSON_TYPE_DATE_TIME.
    *
  - * @return BSON_OK or BSON_ERROR.
  + * Returns: true if sucessful; otherwise false.
    */
  -MONGO_EXPORT int bson_append_finish_array( bson *b );
  -
  -void bson_numstr( char *str, int i );
  -
  -void bson_incnumstr( char *str );
  -
  -/* Error handling and standard library function over-riding. */
  -/* -------------------------------------------------------- */
  -
  -/* bson_err_handlers shouldn't return!!! */
  -typedef void( *bson_err_handler )( const char *errmsg );
  +bool
  +bson_append_date_time (bson_t      *bson,
  +                       const char  *key,
  +                       int          key_length,
  +                       int64_t value);
   
  -typedef int (*bson_printf_func)( const char *, ... );
  -typedef int (*bson_fprintf_func)( FILE *, const char *, ... );
  -typedef int (*bson_sprintf_func)( char *, const char *, ... );
  -
  -extern void *( *bson_malloc_func )( size_t );
  -extern void *( *bson_realloc_func )( void *, size_t );
  -extern void ( *bson_free_func )( void * );
  -
  -extern bson_printf_func bson_printf;
  -extern bson_fprintf_func bson_fprintf;
  -extern bson_sprintf_func bson_sprintf;
  -extern bson_printf_func bson_errprintf;
  -
  -MONGO_EXPORT void bson_free( void *ptr );
   
   /**
  - * Allocates memory and checks return value, exiting fatally if malloc() fails.
  - *
  - * @param size bytes to allocate.
  + * bson_append_now_utc:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @key_length: The length of @key or -1 if it is NULL terminated.
    *
  - * @return a pointer to the allocated memory.
  + * Appends a BSON_TYPE_DATE_TIME field to @bson using the current time in UTC
  + * as the field value.
    *
  - * @sa malloc(3)
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT void *bson_malloc( size_t size );
  +bool
  +bson_append_now_utc (bson_t     *bson,
  +                     const char *key,
  +                     int         key_length);
   
   /**
  - * Changes the size of allocated memory and checks return value,
  - * exiting fatally if realloc() fails.
  + * bson_append_timestamp:
  + * @bson: A bson_t.
  + * @key: The key for the field.
  + * @timestamp: 4 byte timestamp.
  + * @increment: 4 byte increment for timestamp.
    *
  - * @param ptr pointer to the space to reallocate.
  - * @param size bytes to allocate.
  + * Appends a field of type BSON_TYPE_TIMESTAMP to @bson. This is a special type
  + * used by MongoDB replication and sharding. If you need generic time and date
  + * fields use bson_append_time_t() or bson_append_timeval().
    *
  - * @return a pointer to the allocated memory.
  + * Setting @increment and @timestamp to zero has special semantics. See
  + * http://bsonspec.org for more information on this field type.
    *
  - * @sa realloc()
  + * Returns: true if successful; false if append would overflow max size.
    */
  -void *bson_realloc( void *ptr, size_t size );
  +bool
  +bson_append_timestamp (bson_t       *bson,
  +                       const char   *key,
  +                       int           key_length,
  +                       uint32_t timestamp,
  +                       uint32_t increment);
  +
   
   /**
  - * Set a function for error handling.
  + * bson_append_undefined:
  + * @bson: A bson_t.
  + * @key: The key for the field.
    *
  - * @param func a bson_err_handler function.
  + * Appends a field of type BSON_TYPE_UNDEFINED. This type is deprecated in the
  + * spec and should not be used for new code. However, it is provided for those
  + * needing to interact with legacy systems.
    *
  - * @return the old error handling function, or NULL.
  + * Returns: true if successful; false if append would overflow max size.
    */
  -MONGO_EXPORT bson_err_handler set_bson_err_handler( bson_err_handler func );
  +bool
  +bson_append_undefined (bson_t     *bson,
  +                       const char *key,
  +                       int         key_length);
   
  -/* does nothing if ok != 0 */
  -/**
  - * Exit fatally.
  - *
  - * @param ok exits if ok is equal to 0.
  - */
  -void bson_fatal( int ok );
   
  -/**
  - * Exit fatally with an error message.
  -  *
  - * @param ok exits if ok is equal to 0.
  - * @param msg prints to stderr before exiting.
  - */
  -void bson_fatal_msg( int ok, const char *msg );
  +bool
  +bson_concat (bson_t       *dst,
  +             const bson_t *src);
   
  -/**
  - * Invoke the error handler, but do not exit.
  - *
  - * @param b the buffer object.
  - */
  -void bson_builder_error( bson *b );
   
  -/**
  - * Cast an int64_t to double. This is necessary for embedding in
  - * certain environments.
  - *
  - */
  -MONGO_EXPORT double bson_int64_to_double( int64_t i64 )
  -	RPM_GNUC_CONST;
  +BSON_END_DECLS
   
  -MONGO_EXPORT void bson_swap_endian32( void *outp, const void *inp );
  -MONGO_EXPORT void bson_swap_endian64( void *outp, const void *inp );
   
  -MONGO_EXTERN_C_END
  +/*==============================================================*/
   
   #endif	/* H_BSON */
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/edon-r.c
  ============================================================================
  $ cvs diff -u -r1.5.4.1 -r1.5.4.2 edon-r.c
  --- rpm/rpmio/edon-r.c	23 Oct 2013 18:23:58 -0000	1.5.4.1
  +++ rpm/rpmio/edon-r.c	30 Sep 2014 22:31:43 -0000	1.5.4.2
  @@ -477,7 +477,7 @@
   		case 256:
   			LastByte = (int)state->unprocessed_bits >> 3;
   			PadOnePosition = 7 - (state->unprocessed_bits & 0x07);
  -			hashState256(state)->LastPart[LastByte] = \
hashState256(state)->LastPart[LastByte] & (0xff << (PadOnePosition + 1) )\  \
+			hashState256(state)->LastPart[LastByte] = \
(hashState256(state)->LastPart[LastByte] & (0xff << (PadOnePosition + 1) ))\  ^ (0x01 \
<< PadOnePosition);  data64 = (uint64_t *)hashState256(state)->LastPart;
   
  @@ -563,7 +563,7 @@
   		case 512:
   			LastByte = (int)state->unprocessed_bits >> 3;
   			PadOnePosition = 7 - (state->unprocessed_bits & 0x07);
  -			hashState512(state)->LastPart[LastByte] = \
hashState512(state)->LastPart[LastByte] & (0xff << (PadOnePosition + 1) )\  \
+			hashState512(state)->LastPart[LastByte] = \
(hashState512(state)->LastPart[LastByte] & (0xff << (PadOnePosition + 1) ))\  ^ (0x01 \
<< PadOnePosition);  data64 = (uint64_t *)hashState512(state)->LastPart;
   
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/librpmio.vers
  ============================================================================
  $ cvs diff -u -r2.199.2.46 -r2.199.2.47 librpmio.vers
  --- rpm/rpmio/librpmio.vers	29 Sep 2014 18:26:48 -0000	2.199.2.46
  +++ rpm/rpmio/librpmio.vers	30 Sep 2014 22:31:43 -0000	2.199.2.47
  @@ -900,225 +900,500 @@
       yarnRelease;
       yarnTwist;
       yarnWaitFor;
  -    bson_alloc;
  -    bson_append;
  -    bson_append32;
  -    bson_append64;
  +    bcon_append;
  +    bcon_append_ctx;
  +    bcon_append_ctx_init;
  +    bcon_append_ctx_va;
  +    _bcon_append_tokenize;
  +    bcon_extract;
  +    bcon_extract_ctx;
  +    bcon_extract_ctx_init;
  +    bcon_extract_ctx_va;
  +    _bcon_extract_tokenize;
  +    bcon_new;
  +    bson_append_array;
  +    bson_append_array_begin;
  +    bson_append_array_end;
       bson_append_binary;
       bson_append_bool;
  -    bson_append_bson;
  -    bson_append_byte;
       bson_append_code;
  -    bson_append_code_n;
  -    bson_append_code_w_scope;
  -    bson_append_date;
  +    bson_append_code_with_scope;
  +    bson_append_date_time;
  +    bson_append_dbpointer;
  +    bson_append_document;
  +    bson_append_document_begin;
  +    bson_append_document_end;
       bson_append_double;
  -    bson_append_element;
  -    bson_append_finish_object;
  -    bson_append_int;
  -    bson_append_long;
  -    bson_append_new_oid;
  +    bson_append_int32;
  +    bson_append_int64;
  +    bson_append_iter;
  +    bson_append_maxkey;
  +    bson_append_minkey;
  +    bson_append_now_utc;
       bson_append_null;
       bson_append_oid;
       bson_append_regex;
  -    bson_append_start_array;
  -    bson_append_start_object;
  -    bson_append_string;
  -    bson_append_string_n;
  -    bson_append_string_base;
       bson_append_symbol;
  -    bson_append_symbol_n;
  -    bson_append_time_t;
       bson_append_timestamp;
  +    bson_append_time_t;
  +    bson_append_timeval;
       bson_append_undefined;
  -    bson_big_endian32;
  -    bson_big_endian64;
  -    bson_buffer_destroy;
  -    bson_buffer_finish;
  -    bson_buffer_init;
  +    bson_append_utf8;
  +    bson_as_json;
  +    bson_bcone_magic;
  +    bson_bcon_magic;
  +    bson_compare;
  +    bson_concat;
  +    bson_context_destroy;
  +    bson_context_get_default;
  +    bson_context_new;
       bson_copy;
  -    bson_data;
  -    bson_dealloc;
  +    bson_copy_to;
  +    bson_copy_to_excluding;
  +    bson_count_keys;
       bson_destroy;
  -    bson_empty;
  -    bson_ensure_space;
  -    bson_errprintf;
  -    bson_fatal;
  -    bson_fatal_msg;
  -    bson_find;
  -    bson_finish;
  -    bson_fprintf;
  +    bson_equal;
       bson_free;
  -    bson_free_func;
  -    bson_from_buffer;
  +    bson_get_data;
  +    bson_get_monotonic_time;
  +    bson_gettimeofday;
  +    bson_has_field;
       bson_init;
  -    bson_init_empty;
  -    bson_init_finished_data;
  -    bson_init_finished_data_with_copy;
  -    bson_iterator_alloc;
  -    bson_iterator_bin_data;
  -    bson_iterator_bin_len;
  -    bson_iterator_bin_type;
  -    bson_iterator_bool;
  -    bson_iterator_bool_raw;
  -    bson_iterator_code;
  -    bson_iterator_code_scope;
  -    bson_iterator_code_scope_init;
  -    bson_iterator_date;
  -    bson_iterator_dealloc;
  -    bson_iterator_double;
  -    bson_iterator_double_raw;
  -    bson_iterator_init;
  -    bson_iterator_int;
  -    bson_iterator_int_raw;
  -    bson_iterator_key;
  -    bson_iterator_long;
  -    bson_iterator_long_raw;
  -    bson_iterator_more;
  -    bson_iterator_next;
  -    bson_iterator_oid;
  -    bson_iterator_regex;
  -    bson_iterator_regex_opts;
  -    bson_iterator_string;
  -    bson_iterator_string_len;
  -    bson_iterator_subiterator;
  -    bson_iterator_subobject;
  -    bson_iterator_subobject_init;
  -    bson_iterator_time_t;
  -    bson_iterator_timestamp;
  -    bson_iterator_timestamp_increment;
  -    bson_iterator_timestamp_time;
  -    bson_iterator_type;
  -    bson_iterator_value;
  -    bson_little_endian64;
  -    bson_little_endian32;
  +    bson_init_from_json;
  +    bson_init_static;
  +    bson_iter_array;
  +    bson_iter_as_bool;
  +    bson_iter_as_int64;
  +    bson_iter_binary;
  +    bson_iter_bool;
  +    bson_iter_code;
  +    bson_iter_codewscope;
  +    bson_iter_date_time;
  +    bson_iter_dbpointer;
  +    bson_iter_document;
  +    bson_iter_double;
  +    bson_iter_dup_utf8;
  +    bson_iter_find;
  +    bson_iter_find_case;
  +    bson_iter_find_descendant;
  +    bson_iter_init;
  +    bson_iter_init_find;
  +    bson_iter_init_find_case;
  +    bson_iter_int32;
  +    bson_iter_int64;
  +    bson_iter_key;
  +    bson_iter_next;
  +    bson_iter_oid;
  +    bson_iter_overwrite_bool;
  +    bson_iter_overwrite_double;
  +    bson_iter_overwrite_int32;
  +    bson_iter_overwrite_int64;
  +    bson_iter_recurse;
  +    bson_iter_regex;
  +    bson_iter_symbol;
  +    bson_iter_timestamp;
  +    bson_iter_time_t;
  +    bson_iter_timeval;
  +    bson_iter_type;
  +    bson_iter_utf8;
  +    bson_iter_visit_all;
  +    bson_json_data_reader_ingest;
  +    bson_json_data_reader_new;
  +    bson_json_reader_destroy;
  +    bson_json_reader_new;
  +    bson_json_reader_new_from_fd;
  +    bson_json_reader_new_from_file;
  +    bson_json_reader_read;
       bson_malloc;
  -    bson_malloc_func;
  -    bson_numstr;
  -    bson_oid_from_string;
  -    bson_oid_gen;
  -    bson_oid_generated_time;
  +    bson_malloc0;
  +    bson_md5_append;
  +    bson_md5_finish;
  +    bson_md5_init;
  +    bson_new;
  +    bson_new_from_data;
  +    bson_new_from_json;
  +    bson_oid_compare;
  +    bson_oid_copy;
  +    bson_oid_equal;
  +    bson_oid_get_time_t;
  +    bson_oid_hash;
  +    bson_oid_init;
  +    bson_oid_init_from_data;
  +    bson_oid_init_from_string;
  +    bson_oid_init_sequence;
  +    bson_oid_is_valid;
       bson_oid_to_string;
  -    bson_print;
  -    bson_print_raw;
  -    bson_printf;
  +    bson_reader_destroy;
  +    bson_reader_new_from_data;
  +    bson_reader_new_from_fd;
  +    bson_reader_new_from_file;
  +    bson_reader_new_from_handle;
  +    bson_reader_read;
  +    bson_reader_set_destroy_func;
  +    bson_reader_set_read_func;
  +    bson_reader_tell;
       bson_realloc;
  -    bson_realloc_func;
  -    bson_set_oid_fuzz;
  -    bson_set_oid_inc;
  -    bson_shared_empty;
  -    bson_size;
  -    bson_sprintf;
  -    bson_swap_endian64;
  -    bson_swap_endian32;
  -    set_bson_err_handler;
  -    gridfile_create;
  -    gridfile_dealloc;
  -    gridfile_destroy;
  -    gridfile_exists;
  -    gridfile_get_boolean;
  -    gridfile_get_chunk;
  -    gridfile_get_chunks;
  -    gridfile_get_chunksize;
  -    gridfile_get_contentlength;
  -    gridfile_get_contenttype;
  -    gridfile_get_field;
  -    gridfile_get_filename;
  -    gridfile_get_md5;
  -    gridfile_get_metadata;
  -    gridfile_get_numchunks;
  -    gridfile_get_uploaddate;
  -    gridfile_init;
  -    gridfile_read;
  -    gridfile_read_buffer;
  -    gridfile_seek;
  -    gridfile_set_size;
  -    gridfile_truncate;
  -    gridfile_write_buffer;
  -    gridfile_write_file;
  -    gridfile_writer_done;
  -    gridfile_writer_init;
  -    gridfs_destroy;
  -    gridfs_find_filename;
  -    gridfs_find_query;
  -    gridfs_init;
  -    gridfs_remove_filename;
  -    gridfs_store_buffer;
  -    gridfs_store_file;
  -    mongo_alloc;
  -    mongo_check_connection;
  -    mongo_clear_errors;
  -    mongo_client;
  -    mongo_cmd_add_user;
  -    mongo_cmd_authenticate;
  -    mongo_cmd_drop_collection;
  -    mongo_cmd_drop_db;
  -    mongo_cmd_get_last_error;
  -    mongo_cmd_get_prev_error;
  -    mongo_cmd_ismaster;
  -    mongo_cmd_reset_error;
  -    mongo_connect;
  -    mongo_connect_pair;
  -    mongo_count;
  -    mongo_create_capped_collection;
  -    mongo_create_index;
  -    mongo_create_simple_index;
  -    mongo_cursor_alloc;
  -    mongo_cursor_bson;
  -    mongo_cursor_dealloc;
  -    mongo_cursor_destroy;
  -    mongo_cursor_get_more;
  -    mongo_cursor_init;
  -    mongo_cursor_next;
  -    mongo_cursor_set_limit;
  -    mongo_cursor_set_query;
  -    mongo_data_append;
  -    mongo_data_append32;
  -    mongo_data_append64;
  -    mongo_dealloc;
  -    mongo_destroy;
  -    mongo_disconnect;
  -    mongo_env_close_socket;
  -    mongo_find;
  -    mongo_find_one;
  -    mongo_get_socket;
  -    mongo_init;
  -    mongo_insert;
  -    mongo_insert_batch;
  -    mongo_md5_append;
  -    mongo_md5_finish;
  -    mongo_md5_init;
  -    mongo_message_create;
  -    mongo_message_send;
  -    mongo_read_response;
  -    mongo_reconnect;
  -    mongo_remove;
  -    mongo_replset_init_conn;
  -    mongo_replset_add_seed;
  -    mongo_replset_connect;
  -    mongo_run_command;
  -    mongo_set_op_timeout;
  -    mongo_set_write_concern;
  -    mongo_simple_int_command;
  -    mongo_simple_str_command;
  -    mongo_update;
  -    mongo_validate_ns;
  -    mongo_write_concern_alloc;
  -    mongo_write_concern_dealloc;
  -    mongo_write_concern_destroy;
  -    mongo_write_concern_finish;
  -    mongo_write_concern_get_cmd;
  -    mongo_write_concern_get_fsync;
  -    mongo_write_concern_get_j;
  -    mongo_write_concern_get_mode;
  -    mongo_write_concern_get_w;
  -    mongo_write_concern_get_wtimeout;
  -    mongo_write_concern_init;
  -    mongo_write_concern_set_fsync;
  -    mongo_write_concern_set_j;
  -    mongo_write_concern_set_mode;
  -    mongo_write_concern_set_w;
  -    mongo_write_concern_set_wtimeout;
  +    bson_reinit;
  +    bson_set_error;
  +    bson_sized_new;
  +    bson_snprintf;
  +    bson_strdup;
  +    bson_strdup_printf;
  +    bson_strdupv_printf;
  +    bson_strerror_r;
  +    bson_strfreev;
  +    bson_string_append;
  +    bson_string_append_c;
  +    bson_string_append_printf;
  +    bson_string_append_unichar;
  +    bson_string_free;
  +    bson_string_new;
  +    bson_string_truncate;
  +    bson_strncpy;
  +    bson_strndup;
  +    bson_strnlen;
  +    bson_uint32_to_string;
  +    bson_utf8_escape_for_json;
  +    bson_utf8_from_unichar;
  +    bson_utf8_get_char;
  +    bson_utf8_next_char;
  +    bson_utf8_validate;
  +    bson_validate;
  +    bson_vsnprintf;
  +    bson_writer_begin;
  +    bson_writer_destroy;
  +    bson_writer_end;
  +    bson_writer_get_length;
  +    bson_writer_new;
  +    bson_writer_rollback;
  +    bson_zero_free;
  +    yajl_alloc;
  +    yajl_buf_alloc;
  +    yajl_buf_append;
  +    yajl_buf_clear;
  +    yajl_buf_data;
  +    yajl_buf_free;
  +    yajl_buf_len;
  +    yajl_buf_truncate;
  +    yajl_complete_parse;
  +    yajl_config;
  +    yajl_do_finish;
  +    yajl_do_parse;
  +    yajl_free;
  +    yajl_free_error;
  +    yajl_gen_alloc;
  +    yajl_gen_array_close;
  +    yajl_gen_array_open;
  +    yajl_gen_bool;
  +    yajl_gen_clear;
  +    yajl_gen_config;
  +    yajl_gen_double;
  +    yajl_gen_free;
  +    yajl_gen_get_buf;
  +    yajl_gen_integer;
  +    yajl_gen_map_close;
  +    yajl_gen_map_open;
  +    yajl_gen_null;
  +    yajl_gen_number;
  +    yajl_gen_string;
  +    yajl_get_bytes_consumed;
  +    yajl_get_error;
  +    yajl_lex_alloc;
  +    yajl_lex_current_char;
  +    yajl_lex_current_line;
  +    yajl_lex_error_to_string;
  +    yajl_lex_free;
  +    yajl_lex_get_error;
  +    yajl_lex_lex;
  +    yajl_lex_peek;
  +    yajl_parse;
  +    yajl_parse_integer;
  +    yajl_render_error_string;
  +    yajl_set_default_alloc_funcs;
  +    yajl_status_to_string;
  +    yajl_string_decode;
  +    yajl_string_encode;
  +    yajl_string_validate_utf8;
  +    yajl_tree_free;
  +    yajl_tree_get;
  +    yajl_tree_parse;
  +    yajl_version;
  +    _mongoc_array_append_vals;
  +    _mongoc_array_destroy;
  +    _mongoc_array_init;
  +    _mongoc_buffer_append_from_stream;
  +    _mongoc_buffer_clear;
  +    _mongoc_buffer_destroy;
  +    _mongoc_buffer_fill;
  +    _mongoc_buffer_init;
  +    mongoc_cleanup;
  +    mongoc_client_command;
  +    mongoc_client_command_simple;
  +    _mongoc_client_create_stream;
  +    mongoc_client_destroy;
  +    mongoc_client_get_collection;
  +    mongoc_client_get_database;
  +    mongoc_client_get_database_names;
  +    mongoc_client_get_gridfs;
  +    mongoc_client_get_read_prefs;
  +    mongoc_client_get_uri;
  +    mongoc_client_get_write_concern;
  +    mongoc_client_new;
  +    mongoc_client_new_from_uri;
  +    mongoc_client_pool_destroy;
  +    mongoc_client_pool_new;
  +    mongoc_client_pool_pop;
  +    mongoc_client_pool_push;
  +    mongoc_client_pool_try_pop;
  +    _mongoc_client_recv;
  +    _mongoc_client_recv_gle;
  +    _mongoc_client_sendv;
  +    mongoc_client_set_read_prefs;
  +    mongoc_client_set_ssl_opts;
  +    mongoc_client_set_write_concern;
  +    _mongoc_client_stamp;
  +    _mongoc_client_warm_up;
  +    _mongoc_cluster_command_early;
  +    _mongoc_cluster_destroy;
  +    _mongoc_cluster_disconnect_node;
  +    _mongoc_cluster_get_primary;
  +    _mongoc_cluster_init;
  +    _mongoc_cluster_reconnect;
  +    _mongoc_cluster_sendv;
  +    _mongoc_cluster_stamp;
  +    _mongoc_cluster_try_recv;
  +    _mongoc_cluster_try_sendv;
  +    mongoc_collection_aggregate;
  +    mongoc_collection_command;
  +    mongoc_collection_command_simple;
  +    mongoc_collection_count;
  +    mongoc_collection_delete;
  +    mongoc_collection_destroy;
  +    mongoc_collection_drop;
  +    mongoc_collection_drop_index;
  +    mongoc_collection_ensure_index;
  +    mongoc_collection_find;
  +    mongoc_collection_get_last_error;
  +    mongoc_collection_get_name;
  +    mongoc_collection_get_read_prefs;
  +    mongoc_collection_get_write_concern;
  +    mongoc_collection_insert;
  +    mongoc_collection_insert_bulk;
  +    mongoc_collection_keys_to_index_string;
  +    _mongoc_collection_new;
  +    mongoc_collection_save;
  +    mongoc_collection_set_read_prefs;
  +    mongoc_collection_set_write_concern;
  +    mongoc_collection_update;
  +    _mongoc_counters_init;
  +    _mongoc_cursor_array_clone;
  +    _mongoc_cursor_array_destroy;
  +    _mongoc_cursor_array_init;
  +    _mongoc_cursor_array_more;
  +    _mongoc_cursor_array_next;
  +    _mongoc_cursor_clone;
  +    mongoc_cursor_clone;
  +    _mongoc_cursor_cursorid_clone;
  +    _mongoc_cursor_cursorid_destroy;
  +    _mongoc_cursor_cursorid_init;
  +    _mongoc_cursor_cursorid_next;
  +    _mongoc_cursor_destroy;
  +    mongoc_cursor_destroy;
  +    _mongoc_cursor_error;
  +    mongoc_cursor_error;
  +    _mongoc_cursor_get_host;
  +    mongoc_cursor_get_host;
  +    _mongoc_cursor_more;
  +    mongoc_cursor_more;
  +    _mongoc_cursor_new;
  +    _mongoc_cursor_next;
  +    mongoc_cursor_next;
  +    mongoc_database_add_user;
  +    mongoc_database_command;
  +    mongoc_database_command_simple;
  +    mongoc_database_create_collection;
  +    mongoc_database_destroy;
  +    mongoc_database_drop;
  +    mongoc_database_get_collection;
  +    mongoc_database_get_collection_names;
  +    mongoc_database_get_name;
  +    mongoc_database_get_read_prefs;
  +    mongoc_database_get_write_concern;
  +    mongoc_database_has_collection;
  +    _mongoc_database_new;
  +    mongoc_database_remove_user;
  +    mongoc_database_set_read_prefs;
  +    mongoc_database_set_write_concern;
  +    mongoc_gridfs_create_file;
  +    mongoc_gridfs_create_file_from_stream;
  +    mongoc_gridfs_destroy;
  +    mongoc_gridfs_drop;
  +    mongoc_gridfs_file_destroy;
  +    mongoc_gridfs_file_error;
  +    mongoc_gridfs_file_get_aliases;
  +    mongoc_gridfs_file_get_chunk_size;
  +    mongoc_gridfs_file_get_content_type;
  +    mongoc_gridfs_file_get_filename;
  +    mongoc_gridfs_file_get_length;
  +    mongoc_gridfs_file_get_md5;
  +    mongoc_gridfs_file_get_metadata;
  +    mongoc_gridfs_file_get_upload_date;
  +    mongoc_gridfs_file_list_destroy;
  +    mongoc_gridfs_file_list_error;
  +    _mongoc_gridfs_file_list_new;
  +    mongoc_gridfs_file_list_next;
  +    _mongoc_gridfs_file_new;
  +    _mongoc_gridfs_file_new_from_bson;
  +    _mongoc_gridfs_file_page_destroy;
  +    _mongoc_gridfs_file_page_get_data;
  +    _mongoc_gridfs_file_page_get_len;
  +    _mongoc_gridfs_file_page_is_dirty;
  +    _mongoc_gridfs_file_page_new;
  +    _mongoc_gridfs_file_page_read;
  +    _mongoc_gridfs_file_page_seek;
  +    _mongoc_gridfs_file_page_tell;
  +    _mongoc_gridfs_file_page_write;
  +    mongoc_gridfs_file_readv;
  +    mongoc_gridfs_file_save;
  +    mongoc_gridfs_file_seek;
  +    mongoc_gridfs_file_set_aliases;
  +    mongoc_gridfs_file_set_content_type;
  +    mongoc_gridfs_file_set_filename;
  +    mongoc_gridfs_file_set_md5;
  +    mongoc_gridfs_file_set_metadata;
  +    mongoc_gridfs_file_tell;
  +    mongoc_gridfs_file_writev;
  +    mongoc_gridfs_find;
  +    mongoc_gridfs_find_one;
  +    mongoc_gridfs_find_one_by_filename;
  +    mongoc_gridfs_get_chunks;
  +    mongoc_gridfs_get_files;
  +    _mongoc_gridfs_new;
  +    _mongoc_hex_md5;
  +    _mongoc_host_list_from_string;
  +    mongoc_index_opt_get_default;
  +    mongoc_index_opt_init;
  +    mongoc_init;
  +    _mongoc_list_append;
  +    _mongoc_list_destroy;
  +    _mongoc_list_foreach;
  +    _mongoc_list_prepend;
  +    _mongoc_list_remove;
  +    mongoc_log;
  +    mongoc_log_default_handler;
  +    mongoc_log_set_handler;
  +    mongoc_matcher_destroy;
  +    mongoc_matcher_match;
  +    mongoc_matcher_new;
  +    _mongoc_matcher_op_compare_new;
  +    _mongoc_matcher_op_destroy;
  +    _mongoc_matcher_op_exists_new;
  +    _mongoc_matcher_op_logical_new;
  +    _mongoc_matcher_op_match;
  +    _mongoc_matcher_op_not_new;
  +    _mongoc_matcher_op_to_bson;
  +    _mongoc_matcher_op_type_new;
  +    _mongoc_queue_get_length;
  +    _mongoc_queue_init;
  +    _mongoc_queue_pop_head;
  +    _mongoc_queue_push_head;
  +    _mongoc_queue_push_tail;
  +    mongoc_read_prefs_add_tag;
  +    mongoc_read_prefs_copy;
  +    mongoc_read_prefs_destroy;
  +    mongoc_read_prefs_get_mode;
  +    mongoc_read_prefs_get_tags;
  +    mongoc_read_prefs_is_valid;
  +    mongoc_read_prefs_new;
  +    _mongoc_read_prefs_score;
  +    mongoc_read_prefs_set_mode;
  +    mongoc_read_prefs_set_tags;
  +    _mongoc_rpc_gather;
  +    _mongoc_rpc_needs_gle;
  +    _mongoc_rpc_printf;
  +    _mongoc_rpc_reply_get_first;
  +    _mongoc_rpc_scatter;
  +    _mongoc_rpc_swab_from_le;
  +    _mongoc_rpc_swab_to_le;
  +    _mongoc_sasl_destroy;
  +    _mongoc_sasl_init;
  +    _mongoc_sasl_set_mechanism;
  +    _mongoc_sasl_set_pass;
  +    _mongoc_sasl_set_service_host;
  +    _mongoc_sasl_set_service_name;
  +    _mongoc_sasl_set_user;
  +    _mongoc_sasl_step;
  +    mongoc_socket_accept;
  +    mongoc_socket_bind;
  +    mongoc_socket_close;
  +    mongoc_socket_connect;
  +    mongoc_socket_destroy;
  +    mongoc_socket_errno;
  +    mongoc_socket_getsockname;
  +    mongoc_socket_listen;
  +    mongoc_socket_new;
  +    mongoc_socket_recv;
  +    mongoc_socket_send;
  +    mongoc_socket_sendv;
  +    _mongoc_socket_setnodelay;
  +    mongoc_socket_setsockopt;
  +    _mongoc_socket_try_sendv;
  +    _mongoc_socket_try_sendv_slow;
  +    _mongoc_socket_wait;
  +    _mongoc_ssl_check_cert;
  +    _mongoc_ssl_cleanup;
  +    _mongoc_ssl_ctx_new;
  +    _mongoc_ssl_extract_subject;
  +    _mongoc_ssl_init;
  +    mongoc_ssl_opt_get_default;
  +    mongoc_stream_buffered_new;
  +    mongoc_stream_close;
  +    mongoc_stream_cork;
  +    mongoc_stream_destroy;
  +    mongoc_stream_file_get_fd;
  +    mongoc_stream_file_new;
  +    mongoc_stream_file_new_for_path;
  +    mongoc_stream_flush;
  +    mongoc_stream_gridfs_new;
  +    mongoc_stream_read;
  +    mongoc_stream_readv;
  +    mongoc_stream_setsockopt;
  +    mongoc_stream_socket_new;
  +    mongoc_stream_tls_check_cert;
  +    mongoc_stream_tls_do_handshake;
  +    mongoc_stream_tls_new;
  +    mongoc_stream_uncork;
  +    mongoc_stream_writev;
  +    mongoc_uri_copy;
  +    mongoc_uri_destroy;
  +    mongoc_uri_get_auth_mechanism;
  +    mongoc_uri_get_auth_source;
  +    mongoc_uri_get_database;
  +    mongoc_uri_get_hosts;
  +    mongoc_uri_get_options;
  +    mongoc_uri_get_password;
  +    mongoc_uri_get_read_prefs;
  +    mongoc_uri_get_replica_set;
  +    mongoc_uri_get_string;
  +    mongoc_uri_get_username;
  +    mongoc_uri_new;
  +    mongoc_uri_new_for_host_port;
  +    mongoc_uri_unescape;
  +    mongoc_write_concern_copy;
  +    mongoc_write_concern_destroy;
  +    _mongoc_write_concern_freeze;
  +    mongoc_write_concern_get_fsync;
  +    mongoc_write_concern_get_journal;
  +    mongoc_write_concern_get_w;
  +    mongoc_write_concern_get_wmajority;
  +    mongoc_write_concern_get_wtimeout;
  +    _mongoc_write_concern_has_gle;
  +    mongoc_write_concern_new;
  +    mongoc_write_concern_set_fsync;
  +    mongoc_write_concern_set_journal;
  +    mongoc_write_concern_set_w;
  +    mongoc_write_concern_set_wmajority;
  +    mongoc_write_concern_set_wtimeout;
     local:
       *;
   };
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/md6.c
  ============================================================================
  $ cvs diff -u -r1.3.4.4 -r1.3.4.5 md6.c
  --- rpm/rpmio/md6.c	29 Sep 2014 18:26:48 -0000	1.3.4.4
  +++ rpm/rpmio/md6.c	30 Sep 2014 22:31:43 -0000	1.3.4.5
  @@ -596,8 +596,8 @@
     /* check that input values are sensible */
     if ( C == NULL ) return MD6_NULL_C;
     if ( B == NULL ) return MD6_NULL_B;
  -  if ( r < 0 | r > md6_max_r ) return MD6_BAD_r;
  -  if ( L < 0 | L > 255 ) return MD6_BAD_L;
  +  if ( r < 0 || r > md6_max_r ) return MD6_BAD_r;
  +  if ( L < 0 || L > 255 ) return MD6_BAD_L;
     if ( ell < 0 || ell > 255 ) return MD6_BAD_ELL;
     if ( p < 0 || p > b*w ) return MD6_BAD_p;
     if ( d <= 0 || d > c*w/2 ) return MD6_BADHASHLEN;
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/mongoc-counters.defs
  ============================================================================
  $ cvs diff -u -r0 -r1.1.2.1 mongoc-counters.defs
  --- /dev/null	2014-10-01 00:31:02.000000000 +0200
  +++ mongoc-counters.defs	2014-10-01 00:31:49.872721524 +0200
  @@ -0,0 +1,61 @@
  +/*
  + * Copyright 2013 MongoDB, Inc.
  + *
  + * Licensed under the Apache License, Version 2.0 (the "License");
  + * you may not use this file except in compliance with the License.
  + * You may obtain a copy of the License at
  + *
  + *   http://www.apache.org/licenses/LICENSE-2.0
  + *
  + * Unless required by applicable law or agreed to in writing, software
  + * distributed under the License is distributed on an "AS IS" BASIS,
  + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  + * See the License for the specific language governing permissions and
  + * limitations under the License.
  + */
  +
  +
  +COUNTER(op_egress_total,        "Operations",   "Egress Total",        "The number \
of sent operations.")  +COUNTER(op_ingress_total,       "Operations",   "Ingress \
Total",       "The number of received operations.")  +COUNTER(op_egress_query,        \
"Operations",   "Egress Queries",      "The number of sent Query operations.")  \
+COUNTER(op_ingress_query,       "Operations",   "Ingress Queries",     "The number \
of received Query operations.")  +COUNTER(op_egress_getmore,      "Operations",   \
"Egress GetMore",      "The number of sent GetMore operations.")  \
+COUNTER(op_ingress_getmore,     "Operations",   "Ingress GetMore",     "The number \
of received GetMore operations.")  +COUNTER(op_egress_insert,       "Operations",   \
"Egress Insert",       "The number of sent Insert operations.")  \
+COUNTER(op_ingress_insert,      "Operations",   "Ingress Insert",      "The number \
of received Insert operations.")  +COUNTER(op_egress_delete,       "Operations",   \
"Egress Delete",       "The number of sent Delete operations.")  \
+COUNTER(op_ingress_delete,      "Operations",   "Ingress Delete",      "The number \
of received Delete operations.")  +COUNTER(op_egress_update,       "Operations",   \
"Egress Update",       "The number of sent Update operations.")  \
+COUNTER(op_ingress_update,      "Operations",   "Ingress Update",      "The number \
of received Update operations.")  +COUNTER(op_egress_killcursors,  "Operations",   \
"Egress KillCursors",  "The number of sent KillCursors operations.")  \
+COUNTER(op_ingress_killcursors, "Operations",   "Ingress KillCursors", "The number \
of received KillCursors operations.")  +COUNTER(op_egress_msg,          "Operations", \
"Egress Msg",          "The number of sent Msg operations.")  \
+COUNTER(op_ingress_msg,         "Operations",   "Ingress Msg",         "The number \
of received Msg operations.")  +COUNTER(op_egress_reply,        "Operations",   \
"Egress Reply",        "The number of sent Reply operations.")  \
+COUNTER(op_ingress_reply,       "Operations",   "Ingress Reply",       "The number \
of received Reply operations.")  +
  +
  +COUNTER(cursors_active,         "Cursors",      "Active",              "The number \
of active cursors.")  +COUNTER(cursors_disposed,       "Cursors",      "Disposed",    \
"The number of disposed cursors.")  +
  +
  +COUNTER(clients_active,         "Clients",      "Active",              "The number \
of active clients.")  +COUNTER(clients_disposed,       "Clients",      "Disposed",    \
"The number of disposed clients.")  +
  +
  +COUNTER(streams_active,         "Streams",      "Active",              "The number \
of active streams.")  +COUNTER(streams_disposed,       "Streams",      "Disposed",    \
"The number of disposed streams.")  +COUNTER(streams_egress,         "Streams",      \
"Egress Bytes",        "The number of bytes sent.")  +COUNTER(streams_ingress,        \
"Streams",      "Ingress Bytes",       "The number of bytes received.")  \
+COUNTER(streams_timeout,        "Streams",      "N Socket Timeouts",   "The number \
of socket timeouts.")  +
  +
  +COUNTER(client_pools_active,    "Client Pools", "Active",              "The number \
of active client pools.")  +COUNTER(client_pools_disposed,  "Client Pools", \
"Disposed",            "The number of disposed client pools.")  +
  +
  +COUNTER(protocol_ingress_error, "Protocol",     "Ingress Errors",      "The number \
of protocol errors on ingress.")  +
  +
  +COUNTER(auth_failure,           "Auth",         "Failures",            "The number \
of failed authentication requests.")  +COUNTER(auth_success,           "Auth",        \
"Success",             "The number of successful authentication requests.")  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/mongoc.c
  ============================================================================
  $ cvs diff -u -r0 -r1.1.2.1 mongoc.c
  --- /dev/null	2014-10-01 00:31:02.000000000 +0200
  +++ mongoc.c	2014-10-01 00:31:51.772806645 +0200
  @@ -0,0 +1,17229 @@
  +/*
  + * Copyright 2013 MongoDB, Inc.
  + *
  + * Licensed under the Apache License, Version 2.0 (the "License");
  + * you may not use this file except in compliance with the License.
  + * You may obtain a copy of the License at
  + *
  + *   http://www.apache.org/licenses/LICENSE-2.0
  + *
  + * Unless required by applicable law or agreed to in writing, software
  + * distributed under the License is distributed on an "AS IS" BASIS,
  + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  + * See the License for the specific language governing permissions and
  + * limitations under the License.
  + */
  +
  +#include "system.h"
  +
  +#include <sys/mman.h>
  +#include <sys/shm.h>
  +
  +#if defined(__linux__)
  +# include <sys/syscall.h>
  +#elif defined(_WIN32)
  +# include <process.h>
  +#endif
  +
  +#ifndef _WIN32
  +# include <netdb.h>
  +# include <netinet/tcp.h>
  +#endif
  +
  +#ifdef _WIN32
  +# include <io.h>
  +# include <share.h>
  +#endif
  +#ifdef _WIN32
  +# include <winsock2.h>
  +# include <winerror.h>
  +#endif
  +
  +#include <sasl/sasl.h>
  +#include <sasl/saslutil.h>
  +
  +#include <openssl/bio.h>
  +#include <openssl/ssl.h>
  +#include <openssl/err.h>
  +#include <openssl/x509v3.h>
  +#include <openssl/crypto.h>
  +
  +#include <mongoc.h>
  +
  +#include "debug.h"
  +
  +/*==============================================================*/
  +/* --- mongoc-array.c */
  +
  +void
  +_mongoc_array_init (mongoc_array_t *array,
  +                    size_t          element_size)
  +{
  +   bson_return_if_fail(array);
  +   bson_return_if_fail(element_size);
  +
  +   array->len = 0;
  +   array->element_size = element_size;
  +   array->allocated = 128;
  +   array->data = bson_malloc0(array->allocated);
  +}
  +
  +
  +void
  +_mongoc_array_destroy (mongoc_array_t *array)
  +{
  +   if (array && array->data) {
  +      bson_free(array->data);
  +   }
  +}
  +
  +
  +void
  +_mongoc_array_append_vals (mongoc_array_t *array,
  +                           const void     *data,
  +                           uint32_t   n_elements)
  +{
  +   size_t len;
  +   size_t off;
  +   size_t next_size;
  +
  +   bson_return_if_fail(array);
  +   bson_return_if_fail(data);
  +
  +   off = array->element_size * array->len;
  +   len = (size_t)n_elements * array->element_size;
  +   if ((off + len) > array->allocated) {
  +      next_size = bson_next_power_of_two((uint32_t)(off + len));
  +      array->data = bson_realloc(array->data, next_size);
  +      array->allocated = next_size;
  +   }
  +
  +   memcpy((uint8_t *)array->data + off, data, len);
  +
  +   array->len += n_elements;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-buffer.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "buffer"
  +
  +#ifndef MONGOC_BUFFER_DEFAULT_SIZE
  +# define MONGOC_BUFFER_DEFAULT_SIZE 1024
  +#endif
  +
  +
  +#define SPACE_FOR(_b, _sz) (((ssize_t)(_b)->datalen - (ssize_t)(_b)->off - \
(ssize_t)(_b)->len) >= (ssize_t)(_sz))  +
  +
  +/**
  + * _mongoc_buffer_init:
  + * @buffer: A mongoc_buffer_t to initialize.
  + * @buf: A data buffer to attach to @buffer.
  + * @buflen: The size of @buflen.
  + * @realloc_func: A function to resize @buf.
  + *
  + * Initializes @buffer for use. If additional space is needed by @buffer, then
  + * @realloc_func will be called to resize @buf.
  + *
  + * @buffer takes ownership of @buf and will realloc it to zero bytes when
  + * cleaning up the data structure.
  + */
  +void
  +_mongoc_buffer_init (mongoc_buffer_t   *buffer,
  +                     uint8_t      *buf,
  +                     size_t             buflen,
  +                     bson_realloc_func  realloc_func)
  +{
  +   bson_return_if_fail(buffer);
  +   bson_return_if_fail(buf || !buflen);
  +
  +   if (!realloc_func) {
  +      realloc_func = bson_realloc;
  +   }
  +
  +   if (!buf || !buflen) {
  +      buf = realloc_func(NULL, MONGOC_BUFFER_DEFAULT_SIZE);
  +      buflen = MONGOC_BUFFER_DEFAULT_SIZE;
  +   }
  +
  +   memset(buffer, 0, sizeof *buffer);
  +
  +   buffer->data = buf;
  +   buffer->datalen = buflen;
  +   buffer->len = 0;
  +   buffer->off = 0;
  +   buffer->realloc_func = realloc_func;
  +}
  +
  +
  +/**
  + * _mongoc_buffer_destroy:
  + * @buffer: A mongoc_buffer_t.
  + *
  + * Cleanup after @buffer and release any allocated resources.
  + */
  +void
  +_mongoc_buffer_destroy (mongoc_buffer_t *buffer)
  +{
  +   bson_return_if_fail(buffer);
  +
  +   if (buffer->data && buffer->realloc_func) {
  +      buffer->realloc_func(buffer->data, 0);
  +   }
  +
  +   memset(buffer, 0, sizeof *buffer);
  +}
  +
  +
  +/**
  + * _mongoc_buffer_clear:
  + * @buffer: A mongoc_buffer_t.
  + * @zero: If the memory should be zeroed.
  + *
  + * Clears a buffers contents and resets it to initial state. You can request
  + * that the memory is zeroed, which might be useful if you know the contents
  + * contain security related information.
  + */
  +void
  +_mongoc_buffer_clear (mongoc_buffer_t *buffer,
  +                      bool      zero)
  +{
  +   bson_return_if_fail(buffer);
  +
  +   if (zero) {
  +      memset(buffer->data, 0, buffer->datalen);
  +   }
  +
  +   buffer->off = 0;
  +   buffer->len = 0;
  +}
  +
  +
  +/**
  + * mongoc_buffer_append_from_stream:
  + * @buffer; A mongoc_buffer_t.
  + * @stream: The stream to read from.
  + * @size: The number of bytes to read.
  + * @timeout_msec: The number of milliseconds to wait or -1 for the default
  + * @error: A location for a bson_error_t, or NULL.
  + *
  + * Reads from stream @size bytes and stores them in @buffer. This can be used
  + * in conjunction with reading RPCs from a stream. You read from the stream
  + * into this buffer and then scatter the buffer into the RPC.
  + *
  + * Returns: true if successful; otherwise false and @error is set.
  + */
  +bool
  +_mongoc_buffer_append_from_stream (mongoc_buffer_t *buffer,
  +                                   mongoc_stream_t *stream,
  +                                   size_t           size,
  +                                   int32_t          timeout_msec,
  +                                   bson_error_t    *error)
  +{
  +   uint8_t *buf;
  +   ssize_t ret;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail (buffer, false);
  +   bson_return_val_if_fail (stream, false);
  +   bson_return_val_if_fail (size, false);
  +
  +   BSON_ASSERT (buffer->datalen);
  +
  +   if (!SPACE_FOR (buffer, size)) {
  +      if (buffer->len) {
  +         memmove(&buffer->data[0], &buffer->data[buffer->off], buffer->len);
  +      }
  +      buffer->off = 0;
  +      if (!SPACE_FOR (buffer, size)) {
  +         buffer->datalen = bson_next_power_of_two ((uint32_t)size);
  +         buffer->data = buffer->realloc_func (buffer->data, buffer->datalen);
  +      }
  +   }
  +
  +   buf = &buffer->data[buffer->off + buffer->len];
  +   ret = mongoc_stream_read (stream, buf, size, size, timeout_msec);
  +   if (ret != size) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_SOCKET,
  +                      "Failed to read %u bytes from socket.",
  +                      (unsigned)size);
  +      RETURN (false);
  +   }
  +
  +   buffer->len += ret;
  +
  +   RETURN (true);
  +}
  +
  +
  +/**
  + * _mongoc_buffer_fill:
  + * @buffer: A mongoc_buffer_t.
  + * @stream: A stream to read from.
  + * @min_bytes: The minumum number of bytes to read.
  + * @error: A location for a bson_error_t or NULL.
  + *
  + * Attempts to fill the entire buffer, or at least @min_bytes.
  + *
  + * Returns: The number of buffered bytes, or -1 on failure.
  + */
  +ssize_t
  +_mongoc_buffer_fill (mongoc_buffer_t *buffer,
  +                     mongoc_stream_t *stream,
  +                     size_t           min_bytes,
  +                     int32_t          timeout_msec,
  +                     bson_error_t    *error)
  +{
  +   ssize_t ret;
  +   size_t avail_bytes;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(buffer, false);
  +   bson_return_val_if_fail(stream, false);
  +   bson_return_val_if_fail(min_bytes >= 0, false);
  +
  +   BSON_ASSERT(buffer->data);
  +   BSON_ASSERT(buffer->datalen);
  +
  +   if (min_bytes <= buffer->len) {
  +      RETURN (buffer->len);
  +   }
  +
  +   min_bytes -= buffer->len;
  +
  +   if (buffer->len) {
  +      memmove (&buffer->data[0], &buffer->data[buffer->off], buffer->len);
  +   }
  +
  +   buffer->off = 0;
  +
  +   if (!SPACE_FOR (buffer, min_bytes)) {
  +      buffer->datalen = bson_next_power_of_two ((uint32_t)(buffer->len + \
min_bytes));  +      buffer->data = bson_realloc (buffer->data, buffer->datalen);
  +   }
  +
  +   avail_bytes = buffer->datalen - buffer->len;
  +
  +   ret = mongoc_stream_read (stream,
  +                             &buffer->data[buffer->off + buffer->len],
  +                             avail_bytes, min_bytes, timeout_msec);
  +
  +   if (ret == -1) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_SOCKET,
  +                      "Failed to buffer %u bytes within %d milliseconds.",
  +                      (unsigned)min_bytes, (int)timeout_msec);
  +      RETURN (-1);
  +   }
  +
  +   buffer->len += ret;
  +
  +   if (buffer->len < min_bytes) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_SOCKET,
  +                      "Could only buffer %u of %u bytes in %d milliseconds.",
  +                      (unsigned)buffer->len,
  +                      (unsigned)min_bytes,
  +                      (int)timeout_msec);
  +      RETURN (-1);
  +   }
  +
  +   RETURN (buffer->len);
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-rpc.c */
  +
  +#define RPC(_name, _code) \
  +   static void \
  +   _mongoc_rpc_gather_##_name (mongoc_rpc_##_name##_t *rpc, \
  +                               mongoc_array_t *array) \
  +   { \
  +      mongoc_iovec_t iov; \
  +      BSON_ASSERT(rpc); \
  +      BSON_ASSERT(array); \
  +      rpc->msg_len = 0; \
  +      _code \
  +   }
  +#define INT32_FIELD(_name) \
  +   iov.iov_base = (void *)&rpc->_name; \
  +   iov.iov_len = 4; \
  +   BSON_ASSERT(iov.iov_len); \
  +   rpc->msg_len += (int32_t)iov.iov_len; \
  +   _mongoc_array_append_val(array, iov);
  +#define INT64_FIELD(_name) \
  +   iov.iov_base = (void *)&rpc->_name; \
  +   iov.iov_len = 8; \
  +   BSON_ASSERT(iov.iov_len); \
  +   rpc->msg_len += (int32_t)iov.iov_len; \
  +   _mongoc_array_append_val(array, iov);
  +#define CSTRING_FIELD(_name) \
  +   BSON_ASSERT(rpc->_name); \
  +   iov.iov_base = (void *)rpc->_name; \
  +   iov.iov_len = strlen(rpc->_name) + 1; \
  +   BSON_ASSERT(iov.iov_len); \
  +   rpc->msg_len += (int32_t)iov.iov_len; \
  +   _mongoc_array_append_val(array, iov);
  +#define BSON_FIELD(_name) \
  +   do { \
  +      int32_t __l; \
  +      memcpy(&__l, rpc->_name, 4); \
  +      __l = BSON_UINT32_FROM_LE(__l); \
  +      iov.iov_base = (void *)rpc->_name; \
  +      iov.iov_len = __l; \
  +      BSON_ASSERT(iov.iov_len); \
  +      rpc->msg_len += (int32_t)iov.iov_len; \
  +      _mongoc_array_append_val(array, iov); \
  +   } while (0);
  +#define BSON_OPTIONAL(_check, _code) \
  +   if (rpc->_check) { _code }
  +#define BSON_ARRAY_FIELD(_name) \
  +   iov.iov_base = (void *)rpc->_name; \
  +   iov.iov_len = rpc->_name##_len; \
  +   BSON_ASSERT(iov.iov_len); \
  +   rpc->msg_len += (int32_t)iov.iov_len; \
  +   _mongoc_array_append_val(array, iov);
  +#define IOVEC_ARRAY_FIELD(_name) \
  +   do { \
  +      ssize_t _i; \
  +      BSON_ASSERT(rpc->n_##_name); \
  +      for (_i = 0; _i < rpc->n_##_name; _i++) { \
  +         BSON_ASSERT(rpc->_name[_i].iov_len); \
  +         rpc->msg_len += (int32_t)rpc->_name[_i].iov_len; \
  +         _mongoc_array_append_val(array, rpc->_name[_i]); \
  +      } \
  +   } while (0);
  +#define RAW_BUFFER_FIELD(_name) \
  +   iov.iov_base = (void *)rpc->_name; \
  +   iov.iov_len = rpc->_name##_len; \
  +   BSON_ASSERT(iov.iov_len); \
  +   rpc->msg_len += (int32_t)iov.iov_len; \
  +   _mongoc_array_append_val(array, iov);
  +#define INT64_ARRAY_FIELD(_len, _name) \
  +   iov.iov_base = (void *)&rpc->_len; \
  +   iov.iov_len = 4; \
  +   BSON_ASSERT(iov.iov_len); \
  +   rpc->msg_len += (int32_t)iov.iov_len; \
  +   _mongoc_array_append_val(array, iov); \
  +   iov.iov_base = (void *)rpc->_name; \
  +   iov.iov_len = rpc->_len * 8; \
  +   BSON_ASSERT(iov.iov_len); \
  +   rpc->msg_len += (int32_t)iov.iov_len; \
  +   _mongoc_array_append_val(array, iov);
  +
  +
  +/*==============================================================*/
  +/* --- op-delete.def */
  +
  +RPC(
  +  delete,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +)
  +/*==============================================================*/
  +/* --- op-get-more.def */
  +
  +RPC(
  +  get_more,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(n_return)
  +  INT64_FIELD(cursor_id)
  +)
  +/*==============================================================*/
  +/* --- op-header.def */
  +
  +RPC(
  +  header,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +)
  +/*==============================================================*/
  +/* --- op-insert.def */
  +
  +RPC(
  +  insert,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  IOVEC_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-kill-cursors.def */
  +
  +RPC(
  +  kill_cursors,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  INT64_ARRAY_FIELD(n_cursors, cursors)
  +)
  +/*==============================================================*/
  +/* --- op-msg.def */
  +
  +RPC(
  +  msg,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  CSTRING_FIELD(msg)
  +)
  +/*==============================================================*/
  +/* --- op-query.def */
  +
  +RPC(
  +  query,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(skip)
  +  INT32_FIELD(n_return)
  +  BSON_FIELD(query)
  +  BSON_OPTIONAL(fields, BSON_FIELD(fields))
  +)
  +/*==============================================================*/
  +/* --- op-reply.def */
  +
  +RPC(
  +  reply,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  INT64_FIELD(cursor_id)
  +  INT32_FIELD(start_from)
  +  INT32_FIELD(n_returned)
  +  BSON_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-update.def */
  +
  +RPC(
  +  update,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +  BSON_FIELD(update)
  +)
  +/*==============================================================*/
  +
  +
  +
  +#undef RPC
  +#undef INT32_FIELD
  +#undef INT64_FIELD
  +#undef INT64_ARRAY_FIELD
  +#undef CSTRING_FIELD
  +#undef BSON_FIELD
  +#undef BSON_ARRAY_FIELD
  +#undef IOVEC_ARRAY_FIELD
  +#undef RAW_BUFFER_FIELD
  +#undef BSON_OPTIONAL
  +
  +
  +#if BSON_BYTE_ORDER == BSON_BIG_ENDIAN
  +
  +#define RPC(_name, _code) \
  +   static void \
  +   _mongoc_rpc_swab_to_le_##_name (mongoc_rpc_##_name##_t *rpc) \
  +   { \
  +      BSON_ASSERT(rpc); \
  +      _code \
  +   }
  +#define INT32_FIELD(_name) \
  +   rpc->_name = BSON_UINT32_FROM_LE(rpc->_name);
  +#define INT64_FIELD(_name) \
  +   rpc->_name = BSON_UINT64_FROM_LE(rpc->_name);
  +#define CSTRING_FIELD(_name)
  +#define BSON_FIELD(_name)
  +#define BSON_ARRAY_FIELD(_name)
  +#define IOVEC_ARRAY_FIELD(_name)
  +#define BSON_OPTIONAL(_check, _code) \
  +   if (rpc->_check) { _code }
  +#define RAW_BUFFER_FIELD(_name)
  +#define INT64_ARRAY_FIELD(_len, _name) \
  +   do { \
  +      ssize_t i; \
  +      for (i = 0; i < rpc->_len; i++) { \
  +         rpc->_name[i] = BSON_UINT64_FROM_LE(rpc->_name[i]); \
  +      } \
  +      rpc->_len = BSON_UINT32_FROM_LE(rpc->_len); \
  +   } while (0);
  +
  +
  +/*==============================================================*/
  +/* --- op-delete.def */
  +
  +RPC(
  +  delete,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +)
  +/*==============================================================*/
  +/* --- op-get-more.def */
  +
  +RPC(
  +  get_more,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(n_return)
  +  INT64_FIELD(cursor_id)
  +)
  +/*==============================================================*/
  +/* --- op-header.def */
  +
  +RPC(
  +  header,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +)
  +/*==============================================================*/
  +/* --- op-insert.def */
  +
  +RPC(
  +  insert,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  IOVEC_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-kill-cursors.def */
  +
  +RPC(
  +  kill_cursors,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  INT64_ARRAY_FIELD(n_cursors, cursors)
  +)
  +/*==============================================================*/
  +/* --- op-msg.def */
  +
  +RPC(
  +  msg,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  CSTRING_FIELD(msg)
  +)
  +/*==============================================================*/
  +/* --- op-query.def */
  +
  +RPC(
  +  query,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(skip)
  +  INT32_FIELD(n_return)
  +  BSON_FIELD(query)
  +  BSON_OPTIONAL(fields, BSON_FIELD(fields))
  +)
  +/*==============================================================*/
  +/* --- op-reply.def */
  +
  +RPC(
  +  reply,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  INT64_FIELD(cursor_id)
  +  INT32_FIELD(start_from)
  +  INT32_FIELD(n_returned)
  +  BSON_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-update.def */
  +
  +RPC(
  +  update,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +  BSON_FIELD(update)
  +)
  +/*==============================================================*/
  +
  +#undef RPC
  +#undef INT64_ARRAY_FIELD
  +
  +#define RPC(_name, _code) \
  +   static void \
  +   _mongoc_rpc_swab_from_le_##_name (mongoc_rpc_##_name##_t *rpc) \
  +   { \
  +      BSON_ASSERT(rpc); \
  +      _code \
  +   }
  +#define INT64_ARRAY_FIELD(_len, _name) \
  +   do { \
  +      ssize_t i; \
  +      rpc->_len = BSON_UINT32_FROM_LE(rpc->_len); \
  +      for (i = 0; i < rpc->_len; i++) { \
  +         rpc->_name[i] = BSON_UINT64_FROM_LE(rpc->_name[i]); \
  +      } \
  +   } while (0);
  +
  +
  +/*==============================================================*/
  +/* --- op-delete.def */
  +
  +RPC(
  +  delete,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +)
  +/*==============================================================*/
  +/* --- op-get-more.def */
  +
  +RPC(
  +  get_more,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(n_return)
  +  INT64_FIELD(cursor_id)
  +)
  +/*==============================================================*/
  +/* --- op-header.def */
  +
  +RPC(
  +  header,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +)
  +/*==============================================================*/
  +/* --- op-insert.def */
  +
  +RPC(
  +  insert,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  IOVEC_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-kill-cursors.def */
  +
  +RPC(
  +  kill_cursors,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  INT64_ARRAY_FIELD(n_cursors, cursors)
  +)
  +/*==============================================================*/
  +/* --- op-msg.def */
  +
  +RPC(
  +  msg,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  CSTRING_FIELD(msg)
  +)
  +/*==============================================================*/
  +/* --- op-query.def */
  +
  +RPC(
  +  query,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(skip)
  +  INT32_FIELD(n_return)
  +  BSON_FIELD(query)
  +  BSON_OPTIONAL(fields, BSON_FIELD(fields))
  +)
  +/*==============================================================*/
  +/* --- op-reply.def */
  +
  +RPC(
  +  reply,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  INT64_FIELD(cursor_id)
  +  INT32_FIELD(start_from)
  +  INT32_FIELD(n_returned)
  +  BSON_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-update.def */
  +
  +RPC(
  +  update,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +  BSON_FIELD(update)
  +)
  +/*==============================================================*/
  +
  +#undef RPC
  +#undef INT32_FIELD
  +#undef INT64_FIELD
  +#undef INT64_ARRAY_FIELD
  +#undef CSTRING_FIELD
  +#undef BSON_FIELD
  +#undef BSON_ARRAY_FIELD
  +#undef IOVEC_ARRAY_FIELD
  +#undef BSON_OPTIONAL
  +#undef RAW_BUFFER_FIELD
  +
  +#endif /* BSON_BYTE_ORDER == BSON_BIG_ENDIAN */
  +
  +
  +#define RPC(_name, _code) \
  +   static void \
  +   _mongoc_rpc_printf_##_name (mongoc_rpc_##_name##_t *rpc) \
  +   { \
  +      BSON_ASSERT(rpc); \
  +      _code \
  +   }
  +#define INT32_FIELD(_name) \
  +   printf("  "#_name" : %d\n", rpc->_name);
  +#define INT64_FIELD(_name) \
  +   printf("  "#_name" : %" PRIi64 "\n", (int64_t)rpc->_name);
  +#define CSTRING_FIELD(_name) \
  +   printf("  "#_name" : %s\n", rpc->_name);
  +#define BSON_FIELD(_name) \
  +   do { \
  +      bson_t b; \
  +      char *s; \
  +      int32_t __l; \
  +      memcpy(&__l, rpc->_name, 4); \
  +      __l = BSON_UINT32_FROM_LE(__l); \
  +      bson_init_static(&b, rpc->_name, __l); \
  +      s = bson_as_json(&b, NULL); \
  +      printf("  "#_name" : %s\n", s); \
  +      bson_free(s); \
  +      bson_destroy(&b); \
  +   } while (0);
  +#define BSON_ARRAY_FIELD(_name) \
  +   do { \
  +      bson_reader_t *__r; \
  +      bool __eof; \
  +      const bson_t *__b; \
  +      __r = bson_reader_new_from_data(rpc->_name, rpc->_name##_len); \
  +      while ((__b = bson_reader_read(__r, &__eof))) { \
  +         char *s = bson_as_json(__b, NULL); \
  +         printf("  "#_name" : %s\n", s); \
  +         bson_free(s); \
  +      } \
  +      bson_reader_destroy(__r); \
  +   } while (0);
  +#define IOVEC_ARRAY_FIELD(_name) \
  +   do { \
  +      ssize_t _i; \
  +      size_t _j; \
  +      for (_i = 0; _i < rpc->n_##_name; _i++) { \
  +         printf("  "#_name" : "); \
  +         for (_j = 0; _j < rpc->_name[_i].iov_len; _j++) { \
  +            uint8_t u; \
  +            u = ((char *)rpc->_name[_i].iov_base)[_j]; \
  +            printf(" %02x", u); \
  +         } \
  +         printf("\n"); \
  +      } \
  +   } while (0);
  +#define BSON_OPTIONAL(_check, _code) \
  +   if (rpc->_check) { _code }
  +#define RAW_BUFFER_FIELD(_name) \
  +   { \
  +      ssize_t __i; \
  +      printf("  "#_name" :"); \
  +      for (__i = 0; __i < rpc->_name##_len; __i++) { \
  +         uint8_t u; \
  +         u = ((char *)rpc->_name)[__i]; \
  +         printf(" %02x", u); \
  +      } \
  +      printf("\n"); \
  +   }
  +#define INT64_ARRAY_FIELD(_len, _name) \
  +   do { \
  +      ssize_t i; \
  +      for (i = 0; i < rpc->_len; i++) { \
  +         printf("  "#_name" : %" PRIi64 "\n", (int64_t)rpc->_name[i]); \
  +      } \
  +      rpc->_len = BSON_UINT32_FROM_LE(rpc->_len); \
  +   } while (0);
  +
  +
  +/*==============================================================*/
  +/* --- op-delete.def */
  +
  +RPC(
  +  delete,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +)
  +/*==============================================================*/
  +/* --- op-get-more.def */
  +
  +RPC(
  +  get_more,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(n_return)
  +  INT64_FIELD(cursor_id)
  +)
  +/*==============================================================*/
  +/* --- op-header.def */
  +
  +RPC(
  +  header,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +)
  +/*==============================================================*/
  +/* --- op-insert.def */
  +
  +RPC(
  +  insert,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  IOVEC_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-kill-cursors.def */
  +
  +RPC(
  +  kill_cursors,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  INT64_ARRAY_FIELD(n_cursors, cursors)
  +)
  +/*==============================================================*/
  +/* --- op-msg.def */
  +
  +RPC(
  +  msg,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  CSTRING_FIELD(msg)
  +)
  +/*==============================================================*/
  +/* --- op-query.def */
  +
  +RPC(
  +  query,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(skip)
  +  INT32_FIELD(n_return)
  +  BSON_FIELD(query)
  +  BSON_OPTIONAL(fields, BSON_FIELD(fields))
  +)
  +/*==============================================================*/
  +/* --- op-reply.def */
  +
  +RPC(
  +  reply,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  INT64_FIELD(cursor_id)
  +  INT32_FIELD(start_from)
  +  INT32_FIELD(n_returned)
  +  BSON_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-update.def */
  +
  +RPC(
  +  update,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +  BSON_FIELD(update)
  +)
  +/*==============================================================*/
  +
  +#undef RPC
  +#undef INT32_FIELD
  +#undef INT64_FIELD
  +#undef INT64_ARRAY_FIELD
  +#undef CSTRING_FIELD
  +#undef BSON_FIELD
  +#undef BSON_ARRAY_FIELD
  +#undef IOVEC_ARRAY_FIELD
  +#undef BSON_OPTIONAL
  +#undef RAW_BUFFER_FIELD
  +
  +
  +#define RPC(_name, _code) \
  +   static bool \
  +   _mongoc_rpc_scatter_##_name (mongoc_rpc_##_name##_t *rpc, \
  +                                const uint8_t *buf, \
  +                                size_t buflen) \
  +   { \
  +      BSON_ASSERT(rpc); \
  +      BSON_ASSERT(buf); \
  +      BSON_ASSERT(buflen); \
  +      _code \
  +      return true; \
  +   }
  +#define INT32_FIELD(_name) \
  +   if (buflen < 4) { \
  +      return false; \
  +   } \
  +   memcpy(&rpc->_name, buf, 4); \
  +   buflen -= 4; \
  +   buf += 4;
  +#define INT64_FIELD(_name) \
  +   if (buflen < 8) { \
  +      return false; \
  +   } \
  +   memcpy(&rpc->_name, buf, 8); \
  +   buflen -= 8; \
  +   buf += 8;
  +#define INT64_ARRAY_FIELD(_len, _name) \
  +   do { \
  +      size_t needed; \
  +      if (buflen < 4) { \
  +         return false; \
  +      } \
  +      memcpy(&rpc->_len, buf, 4); \
  +      buflen -= 4; \
  +      buf += 4; \
  +      needed = BSON_UINT32_FROM_LE(rpc->_len) * 8; \
  +      if (needed > buflen) { \
  +         return false; \
  +      } \
  +      rpc->_name = (void *)buf; \
  +      buf += needed; \
  +      buflen -= needed; \
  +   } while (0);
  +#define CSTRING_FIELD(_name) \
  +   do { \
  +      size_t __i; \
  +      bool found = false; \
  +      for (__i = 0; __i < buflen; __i++) { \
  +         if (!buf[__i]) { \
  +            rpc->_name = (const char *)buf; \
  +            buflen -= __i + 1; \
  +            buf += __i + 1; \
  +            found = true; \
  +            break; \
  +         } \
  +      } \
  +      if (!found) { \
  +         return false; \
  +      } \
  +   } while (0);
  +#define BSON_FIELD(_name) \
  +   do { \
  +      uint32_t __l; \
  +      if (buflen < 4) { \
  +         return false; \
  +      } \
  +      memcpy(&__l, buf, 4); \
  +      __l = BSON_UINT32_FROM_LE(__l); \
  +      if (__l < 5 || __l > buflen) { \
  +         return false; \
  +      } \
  +      rpc->_name = (uint8_t *)buf; \
  +      buf += __l; \
  +      buflen -= __l; \
  +   } while (0);
  +#define BSON_ARRAY_FIELD(_name) \
  +   rpc->_name = (uint8_t *)buf; \
  +   rpc->_name##_len = (int32_t)buflen; \
  +   buf = NULL; \
  +   buflen = 0;
  +#define BSON_OPTIONAL(_check, _code) \
  +   if (buflen) { \
  +      _code \
  +   }
  +#define IOVEC_ARRAY_FIELD(_name) \
  +   rpc->_name##_recv.iov_base = (void *)buf; \
  +   rpc->_name##_recv.iov_len = buflen; \
  +   rpc->_name = &rpc->_name##_recv; \
  +   rpc->n_##_name = 1; \
  +   buf = NULL; \
  +   buflen = 0;
  +#define RAW_BUFFER_FIELD(_name) \
  +   rpc->_name = (void *)buf; \
  +   rpc->_name##_len = (int32_t)buflen; \
  +   buf = NULL; \
  +   buflen = 0;
  +
  +
  +/*==============================================================*/
  +/* --- op-delete.def */
  +
  +RPC(
  +  delete,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +)
  +/*==============================================================*/
  +/* --- op-get-more.def */
  +
  +RPC(
  +  get_more,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(n_return)
  +  INT64_FIELD(cursor_id)
  +)
  +/*==============================================================*/
  +/* --- op-header.def */
  +
  +RPC(
  +  header,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +)
  +/*==============================================================*/
  +/* --- op-insert.def */
  +
  +RPC(
  +  insert,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  IOVEC_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-kill-cursors.def */
  +
  +RPC(
  +  kill_cursors,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  INT64_ARRAY_FIELD(n_cursors, cursors)
  +)
  +/*==============================================================*/
  +/* --- op-msg.def */
  +
  +RPC(
  +  msg,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  CSTRING_FIELD(msg)
  +)
  +/*==============================================================*/
  +/* --- op-query.def */
  +
  +RPC(
  +  query,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(skip)
  +  INT32_FIELD(n_return)
  +  BSON_FIELD(query)
  +  BSON_OPTIONAL(fields, BSON_FIELD(fields))
  +)
  +/*==============================================================*/
  +/* --- op-reply.def */
  +
  +RPC(
  +  reply,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(flags)
  +  INT64_FIELD(cursor_id)
  +  INT32_FIELD(start_from)
  +  INT32_FIELD(n_returned)
  +  BSON_ARRAY_FIELD(documents)
  +)
  +/*==============================================================*/
  +/* --- op-update.def */
  +
  +RPC(
  +  update,
  +  INT32_FIELD(msg_len)
  +  INT32_FIELD(request_id)
  +  INT32_FIELD(response_to)
  +  INT32_FIELD(opcode)
  +  INT32_FIELD(zero)
  +  CSTRING_FIELD(collection)
  +  INT32_FIELD(flags)
  +  BSON_FIELD(selector)
  +  BSON_FIELD(update)
  +)
  +/*==============================================================*/
  +
  +
  +#undef RPC
  +#undef INT32_FIELD
  +#undef INT64_FIELD
  +#undef INT64_ARRAY_FIELD
  +#undef CSTRING_FIELD
  +#undef BSON_FIELD
  +#undef BSON_ARRAY_FIELD
  +#undef IOVEC_ARRAY_FIELD
  +#undef BSON_OPTIONAL
  +#undef RAW_BUFFER_FIELD
  +
  +
  +void
  +_mongoc_rpc_gather (mongoc_rpc_t   *rpc,
  +                    mongoc_array_t *array)
  +{
  +   bson_return_if_fail(rpc);
  +   bson_return_if_fail(array);
  +
  +   switch ((mongoc_opcode_t)rpc->header.opcode) {
  +   case MONGOC_OPCODE_REPLY:
  +      _mongoc_rpc_gather_reply(&rpc->reply, array);
  +      return;
  +   case MONGOC_OPCODE_MSG:
  +      _mongoc_rpc_gather_msg(&rpc->msg, array);
  +      return;
  +   case MONGOC_OPCODE_UPDATE:
  +      _mongoc_rpc_gather_update(&rpc->update, array);
  +      return;
  +   case MONGOC_OPCODE_INSERT:
  +      _mongoc_rpc_gather_insert(&rpc->insert, array);
  +      return;
  +   case MONGOC_OPCODE_QUERY:
  +      _mongoc_rpc_gather_query(&rpc->query, array);
  +      return;
  +   case MONGOC_OPCODE_GET_MORE:
  +      _mongoc_rpc_gather_get_more(&rpc->get_more, array);
  +      return;
  +   case MONGOC_OPCODE_DELETE:
  +      _mongoc_rpc_gather_delete(&rpc->delete, array);
  +      return;
  +   case MONGOC_OPCODE_KILL_CURSORS:
  +      _mongoc_rpc_gather_kill_cursors(&rpc->kill_cursors, array);
  +      return;
  +   default:
  +      MONGOC_WARNING("Unknown rpc type: 0x%08x", rpc->header.opcode);
  +      break;
  +   }
  +}
  +
  +
  +void
  +_mongoc_rpc_swab_to_le (mongoc_rpc_t *rpc)
  +{
  +#if BSON_BYTE_ORDER != BSON_LITTLE_ENDIAN
  +   mongoc_opcode_t opcode;
  +
  +   bson_return_if_fail(rpc);
  +
  +   opcode = rpc->header.opcode;
  +
  +   switch (opcode) {
  +   case MONGOC_OPCODE_REPLY:
  +      _mongoc_rpc_swab_to_le_reply(&rpc->reply);
  +      break;
  +   case MONGOC_OPCODE_MSG:
  +      _mongoc_rpc_swab_to_le_msg(&rpc->msg);
  +      break;
  +   case MONGOC_OPCODE_UPDATE:
  +      _mongoc_rpc_swab_to_le_update(&rpc->update);
  +      break;
  +   case MONGOC_OPCODE_INSERT:
  +      _mongoc_rpc_swab_to_le_insert(&rpc->insert);
  +      break;
  +   case MONGOC_OPCODE_QUERY:
  +      _mongoc_rpc_swab_to_le_query(&rpc->query);
  +      break;
  +   case MONGOC_OPCODE_GET_MORE:
  +      _mongoc_rpc_swab_to_le_get_more(&rpc->get_more);
  +      break;
  +   case MONGOC_OPCODE_DELETE:
  +      _mongoc_rpc_swab_to_le_delete(&rpc->delete);
  +      break;
  +   case MONGOC_OPCODE_KILL_CURSORS:
  +      _mongoc_rpc_swab_to_le_kill_cursors(&rpc->kill_cursors);
  +      break;
  +   default:
  +      MONGOC_WARNING("Unknown rpc type: 0x%08x", opcode);
  +      break;
  +   }
  +#endif
  +}
  +
  +
  +void
  +_mongoc_rpc_swab_from_le (mongoc_rpc_t *rpc)
  +{
  +#if BSON_BYTE_ORDER != BSON_LITTLE_ENDIAN
  +   mongoc_opcode_t opcode;
  +
  +   bson_return_if_fail(rpc);
  +
  +   opcode = BSON_UINT32_FROM_LE(rpc->header.opcode);
  +
  +   switch (opcode) {
  +   case MONGOC_OPCODE_REPLY:
  +      _mongoc_rpc_swab_from_le_reply(&rpc->reply);
  +      break;
  +   case MONGOC_OPCODE_MSG:
  +      _mongoc_rpc_swab_from_le_msg(&rpc->msg);
  +      break;
  +   case MONGOC_OPCODE_UPDATE:
  +      _mongoc_rpc_swab_from_le_update(&rpc->update);
  +      break;
  +   case MONGOC_OPCODE_INSERT:
  +      _mongoc_rpc_swab_from_le_insert(&rpc->insert);
  +      break;
  +   case MONGOC_OPCODE_QUERY:
  +      _mongoc_rpc_swab_from_le_query(&rpc->query);
  +      break;
  +   case MONGOC_OPCODE_GET_MORE:
  +      _mongoc_rpc_swab_from_le_get_more(&rpc->get_more);
  +      break;
  +   case MONGOC_OPCODE_DELETE:
  +      _mongoc_rpc_swab_from_le_delete(&rpc->delete);
  +      break;
  +   case MONGOC_OPCODE_KILL_CURSORS:
  +      _mongoc_rpc_swab_from_le_kill_cursors(&rpc->kill_cursors);
  +      break;
  +   default:
  +      MONGOC_WARNING("Unknown rpc type: 0x%08x", rpc->header.opcode);
  +      break;
  +   }
  +#endif
  +}
  +
  +
  +void
  +_mongoc_rpc_printf (mongoc_rpc_t *rpc)
  +{
  +   bson_return_if_fail(rpc);
  +
  +   switch ((mongoc_opcode_t)rpc->header.opcode) {
  +   case MONGOC_OPCODE_REPLY:
  +      _mongoc_rpc_printf_reply(&rpc->reply);
  +      break;
  +   case MONGOC_OPCODE_MSG:
  +      _mongoc_rpc_printf_msg(&rpc->msg);
  +      break;
  +   case MONGOC_OPCODE_UPDATE:
  +      _mongoc_rpc_printf_update(&rpc->update);
  +      break;
  +   case MONGOC_OPCODE_INSERT:
  +      _mongoc_rpc_printf_insert(&rpc->insert);
  +      break;
  +   case MONGOC_OPCODE_QUERY:
  +      _mongoc_rpc_printf_query(&rpc->query);
  +      break;
  +   case MONGOC_OPCODE_GET_MORE:
  +      _mongoc_rpc_printf_get_more(&rpc->get_more);
  +      break;
  +   case MONGOC_OPCODE_DELETE:
  +      _mongoc_rpc_printf_delete(&rpc->delete);
  +      break;
  +   case MONGOC_OPCODE_KILL_CURSORS:
  +      _mongoc_rpc_printf_kill_cursors(&rpc->kill_cursors);
  +      break;
  +   default:
  +      MONGOC_WARNING("Unknown rpc type: 0x%08x", rpc->header.opcode);
  +      break;
  +   }
  +}
  +
  +
  +bool
  +_mongoc_rpc_scatter (mongoc_rpc_t  *rpc,
  +                     const uint8_t *buf,
  +                     size_t         buflen)
  +{
  +   mongoc_opcode_t opcode;
  +
  +   bson_return_val_if_fail(rpc, false);
  +   bson_return_val_if_fail(buf, false);
  +   bson_return_val_if_fail(buflen, false);
  +
  +   if (BSON_UNLIKELY(buflen < 16)) {
  +      return false;
  +   }
  +
  +   if (!_mongoc_rpc_scatter_header(&rpc->header, buf, 16)) {
  +      return false;
  +   }
  +
  +   opcode = BSON_UINT32_FROM_LE(rpc->header.opcode);
  +
  +   switch (opcode) {
  +   case MONGOC_OPCODE_REPLY:
  +      return _mongoc_rpc_scatter_reply(&rpc->reply, buf, buflen);
  +   case MONGOC_OPCODE_MSG:
  +      return _mongoc_rpc_scatter_msg(&rpc->msg, buf, buflen);
  +   case MONGOC_OPCODE_UPDATE:
  +      return _mongoc_rpc_scatter_update(&rpc->update, buf, buflen);
  +   case MONGOC_OPCODE_INSERT:
  +      return _mongoc_rpc_scatter_insert(&rpc->insert, buf, buflen);
  +   case MONGOC_OPCODE_QUERY:
  +      return _mongoc_rpc_scatter_query(&rpc->query, buf, buflen);
  +   case MONGOC_OPCODE_GET_MORE:
  +      return _mongoc_rpc_scatter_get_more(&rpc->get_more, buf, buflen);
  +   case MONGOC_OPCODE_DELETE:
  +      return _mongoc_rpc_scatter_delete(&rpc->delete, buf, buflen);
  +   case MONGOC_OPCODE_KILL_CURSORS:
  +      return _mongoc_rpc_scatter_kill_cursors(&rpc->kill_cursors, buf, buflen);
  +   default:
  +      MONGOC_WARNING("Unknown rpc type: 0x%08x", opcode);
  +      return false;
  +   }
  +}
  +
  +
  +bool
  +_mongoc_rpc_reply_get_first (mongoc_rpc_reply_t *reply,
  +                             bson_t             *bson)
  +{
  +   int32_t len;
  +
  +   bson_return_val_if_fail(reply, false);
  +   bson_return_val_if_fail(bson, false);
  +
  +   if (!reply->documents || reply->documents_len < 4) {
  +      return false;
  +   }
  +
  +   memcpy(&len, reply->documents, 4);
  +   len = BSON_UINT32_FROM_LE(len);
  +   if (reply->documents_len < len) {
  +      return false;
  +   }
  +
  +   return bson_init_static(bson, reply->documents, len);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_rpc_needs_gle --
  + *
  + *       Checks to see if an rpc requires a getlasterror command to
  + *       determine the success of the rpc.
  + *
  + *       The write_concern is checked to ensure that the caller wants
  + *       to know about a failure.
  + *
  + * Returns:
  + *       true if a getlasterror should be delivered; otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +_mongoc_rpc_needs_gle (mongoc_rpc_t                 *rpc,
  +                       const mongoc_write_concern_t *write_concern)
  +{
  +   bson_return_val_if_fail(rpc, false);
  +
  +   switch (rpc->header.opcode) {
  +   case MONGOC_OPCODE_REPLY:
  +   case MONGOC_OPCODE_QUERY:
  +   case MONGOC_OPCODE_MSG:
  +   case MONGOC_OPCODE_GET_MORE:
  +   case MONGOC_OPCODE_KILL_CURSORS:
  +      return false;
  +   case MONGOC_OPCODE_INSERT:
  +   case MONGOC_OPCODE_UPDATE:
  +   case MONGOC_OPCODE_DELETE:
  +   default:
  +      break;
  +   }
  +
  +   if (!write_concern || !mongoc_write_concern_get_w(write_concern)) {
  +      return false;
  +   }
  +
  +   return true;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-client.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "client"
  +
  +
  +#ifndef DEFAULT_CONNECTTIMEOUTMS
  +#define DEFAULT_CONNECTTIMEOUTMS (10 * 1000L)
  +#endif
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_connect_tcp --
  + *
  + *       Connect to a host using a TCP socket.
  + *
  + *       This will be performed synchronously and return a mongoc_stream_t
  + *       that can be used to connect with the remote host.
  + *
  + * Returns:
  + *       A newly allocated mongoc_stream_t if successful; otherwise
  + *       NULL and @error is set.
  + *
  + * Side effects:
  + *       @error is set if return value is NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static mongoc_stream_t *
  +mongoc_client_connect_tcp (const mongoc_uri_t       *uri,
  +                           const mongoc_host_list_t *host,
  +                           bson_error_t             *error)
  +{
  +   mongoc_socket_t *sock;
  +   struct addrinfo hints;
  +   struct addrinfo *result, *rp;
  +   int32_t connecttimeoutms = DEFAULT_CONNECTTIMEOUTMS;
  +   int64_t expire_at;
  +   const bson_t *options;
  +   bson_iter_t iter;
  +   char portstr [8];
  +   int s;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail (uri, NULL);
  +   bson_return_val_if_fail (host, NULL);
  +
  +   if ((options = mongoc_uri_get_options (uri)) &&
  +       bson_iter_init_find (&iter, options, "connecttimeoutms") &&
  +       BSON_ITER_HOLDS_INT32 (&iter)) {
  +      if (!(connecttimeoutms = bson_iter_int32(&iter))) {
  +         connecttimeoutms = DEFAULT_CONNECTTIMEOUTMS;
  +      }
  +   }
  +
  +   BSON_ASSERT (connecttimeoutms);
  +   expire_at = bson_get_monotonic_time () + (connecttimeoutms * 1000L);
  +
  +   bson_snprintf (portstr, sizeof portstr, "%hu", host->port);
  +
  +   memset (&hints, 0, sizeof hints);
  +   hints.ai_family = host->family;
  +   hints.ai_socktype = SOCK_STREAM;
  +   hints.ai_flags = 0;
  +   hints.ai_protocol = 0;
  +
  +   s = getaddrinfo (host->host, portstr, &hints, &result);
  +
  +   if (s != 0) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_STREAM,
  +                     MONGOC_ERROR_STREAM_NAME_RESOLUTION,
  +                     "Failed to resolve %s",
  +                     host->host);
  +      RETURN (NULL);
  +   }
  +
  +   for (rp = result; rp; rp = rp->ai_next) {
  +      /*
  +       * Create a new non-blocking socket.
  +       */
  +      if (!(sock = mongoc_socket_new (rp->ai_family,
  +                                      rp->ai_socktype,
  +                                      rp->ai_protocol))) {
  +         continue;
  +      }
  +
  +      /*
  +       * Try to connect to the peer.
  +       */
  +      if (0 != mongoc_socket_connect (sock,
  +                                      rp->ai_addr,
  +                                      (socklen_t)rp->ai_addrlen,
  +                                      expire_at)) {
  +         mongoc_socket_destroy (sock);
  +         sock = NULL;
  +         continue;
  +      }
  +
  +      break;
  +   }
  +
  +   if (!sock) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_CONNECT,
  +                      "Failed to connect to target host.");
  +      freeaddrinfo (result);
  +      RETURN (NULL);
  +   }
  +
  +   freeaddrinfo (result);
  +
  +   return mongoc_stream_socket_new (sock);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_connect_unix --
  + *
  + *       Connect to a MongoDB server using a UNIX domain socket.
  + *
  + * Returns:
  + *       A newly allocated mongoc_stream_t if successful; otherwise
  + *       NULL and @error is set.
  + *
  + * Side effects:
  + *       @error is set if return value is NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static mongoc_stream_t *
  +mongoc_client_connect_unix (const mongoc_uri_t       *uri,
  +                            const mongoc_host_list_t *host,
  +                            bson_error_t             *error)
  +{
  +#ifdef _WIN32
  +   ENTRY;
  +   bson_set_error (error,
  +                   MONGOC_ERROR_STREAM,
  +                   MONGOC_ERROR_STREAM_CONNECT,
  +                   "UNIX domain sockets not supported on win32.");
  +   RETURN (NULL);
  +#else
  +   struct sockaddr_un saddr;
  +   mongoc_socket_t *sock;
  +   mongoc_stream_t *ret = NULL;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail (uri, NULL);
  +   bson_return_val_if_fail (host, NULL);
  +
  +   memset (&saddr, 0, sizeof saddr);
  +   saddr.sun_family = AF_UNIX;
  +   bson_snprintf (saddr.sun_path, sizeof saddr.sun_path - 1,
  +                  "%s", host->host_and_port);
  +
  +   sock = mongoc_socket_new (AF_UNIX, SOCK_STREAM, 0);
  +
  +   if (sock == NULL) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_SOCKET,
  +                      "Failed to create socket.");
  +      RETURN (NULL);
  +   }
  +
  +   if (-1 == mongoc_socket_connect (sock,
  +                                    (struct sockaddr *)&saddr,
  +                                    sizeof saddr,
  +                                    -1)) {
  +      mongoc_socket_destroy (sock);
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_CONNECT,
  +                      "Failed to connect to UNIX domain socket.");
  +      RETURN (NULL);
  +   }
  +
  +   ret = mongoc_stream_socket_new (sock);
  +
  +   RETURN (ret);
  +#endif
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_default_stream_initiator --
  + *
  + *       A mongoc_stream_initiator_t that will handle the various type
  + *       of supported sockets by MongoDB including TCP and UNIX.
  + *
  + *       Language binding authors may want to implement an alternate
  + *       version of this method to use their native stream format.
  + *
  + * Returns:
  + *       A mongoc_stream_t if successful; otherwise NULL and @error is set.
  + *
  + * Side effects:
  + *       @error is set if return value is NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static mongoc_stream_t *
  +mongoc_client_default_stream_initiator (const mongoc_uri_t       *uri,
  +                                        const mongoc_host_list_t *host,
  +                                        void                     *user_data,
  +                                        bson_error_t             *error)
  +{
  +   mongoc_stream_t *base_stream = NULL;
  +#ifdef MONGOC_ENABLE_SSL
  +   mongoc_client_t *client = user_data;
  +   const bson_t *options;
  +   bson_iter_t iter;
  +   const char *mechanism;
  +#endif
  +
  +   bson_return_val_if_fail (uri, NULL);
  +   bson_return_val_if_fail (host, NULL);
  +
  +   switch (host->family) {
  +   case AF_INET:
  +      base_stream = mongoc_client_connect_tcp (uri, host, error);
  +      break;
  +   case AF_UNIX:
  +      base_stream = mongoc_client_connect_unix (uri, host, error);
  +      break;
  +   default:
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_INVALID_TYPE,
  +                      "Invalid address family: 0x%02x", host->family);
  +      break;
  +   }
  +
  +#ifdef MONGOC_ENABLE_SSL
  +   options = mongoc_uri_get_options (uri);
  +   mechanism = mongoc_uri_get_auth_mechanism (uri);
  +
  +   if ((bson_iter_init_find_case (&iter, options, "ssl") &&
  +        bson_iter_as_bool (&iter)) ||
  +       (mechanism && (0 == strcmp (mechanism, "MONGODB-X509")))) {
  +      base_stream = mongoc_stream_tls_new (base_stream, &client->ssl_opts,
  +                                           true);
  +
  +      if (!base_stream) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_STREAM,
  +                         MONGOC_ERROR_STREAM_SOCKET,
  +                         "Failed initialize TLS state.");
  +         return NULL;
  +      }
  +
  +      if (!mongoc_stream_tls_do_handshake (base_stream, -1) ||
  +          !mongoc_stream_tls_check_cert (base_stream, host->host)) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_STREAM,
  +                         MONGOC_ERROR_STREAM_SOCKET,
  +                         "Failed to handshake and validate TLS certificate.");
  +         mongoc_stream_destroy (base_stream);
  +         base_stream = NULL;
  +         return NULL;
  +      }
  +   }
  +#endif
  +
  +   return base_stream ? mongoc_stream_buffered_new (base_stream, 1024) : NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_client_create_stream --
  + *
  + *       INTERNAL API
  + *
  + *       This function is used by the mongoc_cluster_t to initiate a
  + *       new stream. This is done because cluster is private API and
  + *       those using mongoc_client_t may need to override this process.
  + *
  + *       This function calls the default initiator for new streams.
  + *
  + * Returns:
  + *       A newly allocated mongoc_stream_t if successful; otherwise
  + *       NULL and @error is set.
  + *
  + * Side effects:
  + *       @error is set if return value is NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_stream_t *
  +_mongoc_client_create_stream (mongoc_client_t          *client,
  +                              const mongoc_host_list_t *host,
  +                              bson_error_t             *error)
  +{
  +   bson_return_val_if_fail(client, NULL);
  +   bson_return_val_if_fail(host, NULL);
  +
  +   return client->initiator (client->uri, host, client->initiator_data, error);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_client_sendv --
  + *
  + *       INTERNAL API
  + *
  + *       This function is used to deliver one or more RPCs to the remote
  + *       MongoDB server.
  + *
  + *       Based on the cluster state and operation type, the request may
  + *       be retried. This is handled by the cluster instance.
  + *
  + * Returns:
  + *       0 upon failure and @error is set. Otherwise non-zero indicating
  + *       the cluster node that performed the request.
  + *
  + * Side effects:
  + *       @error is set if return value is 0.
  + *       @rpcs is mutated and therefore invalid after calling.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +uint32_t
  +_mongoc_client_sendv (mongoc_client_t              *client,
  +                      mongoc_rpc_t                 *rpcs,
  +                      size_t                        rpcs_len,
  +                      uint32_t                 hint,
  +                      const mongoc_write_concern_t *write_concern,
  +                      const mongoc_read_prefs_t    *read_prefs,
  +                      bson_error_t                 *error)
  +{
  +   size_t i;
  +
  +   bson_return_val_if_fail(client, false);
  +   bson_return_val_if_fail(rpcs, false);
  +   bson_return_val_if_fail(rpcs_len, false);
  +
  +   if (client->in_exhaust) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_CLIENT,
  +                     MONGOC_ERROR_CLIENT_IN_EXHAUST,
  +                     "A cursor derived from this client is in exhaust.");
  +      RETURN(false);
  +   }
  +
  +   for (i = 0; i < rpcs_len; i++) {
  +      rpcs[i].header.msg_len = 0;
  +      rpcs[i].header.request_id = ++client->request_id;
  +   }
  +
  +   switch (client->cluster.state) {
  +   case MONGOC_CLUSTER_STATE_BORN:
  +      return _mongoc_cluster_sendv(&client->cluster, rpcs, rpcs_len, hint,
  +                                   write_concern, read_prefs, error);
  +   case MONGOC_CLUSTER_STATE_HEALTHY:
  +   case MONGOC_CLUSTER_STATE_UNHEALTHY:
  +      return _mongoc_cluster_try_sendv(&client->cluster, rpcs, rpcs_len, hint,
  +                                       write_concern, read_prefs, error);
  +   case MONGOC_CLUSTER_STATE_DEAD:
  +      bson_set_error(error,
  +                     MONGOC_ERROR_CLIENT,
  +                     MONGOC_ERROR_CLIENT_NOT_READY,
  +                     "No healthy connections.");
  +      return false;
  +   default:
  +      BSON_ASSERT(false);
  +      return 0;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_client_recv --
  + *
  + *       Receives a RPC from a remote MongoDB cluster node. @hint should
  + *       be the result from a previous call to mongoc_client_sendv() to
  + *       signify which node to recv from.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @error is set if return value is false.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +_mongoc_client_recv (mongoc_client_t *client,
  +                     mongoc_rpc_t    *rpc,
  +                     mongoc_buffer_t *buffer,
  +                     uint32_t    hint,
  +                     bson_error_t    *error)
  +{
  +   bson_return_val_if_fail(client, false);
  +   bson_return_val_if_fail(rpc, false);
  +   bson_return_val_if_fail(buffer, false);
  +   bson_return_val_if_fail(hint, false);
  +   bson_return_val_if_fail(hint <= MONGOC_CLUSTER_MAX_NODES, false);
  +
  +   return _mongoc_cluster_try_recv (&client->cluster, rpc, buffer, hint,
  +                                    error);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _bson_to_error --
  + *
  + *       A helper routine to convert a bson document to a bson_error_t.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @error is set if non-null.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_bson_to_error (const bson_t *b,
  +                bson_error_t *error)
  +{
  +   bson_iter_t iter;
  +   int code = 0;
  +
  +   BSON_ASSERT(b);
  +
  +   if (!error) {
  +      return;
  +   }
  +
  +   if (bson_iter_init_find(&iter, b, "code") && BSON_ITER_HOLDS_INT32(&iter)) {
  +      code = bson_iter_int32(&iter);
  +   }
  +
  +   if (bson_iter_init_find(&iter, b, "$err") && BSON_ITER_HOLDS_UTF8(&iter)) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_QUERY,
  +                     code,
  +                     "%s",
  +                     bson_iter_utf8(&iter, NULL));
  +      return;
  +   }
  +
  +   if (bson_iter_init_find(&iter, b, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_QUERY,
  +                     code,
  +                     "%s",
  +                     bson_iter_utf8(&iter, NULL));
  +      return;
  +   }
  +
  +   bson_set_error(error,
  +                  MONGOC_ERROR_QUERY,
  +                  MONGOC_ERROR_QUERY_FAILURE,
  +                  "An unknown error ocurred on the server.");
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_recv_gle --
  + *
  + *       INTERNAL API
  + *
  + *       This function is used to receive the next RPC from a cluster
  + *       node, expecting it to be the response to a getlasterror command.
  + *
  + *       The RPC is parsed into @error if it is an error and false is
  + *       returned.
  + *
  + *       If the operation was successful, true is returned.
  + *
  + *       if @gle_doc is not NULL, then the actual response document for
  + *       the gle command will be stored as an out parameter. The caller
  + *       is responsible for freeing it in this case.
  + *
  + * Returns:
  + *       true if getlasterror was success; otherwise false and @error
  + *       is set.
  + *
  + * Side effects:
  + *       @error if return value is false.
  + *       @gle_doc will be set if non NULL and a reply was received.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +_mongoc_client_recv_gle (mongoc_client_t  *client,
  +                         uint32_t          hint,
  +                         bson_t          **gle_doc,
  +                         bson_error_t     *error)
  +{
  +   mongoc_buffer_t buffer;
  +   mongoc_rpc_t rpc;
  +   bson_iter_t iter;
  +   bool ret = false;
  +   bson_t b;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail (client, false);
  +   bson_return_val_if_fail (hint, false);
  +
  +   if (gle_doc) {
  +      *gle_doc = NULL;
  +   }
  +
  +   _mongoc_buffer_init (&buffer, NULL, 0, NULL);
  +
  +   if (!_mongoc_cluster_try_recv (&client->cluster, &rpc, &buffer,
  +                                  hint, error)) {
  +      GOTO (cleanup);
  +   }
  +
  +   if (rpc.header.opcode != MONGOC_OPCODE_REPLY) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_PROTOCOL,
  +                      MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
  +                      "Received message other than OP_REPLY.");
  +      GOTO (cleanup);
  +   }
  +
  +   if (_mongoc_rpc_reply_get_first (&rpc.reply, &b)) {
  +      if (gle_doc) {
  +         *gle_doc = bson_copy (&b);
  +      }
  +
  +      if ((rpc.reply.flags & MONGOC_REPLY_QUERY_FAILURE)) {
  +         _bson_to_error (&b, error);
  +         bson_destroy (&b);
  +         GOTO (cleanup);
  +      }
  +
  +      if (!bson_iter_init_find (&iter, &b, "ok") ||
  +          BSON_ITER_HOLDS_DOUBLE (&iter)) {
  +        if (bson_iter_double (&iter) == 0.0) {
  +          _bson_to_error (&b, error);
  +        }
  +      }
  +
  +      bson_destroy (&b);
  +   }
  +
  +   ret = true;
  +
  +cleanup:
  +   _mongoc_buffer_destroy (&buffer);
  +
  +   RETURN (ret);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_new --
  + *
  + *       Create a new mongoc_client_t using the URI provided.
  + *
  + *       @uri should be a MongoDB URI string such as "mongodb://localhost/"
  + *       More information on the format can be found at
  + *       http://docs.mongodb.org/manual/reference/connection-string/
  + *
  + * Returns:
  + *       A newly allocated mongoc_client_t or NULL if @uri_string is
  + *       invalid.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_client_t *
  +mongoc_client_new (const char *uri_string)
  +{
  +   mongoc_client_t *client;
  +   mongoc_uri_t *uri;
  +   const bson_t *options;
  +   bson_iter_t iter;
  +   bool has_ssl = false;
  +
  +   if (!uri_string) {
  +      uri_string = "mongodb://127.0.0.1/";
  +   }
  +
  +   if (!(uri = mongoc_uri_new(uri_string))) {
  +      return NULL;
  +   }
  +
  +   options = mongoc_uri_get_options (uri);
  +
  +   if (bson_iter_init_find (&iter, options, "ssl") &&
  +       BSON_ITER_HOLDS_BOOL (&iter) &&
  +       bson_iter_bool (&iter)) {
  +      has_ssl = true;
  +   }
  +
  +#ifndef MONGOC_ENABLE_SSL
  +   if (has_ssl) {
  +      MONGOC_WARNING ("SSL is not supported in this build!");
  +      return NULL;
  +   }
  +#endif
  +
  +   client = bson_malloc0(sizeof *client);
  +   client->uri = uri;
  +   client->request_id = rand ();
  +   client->initiator = mongoc_client_default_stream_initiator;
  +   client->initiator_data = client;
  +
  +   _mongoc_cluster_init (&client->cluster, client->uri, client);
  +
  +   mongoc_counter_clients_active_inc ();
  +
  +#ifdef MONGOC_ENABLE_SSL
  +   if (has_ssl) {
  +      mongoc_client_set_ssl_opts (client, mongoc_ssl_opt_get_default ());
  +   }
  +#endif
  +
  +   return client;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_set_ssl_opts
  + *
  + *       set ssl opts for a client
  + *
  + * Returns:
  + *       Nothing
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +#ifdef MONGOC_ENABLE_SSL
  +void
  +mongoc_client_set_ssl_opts (mongoc_client_t        *client,
  +                            const mongoc_ssl_opt_t *opts)
  +{
  +
  +   BSON_ASSERT (client);
  +   BSON_ASSERT (opts);
  +
  +   memcpy (&client->ssl_opts, opts, sizeof client->ssl_opts);
  +
  +   bson_free (client->pem_subject);
  +   client->pem_subject = NULL;
  +
  +   if (opts->pem_file) {
  +      client->pem_subject = _mongoc_ssl_extract_subject (opts->pem_file);
  +   }
  +}
  +#endif
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_new_from_uri --
  + *
  + *       Create a new mongoc_client_t for a mongoc_uri_t.
  + *
  + * Returns:
  + *       A newly allocated mongoc_client_t.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_client_t *
  +mongoc_client_new_from_uri (const mongoc_uri_t *uri)
  +{
  +   const char *uristr;
  +
  +   bson_return_val_if_fail(uri, NULL);
  +
  +   uristr = mongoc_uri_get_string(uri);
  +   return mongoc_client_new(uristr);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_destroy --
  + *
  + *       Destroys a mongoc_client_t and cleans up all resources associated
  + *       with the client instance.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @client is destroyed.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_client_destroy (mongoc_client_t *client)
  +{
  +   if (client) {
  +#ifdef MONGOC_ENABLE_SSL
  +      bson_free (client->pem_subject);
  +#endif
  +
  +      _mongoc_cluster_destroy (&client->cluster);
  +      mongoc_uri_destroy (client->uri);
  +      bson_free (client);
  +
  +      mongoc_counter_clients_active_dec ();
  +      mongoc_counter_clients_disposed_inc ();
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_get_uri --
  + *
  + *       Fetch the URI used for @client.
  + *
  + * Returns:
  + *       A mongoc_uri_t that should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const mongoc_uri_t *
  +mongoc_client_get_uri (const mongoc_client_t *client)
  +{
  +   bson_return_val_if_fail(client, NULL);
  +
  +   return client->uri;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_stamp --
  + *
  + *       INTERNAL API
  + *
  + *       Fetch the stamp for @node within @client. This is used to track
  + *       if there have been changes or disconnects from a node between
  + *       the last operation.
  + *
  + * Returns:
  + *       A 32-bit monotonic stamp.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +uint32_t
  +_mongoc_client_stamp (mongoc_client_t *client,
  +                      uint32_t    node)
  +{
  +   bson_return_val_if_fail (client, 0);
  +
  +   return _mongoc_cluster_stamp (&client->cluster, node);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_get_database --
  + *
  + *       Fetches a newly allocated database structure to communicate with
  + *       a database over @client.
  + *
  + *       @database should be a db name such as "test".
  + *
  + *       This structure should be freed when the caller is done with it
  + *       using mongoc_database_destroy().
  + *
  + * Returns:
  + *       A newly allocated mongoc_database_t.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_database_t *
  +mongoc_client_get_database (mongoc_client_t *client,
  +                            const char      *name)
  +{
  +   bson_return_val_if_fail(client, NULL);
  +   bson_return_val_if_fail(name, NULL);
  +
  +   return _mongoc_database_new(client, name, client->read_prefs, \
client->write_concern);  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_get_collection --
  + *
  + *       This function returns a newly allocated collection structure.
  + *
  + *       @db should be the name of the database, such as "test".
  + *       @collection should be the name of the collection such as "test".
  + *
  + *       The above would result in the namespace "test.test".
  + *
  + *       You should free this structure when you are done with it using
  + *       mongoc_collection_destroy().
  + *
  + * Returns:
  + *       A newly allocated mongoc_collection_t that should be freed with
  + *       mongoc_collection_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_collection_t *
  +mongoc_client_get_collection (mongoc_client_t *client,
  +                              const char      *db,
  +                              const char      *collection)
  +{
  +   bson_return_val_if_fail(client, NULL);
  +   bson_return_val_if_fail(db, NULL);
  +   bson_return_val_if_fail(collection, NULL);
  +
  +   return _mongoc_collection_new(client, db, collection, client->read_prefs,
  +                                 client->write_concern);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_get_gridfs --
  + *
  + *       This function returns a newly allocated collection structure.
  + *
  + *       @db should be the name of the database, such as "test".
  + *       @collection should be the name of the collection such as "test".
  + *
  + *       The above would result in the namespace "test.test".
  + *
  + *       You should free this structure when you are done with it using
  + *       mongoc_collection_destroy().
  + *
  + * Returns:
  + *       A newly allocated mongoc_collection_t that should be freed with
  + *       mongoc_collection_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_gridfs_t *
  +mongoc_client_get_gridfs (mongoc_client_t *client,
  +                          const char      *db,
  +                          const char      *prefix,
  +                          bson_error_t    *error)
  +{
  +   bson_return_val_if_fail(client, NULL);
  +   bson_return_val_if_fail(db, NULL);
  +
  +   if (! prefix) {
  +      prefix = "fs";
  +   }
  +
  +   return _mongoc_gridfs_new(client, db, prefix, error);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_get_write_concern --
  + *
  + *       Fetches the default write concern for @client.
  + *
  + * Returns:
  + *       A mongoc_write_concern_t that should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const mongoc_write_concern_t *
  +mongoc_client_get_write_concern (const mongoc_client_t *client)
  +{
  +   bson_return_val_if_fail(client, NULL);
  +
  +   return client->write_concern;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_set_write_concern --
  + *
  + *       Sets the default write concern for @client.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_client_set_write_concern (mongoc_client_t              *client,
  +                                 const mongoc_write_concern_t *write_concern)
  +{
  +   bson_return_if_fail(client);
  +
  +   if (write_concern != client->write_concern) {
  +      if (client->write_concern) {
  +         mongoc_write_concern_destroy(client->write_concern);
  +      }
  +      client->write_concern = write_concern ?
  +         mongoc_write_concern_copy(write_concern) :
  +         mongoc_write_concern_new();
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_get_read_prefs --
  + *
  + *       Fetch the default read preferences for @client.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const mongoc_read_prefs_t *
  +mongoc_client_get_read_prefs (const mongoc_client_t *client)
  +{
  +   bson_return_val_if_fail (client, NULL);
  +
  +   return client->read_prefs;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_client_set_read_prefs --
  + *
  + *       Set the default read preferences for @client.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_client_set_read_prefs (mongoc_client_t           *client,
  +                              const mongoc_read_prefs_t *read_prefs)
  +{
  +   bson_return_if_fail (client);
  +
  +   if (read_prefs != client->read_prefs) {
  +      if (client->read_prefs) {
  +         mongoc_read_prefs_destroy(client->read_prefs);
  +      }
  +      client->read_prefs = read_prefs ?
  +         mongoc_read_prefs_copy(read_prefs) :
  +         mongoc_read_prefs_new(MONGOC_READ_PRIMARY);
  +   }
  +}
  +
  +
  +bool
  +_mongoc_client_warm_up (mongoc_client_t *client,
  +                        bson_error_t    *error)
  +{
  +   bool ret = true;
  +   bson_t cmd;
  +
  +   BSON_ASSERT (client);
  +
  +   if (client->cluster.state == MONGOC_CLUSTER_STATE_BORN) {
  +      bson_init (&cmd);
  +      bson_append_int32 (&cmd, "ping", 4, 1);
  +      ret = _mongoc_cluster_command_early (&client->cluster, "admin", &cmd,
  +                                           NULL, error);
  +      bson_destroy (&cmd);
  +   } else if (client->cluster.state == MONGOC_CLUSTER_STATE_DEAD) {
  +      ret = _mongoc_cluster_reconnect(&client->cluster, error);
  +   }
  +
  +   return ret;
  +}
  +
  +
  +mongoc_cursor_t *
  +mongoc_client_command (mongoc_client_t           *client,
  +                       const char                *db_name,
  +                       mongoc_query_flags_t       flags,
  +                       uint32_t              skip,
  +                       uint32_t              limit,
  +                       uint32_t              batch_size,
  +                       const bson_t              *query,
  +                       const bson_t              *fields,
  +                       const mongoc_read_prefs_t *read_prefs)
  +{
  +   char ns[MONGOC_NAMESPACE_MAX];
  +
  +   BSON_ASSERT (client);
  +   BSON_ASSERT (db_name);
  +   BSON_ASSERT (query);
  +
  +   if (!read_prefs) {
  +      read_prefs = client->read_prefs;
  +   }
  +
  +   bson_snprintf (ns, sizeof ns, "%s.$cmd", db_name);
  +
  +   return _mongoc_cursor_new (client, ns, flags, skip, limit, batch_size, true,
  +                              query, fields, read_prefs);
  +}
  +
  +
  +/**
  + * mongoc_client_command_simple:
  + * @client: A mongoc_client_t.
  + * @db_name: The namespace, such as "admin".
  + * @command: The command to execute.
  + * @read_prefs: The read preferences or NULL.
  + * @reply: A location for the reply document or NULL.
  + * @error: A location for the error, or NULL.
  + *
  + * This wrapper around mongoc_client_command() aims to make it simpler to
  + * run a command and check the output result.
  + *
  + * false is returned if the command failed to be delivered or if the execution
  + * of the command failed. For example, a command that returns {'ok': 0} will
  + * result in this function returning false.
  + *
  + * To allow the caller to disambiguate between command execution failure and
  + * failure to send the command, reply will always be set if non-NULL. The
  + * caller should release this with bson_destroy().
  + *
  + * Returns: true if the command executed and resulted in success. Otherwise
  + *   false and @error is set. @reply is always set, either to the resulting
  + *   document or an empty bson document upon failure.
  + */
  +bool
  +mongoc_client_command_simple (mongoc_client_t           *client,
  +                              const char                *db_name,
  +                              const bson_t              *command,
  +                              const mongoc_read_prefs_t *read_prefs,
  +                              bson_t                    *reply,
  +                              bson_error_t              *error)
  +{
  +   mongoc_cursor_t *cursor;
  +   const bson_t *doc;
  +   bool ret;
  +
  +   BSON_ASSERT (client);
  +   BSON_ASSERT (db_name);
  +   BSON_ASSERT (command);
  +
  +   cursor = mongoc_client_command (client, db_name, MONGOC_QUERY_NONE, 0, 1, 0,
  +                                   command, NULL, read_prefs);
  +
  +   ret = mongoc_cursor_next (cursor, &doc);
  +
  +   if (reply) {
  +      if (ret) {
  +         bson_copy_to (doc, reply);
  +      } else {
  +         bson_init (reply);
  +      }
  +   }
  +
  +   if (!ret) {
  +      mongoc_cursor_error (cursor, error);
  +   }
  +
  +   mongoc_cursor_destroy (cursor);
  +
  +   return ret;
  +}
  +
  +
  +char **
  +mongoc_client_get_database_names (mongoc_client_t *client,
  +                                  bson_error_t    *error)
  +{
  +   bson_iter_t iter;
  +   bson_iter_t child;
  +   bson_iter_t child2;
  +   const char *name;
  +   bson_t cmd = BSON_INITIALIZER;
  +   bson_t reply;
  +   char **ret = NULL;
  +   int i = 0;
  +
  +   BSON_ASSERT (client);
  +
  +   BSON_APPEND_INT32 (&cmd, "listDatabases", 1);
  +
  +   if (!mongoc_client_command_simple (client, "admin", &cmd, NULL,
  +                                      &reply, error)) {
  +      bson_destroy (&cmd);
  +      return NULL;
  +   }
  +
  +   if (bson_iter_init_find (&iter, &reply, "databases") &&
  +       BSON_ITER_HOLDS_ARRAY (&iter) &&
  +       bson_iter_recurse (&iter, &child)) {
  +      while (bson_iter_next (&child)) {
  +         if (BSON_ITER_HOLDS_DOCUMENT (&child) &&
  +             bson_iter_recurse (&child, &child2) &&
  +             bson_iter_find (&child2, "name") &&
  +             BSON_ITER_HOLDS_UTF8 (&child2) &&
  +             (name = bson_iter_utf8 (&child2, NULL)) &&
  +             (0 != strcmp (name, "local"))) {
  +            ret = bson_realloc (ret, sizeof(char*) * (i + 2));
  +            ret [i] = bson_strdup (name);
  +            ret [++i] = NULL;
  +         }
  +      }
  +   }
  +
  +   if (!ret) {
  +      ret = bson_malloc0 (sizeof (void*));
  +   }
  +
  +   bson_destroy (&cmd);
  +   bson_destroy (&reply);
  +
  +   return ret;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-client-pool.c */
  +
  +struct _mongoc_client_pool_t
  +{
  +   mongoc_mutex_t    mutex;
  +   mongoc_cond_t     cond;
  +   mongoc_queue_t  queue;
  +   mongoc_uri_t   *uri;
  +   uint32_t   min_pool_size;
  +   uint32_t   max_pool_size;
  +   uint32_t   size;
  +};
  +
  +
  +mongoc_client_pool_t *
  +mongoc_client_pool_new (const mongoc_uri_t *uri)
  +{
  +   mongoc_client_pool_t *pool;
  +   const bson_t *b;
  +   bson_iter_t iter;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(uri, NULL);
  +
  +   pool = bson_malloc0(sizeof *pool);
  +   mongoc_mutex_init(&pool->mutex);
  +   _mongoc_queue_init(&pool->queue);
  +   pool->uri = mongoc_uri_copy(uri);
  +   pool->min_pool_size = 0;
  +   pool->max_pool_size = 100;
  +   pool->size = 0;
  +
  +   b = mongoc_uri_get_options(pool->uri);
  +
  +   if (bson_iter_init_find_case(&iter, b, "minpoolsize")) {
  +      if (BSON_ITER_HOLDS_INT32(&iter)) {
  +         pool->min_pool_size = MAX(0, bson_iter_int32(&iter));
  +      }
  +   }
  +
  +   if (bson_iter_init_find_case(&iter, b, "maxpoolsize")) {
  +      if (BSON_ITER_HOLDS_INT32(&iter)) {
  +         pool->max_pool_size = MAX(1, bson_iter_int32(&iter));
  +      }
  +   }
  +
  +   mongoc_counter_client_pools_active_inc();
  +
  +   RETURN(pool);
  +}
  +
  +
  +void
  +mongoc_client_pool_destroy (mongoc_client_pool_t *pool)
  +{
  +   mongoc_client_t *client;
  +
  +   ENTRY;
  +
  +   bson_return_if_fail(pool);
  +
  +   while ((client = _mongoc_queue_pop_head(&pool->queue))) {
  +      mongoc_client_destroy(client);
  +   }
  +
  +   mongoc_uri_destroy(pool->uri);
  +   mongoc_mutex_destroy(&pool->mutex);
  +   mongoc_cond_destroy(&pool->cond);
  +   bson_free(pool);
  +
  +   mongoc_counter_client_pools_active_dec();
  +   mongoc_counter_client_pools_disposed_inc();
  +
  +   EXIT;
  +}
  +
  +
  +mongoc_client_t *
  +mongoc_client_pool_pop (mongoc_client_pool_t *pool)
  +{
  +   mongoc_client_t *client;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(pool, NULL);
  +
  +   mongoc_mutex_lock(&pool->mutex);
  +
  +again:
  +   if (!(client = _mongoc_queue_pop_head(&pool->queue))) {
  +      if (pool->size < pool->max_pool_size) {
  +         client = mongoc_client_new_from_uri(pool->uri);
  +         pool->size++;
  +      } else {
  +         mongoc_cond_wait(&pool->cond, &pool->mutex);
  +         GOTO(again);
  +      }
  +   }
  +
  +   mongoc_mutex_unlock(&pool->mutex);
  +
  +   RETURN(client);
  +}
  +
  +
  +mongoc_client_t *
  +mongoc_client_pool_try_pop (mongoc_client_pool_t *pool)
  +{
  +   mongoc_client_t *client;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(pool, NULL);
  +
  +   mongoc_mutex_lock(&pool->mutex);
  +
  +   if (!(client = _mongoc_queue_pop_head(&pool->queue))) {
  +      if (pool->size < pool->max_pool_size) {
  +         client = mongoc_client_new_from_uri(pool->uri);
  +         pool->size++;
  +      }
  +   }
  +
  +   mongoc_mutex_unlock(&pool->mutex);
  +
  +   RETURN(client);
  +}
  +
  +
  +void
  +mongoc_client_pool_push (mongoc_client_pool_t *pool,
  +                         mongoc_client_t      *client)
  +{
  +   ENTRY;
  +
  +   bson_return_if_fail(pool);
  +   bson_return_if_fail(client);
  +
  +   /*
  +    * TODO: Shutdown old client connections.
  +    */
  +
  +   /*
  +    * TODO: We should try to make a client healthy again if it
  +    *       is unhealthy since this is typically where a thread
  +    *       is done with its work.
  +    */
  +
  +   mongoc_mutex_lock(&pool->mutex);
  +   _mongoc_queue_push_head(&pool->queue, client);
  +   mongoc_cond_signal(&pool->cond);
  +   mongoc_mutex_unlock(&pool->mutex);
  +
  +   EXIT;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-cluster.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "cluster"
  +
  +
  +#ifdef _WIN32
  +# define strcasecmp _stricmp
  +#endif
  +
  +#ifndef MAX_RETRY_COUNT
  +#define MAX_RETRY_COUNT 3
  +#endif
  +
  +
  +#define MIN_WIRE_VERSION 0
  +#define MAX_WIRE_VERSION 2
  +
  +
  +#ifndef DEFAULT_SOCKET_TIMEOUT_MSEC
  +/*
  + * NOTE: The default socket timeout for connections is 5 minutes. This
  + *       means that if your MongoDB server dies or becomes unavailable
  + *       it will take 5 minutes to detect this.
  + *
  + *       You can change this by providing sockettimeoutms= in your
  + *       connection URI.
  + */
  +#define DEFAULT_SOCKET_TIMEOUT_MSEC (1000L * 60L * 5L)
  +#endif
  +
  +
  +#ifndef UNHEALTHY_RECONNECT_TIMEOUT_USEC
  +/*
  + * Try reconnect every 20 seconds if we are unhealthy.
  + */
  +#define UNHEALTHY_RECONNECT_TIMEOUT_USEC (1000L * 1000L * 20L)
  +#endif
  +
  +
  +#define DB_AND_CMD_FROM_COLLECTION(outstr, name) \
  +   do { \
  +      const char *dot = strchr(name, '.'); \
  +      if (!dot || ((dot - name) > (sizeof outstr - 6))) { \
  +         bson_snprintf(outstr, sizeof outstr, "admin.$cmd"); \
  +      } else { \
  +         memcpy(outstr, name, dot - name); \
  +         memcpy(outstr + (dot - name), ".$cmd", 6); \
  +      } \
  +   } while (0)
  +
  +
  +/**
  + * _mongoc_cluster_negotiate_wire_version:
  + * @node: A #mongoc_cluster_t.
  + *
  + * Negotiate the wire-protocol version between all of our connected
  + * cluster nodes.
  + *
  + * If we cannot negotiate a wire-version amongst all of the nodes,
  + * then %false is returned and the connection should be dropped.
  + *
  + * If we can negotiate the wire-version amongst all of the nodes,
  + * then %true is returned and the clusters wire-version will be
  + * updated to reflect the coordinated version.
  + *
  + * Returns: %true if we negotiated, otherwise %false.
  + */
  +static int32_t
  +_mongoc_cluster_negotiate_wire_version (mongoc_cluster_t *cluster)
  +{
  +   mongoc_cluster_node_t *node;
  +   int32_t min_wire_version = MIN_WIRE_VERSION;
  +   int32_t max_wire_version = MAX_WIRE_VERSION;
  +   int i;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (cluster);
  +
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      node = &cluster->nodes[i];
  +
  +      if (node->stream) {
  +         if ((node->min_wire_version > max_wire_version) ||
  +             (node->max_wire_version < min_wire_version)) {
  +            RETURN (false);
  +         }
  +
  +         min_wire_version = MAX (min_wire_version, node->min_wire_version);
  +         max_wire_version = MIN (max_wire_version, node->max_wire_version);
  +      }
  +   }
  +
  +   BSON_ASSERT (min_wire_version <= max_wire_version);
  +   BSON_ASSERT (min_wire_version <= MAX_WIRE_VERSION);
  +   BSON_ASSERT (max_wire_version >= MIN_WIRE_VERSION);
  +
  +   cluster->wire_version = MAX (min_wire_version, max_wire_version);
  +
  +   RETURN (true);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_cluster_update_state --
  + *
  + *       Check the all peer nodes to update the cluster state.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_mongoc_cluster_update_state (mongoc_cluster_t *cluster)
  +{
  +   mongoc_cluster_state_t state;
  +   mongoc_cluster_node_t *node;
  +   int up_nodes = 0;
  +   int down_nodes = 0;
  +   int i;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      node = &cluster->nodes[i];
  +      if (node->stamp && !node->stream) {
  +         down_nodes++;
  +      } else if (node->stream) {
  +         up_nodes++;
  +      }
  +   }
  +
  +   if (!up_nodes && !down_nodes) {
  +      state = MONGOC_CLUSTER_STATE_BORN;
  +   } else if (!up_nodes && down_nodes) {
  +      state = MONGOC_CLUSTER_STATE_DEAD;
  +   } else if (up_nodes && !down_nodes) {
  +      state = MONGOC_CLUSTER_STATE_HEALTHY;
  +   } else {
  +      BSON_ASSERT(up_nodes);
  +      BSON_ASSERT(down_nodes);
  +      state = MONGOC_CLUSTER_STATE_UNHEALTHY;
  +   }
  +
  +   cluster->state = state;
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_cluster_add_peer --
  + *
  + *       Adds a peer to the list of peers that should be potentially
  + *       connected to as part of a replicaSet.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_mongoc_cluster_add_peer (mongoc_cluster_t *cluster,
  +                          const char       *peer)
  +{
  +   mongoc_list_t *iter;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +   BSON_ASSERT(peer);
  +
  +   MONGOC_DEBUG("Registering potential peer: %s", peer);
  +
  +   for (iter = cluster->peers; iter; iter = iter->next) {
  +      if (!strcmp(iter->data, peer)) {
  +         EXIT;
  +      }
  +   }
  +
  +   cluster->peers = _mongoc_list_prepend(cluster->peers, bson_strdup(peer));
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_clear_peers --
  + *
  + *       Clears list of cached potential peers that we've seen in the
  + *       "hosts" field of replicaSet nodes.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_mongoc_cluster_clear_peers (mongoc_cluster_t *cluster)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +
  +   _mongoc_list_foreach(cluster->peers, (void *)bson_free, NULL);
  +   _mongoc_list_destroy(cluster->peers);
  +   cluster->peers = NULL;
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_node_init --
  + *
  + *       Initialize a mongoc_cluster_node_t.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_mongoc_cluster_node_init (mongoc_cluster_node_t *node)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT(node);
  +
  +   memset(node, 0, sizeof *node);
  +
  +   node->index = 0;
  +   node->ping_avg_msec = -1;
  +   memset(node->pings, 0xFF, sizeof node->pings);
  +   node->pings_pos = 0;
  +   node->stamp = 0;
  +   bson_init(&node->tags);
  +   node->primary = 0;
  +   node->needs_auth = 0;
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_node_track_ping --
  + *
  + *       Add the ping time to the mongoc_cluster_node_t.
  + *       Increment the position in the ring buffer and update the
  + *       rolling average.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_mongoc_cluster_node_track_ping (mongoc_cluster_node_t *node,
  +                                 int32_t           ping)
  +{
  +   int total = 0;
  +   int count = 0;
  +   int i;
  +
  +   BSON_ASSERT(node);
  +
  +   node->pings[node->pings_pos] = ping;
  +   node->pings_pos = (node->pings_pos + 1) % MONGOC_CLUSTER_PING_NUM_SAMPLES;
  +
  +   for (i = 0; i < MONGOC_CLUSTER_PING_NUM_SAMPLES; i++) {
  +      if (node->pings[i] != -1) {
  +         total += node->pings[i];
  +         count++;
  +      }
  +   }
  +
  +   node->ping_avg_msec = count ? (int)((double)total / (double)count) : -1;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_cluster_node_destroy --
  + *
  + *       Destroy allocated resources within @node.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static void
  +_mongoc_cluster_node_destroy (mongoc_cluster_node_t *node)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT(node);
  +
  +   if (node->stream) {
  +      mongoc_stream_close(node->stream);
  +      mongoc_stream_destroy(node->stream);
  +      node->stream = NULL;
  +   }
  +
  +   bson_destroy(&node->tags);
  +
  +   bson_free(node->replSet);
  +   node->replSet = NULL;
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_cluster_build_basic_auth_digest --
  + *
  + *       Computes the Basic Authentication digest using the credentials
  + *       configured for @cluster and the @nonce provided.
  + *
  + *       The result should be freed by the caller using bson_free() when
  + *       they are finished with it.
  + *
  + * Returns:
  + *       A newly allocated string containing the digest.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static char *
  +_mongoc_cluster_build_basic_auth_digest (mongoc_cluster_t *cluster,
  +                                         const char       *nonce)
  +{
  +   const char *username;
  +   const char *password;
  +   char *password_digest;
  +   char *password_md5;
  +   char *digest_in;
  +   char *ret;
  +
  +   ENTRY;
  +
  +   /*
  +    * The following generates the digest to be used for basic authentication
  +    * with a MongoDB server. More information on the format can be found
  +    * at the following location:
  +    *
  +    * http://docs.mongodb.org/meta-driver/latest/legacy/
  +    *   implement-authentication-in-driver/
  +    */
  +
  +   BSON_ASSERT(cluster);
  +   BSON_ASSERT(cluster->uri);
  +
  +   username = mongoc_uri_get_username(cluster->uri);
  +   password = mongoc_uri_get_password(cluster->uri);
  +   password_digest = bson_strdup_printf("%s:mongo:%s", username, password);
  +   password_md5 = _mongoc_hex_md5(password_digest);
  +   digest_in = bson_strdup_printf("%s%s%s", nonce, username, password_md5);
  +   ret = _mongoc_hex_md5(digest_in);
  +   bson_free(digest_in);
  +   bson_free(password_md5);
  +   bson_free(password_digest);
  +
  +   RETURN(ret);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_cluster_disconnect_node --
  + *
  + *       Disconnects a cluster node and reinitializes it so it may be
  + *       connected to again in the future.
  + *
  + *       The stream is closed and destroyed.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +_mongoc_cluster_disconnect_node (mongoc_cluster_t      *cluster,
  +                                 mongoc_cluster_node_t *node)
  +{
  +   ENTRY;
  +
  +   bson_return_if_fail(node);
  +
  +   if (node->stream) {
  +      mongoc_stream_close(node->stream);
  +      mongoc_stream_destroy(node->stream);
  +      node->stream = NULL;
  +   }
  +
  +   node->needs_auth = cluster->requires_auth;
  +   node->ping_avg_msec = -1;
  +   memset(node->pings, 0xFF, sizeof node->pings);
  +   node->pings_pos = 0;
  +   node->stamp++;
  +   node->primary = 0;
  +
  +   bson_destroy (&node->tags);
  +   bson_init (&node->tags);
  +
  +   _mongoc_cluster_update_state (cluster);
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_init --
  + *
  + *       Initializes @cluster using the @uri and @client provided. The
  + *       @uri is used to determine the "mode" of the cluster. Based on the
  + *       uri we can determine if we are connected to a single host, a
  + *       replicaSet, or a shardedCluster.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @cluster is initialized.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +_mongoc_cluster_init (mongoc_cluster_t   *cluster,
  +                      const mongoc_uri_t *uri,
  +                      void               *client)
  +{
  +   const mongoc_host_list_t *hosts;
  +   uint32_t sockettimeoutms = DEFAULT_SOCKET_TIMEOUT_MSEC;
  +   uint32_t i;
  +   const bson_t *b;
  +   bson_iter_t iter;
  +
  +   ENTRY;
  +
  +   bson_return_if_fail (cluster);
  +   bson_return_if_fail (uri);
  +
  +   memset (cluster, 0, sizeof *cluster);
  +
  +   b = mongoc_uri_get_options(uri);
  +   hosts = mongoc_uri_get_hosts(uri);
  +
  +   if (bson_iter_init_find_case(&iter, b, "replicaSet")) {
  +      cluster->mode = MONGOC_CLUSTER_REPLICA_SET;
  +      MONGOC_INFO("Client initialized in replica set mode.");
  +   } else if (hosts->next) {
  +      cluster->mode = MONGOC_CLUSTER_SHARDED_CLUSTER;
  +      MONGOC_INFO("Client initialized in sharded cluster mode.");
  +   } else {
  +      cluster->mode = MONGOC_CLUSTER_DIRECT;
  +      MONGOC_INFO("Client initialized in direct mode.");
  +   }
  +
  +   if (bson_iter_init_find_case(&iter, b, "sockettimeoutms")) {
  +      if (!(sockettimeoutms = bson_iter_int32 (&iter))) {
  +         sockettimeoutms = DEFAULT_SOCKET_TIMEOUT_MSEC;
  +      }
  +   }
  +
  +   cluster->uri = mongoc_uri_copy(uri);
  +   cluster->client = client;
  +   cluster->sec_latency_ms = 15;
  +   cluster->max_msg_size = 1024 * 1024 * 48;
  +   cluster->max_bson_size = 1024 * 1024 * 16;
  +   cluster->requires_auth = (mongoc_uri_get_username (uri) ||
  +                             mongoc_uri_get_auth_mechanism (uri));
  +   cluster->sockettimeoutms = sockettimeoutms;
  +   cluster->wire_version = MAX_WIRE_VERSION;
  +
  +   if (bson_iter_init_find_case(&iter, b, "secondaryacceptablelatencyms") &&
  +       BSON_ITER_HOLDS_INT32(&iter)) {
  +      cluster->sec_latency_ms = bson_iter_int32(&iter);
  +   }
  +
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      _mongoc_cluster_node_init(&cluster->nodes[i]);
  +      cluster->nodes[i].stamp = 0;
  +      cluster->nodes[i].index = i;
  +      cluster->nodes[i].ping_avg_msec = -1;
  +      cluster->nodes[i].needs_auth = cluster->requires_auth;
  +   }
  +
  +   _mongoc_array_init (&cluster->iov, sizeof (mongoc_iovec_t));
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_destroy --
  + *
  + *       Clean up after @cluster and destroy all active connections.
  + *       All resources for @cluster are released.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       Everything.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +_mongoc_cluster_destroy (mongoc_cluster_t *cluster) /* INOUT */
  +{
  +   uint32_t i;
  +
  +   ENTRY;
  +
  +   bson_return_if_fail (cluster);
  +
  +   mongoc_uri_destroy (cluster->uri);
  +
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      if (cluster->nodes[i].stream) {
  +         mongoc_stream_destroy (cluster->nodes[i].stream);
  +         cluster->nodes[i].stream = NULL;
  +         cluster->nodes[i].stamp++;
  +      }
  +   }
  +
  +   _mongoc_cluster_clear_peers (cluster);
  +
  +   _mongoc_array_destroy (&cluster->iov);
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_select --
  + *
  + *       Selects a cluster node that is suitable for handling the required
  + *       set of rpc messages. The read_prefs are taken into account.
  + *
  + *       If any operation is a write, primary will be forced.
  + *
  + * Returns:
  + *       A mongoc_cluster_node_t if successful; otherwise NULL and
  + *       @error is set.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static mongoc_cluster_node_t *
  +_mongoc_cluster_select (mongoc_cluster_t             *cluster,
  +                        mongoc_rpc_t                 *rpcs,
  +                        size_t                        rpcs_len,
  +                        uint32_t                 hint,
  +                        const mongoc_write_concern_t *write_concern,
  +                        const mongoc_read_prefs_t    *read_prefs,
  +                        bson_error_t                 *error)
  +{
  +   mongoc_cluster_node_t *nodes[MONGOC_CLUSTER_MAX_NODES];
  +   mongoc_read_mode_t read_mode = MONGOC_READ_PRIMARY;
  +   uint32_t count;
  +   uint32_t watermark;
  +   int32_t nearest = -1;
  +   bool need_primary;
  +   bool need_secondary;
  +   unsigned i;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(cluster, NULL);
  +   bson_return_val_if_fail(rpcs, NULL);
  +   bson_return_val_if_fail(rpcs_len, NULL);
  +   bson_return_val_if_fail(hint <= MONGOC_CLUSTER_MAX_NODES, NULL);
  +
  +   /*
  +    * We can take a few short-cut's if we are not talking to a replica set.
  +    */
  +   switch (cluster->mode) {
  +   case MONGOC_CLUSTER_DIRECT:
  +      RETURN (cluster->nodes[0].stream ? &cluster->nodes[0] : NULL);
  +   case MONGOC_CLUSTER_SHARDED_CLUSTER:
  +      need_primary = false;
  +      need_secondary = false;
  +      GOTO (dispatch);
  +   case MONGOC_CLUSTER_REPLICA_SET:
  +   default:
  +      break;
  +   }
  +
  +   /*
  +    * Determine if our read preference requires communicating with PRIMARY.
  +    */
  +   if (read_prefs)
  +      read_mode = mongoc_read_prefs_get_mode(read_prefs);
  +   need_primary = (read_mode == MONGOC_READ_PRIMARY);
  +   need_secondary = (read_mode == MONGOC_READ_SECONDARY);
  +
  +   /*
  +    * Check to see if any RPCs require the primary. If so, we pin all
  +    * of the RPCs to the primary.
  +    */
  +   for (i = 0; !need_primary && (i < rpcs_len); i++) {
  +      switch (rpcs[i].header.opcode) {
  +      case MONGOC_OPCODE_KILL_CURSORS:
  +      case MONGOC_OPCODE_GET_MORE:
  +      case MONGOC_OPCODE_MSG:
  +      case MONGOC_OPCODE_REPLY:
  +         break;
  +      case MONGOC_OPCODE_QUERY:
  +         if ((read_mode & MONGOC_READ_SECONDARY) != 0) {
  +            rpcs[i].query.flags |= MONGOC_QUERY_SLAVE_OK;
  +         } else if (!(rpcs[i].query.flags & MONGOC_QUERY_SLAVE_OK)) {
  +            need_primary = true;
  +         }
  +         break;
  +      case MONGOC_OPCODE_DELETE:
  +      case MONGOC_OPCODE_INSERT:
  +      case MONGOC_OPCODE_UPDATE:
  +      default:
  +         need_primary = true;
  +         break;
  +      }
  +   }
  +
  +dispatch:
  +
  +   /*
  +    * Build our list of nodes with established connections. Short circuit if
  +    * we require a primary and we found one.
  +    */
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      if (need_primary && cluster->nodes[i].primary) {
  +         RETURN(&cluster->nodes[i]);
  +      } else if (need_secondary && cluster->nodes[i].primary) {
  +         nodes[i] = NULL;
  +      } else {
  +         nodes[i] = cluster->nodes[i].stream ? &cluster->nodes[i] : NULL;
  +      }
  +   }
  +
  +   /*
  +    * Check if we failed to locate a primary.
  +    */
  +   if (need_primary) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_CLIENT,
  +                     MONGOC_ERROR_CLIENT_NO_ACCEPTABLE_PEER,
  +                     "Requested PRIMARY node is not available.");
  +      RETURN(NULL);
  +   }
  +
  +   /*
  +    * Apply the hint if the client knows who they would like to continue
  +    * communicating with.
  +    */
  +   if (hint) {
  +      if (!nodes[hint - 1]) {
  +         bson_set_error(error,
  +                        MONGOC_ERROR_CLIENT,
  +                        MONGOC_ERROR_CLIENT_NO_ACCEPTABLE_PEER,
  +                        "Requested node (%u) is not available.",
  +                        hint);
  +      }
  +      RETURN(nodes[hint - 1]);
  +   }
  +
  +   /*
  +    * Now, we start removing connections that don't match the requirements of
  +    * our requested event.
  +    *
  +    * - If read preferences are set, remove all non-matching.
  +    * - If slaveOk exists and is false, then remove secondaries.
  +    * - Find the nearest leftover node and remove those not within threshold.
  +    * - Select a leftover node at random.
  +    */
  +
  +   /*
  +    * TODO: This whole section is ripe for optimization. It is very much
  +    *       in the fast path of command dispatching.
  +    */
  +
  +#define IS_NEARER_THAN(n, msec) \
  +   ((msec < 0 && (n)->ping_avg_msec >= 0) || ((n)->ping_avg_msec < msec))
  +
  +   count = 0;
  +
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      if (nodes[i]) {
  +         if (read_prefs) {
  +            int score = _mongoc_read_prefs_score(read_prefs, nodes[i]);
  +            if (score < 0) {
  +               nodes[i] = NULL;
  +               continue;
  +            }
  +         }
  +         if (IS_NEARER_THAN(nodes[i], nearest)) {
  +            nearest = nodes[i]->ping_avg_msec;
  +         }
  +         count++;
  +      }
  +   }
  +
  +#undef IS_NEARAR_THAN
  +
  +   /*
  +    * Filter nodes with latency outside threshold of nearest.
  +    */
  +   if (nearest != -1) {
  +      watermark = nearest + cluster->sec_latency_ms;
  +      for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +         if (nodes[i]) {
  +			 if (nodes[i]->ping_avg_msec >(int32_t)watermark) {
  +               nodes[i] = NULL;
  +            }
  +         }
  +      }
  +   }
  +
  +   /*
  +    * Choose a cluster node within threshold at random.
  +    */
  +   count = count ? rand() % count : count;
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      if (nodes[i]) {
  +         if (!count) {
  +            RETURN(nodes[i]);
  +         }
  +         count--;
  +      }
  +   }
  +
  +   RETURN(NULL);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_run_command --
  + *
  + *       Helper to run a command on a given mongoc_cluster_node_t.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @reply is set and should ALWAYS be released with bson_destroy().
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_cluster_run_command (mongoc_cluster_t      *cluster,
  +                             mongoc_cluster_node_t *node,
  +                             const char            *db_name,
  +                             const bson_t          *command,
  +                             bson_t                *reply,
  +                             bson_error_t          *error)
  +{
  +   mongoc_buffer_t buffer;
  +   mongoc_array_t ar;
  +   mongoc_rpc_t rpc;
  +   int32_t msg_len;
  +   bson_t reply_local;
  +   char ns[MONGOC_NAMESPACE_MAX];
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +   BSON_ASSERT(node);
  +   BSON_ASSERT(node->stream);
  +   BSON_ASSERT(db_name);
  +   BSON_ASSERT(command);
  +
  +   bson_snprintf(ns, sizeof ns, "%s.$cmd", db_name);
  +
  +   rpc.query.msg_len = 0;
  +   rpc.query.request_id = ++cluster->request_id;
  +   rpc.query.response_to = 0;
  +   rpc.query.opcode = MONGOC_OPCODE_QUERY;
  +   rpc.query.flags = MONGOC_QUERY_SLAVE_OK;
  +   rpc.query.collection = ns;
  +   rpc.query.skip = 0;
  +   rpc.query.n_return = -1;
  +   rpc.query.query = bson_get_data(command);
  +   rpc.query.fields = NULL;
  +
  +   _mongoc_array_init (&ar, sizeof (mongoc_iovec_t));
  +   _mongoc_buffer_init (&buffer, NULL, 0, NULL);
  +
  +   _mongoc_rpc_gather(&rpc, &ar);
  +   _mongoc_rpc_swab_to_le(&rpc);
  +
  +   if (!mongoc_stream_writev(node->stream, ar.data, ar.len,
  +                             cluster->sockettimeoutms)) {
  +      GOTO(failure);
  +   }
  +
  +   if (!_mongoc_buffer_append_from_stream(&buffer, node->stream, 4,
  +                                          cluster->sockettimeoutms, error)) {
  +      GOTO(failure);
  +   }
  +
  +   BSON_ASSERT(buffer.len == 4);
  +
  +   memcpy(&msg_len, buffer.data, 4);
  +   msg_len = BSON_UINT32_FROM_LE(msg_len);
  +   if ((msg_len < 16) || (msg_len > (1024 * 1024 * 16))) {
  +      GOTO(invalid_reply);
  +   }
  +
  +   if (!_mongoc_buffer_append_from_stream(&buffer, node->stream, msg_len - 4,
  +                                          cluster->sockettimeoutms, error)) {
  +      GOTO(failure);
  +   }
  +
  +   if (!_mongoc_rpc_scatter(&rpc, buffer.data, buffer.len)) {
  +      GOTO(invalid_reply);
  +   }
  +
  +   _mongoc_rpc_swab_from_le(&rpc);
  +
  +   if (rpc.header.opcode != MONGOC_OPCODE_REPLY) {
  +      GOTO(invalid_reply);
  +   }
  +
  +   if (reply) {
  +      if (!_mongoc_rpc_reply_get_first(&rpc.reply, &reply_local)) {
  +         GOTO(failure);
  +      }
  +      bson_copy_to(&reply_local, reply);
  +      bson_destroy(&reply_local);
  +   }
  +
  +   _mongoc_buffer_destroy(&buffer);
  +   _mongoc_array_destroy(&ar);
  +
  +   RETURN(true);
  +
  +invalid_reply:
  +   bson_set_error(error,
  +                  MONGOC_ERROR_PROTOCOL,
  +                  MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
  +                  "Invalid reply from server.");
  +
  +failure:
  +   _mongoc_buffer_destroy(&buffer);
  +   _mongoc_array_destroy(&ar);
  +
  +   if (reply) {
  +      bson_init(reply);
  +   }
  +
  +   _mongoc_cluster_disconnect_node(cluster, node);
  +
  +   RETURN(false);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_ismaster --
  + *
  + *       Executes an isMaster command on a given mongoc_cluster_node_t.
  + *
  + *       node->primary will be set to true if the node is discovered to
  + *       be a primary node.
  + *
  + * Returns:
  + *       true if successful; otehrwise false and @error is set.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_cluster_ismaster (mongoc_cluster_t      *cluster,
  +                         mongoc_cluster_node_t *node,
  +                         bson_error_t          *error)
  +{
  +   int32_t v32;
  +   bool ret = false;
  +   bson_iter_t child;
  +   bson_iter_t iter;
  +   bson_t command;
  +   bson_t reply;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +   BSON_ASSERT(node);
  +   BSON_ASSERT(node->stream);
  +
  +   bson_init(&command);
  +   bson_append_int32(&command, "isMaster", 8, 1);
  +
  +   if (!_mongoc_cluster_run_command (cluster, node, "admin", &command, &reply,
  +                                     error)) {
  +      _mongoc_cluster_disconnect_node (cluster, node);
  +      GOTO (failure);
  +   }
  +
  +   node->primary = false;
  +
  +   bson_free (node->replSet);
  +   node->replSet = NULL;
  +
  +   if (bson_iter_init_find_case (&iter, &reply, "isMaster") &&
  +       BSON_ITER_HOLDS_BOOL (&iter) &&
  +       bson_iter_bool (&iter)) {
  +      node->primary = true;
  +   }
  +
  +   if (bson_iter_init_find_case(&iter, &reply, "maxMessageSizeBytes")) {
  +      v32 = bson_iter_int32(&iter);
  +      if (!cluster->max_msg_size || (v32 < (int32_t)cluster->max_msg_size)) {
  +         cluster->max_msg_size = v32;
  +      }
  +   }
  +
  +   if (bson_iter_init_find_case(&iter, &reply, "maxBsonObjectSize")) {
  +      v32 = bson_iter_int32(&iter);
  +	  if (!cluster->max_bson_size || (v32 < (int32_t)cluster->max_bson_size)) {
  +         cluster->max_bson_size = v32;
  +      }
  +   }
  +
  +   if (bson_iter_init_find_case (&iter, &reply, "maxWriteBatchSize")) {
  +      v32 = bson_iter_int32 (&iter);
  +      node->max_write_batch_size = v32;
  +   }
  +
  +   if (bson_iter_init_find_case(&iter, &reply, "maxWireVersion") &&
  +       BSON_ITER_HOLDS_INT32(&iter)) {
  +      node->max_wire_version = bson_iter_int32(&iter);
  +   }
  +
  +   if (bson_iter_init_find_case(&iter, &reply, "minWireVersion") &&
  +       BSON_ITER_HOLDS_INT32(&iter)) {
  +      node->min_wire_version = bson_iter_int32(&iter);
  +   }
  +
  +   if (!_mongoc_cluster_negotiate_wire_version (cluster)) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_PROTOCOL,
  +                      MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION,
  +                      "Failed to negotiate wire version among all "
  +                      "cluster peers. Current wire version is %u. "
  +                      "%s is [%u,%u].",
  +                      cluster->wire_version,
  +                      node->host.host_and_port,
  +                      node->min_wire_version,
  +                      node->max_wire_version);
  +      GOTO (failure);
  +   }
  +
  +   if (bson_iter_init_find (&iter, &reply, "msg") &&
  +       BSON_ITER_HOLDS_UTF8 (&iter) &&
  +       (strcmp ("isdbgrid", bson_iter_utf8 (&iter, NULL)) == 0)) {
  +      /* TODO: is this sufficient to detect sharded clusters? */
  +
  +      cluster->isdbgrid = true;
  +      /*
  +       * TODO: This is actually a sharded cluster!
  +       */
  +      if (cluster->mode != MONGOC_CLUSTER_SHARDED_CLUSTER) {
  +         MONGOC_INFO ("Unexpectedly connected to sharded cluster: %s",
  +                      node->host.host_and_port);
  +      }
  +   } else {
  +      cluster->isdbgrid = false;
  +   }
  +
  +   /*
  +    * If we are in replicaSet mode, we need to track our potential peers for
  +    * further connections.
  +    */
  +   if (cluster->mode == MONGOC_CLUSTER_REPLICA_SET) {
  +      if (bson_iter_init_find (&iter, &reply, "hosts") &&
  +          bson_iter_recurse (&iter, &child)) {
  +         if (node->primary) {
  +            _mongoc_cluster_clear_peers (cluster);
  +         }
  +         while (bson_iter_next (&child) && BSON_ITER_HOLDS_UTF8 (&child)) {
  +            _mongoc_cluster_add_peer (cluster, bson_iter_utf8(&child, NULL));
  +         }
  +      }
  +      if (bson_iter_init_find(&iter, &reply, "setName") &&
  +          BSON_ITER_HOLDS_UTF8(&iter)) {
  +         node->replSet = bson_iter_dup_utf8(&iter, NULL);
  +      }
  +   }
  +
  +   ret = true;
  +
  +failure:
  +   bson_destroy(&command);
  +   bson_destroy(&reply);
  +
  +   RETURN(ret);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_ping_node --
  + *
  + *       Ping a remote node and return the round-trip latency.
  + *
  + * Returns:
  + *       A 32-bit integer counting the number of milliseconds to complete.
  + *       -1 if there was a failure to communicate.
  + *
  + * Side effects:
  + *       @error is set of -1 is returned.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static int32_t
  +_mongoc_cluster_ping_node (mongoc_cluster_t      *cluster,
  +                           mongoc_cluster_node_t *node,
  +                           bson_error_t          *error)
  +{
  +   int64_t t_begin;
  +   int64_t t_end;
  +   int32_t ret;
  +   bool r;
  +   bson_t cmd;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +   BSON_ASSERT(node);
  +   BSON_ASSERT(node->stream);
  +
  +   bson_init(&cmd);
  +   bson_append_int32(&cmd, "ping", 4, 1);
  +
  +   t_begin = bson_get_monotonic_time ();
  +   r = _mongoc_cluster_run_command (cluster, node, "admin", &cmd, NULL, error);
  +   t_end = bson_get_monotonic_time ();
  +
  +   bson_destroy(&cmd);
  +
  +   ret = r ? (int32_t) ((t_end - t_begin) / 1000L) : -1;
  +
  +   RETURN(ret);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_auth_node_cr --
  + *
  + *       Performs authentication of @node using the credentials provided
  + *       when configuring the @cluster instance.
  + *
  + *       This is the Challenge-Response mode of authentication.
  + *
  + * Returns:
  + *       true if authentication was successful; otherwise false and
  + *       @error is set.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_cluster_auth_node_cr (mongoc_cluster_t      *cluster,
  +                              mongoc_cluster_node_t *node,
  +                              bson_error_t          *error)
  +{
  +   bson_iter_t iter;
  +   const char *auth_source;
  +   bson_t command = { 0 };
  +   bson_t reply = { 0 };
  +   char *digest;
  +   char *nonce;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +   BSON_ASSERT(node);
  +
  +   if (!(auth_source = mongoc_uri_get_auth_source(cluster->uri))) {
  +      auth_source = "admin";
  +   }
  +
  +   /*
  +    * To authenticate a node using basic authentication, we need to first
  +    * get the nonce from the server. We use that to hash our password which
  +    * is sent as a reply to the server. If everything went good we get a
  +    * success notification back from the server.
  +    */
  +
  +   /*
  +    * Execute the getnonce command to fetch the nonce used for generating
  +    * md5 digest of our password information.
  +    */
  +   bson_init (&command);
  +   bson_append_int32 (&command, "getnonce", 8, 1);
  +   if (!_mongoc_cluster_run_command (cluster, node, auth_source, &command,
  +                                     &reply, error)) {
  +      bson_destroy (&command);
  +      RETURN (false);
  +   }
  +   bson_destroy (&command);
  +   if (!bson_iter_init_find_case (&iter, &reply, "nonce")) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_CLIENT,
  +                      MONGOC_ERROR_CLIENT_GETNONCE,
  +                      "Invalid reply from getnonce");
  +      bson_destroy (&reply);
  +      RETURN (false);
  +   }
  +
  +   /*
  +    * Build our command to perform the authentication.
  +    */
  +   nonce = bson_iter_dup_utf8(&iter, NULL);
  +   digest = _mongoc_cluster_build_basic_auth_digest(cluster, nonce);
  +   bson_init(&command);
  +   bson_append_int32(&command, "authenticate", 12, 1);
  +   bson_append_utf8(&command, "user", 4,
  +                    mongoc_uri_get_username(cluster->uri), -1);
  +   bson_append_utf8(&command, "nonce", 5, nonce, -1);
  +   bson_append_utf8(&command, "key", 3, digest, -1);
  +   bson_destroy(&reply);
  +   bson_free(nonce);
  +   bson_free(digest);
  +
  +   /*
  +    * Execute the authenticate command and check for {ok:1}
  +    */
  +   if (!_mongoc_cluster_run_command (cluster, node, auth_source, &command,
  +                                     &reply, error)) {
  +      bson_destroy (&command);
  +      RETURN (false);
  +   }
  +
  +   bson_destroy (&command);
  +
  +   if (!bson_iter_init_find_case(&iter, &reply, "ok") ||
  +       !bson_iter_as_bool(&iter)) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_CLIENT,
  +                     MONGOC_ERROR_CLIENT_AUTHENTICATE,
  +                     "Failed to authenticate credentials.");
  +      bson_destroy(&reply);
  +      RETURN(false);
  +   }
  +
  +   bson_destroy(&reply);
  +
  +   RETURN(true);
  +}
  +
  +
  +#ifdef MONGOC_ENABLE_SASL
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_auth_node_sasl --
  + *
  + *       Perform authentication for a cluster node using SASL. This is
  + *       only supported for GSSAPI at the moment.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_cluster_auth_node_sasl (mongoc_cluster_t      *cluster,
  +                                mongoc_cluster_node_t *node,
  +                                bson_error_t          *error)
  +{
  +   uint32_t buflen = 0;
  +   mongoc_sasl_t sasl;
  +   const bson_t *options;
  +   bson_iter_t iter;
  +   bool ret = false;
  +   const char *service_name;
  +   const char *mechanism;
  +   const char *tmpstr;
  +   uint8_t buf[4096] = { 0 };
  +   bson_t cmd;
  +   bson_t reply;
  +   int conv_id = 0;
  +
  +   BSON_ASSERT (cluster);
  +   BSON_ASSERT (node);
  +
  +   options = mongoc_uri_get_options (cluster->uri);
  +
  +   _mongoc_sasl_init (&sasl);
  +
  +   if ((mechanism = mongoc_uri_get_auth_mechanism (cluster->uri))) {
  +      _mongoc_sasl_set_mechanism (&sasl, mechanism);
  +   }
  +
  +   if (bson_iter_init_find_case (&iter, options, "gssapiservicename") &&
  +       BSON_ITER_HOLDS_UTF8 (&iter) &&
  +       (service_name = bson_iter_utf8 (&iter, NULL))) {
  +      _mongoc_sasl_set_service_name (&sasl, service_name);
  +   }
  +
  +   _mongoc_sasl_set_pass (&sasl, mongoc_uri_get_password (cluster->uri));
  +   _mongoc_sasl_set_user (&sasl, mongoc_uri_get_username (cluster->uri));
  +   _mongoc_sasl_set_service_host (&sasl, node->host.host);
  +
  +   for (;;) {
  +      if (!_mongoc_sasl_step (&sasl, buf, buflen, buf, sizeof buf, &buflen, \
error)) {  +         goto failure;
  +      }
  +
  +      bson_init (&cmd);
  +
  +      if (sasl.step == 1) {
  +         BSON_APPEND_INT32 (&cmd, "saslStart", 1);
  +         BSON_APPEND_UTF8 (&cmd, "mechanism", mechanism ? mechanism : "GSSAPI");
  +         bson_append_utf8 (&cmd, "payload", 7, (const char *)buf, buflen);
  +         BSON_APPEND_INT32 (&cmd, "autoAuthorize", 1);
  +      } else {
  +         BSON_APPEND_INT32 (&cmd, "saslContinue", 1);
  +         BSON_APPEND_INT32 (&cmd, "conversationId", conv_id);
  +         bson_append_utf8 (&cmd, "payload", 7, (const char *)buf, buflen);
  +      }
  +
  +      if (!_mongoc_cluster_run_command (cluster, node, "$external", &cmd, &reply, \
error)) {  +         bson_destroy (&cmd);
  +         goto failure;
  +      }
  +
  +      bson_destroy (&cmd);
  +
  +      if (bson_iter_init_find (&iter, &reply, "done") &&
  +          bson_iter_as_bool (&iter)) {
  +         bson_destroy (&reply);
  +         break;
  +      }
  +
  +      if (!bson_iter_init_find (&iter, &reply, "ok") ||
  +          !bson_iter_as_bool (&iter) ||
  +          !bson_iter_init_find (&iter, &reply, "conversationId") ||
  +          !BSON_ITER_HOLDS_INT32 (&iter) ||
  +          !(conv_id = bson_iter_int32 (&iter)) ||
  +          !bson_iter_init_find (&iter, &reply, "payload") ||
  +          !BSON_ITER_HOLDS_UTF8 (&iter)) {
  +         bson_destroy (&reply);
  +         bson_set_error (error,
  +                         MONGOC_ERROR_CLIENT,
  +                         MONGOC_ERROR_CLIENT_AUTHENTICATE,
  +                         "Received invalid SASL reply from MongoDB server.");
  +         goto failure;
  +      }
  +
  +      tmpstr = bson_iter_utf8 (&iter, &buflen);
  +
  +      if (buflen > sizeof buf) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_CLIENT,
  +                         MONGOC_ERROR_CLIENT_AUTHENTICATE,
  +                         "SASL reply from MongoDB is too large.");
  +         goto failure;
  +      }
  +
  +      memcpy (buf, tmpstr, buflen);
  +
  +      bson_destroy (&reply);
  +   }
  +
  +   ret = true;
  +
  +failure:
  +   _mongoc_sasl_destroy (&sasl);
  +
  +   return ret;
  +}
  +#endif
  +
  +
  +#ifdef MONGOC_ENABLE_SASL
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_auth_node_plain --
  + *
  + *       Perform SASL PLAIN authentication for @node. We do this manually
  + *       instead of using the SASL module because its rather simplistic.
  + *
  + * Returns:
  + *       true if successful; otherwise false and error is set.
  + *
  + * Side effects:
  + *       error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_cluster_auth_node_plain (mongoc_cluster_t      *cluster,
  +                                 mongoc_cluster_node_t *node,
  +                                 bson_error_t          *error)
  +{
  +   char buf[4096];
  +   unsigned buflen = 0;
  +   bson_iter_t iter;
  +   const char *username;
  +   const char *password;
  +   const char *errmsg = "Unknown authentication error.";
  +   bson_t b = BSON_INITIALIZER;
  +   bson_t reply;
  +   size_t len;
  +   char *str;
  +   int ret;
  +
  +   BSON_ASSERT (cluster);
  +   BSON_ASSERT (node);
  +
  +   username = mongoc_uri_get_username (cluster->uri);
  +   if (!username) {
  +      username = "";
  +   }
  +
  +   password = mongoc_uri_get_password (cluster->uri);
  +   if (!password) {
  +      password = "";
  +   }
  +
  +   str = bson_strdup_printf ("%c%s%c%s", '\0', username, '\0', password);
  +   len = strlen (username) + strlen (password) + 2;
  +   ret = sasl_encode64 (str, len, buf, sizeof buf, &buflen);
  +   bson_free (str);
  +
  +   if (ret != SASL_OK) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_CLIENT,
  +                      MONGOC_ERROR_CLIENT_AUTHENTICATE,
  +                      "sasl_encode64() returned %d.",
  +                      ret);
  +      return false;
  +   }
  +
  +   BSON_APPEND_INT32 (&b, "saslStart", 1);
  +   BSON_APPEND_UTF8 (&b, "mechanism", "PLAIN");
  +   bson_append_utf8 (&b, "payload", 7, (const char *)buf, buflen);
  +   BSON_APPEND_INT32 (&b, "autoAuthorize", 1);
  +
  +   if (!_mongoc_cluster_run_command (cluster, node, "$external", &b, &reply, \
error)) {  +      bson_destroy (&b);
  +      return false;
  +   }
  +
  +   bson_destroy (&b);
  +
  +   if (!bson_iter_init_find_case (&iter, &reply, "ok") ||
  +       !bson_iter_as_bool (&iter)) {
  +      if (bson_iter_init_find_case (&iter, &reply, "errmsg") &&
  +          BSON_ITER_HOLDS_UTF8 (&iter)) {
  +         errmsg = bson_iter_utf8 (&iter, NULL);
  +      }
  +      bson_destroy (&reply);
  +      bson_set_error (error,
  +                      MONGOC_ERROR_CLIENT,
  +                      MONGOC_ERROR_CLIENT_AUTHENTICATE,
  +                      "%s", errmsg);
  +      return false;
  +   }
  +
  +   bson_destroy (&reply);
  +
  +   return true;
  +}
  +#endif
  +
  +
  +#ifdef MONGOC_ENABLE_SSL
  +static bool
  +_mongoc_cluster_auth_node_x509 (mongoc_cluster_t      *cluster,
  +                                mongoc_cluster_node_t *node,
  +                                bson_error_t          *error)
  +{
  +   const char *username = "";
  +   const char *errmsg = "X509 authentication failure";
  +   bson_iter_t iter;
  +   bool ret = false;
  +   bson_t cmd;
  +   bson_t reply;
  +
  +   BSON_ASSERT (cluster);
  +   BSON_ASSERT (node);
  +
  +   if (!cluster->client->ssl_opts.pem_file) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_CLIENT,
  +                      MONGOC_ERROR_CLIENT_AUTHENTICATE,
  +                      "mongoc_client_set_ssl_opts() must be called "
  +                      "with pem file for X-509 auth.");
  +      return false;
  +   }
  +
  +   if (cluster->client->pem_subject) {
  +      username = cluster->client->pem_subject;
  +   }
  +
  +   bson_init (&cmd);
  +   BSON_APPEND_INT32 (&cmd, "authenticate", 1);
  +   BSON_APPEND_UTF8 (&cmd, "mechanism", "MONGODB-X509");
  +   BSON_APPEND_UTF8 (&cmd, "user", username);
  +
  +   if (!_mongoc_cluster_run_command (cluster, node, "$external", &cmd, &reply,
  +                                     error)) {
  +      bson_destroy (&cmd);
  +      return false;
  +   }
  +
  +   if (!bson_iter_init_find (&iter, &reply, "ok") ||
  +       !bson_iter_as_bool (&iter)) {
  +      if (bson_iter_init_find (&iter, &reply, "errmsg") &&
  +          BSON_ITER_HOLDS_UTF8 (&iter)) {
  +         errmsg = bson_iter_utf8 (&iter, NULL);
  +      }
  +      bson_set_error (error,
  +                      MONGOC_ERROR_CLIENT,
  +                      MONGOC_ERROR_CLIENT_AUTHENTICATE,
  +                      "%s", errmsg);
  +      goto failure;
  +   }
  +
  +   ret = true;
  +
  +failure:
  +
  +   bson_destroy (&cmd);
  +   bson_destroy (&reply);
  +
  +   return ret;
  +}
  +#endif
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_auth_node --
  + *
  + *       Authenticate a cluster node depending on the required mechanism.
  + *
  + * Returns:
  + *       true if authenticated. false on failure and @error is set.
  + *
  + * Side effects:
  + *       @error is set on failure.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_cluster_auth_node (mongoc_cluster_t      *cluster,
  +                           mongoc_cluster_node_t *node,
  +                           bson_error_t          *error)
  +{
  +   bool ret = false;
  +   const char *mechanism;
  +
  +   BSON_ASSERT (cluster);
  +   BSON_ASSERT (node);
  +
  +   mechanism = mongoc_uri_get_auth_mechanism (cluster->uri);
  +
  +   if (!mechanism) {
  +      mechanism = "MONGODB-CR";
  +   }
  +
  +   if (0 == strcasecmp (mechanism, "MONGODB-CR")) {
  +      ret = _mongoc_cluster_auth_node_cr (cluster, node, error);
  +#ifdef MONGOC_ENABLE_SSL
  +   } else if (0 == strcasecmp (mechanism, "MONGODB-X509")) {
  +      ret = _mongoc_cluster_auth_node_x509 (cluster, node, error);
  +#endif
  +#ifdef MONGOC_ENABLE_SASL
  +   } else if (0 == strcasecmp (mechanism, "GSSAPI")) {
  +      ret = _mongoc_cluster_auth_node_sasl (cluster, node, error);
  +   } else if (0 == strcasecmp (mechanism, "PLAIN")) {
  +      ret = _mongoc_cluster_auth_node_plain (cluster, node, error);
  +#endif
  +   } else {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_CLIENT,
  +                      MONGOC_ERROR_CLIENT_AUTHENTICATE,
  +                      "The authentication mechanism \"%s\" is not supported.",
  +                      mechanism);
  +   }
  +
  +   if (!ret) {
  +      mongoc_counter_auth_failure_inc ();
  +   } else {
  +      mongoc_counter_auth_success_inc ();
  +   }
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_cluster_reconnect_direct --
  + *
  + *       Reconnect to our only configured node.
  + *
  + *       "isMaster" is run after connecting to determine PRIMARY status.
  + *
  + *       If the node is valid, we will also greedily authenticate the
  + *       configured user if available.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_cluster_reconnect_direct (mongoc_cluster_t *cluster,
  +                                  bson_error_t     *error)
  +{
  +   const mongoc_host_list_t *hosts;
  +   mongoc_cluster_node_t *node;
  +   mongoc_stream_t *stream;
  +   struct timeval timeout;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +
  +   if (!(hosts = mongoc_uri_get_hosts(cluster->uri))) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_CLIENT,
  +                     MONGOC_ERROR_CLIENT_NOT_READY,
  +                     "Invalid host list supplied.");
  +      RETURN(false);
  +   }
  +
  +   cluster->last_reconnect = bson_get_monotonic_time();
  +
  +   node = &cluster->nodes[0];
  +
  +   node->index = 0;
  +   node->host = *hosts;
  +   node->needs_auth = cluster->requires_auth;
  +   node->primary = false;
  +   node->ping_avg_msec = -1;
  +   memset(node->pings, 0xFF, sizeof node->pings);
  +   node->pings_pos = 0;
  +   node->stream = NULL;
  +   node->stamp++;
  +   bson_init(&node->tags);
  +
  +   stream = _mongoc_client_create_stream (cluster->client, hosts, error);
  +   if (!stream) {
  +      RETURN (false);
  +   }
  +
  +   node->stream = stream;
  +   node->stamp++;
  +
  +   timeout.tv_sec = cluster->sockettimeoutms / 1000UL;
  +   timeout.tv_usec = (cluster->sockettimeoutms % 1000UL) * 1000UL;
  +   mongoc_stream_setsockopt (stream, SOL_SOCKET, SO_RCVTIMEO,
  +                             &timeout, sizeof timeout);
  +   mongoc_stream_setsockopt (stream, SOL_SOCKET, SO_SNDTIMEO,
  +                             &timeout, sizeof timeout);
  +
  +   if (!_mongoc_cluster_ismaster (cluster, node, error)) {
  +      _mongoc_cluster_disconnect_node (cluster, node);
  +      RETURN (false);
  +   }
  +
  +   if (node->needs_auth) {
  +      if (!_mongoc_cluster_auth_node (cluster, node, error)) {
  +         _mongoc_cluster_disconnect_node (cluster, node);
  +         RETURN (false);
  +      }
  +      node->needs_auth = false;
  +   }
  +
  +   _mongoc_cluster_update_state (cluster);
  +
  +   RETURN (true);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_reconnect_replica_set --
  + *
  + *       Reconnect to replicaSet members that are unhealthy.
  + *
  + *       Each of them will be checked for matching replicaSet name
  + *       and capabilities via an "isMaster" command.
  + *
  + *       The nodes will also be greedily authenticated with the
  + *       configured user if available.
  + *
  + * Returns:
  + *       true if there is an established stream that may be used,
  + *       otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @error is set upon failure if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_cluster_reconnect_replica_set (mongoc_cluster_t *cluster,
  +                                       bson_error_t     *error)
  +{
  +   const mongoc_host_list_t *hosts;
  +   const mongoc_host_list_t *iter;
  +   mongoc_cluster_node_t node;
  +   mongoc_cluster_node_t saved_nodes [MONGOC_CLUSTER_MAX_NODES];
  +   mongoc_host_list_t host;
  +   mongoc_stream_t *stream;
  +   mongoc_list_t *list;
  +   mongoc_list_t *liter;
  +   int32_t ping;
  +   const char *replSet;
  +   int i;
  +   int j;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cluster);
  +   BSON_ASSERT(cluster->mode == MONGOC_CLUSTER_REPLICA_SET);
  +
  +   MONGOC_DEBUG("Reconnecting to replica set.");
  +
  +   if (!(hosts = mongoc_uri_get_hosts(cluster->uri))) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_CLIENT,
  +                     MONGOC_ERROR_CLIENT_NOT_READY,
  +                     "Invalid host list supplied.");
  +      RETURN(false);
  +   }
  +
  +   replSet = mongoc_uri_get_replica_set(cluster->uri);
  +   BSON_ASSERT(replSet);
  +
  +   /*
  +    * Replica Set (Re)Connection Strategy
  +    * ===================================
  +    *
  +    * First we break all existing connections. This may change.
  +    *
  +    * To perform the replica set connection, we connect to each of the
  +    * pre-configured replicaSet nodes. (There may in fact only be one).
  +    *
  +    * TODO: We should perform this initial connection in parallel.
  +    *
  +    * Using the result of an "isMaster" on each of these nodes, we can
  +    * prime the cluster nodes we want to connect to.
  +    *
  +    * We then connect to all of these nodes in parallel. Once we have
  +    * all of the working nodes established, we can complete the process.
  +    *
  +    * We return true if any of the connections were successful, however
  +    * we must update the cluster health appropriately so that callers
  +    * that need a PRIMARY node can force reconnection.
  +    *
  +    * TODO: At some point in the future, we will need to authenticate
  +    *       before calling an "isMaster". But that is dependent on a
  +    *       few server "features" first.
  +    */
  +
  +   cluster->last_reconnect = bson_get_monotonic_time();
  +
  +   _mongoc_cluster_clear_peers (cluster);
  +
  +   /*
  +    * Discover all the potential peers from our seeds.
  +    */
  +   for (iter = hosts; iter; iter = iter->next) {
  +      stream = _mongoc_client_create_stream(cluster->client, iter, error);
  +      if (!stream) {
  +         MONGOC_WARNING("Failed connection to %s", iter->host_and_port);
  +         continue;
  +      }
  +
  +      _mongoc_cluster_node_init(&node);
  +      node.host = *iter;
  +      node.stream = stream;
  +
  +      if (!_mongoc_cluster_ismaster (cluster, &node, error)) {
  +         _mongoc_cluster_node_destroy (&node);
  +         continue;
  +      }
  +
  +      if (!node.replSet || !!strcmp (node.replSet, replSet)) {
  +         MONGOC_INFO("%s: Got replicaSet \"%s\" expected \"%s\".",
  +                     iter->host_and_port, node.replSet, replSet);
  +      }
  +
  +      if (node.primary) {
  +         _mongoc_cluster_node_destroy (&node);
  +         break;
  +      }
  +
  +      _mongoc_cluster_node_destroy (&node);
  +   }
  +
  +   list = cluster->peers;
  +   cluster->peers = NULL;
  +
  +   /*
  +    * To avoid reconnecting to all of the peers, we will save the
  +    * functional connections (and save their ping times) so that
  +    * we don't waste time doing that again.
  +    */
  +
  +   memset (saved_nodes, 0, sizeof saved_nodes);
  +
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      if (cluster->nodes [i].stream) {
  +         saved_nodes [i].host = cluster->nodes [i].host;
  +         saved_nodes [i].stream = cluster->nodes [i].stream;
  +         cluster->nodes [i].stream = NULL;
  +      }
  +   }
  +
  +   for (liter = list, i = 0;
  +        liter && (i < MONGOC_CLUSTER_MAX_NODES);
  +        liter = liter->next) {
  +
  +      if (!_mongoc_host_list_from_string(&host, liter->data)) {
  +         MONGOC_WARNING("Failed to parse host and port: \"%s\"",
  +                        (char *)liter->data);
  +         continue;
  +      }
  +
  +      stream = NULL;
  +
  +      for (j = 0; j < MONGOC_CLUSTER_MAX_NODES; j++) {
  +         if (0 == strcmp (saved_nodes [j].host.host_and_port,
  +                          host.host_and_port)) {
  +            stream = saved_nodes [j].stream;
  +            saved_nodes [j].stream = NULL;
  +         }
  +      }
  +
  +      if (!stream) {
  +         stream = _mongoc_client_create_stream (cluster->client, &host, error);
  +
  +         if (!stream) {
  +            MONGOC_WARNING("Failed connection to %s", host.host_and_port);
  +            continue;
  +         }
  +      }
  +
  +      _mongoc_cluster_node_init(&cluster->nodes[i]);
  +
  +      cluster->nodes[i].host = host;
  +      cluster->nodes[i].index = i;
  +      cluster->nodes[i].stream = stream;
  +
  +      if (!_mongoc_cluster_ismaster(cluster, &cluster->nodes[i], error)) {
  +         _mongoc_cluster_node_destroy(&cluster->nodes[i]);
  +         continue;
  +      }
  +
  +      if (!cluster->nodes[i].replSet ||
  +          !!strcmp (cluster->nodes[i].replSet, replSet)) {
  +         MONGOC_INFO ("%s: Got replicaSet \"%s\" expected \"%s\".",
  +                      host.host_and_port,
  +                      cluster->nodes[i].replSet,
  +                      replSet);
  +         _mongoc_cluster_node_destroy (&cluster->nodes[i]);
  +         continue;
  +      }
  +
  +      if (cluster->nodes[i].needs_auth) {
  +         if (!_mongoc_cluster_auth_node (cluster, &cluster->nodes[i], error)) {
  +            _mongoc_cluster_node_destroy (&cluster->nodes[i]);
  +            RETURN (false);
  +         }
  +         cluster->nodes[i].needs_auth = false;
  +      }
  +
  +      if (-1 == (ping = _mongoc_cluster_ping_node (cluster,
  +                                                   &cluster->nodes[i],
  +                                                   error))) {
  +         MONGOC_INFO("%s: Lost connection during ping.",
  +                     host.host_and_port);
  +         _mongoc_cluster_node_destroy (&cluster->nodes[i]);
  +         continue;
  +      }
  +
  +      _mongoc_cluster_node_track_ping(&cluster->nodes[i], ping);
  +
  +      i++;
  +   }
  +
  +   _mongoc_list_foreach(list, (void *)bson_free, NULL);
  +   _mongoc_list_destroy(list);
  +
  +   /*
  +    * Cleanup all potential saved connections that were not used.
  +    */
  +
  +   for (j = 0; j < MONGOC_CLUSTER_MAX_NODES; j++) {
  +      if (saved_nodes [j].stream) {
  +         mongoc_stream_destroy (saved_nodes [j].stream);
  +         saved_nodes [j].stream = NULL;
  +      }
  +   }
  +
  +   if (i == 0) {
  +      bson_set_error(error,
  +                     MONGOC_ERROR_CLIENT,
  +                     MONGOC_ERROR_CLIENT_NO_ACCEPTABLE_PEER,
  +                     "No acceptable peer could be found.");
  +      RETURN(false);
  +   }
  +
  +   _mongoc_cluster_update_state (cluster);
  +
  +   RETURN(true);
  +}
  +
  +
  +static bool
  +_mongoc_cluster_reconnect_sharded_cluster (mongoc_cluster_t *cluster,
  +                                           bson_error_t     *error)
  +{
  +   const mongoc_host_list_t *hosts;
  +   const mongoc_host_list_t *iter;
  +   mongoc_stream_t *stream;
  +   uint32_t i;
  +   int32_t ping;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (cluster);
  +
  +   MONGOC_DEBUG ("Reconnecting to sharded cluster.");
  +
  +   /*
  +    * Sharded Cluster (Re)Connection Strategy
  +    * =======================================
  +    *
  +    * First we break all existing connections. This may and probably
  +    * should change.
  +    *
  +    * Sharded cluster connection is pretty simple, in that we just need
  +    * to connect to all of the nodes that we are configured to know
  +    * about. The reconnect_direct case will also update things if it
  +    * discovers that the node it connected to was a sharded cluster.
  +    *
  +    * We need to check for "msg" field of the "isMaster" command to
  +    * ensure that we have connected to an "isdbgrid".
  +    *
  +    * If we can connect to all of the nodes, we are in a good state,
  +    * otherwise we are in an unhealthy state. If no connections were
  +    * established then we are in a failed state.
  +    */
  +
  +   cluster->last_reconnect = bson_get_monotonic_time ();
  +
  +   hosts = mongoc_uri_get_hosts (cluster->uri);
  +
  +   /*
  +    * Reconnect to each of our configured hosts.
  +    */
  +   for (iter = hosts, i = 0; iter; iter = iter->next) {
  +      stream = _mongoc_client_create_stream (cluster->client, iter, error);
  +
  +      if (!stream) {
  +         MONGOC_WARNING ("Failed connection to %s", iter->host_and_port);
  +         continue;
  +      }
  +
  +      _mongoc_cluster_node_init (&cluster->nodes[i]);
  +
  +      cluster->nodes[i].host = *iter;
  +      cluster->nodes[i].index = i;
  +      cluster->nodes[i].stream = stream;
  +
  +      if (!_mongoc_cluster_ismaster (cluster, &cluster->nodes[i], error)) {
  +         _mongoc_cluster_node_destroy (&cluster->nodes[i]);
  +         continue;
  +      }
  +
  +      if (cluster->nodes[i].needs_auth) {
  +         if (!_mongoc_cluster_auth_node (cluster, &cluster->nodes[i], error)) {
  +            _mongoc_cluster_node_destroy (&cluster->nodes[i]);
  +            RETURN (false);
  +         }
  +         cluster->nodes[i].needs_auth = false;
  +      }
  +
  +      if (-1 == (ping = _mongoc_cluster_ping_node (cluster,
  +                                                   &cluster->nodes[i],
  +                                                   error))) {
  +         MONGOC_INFO ("%s: Lost connection during ping.",
  +                      iter->host_and_port);
  +         _mongoc_cluster_node_destroy (&cluster->nodes[i]);
  +         continue;
  +      }
  +
  +      _mongoc_cluster_node_track_ping (&cluster->nodes[i], ping);
  +
  +      i++;
  +   }
  +
  +   if (i == 0) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_CLIENT,
  +                      MONGOC_ERROR_CLIENT_NO_ACCEPTABLE_PEER,
  +                      "No acceptable peer could be found.");
  +      RETURN (false);
  +   }
  +
  +   RETURN (true);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_cluster_reconnect --
  + *
  + *       Reconnect to the cluster nodes.
  + *
  + *       This is called when no nodes were available to execute an
  + *       operation on.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +_mongoc_cluster_reconnect (mongoc_cluster_t *cluster,
  +                           bson_error_t     *error)
  +{
  +   bool ret;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail (cluster, false);
  +
  +   switch (cluster->mode) {
  +   case MONGOC_CLUSTER_DIRECT:
  +      ret = _mongoc_cluster_reconnect_direct (cluster, error);
  +      RETURN (ret);
  +   case MONGOC_CLUSTER_REPLICA_SET:
  +      ret = _mongoc_cluster_reconnect_replica_set (cluster, error);
  +      RETURN (ret);
  +   case MONGOC_CLUSTER_SHARDED_CLUSTER:
  +      ret = _mongoc_cluster_reconnect_sharded_cluster (cluster, error);
  +      RETURN (ret);
  +   default:
  +      break;
  +   }
  +
  +   bson_set_error(error,
  +                  MONGOC_ERROR_CLIENT,
  +                  MONGOC_ERROR_CLIENT_NOT_READY,
  +                  "Unsupported cluster mode: %02x",
  +                  cluster->mode);
  +
  +   RETURN (false);
  +}
  +
  +
  +bool
  +_mongoc_cluster_command_early (mongoc_cluster_t *cluster,
  +                               const char       *dbname,
  +                               const bson_t     *command,
  +                               bson_t           *reply,
  +                               bson_error_t     *error)
  +{
  +   mongoc_cluster_node_t *node;
  +   int i;
  +
  +   BSON_ASSERT (cluster);
  +   BSON_ASSERT (cluster->state == MONGOC_CLUSTER_STATE_BORN);
  +   BSON_ASSERT (dbname);
  +   BSON_ASSERT (command);
  +
  +   if (!_mongoc_cluster_reconnect (cluster, error)) {
  +      return false;
  +   }
  +
  +   node = _mongoc_cluster_get_primary (cluster);
  +
  +   for (i = 0; !node && i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      if (cluster->nodes[i].stream) {
  +         node = &cluster->nodes[i];
  +      }
  +   }
  +
  +   return _mongoc_cluster_run_command (cluster, node, dbname, command,
  +                                       reply, error);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_inc_egress_rpc --
  + *
  + *       Helper to increment the counter for a particular RPC based on
  + *       it's opcode.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE void
  +_mongoc_cluster_inc_egress_rpc (const mongoc_rpc_t *rpc)
  +{
  +   mongoc_counter_op_egress_total_inc();
  +
  +   switch (rpc->header.opcode) {
  +   case MONGOC_OPCODE_DELETE:
  +      mongoc_counter_op_egress_delete_inc();
  +      break;
  +   case MONGOC_OPCODE_UPDATE:
  +      mongoc_counter_op_egress_update_inc();
  +      break;
  +   case MONGOC_OPCODE_INSERT:
  +      mongoc_counter_op_egress_insert_inc();
  +      break;
  +   case MONGOC_OPCODE_KILL_CURSORS:
  +      mongoc_counter_op_egress_killcursors_inc();
  +      break;
  +   case MONGOC_OPCODE_GET_MORE:
  +      mongoc_counter_op_egress_getmore_inc();
  +      break;
  +   case MONGOC_OPCODE_REPLY:
  +      mongoc_counter_op_egress_reply_inc();
  +      break;
  +   case MONGOC_OPCODE_MSG:
  +      mongoc_counter_op_egress_msg_inc();
  +      break;
  +   case MONGOC_OPCODE_QUERY:
  +      mongoc_counter_op_egress_query_inc();
  +      break;
  +   default:
  +      BSON_ASSERT(false);
  +      break;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_inc_ingress_rpc --
  + *
  + *       Helper to increment the counter for a particular RPC based on
  + *       it's opcode.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static BSON_INLINE void
  +_mongoc_cluster_inc_ingress_rpc (const mongoc_rpc_t *rpc)
  +{
  +   mongoc_counter_op_ingress_total_inc ();
  +
  +   switch (rpc->header.opcode) {
  +   case MONGOC_OPCODE_DELETE:
  +      mongoc_counter_op_ingress_delete_inc ();
  +      break;
  +   case MONGOC_OPCODE_UPDATE:
  +      mongoc_counter_op_ingress_update_inc ();
  +      break;
  +   case MONGOC_OPCODE_INSERT:
  +      mongoc_counter_op_ingress_insert_inc ();
  +      break;
  +   case MONGOC_OPCODE_KILL_CURSORS:
  +      mongoc_counter_op_ingress_killcursors_inc ();
  +      break;
  +   case MONGOC_OPCODE_GET_MORE:
  +      mongoc_counter_op_ingress_getmore_inc ();
  +      break;
  +   case MONGOC_OPCODE_REPLY:
  +      mongoc_counter_op_ingress_reply_inc ();
  +      break;
  +   case MONGOC_OPCODE_MSG:
  +      mongoc_counter_op_ingress_msg_inc ();
  +      break;
  +   case MONGOC_OPCODE_QUERY:
  +      mongoc_counter_op_ingress_query_inc ();
  +      break;
  +   default:
  +      BSON_ASSERT (false);
  +      break;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_sendv --
  + *
  + *       Deliver an RPC to the MongoDB server.
  + *
  + *       If @hint is non-zero, the connection matching that hint will be
  + *       used or the operation will fail. This is primarily used to force
  + *       sending an RPC on the same connection as a previous RPC. This
  + *       is often the case with OP_QUERY followed by OP_GETMORE.
  + *
  + *       @rpcs should be an array of mongoc_rpc_t that have not yet been
  + *       gathered or swab'ed. The state of @rpcs is undefined after calling
  + *       this function and should not be used afterwards.
  + *
  + *       @write_concern is optional. Providing it may cause this function
  + *       to block until an operation has completed on the remote MongoDB
  + *       server.
  + *
  + *       @read_prefs is optional and can be used to dictate which machines
  + *       may be used to perform a query upon.
  + *
  + *       This function will continue to try to deliver an RPC until
  + *       successful or the retry count has surprased.
  + *
  + * Returns:
  + *       Zero on failure. A non-zero value is the hint of the connection
  + *       that was used to communicate with a remote MongoDB server. This
  + *       value may be passed as @hint in future calls to use the same
  + *       connection.
  + *
  + *       If the result is zero, then @error will be set with information
  + *       about the failure.
  + *
  + * Side effects:
  + *       @rpcs may be muted and should be considered invalid after calling
  + *       this function.
  + *
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +uint32_t
  +_mongoc_cluster_sendv (mongoc_cluster_t             *cluster,
  +                       mongoc_rpc_t                 *rpcs,
  +                       size_t                        rpcs_len,
  +                       uint32_t                 hint,
  +                       const mongoc_write_concern_t *write_concern,
  +                       const mongoc_read_prefs_t    *read_prefs,
  +                       bson_error_t                 *error)
  +{
  +   mongoc_cluster_node_t *node;
  +   mongoc_iovec_t *iov;
  +   const bson_t *b;
  +   mongoc_rpc_t gle;
  +   int64_t now;
  +   size_t iovcnt;
  +   size_t i;
  +   bool need_gle;
  +   char cmdname[140];
  +   int retry_count = 0;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(cluster, false);
  +   bson_return_val_if_fail(rpcs, false);
  +   bson_return_val_if_fail(rpcs_len, false);
  +
  +   /*
  +    * If we are in an unhealthy state, and enough time has elapsed since
  +    * our last reconnection, go ahead and try to perform reconnection
  +    * immediately.
  +    */
  +   now = bson_get_monotonic_time();
  +   if ((cluster->state == MONGOC_CLUSTER_STATE_DEAD) ||
  +       ((cluster->state == MONGOC_CLUSTER_STATE_UNHEALTHY) &&
  +        (cluster->last_reconnect + UNHEALTHY_RECONNECT_TIMEOUT_USEC) <= now)) {
  +      if (!_mongoc_cluster_reconnect(cluster, error)) {
  +         RETURN(false);
  +      }
  +   }
  +
  +   /*
  +    * Try to find a node to deliver to. Since we are allowed to block in this
  +    * version of sendv, we try to reconnect if we cannot select a node.
  +    */
  +   while (!(node = _mongoc_cluster_select (cluster, rpcs, rpcs_len, hint,
  +                                           write_concern, read_prefs,
  +                                           error))) {
  +      if ((retry_count++ == MAX_RETRY_COUNT) ||
  +          !_mongoc_cluster_reconnect (cluster, error)) {
  +         RETURN (false);
  +      }
  +   }
  +
  +   BSON_ASSERT(node->stream);
  +
  +   _mongoc_array_clear (&cluster->iov);
  +
  +   /*
  +    * TODO: We can probably remove the need for sendv and just do send since
  +    * we support write concerns now. Also, we clobber our getlasterror on
  +    * each subsequent mutation. It's okay, since it comes out correct anyway,
  +    * just useless work (and technically the request_id changes).
  +    */
  +
  +   for (i = 0; i < rpcs_len; i++) {
  +      _mongoc_cluster_inc_egress_rpc (&rpcs[i]);
  +      rpcs[i].header.request_id = ++cluster->request_id;
  +      need_gle = _mongoc_rpc_needs_gle(&rpcs[i], write_concern);
  +      _mongoc_rpc_gather (&rpcs[i], &cluster->iov);
  +
  +	  if (rpcs[i].header.msg_len >(int32_t)cluster->max_msg_size) {
  +         bson_set_error(error,
  +                        MONGOC_ERROR_CLIENT,
  +                        MONGOC_ERROR_CLIENT_TOO_BIG,
  +                        "Attempted to send an RPC larger than the "
  +                        "max allowed message size. Was %u, allowed %u.",
  +                        rpcs[i].header.msg_len,
  +                        cluster->max_msg_size);
  +         RETURN(0);
  +      }
  +
  +      if (need_gle) {
  +         gle.query.msg_len = 0;
  +         gle.query.request_id = ++cluster->request_id;
  +         gle.query.response_to = 0;
  +         gle.query.opcode = MONGOC_OPCODE_QUERY;
  +         gle.query.flags = MONGOC_QUERY_NONE;
  +         switch (rpcs[i].header.opcode) {
  +         case MONGOC_OPCODE_INSERT:
  +            DB_AND_CMD_FROM_COLLECTION(cmdname, rpcs[i].insert.collection);
  +            break;
  +         case MONGOC_OPCODE_DELETE:
  +            DB_AND_CMD_FROM_COLLECTION(cmdname, rpcs[i].delete.collection);
  +            break;
  +         case MONGOC_OPCODE_UPDATE:
  +            DB_AND_CMD_FROM_COLLECTION(cmdname, rpcs[i].update.collection);
  +            break;
  +         default:
  +            BSON_ASSERT(false);
  +            DB_AND_CMD_FROM_COLLECTION(cmdname, "admin.$cmd");
  +            break;
  +         }
  +         gle.query.collection = cmdname;
  +         gle.query.skip = 0;
  +         gle.query.n_return = 1;
  +         b = _mongoc_write_concern_freeze((void*)write_concern);
  +         gle.query.query = bson_get_data(b);
  +         gle.query.fields = NULL;
  +         _mongoc_rpc_gather(&gle, &cluster->iov);
  +         _mongoc_rpc_swab_to_le(&gle);
  +      }
  +
  +      _mongoc_rpc_swab_to_le(&rpcs[i]);
  +   }
  +
  +   iov = cluster->iov.data;
  +   iovcnt = cluster->iov.len;
  +   errno = 0;
  +
  +   BSON_ASSERT (cluster->iov.len);
  +
  +   if (!mongoc_stream_writev (node->stream, iov, iovcnt,
  +                              cluster->sockettimeoutms)) {
  +      char buf[128];
  +      char * errstr;
  +      errstr = bson_strerror_r(errno, buf, sizeof buf);
  +
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_SOCKET,
  +                      "Failure during socket delivery: %s",
  +                      errstr);
  +      _mongoc_cluster_disconnect_node (cluster, node);
  +      RETURN (0);
  +   }
  +
  +   RETURN (node->index + 1);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_try_sendv --
  + *
  + *       Deliver an RPC to a remote MongoDB instance.
  + *
  + *       This function is similar to _mongoc_cluster_sendv() except that it
  + *       will not try to reconnect until a connection has been made.
  + *
  + *       This is useful if you want to fire-and-forget ignoring network
  + *       errors. Kill Cursors would be a candidate for this.
  + *
  + * Returns:
  + *       0 on failure and @error is set.
  + *
  + *       Non-zero on success. The return value is a hint for the
  + *       connection that was used to communicate with the server.
  + *
  + * Side effects:
  + *       @rpcs will be invalid after calling this function.
  + *       @error may be set if 0 is returned.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +uint32_t
  +_mongoc_cluster_try_sendv (mongoc_cluster_t             *cluster,
  +                           mongoc_rpc_t                 *rpcs,
  +                           size_t                        rpcs_len,
  +                           uint32_t                 hint,
  +                           const mongoc_write_concern_t *write_concern,
  +                           const mongoc_read_prefs_t    *read_prefs,
  +                           bson_error_t                 *error)
  +{
  +   mongoc_cluster_node_t *node;
  +   mongoc_iovec_t *iov;
  +   const bson_t *b;
  +   mongoc_rpc_t gle;
  +   bool need_gle;
  +   size_t iovcnt;
  +   size_t i;
  +   char cmdname[140];
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(cluster, false);
  +   bson_return_val_if_fail(rpcs, false);
  +   bson_return_val_if_fail(rpcs_len, false);
  +
  +   if (!(node = _mongoc_cluster_select(cluster, rpcs, rpcs_len, hint,
  +                                       write_concern, read_prefs, error))) {
  +      RETURN (0);
  +   }
  +
  +   BSON_ASSERT (node->stream);
  +
  +   _mongoc_array_clear (&cluster->iov);
  +
  +   for (i = 0; i < rpcs_len; i++) {
  +      _mongoc_cluster_inc_egress_rpc (&rpcs[i]);
  +      rpcs[i].header.request_id = ++cluster->request_id;
  +      need_gle = _mongoc_rpc_needs_gle (&rpcs[i], write_concern);
  +      _mongoc_rpc_gather (&rpcs[i], &cluster->iov);
  +
  +	  if (rpcs[i].header.msg_len >(int32_t)cluster->max_msg_size) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_CLIENT,
  +                         MONGOC_ERROR_CLIENT_TOO_BIG,
  +                         "Attempted to send an RPC larger than the "
  +                         "max allowed message size. Was %u, allowed %u.",
  +                         rpcs[i].header.msg_len,
  +                         cluster->max_msg_size);
  +         RETURN (0);
  +      }
  +
  +      if (need_gle) {
  +         gle.query.msg_len = 0;
  +         gle.query.request_id = ++cluster->request_id;
  +         gle.query.response_to = 0;
  +         gle.query.opcode = MONGOC_OPCODE_QUERY;
  +         gle.query.flags = MONGOC_QUERY_NONE;
  +
  +         switch (rpcs[i].header.opcode) {
  +         case MONGOC_OPCODE_INSERT:
  +            DB_AND_CMD_FROM_COLLECTION(cmdname, rpcs[i].insert.collection);
  +            break;
  +         case MONGOC_OPCODE_DELETE:
  +            DB_AND_CMD_FROM_COLLECTION(cmdname, rpcs[i].delete.collection);
  +            break;
  +         case MONGOC_OPCODE_UPDATE:
  +            gle.query.collection = rpcs[i].update.collection;
  +            DB_AND_CMD_FROM_COLLECTION(cmdname, rpcs[i].update.collection);
  +            break;
  +         default:
  +            BSON_ASSERT(false);
  +            DB_AND_CMD_FROM_COLLECTION(cmdname, "admin.$cmd");
  +            break;
  +         }
  +
  +         gle.query.collection = cmdname;
  +         gle.query.skip = 0;
  +         gle.query.n_return = 1;
  +
  +         b = _mongoc_write_concern_freeze ((void *)write_concern);
  +
  +         gle.query.query = bson_get_data (b);
  +         gle.query.fields = NULL;
  +
  +         _mongoc_rpc_gather (&gle, &cluster->iov);
  +         _mongoc_rpc_swab_to_le (&gle);
  +      }
  +
  +      _mongoc_rpc_swab_to_le (&rpcs[i]);
  +   }
  +
  +   iov = cluster->iov.data;
  +   iovcnt = cluster->iov.len;
  +   errno = 0;
  +
  +   DUMP_IOVEC (iov, iov, iovcnt);
  +
  +   if (!mongoc_stream_writev (node->stream, iov, iovcnt,
  +                              cluster->sockettimeoutms)) {
  +      char buf[128];
  +      char * errstr;
  +      errstr = bson_strerror_r(errno, buf, sizeof buf);
  +
  +      bson_set_error (error,
  +                      MONGOC_ERROR_STREAM,
  +                      MONGOC_ERROR_STREAM_SOCKET,
  +                      "Failure during socket delivery: %s",
  +                      errstr);
  +      _mongoc_cluster_disconnect_node (cluster, node);
  +      RETURN (0);
  +   }
  +
  +   RETURN(node->index + 1);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_cluster_try_recv --
  + *
  + *       Tries to receive the next event from the node in the cluster
  + *       specified by @hint. The contents are loaded into @buffer and then
  + *       scattered into the @rpc structure. @rpc is valid as long as
  + *       @buffer contains the contents read into it.
  + *
  + *       Callers that can optimize a reuse of @buffer should do so. It
  + *       can save many memory allocations.
  + *
  + * Returns:
  + *       0 on failure and @error is set.
  + *       non-zero on success where the value is the hint of the connection
  + *       that was used.
  + *
  + * Side effects:
  + *       @error if return value is zero.
  + *       @rpc is set if result is non-zero.
  + *       @buffer will be filled with the input data.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +_mongoc_cluster_try_recv (mongoc_cluster_t *cluster,
  +                          mongoc_rpc_t     *rpc,
  +                          mongoc_buffer_t  *buffer,
  +                          uint32_t          hint,
  +                          bson_error_t     *error)
  +{
  +   mongoc_cluster_node_t *node;
  +   int32_t msg_len;
  +   off_t pos;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail (cluster, false);
  +   bson_return_val_if_fail (rpc, false);
  +   bson_return_val_if_fail (buffer, false);
  +   bson_return_val_if_fail (hint, false);
  +   bson_return_val_if_fail (hint <= MONGOC_CLUSTER_MAX_NODES, false);
  +
  +   /*
  +    * Fetch the node to communicate over.
  +    */
  +   node = &cluster->nodes[hint-1];
  +   if (!node->stream) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_CLIENT,
  +                      MONGOC_ERROR_CLIENT_NOT_READY,
  +                      "Failed to receive message, lost connection to node.");
  +      RETURN (false);
  +   }
  +
  +   TRACE ("Waiting for reply from \"%s\"", node->host.host_and_port);
  +
  +   /*
  +    * Buffer the message length to determine how much more to read.
  +    */
  +   pos = buffer->len;
  +   if (!_mongoc_buffer_append_from_stream (buffer, node->stream, 4,
  +                                           cluster->sockettimeoutms, error)) {
  +      mongoc_counter_protocol_ingress_error_inc ();
  +      _mongoc_cluster_disconnect_node (cluster, node);
  +      RETURN (false);
  +   }
  +
  +   /*
  +    * Read the msg length from the buffer.
  +    */
  +   memcpy (&msg_len, &buffer->data[buffer->off + pos], 4);
  +   msg_len = BSON_UINT32_FROM_LE (msg_len);
  +   if ((msg_len < 16) || (msg_len >(int32_t)cluster->max_bson_size)) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_PROTOCOL,
  +                      MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
  +                      "Corrupt or malicious reply received.");
  +      _mongoc_cluster_disconnect_node (cluster, node);
  +      mongoc_counter_protocol_ingress_error_inc ();
  +      RETURN (false);
  +   }
  +
  +   /*
  +    * Read the rest of the message from the stream.
  +    */
  +   if (!_mongoc_buffer_append_from_stream (buffer, node->stream, msg_len - 4,
  +                                           cluster->sockettimeoutms, error)) {
  +      _mongoc_cluster_disconnect_node (cluster, node);
  +      mongoc_counter_protocol_ingress_error_inc ();
  +      RETURN (false);
  +   }
  +
  +   /*
  +    * Scatter the buffer into the rpc structure.
  +    */
  +   if (!_mongoc_rpc_scatter (rpc, &buffer->data[buffer->off + pos], msg_len)) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_PROTOCOL,
  +                      MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
  +                      "Failed to decode reply from server.");
  +      _mongoc_cluster_disconnect_node (cluster, node);
  +      mongoc_counter_protocol_ingress_error_inc ();
  +      RETURN (false);
  +   }
  +
  +   DUMP_BYTES (buffer, buffer->data + buffer->off, buffer->len);
  +
  +   _mongoc_rpc_swab_from_le (rpc);
  +
  +   _mongoc_cluster_inc_ingress_rpc (rpc);
  +
  +   RETURN(true);
  +}
  +
  +
  +/**
  + * _mongoc_cluster_stamp:
  + * @cluster: A mongoc_cluster_t.
  + * @node: The node identifier.
  + *
  + * Returns the stamp of the node provided. The stamp is a monotonic counter
  + * that tracks changes to a node within the cluster. As changes to the node
  + * instance are made, the value is incremented. This helps cursors and other
  + * connection sensitive portions fail gracefully (or reset) upon loss of
  + * connection.
  + *
  + * Returns: A 32-bit stamp indiciating the node version.
  + */
  +uint32_t
  +_mongoc_cluster_stamp (const mongoc_cluster_t *cluster,
  +                       uint32_t           node)
  +{
  +   bson_return_val_if_fail(cluster, 0);
  +   bson_return_val_if_fail(node > 0, 0);
  +   bson_return_val_if_fail(node <= MONGOC_CLUSTER_MAX_NODES, 0);
  +
  +   return cluster->nodes[node].stamp;
  +}
  +
  +
  +/**
  + * _mongoc_cluster_get_primary:
  + * @cluster: A #mongoc_cluster_t.
  + *
  + * Fetches the node we currently believe is PRIMARY.
  + *
  + * Returns: A #mongoc_cluster_node_t or %NULL.
  + */
  +mongoc_cluster_node_t *
  +_mongoc_cluster_get_primary (mongoc_cluster_t *cluster)
  +{
  +   uint32_t i;
  +
  +   BSON_ASSERT (cluster);
  +
  +   for (i = 0; i < MONGOC_CLUSTER_MAX_NODES; i++) {
  +      if (cluster->nodes[i].primary) {
  +         return &cluster->nodes[i];
  +      }
  +   }
  +
  +   return NULL;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-collection.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "collection"
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_collection_new --
  + *
  + *       INTERNAL API
  + *
  + *       Create a new mongoc_collection_t structure for the given client.
  + *
  + *       @client must remain valid during the lifetime of this structure.
  + *       @db is the db name of the collection.
  + *       @collection is the name of the collection.
  + *       @read_prefs is the default read preferences to apply or NULL.
  + *       @write_concern is the default write concern to apply or NULL.
  + *
  + * Returns:
  + *       A newly allocated mongoc_collection_t that should be freed with
  + *       mongoc_collection_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_collection_t *
  +_mongoc_collection_new (mongoc_client_t              *client,
  +                        const char                   *db,
  +                        const char                   *collection,
  +                        const mongoc_read_prefs_t    *read_prefs,
  +                        const mongoc_write_concern_t *write_concern)
  +{
  +   mongoc_collection_t *col;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(client, NULL);
  +   bson_return_val_if_fail(db, NULL);
  +   bson_return_val_if_fail(collection, NULL);
  +
  +   col = bson_malloc0(sizeof *col);
  +   col->client = client;
  +   col->write_concern = write_concern ?
  +      mongoc_write_concern_copy(write_concern) :
  +      mongoc_write_concern_new();
  +   col->read_prefs = read_prefs ?
  +      mongoc_read_prefs_copy(read_prefs) :
  +      mongoc_read_prefs_new(MONGOC_READ_PRIMARY);
  +
  +   bson_snprintf (col->ns, sizeof col->ns - 1, "%s.%s",
  +                  db, collection);
  +   bson_snprintf (col->db, sizeof col->db - 1, "%s", db);
  +   bson_snprintf (col->collection, sizeof col->collection - 1,
  +                  "%s", collection);
  +
  +   col->collectionlen = (uint32_t)strlen(col->collection);
  +   col->nslen = (uint32_t)strlen(col->ns);
  +
  +   _mongoc_buffer_init(&col->buffer, NULL, 0, NULL);
  +
  +   col->gle = NULL;
  +
  +   RETURN(col);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_destroy --
  + *
  + *       Release resources associated with @collection and frees the
  + *       structure.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       Everything.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_collection_destroy (mongoc_collection_t *collection) /* IN */
  +{
  +   ENTRY;
  +
  +   bson_return_if_fail(collection);
  +
  +   bson_clear (&collection->gle);
  +
  +   _mongoc_buffer_destroy(&collection->buffer);
  +
  +   if (collection->read_prefs) {
  +      mongoc_read_prefs_destroy(collection->read_prefs);
  +      collection->read_prefs = NULL;
  +   }
  +
  +   if (collection->write_concern) {
  +      mongoc_write_concern_destroy(collection->write_concern);
  +      collection->write_concern = NULL;
  +   }
  +
  +   bson_free(collection);
  +
  +   EXIT;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_aggregate --
  + *
  + *       Send an "aggregate" command to the MongoDB server.
  + *
  + *       This varies it's behavior based on the wire version.  If we're on
  + *       wire_version > 0, we use the new aggregate command, which returns a
  + *       database cursor.  On wire_version == 0, we create synthetic cursor on
  + *       top of the array returned in result.
  + *
  + *       This function will always return a new mongoc_cursor_t that should
  + *       be freed with mongoc_cursor_destroy().
  + *
  + *       The cursor may fail once iterated upon, so check
  + *       mongoc_cursor_error() if mongoc_cursor_next() returns false.
  + *
  + *       See http://docs.mongodb.org/manual/aggregation/ for more
  + *       information on how to build aggregation pipelines.
  + *
  + * Requires:
  + *       MongoDB >= 2.1.0
  + *
  + * Parameters:
  + *       @flags: bitwise or of mongoc_query_flags_t or 0.
  + *       @pipeline: A bson_t containing the pipeline request. @pipeline
  + *                  will be sent as an array type in the request.
  + *       @read_prefs: Optional read preferences for the command.
  + *
  + * Returns:
  + *       A newly allocated mongoc_cursor_t that should be freed with
  + *       mongoc_cursor_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_cursor_t *
  +mongoc_collection_aggregate (mongoc_collection_t       *collection, /* IN */
  +                             mongoc_query_flags_t       flags,      /* IN */
  +                             const bson_t              *pipeline,   /* IN */
  +                             const mongoc_read_prefs_t *read_prefs) /* IN */
  +{
  +   mongoc_cursor_t *cursor;
  +   bson_t command;
  +   bson_t child;
  +   int32_t wire_version;
  +
  +   bson_return_val_if_fail(collection, NULL);
  +   bson_return_val_if_fail(pipeline, NULL);
  +
  +   wire_version = collection->client->cluster.wire_version;
  +
  +   bson_init(&command);
  +   bson_append_utf8(&command, "aggregate", 9,
  +                    collection->collection,
  +                    collection->collectionlen);
  +   bson_append_array(&command, "pipeline", 8, pipeline);
  +
  +   /* for newer version, we include a cursor subdocument */
  +   if (wire_version > 0) {
  +      bson_append_document_begin(&command, "cursor", 6, &child);
  +      bson_append_int32(&child, "batchSize", 9, 0);
  +      bson_append_document_end(&command, &child);
  +   }
  +
  +   cursor = mongoc_collection_command(collection, flags, 0, 1, 0, &command,
  +                                      NULL, read_prefs);
  +
  +   if (wire_version > 0) {
  +      /* even for newer versions, we get back a cursor document, that we have
  +       * to patch in */
  +      _mongoc_cursor_cursorid_init(cursor);
  +   } else {
  +      /* for older versions we get an array that we can create a synthetic
  +       * cursor on top of */
  +      _mongoc_cursor_array_init(cursor);
  +   }
  +
  +   bson_destroy(&command);
  +
  +   return cursor;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_find --
  + *
  + *       Performs a query against the configured MongoDB server. If @read_prefs
  + *       is provided, it will be used to locate a MongoDB node in the cluster
  + *       to deliver the query to.
  + *
  + *       @flags may be bitwise-or'd flags or MONGOC_QUERY_NONE.
  + *
  + *       @skip may contain the number of documents to skip before returning the
  + *       matching document.
  + *
  + *       @limit may contain the maximum number of documents that may be
  + *       returned.
  + *
  + *       This function will always return a cursor, with the exception of
  + *       invalid API use.
  + *
  + * Parameters:
  + *       @collection: A mongoc_collection_t.
  + *       @flags: A bitwise or of mongoc_query_flags_t.
  + *       @skip: The number of documents to skip.
  + *       @limit: The maximum number of items.
  + *       @batch_size: The batch size
  + *       @query: The query to locate matching documents.
  + *       @fields: The fields to return, or NULL for all fields.
  + *       @read_prefs: Read preferences to choose cluster node.
  + *
  + * Returns:
  + *       A newly allocated mongoc_cursor_t that should be freed with
  + *       mongoc_cursor_destroy().
  + *
  + *       The client used by mongoc_collection_t must be valid for the
  + *       lifetime of the resulting mongoc_cursor_t.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_cursor_t *
  +mongoc_collection_find (mongoc_collection_t       *collection, /* IN */
  +                        mongoc_query_flags_t       flags,      /* IN */
  +                        uint32_t              skip,       /* IN */
  +                        uint32_t              limit,      /* IN */
  +                        uint32_t              batch_size, /* IN */
  +                        const bson_t              *query,      /* IN */
  +                        const bson_t              *fields,     /* IN */
  +                        const mongoc_read_prefs_t *read_prefs) /* IN */
  +{
  +   bson_return_val_if_fail(collection, NULL);
  +   bson_return_val_if_fail(query, NULL);
  +
  +   bson_clear (&collection->gle);
  +
  +   if (!read_prefs) {
  +      read_prefs = collection->read_prefs;
  +   }
  +
  +   return _mongoc_cursor_new(collection->client, collection->ns, flags, skip,
  +                             limit, batch_size, false, query, fields, read_prefs);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_command --
  + *
  + *       Executes a command on a cluster node matching @read_prefs. If
  + *       @read_prefs is not provided, it will be run on the primary node.
  + *
  + *       This function will always return a mongoc_cursor_t.
  + *
  + * Parameters:
  + *       @collection: A mongoc_collection_t.
  + *       @flags: Bitwise-or'd flags for command.
  + *       @skip: Number of documents to skip, typically 0.
  + *       @limit : Number of documents to return
  + *       @batch_size : Batch size
  + *       @query: The command to execute.
  + *       @fields: The fields to return, or NULL.
  + *       @read_prefs: Command read preferences or NULL.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_cursor_t *
  +mongoc_collection_command (mongoc_collection_t       *collection,
  +                           mongoc_query_flags_t       flags,
  +                           uint32_t              skip,
  +                           uint32_t              limit,
  +                           uint32_t              batch_size,
  +                           const bson_t              *query,
  +                           const bson_t              *fields,
  +                           const mongoc_read_prefs_t *read_prefs)
  +{
  +   BSON_ASSERT (collection);
  +   BSON_ASSERT (query);
  +
  +   if (!read_prefs) {
  +      read_prefs = collection->read_prefs;
  +   }
  +
  +   bson_clear (&collection->gle);
  +
  +   return mongoc_client_command (collection->client, collection->db, flags,
  +                                 skip, limit, batch_size, query, fields, \
read_prefs);  +}
  +
  +bool
  +mongoc_collection_command_simple (mongoc_collection_t       *collection,
  +                                  const bson_t              *command,
  +                                  const mongoc_read_prefs_t *read_prefs,
  +                                  bson_t                    *reply,
  +                                  bson_error_t              *error)
  +{
  +   BSON_ASSERT (collection);
  +   BSON_ASSERT (command);
  +
  +   bson_clear (&collection->gle);
  +
  +   return mongoc_client_command_simple (collection->client, collection->db,
  +                                        command, read_prefs, reply, error);
  +}
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_count --
  + *
  + *       Count the number of documents matching @query.
  + *
  + * Parameters:
  + *       @flags: A mongoc_query_flags_t describing the query flags or 0.
  + *       @query: The query to perform or NULL for {}.
  + *       @skip: The $skip to perform within the query or 0.
  + *       @limit: The $limit to perform within the query or 0.
  + *       @read_prefs: desired read preferences or NULL.
  + *       @error: A location for an error or NULL.
  + *
  + * Returns:
  + *       -1 on failure; otherwise the number of matching documents.
  + *
  + * Side effects:
  + *       @error is set upon failure if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +int64_t
  +mongoc_collection_count (mongoc_collection_t       *collection,  /* IN */
  +                         mongoc_query_flags_t       flags,       /* IN */
  +                         const bson_t              *query,       /* IN */
  +                         int64_t               skip,        /* IN */
  +                         int64_t               limit,       /* IN */
  +                         const mongoc_read_prefs_t *read_prefs,  /* IN */
  +                         bson_error_t              *error)       /* OUT */
  +{
  +   int64_t ret = -1;
  +   bson_iter_t iter;
  +   bson_t reply;
  +   bson_t cmd;
  +   bson_t q;
  +
  +   bson_return_val_if_fail(collection, -1);
  +
  +   bson_init(&cmd);
  +   bson_append_utf8(&cmd, "count", 5, collection->collection,
  +                    collection->collectionlen);
  +   if (query) {
  +      bson_append_document(&cmd, "query", 5, query);
  +   } else {
  +      bson_init(&q);
  +      bson_append_document(&cmd, "query", 5, &q);
  +      bson_destroy(&q);
  +   }
  +   if (limit) {
  +      bson_append_int64(&cmd, "limit", 5, limit);
  +   }
  +   if (skip) {
  +      bson_append_int64(&cmd, "skip", 4, skip);
  +   }
  +   if (mongoc_collection_command_simple(collection, &cmd, read_prefs,
  +                                        &reply, error) &&
  +       bson_iter_init_find(&iter, &reply, "n")) {
  +      ret = bson_iter_as_int64(&iter);
  +   }
  +   bson_destroy(&reply);
  +   bson_destroy(&cmd);
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_drop --
  + *
  + *       Request the MongoDB server drop the collection.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @error is set upon failure.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_collection_drop (mongoc_collection_t *collection, /* IN */
  +                        bson_error_t        *error)      /* OUT */
  +{
  +   bool ret;
  +   bson_t cmd;
  +
  +   bson_return_val_if_fail(collection, false);
  +
  +   bson_init(&cmd);
  +   bson_append_utf8(&cmd, "drop", 4, collection->collection,
  +                    collection->collectionlen);
  +   ret = mongoc_collection_command_simple(collection, &cmd, NULL, NULL, error);
  +   bson_destroy(&cmd);
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_drop_index --
  + *
  + *       Request the MongoDB server drop the named index.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @error is setup upon failure if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_collection_drop_index (mongoc_collection_t *collection, /* IN */
  +                              const char          *index_name, /* IN */
  +                              bson_error_t        *error)      /* OUT */
  +{
  +   bool ret;
  +   bson_t cmd;
  +
  +   bson_return_val_if_fail(collection, false);
  +   bson_return_val_if_fail(index_name, false);
  +
  +   bson_init(&cmd);
  +   bson_append_utf8(&cmd, "dropIndexes", -1, collection->collection,
  +                    collection->collectionlen);
  +   bson_append_utf8(&cmd, "index", -1, index_name, -1);
  +   ret = mongoc_collection_command_simple(collection, &cmd, NULL, NULL, error);
  +   bson_destroy(&cmd);
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_ensure_index --
  + *
  + *       Request the MongoDB server create the named index.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @error is setup upon failure if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +char *
  +mongoc_collection_keys_to_index_string (const bson_t *keys)
  +{
  +   bson_string_t *s;
  +   bson_iter_t iter;
  +   int i = 0;
  +
  +   BSON_ASSERT (keys);
  +
  +   if (!bson_iter_init (&iter, keys)) {
  +      return NULL;
  +   }
  +
  +   s = bson_string_new (NULL);
  +
  +   while (bson_iter_next (&iter)) {
  +      bson_string_append_printf (s,
  +                                 (i++ ? "_%s_%d" : "%s_%d"),
  +                                 bson_iter_key (&iter),
  +                                 bson_iter_int32 (&iter));
  +   }
  +
  +   return bson_string_free (s, false);
  +}
  +
  +bool
  +mongoc_collection_ensure_index (mongoc_collection_t      *collection,
  +                                const bson_t             *keys,
  +                                const mongoc_index_opt_t *opt,
  +                                bson_error_t             *error)
  +{
  +   const mongoc_index_opt_t *def_opt;
  +   mongoc_collection_t *col;
  +   bool ret;
  +   bson_t insert;
  +   char *name;
  +
  +   bson_return_val_if_fail (collection, false);
  +
  +   /*
  +    * TODO: this is supposed to be cached and cheap... make it that way
  +    */
  +
  +   def_opt = mongoc_index_opt_get_default ();
  +   opt = opt ? opt : def_opt;
  +
  +   if (!opt->is_initialized) {
  +      MONGOC_WARNING("Options have not yet been initialized");
  +      return false;
  +   }
  +
  +   bson_init (&insert);
  +
  +   bson_append_document (&insert, "key", -1, keys);
  +   bson_append_utf8 (&insert, "ns", -1, collection->ns, -1);
  +
  +   if (opt->background != def_opt->background) {
  +      bson_append_bool (&insert, "background", -1, opt->background);
  +   }
  +
  +   if (opt->unique != def_opt->unique) {
  +      bson_append_bool (&insert, "unique", -1, opt->unique);
  +   }
  +
  +   if (opt->name != def_opt->name) {
  +      bson_append_utf8 (&insert, "name", -1, opt->name, -1);
  +   } else {
  +      name = mongoc_collection_keys_to_index_string(keys);
  +      bson_append_utf8 (&insert, "name", -1, name, -1);
  +      bson_free (name);
  +   }
  +
  +   if (opt->drop_dups != def_opt->drop_dups) {
  +      bson_append_bool (&insert, "dropDups", -1, opt->drop_dups);
  +   }
  +
  +   if (opt->sparse != def_opt->sparse) {
  +      bson_append_bool (&insert, "sparse", -1, opt->sparse);
  +   }
  +
  +   if (opt->expire_after_seconds != def_opt->expire_after_seconds) {
  +      bson_append_int32 (&insert,
  +                         "expireAfterSeconds", -1,
  +                         opt->expire_after_seconds);
  +   }
  +
  +   if (opt->v != def_opt->v) {
  +      bson_append_int32 (&insert, "v", -1, opt->v);
  +   }
  +
  +   if (opt->weights != def_opt->weights) {
  +      bson_append_document (&insert, "weights", -1, opt->weights);
  +   }
  +
  +   if (opt->default_language != def_opt->default_language) {
  +      bson_append_utf8 (&insert,
  +                        "defaultLanguage", -1,
  +                        opt->default_language, -1);
  +   }
  +
  +   if (opt->language_override != def_opt->language_override) {
  +      bson_append_utf8 (&insert,
  +                        "languageOverride", -1,
  +                        opt->language_override, -1);
  +   }
  +
  +   col = mongoc_client_get_collection (collection->client, collection->db,
  +                                       "system.indexes");
  +
  +   ret = mongoc_collection_insert (col, MONGOC_INSERT_NONE, &insert, NULL,
  +                                   error);
  +
  +   mongoc_collection_destroy(col);
  +
  +   bson_destroy (&insert);
  +
  +   return ret;
  +}
  +
  +
  +static bool
  +_mongoc_collection_insert_bulk_raw (mongoc_collection_t          *collection,
  +                                    mongoc_insert_flags_t         flags,
  +                                    const mongoc_iovec_t         *documents,
  +                                    uint32_t                      n_documents,
  +                                    const mongoc_write_concern_t *write_concern,
  +                                    bson_error_t                 *error)
  +{
  +   mongoc_buffer_t buffer;
  +   uint32_t hint;
  +   mongoc_rpc_t rpc;
  +   mongoc_rpc_t reply;
  +   char ns[MONGOC_NAMESPACE_MAX];
  +   bson_t reply_bson;
  +   bson_iter_t reply_iter;
  +   int code = 0;
  +   const char * errmsg;
  +
  +   BSON_ASSERT(collection);
  +   BSON_ASSERT(documents);
  +   BSON_ASSERT(n_documents);
  +
  +   bson_clear (&collection->gle);
  +
  +   if (!write_concern) {
  +      write_concern = collection->write_concern;
  +   }
  +
  +   if (!_mongoc_client_warm_up (collection->client, error)) {
  +      return false;
  +   }
  +
  +   /*
  +    * WARNING:
  +    *
  +    *    Because we do lazy connections, we potentially have a situation
  +    *    here for which we have not connected to a master and determined
  +    *    the wire versions.
  +    *
  +    *    We might need to ensure we have a connection at this point.
  +    */
  +
  +   if (collection->client->cluster.wire_version == 0) {
  +      /*
  +       * TODO: Do old style write commands.
  +       */
  +   } else {
  +      /*
  +       * TODO: Do new style write commands.
  +       */
  +   }
  +
  +   /*
  +    * Build our insert RPC.
  +    */
  +   rpc.insert.msg_len = 0;
  +   rpc.insert.request_id = 0;
  +   rpc.insert.response_to = 0;
  +   rpc.insert.opcode = MONGOC_OPCODE_INSERT;
  +   rpc.insert.flags = flags;
  +   rpc.insert.collection = collection->ns;
  +   rpc.insert.documents = documents;
  +   rpc.insert.n_documents = n_documents;
  +
  +   bson_snprintf (ns, sizeof ns, "%s.$cmd", collection->db);
  +
  +   if (!(hint = _mongoc_client_sendv(collection->client, &rpc, 1, 0,
  +                                     write_concern, NULL, error))) {
  +      return false;
  +   }
  +
  +   if (_mongoc_write_concern_has_gle (write_concern)) {
  +      _mongoc_buffer_init (&buffer, NULL, 0, NULL);
  +
  +      if (!_mongoc_client_recv (collection->client, &reply, &buffer,
  +                                hint, error)) {
  +         _mongoc_buffer_destroy (&buffer);
  +         return false;
  +      }
  +
  +      bson_init_static (&reply_bson, reply.reply.documents,
  +                        reply.reply.documents_len);
  +
  +      collection->gle = bson_copy (&reply_bson);
  +
  +      if (bson_iter_init_find (&reply_iter, &reply_bson, "err") &&
  +          BSON_ITER_HOLDS_UTF8 (&reply_iter)) {
  +         errmsg = bson_iter_utf8 (&reply_iter, NULL);
  +
  +         if (bson_iter_init_find (&reply_iter, &reply_bson, "code") &&
  +             BSON_ITER_HOLDS_INT32 (&reply_iter)) {
  +            code = bson_iter_int32 (&reply_iter);
  +         }
  +
  +         bson_set_error (error,
  +                         MONGOC_ERROR_INSERT,
  +                         code,
  +                         "%s", errmsg ? errmsg : "Unknown insert failure");
  +
  +         _mongoc_buffer_destroy (&buffer);
  +
  +         return false;
  +      }
  +
  +      _mongoc_buffer_destroy (&buffer);
  +   }
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_insert_bulk --
  + *
  + *       Bulk insert documents into a MongoDB collection.
  + *
  + * Parameters:
  + *       @collection: A mongoc_collection_t.
  + *       @flags: flags for the insert or 0.
  + *       @documents: The documents to insert.
  + *       @n_documents: The number of documents to insert.
  + *       @write_concern: A write concern or NULL.
  + *       @error: a location for an error or NULL.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + *       If the write concern does not dictate checking the result of the
  + *       insert, then true may be returned even though the document was
  + *       not actually inserted on the MongoDB server or cluster.
  + *
  + * Side effects:
  + *       @collection->gle is setup, depending on write_concern->w value.
  + *       @error may be set upon failure if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_collection_insert_bulk (mongoc_collection_t           *collection,
  +                               mongoc_insert_flags_t          flags,
  +                               const bson_t                 **documents,
  +                               uint32_t                  n_documents,
  +                               const mongoc_write_concern_t  *write_concern,
  +                               bson_error_t                  *error)
  +{
  +   mongoc_iovec_t *iov;
  +   size_t i;
  +   size_t err_offset;
  +   bool r;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (documents);
  +   BSON_ASSERT (n_documents);
  +
  +   bson_clear (&collection->gle);
  +
  +   if (!(flags & MONGOC_INSERT_NO_VALIDATE)) {
  +      for (i = 0; i < n_documents; i++) {
  +         if (!bson_validate (documents [i],
  +                             (BSON_VALIDATE_UTF8 |
  +                              BSON_VALIDATE_UTF8_ALLOW_NULL |
  +                              BSON_VALIDATE_DOLLAR_KEYS |
  +                              BSON_VALIDATE_DOT_KEYS),
  +                             &err_offset)) {
  +            bson_set_error (error,
  +                            MONGOC_ERROR_BSON,
  +                            MONGOC_ERROR_BSON_INVALID,
  +                            "A document was corrupt or contained "
  +                            "invalid characters . or $");
  +            return false;
  +         }
  +      }
  +   } else {
  +      flags &= ~MONGOC_INSERT_NO_VALIDATE;
  +   }
  +
  +   if (!_mongoc_client_warm_up (collection->client, error)) {
  +      RETURN (false);
  +   }
  +
  +   iov = bson_malloc (sizeof (mongoc_iovec_t) * n_documents);
  +
  +   for (i = 0; i < n_documents; i++) {
  +      iov [i].iov_base = (void *)bson_get_data (documents [i]);
  +      iov [i].iov_len = documents [i]->len;
  +   }
  +
  +   r = _mongoc_collection_insert_bulk_raw (collection, flags, iov, n_documents,
  +                                           write_concern, error);
  +
  +   bson_free (iov);
  +
  +   RETURN (r);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_insert --
  + *
  + *       Insert a document into a MongoDB collection.
  + *
  + * Parameters:
  + *       @collection: A mongoc_collection_t.
  + *       @flags: flags for the insert or 0.
  + *       @document: The document to insert.
  + *       @write_concern: A write concern or NULL.
  + *       @error: a location for an error or NULL.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + *       If the write concern does not dictate checking the result of the
  + *       insert, then true may be returned even though the document was
  + *       not actually inserted on the MongoDB server or cluster.
  + *
  + * Side effects:
  + *       @collection->gle is setup, depending on write_concern->w value.
  + *       @error may be set upon failure if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_collection_insert (mongoc_collection_t          *collection,
  +                          mongoc_insert_flags_t         flags,
  +                          const bson_t                 *document,
  +                          const mongoc_write_concern_t *write_concern,
  +                          bson_error_t                 *error)
  +{
  +   bool ret;
  +   bson_iter_t iter;
  +   bson_oid_t oid;
  +   bson_t copy = BSON_INITIALIZER;
  +
  +   bson_return_val_if_fail (collection, false);
  +   bson_return_val_if_fail (document, false);
  +
  +   bson_clear (&collection->gle);
  +
  +   if (!bson_iter_init_find (&iter, document, "_id")) {
  +      bson_oid_init (&oid, NULL);
  +      bson_append_oid (&copy, "_id", 3, &oid);
  +      bson_concat (&copy, document);
  +      document = &copy;
  +   }
  +
  +   ret = mongoc_collection_insert_bulk (collection, flags, &document, 1,
  +                                        write_concern, error);
  +
  +   bson_destroy (&copy);
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_update --
  + *
  + *       Updates one or more documents matching @selector with @update.
  + *
  + * Parameters:
  + *       @collection: A mongoc_collection_t.
  + *       @flags: The flags for the update.
  + *       @selector: A bson_t containing your selector.
  + *       @update: A bson_t containing your update document.
  + *       @write_concern: The write concern or NULL.
  + *       @error: A location for an error or NULL.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @collection->gle is setup, depending on write_concern->w value.
  + *       @error is setup upon failure.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_collection_update (mongoc_collection_t          *collection,
  +                          mongoc_update_flags_t         flags,
  +                          const bson_t                 *selector,
  +                          const bson_t                 *update,
  +                          const mongoc_write_concern_t *write_concern,
  +                          bson_error_t                 *error)
  +{
  +   uint32_t hint;
  +   mongoc_rpc_t rpc;
  +   bson_iter_t iter;
  +   size_t err_offset;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(collection, false);
  +   bson_return_val_if_fail(selector, false);
  +   bson_return_val_if_fail(update, false);
  +
  +   bson_clear (&collection->gle);
  +
  +   if (!(flags & MONGOC_UPDATE_NO_VALIDATE) &&
  +       bson_iter_init (&iter, update) &&
  +       bson_iter_next (&iter) &&
  +       (bson_iter_key (&iter) [0] != '$') &&
  +       !bson_validate (update,
  +                       (BSON_VALIDATE_UTF8 |
  +                        BSON_VALIDATE_UTF8_ALLOW_NULL |
  +                        BSON_VALIDATE_DOLLAR_KEYS |
  +                        BSON_VALIDATE_DOT_KEYS),
  +                       &err_offset)) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_BSON,
  +                      MONGOC_ERROR_BSON_INVALID,
  +                      "update document is corrupt or contains "
  +                      "invalid keys including $ or .");
  +      return false;
  +   } else {
  +      flags &= ~MONGOC_UPDATE_NO_VALIDATE;
  +   }
  +
  +   if (!write_concern) {
  +      write_concern = collection->write_concern;
  +   }
  +
  +   if (!_mongoc_client_warm_up (collection->client, error)) {
  +      RETURN (false);
  +   }
  +
  +   rpc.update.msg_len = 0;
  +   rpc.update.request_id = 0;
  +   rpc.update.response_to = 0;
  +   rpc.update.opcode = MONGOC_OPCODE_UPDATE;
  +   rpc.update.zero = 0;
  +   rpc.update.collection = collection->ns;
  +   rpc.update.flags = flags;
  +   rpc.update.selector = bson_get_data(selector);
  +   rpc.update.update = bson_get_data(update);
  +
  +   if (!(hint = _mongoc_client_sendv (collection->client, &rpc, 1, 0,
  +                                      write_concern, NULL, error))) {
  +      RETURN(false);
  +   }
  +
  +   if (_mongoc_write_concern_has_gle (write_concern)) {
  +      if (!_mongoc_client_recv_gle (collection->client, hint,
  +                                    &collection->gle, error)) {
  +         RETURN(false);
  +      }
  +   }
  +
  +   RETURN(true);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_save --
  + *
  + *       Save @document to @collection.
  + *
  + *       If the document has an _id field, it will be updated. Otherwise,
  + *       the document will be inserted into the collection.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @error is set upon failure if non-NULL.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_collection_save (mongoc_collection_t          *collection,
  +                        const bson_t                 *document,
  +                        const mongoc_write_concern_t *write_concern,
  +                        bson_error_t                 *error)
  +{
  +   bson_iter_t iter;
  +   bool ret;
  +   bson_t selector;
  +
  +   bson_return_val_if_fail(collection, false);
  +   bson_return_val_if_fail(document, false);
  +
  +   if (!bson_iter_init_find(&iter, document, "_id")) {
  +      return mongoc_collection_insert(collection,
  +                                      MONGOC_INSERT_NONE,
  +                                      document,
  +                                      write_concern,
  +                                      error);
  +   }
  +
  +   bson_init(&selector);
  +   bson_append_iter(&selector, NULL, 0, &iter);
  +
  +   ret = mongoc_collection_update(collection,
  +                                  MONGOC_UPDATE_UPSERT,
  +                                  &selector,
  +                                  document,
  +                                  write_concern,
  +                                  error);
  +
  +   bson_destroy(&selector);
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_delete --
  + *
  + *       Delete one or more items from a collection. If you want to
  + *       limit to a single delete, provided MONGOC_DELETE_SINGLE_REMOVE
  + *       for @flags.
  + *
  + * Parameters:
  + *       @collection: A mongoc_collection_t.
  + *       @flags: the delete flags or 0.
  + *       @selector: A selector of documents to delete.
  + *       @write_concern: A write concern or NULL. If NULL, the default
  + *                       write concern for the collection will be used.
  + *       @error: A location for an error or NULL.
  + *
  + * Returns:
  + *       true if successful; otherwise false and error is set.
  + *
  + *       If the write concern does not dictate checking the result, this
  + *       function may return true even if it failed.
  + *
  + * Side effects:
  + *       @collection->gle is setup, depending on write_concern->w value.
  + *       @error is setup upon failure.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_collection_delete (mongoc_collection_t          *collection,
  +                          mongoc_delete_flags_t         flags,
  +                          const bson_t                 *selector,
  +                          const mongoc_write_concern_t *write_concern,
  +                          bson_error_t                 *error)
  +{
  +   uint32_t hint;
  +   mongoc_rpc_t rpc;
  +
  +   bson_return_val_if_fail(collection, false);
  +   bson_return_val_if_fail(selector, false);
  +
  +   bson_clear (&collection->gle);
  +
  +   if (!write_concern) {
  +      write_concern = collection->write_concern;
  +   }
  +
  +   if (!_mongoc_client_warm_up (collection->client, error)) {
  +      return false;
  +   }
  +
  +   rpc.delete.msg_len = 0;
  +   rpc.delete.request_id = 0;
  +   rpc.delete.response_to = 0;
  +   rpc.delete.opcode = MONGOC_OPCODE_DELETE;
  +   rpc.delete.zero = 0;
  +   rpc.delete.collection = collection->ns;
  +   rpc.delete.flags = flags;
  +   rpc.delete.selector = bson_get_data(selector);
  +
  +   if (!(hint = _mongoc_client_sendv(collection->client, &rpc, 1, 0,
  +                                     write_concern, NULL, error))) {
  +      return false;
  +   }
  +
  +   if (_mongoc_write_concern_has_gle(write_concern)) {
  +      if (!_mongoc_client_recv_gle (collection->client, hint,
  +                                    &collection->gle, error)) {
  +         return false;
  +      }
  +   }
  +
  +   return true;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_get_read_prefs --
  + *
  + *       Fetch the default read preferences for the collection.
  + *
  + * Returns:
  + *       A mongoc_read_prefs_t that should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const mongoc_read_prefs_t *
  +mongoc_collection_get_read_prefs (const mongoc_collection_t *collection)
  +{
  +   bson_return_val_if_fail(collection, NULL);
  +   return collection->read_prefs;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_set_read_prefs --
  + *
  + *       Sets the default read preferences for the collection instance.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_collection_set_read_prefs (mongoc_collection_t       *collection,
  +                                  const mongoc_read_prefs_t *read_prefs)
  +{
  +   bson_return_if_fail(collection);
  +
  +   if (collection->read_prefs) {
  +      mongoc_read_prefs_destroy(collection->read_prefs);
  +      collection->read_prefs = NULL;
  +   }
  +
  +   if (read_prefs) {
  +      collection->read_prefs = mongoc_read_prefs_copy(read_prefs);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_get_write_concern --
  + *
  + *       Fetches the default write concern for the collection instance.
  + *
  + * Returns:
  + *       A mongoc_write_concern_t that should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const mongoc_write_concern_t *
  +mongoc_collection_get_write_concern (const mongoc_collection_t *collection)
  +{
  +   bson_return_val_if_fail (collection, NULL);
  +
  +   return collection->write_concern;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_set_write_concern --
  + *
  + *       Sets the default write concern for the collection instance.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_collection_set_write_concern (mongoc_collection_t          *collection,
  +                                     const mongoc_write_concern_t *write_concern)
  +{
  +   bson_return_if_fail(collection);
  +
  +   if (collection->write_concern) {
  +      mongoc_write_concern_destroy(collection->write_concern);
  +      collection->write_concern = NULL;
  +   }
  +
  +   if (write_concern) {
  +      collection->write_concern = mongoc_write_concern_copy(write_concern);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_get_name --
  + *
  + *       Returns the name of the collection, excluding the database name.
  + *
  + * Returns:
  + *       A string which should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const char *
  +mongoc_collection_get_name (mongoc_collection_t *collection)
  +{
  +   BSON_ASSERT (collection);
  +
  +   return collection->collection;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_collection_get_last_error --
  + *
  + *       Returns getLastError document, according to write_concern on last
  + *       executed command for current collection instance.
  + *
  + * Returns:
  + *       NULL or a bson_t that should not be modified or freed. This value
  + *       is not guaranteed to be persistent between calls into the
  + *       mongoc_collection_t instance, and therefore must be copied if
  + *       you would like to keep it around.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const bson_t *
  +mongoc_collection_get_last_error (const mongoc_collection_t *collection) /* IN */
  +{
  +   bson_return_val_if_fail (collection, NULL);
  +
  +   return collection->gle;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-counters.c */
  +
  +
  +#pragma pack(1)
  +typedef struct
  +{
  +   uint32_t offset;
  +   uint32_t slot;
  +   char          category[24];
  +   char          name[32];
  +   char          description[64];
  +} mongoc_counter_info_t;
  +#pragma pack()
  +
  +
  +BSON_STATIC_ASSERT(sizeof(mongoc_counter_info_t) == 128);
  +
  +
  +#pragma pack(1)
  +typedef struct
  +{
  +   uint32_t size;
  +   uint32_t n_cpu;
  +   uint32_t n_counters;
  +   uint32_t infos_offset;
  +   uint32_t values_offset;
  +   uint8_t  padding[44];
  +} mongoc_counters_t;
  +#pragma pack()
  +
  +
  +BSON_STATIC_ASSERT(sizeof(mongoc_counters_t) == 64);
  +
  +
  +#define COUNTER(ident, Category, Name, Description) \
  +   mongoc_counter_t __mongoc_counter_##ident;
  +#include "mongoc-counters.defs"
  +#undef COUNTER
  +
  +
  +/**
  + * mongoc_counters_use_shm:
  + *
  + * Checks to see if counters should be exported over a shared memory segment.
  + *
  + * Returns: true if SHM is to be used.
  + */
  +#ifdef BSON_OS_UNIX
  +static bool
  +mongoc_counters_use_shm (void)
  +{
  +   return !getenv("MONGOC_DISABLE_SHM");
  +}
  +#endif
  +
  +
  +/**
  + * mongoc_counters_calc_size:
  + *
  + * Returns the number of bytes required for the shared memory segment of
  + * the process. This segment contains the various statistical counters for
  + * the process.
  + *
  + * Returns: The number of bytes required.
  + */
  +static size_t
  +mongoc_counters_calc_size (void)
  +{
  +   size_t n_cpu;
  +   size_t n_groups;
  +   size_t size;
  +
  +   n_cpu = _mongoc_get_cpu_count();
  +   n_groups = (LAST_COUNTER / SLOTS_PER_CACHELINE) + 1;
  +   size = (sizeof(mongoc_counters_t) +
  +           (LAST_COUNTER * sizeof(mongoc_counter_info_t)) +
  +           (n_cpu * n_groups * sizeof(mongoc_counter_slots_t)));
  +
  +#ifdef BSON_OS_UNIX
  +   return MAX(getpagesize(), size);
  +#else
  +   return size;
  +#endif
  +}
  +
  +
  +/**
  + * mongoc_counters_destroy:
  + *
  + * Removes the shared memory segment for the current processes counters.
  + */
  +#ifdef BSON_OS_UNIX
  +static void
  +mongoc_counters_destroy (void)
  +{
  +   char name [32];
  +   int pid;
  +
  +   pid = getpid ();
  +   bson_snprintf (name, sizeof name, "/mongoc-%u", pid);
  +   shm_unlink (name);
  +}
  +#endif
  +
  +
  +/**
  + * mongoc_counters_alloc:
  + * @size: The size of the shared memory segment.
  + *
  + * This function allocates the shared memory segment for use by counters
  + * within the process.
  + *
  + * Returns: A shared memory segment, or malloc'd memory on failure.
  + */
  +static void *
  +mongoc_counters_alloc (size_t size)
  +{
  +#ifdef BSON_OS_UNIX
  +   void *mem;
  +   char name[32];
  +   int pid;
  +   int fd;
  +
  +   if (!mongoc_counters_use_shm()) {
  +      goto use_malloc;
  +   }
  +
  +   pid = getpid ();
  +   bson_snprintf (name, sizeof name, "/mongoc-%u", pid);
  +
  +   if (-1 == (fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR))) {
  +      goto use_malloc;
  +   }
  +
  +   /*
  +    * NOTE:
  +    *
  +    * ftruncate() will cause reads to be zero. Therefore, we don't need to
  +    * do write() of zeroes to initialize the shared memory area.
  +    */
  +   if (-1 == ftruncate(fd, size)) {
  +      goto failure;
  +   }
  +
  +   mem = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  +   if (mem == MAP_FAILED) {
  +      goto failure;
  +   }
  +
  +   close(fd);
  +   memset(mem, 0, size);
  +   atexit(mongoc_counters_destroy);
  +
  +   return mem;
  +
  +failure:
  +   shm_unlink(name);
  +   close(fd);
  +
  +use_malloc:
  +#endif
  +
  +   return bson_malloc0(size);
  +}
  +
  +
  +/**
  + * mongoc_counters_register:
  + * @counters: A mongoc_counter_t.
  + * @num: The counter number.
  + * @category: The counter category.
  + * @name: THe counter name.
  + * @description The counter description.
  + *
  + * Registers a new counter in the memory segment for counters. If the counters
  + * are exported over shared memory, it will be made available.
  + *
  + * Returns: The offset to the data for the counters values.
  + */
  +static size_t
  +mongoc_counters_register (mongoc_counters_t *counters,
  +                          uint32_t      num,
  +                          const char        *category,
  +                          const char        *name,
  +                          const char        *description)
  +{
  +   mongoc_counter_info_t *infos;
  +   char *segment;
  +   int n_cpu;
  +
  +   BSON_ASSERT(counters);
  +   BSON_ASSERT(category);
  +   BSON_ASSERT(name);
  +   BSON_ASSERT(description);
  +
  +   /*
  +    * Implementation Note:
  +    *
  +    * The memory barrier is required so that all of the above has been
  +    * completed. Then increment the n_counters so that a reading application
  +    * only knows about the counter after we have initialized it.
  +    */
  +
  +   n_cpu = _mongoc_get_cpu_count();
  +   segment = (char *)counters;
  +
  +   infos = (mongoc_counter_info_t *)(segment + counters->infos_offset);
  +   infos = &infos[counters->n_counters];
  +   infos->slot = num % SLOTS_PER_CACHELINE;
  +   infos->offset = (counters->values_offset +
  +                    ((num / SLOTS_PER_CACHELINE) *
  +                     n_cpu * sizeof(mongoc_counter_slots_t)));
  +
  +   bson_strncpy (infos->category, category, sizeof infos->category);
  +   bson_strncpy (infos->name, name, sizeof infos->name);
  +   bson_strncpy (infos->description, description, sizeof infos->description);
  +
  +   bson_memory_barrier ();
  +
  +   counters->n_counters++;
  +
  +   return infos->offset;
  +}
  +
  +
  +/**
  + * mongoc_counters_init:
  + *
  + * Initializes the mongoc counters system. This should be run on library
  + * initialization using the GCC constructor attribute.
  + */
  +void
  +_mongoc_counters_init (void)
  +{
  +   mongoc_counter_info_t *info;
  +   mongoc_counters_t *counters;
  +   size_t infos_size;
  +   size_t off;
  +   size_t size;
  +   char *segment;
  +
  +   size = mongoc_counters_calc_size();
  +   segment = mongoc_counters_alloc(size);
  +   infos_size = LAST_COUNTER * sizeof *info;
  +
  +   counters = (mongoc_counters_t *)segment;
  +   counters->n_cpu = _mongoc_get_cpu_count();
  +   counters->n_counters = 0;
  +   counters->infos_offset = sizeof *counters;
  +   counters->values_offset = (uint32_t)(counters->infos_offset + infos_size);
  +
  +   BSON_ASSERT ((counters->values_offset % 64) == 0);
  +
  +#define COUNTER(ident, Category, Name, Desc) \
  +   off = mongoc_counters_register(counters, COUNTER_##ident, Category, Name, \
Desc); \  +   __mongoc_counter_##ident.cpus = (void *)(segment + off);
  +#include "mongoc-counters.defs"
  +#undef COUNTER
  +
  +   /*
  +    * NOTE:
  +    *
  +    * Only update the size of the shared memory area for the client after
  +    * we have initialized the rest of the counters. Don't forget our memory
  +    * barrier to prevent compiler reordering.
  +    */
  +   bson_memory_barrier ();
  +   counters->size = (uint32_t)size;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-cursor-array.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "cursor-array"
  +
  +
  +typedef struct
  +{
  +   const bson_t       *result;
  +   bool         has_array;
  +   bson_iter_t         iter;
  +   bson_t              bson;
  +   uint32_t       document_len;
  +   const uint8_t *document;
  +} mongoc_cursor_array_t;
  +
  +
  +static void *
  +_mongoc_cursor_array_new (void)
  +{
  +   mongoc_cursor_array_t *arr;
  +
  +   ENTRY;
  +
  +   arr = bson_malloc0 (sizeof *arr);
  +
  +   RETURN (arr);
  +}
  +
  +
  +void
  +_mongoc_cursor_array_destroy (mongoc_cursor_t *cursor)
  +{
  +   ENTRY;
  +
  +   bson_free (cursor->iface_data);
  +   _mongoc_cursor_destroy (cursor);
  +
  +   EXIT;
  +}
  +
  +
  +bool
  +_mongoc_cursor_array_next (mongoc_cursor_t *cursor,
  +                           const bson_t   **bson)
  +{
  +   bool ret = true;
  +   mongoc_cursor_array_t *arr;
  +   bson_iter_t iter;
  +
  +   ENTRY;
  +
  +   arr = cursor->iface_data;
  +   *bson = NULL;
  +
  +   if (!arr->has_array) {
  +      arr->has_array = true;
  +
  +      ret = _mongoc_cursor_next (cursor, &arr->result);
  +
  +      if (!(ret &&
  +            bson_iter_init_find (&iter, arr->result, "result") &&
  +            BSON_ITER_HOLDS_ARRAY (&iter) &&
  +            bson_iter_recurse (&iter, &arr->iter) &&
  +            bson_iter_next (&arr->iter))) {
  +         ret = false;
  +      }
  +   } else {
  +      ret = bson_iter_next (&arr->iter);
  +   }
  +
  +   if (ret) {
  +      bson_iter_document (&arr->iter, &arr->document_len, &arr->document);
  +      bson_init_static (&arr->bson, arr->document, arr->document_len);
  +
  +      *bson = &arr->bson;
  +   }
  +
  +   RETURN (ret);
  +}
  +
  +
  +mongoc_cursor_t *
  +_mongoc_cursor_array_clone (const mongoc_cursor_t *cursor)
  +{
  +   mongoc_cursor_t *clone;
  +
  +   ENTRY;
  +
  +   clone = _mongoc_cursor_clone (cursor);
  +   _mongoc_cursor_array_init (clone);
  +
  +   RETURN (clone);
  +}
  +
  +
  +bool
  +_mongoc_cursor_array_more (mongoc_cursor_t *cursor)
  +{
  +   bool ret;
  +   mongoc_cursor_array_t *arr;
  +   bson_iter_t iter;
  +
  +   ENTRY;
  +
  +   arr = cursor->iface_data;
  +
  +   if (arr->has_array) {
  +      memcpy (&iter, &arr->iter, sizeof iter);
  +
  +      ret = bson_iter_next (&iter);
  +   } else {
  +      ret = true;
  +   }
  +
  +   RETURN (ret);
  +}
  +
  +
  +static mongoc_cursor_interface_t gMongocCursorArray = {
  +   _mongoc_cursor_array_clone,
  +   _mongoc_cursor_array_destroy,
  +   _mongoc_cursor_array_more,
  +   _mongoc_cursor_array_next,
  +};
  +
  +
  +void
  +_mongoc_cursor_array_init (mongoc_cursor_t *cursor)
  +{
  +   ENTRY;
  +
  +   cursor->iface_data = _mongoc_cursor_array_new ();
  +
  +   memcpy (&cursor->iface, &gMongocCursorArray,
  +           sizeof (mongoc_cursor_interface_t));
  +
  +   EXIT;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-cursor.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "cursor"
  +
  +
  +static const char *
  +_mongoc_cursor_get_read_mode_string (mongoc_read_mode_t mode)
  +{
  +   switch (mode) {
  +   case MONGOC_READ_PRIMARY:
  +      return "primary";
  +   case MONGOC_READ_PRIMARY_PREFERRED:
  +      return "primaryPreferred";
  +   case MONGOC_READ_SECONDARY:
  +      return "secondary";
  +   case MONGOC_READ_SECONDARY_PREFERRED:
  +      return "secondaryPreferred";
  +   case MONGOC_READ_NEAREST:
  +      return "nearest";
  +   default:
  +      return "";
  +   }
  +}
  +
  +static int32_t
  +_mongoc_n_return (mongoc_cursor_t * cursor)
  +{
  +   /* by default, use the batch size */
  +   int32_t r = cursor->batch_size;
  +
  +   /* if we have a limit */
  +   if (cursor->limit) {
  +      /* calculate remaining */
  +      uint32_t remaining = cursor->limit - cursor->count;
  +
  +      /* if we had a batch size */
  +      if (r) {
  +         /* use min of batch or remaining */
  +         r = MIN(r, (int32_t)remaining);
  +      } else {
  +         /* if we didn't, just use the remaining */
  +         r = remaining;
  +      }
  +
  +      r = -r;
  +   }
  +
  +   return r;
  +}
  +
  +mongoc_cursor_t *
  +_mongoc_cursor_new (mongoc_client_t           *client,
  +                    const char                *db_and_collection,
  +                    mongoc_query_flags_t       flags,
  +                    uint32_t                   skip,
  +                    uint32_t                   limit,
  +                    uint32_t                   batch_size,
  +                    bool                       is_command,
  +                    const bson_t              *query,
  +                    const bson_t              *fields,
  +                    const mongoc_read_prefs_t *read_prefs)
  +{
  +   mongoc_read_mode_t mode;
  +   mongoc_cursor_t *cursor;
  +   const bson_t *tags;
  +   const char *mode_str;
  +   bson_t child;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(client);
  +   BSON_ASSERT(db_and_collection);
  +   BSON_ASSERT(query);
  +
  +   /* we can't have exhaust queries with limits */
  +   BSON_ASSERT (!((flags & MONGOC_QUERY_EXHAUST) && limit));
  +
  +   /* we can't have exhaust queries with sharded clusters */
  +   BSON_ASSERT (!((flags & MONGOC_QUERY_EXHAUST) && client->cluster.isdbgrid));
  +
  +   /*
  +    * Cursors execute their query lazily. This sadly means that we must copy
  +    * some extra data around between the bson_t structures. This should be
  +    * small in most cases, so it reduces to a pure memcpy. The benefit to this
  +    * design is simplified error handling by API consumers.
  +    */
  +
  +   cursor = bson_malloc0(sizeof *cursor);
  +   cursor->client = client;
  +   bson_strncpy (cursor->ns, db_and_collection, sizeof cursor->ns);
  +   cursor->nslen = (uint32_t)strlen(cursor->ns);
  +   cursor->flags = flags;
  +   cursor->skip = skip;
  +   cursor->limit = limit;
  +   cursor->batch_size = batch_size;
  +
  +   cursor->is_command = is_command;
  +
  +   if (!cursor->is_command && !bson_has_field (query, "$query")) {
  +      bson_init (&cursor->query);
  +      bson_append_document (&cursor->query, "$query", 6, query);
  +   } else {
  +      bson_copy_to (query, &cursor->query);
  +   }
  +
  +   if (read_prefs) {
  +      cursor->read_prefs = mongoc_read_prefs_copy (read_prefs);
  +
  +      mode = mongoc_read_prefs_get_mode (read_prefs);
  +      tags = mongoc_read_prefs_get_tags (read_prefs);
  +
  +      if (mode != MONGOC_READ_PRIMARY) {
  +         flags |= MONGOC_QUERY_SLAVE_OK;
  +
  +         if ((mode != MONGOC_READ_SECONDARY_PREFERRED) || tags) {
  +            bson_append_document_begin (&cursor->query, "$readPreference",
  +                                        15, &child);
  +            mode_str = _mongoc_cursor_get_read_mode_string (mode);
  +            bson_append_utf8 (&child, "mode", 4, mode_str, -1);
  +            if (tags) {
  +               bson_append_array (&child, "tags", 4, tags);
  +            }
  +            bson_append_document_end (&cursor->query, &child);
  +         }
  +      }
  +   }
  +
  +   if (fields) {
  +      bson_copy_to(fields, &cursor->fields);
  +   } else {
  +      bson_init(&cursor->fields);
  +   }
  +
  +   _mongoc_buffer_init(&cursor->buffer, NULL, 0, NULL);
  +
  +   mongoc_counter_cursors_active_inc();
  +
  +   RETURN(cursor);
  +}
  +
  +
  +static void
  +_mongoc_cursor_kill_cursor (mongoc_cursor_t *cursor,
  +                            int64_t     cursor_id)
  +{
  +   mongoc_rpc_t rpc = {{ 0 }};
  +
  +   ENTRY;
  +
  +   bson_return_if_fail(cursor);
  +   bson_return_if_fail(cursor_id);
  +
  +   rpc.kill_cursors.msg_len = 0;
  +   rpc.kill_cursors.request_id = 0;
  +   rpc.kill_cursors.response_to = 0;
  +   rpc.kill_cursors.opcode = MONGOC_OPCODE_KILL_CURSORS;
  +   rpc.kill_cursors.zero = 0;
  +   rpc.kill_cursors.cursors = &cursor_id;
  +   rpc.kill_cursors.n_cursors = 1;
  +
  +   _mongoc_client_sendv (cursor->client, &rpc, 1, 0, NULL, NULL, NULL);
  +
  +   EXIT;
  +}
  +
  +
  +void
  +mongoc_cursor_destroy (mongoc_cursor_t *cursor)
  +{
  +   BSON_ASSERT(cursor);
  +
  +   if (cursor->iface.destroy) {
  +      cursor->iface.destroy(cursor);
  +   } else {
  +      _mongoc_cursor_destroy(cursor);
  +   }
  +
  +   EXIT;
  +}
  +
  +void
  +_mongoc_cursor_destroy (mongoc_cursor_t *cursor)
  +{
  +   ENTRY;
  +
  +   bson_return_if_fail(cursor);
  +
  +   if (cursor->in_exhaust) {
  +      cursor->client->in_exhaust = false;
  +
  +      if (!cursor->done) {
  +         _mongoc_cluster_disconnect_node (
  +            &cursor->client->cluster,
  +            &cursor->client->cluster.nodes[cursor->hint - 1]);
  +      }
  +   } else if (cursor->rpc.reply.cursor_id) {
  +      _mongoc_cursor_kill_cursor(cursor, cursor->rpc.reply.cursor_id);
  +   }
  +
  +   if (cursor->reader) {
  +      bson_reader_destroy(cursor->reader);
  +      cursor->reader = NULL;
  +   }
  +
  +   bson_destroy(&cursor->query);
  +   bson_destroy(&cursor->fields);
  +   _mongoc_buffer_destroy(&cursor->buffer);
  +   mongoc_read_prefs_destroy(cursor->read_prefs);
  +
  +   bson_free(cursor);
  +
  +   mongoc_counter_cursors_active_dec();
  +   mongoc_counter_cursors_disposed_inc();
  +
  +   EXIT;
  +}
  +
  +
  +static void
  +_mongoc_cursor_populate_error (mongoc_cursor_t *cursor,
  +                               const bson_t    *doc,
  +                               bson_error_t    *error)
  +{
  +   uint32_t code = MONGOC_ERROR_QUERY_FAILURE;
  +   bson_iter_t iter;
  +   const char *msg = "Unknown query failure";
  +
  +   BSON_ASSERT (cursor);
  +   BSON_ASSERT (doc);
  +   BSON_ASSERT (error);
  +
  +   if (bson_iter_init_find (&iter, doc, "code") &&
  +       BSON_ITER_HOLDS_INT32 (&iter)) {
  +      code = bson_iter_int32 (&iter);
  +   }
  +
  +   if (bson_iter_init_find (&iter, doc, "$err") &&
  +       BSON_ITER_HOLDS_UTF8 (&iter)) {
  +      msg = bson_iter_utf8 (&iter, NULL);
  +   }
  +
  +   if (cursor->is_command &&
  +       bson_iter_init_find (&iter, doc, "errmsg") &&
  +       BSON_ITER_HOLDS_UTF8 (&iter)) {
  +      msg = bson_iter_utf8 (&iter, NULL);
  +   }
  +
  +   bson_set_error(error, MONGOC_ERROR_QUERY, code, "%s", msg);
  +}
  +
  +
  +static bool
  +_mongoc_cursor_unwrap_failure (mongoc_cursor_t *cursor)
  +{
  +   bson_iter_t iter;
  +   bson_t b;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(cursor, false);
  +
  +   if (cursor->rpc.header.opcode != MONGOC_OPCODE_REPLY) {
  +      bson_set_error(&cursor->error,
  +                     MONGOC_ERROR_PROTOCOL,
  +                     MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
  +                     "Received rpc other than OP_REPLY.");
  +      RETURN(true);
  +   }
  +
  +   if ((cursor->rpc.reply.flags & MONGOC_REPLY_QUERY_FAILURE)) {
  +      if (_mongoc_rpc_reply_get_first(&cursor->rpc.reply, &b)) {
  +         _mongoc_cursor_populate_error(cursor, &b, &cursor->error);
  +         bson_destroy(&b);
  +      } else {
  +         bson_set_error(&cursor->error,
  +                        MONGOC_ERROR_QUERY,
  +                        MONGOC_ERROR_QUERY_FAILURE,
  +                        "Unknown query failure.");
  +      }
  +      RETURN(true);
  +   } else if (cursor->is_command) {
  +      if (_mongoc_rpc_reply_get_first(&cursor->rpc.reply, &b)) {
  +         if ( bson_iter_init_find(&iter, &b, "ok") &&
  +              bson_iter_as_bool(&iter)) {
  +            return false;
  +         } else {
  +            _mongoc_cursor_populate_error(cursor, &b, &cursor->error);
  +            bson_destroy(&b);
  +            return true;
  +         }
  +      } else {
  +         return true;
  +      }
  +   }
  +
  +   if ((cursor->rpc.reply.flags & MONGOC_REPLY_CURSOR_NOT_FOUND)) {
  +      bson_set_error(&cursor->error,
  +                     MONGOC_ERROR_CURSOR,
  +                     MONGOC_ERROR_CURSOR_INVALID_CURSOR,
  +                     "The cursor is invalid or has expired.");
  +      RETURN(true);
  +   }
  +
  +   RETURN(false);
  +}
  +
  +
  +static bool
  +_mongoc_cursor_query (mongoc_cursor_t *cursor)
  +{
  +   uint32_t hint;
  +   uint32_t request_id;
  +   mongoc_rpc_t rpc;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(cursor, false);
  +
  +   if (!_mongoc_client_warm_up (cursor->client, &cursor->error)) {
  +      cursor->failed = true;
  +      RETURN (false);
  +   }
  +
  +   rpc.query.msg_len = 0;
  +   rpc.query.request_id = 0;
  +   rpc.query.response_to = 0;
  +   rpc.query.opcode = MONGOC_OPCODE_QUERY;
  +   rpc.query.flags = cursor->flags;
  +   rpc.query.collection = cursor->ns;
  +   rpc.query.skip = cursor->skip;
  +   if ((cursor->flags & MONGOC_QUERY_TAILABLE_CURSOR)) {
  +      rpc.query.n_return = 0;
  +   } else {
  +      rpc.query.n_return = _mongoc_n_return(cursor);
  +   }
  +   rpc.query.query = bson_get_data(&cursor->query);
  +   rpc.query.fields = bson_get_data(&cursor->fields);
  +
  +   if (!(hint = _mongoc_client_sendv (cursor->client, &rpc, 1, 0,
  +                                      NULL, cursor->read_prefs,
  +                                      &cursor->error))) {
  +      goto failure;
  +   }
  +
  +   cursor->hint = hint;
  +   request_id = BSON_UINT32_FROM_LE(rpc.header.request_id);
  +
  +   _mongoc_buffer_clear(&cursor->buffer, false);
  +
  +   if (!_mongoc_client_recv(cursor->client,
  +                            &cursor->rpc,
  +                            &cursor->buffer,
  +                            hint,
  +                            &cursor->error)) {
  +      goto failure;
  +   }
  +
  +   if ((cursor->rpc.header.opcode != MONGOC_OPCODE_REPLY) ||
  +       (cursor->rpc.header.response_to != request_id)) {
  +      bson_set_error(&cursor->error,
  +                     MONGOC_ERROR_PROTOCOL,
  +                     MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
  +                     "A reply to an invalid request id was received.");
  +      goto failure;
  +   }
  +
  +   if (_mongoc_cursor_unwrap_failure(cursor)) {
  +      if ((cursor->error.domain == MONGOC_ERROR_QUERY) &&
  +          (cursor->error.code == MONGOC_ERROR_QUERY_NOT_TAILABLE)) {
  +         cursor->failed = true;
  +      }
  +      goto failure;
  +   }
  +
  +   if (cursor->reader) {
  +      bson_reader_destroy(cursor->reader);
  +   }
  +
  +   cursor->reader = bson_reader_new_from_data(cursor->rpc.reply.documents,
  +                                              cursor->rpc.reply.documents_len);
  +
  +   if (cursor->flags & MONGOC_QUERY_EXHAUST) {
  +      cursor->in_exhaust = true;
  +      cursor->client->in_exhaust = true;
  +   }
  +
  +   cursor->done = false;
  +   cursor->end_of_event = false;
  +   cursor->sent = true;
  +   RETURN(true);
  +
  +failure:
  +   cursor->failed = true;
  +   cursor->done = true;
  +   RETURN(false);
  +}
  +
  +
  +static bool
  +_mongoc_cursor_get_more (mongoc_cursor_t *cursor)
  +{
  +   uint64_t cursor_id;
  +   uint32_t request_id;
  +   mongoc_rpc_t rpc;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cursor);
  +
  +   if (! cursor->in_exhaust) {
  +      if (!_mongoc_client_warm_up (cursor->client, &cursor->error)) {
  +         cursor->failed = true;
  +         RETURN (false);
  +      }
  +
  +      if (!(cursor_id = cursor->rpc.reply.cursor_id)) {
  +         bson_set_error(&cursor->error,
  +                        MONGOC_ERROR_CURSOR,
  +                        MONGOC_ERROR_CURSOR_INVALID_CURSOR,
  +                        "No valid cursor was provided.");
  +         goto failure;
  +      }
  +
  +      rpc.get_more.msg_len = 0;
  +      rpc.get_more.request_id = 0;
  +      rpc.get_more.response_to = 0;
  +      rpc.get_more.opcode = MONGOC_OPCODE_GET_MORE;
  +      rpc.get_more.zero = 0;
  +      rpc.get_more.collection = cursor->ns;
  +      if ((cursor->flags & MONGOC_QUERY_TAILABLE_CURSOR)) {
  +         rpc.get_more.n_return = 0;
  +      } else {
  +         rpc.get_more.n_return = _mongoc_n_return(cursor);
  +      }
  +      rpc.get_more.cursor_id = cursor_id;
  +
  +      /*
  +       * TODO: Stamp protections for disconnections.
  +       */
  +
  +      if (!_mongoc_client_sendv(cursor->client, &rpc, 1, cursor->hint,
  +                                NULL, cursor->read_prefs, &cursor->error)) {
  +         cursor->done = true;
  +         cursor->failed = true;
  +         RETURN(false);
  +      }
  +
  +      request_id = BSON_UINT32_FROM_LE(rpc.header.request_id);
  +   } else {
  +      request_id = BSON_UINT32_FROM_LE(cursor->rpc.header.request_id);
  +   }
  +
  +   _mongoc_buffer_clear(&cursor->buffer, false);
  +
  +   if (!_mongoc_client_recv(cursor->client,
  +                            &cursor->rpc,
  +                            &cursor->buffer,
  +                            cursor->hint,
  +                            &cursor->error)) {
  +      goto failure;
  +   }
  +
  +   if ((cursor->rpc.header.opcode != MONGOC_OPCODE_REPLY) ||
  +       (cursor->rpc.header.response_to != request_id)) {
  +      bson_set_error(&cursor->error,
  +                     MONGOC_ERROR_PROTOCOL,
  +                     MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
  +                     "A reply to an invalid request id was received.");
  +      goto failure;
  +   }
  +
  +   if (_mongoc_cursor_unwrap_failure(cursor)) {
  +      goto failure;
  +   }
  +
  +   if (cursor->reader) {
  +      bson_reader_destroy(cursor->reader);
  +   }
  +
  +   cursor->reader = bson_reader_new_from_data(cursor->rpc.reply.documents,
  +                                              cursor->rpc.reply.documents_len);
  +
  +   cursor->end_of_event = false;
  +
  +   RETURN(true);
  +
  +failure:
  +   cursor->done = true;
  +   cursor->failed = true;
  +
  +   RETURN(false);
  +}
  +
  +
  +bool
  +mongoc_cursor_error (mongoc_cursor_t *cursor,
  +                     bson_error_t    *error)
  +{
  +   bool ret;
  +
  +   BSON_ASSERT(cursor);
  +
  +   if (cursor->iface.error) {
  +      ret = cursor->iface.error(cursor, error);
  +   } else {
  +      ret = _mongoc_cursor_error(cursor, error);
  +   }
  +
  +   if (ret && error) {
  +      /*
  +       * Rewrite the error code if we are talking to an older mongod
  +       * and the command was not found. It used to simply return an
  +       * error code of 17 and we can synthesize 59.
  +       */
  +      if (cursor->is_command &&
  +          (error->code == MONGOC_ERROR_PROTOCOL_ERROR)) {
  +         error->code = MONGOC_ERROR_QUERY_COMMAND_NOT_FOUND;
  +      }
  +   }
  +
  +   RETURN(ret);
  +}
  +
  +
  +bool
  +_mongoc_cursor_error (mongoc_cursor_t *cursor,
  +                      bson_error_t    *error)
  +{
  +   ENTRY;
  +
  +   bson_return_val_if_fail(cursor, false);
  +
  +   if (BSON_UNLIKELY(cursor->failed)) {
  +      bson_set_error(error,
  +                     cursor->error.domain,
  +                     cursor->error.code,
  +                     "%s",
  +                     cursor->error.message);
  +      RETURN(true);
  +   }
  +
  +   RETURN(false);
  +}
  +
  +
  +bool
  +mongoc_cursor_next (mongoc_cursor_t  *cursor,
  +                    const bson_t    **bson)
  +{
  +   bool ret;
  +
  +   BSON_ASSERT(cursor);
  +   BSON_ASSERT(bson);
  +
  +   if (cursor->iface.next) {
  +      ret = cursor->iface.next(cursor, bson);
  +   } else {
  +      ret = _mongoc_cursor_next(cursor, bson);
  +   }
  +
  +   cursor->count++;
  +
  +   RETURN(ret);
  +}
  +
  +
  +bool
  +_mongoc_cursor_next (mongoc_cursor_t  *cursor,
  +                     const bson_t    **bson)
  +{
  +   const bson_t *b;
  +   bool eof;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cursor);
  +
  +
  +   if (cursor->client->in_exhaust && ! cursor->in_exhaust) {
  +      bson_set_error(&cursor->error,
  +                     MONGOC_ERROR_CLIENT,
  +                     MONGOC_ERROR_CLIENT_IN_EXHAUST,
  +                     "Another cursor derived from this client is in exhaust.");
  +      cursor->failed = true;
  +      RETURN(false);
  +   }
  +
  +   if (bson) {
  +      *bson = NULL;
  +   }
  +
  +   if (cursor->limit && cursor->count >= cursor->limit) {
  +      return false;
  +   }
  +
  +   /*
  +    * Short circuit if we are finished already.
  +    */
  +   if (BSON_UNLIKELY(cursor->done)) {
  +      RETURN(false);
  +   }
  +
  +   /*
  +    * Check to see if we need to send a GET_MORE for more results.
  +    */
  +   if (!cursor->sent) {
  +      if (!_mongoc_cursor_query(cursor)) {
  +         RETURN(false);
  +      }
  +   } else if (BSON_UNLIKELY(cursor->end_of_event)) {
  +      if (!_mongoc_cursor_get_more(cursor)) {
  +         RETURN(false);
  +      }
  +   }
  +
  +   /*
  +    * Read the next BSON document from the event.
  +    */
  +   eof = false;
  +   b = bson_reader_read(cursor->reader, &eof);
  +   cursor->end_of_event = eof;
  +
  +   cursor->done = cursor->end_of_event && (
  +      (cursor->in_exhaust && !cursor->rpc.reply.cursor_id) ||
  +      (!b && !(cursor->flags & MONGOC_QUERY_TAILABLE_CURSOR))
  +      );
  +
  +   /*
  +    * Do a supplimental check to see if we had a corrupted reply in the
  +    * document stream.
  +    */
  +   if (!b && !eof) {
  +      cursor->failed = true;
  +      bson_set_error(&cursor->error,
  +                     MONGOC_ERROR_CURSOR,
  +                     MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
  +                     "The reply was corrupt.");
  +      RETURN(false);
  +   }
  +
  +   if (bson) {
  +      *bson = b;
  +   }
  +
  +   RETURN(!!b);
  +}
  +
  +
  +bool
  +mongoc_cursor_more (mongoc_cursor_t *cursor)
  +{
  +   bool ret;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT(cursor);
  +
  +   if (cursor->iface.more) {
  +      ret = cursor->iface.more(cursor);
  +   } else {
  +      ret = _mongoc_cursor_more(cursor);
  +   }
  +
  +   RETURN(ret);
  +}
  +
  +
  +bool
  +_mongoc_cursor_more (mongoc_cursor_t *cursor)
  +{
  +   bson_return_val_if_fail(cursor, false);
  +
  +   return ((!cursor->sent) ||
  +           (cursor->rpc.reply.cursor_id) ||
  +           !cursor->end_of_event);
  +}
  +
  +
  +void
  +mongoc_cursor_get_host (mongoc_cursor_t    *cursor,
  +                        mongoc_host_list_t *host)
  +{
  +   BSON_ASSERT(cursor);
  +   BSON_ASSERT(host);
  +
  +   if (cursor->iface.get_host) {
  +      cursor->iface.get_host(cursor, host);
  +   } else {
  +      _mongoc_cursor_get_host(cursor, host);
  +   }
  +
  +   EXIT;
  +}
  +
  +
  +void
  +_mongoc_cursor_get_host (mongoc_cursor_t    *cursor,
  +                         mongoc_host_list_t *host)
  +{
  +   bson_return_if_fail(cursor);
  +   bson_return_if_fail(host);
  +
  +   memset(host, 0, sizeof *host);
  +
  +   if (!cursor->hint) {
  +      MONGOC_WARNING("%s(): Must send query before fetching peer.",
  +                     __FUNCTION__);
  +      return;
  +   }
  +
  +   *host = cursor->client->cluster.nodes[cursor->hint - 1].host;
  +   host->next = NULL;
  +}
  +
  +
  +mongoc_cursor_t *
  +mongoc_cursor_clone (const mongoc_cursor_t *cursor)
  +{
  +   mongoc_cursor_t *ret;
  +
  +   BSON_ASSERT(cursor);
  +
  +   if (cursor->iface.clone) {
  +      ret = cursor->iface.clone(cursor);
  +   } else {
  +      ret = _mongoc_cursor_clone(cursor);
  +   }
  +
  +   RETURN(ret);
  +}
  +
  +
  +mongoc_cursor_t *
  +_mongoc_cursor_clone (const mongoc_cursor_t *cursor)
  +{
  +   mongoc_cursor_t *_clone;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (cursor);
  +
  +   _clone = bson_malloc0 (sizeof *_clone);
  +
  +   _clone->client = cursor->client;
  +   _clone->is_command = cursor->is_command;
  +   _clone->flags = cursor->flags;
  +   _clone->skip = cursor->skip;
  +   _clone->batch_size = cursor->batch_size;
  +   _clone->limit = cursor->limit;
  +   _clone->nslen = cursor->nslen;
  +
  +   if (cursor->read_prefs) {
  +      _clone->read_prefs = mongoc_read_prefs_copy (cursor->read_prefs);
  +   }
  +
  +   bson_copy_to (&cursor->query, &_clone->query);
  +   bson_copy_to (&cursor->fields, &_clone->fields);
  +
  +   bson_strncpy (_clone->ns, cursor->ns, sizeof _clone->ns);
  +
  +   _mongoc_buffer_init (&_clone->buffer, NULL, 0, NULL);
  +
  +   mongoc_counter_cursors_active_inc ();
  +
  +   RETURN (_clone);
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-cursor-cursorid.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "cursor-cursorid"
  +
  +
  +typedef struct
  +{
  +   bool has_cursor;
  +} mongoc_cursor_cursorid_t;
  +
  +
  +static void *
  +_mongoc_cursor_cursorid_new (void)
  +{
  +   mongoc_cursor_cursorid_t *cid;
  +
  +   ENTRY;
  +
  +   cid = bson_malloc0 (sizeof *cid);
  +
  +   RETURN (cid);
  +}
  +
  +
  +void
  +_mongoc_cursor_cursorid_destroy (mongoc_cursor_t *cursor)
  +{
  +   ENTRY;
  +
  +   bson_free (cursor->iface_data);
  +   _mongoc_cursor_destroy (cursor);
  +
  +   EXIT;
  +}
  +
  +
  +bool
  +_mongoc_cursor_cursorid_next (mongoc_cursor_t *cursor,
  +                              const bson_t   **bson)
  +{
  +   bool ret;
  +   mongoc_cursor_cursorid_t *cid;
  +   bson_iter_t iter;
  +   bson_iter_t child;
  +   const char *ns;
  +
  +   ENTRY;
  +
  +   cid = cursor->iface_data;
  +
  +   ret = _mongoc_cursor_next (cursor, bson);
  +
  +   if (!cid->has_cursor) {
  +      cid->has_cursor = true;
  +
  +      if (ret &&
  +          bson_iter_init_find (&iter, *bson, "cursor") &&
  +          BSON_ITER_HOLDS_DOCUMENT (&iter) &&
  +          bson_iter_recurse (&iter, &child)) {
  +         while (bson_iter_next (&child)) {
  +            if (strcmp (bson_iter_key (&child), "id") == 0) {
  +               cursor->rpc.reply.cursor_id = bson_iter_int64 (&child);
  +            } else if (strcmp (bson_iter_key (&child), "ns") == 0) {
  +               ns = bson_iter_utf8 (&child, &cursor->nslen);
  +               bson_strncpy (cursor->ns, ns, sizeof cursor->ns);
  +            }
  +         }
  +
  +         cursor->is_command = false;
  +
  +         ret = _mongoc_cursor_next (cursor, bson);
  +      }
  +   }
  +
  +
  +   RETURN (ret);
  +}
  +
  +
  +mongoc_cursor_t *
  +_mongoc_cursor_cursorid_clone (const mongoc_cursor_t *cursor)
  +{
  +   mongoc_cursor_t *clone;
  +
  +   ENTRY;
  +
  +   clone = _mongoc_cursor_clone (cursor);
  +   _mongoc_cursor_cursorid_init (clone);
  +
  +   RETURN (clone);
  +}
  +
  +
  +static mongoc_cursor_interface_t gMongocCursorCursorid = {
  +   _mongoc_cursor_cursorid_clone,
  +   _mongoc_cursor_cursorid_destroy,
  +   NULL,
  +   _mongoc_cursor_cursorid_next,
  +};
  +
  +
  +void
  +_mongoc_cursor_cursorid_init (mongoc_cursor_t *cursor)
  +{
  +   ENTRY;
  +
  +   cursor->iface_data = _mongoc_cursor_cursorid_new ();
  +
  +   memcpy (&cursor->iface, &gMongocCursorCursorid,
  +           sizeof (mongoc_cursor_interface_t));
  +
  +   EXIT;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-database.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "database"
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_database_new --
  + *
  + *       Create a new instance of mongoc_database_t for @client.
  + *
  + *       @client must stay valid for the life of the resulting
  + *       database structure.
  + *
  + * Returns:
  + *       A newly allocated mongoc_database_t that should be freed with
  + *       mongoc_database_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_database_t *
  +_mongoc_database_new (mongoc_client_t              *client,
  +                      const char                   *name,
  +                      const mongoc_read_prefs_t    *read_prefs,
  +                      const mongoc_write_concern_t *write_concern)
  +{
  +   mongoc_database_t *db;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(client, NULL);
  +   bson_return_val_if_fail(name, NULL);
  +
  +   db = bson_malloc0(sizeof *db);
  +   db->client = client;
  +   db->write_concern = write_concern ?
  +      mongoc_write_concern_copy(write_concern) :
  +      mongoc_write_concern_new();
  +   db->read_prefs = read_prefs ?
  +      mongoc_read_prefs_copy(read_prefs) :
  +      mongoc_read_prefs_new(MONGOC_READ_PRIMARY);
  +
  +   bson_strncpy (db->name, name, sizeof db->name);
  +
  +   RETURN(db);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_database_destroy --
  + *
  + *       Releases resources associated with @database.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       Everything.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_database_destroy (mongoc_database_t *database)
  +{
  +   ENTRY;
  +
  +   bson_return_if_fail(database);
  +
  +   if (database->read_prefs) {
  +      mongoc_read_prefs_destroy(database->read_prefs);
  +      database->read_prefs = NULL;
  +   }
  +
  +   if (database->write_concern) {
  +      mongoc_write_concern_destroy(database->write_concern);
  +      database->write_concern = NULL;
  +   }
  +
  +   bson_free(database);
  +
  +   EXIT;
  +}
  +
  +
  +mongoc_cursor_t *
  +mongoc_database_command (mongoc_database_t         *database,
  +                         mongoc_query_flags_t       flags,
  +                         uint32_t              skip,
  +                         uint32_t              limit,
  +                         uint32_t              batch_size,
  +                         const bson_t              *command,
  +                         const bson_t              *fields,
  +                         const mongoc_read_prefs_t *read_prefs)
  +{
  +   BSON_ASSERT (database);
  +   BSON_ASSERT (command);
  +
  +   if (!read_prefs) {
  +      read_prefs = database->read_prefs;
  +   }
  +
  +   return mongoc_client_command (database->client, database->name, flags, skip,
  +                                 limit, batch_size, command, fields, read_prefs);
  +}
  +
  +
  +bool
  +mongoc_database_command_simple (mongoc_database_t         *database,
  +                                const bson_t              *command,
  +                                const mongoc_read_prefs_t *read_prefs,
  +                                bson_t                    *reply,
  +                                bson_error_t              *error)
  +{
  +   BSON_ASSERT (database);
  +   BSON_ASSERT (command);
  +
  +   if (!read_prefs) {
  +      read_prefs = database->read_prefs;
  +   }
  +
  +   return mongoc_client_command_simple (database->client, database->name,
  +                                        command, read_prefs, reply, error);
  +}
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_database_drop --
  + *
  + *       Requests that the MongoDB server drops @database, including all
  + *       collections and indexes associated with @database.
  + *
  + *       Make sure this is really what you want!
  + *
  + * Returns:
  + *       true if @database was dropped.
  + *
  + * Side effects:
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_database_drop (mongoc_database_t *database,
  +                      bson_error_t      *error)
  +{
  +   bool ret;
  +   bson_t cmd;
  +
  +   bson_return_val_if_fail(database, false);
  +
  +   bson_init(&cmd);
  +   bson_append_int32(&cmd, "dropDatabase", 12, 1);
  +   ret = mongoc_database_command_simple(database, &cmd, NULL, NULL, error);
  +   bson_destroy(&cmd);
  +
  +   return ret;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_database_add_user_legacy --
  + *
  + *       A helper to add a user or update their password on @database.
  + *       This uses the legacy protocol by inserting into system.users.
  + *
  + * Returns:
  + *       true if successful; otherwise false and @error is set.
  + *
  + * Side effects:
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +mongoc_database_add_user_legacy (mongoc_database_t *database,
  +                                 const char        *username,
  +                                 const char        *password,
  +                                 bson_error_t      *error)
  +{
  +   mongoc_collection_t *collection;
  +   mongoc_cursor_t *cursor = NULL;
  +   const bson_t *doc;
  +   bool ret = false;
  +   bson_t query;
  +   bson_t user;
  +   char *input;
  +   char *pwd = NULL;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail(database, false);
  +   bson_return_val_if_fail(username, false);
  +   bson_return_val_if_fail(password, false);
  +
  +   /*
  +    * Users are stored in the <dbname>.system.users virtual collection.
  +    * However, this will likely change to a command soon.
  +    */
  +   collection = mongoc_client_get_collection(database->client,
  +                                             database->name,
  +                                             "system.users");
  +   BSON_ASSERT(collection);
  +
  +   /*
  +    * Hash the users password.
  +    */
  +   input = bson_strdup_printf("%s:mongo:%s", username, password);
  +   pwd = _mongoc_hex_md5(input);
  +   bson_free(input);
  +
  +   /*
  +    * Check to see if the user exists. If so, we will update the
  +    * password instead of inserting a new user.
  +    */
  +   bson_init(&query);
  +   bson_append_utf8(&query, "user", 4, username, -1);
  +   cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0,
  +                                   &query, NULL, NULL);
  +   if (!mongoc_cursor_next(cursor, &doc)) {
  +      if (mongoc_cursor_error(cursor, error)) {
  +         GOTO (failure);
  +      }
  +      bson_init(&user);
  +      bson_append_utf8(&user, "user", 4, username, -1);
  +      bson_append_bool(&user, "readOnly", 8, false);
  +      bson_append_utf8(&user, "pwd", 3, pwd, -1);
  +   } else {
  +      bson_copy_to_excluding(doc, &user, "pwd", (char *)NULL);
  +      bson_append_utf8(&user, "pwd", 3, pwd, -1);
  +   }
  +
  +   if (!mongoc_collection_save(collection, &user, NULL, error)) {
  +      GOTO (failure_with_user);
  +   }
  +
  +   ret = true;
  +
  +failure_with_user:
  +   bson_destroy(&user);
  +
  +failure:
  +   if (cursor) {
  +      mongoc_cursor_destroy(cursor);
  +   }
  +   mongoc_collection_destroy(collection);
  +   bson_destroy(&query);
  +   bson_free(pwd);
  +
  +   RETURN (ret);
  +}
  +
  +
  +bool
  +mongoc_database_remove_user (mongoc_database_t *database,
  +                             const char        *username,
  +                             bson_error_t      *error)
  +{
  +   mongoc_collection_t *col;
  +   bson_error_t lerror;
  +   bson_t cmd;
  +   bool ret;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail (database, false);
  +   bson_return_val_if_fail (username, false);
  +
  +   bson_init (&cmd);
  +   BSON_APPEND_UTF8 (&cmd, "dropUser", username);
  +   ret = mongoc_database_command_simple (database, &cmd, NULL, NULL, &lerror);
  +   bson_destroy (&cmd);
  +
  +   if (!ret && (lerror.code == MONGOC_ERROR_QUERY_COMMAND_NOT_FOUND)) {
  +      bson_init (&cmd);
  +      BSON_APPEND_UTF8 (&cmd, "user", username);
  +
  +      col = mongoc_client_get_collection (database->client, database->name,
  +                                          "system.users");
  +      BSON_ASSERT (col);
  +
  +      ret = mongoc_collection_delete (col,
  +                                      MONGOC_DELETE_SINGLE_REMOVE,
  +                                      &cmd,
  +                                      NULL,
  +                                      error);
  +
  +      bson_destroy (&cmd);
  +      mongoc_collection_destroy (col);
  +   }
  +
  +   RETURN (ret);
  +}
  +
  +
  +/**
  + * mongoc_database_add_user:
  + * @database: A #mongoc_database_t.
  + * @username: A string containing the username.
  + * @password: (allow-none): A string containing password, or NULL.
  + * @roles: (allow-none): An optional bson_t of roles.
  + * @custom_data: (allow-none): An optional bson_t of data to store.
  + * @error: (out) (allow-none): A location for a bson_error_t or %NULL.
  + *
  + * Creates a new user with access to @database.
  + *
  + * Returns: None.
  + * Side effects: None.
  + */
  +bool
  +mongoc_database_add_user (mongoc_database_t *database,
  +                          const char        *username,
  +                          const char        *password,
  +                          const bson_t      *roles,
  +                          const bson_t      *custom_data,
  +                          bson_error_t      *error)
  +{
  +   bson_error_t lerror;
  +   bson_t cmd;
  +   bson_t ar;
  +   char *input;
  +   char *hashed_password;
  +   bool ret = false;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (database);
  +   BSON_ASSERT (username);
  +
  +   /*
  +    * CDRIVER-232:
  +    *
  +    * Perform a (slow and tedious) round trip to mongod to determine if
  +    * we can safely call createUser. Otherwise, we will fallback and
  +    * perform legacy insertion into users collection.
  +    */
  +   bson_init (&cmd);
  +   BSON_APPEND_UTF8 (&cmd, "usersInfo", username);
  +   ret = mongoc_database_command_simple (database, &cmd, NULL, NULL, &lerror);
  +   bson_destroy (&cmd);
  +
  +   if (!ret && (lerror.code == MONGOC_ERROR_QUERY_COMMAND_NOT_FOUND)) {
  +      ret = mongoc_database_add_user_legacy (database, username, password, error);
  +   } else if (ret) {
  +      input = bson_strdup_printf ("%s:mongo:%s", username, password);
  +      hashed_password = _mongoc_hex_md5 (input);
  +      bson_free (input);
  +
  +      bson_init (&cmd);
  +      BSON_APPEND_UTF8 (&cmd, "createUser", username);
  +      BSON_APPEND_UTF8 (&cmd, "pwd", hashed_password);
  +      BSON_APPEND_BOOL (&cmd, "digestPassword", false);
  +      if (custom_data) {
  +         BSON_APPEND_DOCUMENT (&cmd, "customData", custom_data);
  +      }
  +      if (roles) {
  +         BSON_APPEND_ARRAY (&cmd, "roles", roles);
  +      } else {
  +         bson_append_array_begin (&cmd, "roles", 5, &ar);
  +         bson_append_array_end (&cmd, &ar);
  +      }
  +
  +      ret = mongoc_database_command_simple (database, &cmd, NULL, NULL, error);
  +
  +      if (!ret) fprintf (stderr, "%s\n", error->message);
  +
  +      bson_destroy (&cmd);
  +   } else if (error) {
  +      memcpy (error, &lerror, sizeof *error);
  +   }
  +
  +   RETURN (ret);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_database_get_read_prefs --
  + *
  + *       Fetch the read preferences for @database.
  + *
  + * Returns:
  + *       A mongoc_read_prefs_t that should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const mongoc_read_prefs_t *
  +mongoc_database_get_read_prefs (const mongoc_database_t *database) /* IN */
  +{
  +   bson_return_val_if_fail(database, NULL);
  +   return database->read_prefs;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_database_set_read_prefs --
  + *
  + *       Sets the default read preferences for @database.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_database_set_read_prefs (mongoc_database_t         *database,
  +                                const mongoc_read_prefs_t *read_prefs)
  +{
  +   bson_return_if_fail(database);
  +
  +   if (database->read_prefs) {
  +      mongoc_read_prefs_destroy(database->read_prefs);
  +      database->read_prefs = NULL;
  +   }
  +
  +   if (read_prefs) {
  +      database->read_prefs = mongoc_read_prefs_copy(read_prefs);
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_database_get_write_concern --
  + *
  + *       Fetches the write concern for @database.
  + *
  + * Returns:
  + *       A mongoc_write_concern_t that should not be modified or freed.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +const mongoc_write_concern_t *
  +mongoc_database_get_write_concern (const mongoc_database_t *database)
  +{
  +   bson_return_val_if_fail(database, NULL);
  +
  +   return database->write_concern;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_database_set_write_concern --
  + *
  + *       Set the default write concern for @database.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_database_set_write_concern (mongoc_database_t            *database,
  +                                   const mongoc_write_concern_t *write_concern)
  +{
  +   bson_return_if_fail(database);
  +
  +   if (database->write_concern) {
  +      mongoc_write_concern_destroy(database->write_concern);
  +      database->write_concern = NULL;
  +   }
  +
  +   if (write_concern) {
  +      database->write_concern = mongoc_write_concern_copy(write_concern);
  +   }
  +}
  +
  +
  +/**
  + * mongoc_database_has_collection:
  + * @database: (in): A #mongoc_database_t.
  + * @name: (in): The name of the collection to check for.
  + * @error: (out) (allow-none): A location for a #bson_error_t, or %NULL.
  + *
  + * Checks to see if a collection exists within the database on the MongoDB
  + * server.
  + *
  + * This will return %false if their was an error communicating with the
  + * server, or if the collection does not exist.
  + *
  + * If @error is provided, it will first be zeroed. Upon error, error.domain
  + * will be set.
  + *
  + * Returns: %true if @name exists, otherwise %false. @error may be set.
  + */
  +bool
  +mongoc_database_has_collection (mongoc_database_t *database,
  +                                const char        *name,
  +                                bson_error_t      *error)
  +{
  +   mongoc_collection_t *collection;
  +   mongoc_read_prefs_t *read_prefs;
  +   mongoc_cursor_t *cursor;
  +   const bson_t *doc;
  +   bson_iter_t iter;
  +   bool ret = false;
  +   const char *cur_name;
  +   bson_t q = BSON_INITIALIZER;
  +   char ns[140];
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (database);
  +   BSON_ASSERT (name);
  +
  +   if (error) {
  +      memset (error, 0, sizeof *error);
  +   }
  +
  +   bson_snprintf (ns, sizeof ns, "%s.%s", database->name, name);
  +
  +   read_prefs = mongoc_read_prefs_new (MONGOC_READ_PRIMARY);
  +   collection = mongoc_client_get_collection (database->client,
  +                                              database->name,
  +                                              "system.namespaces");
  +   cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 0, 0, &q,
  +                                    NULL, read_prefs);
  +
  +   while (!mongoc_cursor_error (cursor, error) &&
  +          mongoc_cursor_more (cursor)) {
  +      while (mongoc_cursor_next (cursor, &doc) &&
  +          bson_iter_init_find (&iter, doc, "name") &&
  +          BSON_ITER_HOLDS_UTF8 (&iter)) {
  +         cur_name = bson_iter_utf8(&iter, NULL);
  +         if (!strcmp(cur_name, ns)) {
  +            ret = true;
  +            GOTO(cleanup);
  +         }
  +      }
  +   }
  +
  +cleanup:
  +   mongoc_cursor_destroy (cursor);
  +   mongoc_collection_destroy (collection);
  +   mongoc_read_prefs_destroy (read_prefs);
  +
  +   RETURN(ret);
  +}
  +
  +
  +char **
  +mongoc_database_get_collection_names (mongoc_database_t *database,
  +                                      bson_error_t      *error)
  +{
  +   mongoc_collection_t *col;
  +   mongoc_cursor_t *cursor;
  +   uint32_t len;
  +   const bson_t *doc;
  +   bson_iter_t iter;
  +   const char *name;
  +   bson_t q = BSON_INITIALIZER;
  +   char **ret = NULL;
  +   int i = 0;
  +
  +   BSON_ASSERT (database);
  +
  +   col = mongoc_client_get_collection (database->client,
  +                                       database->name,
  +                                       "system.namespaces");
  +
  +   cursor = mongoc_collection_find (col, MONGOC_QUERY_NONE, 0, 0, 0, &q,
  +                                    NULL, NULL);
  +
  +   len = strlen (database->name) + 1;
  +
  +   while (mongoc_cursor_more (cursor) &&
  +          !mongoc_cursor_error (cursor, error)) {
  +      if (mongoc_cursor_next (cursor, &doc)) {
  +         if (bson_iter_init_find (&iter, doc, "name") &&
  +             BSON_ITER_HOLDS_UTF8 (&iter) &&
  +             (name = bson_iter_utf8 (&iter, NULL)) &&
  +             !strchr (name, '$') &&
  +             (0 == strncmp (name, database->name, len - 1))) {
  +            ret = bson_realloc (ret, sizeof(char*) * (i + 2));
  +            ret [i] = bson_strdup (bson_iter_utf8 (&iter, NULL) + len);
  +            ret [++i] = NULL;
  +         }
  +      }
  +   }
  +
  +   if (!ret && !mongoc_cursor_error (cursor, error)) {
  +      ret = bson_malloc0 (sizeof (void*));
  +   }
  +
  +   mongoc_cursor_destroy (cursor);
  +   mongoc_collection_destroy (col);
  +
  +   return ret;
  +}
  +
  +
  +mongoc_collection_t *
  +mongoc_database_create_collection (mongoc_database_t *database,
  +                                   const char        *name,
  +                                   const bson_t      *options,
  +                                   bson_error_t      *error)
  +{
  +   mongoc_collection_t *collection = NULL;
  +   bson_iter_t iter;
  +   bson_t cmd;
  +   bool capped = false;
  +
  +   bson_return_val_if_fail (database, NULL);
  +   bson_return_val_if_fail (name, NULL);
  +
  +   if (strchr (name, '$')) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_NAMESPACE,
  +                      MONGOC_ERROR_NAMESPACE_INVALID,
  +                      "The namespace \"%s\" is invalid.",
  +                      name);
  +      return NULL;
  +   }
  +
  +   if (options) {
  +      if (bson_iter_init_find (&iter, options, "capped")) {
  +         if (!BSON_ITER_HOLDS_BOOL (&iter)) {
  +            bson_set_error (error,
  +                            MONGOC_ERROR_COMMAND,
  +                            MONGOC_ERROR_COMMAND_INVALID_ARG,
  +                            "The argument \"capped\" must be a boolean.");
  +            return NULL;
  +         }
  +         capped = bson_iter_bool (&iter);
  +      }
  +
  +      if (bson_iter_init_find (&iter, options, "autoIndexId") &&
  +          !BSON_ITER_HOLDS_BOOL (&iter)) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_COMMAND,
  +                         MONGOC_ERROR_COMMAND_INVALID_ARG,
  +                         "The argument \"autoIndexId\" must be a boolean.");
  +         return NULL;
  +      }
  +
  +      if (bson_iter_init_find (&iter, options, "size")) {
  +         if (!BSON_ITER_HOLDS_INT32 (&iter) &&
  +             !BSON_ITER_HOLDS_INT64 (&iter)) {
  +            bson_set_error (error,
  +                            MONGOC_ERROR_COMMAND,
  +                            MONGOC_ERROR_COMMAND_INVALID_ARG,
  +                            "The argument \"size\" must be an integer.");
  +            return NULL;
  +         }
  +         if (!capped) {
  +            bson_set_error (error,
  +                            MONGOC_ERROR_COMMAND,
  +                            MONGOC_ERROR_COMMAND_INVALID_ARG,
  +                            "The \"size\" parameter requires {\"capped\": true}");
  +            return NULL;
  +         }
  +      }
  +
  +      if (bson_iter_init_find (&iter, options, "max")) {
  +         if (!BSON_ITER_HOLDS_INT32 (&iter) &&
  +             !BSON_ITER_HOLDS_INT64 (&iter)) {
  +            bson_set_error (error,
  +                            MONGOC_ERROR_COMMAND,
  +                            MONGOC_ERROR_COMMAND_INVALID_ARG,
  +                            "The argument \"max\" must be an integer.");
  +            return NULL;
  +         }
  +         if (!capped) {
  +            bson_set_error (error,
  +                            MONGOC_ERROR_COMMAND,
  +                            MONGOC_ERROR_COMMAND_INVALID_ARG,
  +                            "The \"size\" parameter requires {\"capped\": true}");
  +            return NULL;
  +         }
  +      }
  +   }
  +
  +   bson_init (&cmd);
  +   BSON_APPEND_UTF8 (&cmd, "create", name);
  +
  +   if (options) {
  +      if (!bson_iter_init (&iter, options)) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_COMMAND,
  +                         MONGOC_ERROR_COMMAND_INVALID_ARG,
  +                         "The argument \"options\" is corrupt or invalid.");
  +         bson_destroy (&cmd);
  +         return NULL;
  +      }
  +
  +      while (bson_iter_next (&iter)) {
  +         if (!bson_append_iter (&cmd, bson_iter_key (&iter), -1, &iter)) {
  +            bson_set_error (error,
  +                            MONGOC_ERROR_COMMAND,
  +                            MONGOC_ERROR_COMMAND_INVALID_ARG,
  +                            "Failed to append \"options\" to create command.");
  +            bson_destroy (&cmd);
  +            return NULL;
  +         }
  +      }
  +   }
  +
  +   if (mongoc_database_command_simple (database, &cmd, NULL, NULL, error)) {
  +      collection = _mongoc_collection_new (database->client,
  +                                           database->name,
  +                                           name,
  +                                           database->read_prefs,
  +                                           database->write_concern);
  +   }
  +
  +   bson_destroy (&cmd);
  +
  +   return collection;
  +}
  +
  +
  +mongoc_collection_t *
  +mongoc_database_get_collection (mongoc_database_t *database,
  +                                const char        *collection)
  +{
  +   bson_return_val_if_fail (database, NULL);
  +   bson_return_val_if_fail (collection, NULL);
  +
  +   return mongoc_client_get_collection (database->client, database->name,
  +                                        collection);
  +}
  +
  +
  +const char *
  +mongoc_database_get_name (mongoc_database_t *database)
  +{
  +   bson_return_val_if_fail (database, NULL);
  +
  +   return database->name;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-gridfs.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "gridfs"
  +
  +#define MONGOC_GRIDFS_STREAM_CHUNK 4096
  +
  +
  +/**
  + * _mongoc_gridfs_ensure_index:
  + *
  + * ensure gridfs indexes
  + *
  + * Ensure fast searches for chunks via [ files_id, n ]
  + * Ensure fast searches for files via [ filename ]
  + */
  +static bool
  +_mongoc_gridfs_ensure_index (mongoc_gridfs_t *gridfs,
  +                             bson_error_t    *error)
  +{
  +   bson_t keys;
  +   mongoc_index_opt_t opt;
  +   bool r;
  +
  +   ENTRY;
  +
  +   bson_init (&keys);
  +
  +   bson_append_int32 (&keys, "files_id", -1, 1);
  +   bson_append_int32 (&keys, "n", -1, 1);
  +
  +   mongoc_index_opt_init (&opt);
  +   opt.unique = 1;
  +
  +   r = mongoc_collection_ensure_index (gridfs->chunks, &keys, &opt, error);
  +
  +   bson_destroy (&keys);
  +
  +   if (!r) { RETURN (r); }
  +
  +   bson_init (&keys);
  +
  +   bson_append_int32 (&keys, "filename", -1, 1);
  +   opt.unique = 0;
  +
  +   r = mongoc_collection_ensure_index (gridfs->chunks, &keys, &opt, error);
  +
  +   bson_destroy (&keys);
  +
  +   if (!r) { RETURN (r); }
  +
  +   RETURN (1);
  +}
  +
  +
  +mongoc_gridfs_t *
  +_mongoc_gridfs_new (mongoc_client_t *client,
  +                    const char      *db,
  +                    const char      *prefix,
  +                    bson_error_t    *error)
  +{
  +   mongoc_gridfs_t *gridfs;
  +   char buf[128];
  +   bool r;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (client);
  +   BSON_ASSERT (db);
  +
  +   if (!prefix) {
  +      prefix = "fs";
  +   }
  +
  +   /* make sure prefix is short enough to bucket the chunks and files
  +    * collections
  +    */
  +#ifndef BSON_DISABLE_ASSERT
  +   {
  +      uint32_t prefix_len;
  +      prefix_len = (uint32_t)strlen (prefix);
  +      BSON_ASSERT (prefix_len + sizeof (".chunks") < sizeof (buf));
  +   }
  +#endif
  +
  +   gridfs = bson_malloc0 (sizeof *gridfs);
  +
  +   gridfs->client = client;
  +
  +   bson_snprintf (buf, sizeof(buf), "%s.chunks", prefix);
  +   gridfs->chunks = _mongoc_collection_new (client, db, buf, NULL, NULL);
  +
  +   bson_snprintf (buf, sizeof(buf), "%s.files", prefix);
  +   gridfs->files = _mongoc_collection_new (client, db, buf, NULL, NULL);
  +
  +   r = _mongoc_gridfs_ensure_index (gridfs, error);
  +
  +   if (!r) { return NULL; }
  +
  +   RETURN (gridfs);
  +}
  +
  +
  +bool
  +mongoc_gridfs_drop (mongoc_gridfs_t *gridfs,
  +                    bson_error_t    *error)
  +{
  +   bool r;
  +
  +   ENTRY;
  +
  +   r = mongoc_collection_drop (gridfs->files, error);
  +   if (!r) {
  +      RETURN (0);
  +   }
  +
  +   r = mongoc_collection_drop (gridfs->chunks, error);
  +   if (!r) {
  +      RETURN (0);
  +   }
  +
  +   RETURN (1);
  +}
  +
  +
  +void
  +mongoc_gridfs_destroy (mongoc_gridfs_t *gridfs)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT (gridfs);
  +
  +   mongoc_collection_destroy (gridfs->files);
  +   mongoc_collection_destroy (gridfs->chunks);
  +
  +   bson_free (gridfs);
  +
  +   EXIT;
  +}
  +
  +
  +/** find all matching gridfs files */
  +mongoc_gridfs_file_list_t *
  +mongoc_gridfs_find (mongoc_gridfs_t *gridfs,
  +                    const bson_t    *query)
  +{
  +   return _mongoc_gridfs_file_list_new (gridfs, query, 0);
  +}
  +
  +
  +/** find a single gridfs file */
  +mongoc_gridfs_file_t *
  +mongoc_gridfs_find_one (mongoc_gridfs_t *gridfs,
  +                        const bson_t    *query,
  +                        bson_error_t    *error)
  +{
  +   mongoc_gridfs_file_list_t *list;
  +   mongoc_gridfs_file_t *file;
  +
  +   ENTRY;
  +
  +   list = _mongoc_gridfs_file_list_new (gridfs, query, 1);
  +
  +   file = mongoc_gridfs_file_list_next (list);
  +   mongoc_gridfs_file_list_error(list, error);
  +
  +   mongoc_gridfs_file_list_destroy (list);
  +
  +   RETURN (file);
  +}
  +
  +
  +/** find a single gridfs file by filename */
  +mongoc_gridfs_file_t *
  +mongoc_gridfs_find_one_by_filename (mongoc_gridfs_t *gridfs,
  +                                    const char      *filename,
  +                                    bson_error_t    *error)
  +{
  +   mongoc_gridfs_file_t *file;
  +
  +   bson_t query;
  +
  +   bson_init (&query);
  +
  +   bson_append_utf8 (&query, "filename", -1, filename, -1);
  +
  +   file = mongoc_gridfs_find_one (gridfs, &query, error);
  +
  +   bson_destroy (&query);
  +
  +   return file;
  +}
  +
  +
  +/** create a gridfs file from a stream
  + *
  + * The stream is fully consumed in creating the file
  + */
  +mongoc_gridfs_file_t *
  +mongoc_gridfs_create_file_from_stream (mongoc_gridfs_t          *gridfs,
  +                                       mongoc_stream_t          *stream,
  +                                       mongoc_gridfs_file_opt_t *opt)
  +{
  +   mongoc_gridfs_file_t *file;
  +   ssize_t r;
  +   uint8_t buf[MONGOC_GRIDFS_STREAM_CHUNK];
  +   mongoc_iovec_t iov;
  +   int timeout;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (gridfs);
  +   BSON_ASSERT (stream);
  +
  +   iov.iov_base = (void *)buf;
  +   iov.iov_len = 0;
  +
  +   file = _mongoc_gridfs_file_new (gridfs, opt);
  +   timeout = gridfs->client->cluster.sockettimeoutms;
  +
  +   for (;; ) {
  +      r = mongoc_stream_read (stream, iov.iov_base, MONGOC_GRIDFS_STREAM_CHUNK,
  +                              0, timeout);
  +
  +      if (r > 0) {
  +         iov.iov_len = r;
  +         mongoc_gridfs_file_writev (file, &iov, 1, timeout);
  +      } else if (r == 0) {
  +         break;
  +      } else {
  +         mongoc_gridfs_file_destroy (file);
  +         RETURN (NULL);
  +      }
  +   }
  +
  +   mongoc_stream_destroy (stream);
  +
  +   mongoc_gridfs_file_seek (file, 0, SEEK_SET);
  +
  +   RETURN (file);
  +}
  +
  +
  +/** create an empty gridfs file */
  +mongoc_gridfs_file_t *
  +mongoc_gridfs_create_file (mongoc_gridfs_t          *gridfs,
  +                           mongoc_gridfs_file_opt_t *opt)
  +{
  +   mongoc_gridfs_file_t *file;
  +
  +   ENTRY;
  +
  +   bson_return_val_if_fail (gridfs, NULL);
  +
  +   file = _mongoc_gridfs_file_new (gridfs, opt);
  +
  +   RETURN (file);
  +}
  +
  +/** accessor functions for collections */
  +mongoc_collection_t *
  +mongoc_gridfs_get_files (mongoc_gridfs_t *gridfs)
  +{
  +   bson_return_val_if_fail (gridfs, NULL);
  +
  +   return gridfs->files;
  +}
  +
  +mongoc_collection_t *
  +mongoc_gridfs_get_chunks (mongoc_gridfs_t *gridfs)
  +{
  +   bson_return_val_if_fail (gridfs, NULL);
  +
  +   return gridfs->chunks;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-gridfs-file.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "gridfs_file"
  +
  +static bool
  +_mongoc_gridfs_file_refresh_page (mongoc_gridfs_file_t *file);
  +
  +static bool
  +_mongoc_gridfs_file_flush_page (mongoc_gridfs_file_t *file);
  +
  +
  +/*****************************************************************
  +* Magic accessor generation
  +*
  +* We need some accessors to get and set properties on files, to handle memory
  +* ownership and to determine dirtiness.  These macros produce the getters and
  +* setters we need
  +*****************************************************************/
  +
  +#define MONGOC_GRIDFS_FILE_STR_ACCESSOR(name) \
  +   const char * \
  +   mongoc_gridfs_file_get_##name (mongoc_gridfs_file_t * file) \
  +   { \
  +      return file->name ? file->name : file->bson_##name; \
  +   } \
  +   void \
  +      mongoc_gridfs_file_set_##name (mongoc_gridfs_file_t * file, \
  +                                     const char           *str)  \
  +   { \
  +      if (file->name) { \
  +         bson_free (file->name); \
  +      } \
  +      file->name = bson_strdup (str); \
  +      file->is_dirty = 1; \
  +   }
  +
  +#define MONGOC_GRIDFS_FILE_BSON_ACCESSOR(name) \
  +   const bson_t * \
  +   mongoc_gridfs_file_get_##name (mongoc_gridfs_file_t * file) \
  +   { \
  +      if (file->name.len) { \
  +         return &file->name; \
  +      } else if (file->bson_##name.len) { \
  +         return &file->bson_##name; \
  +      } else { \
  +         return NULL; \
  +      } \
  +   } \
  +   void \
  +      mongoc_gridfs_file_set_##name (mongoc_gridfs_file_t * file, \
  +                                     const bson_t * bson) \
  +   { \
  +      if (file->name.len) { \
  +         bson_destroy (&file->name); \
  +      } \
  +      bson_copy_to (bson, &(file->name)); \
  +      file->is_dirty = 1; \
  +   }
  +
  +MONGOC_GRIDFS_FILE_STR_ACCESSOR (md5);
  +MONGOC_GRIDFS_FILE_STR_ACCESSOR (filename);
  +MONGOC_GRIDFS_FILE_STR_ACCESSOR (content_type);
  +MONGOC_GRIDFS_FILE_BSON_ACCESSOR (aliases);
  +MONGOC_GRIDFS_FILE_BSON_ACCESSOR (metadata);
  +
  +
  +/** save a gridfs file */
  +bool
  +mongoc_gridfs_file_save (mongoc_gridfs_file_t *file)
  +{
  +   bson_t *selector, *update, child;
  +   const char *md5;
  +   const char *filename;
  +   const char *content_type;
  +   const bson_t *aliases;
  +   const bson_t *metadata;
  +   bool r;
  +
  +   ENTRY;
  +
  +   if (!file->is_dirty) {
  +      return 1;
  +   }
  +
  +   if (file->page && _mongoc_gridfs_file_page_is_dirty (file->page)) {
  +      _mongoc_gridfs_file_flush_page (file);
  +   }
  +
  +   md5 = mongoc_gridfs_file_get_md5 (file);
  +   filename = mongoc_gridfs_file_get_filename (file);
  +   content_type = mongoc_gridfs_file_get_content_type (file);
  +   aliases = mongoc_gridfs_file_get_aliases (file);
  +   metadata = mongoc_gridfs_file_get_metadata (file);
  +
  +   selector = bson_new ();
  +   bson_append_oid (selector, "_id", -1, &file->files_id);
  +
  +   update = bson_new ();
  +   bson_append_document_begin (update, "$set", -1, &child);
  +   bson_append_int64 (&child, "length", -1, file->length);
  +   bson_append_int32 (&child, "chunkSize", -1, file->chunk_size);
  +   bson_append_date_time (&child, "uploadDate", -1, file->upload_date);
  +
  +   if (md5) {
  +      bson_append_utf8 (&child, "md5", -1, md5, -1);
  +   }
  +
  +   if (filename) {
  +      bson_append_utf8 (&child, "filename", -1, filename, -1);
  +   }
  +
  +   if (content_type) {
  +      bson_append_utf8 (&child, "contentType", -1, content_type, -1);
  +   }
  +
  +   if (aliases) {
  +      bson_append_array (&child, "aliases", -1, aliases);
  +   }
  +
  +   if (metadata) {
  +      bson_append_document (&child, "metadata", -1, metadata);
  +   }
  +
  +   bson_append_document_end (update, &child);
  +
  +   r = mongoc_collection_update (file->gridfs->files, MONGOC_UPDATE_UPSERT,
  +                                 selector, update, NULL, &file->error);
  +
  +   file->failed = !r;
  +
  +
  +   bson_destroy (selector);
  +   bson_destroy (update);
  +
  +   file->is_dirty = 0;
  +
  +   RETURN (r);
  +}
  +
  +
  +/**
  + * _mongoc_gridfs_file_new_from_bson:
  + *
  + * creates a gridfs file from a bson object
  + *
  + * This is only really useful for instantiating a gridfs file from a server
  + * side object
  + */
  +mongoc_gridfs_file_t *
  +_mongoc_gridfs_file_new_from_bson (mongoc_gridfs_t *gridfs,
  +                                   const bson_t    *data)
  +{
  +   mongoc_gridfs_file_t *file;
  +   const char *key;
  +   bson_iter_t iter;
  +   const uint8_t *buf;
  +   uint32_t buf_len;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (gridfs);
  +   BSON_ASSERT (data);
  +
  +   file = bson_malloc0 (sizeof *file);
  +
  +   file->gridfs = gridfs;
  +   bson_copy_to (data, &file->bson);
  +
  +   bson_iter_init (&iter, &file->bson);
  +
  +   while (bson_iter_next (&iter)) {
  +      key = bson_iter_key (&iter);
  +
  +      if (0 == strcmp (key, "_id")) {
  +         bson_oid_copy (bson_iter_oid (&iter), &file->files_id);
  +      } else if (0 == strcmp (key, "length")) {
  +         file->length = bson_iter_as_int64 (&iter);
  +      } else if (0 == strcmp (key, "chunkSize")) {
  +         file->chunk_size = bson_iter_int32 (&iter);
  +      } else if (0 == strcmp (key, "uploadDate")) {
  +         file->upload_date = bson_iter_date_time (&iter);
  +      } else if (0 == strcmp (key, "md5")) {
  +         file->bson_md5 = bson_iter_utf8 (&iter, NULL);
  +      } else if (0 == strcmp (key, "filename")) {
  +         file->bson_filename = bson_iter_utf8 (&iter, NULL);
  +      } else if (0 == strcmp (key, "contentType")) {
  +         file->bson_content_type = bson_iter_utf8 (&iter, NULL);
  +      } else if (0 == strcmp (key, "aliases")) {
  +         bson_iter_array (&iter, &buf_len, &buf);
  +         bson_init_static (&file->bson_aliases, buf, buf_len);
  +      } else if (0 == strcmp (key, "metadata")) {
  +         bson_iter_document (&iter, &buf_len, &buf);
  +         bson_init_static (&file->bson_metadata, buf, buf_len);
  +      }
  +   }
  +
  +   /* TODO: is there are a minimal object we should be verifying that we
  +    * actually have here? */
  +
  +   RETURN (file);
  +}
  +
  +
  +/**
  + * _mongoc_gridfs_file_new:
  + *
  + * Create a new empty gridfs file
  + */
  +mongoc_gridfs_file_t *
  +_mongoc_gridfs_file_new (mongoc_gridfs_t          *gridfs,
  +                         mongoc_gridfs_file_opt_t *opt)
  +{
  +   mongoc_gridfs_file_t *file;
  +   mongoc_gridfs_file_opt_t default_opt = { 0 };
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (gridfs);
  +
  +   if (!opt) {
  +      opt = &default_opt;
  +   }
  +
  +   file = bson_malloc0 (sizeof *file);
  +
  +   file->gridfs = gridfs;
  +   file->is_dirty = 1;
  +
  +   if (opt->chunk_size) {
  +      file->chunk_size = opt->chunk_size;
  +   } else {
  +      /** default chunk size is 256k */
  +      file->chunk_size = 2 << 17;
  +   }
  +
  +   bson_oid_init (&file->files_id, NULL);
  +
  +   file->upload_date = time (NULL) * 1000;
  +
  +   if (opt->md5) {
  +      file->md5 = bson_strdup (opt->md5);
  +   }
  +
  +   if (opt->filename) {
  +      file->filename = bson_strdup (opt->filename);
  +   }
  +
  +   if (opt->content_type) {
  +      file->content_type = bson_strdup (opt->content_type);
  +   }
  +
  +   if (opt->aliases) {
  +      bson_copy_to (opt->aliases, &(file->aliases));
  +   }
  +
  +   if (opt->metadata) {
  +      bson_copy_to (opt->metadata, &(file->metadata));
  +   }
  +
  +   RETURN (file);
  +}
  +
  +void
  +mongoc_gridfs_file_destroy (mongoc_gridfs_file_t *file)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT (file);
  +
  +   if (file->page) {
  +      _mongoc_gridfs_file_page_destroy (file->page);
  +   }
  +
  +   if (file->bson.len) {
  +      bson_destroy (&file->bson);
  +   }
  +
  +   if (file->cursor) {
  +      mongoc_cursor_destroy (file->cursor);
  +   }
  +
  +   if (file->md5) {
  +      bson_free (file->md5);
  +   }
  +
  +   if (file->filename) {
  +      bson_free (file->filename);
  +   }
  +
  +   if (file->content_type) {
  +      bson_free (file->content_type);
  +   }
  +
  +   if (file->aliases.len) {
  +      bson_destroy (&file->aliases);
  +   }
  +
  +   if (file->bson_aliases.len) {
  +      bson_destroy (&file->bson_aliases);
  +   }
  +
  +   if (file->metadata.len) {
  +      bson_destroy (&file->metadata);
  +   }
  +
  +   if (file->bson_metadata.len) {
  +      bson_destroy (&file->bson_metadata);
  +   }
  +
  +   bson_free (file);
  +
  +   EXIT;
  +}
  +
  +
  +/** readv against a gridfs file */
  +ssize_t
  +mongoc_gridfs_file_readv (mongoc_gridfs_file_t *file,
  +                          mongoc_iovec_t       *iov,
  +                          size_t                iovcnt,
  +                          size_t                min_bytes,
  +                          uint32_t              timeout_msec)
  +{
  +   uint32_t bytes_read = 0;
  +   int32_t r;
  +   size_t i;
  +   uint32_t iov_pos;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (file);
  +   BSON_ASSERT (iov);
  +   BSON_ASSERT (iovcnt);
  +   BSON_ASSERT (timeout_msec <= INT_MAX);
  +
  +   /* TODO: we should probably do something about timeout_msec here */
  +
  +   if (!file->page) {
  +      _mongoc_gridfs_file_refresh_page (file);
  +   }
  +
  +   for (i = 0; i < iovcnt; i++) {
  +      iov_pos = 0;
  +
  +      for (;; ) {
  +         r = _mongoc_gridfs_file_page_read (file->page,
  +                                           (uint8_t *)iov[i].iov_base + iov_pos,
  +                                           (uint32_t)(iov[i].iov_len - iov_pos));
  +         BSON_ASSERT (r >= 0);
  +
  +         iov_pos += r;
  +         file->pos += r;
  +         bytes_read += r;
  +
  +         if (iov_pos == iov[i].iov_len) {
  +            /* filled a bucket, keep going */
  +            break;
  +         } else if (file->length == file->pos) {
  +            /* we're at the end of the file.  So we're done */
  +            RETURN (bytes_read);
  +         } else if (bytes_read >= min_bytes) {
  +            /* we need a new page, but we've read enough bytes to stop */
  +            RETURN (bytes_read);
  +         } else {
  +            /* more to read, just on a new page */
  +            _mongoc_gridfs_file_refresh_page (file);
  +         }
  +      }
  +   }
  +
  +   RETURN (bytes_read);
  +}
  +
  +
  +/** writev against a gridfs file */
  +ssize_t
  +mongoc_gridfs_file_writev (mongoc_gridfs_file_t *file,
  +                           mongoc_iovec_t       *iov,
  +                           size_t                iovcnt,
  +                           uint32_t              timeout_msec)
  +{
  +   uint32_t bytes_written = 0;
  +   int32_t r;
  +   size_t i;
  +   uint32_t iov_pos;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (file);
  +   BSON_ASSERT (iov);
  +   BSON_ASSERT (iovcnt);
  +   BSON_ASSERT (timeout_msec <= INT_MAX);
  +
  +   /* TODO: we should probably do something about timeout_msec here */
  +
  +   for (i = 0; i < iovcnt; i++) {
  +      iov_pos = 0;
  +
  +      for (;; ) {
  +         if (!file->page) {
  +            _mongoc_gridfs_file_refresh_page (file);
  +         }
  +
  +         r = _mongoc_gridfs_file_page_write (file->page,
  +                                            (uint8_t *)iov[i].iov_base + iov_pos,
  +                                            (uint32_t)(iov[i].iov_len - iov_pos));
  +         BSON_ASSERT (r >= 0);
  +
  +         iov_pos += r;
  +         file->pos += r;
  +         bytes_written += r;
  +
  +         file->length = MAX (file->length, (int64_t)file->pos);
  +
  +         if (iov_pos == iov[i].iov_len) {
  +            /** filled a bucket, keep going */
  +            break;
  +         } else {
  +            /** flush the buffer, the next pass through will bring in a new page
  +             *
  +             * Our file pointer is now on the new page, so push it back one so
  +             * that flush knows to flush the old page rather than a new one.
  +             * This is a little hacky
  +             */
  +            file->pos--;
  +            _mongoc_gridfs_file_flush_page (file);
  +            file->pos++;
  +         }
  +      }
  +   }
  +
  +   file->is_dirty = 1;
  +
  +   RETURN (bytes_written);
  +}
  +
  +
  +/** flush a gridfs file's current page to the db */
  +static bool
  +_mongoc_gridfs_file_flush_page (mongoc_gridfs_file_t *file)
  +{
  +   bson_t *selector, *update;
  +   bool r;
  +   const uint8_t *buf;
  +   uint32_t len;
  +
  +   ENTRY;
  +   BSON_ASSERT (file);
  +   BSON_ASSERT (file->page);
  +
  +   buf = _mongoc_gridfs_file_page_get_data (file->page);
  +   len = _mongoc_gridfs_file_page_get_len (file->page);
  +
  +   selector = bson_new ();
  +
  +   bson_append_oid (selector, "files_id", -1, &(file->files_id));
  +   bson_append_int32 (selector, "n", -1, (int32_t)(file->pos / file->chunk_size));
  +
  +   update = bson_sized_new (file->chunk_size + 100);
  +
  +   bson_append_oid (update, "files_id", -1, &(file->files_id));
  +   bson_append_int32 (update, "n", -1, (int32_t)(file->pos / file->chunk_size));
  +   bson_append_binary (update, "data", -1, BSON_SUBTYPE_BINARY, buf, len);
  +
  +   r = mongoc_collection_update (file->gridfs->chunks, MONGOC_UPDATE_UPSERT,
  +                                 selector, update, NULL, &file->error);
  +
  +   file->failed = !r;
  +
  +   bson_destroy (selector);
  +   bson_destroy (update);
  +
  +   if (r) {
  +      _mongoc_gridfs_file_page_destroy (file->page);
  +      file->page = NULL;
  +      r = mongoc_gridfs_file_save (file);
  +   }
  +
  +   RETURN (r);
  +}
  +
  +
  +/** referesh a gridfs file's underlying page
  + *
  + * This unconditionally fetches the current page, even if the current page
  + * covers the same theoretical chunk.
  + */
  +static bool
  +_mongoc_gridfs_file_refresh_page (mongoc_gridfs_file_t *file)
  +{
  +   bson_t *query, *fields, child, child2;
  +   const bson_t *chunk;
  +   const char *key;
  +   bson_iter_t iter;
  +
  +   uint32_t n;
  +   const uint8_t *data;
  +   uint32_t len;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (file);
  +
  +   n = (uint32_t)(file->pos / file->chunk_size);
  +
  +   if (file->page) {
  +      _mongoc_gridfs_file_page_destroy (file->page);
  +      file->page = NULL;
  +   }
  +
  +   /* if the file pointer is past the end of the current file (I.e. pointing to
  +    * a new chunk) and we're on a chunk boundary, we'll pass the page
  +    * constructor a new empty page */
  +   if ((int64_t)file->pos >= file->length && !(file->pos % file->chunk_size)) {
  +      data = (uint8_t *)"";
  +      len = 0;
  +   } else {
  +      /* if we have a cursor, but the cursor doesn't have the chunk we're going
  +       * to need, destroy it (we'll grab a new one immediately there after) */
  +      if (file->cursor &&
  +          !(file->cursor_range[0] >= n && file->cursor_range[1] <= n)) {
  +         mongoc_cursor_destroy (file->cursor);
  +         file->cursor = NULL;
  +      }
  +
  +      if (!file->cursor) {
  +         query = bson_new ();
  +
  +         bson_append_document_begin(query, "$query", -1, &child);
  +            bson_append_oid (&child, "files_id", -1, &file->files_id);
  +
  +            bson_append_document_begin (&child, "n", -1, &child2);
  +               bson_append_int32 (&child2, "$gte", -1, (int32_t)(file->pos / \
file->chunk_size));  +            bson_append_document_end (&child, &child2);
  +         bson_append_document_end(query, &child);
  +
  +         bson_append_document_begin(query, "$orderby", -1, &child);
  +            bson_append_int32 (&child, "n", -1, 1);
  +         bson_append_document_end(query, &child);
  +
  +         fields = bson_new ();
  +         bson_append_int32 (fields, "n", -1, 1);
  +         bson_append_int32 (fields, "data", -1, 1);
  +         bson_append_int32 (fields, "_id", -1, 0);
  +
  +         /* find all chunks greater than or equal to our current file pos */
  +         file->cursor = mongoc_collection_find (file->gridfs->chunks,
  +                                                MONGOC_QUERY_NONE, 0, 0, 0, query,
  +                                                fields, NULL);
  +
  +         file->cursor_range[0] = n;
  +         file->cursor_range[1] = (uint32_t)(file->length / file->chunk_size);
  +
  +         bson_destroy (query);
  +         bson_destroy (fields);
  +
  +         BSON_ASSERT (file->cursor);
  +      }
  +
  +      /* we might have had a cursor before, then seeked ahead past a chunk.
  +       * iterate until we're on the right chunk */
  +      while (file->cursor_range[0] <= n) {
  +         if (!mongoc_cursor_next (file->cursor, &chunk)) {
  +            if (file->cursor->failed) {
  +               memcpy (&(file->error), &(file->cursor->error),
  +                       sizeof (bson_error_t));
  +               file->failed = true;
  +            }
  +
  +            RETURN (0);
  +         }
  +
  +         file->cursor_range[0]++;
  +      }
  +
  +      bson_iter_init (&iter, chunk);
  +
  +      /* grab out what we need from the chunk */
  +      while (bson_iter_next (&iter)) {
  +         key = bson_iter_key (&iter);
  +
  +         if (strcmp (key, "n") == 0) {
  +            n = bson_iter_int32 (&iter);
  +         } else if (strcmp (key, "data") == 0) {
  +            bson_iter_binary (&iter, NULL, &len, &data);
  +         } else {
  +            RETURN (0);
  +         }
  +      }
  +
  +      /* we're on the wrong chunk somehow... probably because our gridfs is
  +       * missing chunks.
  +       *
  +       * TODO: maybe we should make more noise here?
  +       */
  +
  +      if (!(n == file->pos / file->chunk_size)) {
  +         return 0;
  +      }
  +   }
  +
  +   file->page = _mongoc_gridfs_file_page_new (data, len, file->chunk_size);
  +
  +   /* seek in the page towards wherever we're supposed to be */
  +   RETURN (_mongoc_gridfs_file_page_seek (file->page, file->pos %
  +                                         file->chunk_size));
  +}
  +
  +
  +/** Seek in a gridfs file to a given location
  + *
  + * @param whence is regular fseek whence.  I.e. SEEK_SET, SEEK_CUR or SEEK_END
  + *
  + */
  +int
  +mongoc_gridfs_file_seek (mongoc_gridfs_file_t *file,
  +                         uint64_t         delta,
  +                         int                   whence)
  +{
  +   uint64_t offset;
  +
  +   BSON_ASSERT(file);
  +
  +   switch (whence) {
  +   case SEEK_SET:
  +      offset = delta;
  +      break;
  +   case SEEK_CUR:
  +      offset = file->pos + delta;
  +      break;
  +   case SEEK_END:
  +      offset = (file->length - 1) + delta;
  +      break;
  +   default:
  +      errno = EINVAL;
  +      return -1;
  +
  +      break;
  +   }
  +
  +   BSON_ASSERT (file->length > (int64_t)offset);
  +
  +   if (offset % file->chunk_size != file->pos % file->chunk_size) {
  +      /** no longer on the same page */
  +
  +      if (file->page) {
  +         if (_mongoc_gridfs_file_page_is_dirty (file->page)) {
  +            _mongoc_gridfs_file_flush_page (file);
  +         } else {
  +            _mongoc_gridfs_file_page_destroy (file->page);
  +         }
  +      }
  +
  +      /** we'll pick up the seek when we fetch a page on the next action.  We \
lazily load */  +   } else {
  +      _mongoc_gridfs_file_page_seek (file->page, offset % file->chunk_size);
  +   }
  +
  +   file->pos = offset;
  +
  +   return 0;
  +}
  +
  +uint64_t
  +mongoc_gridfs_file_tell (mongoc_gridfs_file_t *file)
  +{
  +   BSON_ASSERT(file);
  +
  +   return file->pos;
  +}
  +
  +bool
  +mongoc_gridfs_file_error (mongoc_gridfs_file_t *file,
  +                          bson_error_t         *error)
  +{
  +   BSON_ASSERT(file);
  +   BSON_ASSERT(error);
  +
  +   if (BSON_UNLIKELY(file->failed)) {
  +      bson_set_error(error,
  +                     file->error.domain,
  +                     file->error.code,
  +                     "%s",
  +                     file->error.message);
  +      RETURN(true);
  +   }
  +
  +   RETURN(false);
  +}
  +
  +int64_t
  +mongoc_gridfs_file_get_length (mongoc_gridfs_file_t *file)
  +{
  +   return file->length;
  +}
  +
  +int32_t
  +mongoc_gridfs_file_get_chunk_size (mongoc_gridfs_file_t *file)
  +{
  +   return file->chunk_size;
  +}
  +
  +int64_t
  +mongoc_gridfs_file_get_upload_date (mongoc_gridfs_file_t *file)
  +{
  +   return file->upload_date;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-gridfs-file-list.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "gridfs_file_list"
  +
  +
  +mongoc_gridfs_file_list_t *
  +_mongoc_gridfs_file_list_new (mongoc_gridfs_t *gridfs,
  +                              const bson_t    *query,
  +                              uint32_t    limit)
  +{
  +   mongoc_gridfs_file_list_t *list;
  +   mongoc_cursor_t *cursor;
  +
  +   cursor = mongoc_collection_find (gridfs->files, MONGOC_QUERY_NONE, 0, limit, 0,
  +                                    query, NULL, NULL);
  +
  +   BSON_ASSERT (cursor);
  +
  +   list = bson_malloc0 (sizeof *list);
  +
  +   list->cursor = cursor;
  +   list->gridfs = gridfs;
  +
  +   return list;
  +}
  +
  +
  +mongoc_gridfs_file_t *
  +mongoc_gridfs_file_list_next (mongoc_gridfs_file_list_t *list)
  +{
  +   const bson_t *bson;
  +
  +   BSON_ASSERT (list);
  +
  +   if (mongoc_cursor_next (list->cursor, &bson)) {
  +      return _mongoc_gridfs_file_new_from_bson (list->gridfs, bson);
  +   } else {
  +      return NULL;
  +   }
  +}
  +
  +
  +bool
  +mongoc_gridfs_file_list_error (mongoc_gridfs_file_list_t *list,
  +                               bson_error_t              *error)
  +{
  +   return mongoc_cursor_error(list->cursor, error);
  +}
  +
  +
  +void
  +mongoc_gridfs_file_list_destroy (mongoc_gridfs_file_list_t *list)
  +{
  +   BSON_ASSERT (list);
  +
  +   mongoc_cursor_destroy (list->cursor);
  +   bson_free (list);
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-gridfs-file-page.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "gridfs_file_page"
  +
  +/** create a new page from a buffer
  + *
  + * The buffer should stick around for the life of the page
  + */
  +mongoc_gridfs_file_page_t *
  +_mongoc_gridfs_file_page_new (const uint8_t *data,
  +                              uint32_t       len,
  +                              uint32_t       chunk_size)
  +{
  +   mongoc_gridfs_file_page_t *page;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (data);
  +   BSON_ASSERT (len <= chunk_size);
  +
  +   page = bson_malloc0 (sizeof *page);
  +
  +   page->chunk_size = chunk_size;
  +   page->read_buf = data;
  +   page->len = len;
  +
  +   RETURN (page);
  +}
  +
  +
  +bool
  +_mongoc_gridfs_file_page_seek (mongoc_gridfs_file_page_t *page,
  +                               uint32_t              offset)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT (page);
  +
  +   BSON_ASSERT (offset <= page->len);
  +
  +   page->offset = offset;
  +
  +   RETURN (1);
  +}
  +
  +
  +int32_t
  +_mongoc_gridfs_file_page_read (mongoc_gridfs_file_page_t *page,
  +                               void                      *dst,
  +                               uint32_t              len)
  +{
  +   int bytes_read;
  +   const uint8_t *src;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (page);
  +   BSON_ASSERT (dst);
  +
  +   bytes_read = MIN (len, page->len - page->offset);
  +
  +   src = page->read_buf ? page->read_buf : page->buf;
  +
  +   memcpy (dst, src + page->offset, bytes_read);
  +
  +   page->offset += bytes_read;
  +
  +   RETURN (bytes_read);
  +}
  +
  +
  +/**
  + * _mongoc_gridfs_file_page_write:
  + *
  + * writes to a page
  + *
  + * writes are copy on write as regards the buf passed during construction.
  + * I.e. the first write allocs a buf large enough for the chunk_size, which
  + * because authoritative from then on out
  + */
  +int32_t
  +_mongoc_gridfs_file_page_write (mongoc_gridfs_file_page_t *page,
  +                                const void                *src,
  +                                uint32_t              len)
  +{
  +   int bytes_written;
  +
  +   ENTRY;
  +
  +   BSON_ASSERT (page);
  +   BSON_ASSERT (src);
  +
  +   bytes_written = MIN (len, page->chunk_size - page->offset);
  +
  +   if (!page->buf) {
  +      page->buf = bson_malloc (page->chunk_size);
  +      memcpy (page->buf, page->read_buf, MIN (page->chunk_size, page->len));
  +   }
  +
  +   memcpy (page->buf + page->offset, src, bytes_written);
  +   page->offset += bytes_written;
  +
  +   page->len = MAX (page->offset, page->len);
  +
  +   RETURN (bytes_written);
  +}
  +
  +
  +const uint8_t *
  +_mongoc_gridfs_file_page_get_data (mongoc_gridfs_file_page_t *page)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT (page);
  +
  +   RETURN (page->buf ? page->buf : page->read_buf);
  +}
  +
  +
  +uint32_t
  +_mongoc_gridfs_file_page_get_len (mongoc_gridfs_file_page_t *page)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT (page);
  +
  +   RETURN (page->len);
  +}
  +
  +
  +uint32_t
  +_mongoc_gridfs_file_page_tell (mongoc_gridfs_file_page_t *page)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT (page);
  +
  +   RETURN (page->offset);
  +}
  +
  +
  +bool
  +_mongoc_gridfs_file_page_is_dirty (mongoc_gridfs_file_page_t *page)
  +{
  +   ENTRY;
  +
  +   BSON_ASSERT (page);
  +
  +   RETURN (page->buf ? 1 : 0);
  +}
  +
  +
  +void
  +_mongoc_gridfs_file_page_destroy (mongoc_gridfs_file_page_t *page)
  +{
  +   ENTRY;
  +
  +   if (page->buf) { bson_free (page->buf); }
  +
  +   bson_free (page);
  +
  +   EXIT;
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-index.c */
  +
  +#undef MONGOC_LOG_DOMAIN
  +#define MONGOC_LOG_DOMAIN "gridfs_index"
  +
  +
  +static mongoc_index_opt_t gMongocIndexOptDefault = {
  +   1,
  +   0,
  +   0,
  +   NULL,
  +   0,
  +   0,
  +   -1,
  +   -1,
  +};
  +
  +
  +const mongoc_index_opt_t *
  +mongoc_index_opt_get_default (void)
  +{
  +   return &gMongocIndexOptDefault;
  +}
  +
  +
  +void
  +mongoc_index_opt_init (mongoc_index_opt_t *opt)
  +{
  +   BSON_ASSERT (opt);
  +
  +   memcpy (opt, &gMongocIndexOptDefault, sizeof *opt);
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-init.c */
  +
  +static MONGOC_ONCE_FUN( _mongoc_do_init)
  +{
  +#ifdef MONGOC_ENABLE_SSL
  +   _mongoc_ssl_init();
  +#endif
  +
  +   _mongoc_counters_init();
  +
  +#ifdef _WIN32
  +   {
  +      WORD wVersionRequested;
  +      WSADATA wsaData;
  +      int err;
  +
  +      wVersionRequested = MAKEWORD (2, 2);
  +
  +      err = WSAStartup (wVersionRequested, &wsaData);
  +
  +      /* check the version perhaps? */
  +
  +      assert (err == 0);
  +
  +      atexit ((void(*)(void))WSACleanup);
  +   }
  +#endif
  +
  +   MONGOC_ONCE_RETURN;
  +}
  +
  +void
  +mongoc_init (void)
  +{
  +   static mongoc_once_t once = MONGOC_ONCE_INIT;
  +   mongoc_once (&once, _mongoc_do_init);
  +}
  +
  +static MONGOC_ONCE_FUN( _mongoc_do_cleanup)
  +{
  +#ifdef MONGOC_ENABLE_SSL
  +   _mongoc_ssl_cleanup();
  +#endif
  +
  +#ifdef _WIN32
  +   WSACleanup ();
  +#endif
  +
  +   MONGOC_ONCE_RETURN;
  +}
  +
  +void
  +mongoc_cleanup (void)
  +{
  +   static mongoc_once_t once = MONGOC_ONCE_INIT;
  +   mongoc_once (&once, _mongoc_do_cleanup);
  +}
  +
  +/*
  + * On GCC, just use __attribute__((constructor)) to perform initialization
  + * automatically for the application.
  + */
  +#ifdef __GNUC__
  +static void _mongoc_init_ctor (void) __attribute__((constructor));
  +static void
  +_mongoc_init_ctor (void)
  +{
  +   mongoc_init ();
  +}
  +
  +static void _mongoc_init_dtor (void) __attribute__((destructor));
  +static void
  +_mongoc_init_dtor (void)
  +{
  +   mongoc_cleanup ();
  +}
  +#endif
  +
  +/*==============================================================*/
  +/* --- mongoc-list.c */
  +
  +/**
  + * mongoc_list_append:
  + * @list: A list to append to, or NULL.
  + * @data: Data to append to @list.
  + *
  + * Appends a new link onto the linked list.
  + *
  + * Returns: @list or a new list if @list is NULL.
  + */
  +mongoc_list_t *
  +_mongoc_list_append (mongoc_list_t *list,
  +                     void          *data)
  +{
  +   mongoc_list_t *item;
  +   mongoc_list_t *iter;
  +
  +   item = bson_malloc0(sizeof *item);
  +   item->data = data;
  +   if (!list) {
  +      return item;
  +   }
  +
  +   for (iter = list; iter->next; iter = iter->next) { }
  +   iter->next = item;
  +
  +   return list;
  +}
  +
  +
  +/**
  + * mongoc_list_prepend:
  + * @list: A mongoc_list_t or NULL.
  + * @data: data to prepend to the list.
  + *
  + * Prepends to @list a new link containing @data.
  + *
  + * Returns: A new link containing data with @list following.
  + */
  +mongoc_list_t *
  +_mongoc_list_prepend (mongoc_list_t *list,
  +                      void          *data)
  +{
  +   mongoc_list_t *item;
  +
  +   item = bson_malloc0(sizeof *item);
  +   item->data = data;
  +   item->next = list;
  +
  +   return item;
  +}
  +
  +
  +/**
  + * mongoc_list_remove:
  + * @list: A mongoc_list_t.
  + * @data: Data to remove from @list.
  + *
  + * Removes the link containing @data from @list.
  + *
  + * Returns: @list with the link containing @data removed.
  + */
  +mongoc_list_t *
  +_mongoc_list_remove (mongoc_list_t *list,
  +                     void          *data)
  +{
  +   mongoc_list_t *iter;
  +   mongoc_list_t *prev = NULL;
  +   mongoc_list_t *ret = list;
  +
  +   bson_return_val_if_fail(list, NULL);
  +
  +   for (iter = list; iter; iter = iter->next) {
  +      if (iter->data == data) {
  +         if (iter != list) {
  +            prev->next = iter->next;
  +         } else {
  +            ret = iter->next;
  +         }
  +         bson_free(iter);
  +         break;
  +      }
  +      prev = iter;
  +   }
  +
  +   return ret;
  +}
  +
  +
  +/**
  + * mongoc_list_foreach:
  + * @list: A mongoc_list_t or NULL.
  + * @func: A func to call for each link in @list.
  + * @user_data: User data for @func.
  + *
  + * Calls @func for each item in @list.
  + */
  +void
  +_mongoc_list_foreach (mongoc_list_t *list,
  +                      void (*func) (void *data, void *user_data),
  +                      void          *user_data)
  +{
  +   mongoc_list_t *iter;
  +
  +   bson_return_if_fail(func);
  +
  +   for (iter = list; iter; iter = iter->next) {
  +      func(iter->data, user_data);
  +   }
  +}
  +
  +
  +/**
  + * mongoc_list_destroy:
  + * @list: A mongoc_list_t.
  + *
  + * Destroys @list and releases any resources.
  + */
  +void
  +_mongoc_list_destroy (mongoc_list_t *list)
  +{
  +   mongoc_list_t *tmp = list;
  +
  +   while (list) {
  +      tmp = list->next;
  +      bson_free(list);
  +      list = tmp;
  +   }
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-log.c */
  +
  +static mongoc_mutex_t       gLogMutex;
  +static mongoc_log_func_t  gLogFunc = mongoc_log_default_handler;
  +static void              *gLogData;
  +
  +static MONGOC_ONCE_FUN( _mongoc_ensure_mutex_once)
  +{
  +   mongoc_mutex_init(&gLogMutex);
  +
  +   MONGOC_ONCE_RETURN;
  +}
  +
  +void
  +mongoc_log_set_handler (mongoc_log_func_t  log_func,
  +                        void              *user_data)
  +{
  +   mongoc_once_t once = MONGOC_ONCE_INIT;
  +   mongoc_once(&once, &_mongoc_ensure_mutex_once);
  +
  +   mongoc_mutex_lock(&gLogMutex);
  +   gLogFunc = log_func;
  +   mongoc_mutex_unlock(&gLogMutex);
  +}
  +
  +
  +void
  +mongoc_log (mongoc_log_level_t  log_level,
  +            const char         *log_domain,
  +            const char         *format,
  +            ...)
  +{
  +   va_list args;
  +   char *message;
  +   static mongoc_once_t once = MONGOC_ONCE_INIT;
  +
  +   mongoc_once(&once, &_mongoc_ensure_mutex_once);
  +
  +   bson_return_if_fail(format);
  +
  +   va_start(args, format);
  +   message = bson_strdupv_printf(format, args);
  +   va_end(args);
  +
  +   mongoc_mutex_lock(&gLogMutex);
  +   gLogFunc(log_level, log_domain, message, gLogData);
  +   mongoc_mutex_unlock(&gLogMutex);
  +
  +   bson_free(message);
  +}
  +
  +
  +static const char *
  +log_level_str (mongoc_log_level_t log_level)
  +{
  +   switch (log_level) {
  +   case MONGOC_LOG_LEVEL_ERROR:
  +      return "ERROR";
  +   case MONGOC_LOG_LEVEL_CRITICAL:
  +      return "CRITICAL";
  +   case MONGOC_LOG_LEVEL_WARNING:
  +      return "WARNING";
  +   case MONGOC_LOG_LEVEL_MESSAGE:
  +      return "MESSAGE";
  +   case MONGOC_LOG_LEVEL_INFO:
  +      return "INFO";
  +   case MONGOC_LOG_LEVEL_DEBUG:
  +      return "DEBUG";
  +   case MONGOC_LOG_LEVEL_TRACE:
  +      return "TRACE";
  +   default:
  +      return "UNKNOWN";
  +   }
  +}
  +
  +
  +void
  +mongoc_log_default_handler (mongoc_log_level_t  log_level,
  +                            const char         *log_domain,
  +                            const char         *message,
  +                            void               *user_data)
  +{
  +   struct timeval tv;
  +   struct tm tt;
  +   time_t t;
  +   FILE *stream;
  +   char nowstr[32];
  +   int pid;
  +
  +   bson_gettimeofday(&tv, NULL);
  +   t = tv.tv_sec;
  +
  +#ifdef _WIN32
  +#  ifdef _MSC_VER
  +     localtime_s(&tt, &t);
  +#  else
  +     tt = *(localtime(&t));
  +#  endif
  +#else
  +   localtime_r(&t, &tt);
  +#endif
  +
  +   strftime (nowstr, sizeof nowstr, "%Y/%m/%d %H:%M:%S", &tt);
  +
  +   switch (log_level) {
  +   case MONGOC_LOG_LEVEL_ERROR:
  +   case MONGOC_LOG_LEVEL_CRITICAL:
  +   case MONGOC_LOG_LEVEL_WARNING:
  +      stream = stderr;
  +   case MONGOC_LOG_LEVEL_MESSAGE:
  +   case MONGOC_LOG_LEVEL_INFO:
  +   case MONGOC_LOG_LEVEL_DEBUG:
  +   case MONGOC_LOG_LEVEL_TRACE:
  +   default:
  +      stream = stdout;
  +   }
  +
  +#ifdef __linux__
  +   pid = syscall (SYS_gettid);
  +#elif defined(_WIN32)
  +   pid = (int)_getpid ();
  +#else
  +   pid = (int)getpid ();
  +#endif
  +
  +   fprintf (stream,
  +            "%s.%04ld: [%5d]: %8s: %12s: %s\n",
  +            nowstr,
  +            tv.tv_usec / 1000L,
  +            pid,
  +            log_level_str(log_level),
  +            log_domain,
  +            message);
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-matcher.c */
  +
  +static mongoc_matcher_op_t *
  +_mongoc_matcher_parse_logical (mongoc_matcher_opcode_t  opcode,
  +                               bson_iter_t             *iter,
  +                               bool                     is_root,
  +                               bson_error_t            *error);
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_parse_compare --
  + *
  + *       Parse a compare spec such as $gt or $in.
  + *
  + *       See the following link for more information.
  + *
  + *          http://docs.mongodb.org/manual/reference/operator/query/
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_op_t if successful; otherwise
  + *       NULL and @error is set.
  + *
  + * Side effects:
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static mongoc_matcher_op_t *
  +_mongoc_matcher_parse_compare (bson_iter_t  *iter,  /* IN */
  +                               const char   *path,  /* IN */
  +                               bson_error_t *error) /* OUT */
  +{
  +   const char * key;
  +   mongoc_matcher_op_t * op = NULL, * op_child;
  +   bson_iter_t child;
  +
  +   BSON_ASSERT (iter);
  +   BSON_ASSERT (path);
  +
  +   if (bson_iter_type (iter) == BSON_TYPE_DOCUMENT) {
  +      if (!bson_iter_recurse (iter, &child) ||
  +          !bson_iter_next (&child)) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_MATCHER,
  +                         MONGOC_ERROR_MATCHER_INVALID,
  +                         "Document contains no operations.");
  +         return NULL;
  +      }
  +
  +      key = bson_iter_key (&child);
  +
  +      if (key[0] != '$') {
  +         op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_EQ, path,
  +                                              iter);
  +      } else if (strcmp(key, "$not") == 0) {
  +         if (!(op_child = _mongoc_matcher_parse_compare (&child, path,
  +                                                         error))) {
  +            return NULL;
  +         }
  +         op = _mongoc_matcher_op_not_new (path, op_child);
  +      } else if (strcmp(key, "$gt") == 0) {
  +         op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_GT, path,
  +                                              &child);
  +      } else if (strcmp(key, "$gte") == 0) {
  +         op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_GTE, path,
  +                                              &child);
  +      } else if (strcmp(key, "$in") == 0) {
  +         op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_IN, path,
  +                                              &child);
  +      } else if (strcmp(key, "$lt") == 0) {
  +         op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_LT, path,
  +                                              &child);
  +      } else if (strcmp(key, "$lte") == 0) {
  +         op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_LTE, path,
  +                                              &child);
  +      } else if (strcmp(key, "$ne") == 0) {
  +         op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_NE, path,
  +                                              &child);
  +      } else if (strcmp(key, "$nin") == 0) {
  +         op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_NIN, path,
  +                                              &child);
  +      } else if (strcmp(key, "$exists") == 0) {
  +         op = _mongoc_matcher_op_exists_new (path, bson_iter_bool (&child));
  +      } else if (strcmp(key, "$type") == 0) {
  +         op = _mongoc_matcher_op_type_new (path, bson_iter_type (&child));
  +      } else {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_MATCHER,
  +                         MONGOC_ERROR_MATCHER_INVALID,
  +                         "Invalid operator \"%s\"",
  +                         key);
  +         return NULL;
  +      }
  +   } else {
  +      op = _mongoc_matcher_op_compare_new (MONGOC_MATCHER_OPCODE_EQ, path, iter);
  +   }
  +
  +   BSON_ASSERT (op);
  +
  +   return op;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_parse --
  + *
  + *       Parse a query spec observed by the current key of @iter.
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_op_t if successful; otherwise
  + *       NULL an @error is set.
  + *
  + * Side effects:
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static mongoc_matcher_op_t *
  +_mongoc_matcher_parse (bson_iter_t  *iter,  /* IN */
  +                       bson_error_t *error) /* OUT */
  +{
  +   bson_iter_t child;
  +   const char *key;
  +
  +   BSON_ASSERT (iter);
  +
  +   key = bson_iter_key (iter);
  +
  +   if (*key != '$') {
  +      return _mongoc_matcher_parse_compare (iter, key, error);
  +   } else {
  +      BSON_ASSERT (bson_iter_type(iter) == BSON_TYPE_ARRAY);
  +
  +      if (!bson_iter_recurse (iter, &child)) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_MATCHER,
  +                         MONGOC_ERROR_MATCHER_INVALID,
  +                         "Invalid value for operator \"%s\"",
  +                         key);
  +         return NULL;
  +      }
  +
  +      if (strcmp (key, "$or") == 0) {
  +         return _mongoc_matcher_parse_logical (MONGOC_MATCHER_OPCODE_OR,
  +                                               &child, false, error);
  +      } else if (strcmp(key, "$and") == 0) {
  +         return _mongoc_matcher_parse_logical (MONGOC_MATCHER_OPCODE_AND,
  +                                               &child, false, error);
  +      } else if (strcmp(key, "$nor") == 0) {
  +         return _mongoc_matcher_parse_logical (MONGOC_MATCHER_OPCODE_NOR,
  +                                               &child, false, error);
  +      }
  +   }
  +
  +   bson_set_error (error,
  +                   MONGOC_ERROR_MATCHER,
  +                   MONGOC_ERROR_MATCHER_INVALID,
  +                   "Invalid operator \"%s\"",
  +                   key);
  +
  +   return NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_parse_logical --
  + *
  + *       Parse a query spec containing a logical operator such as
  + *       $or, $and, $not, and $nor.
  + *
  + *       See the following link for more information.
  + *
  + *       http://docs.mongodb.org/manual/reference/operator/query/
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_op_t if successful; otherwise
  + *       NULL and @error is set.
  + *
  + * Side effects:
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static mongoc_matcher_op_t *
  +_mongoc_matcher_parse_logical (mongoc_matcher_opcode_t  opcode,  /* IN */
  +                               bson_iter_t             *iter,    /* IN */
  +                               bool                     is_root, /* IN */
  +                               bson_error_t            *error)   /* OUT */
  +{
  +   mongoc_matcher_op_t *left;
  +   mongoc_matcher_op_t *right;
  +   mongoc_matcher_op_t *more;
  +   mongoc_matcher_op_t *more_wrap;
  +   bson_iter_t child;
  +
  +   BSON_ASSERT (opcode);
  +   BSON_ASSERT (iter);
  +   BSON_ASSERT (iter);
  +
  +   if (!bson_iter_next (iter)) {
  +      bson_set_error (error,
  +                      MONGOC_ERROR_MATCHER,
  +                      MONGOC_ERROR_MATCHER_INVALID,
  +                      "Invalid logical operator.");
  +      return NULL;
  +   }
  +
  +   if (is_root) {
  +      if (!(left = _mongoc_matcher_parse (iter, error))) {
  +         return NULL;
  +      }
  +   } else {
  +      if (!BSON_ITER_HOLDS_DOCUMENT (iter)) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_MATCHER,
  +                         MONGOC_ERROR_MATCHER_INVALID,
  +                         "Expected document in value.");
  +         return NULL;
  +      }
  +
  +      bson_iter_recurse (iter, &child);
  +      bson_iter_next (&child);
  +
  +      if (!(left = _mongoc_matcher_parse (&child, error))) {
  +         return NULL;
  +      }
  +   }
  +
  +   if (!bson_iter_next (iter)) {
  +      return left;
  +   }
  +
  +   if (is_root) {
  +      if (!(right = _mongoc_matcher_parse (iter, error))) {
  +         return NULL;
  +      }
  +   } else {
  +      if (!BSON_ITER_HOLDS_DOCUMENT (iter)) {
  +         bson_set_error (error,
  +                         MONGOC_ERROR_MATCHER,
  +                         MONGOC_ERROR_MATCHER_INVALID,
  +                         "Expected document in value.");
  +         return NULL;
  +      }
  +
  +      bson_iter_recurse (iter, &child);
  +      bson_iter_next (&child);
  +
  +      if (!(right = _mongoc_matcher_parse (&child, error))) {
  +         return NULL;
  +      }
  +   }
  +
  +   more = _mongoc_matcher_parse_logical (opcode, iter, is_root, error);
  +
  +   if (more) {
  +      more_wrap = _mongoc_matcher_op_logical_new (opcode, right, more);
  +      return _mongoc_matcher_op_logical_new (opcode, left, more_wrap);
  +   }
  +
  +   return _mongoc_matcher_op_logical_new (opcode, left, right);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_matcher_new --
  + *
  + *       Create a new mongoc_matcher_t using the query specification
  + *       provided in @query.
  + *
  + *       This will build an operation tree that can be applied to arbitrary
  + *       bson documents using mongoc_matcher_match().
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_t if successful; otherwise NULL
  + *       and @error is set.
  + *
  + *       The mongoc_matcher_t should be freed with
  + *       mongoc_matcher_destroy().
  + *
  + * Side effects:
  + *       @error may be set.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_matcher_t *
  +mongoc_matcher_new (const bson_t *query, /* IN */
  +                    bson_error_t *error) /* OUT */
  +{
  +   mongoc_matcher_op_t *op;
  +   mongoc_matcher_t *matcher;
  +   bson_iter_t iter;
  +
  +   BSON_ASSERT (query);
  +
  +   matcher = bson_malloc0 (sizeof *matcher);
  +   bson_copy_to (query, &matcher->query);
  +
  +   if (!bson_iter_init (&iter, &matcher->query)) {
  +      goto failure;
  +   }
  +
  +   if (!(op = _mongoc_matcher_parse_logical (MONGOC_MATCHER_OPCODE_AND, &iter,
  +                                             true, error))) {
  +      goto failure;
  +   }
  +
  +   matcher->optree = op;
  +
  +   return matcher;
  +
  +failure:
  +   bson_destroy (&matcher->query);
  +   bson_free (matcher);
  +   return NULL;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_matcher_match --
  + *
  + *       Checks to see if @bson matches the query specified when creating
  + *       @matcher.
  + *
  + * Returns:
  + *       TRUE if @bson matched the query, otherwise FALSE.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +mongoc_matcher_match (const mongoc_matcher_t *matcher, /* IN */
  +                      const bson_t           *bson)    /* IN */
  +{
  +   BSON_ASSERT (matcher);
  +   BSON_ASSERT (matcher->optree);
  +   BSON_ASSERT (bson);
  +
  +   return _mongoc_matcher_op_match (matcher->optree, bson);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * mongoc_matcher_destroy --
  + *
  + *       Release all resources associated with @matcher.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +mongoc_matcher_destroy (mongoc_matcher_t *matcher) /* IN */
  +{
  +   BSON_ASSERT (matcher);
  +
  +   _mongoc_matcher_op_destroy (matcher->optree);
  +   bson_destroy (&matcher->query);
  +   bson_free (matcher);
  +}
  +
  +/*==============================================================*/
  +/* --- mongoc-matcher-op.c */
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_exists_new --
  + *
  + *       Create a new op for checking {$exists: bool}.
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_op_t that should be freed with
  + *       _mongoc_matcher_op_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_matcher_op_t *
  +_mongoc_matcher_op_exists_new (const char  *path,   /* IN */
  +                               bool  exists) /* IN */
  +{
  +   mongoc_matcher_op_t *op;
  +
  +   BSON_ASSERT (path);
  +
  +   op = bson_malloc0 (sizeof *op);
  +   op->exists.base.opcode = MONGOC_MATCHER_OPCODE_EXISTS;
  +   op->exists.path = bson_strdup (path);
  +   op->exists.exists = exists;
  +
  +   return op;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_type_new --
  + *
  + *       Create a new op for checking {$type: int}.
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_op_t that should be freed with
  + *       _mongoc_matcher_op_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_matcher_op_t *
  +_mongoc_matcher_op_type_new (const char  *path, /* IN */
  +                             bson_type_t  type) /* IN */
  +{
  +   mongoc_matcher_op_t *op;
  +
  +   BSON_ASSERT (path);
  +   BSON_ASSERT (type);
  +
  +   op = bson_malloc0 (sizeof *op);
  +   op->type.base.opcode = MONGOC_MATCHER_OPCODE_TYPE;
  +   op->type.path = bson_strdup (path);
  +   op->type.type = type;
  +
  +   return op;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_logical_new --
  + *
  + *       Create a new op for checking any of:
  + *
  + *          {$or: []}
  + *          {$nor: []}
  + *          {$and: []}
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_op_t that should be freed with
  + *       _mongoc_matcher_op_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_matcher_op_t *
  +_mongoc_matcher_op_logical_new (mongoc_matcher_opcode_t  opcode, /* IN */
  +                                mongoc_matcher_op_t     *left,   /* IN */
  +                                mongoc_matcher_op_t     *right)  /* IN */
  +{
  +   mongoc_matcher_op_t *op;
  +
  +   BSON_ASSERT (left);
  +   BSON_ASSERT ((opcode >= MONGOC_MATCHER_OPCODE_OR) &&
  +                (opcode <= MONGOC_MATCHER_OPCODE_NOR));
  +
  +   op = bson_malloc0 (sizeof *op);
  +   op->logical.base.opcode = opcode;
  +   op->logical.left = left;
  +   op->logical.right = right;
  +
  +   return op;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_compare_new --
  + *
  + *       Create a new op for checking any of:
  + *
  + *          {"abc": "def"}
  + *          {$gt: {...}
  + *          {$gte: {...}
  + *          {$lt: {...}
  + *          {$lte: {...}
  + *          {$ne: {...}
  + *          {$in: [...]}
  + *          {$nin: [...]}
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_op_t that should be freed with
  + *       _mongoc_matcher_op_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_matcher_op_t *
  +_mongoc_matcher_op_compare_new (mongoc_matcher_opcode_t  opcode, /* IN */
  +                                const char              *path,   /* IN */
  +                                const bson_iter_t       *iter)   /* IN */
  +{
  +   mongoc_matcher_op_t *op;
  +
  +   BSON_ASSERT ((opcode >= MONGOC_MATCHER_OPCODE_EQ) &&
  +                (opcode <= MONGOC_MATCHER_OPCODE_NIN));
  +   BSON_ASSERT (path);
  +   BSON_ASSERT (iter);
  +
  +   op = bson_malloc0 (sizeof *op);
  +   op->compare.base.opcode = opcode;
  +   op->compare.path = bson_strdup (path);
  +   memcpy (&op->compare.iter, iter, sizeof *iter);
  +
  +   return op;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_not_new --
  + *
  + *       Create a new op for checking {$not: {...}}
  + *
  + * Returns:
  + *       A newly allocated mongoc_matcher_op_t that should be freed with
  + *       _mongoc_matcher_op_destroy().
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +mongoc_matcher_op_t *
  +_mongoc_matcher_op_not_new (const char          *path,  /* IN */
  +                            mongoc_matcher_op_t *child) /* IN */
  +{
  +   mongoc_matcher_op_t *op;
  +
  +   BSON_ASSERT (path);
  +   BSON_ASSERT (child);
  +
  +   op = bson_malloc0 (sizeof *op);
  +   op->not.base.opcode = MONGOC_MATCHER_OPCODE_NOT;
  +   op->not.path = bson_strdup (path);
  +   op->not.child = child;
  +
  +   return op;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_destroy --
  + *
  + *       Free a mongoc_matcher_op_t structure and all children structures.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +void
  +_mongoc_matcher_op_destroy (mongoc_matcher_op_t *op) /* IN */
  +{
  +   BSON_ASSERT (op);
  +
  +   switch (op->base.opcode) {
  +   case MONGOC_MATCHER_OPCODE_EQ:
  +   case MONGOC_MATCHER_OPCODE_GT:
  +   case MONGOC_MATCHER_OPCODE_GTE:
  +   case MONGOC_MATCHER_OPCODE_IN:
  +   case MONGOC_MATCHER_OPCODE_LT:
  +   case MONGOC_MATCHER_OPCODE_LTE:
  +   case MONGOC_MATCHER_OPCODE_NE:
  +   case MONGOC_MATCHER_OPCODE_NIN:
  +      bson_free (op->compare.path);
  +      break;
  +   case MONGOC_MATCHER_OPCODE_OR:
  +   case MONGOC_MATCHER_OPCODE_AND:
  +   case MONGOC_MATCHER_OPCODE_NOR:
  +      if (op->logical.left)
  +         _mongoc_matcher_op_destroy (op->logical.left);
  +      if (op->logical.right)
  +         _mongoc_matcher_op_destroy (op->logical.right);
  +      break;
  +   case MONGOC_MATCHER_OPCODE_NOT:
  +      _mongoc_matcher_op_destroy (op->not.child);
  +      bson_free (op->not.path);
  +      break;
  +   case MONGOC_MATCHER_OPCODE_EXISTS:
  +      bson_free (op->exists.path);
  +      break;
  +   case MONGOC_MATCHER_OPCODE_TYPE:
  +      bson_free (op->type.path);
  +      break;
  +   default:
  +      break;
  +   }
  +
  +   bson_free (op);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_exists_match --
  + *
  + *       Checks to see if @bson matches @exists requirements. The
  + *       {$exists: bool} query can be either true or fase so we must
  + *       handle false as "not exists".
  + *
  + * Returns:
  + *       true if the field exists and the spec expected it.
  + *       true if the field does not exist and the spec expected it to not
  + *       exist.
  + *       Otherwise, false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_exists_match (mongoc_matcher_op_exists_t *exists, /* IN */
  +                                 const bson_t               *bson)   /* IN */
  +{
  +   bson_iter_t iter;
  +   bson_iter_t desc;
  +   bool found;
  +
  +   BSON_ASSERT (exists);
  +   BSON_ASSERT (bson);
  +
  +   found = (bson_iter_init (&iter, bson) &&
  +            bson_iter_find_descendant (&iter, exists->path, &desc));
  +
  +   return (found == exists->exists);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_type_match --
  + *
  + *       Checks if @bson matches the {$type: ...} op.
  + *
  + * Returns:
  + *       true if the requested field was found and the type matched
  + *       the requested type.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_type_match (mongoc_matcher_op_type_t *type, /* IN */
  +                               const bson_t             *bson) /* IN */
  +{
  +   bson_iter_t iter;
  +   bson_iter_t desc;
  +
  +   BSON_ASSERT (type);
  +   BSON_ASSERT (bson);
  +
  +   if (bson_iter_init (&iter, bson) &&
  +       bson_iter_find_descendant (&iter, type->path, &desc)) {
  +      return (bson_iter_type (&iter) == type->type);
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_not_match --
  + *
  + *       Checks if the {$not: ...} expression matches by negating the
  + *       child expression.
  + *
  + * Returns:
  + *       true if the child expression returned false.
  + *       Otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_not_match (mongoc_matcher_op_not_t *not,  /* IN */
  +                              const bson_t            *bson) /* IN */
  +{
  +   BSON_ASSERT (not);
  +   BSON_ASSERT (bson);
  +
  +   return !_mongoc_matcher_op_match (not->child, bson);
  +}
  +
  +
  +/*
  + * NOTE: The compare operators are opposite since the left and right are
  + * somewhat inverted being that the spec is on the left and the value on the
  + * right.
  + */
  +#define _TYPE_CODE(l, r) ((((int)(l)) << 8) | ((int)(r)))
  +#define _NATIVE_COMPARE(op, t1, t2) \
  +   (bson_iter##t1(&compare->iter) op bson_iter##t2(iter))
  +#define _EQ_COMPARE(t1, t2)  _NATIVE_COMPARE(==, t1, t2)
  +#define _NE_COMPARE(t1, t2)  _NATIVE_COMPARE(!=, t1, t2)
  +#define _GT_COMPARE(t1, t2)  _NATIVE_COMPARE(<=, t1, t2)
  +#define _GTE_COMPARE(t1, t2) _NATIVE_COMPARE(<, t1, t2)
  +#define _LT_COMPARE(t1, t2)  _NATIVE_COMPARE(>=, t1, t2)
  +#define _LTE_COMPARE(t1, t2) _NATIVE_COMPARE(>, t1, t2)
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_eq_match --
  + *
  + *       Performs equality match for all types on either left or right
  + *       side of the equation.
  + *
  + *       We try to default to what the compiler would do for comparing
  + *       things like integers. Therefore, we just have MACRO'tized
  + *       everything so that the compiler sees the native values. (Such
  + *       as (double == int64).
  + *
  + *       The _TYPE_CODE() stuff allows us to shove the type of the left
  + *       and the right into a single integer and then do a jump table
  + *       with a switch/case for all our supported types.
  + *
  + *       I imagine a bunch more of these will need to be added, so feel
  + *       free to submit patches.
  + *
  + * Returns:
  + *       true if the equality match succeeded.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_eq_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                             bson_iter_t                 *iter)    /* IN */
  +{
  +   int code;
  +
  +   BSON_ASSERT (compare);
  +   BSON_ASSERT (iter);
  +
  +   code = _TYPE_CODE (bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +
  +   switch (code) {
  +
  +   /* Double on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_DOUBLE):
  +      return _EQ_COMPARE (_double, _double);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_BOOL):
  +      return _EQ_COMPARE (_double, _bool);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT32):
  +      return _EQ_COMPARE (_double, _int32);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT64):
  +      return _EQ_COMPARE (_double, _int64);
  +
  +   /* UTF8 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_UTF8, BSON_TYPE_UTF8):
  +      {
  +         uint32_t llen;
  +         uint32_t rlen;
  +         const char *lstr;
  +         const char *rstr;
  +
  +         lstr = bson_iter_utf8 (&compare->iter, &llen);
  +         rstr = bson_iter_utf8 (iter, &rlen);
  +
  +         return ((llen == rlen) && (0 == memcmp (lstr, rstr, llen)));
  +      }
  +
  +   /* Int32 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_DOUBLE):
  +      return _EQ_COMPARE (_int32, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_BOOL):
  +      return _EQ_COMPARE (_int32, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT32):
  +      return _EQ_COMPARE (_int32, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT64):
  +      return _EQ_COMPARE (_int32, _int64);
  +
  +   /* Int64 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_DOUBLE):
  +      return _EQ_COMPARE (_int64, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_BOOL):
  +      return _EQ_COMPARE (_int64, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT32):
  +      return _EQ_COMPARE (_int64, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT64):
  +      return _EQ_COMPARE (_int64, _int64);
  +
  +   /* Null on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_NULL, BSON_TYPE_NULL):
  +   case _TYPE_CODE(BSON_TYPE_NULL, BSON_TYPE_UNDEFINED):
  +      return true;
  +
  +   default:
  +      return false;
  +   }
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_gt_match --
  + *
  + *       Perform {$gt: ...} match using @compare.
  + *
  + *       In general, we try to default to what the compiler would do
  + *       for comparison between different types.
  + *
  + * Returns:
  + *       true if the document field was > the spec value.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_gt_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                             bson_iter_t                 *iter)    /* IN */
  +{
  +   int code;
  +
  +   BSON_ASSERT (compare);
  +   BSON_ASSERT (iter);
  +
  +   code = _TYPE_CODE (bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +
  +   switch (code) {
  +
  +   /* Double on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_DOUBLE):
  +      return _GT_COMPARE (_double, _double);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_BOOL):
  +      return _GT_COMPARE (_double, _bool);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT32):
  +      return _GT_COMPARE (_double, _int32);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT64):
  +      return _GT_COMPARE (_double, _int64);
  +
  +   /* Int32 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_DOUBLE):
  +      return _GT_COMPARE (_int32, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_BOOL):
  +      return _GT_COMPARE (_int32, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT32):
  +      return _GT_COMPARE (_int32, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT64):
  +      return _GT_COMPARE (_int32, _int64);
  +
  +   /* Int64 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_DOUBLE):
  +      return _GT_COMPARE (_int64, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_BOOL):
  +      return _GT_COMPARE (_int64, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT32):
  +      return _GT_COMPARE (_int64, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT64):
  +      return _GT_COMPARE (_int64, _int64);
  +
  +   default:
  +      MONGOC_WARNING ("Implement for (Type(%d) > Type(%d))",
  +                      bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +      break;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_gte_match --
  + *
  + *       Perform a match of {"path": {"$gte": value}}.
  + *
  + * Returns:
  + *       true if the the spec matches, otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_gte_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                              bson_iter_t                 *iter)    /* IN */
  +{
  +   int code;
  +
  +   BSON_ASSERT (compare);
  +   BSON_ASSERT (iter);
  +
  +   code = _TYPE_CODE (bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +
  +   switch (code) {
  +
  +   /* Double on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_DOUBLE):
  +      return _GTE_COMPARE (_double, _double);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_BOOL):
  +      return _GTE_COMPARE (_double, _bool);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT32):
  +      return _GTE_COMPARE (_double, _int32);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT64):
  +      return _GTE_COMPARE (_double, _int64);
  +
  +   /* Int32 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_DOUBLE):
  +      return _GTE_COMPARE (_int32, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_BOOL):
  +      return _GTE_COMPARE (_int32, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT32):
  +      return _GTE_COMPARE (_int32, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT64):
  +      return _GTE_COMPARE (_int32, _int64);
  +
  +   /* Int64 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_DOUBLE):
  +      return _GTE_COMPARE (_int64, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_BOOL):
  +      return _GTE_COMPARE (_int64, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT32):
  +      return _GTE_COMPARE (_int64, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT64):
  +      return _GTE_COMPARE (_int64, _int64);
  +
  +   default:
  +      MONGOC_WARNING ("Implement for (Type(%d) >= Type(%d))",
  +                      bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +      break;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_in_match --
  + *
  + *       Checks the spec {"path": {"$in": [value1, value2, ...]}}.
  + *
  + * Returns:
  + *       true if the spec matched, otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_in_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                             bson_iter_t                 *iter)    /* IN */
  +{
  +   mongoc_matcher_op_compare_t op;
  +
  +   op.base.opcode = MONGOC_MATCHER_OPCODE_EQ;
  +   op.path = compare->path;
  +
  +   if (!BSON_ITER_HOLDS_ARRAY (&compare->iter) ||
  +       !bson_iter_recurse (&compare->iter, &op.iter)) {
  +      return false;
  +   }
  +
  +   while (bson_iter_next (&op.iter)) {
  +      if (_mongoc_matcher_op_eq_match (&op, iter)) {
  +         return true;
  +      }
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_lt_match --
  + *
  + *       Perform a {"path": "$lt": {value}} match.
  + *
  + * Returns:
  + *       true if the spec matched, otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_lt_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                             bson_iter_t                 *iter)    /* IN */
  +{
  +   int code;
  +
  +   BSON_ASSERT (compare);
  +   BSON_ASSERT (iter);
  +
  +   code = _TYPE_CODE (bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +
  +   switch (code) {
  +
  +   /* Double on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_DOUBLE):
  +      return _LT_COMPARE (_double, _double);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_BOOL):
  +      return _LT_COMPARE (_double, _bool);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT32):
  +      return _LT_COMPARE (_double, _int32);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT64):
  +      return _LT_COMPARE (_double, _int64);
  +
  +   /* Int32 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_DOUBLE):
  +      return _LT_COMPARE (_int32, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_BOOL):
  +      return _LT_COMPARE (_int32, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT32):
  +      return _LT_COMPARE (_int32, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT64):
  +      return _LT_COMPARE (_int32, _int64);
  +
  +   /* Int64 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_DOUBLE):
  +      return _LT_COMPARE (_int64, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_BOOL):
  +      return _LT_COMPARE (_int64, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT32):
  +      return _LT_COMPARE (_int64, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT64):
  +      return _LT_COMPARE (_int64, _int64);
  +
  +   default:
  +      MONGOC_WARNING ("Implement for (Type(%d) < Type(%d))",
  +                      bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +      break;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_lte_match --
  + *
  + *       Perform a {"$path": {"$lte": value}} match.
  + *
  + * Returns:
  + *       true if the spec matched, otherwise false.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_lte_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                              bson_iter_t                 *iter)    /* IN */
  +{
  +   int code;
  +
  +   BSON_ASSERT (compare);
  +   BSON_ASSERT (iter);
  +
  +   code = _TYPE_CODE (bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +
  +   switch (code) {
  +
  +   /* Double on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_DOUBLE):
  +      return _LTE_COMPARE (_double, _double);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_BOOL):
  +      return _LTE_COMPARE (_double, _bool);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT32):
  +      return _LTE_COMPARE (_double, _int32);
  +   case _TYPE_CODE(BSON_TYPE_DOUBLE, BSON_TYPE_INT64):
  +      return _LTE_COMPARE (_double, _int64);
  +
  +   /* Int32 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_DOUBLE):
  +      return _LTE_COMPARE (_int32, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_BOOL):
  +      return _LTE_COMPARE (_int32, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT32):
  +      return _LTE_COMPARE (_int32, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT32, BSON_TYPE_INT64):
  +      return _LTE_COMPARE (_int32, _int64);
  +
  +   /* Int64 on Left Side */
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_DOUBLE):
  +      return _LTE_COMPARE (_int64, _double);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_BOOL):
  +      return _LTE_COMPARE (_int64, _bool);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT32):
  +      return _LTE_COMPARE (_int64, _int32);
  +   case _TYPE_CODE(BSON_TYPE_INT64, BSON_TYPE_INT64):
  +      return _LTE_COMPARE (_int64, _int64);
  +
  +   default:
  +      MONGOC_WARNING ("Implement for (Type(%d) <= Type(%d))",
  +                      bson_iter_type (&compare->iter),
  +                      bson_iter_type (iter));
  +      break;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_ne_match --
  + *
  + *       Perform a {"path": {"$ne": value}} match.
  + *
  + * Returns:
  + *       true if the field "path" was not found or the value is not-equal
  + *       to value.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_ne_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                             bson_iter_t                 *iter)    /* IN */
  +{
  +   return !_mongoc_matcher_op_eq_match (compare, iter);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_nin_match --
  + *
  + *       Perform a {"path": {"$nin": value}} match.
  + *
  + * Returns:
  + *       true if value was not found in the array at "path".
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_nin_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                              bson_iter_t                 *iter)    /* IN */
  +{
  +   return !_mongoc_matcher_op_in_match (compare, iter);
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_compare_match --
  + *
  + *       Dispatch function for mongoc_matcher_op_compare_t operations
  + *       to perform a match.
  + *
  + * Returns:
  + *       Opcode dependent.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_compare_match (mongoc_matcher_op_compare_t *compare, /* IN */
  +                                  const bson_t                *bson)    /* IN */
  +{
  +   bson_iter_t iter;
  +
  +   BSON_ASSERT (compare);
  +   BSON_ASSERT (bson);
  +
  +   if (!bson_iter_init_find (&iter, bson, compare->path)) {
  +      return false;
  +   }
  +
  +   switch ((int)compare->base.opcode) {
  +   case MONGOC_MATCHER_OPCODE_EQ:
  +      return _mongoc_matcher_op_eq_match (compare, &iter);
  +   case MONGOC_MATCHER_OPCODE_GT:
  +      return _mongoc_matcher_op_gt_match (compare, &iter);
  +   case MONGOC_MATCHER_OPCODE_GTE:
  +      return _mongoc_matcher_op_gte_match (compare, &iter);
  +   case MONGOC_MATCHER_OPCODE_IN:
  +      return _mongoc_matcher_op_in_match (compare, &iter);
  +   case MONGOC_MATCHER_OPCODE_LT:
  +      return _mongoc_matcher_op_lt_match (compare, &iter);
  +   case MONGOC_MATCHER_OPCODE_LTE:
  +      return _mongoc_matcher_op_lte_match (compare, &iter);
  +   case MONGOC_MATCHER_OPCODE_NE:
  +      return _mongoc_matcher_op_ne_match (compare, &iter);
  +   case MONGOC_MATCHER_OPCODE_NIN:
  +      return _mongoc_matcher_op_nin_match (compare, &iter);
  +   default:
  +      BSON_ASSERT (false);
  +      break;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_logical_match --
  + *
  + *       Dispatch function for mongoc_matcher_op_logical_t operations
  + *       to perform a match.
  + *
  + * Returns:
  + *       Opcode specific.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +static bool
  +_mongoc_matcher_op_logical_match (mongoc_matcher_op_logical_t *logical, /* IN */
  +                                  const bson_t                *bson)    /* IN */
  +{
  +   BSON_ASSERT (logical);
  +   BSON_ASSERT (bson);
  +
  +   switch ((int)logical->base.opcode) {
  +   case MONGOC_MATCHER_OPCODE_OR:
  +      return (_mongoc_matcher_op_match (logical->left, bson) ||
  +              _mongoc_matcher_op_match (logical->right, bson));
  +   case MONGOC_MATCHER_OPCODE_AND:
  +      return (_mongoc_matcher_op_match (logical->left, bson) &&
  +              _mongoc_matcher_op_match (logical->right, bson));
  +   case MONGOC_MATCHER_OPCODE_NOR:
  +      return !(_mongoc_matcher_op_match (logical->left, bson) ||
  +               _mongoc_matcher_op_match (logical->right, bson));
  +   default:
  +      BSON_ASSERT (false);
  +      break;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_match --
  + *
  + *       Dispatch function for all operation types to perform a match.
  + *
  + * Returns:
  + *       Opcode specific.
  + *
  + * Side effects:
  + *       None.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +bool
  +_mongoc_matcher_op_match (mongoc_matcher_op_t *op,   /* IN */
  +                          const bson_t        *bson) /* IN */
  +{
  +   BSON_ASSERT (op);
  +   BSON_ASSERT (bson);
  +
  +   switch (op->base.opcode) {
  +   case MONGOC_MATCHER_OPCODE_EQ:
  +   case MONGOC_MATCHER_OPCODE_GT:
  +   case MONGOC_MATCHER_OPCODE_GTE:
  +   case MONGOC_MATCHER_OPCODE_IN:
  +   case MONGOC_MATCHER_OPCODE_LT:
  +   case MONGOC_MATCHER_OPCODE_LTE:
  +   case MONGOC_MATCHER_OPCODE_NE:
  +   case MONGOC_MATCHER_OPCODE_NIN:
  +      return _mongoc_matcher_op_compare_match (&op->compare, bson);
  +   case MONGOC_MATCHER_OPCODE_OR:
  +   case MONGOC_MATCHER_OPCODE_AND:
  +   case MONGOC_MATCHER_OPCODE_NOR:
  +      return _mongoc_matcher_op_logical_match (&op->logical, bson);
  +   case MONGOC_MATCHER_OPCODE_NOT:
  +      return _mongoc_matcher_op_not_match (&op->not, bson);
  +   case MONGOC_MATCHER_OPCODE_EXISTS:
  +      return _mongoc_matcher_op_exists_match (&op->exists, bson);
  +   case MONGOC_MATCHER_OPCODE_TYPE:
  +      return _mongoc_matcher_op_type_match (&op->type, bson);
  +   default:
  +      break;
  +   }
  +
  +   return false;
  +}
  +
  +
  +/*
  + *--------------------------------------------------------------------------
  + *
  + * _mongoc_matcher_op_to_bson --
  + *
  + *       Convert the optree specified by @op to a bson document similar
  + *       to what the query would have been. This is not perfectly the
  + *       same, and so should not be used as such.
  + *
  + * Returns:
  + *       None.
  + *
  + * Side effects:
  + *       @bson is appended to, and therefore must be initialized before
  + *       calling this function.
  + *
  + *--------------------------------------------------------------------------
  + */
  +
  +void
  +_mongoc_matcher_op_to_bson (mongoc_matcher_op_t *op,   /* IN */
  +                            bson_t              *bson) /* IN */
  +{
  +   const char *str;
  +   bson_t child;
  +   bson_t child2;
  +
  +   BSON_ASSERT (op);
  +   BSON_ASSERT (bson);
  +
  +   switch (op->base.opcode) {
  +   case MONGOC_MATCHER_OPCODE_EQ:
  +      bson_append_iter (bson, op->compare.path, -1, &op->compare.iter);
  +      break;
  +   case MONGOC_MATCHER_OPCODE_GT:
  +   case MONGOC_MATCHER_OPCODE_GTE:
  +   case MONGOC_MATCHER_OPCODE_IN:
  +   case MONGOC_MATCHER_OPCODE_LT:
  +   case MONGOC_MATCHER_OPCODE_LTE:
  +   case MONGOC_MATCHER_OPCODE_NE:
  +   case MONGOC_MATCHER_OPCODE_NIN:
  +      switch ((int)op->base.opcode) {
  +      case MONGOC_MATCHER_OPCODE_GT:
  +         str = "$gt";
  +         break;
  +      case MONGOC_MATCHER_OPCODE_GTE:
  +         str = "$gte";
  +         break;
  +      case MONGOC_MATCHER_OPCODE_IN:
  +         str = "$in";
  +         break;
  +      case MONGOC_MATCHER_OPCODE_LT:
  +         str = "$lt";
  +         break;
  +      case MONGOC_MATCHER_OPCODE_LTE:
  +         str = "$lte";
  +         break;
  +      case MONGOC_MATCHER_OPCODE_NE:
  +         str = "$ne";
  +         break;
  +      case MONGOC_MATCHER_OPCODE_NIN:
  +         str = "$nin";
  +         break;
  +      default:
  +         str = "???";
  +         break;
  +      }
  +      bson_append_document_begin (bson, op->compare.path, -1, &child);
  +      bson_append_iter (&child, str, -1, &op->compare.iter);
  +      bson_append_document_end (bson, &child);
  +      break;
  +   case MONGOC_MATCHER_OPCODE_OR:
  +   case MONGOC_MATCHER_OPCODE_AND:
  +   case MONGOC_MATCHER_OPCODE_NOR:
  +      if (op->base.opcode == MONGOC_MATCHER_OPCODE_OR) {
  +         str = "$or";
  +      } else if (op->base.opcode == MONGOC_MATCHER_OPCODE_AND) {
  +         str = "$and";
  +      } else if (op->base.opcode == MONGOC_MATCHER_OPCODE_NOR) {
  +         str = "$nor";
  +      } else {
  +         BSON_ASSERT (false);
  +         str = NULL;
  +      }
  +      bson_append_array_begin (bson, str, -1, &child);
  +      bson_append_document_begin (&child, "0", 1, &child2);
  +      _mongoc_matcher_op_to_bson (op->logical.left, &child2);
  +      bson_append_document_end (&child, &child2);
  +      if (op->logical.right) {
  +         bson_append_document_begin (&child, "1", 1, &child2);
  +         _mongoc_matcher_op_to_bson (op->logical.right, &child2);
  +         bson_append_document_end (&child, &child2);
  +      }
  +      bson_append_array_end (bson, &child);
  +      break;
  +   case MONGOC_MATCHER_OPCODE_NOT:
  +      bson_append_document_begin (bson, op->not.path, -1, &child);
  +      bson_append_document_begin (&child, "$not", 4, &child2);
  +      _mongoc_matcher_op_to_bson (op->not.child, &child2);
  +      bson_append_document_end (&child, &child2);
  +      bson_append_document_end (bson, &child);
  +      break;
  +   case MONGOC_M


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic