[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 (¤t_iter, STACK_ITER_CHILD, sizeof current_iter);
+
+ if (!bson_iter_find (¤t_iter, key)) { return false; }
+
+ switch ((int)type) {
+ case BCON_TYPE_DOC_START:
+
+ if (bson_iter_type (¤t_iter) !=
+ BSON_TYPE_DOCUMENT) { return false; }
+
+ STACK_PUSH_DOC (bson_iter_recurse (¤t_iter,
+ STACK_ITER_CHILD));
+ break;
+ case BCON_TYPE_DOC_END:
+ STACK_POP_DOC (_noop ());
+ break;
+ case BCON_TYPE_ARRAY_START:
+
+ if (bson_iter_type (¤t_iter) !=
+ BSON_TYPE_ARRAY) { return false; }
+
+ STACK_PUSH_ARRAY (bson_iter_recurse (¤t_iter,
+ STACK_ITER_CHILD));
+ break;
+ case BCON_TYPE_ARRAY_END:
+ STACK_POP_ARRAY (_noop ());
+ break;
+ default:
+
+ if (!_bcon_extract_single (¤t_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, ×tamp, &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 (©, "_id", 3, &oid);
+ bson_concat (©, document);
+ document = ©
+ }
+
+ ret = mongoc_collection_insert_bulk (collection, flags, &document, 1,
+ write_concern, error);
+
+ bson_destroy (©);
+
+ 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