Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

CDRIVER-164 - BCON (BSON C Object Notation) C initializers for BSON

  • Loading branch information...
commit dc70faf4a02031515c4cd49e03205431729b4017 1 parent 247c7ce
@gjmurakami-10gen gjmurakami-10gen authored
View
14 Makefile
@@ -30,14 +30,14 @@ BSON_LIBNAME=libbson
ENV?=posix
# TODO: add replica set test, cpp test, platform tests, json_test
-TESTS=test_auth test_bson test_bson_subobject test_count_delete \
+TESTS=test_auth test_bcon test_bson test_bson_subobject test_count_delete \
test_cursors test_endian_swap test_errors test_examples \
test_functions test_gridfs test_helpers \
test_oid test_resize test_simple test_sizes test_update \
test_validate test_write_concern test_commands
-MONGO_OBJECTS=src/bson.o src/encoding.o src/gridfs.o src/md5.o src/mongo.o \
+MONGO_OBJECTS=src/bcon.o src/bson.o src/encoding.o src/gridfs.o src/md5.o src/mongo.o \
src/numbers.o
-BSON_OBJECTS=src/bson.o src/numbers.o src/encoding.o
+BSON_OBJECTS=src/bcon.o src/bson.o src/numbers.o src/encoding.o
ifeq ($(ENV),posix)
TESTS+=test_env_posix test_unix_socket
@@ -136,6 +136,7 @@ INSTALL_LIBRARY_PATH?=/usr/local/lib
all: $(MONGO_DYLIBNAME) $(BSON_DYLIBNAME) $(MONGO_STLIBNAME) $(BSON_STLIBNAME)
# Dependency targets. Run 'make deps' to generate these.
+bcon.o: src/bcon.c src/bcon.h src/bson.h
bson.o: src/bson.c src/bson.h src/encoding.h
encoding.o: src/encoding.c src/bson.h src/encoding.h
env_standard.o: src/env_standard.c src/env.h src/mongo.h src/bson.h
@@ -182,7 +183,10 @@ docs:
python docs/buildscripts/docs.py
clean:
- rm -rf $(MONGO_DYLIBNAME) $(MONGO_STLIBNAME) $(BSON_DYLIBNAME) $(BSON_STLIBNAME) src/*.o src/*.os test_*
+ rm -rf src/*.o src/*.os test/*.o test/*.os test_* .scon* config.log
+
+clobber: clean
+ rm -rf $(MONGO_DYLIBNAME) $(MONGO_STLIBNAME) $(BSON_DYLIBNAME) $(BSON_STLIBNAME) docs/html docs/source/doxygen
deps:
$(CC) -MM -DMONGO_HAVE_STDINT src/*.c
@@ -199,4 +203,4 @@ test_%: test/%_test.c test/test.h $(MONGO_STLIBNAME)
%.os: %.c
$(CC) -o $@ -c $(ALL_CFLAGS) $(DYN_FLAGS) $<
-.PHONY: 32bit all clean deps docs install test valgrind
+.PHONY: 32bit all clean clobber deps docs install test valgrind
View
3  docs/buildscripts/docs.py
@@ -33,7 +33,8 @@ def gen_sphinx(dir):
subprocess.call(["make", "html"], stdout=null, stderr=null)
os.chdir("../../../")
- os.rename("docs/source/sphinx/build/html", dir)
+ if os.path.isdir("docs/source/sphinx/build/html"):
+ os.rename("docs/source/sphinx/build/html", dir)
def version():
"""Get the driver version from doxygenConfig.
View
2  runtests.sh
@@ -31,8 +31,10 @@ for i in `find . -name test_\*`
do
if [ $valgrind -eq 1 ]
then
+ echo valgrind $i
valgrind $i
else
+ echo $i
$i
fi
View
368 src/bcon.c
@@ -0,0 +1,368 @@
+/* bcon.c */
+
+/* Copyright 2009-2012 10gen 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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+#include "bcon.h"
+
+#ifndef NOT_REACHED
+#define NOT_REACHED 0
+#endif
+
+#define ARRAY_INDEX_BUFFER_SIZE 9
+
+char *bcon_errstr[] = {
+ "OK",
+ "ERROR",
+ "bcon document or nesting incomplete",
+ "bson finish error"
+};
+
+int bcon_error(bson *b, const bcon *bc, size_t i, bcon_error_t err) {
+ b->err = err;
+ b->errstr = bcon_errstr[err];
+ return BCON_ERROR;
+}
+
+bcon_error_t bson_append_bcon_array(bson *b, const bcon *bc);
+
+bcon_token_t bcon_token(char *s) {
+ if (s == 0) return Token_EOD;
+ switch (s[0]) {
+ case ':': if (s[1] != '\0' && s[2] != '\0' && s[3] != '\0' && s[4] == '\0' &&
+ s[3] == ':' && (s[1] == '_' || s[1] == 'P'))
+ return Token_Typespec; break;
+ case '{': if (s[1] == '\0') return Token_OpenBrace; break;
+ case '}': if (s[1] == '\0') return Token_CloseBrace; break;
+ case '[': if (s[1] == '\0') return Token_OpenBracket; break;
+ case ']': if (s[1] == '\0') return Token_CloseBracket; break;
+ case '.': if (s[1] == '\0') return Token_End; break;
+ }
+ return Token_Default;
+}
+
+bcon_error_t bson_bcon_key_value(bson *b, const char *key, const char *typespec, const bcon bci) {
+ bcon_error_t ret = BCON_OK;
+ bson_oid_t oid;
+ char ptype = typespec ? typespec[1] : '_';
+ char utype = typespec ? typespec[2] : '_';
+ switch (ptype) {
+ case '_': /* kv(b, key, utype, bci) */
+ switch (utype) {
+ case '_': /* fall through */
+ case 's': bson_append_string( b, key, bci.s ); break;
+ case 'f': bson_append_double( b, key, bci.f ); break;
+ case 'o': if (*bci.o == '\0') bson_oid_gen( &oid ); else bson_oid_from_string( &oid, bci.o ); bson_append_oid( b, key, &oid ); break;
+ case 'b': bson_append_bool( b, key, bci.b ); break;
+ case 't': bson_append_time_t( b, key, bci.t ); break;
+ case 'v': bson_append_null( b, key ); break; /* void */
+ case 'x': bson_append_symbol( b, key, bci.x ); break;
+ case 'i': bson_append_int( b, key, bci.i ); break;
+ case 'l': bson_append_long( b, key, bci.l ); break;
+ default: printf("\nptype:'%c' utype:'%c'\n", ptype, utype); assert(NOT_REACHED); break;
+ }
+ break;
+ case 'P': /* kpv(b, key, utype, bci) */
+ switch (utype) {
+ /*case '_': */ /* fall through */
+ case 's': bson_append_string( b, key, *bci.Ps ); break;
+ case 'f': bson_append_double( b, key, *bci.Pf ); break;
+ case 'o': if (**bci.Po == '\0') bson_oid_gen( &oid ); else bson_oid_from_string( &oid, *bci.Po ); bson_append_oid( b, key, &oid ); break;
+ case 'b': bson_append_bool( b, key, *bci.Pb ); break;
+ case 't': bson_append_time_t( b, key, *bci.Pt ); break;
+ case 'x': bson_append_symbol( b, key, *bci.Px ); break;
+ case 'i': bson_append_int( b, key, *bci.Pi ); break;
+ case 'l': bson_append_long( b, key, *bci.Pl ); break;
+ case 'D':
+ bson_append_start_object( b, key );
+ ret = bson_append_bcon( b, bci.PD );
+ bson_append_finish_object( b );
+ break;
+ case 'A':
+ bson_append_start_array( b, key );
+ ret = bson_append_bcon_array( b, bci.PA );
+ bson_append_finish_array( b );
+ break;
+ default: printf("\nptype:'%c' utype:'%c'\n", ptype, utype); assert(NOT_REACHED); break;
+ }
+ break;
+ default:
+ printf("\nptype:'%c' utype:'%c'\n", ptype, utype); assert(NOT_REACHED);
+ break;
+ }
+ return ret;
+}
+
+typedef enum bcon_state_t {
+ State_Element, State_DocSpecValue, State_DocValue,
+ State_ArraySpecValue, State_ArrayValue
+} bcon_state_t;
+
+#define DOC_STACK_SIZE 1024
+#define ARRAY_INDEX_STACK_SIZE 1024
+
+#define DOC_PUSH_STATE(return_state) ( doc_stack[doc_stack_pointer++] = (return_state) )
+#define DOC_POP_STATE ( state = doc_stack[--doc_stack_pointer] )
+#define ARRAY_PUSH_RESET_INDEX_STATE(return_state) ( array_index_stack[array_index_stack_pointer++] = array_index, array_index = 0, DOC_PUSH_STATE(return_state) )
+#define ARRAY_POP_INDEX_STATE ( array_index = array_index_stack[--array_index_stack_pointer], DOC_POP_STATE )
+
+#define ARRAY_KEY_STRING(l) (bson_numstr(array_index_buffer, (int)(l)), array_index_buffer)
+
+/*
+ * simplified FSM to parse BCON structure, uses stacks for sub-documents and sub-arrays
+ */
+bcon_error_t bson_append_bcon_with_state(bson *b, const bcon *bc, bcon_state_t start_state) {
+ bcon_error_t ret = BCON_OK;
+ bcon_state_t state = start_state;
+ char *key = 0;
+ char *typespec = 0;
+ unsigned char doc_stack[DOC_STACK_SIZE];
+ size_t doc_stack_pointer = 0;
+ size_t array_index = 0;
+ unsigned int array_index_stack[ARRAY_INDEX_STACK_SIZE];
+ size_t array_index_stack_pointer = 0;
+ char array_index_buffer[ARRAY_INDEX_BUFFER_SIZE]; /* max BSON size */
+ int end_of_data;
+ const bcon *bcp;
+ for (end_of_data = 0, bcp = bc; ret == BCON_OK && !end_of_data; bcp++) {
+ bcon bci = *bcp;
+ char *s = bci.s;
+ switch (state) {
+ case State_Element:
+ switch (bcon_token(s)) {
+ case Token_CloseBrace:
+ bson_append_finish_object( b );
+ DOC_POP_STATE; /* state = ...; */
+ break;
+ case Token_End:
+ end_of_data = 1;
+ break;
+ default:
+ key = s;
+ state = State_DocSpecValue;
+ break;
+ }
+ break;
+ case State_DocSpecValue:
+ switch (bcon_token(s)) {
+ case Token_Typespec:
+ typespec = s;
+ state = State_DocValue;
+ break;
+ case Token_OpenBrace:
+ bson_append_start_object( b, key );
+ DOC_PUSH_STATE(State_Element);
+ state = State_Element;
+ break;
+ case Token_OpenBracket:
+ bson_append_start_array( b, key );
+ ARRAY_PUSH_RESET_INDEX_STATE(State_Element);
+ state = State_ArraySpecValue;
+ break;
+ case Token_End:
+ end_of_data = 1;
+ break;
+ default:
+ ret = bson_bcon_key_value(b, key, typespec, bci);
+ state = State_Element;
+ break;
+ }
+ break;
+ case State_DocValue:
+ ret = bson_bcon_key_value(b, key, typespec, bci);
+ state = State_Element;
+ typespec = 0;
+ break;
+ case State_ArraySpecValue:
+ switch (bcon_token(s)) {
+ case Token_Typespec:
+ typespec = s;
+ state = State_ArrayValue;
+ break;
+ case Token_OpenBrace:
+ key = ARRAY_KEY_STRING(array_index++);
+ bson_append_start_object( b, key );
+ DOC_PUSH_STATE(State_ArraySpecValue);
+ state = State_Element;
+ break;
+ case Token_OpenBracket:
+ key = ARRAY_KEY_STRING(array_index++);
+ bson_append_start_array( b, key );
+ ARRAY_PUSH_RESET_INDEX_STATE(State_ArraySpecValue);
+ /* state = State_ArraySpecValue; */
+ break;
+ case Token_CloseBracket:
+ bson_append_finish_array( b );
+ ARRAY_POP_INDEX_STATE; /* state = ...; */
+ break;
+ case Token_End:
+ end_of_data = 1;
+ break;
+ default:
+ key = ARRAY_KEY_STRING(array_index++);
+ ret = bson_bcon_key_value(b, key, typespec, bci);
+ /* state = State_ArraySpecValue; */
+ break;
+ }
+ break;
+ case State_ArrayValue:
+ key = ARRAY_KEY_STRING(array_index++);
+ ret = bson_bcon_key_value(b, key, typespec, bci);
+ state = State_ArraySpecValue;
+ typespec = 0;
+ break;
+ default: assert(NOT_REACHED); break;
+ }
+ }
+ return state == start_state ? BCON_OK : BCON_DOCUMENT_INCOMPLETE;
+}
+
+bcon_error_t bson_append_bcon(bson *b, const bcon *bc) {
+ return bson_append_bcon_with_state(b, bc, State_Element);
+}
+
+bcon_error_t bson_append_bcon_array(bson *b, const bcon *bc) {
+ return bson_append_bcon_with_state(b, bc, State_ArraySpecValue);
+}
+
+/**
+ * Generate BSON from BCON
+ * @param b a BSON object
+ * @param bc a BCON object
+ * match with bson_destroy
+ */
+bcon_error_t bson_from_bcon(bson *b, const bcon *bc) {
+ bcon_error_t ret = BSON_OK;
+ bson_init( b );
+ ret = bson_append_bcon_with_state( b, bc, State_Element );
+ if (ret != BCON_OK) return ret;
+ ret = bson_finish( b );
+ return ( ret == BSON_OK ? BCON_OK : BCON_BSON_ERROR );
+}
+
+void bcon_print(const bcon *bc) { /* prints internal representation, not JSON */
+ char *typespec = 0;
+ char *delim = "";
+ int end_of_data;
+ bcon *bcp;
+ putchar('{');
+ for (end_of_data = 0, bcp = (bcon*)bc; !end_of_data; bcp++) {
+ bcon bci = *bcp;
+ char *typespec_next = 0;
+ if (typespec) {
+ switch (typespec[1]) {
+ case '_':
+ switch (typespec[2]) {
+ case 's': printf("%s\"%s\"", delim, bci.s); break;
+ case 'f': printf("%s%f", delim, bci.f); break;
+ case 'o': printf("%s\"%s\"", delim, bci.o); break;
+ case 'b': printf("%s%d", delim, bci.b); break;
+ case 't': printf("%s%ld", delim, (long)bci.t); break;
+ case 'v': printf("%s\"%s\"", delim, bci.v); break;
+ case 'x': printf("%s\"%s\"", delim, bci.x); break;
+ case 'i': printf("%s%d", delim, bci.i); break;
+ case 'l': printf("%s%ld", delim, bci.l); break;
+ default: printf("\ntypespec:\"%s\"\n", typespec); assert(NOT_REACHED); break;
+ }
+ break;
+ case 'P':
+ switch (typespec[2]) {
+ case 's': printf("%sPs(0x%lx,\"%s\")", delim, (unsigned long)bci.Ps, *bci.Ps); break;
+ case 'f': printf("%sPf(0x%lx,%f)", delim, (unsigned long)bci.Pf, *bci.Pf); break;
+ case 'o': printf("%sPo(0x%lx,\"%s\")", delim, (unsigned long)bci.Po, *bci.Po); break;
+ case 'b': printf("%sPb(0x%lx,%d)", delim, (unsigned long)bci.Pb, *bci.Pb); break;
+ case 't': printf("%sPt(0x%lx,%ld)", delim, (unsigned long)bci.Pt, (long)*bci.Pt); break;
+ case 'x': printf("%sPx(0x%lx,\"%s\")", delim, (unsigned long)bci.Px, *bci.Px); break;
+ case 'i': printf("%sPi(0x%lx,%d)", delim, (unsigned long)bci.Pi, *bci.Pi); break;
+ case 'l': printf("%sPl(0x%lx,%ld)", delim, (unsigned long)bci.Pl, *bci.Pl); break;
+ case 'D': printf("%sPD(0x%lx,..)", delim, (unsigned long)bci.PD); break;
+ case 'A': printf("%sPA(0x%lx,....)", delim, (unsigned long)bci.PA); break;
+ default: printf("\ntypespec:\"%s\"\n", typespec); assert(NOT_REACHED); break;
+ }
+ break;
+ default:
+ printf("\ntypespec:\"%s\"\n", typespec); assert(NOT_REACHED);
+ break;
+ }
+ }
+ else {
+ char *s = bci.s;
+ switch (s[0]) {
+ case '.':
+ end_of_data = (s[1] == '\0');
+ break;
+ case ':':
+ typespec_next = bcon_token(s) == Token_Typespec ? s : 0;
+ break;
+ }
+ printf("%s\"%s\"", delim, s);
+ }
+ typespec = typespec_next;
+ delim = ",";
+ }
+ putchar('}');
+}
+
+/* TODO - incomplete */
+void bcon_json_print(bcon *bc, int n) {
+ int t = 0;
+ int key_value_count = 0;
+ char *s;
+ int end_of_data;
+ bcon *bcp;
+ putchar('{');
+ for (end_of_data = 0, bcp = bc; !end_of_data; bcp++) {
+ bcon bci = *bcp;
+ switch (t) {
+ case 'l':
+ if (key_value_count & 0x1) putchar(':');
+ printf("%ld", bci.l);
+ t = 0;
+ key_value_count++;
+ break;
+ case 's': /* fall through */
+ default:
+ s = bci.s;
+ switch (*s) {
+ case ':':
+ ++s;
+ t = *++s;
+ break;
+ case '{':
+ if (key_value_count & 0x1) putchar(':');
+ putchar(*s);
+ key_value_count = 0;
+ break;
+ case '}':
+ putchar(*s);
+ key_value_count = 2;
+ break;
+ default:
+ if (key_value_count & 0x1) putchar(':');
+ else if (key_value_count > 1) putchar(',');
+ printf("\"%s\"", s);
+ t = 0;
+ key_value_count++;
+ break;
+ }
+ break;
+ }
+ }
+ putchar('}');
+}
View
322 src/bcon.h
@@ -0,0 +1,322 @@
+/**
+ * @file bcon.h
+ * @brief BCON (BSON C Object Notation) Declarations
+ */
+
+/* Copyright 2009-2012 10gen 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.
+ */
+
+#ifndef BCON_H_
+#define BCON_H_
+
+#include "bson.h"
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+MONGO_EXTERN_C_START
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+/**
+ * BCON - BSON C Object Notation.
+ *
+ * Description
+ * -----------
+ * BCON provides for JSON-like (or BSON-like) initializers in C.
+ * Without this, BSON must be constructed by procedural coding via explicit function calls.
+ * With this, you now have convenient data-driven definition of BSON documents.
+ * Here are a couple of introductory examples.
+ *
+ * bcon hello[] = { "hello", "world", "." };
+ * bcon pi[] = { "pi", BF(3.14159), BEND };
+ *
+ * BCON is an array of bcon union elements with the default type of cstring (char *).
+ * A BCON document must be terminated with a cstring containing a single dot, i.e., ".", or the macro equivalent BEND.
+ *
+ * Cstring literals in double quotes are used for keys as well as for string values.
+ * There is no explicit colon (':') separator between key and value, just a comma,
+ * however it must be explicit or C will quietly concatenate the key and value strings for you.
+ * Readability may be improved by using multiple lines with a key-value pair per line.
+ *
+ * Macros are used to enclose specific types, and an internal type-specifier string prefixes a typed value.
+ * Macros are also used to specify interpolation of values from pointers to specified types.
+ *
+ * Sub-documents are framed by "{" "}" string literals, and sub-arrays are framed by "[" "]" literals.
+ *
+ * All of this is needed because C arrays and initializers are mono-typed unlike dict/array types in modern languages.
+ * BCON attempts to be readable and JSON-like within the context and restrictions of the C language.
+ *
+ * Examples
+ * --------
+ *
+ * bcon goodbye[] = { "hello", "world", "goodbye", "world", "." };
+ * bcon awesome[] = { "BSON", "[", "awesome", BF(5.05), BI(1986), "]", "." };
+ * bcon contact_info[] = {
+ * "firstName", "John",
+ * "lastName" , "Smith",
+ * "age" , BI(25),
+ * "address" ,
+ * "{",
+ * "streetAddress", "21 2nd Street",
+ * "city" , "New York",
+ * "state" , "NY",
+ * "postalCode" , "10021",
+ * "}",
+ * "phoneNumber",
+ * "[",
+ * "{",
+ * "type" , "home",
+ * "number", "212 555-1234",
+ * "}",
+ * "{",
+ * "type" , "fax",
+ * "number", "646 555-4567",
+ * "}",
+ * "]",
+ * BEND
+ * };
+ *
+ * Comparison
+ * ----------
+ *
+ * JSON:
+ * { "BSON" : [ "awesome", 5.05, 1986 ] }
+ *
+ * BCON:
+ * bcon awesome[] = { "BSON", "[", "awesome", BF(5.05), BI(1986), "]", BEND };
+ *
+ * C driver calls:
+ * bson_init( b );
+ * bson_append_start_array( b, "BSON" );
+ * bson_append_string( b, "0", "awesome" );
+ * bson_append_double( b, "1", 5.05 );
+ * bson_append_int( b, "2", 1986 );
+ * bson_append_finish_array( b );
+ * ret = bson_finish( b );
+ * bson_print( b );
+ * bson_destroy( b );
+ *
+ * Peformance
+ * ----------
+ * BCON costs about three times as much as the equivalent bson function calls required to explicitly construct the document.
+ * This is significantly less than the cost of parsing JSON and constructing BSON, and BCON allows value interpolation via pointers.
+ *
+ * Specification
+ * -------------
+ * This specification parallels the BSON specification - http://bsonspec.org/#/specification
+ *
+ * document ::= elist
+ * e_list ::= element e_list
+ * element ::= e_name value
+ * value ::= cstring String
+ * | ":_f:" double Floating point
+ * | ":Pf:" *double *Floating point interpolation
+ * | ":_s" cstring String
+ * | ":Ps" *cstring *String interpolation
+ * | "{" document "}" Embedded document
+ * | ":PD" *document *Embedded document interpolation
+ * | "[" v_list "]" Array
+ * | ":PA" *v_list *Array interpolation
+ * | ":_b:" "\x00" Boolean "false"
+ * | ":_b:" "\x01" Boolean "true"
+ * | ":Pb:" *int *Boolean interpolation
+ * | ":_t:" long UTC datetime
+ * | ":Pt:" *long *UTC datetime interpolation
+ * | ":_v:" "" Null value (empty string arg ignored)
+ * | ":_x:" cstring Symbol
+ * | ":Px:" *cstring *Symbol interpolation
+ * | ":_i:" int 32-bit integer
+ * | ":_i:" *int *32-bit integer
+ * | ":_l:" long 64-bit integer
+ * | ":_l:" *long *64-bit integer
+ * vlist ::= value v_list
+ * | ""
+ * e_name ::= cstring
+ * cstring ::= (byte*) "\x00"
+ *
+ * Notes
+ * -----
+ * Use the BS macro or the ":_s:" type specifier for string to allow string values that collide with type specifiers, braces, or square brackets.
+ */
+
+typedef union bcon {
+ char *s; /**< 02 e_name string UTF-8 string */
+ char **Ps; /**< 02 e_name string UTF-8 string interpolation */
+ double f; /**< 01 e_name double Floating point */
+ double *Pf; /**< 01 e_name double Floating point interpolation */
+ union bcon *PD; /**< 03 e_name document Embedded document interpolation */
+ union bcon *PA; /**< 04 e_name document Array interpolation */
+ char *o; /**< 07 e_name (byte*12) ObjectId */
+ char **Po; /**< 07 e_name (byte*12) ObjectId interpolation */
+ bson_bool_t b; /**< 08 e_name 00 Boolean "false"
+ 08 e_name 01 Boolean "true" */
+ bson_bool_t *Pb; /**< 08 e_name 01 Boolean interpolation */
+ time_t t; /**< 09 e_name int64 UTC datetime */
+ time_t *Pt; /**< 09 e_name int64 UTC datetime interpolation */
+ char *v; /**< 0A e_name Null value */
+ char *x; /**< 0E e_name string Symbol */
+ char **Px; /**< 0E e_name string Symbol interpolation */
+ int i; /**< 10 e_name int32 32-bit Integer */
+ int *Pi; /**< 10 e_name int32 32-bit Integer interpolation */
+ long l; /**< 12 e_name int64 64-bit Integer */
+ long *Pl; /**< 12 e_name int64 64-bit Integer interpolation */
+ /* "{" "}" */ /* 03 e_name document Embedded document */
+ /* "[" "]" */ /* 04 e_name document Array */
+ /* 05 e_name binary Binary data */
+ /* 06 e_name undefined - deprecated */
+ /* 0B e_name cstring cstring Regular expression */
+ /* 0C e_name string (byte*12) DBPointer - Deprecated */
+ /* 0D e_name string JavaScript code */
+ /* 0F e_name code_w_s JavaScript code w/ scope */
+ /* 11 e_name int64 Timestamp */
+ /* FF e_name Min key */
+ /* 7F e_name Max key */
+} bcon;
+
+/** BCON document terminator */
+#define BEND "."
+
+/** BCON internal 02 cstring string type-specifier */
+#define BTS ":_s:"
+/** BCON internal 01 double Floating point type-specifier */
+#define BTF ":_f:"
+/** BCON internal 07 cstring ObjectId type-specifier */
+#define BTO ":_o:"
+/** BCON internal 08 int Boolean type-specifier */
+#define BTB ":_b:"
+/** BCON internal 09 int64 UTC datetime type-specifier */
+#define BTT ":_t:"
+/** BCON internal 0A Null type-specifier */
+#define BTN ":_v:"
+/** BCON internal 0E cstring Symbol type-specifier */
+#define BTX ":_x:"
+/** BCON internal 10 int32 64-bit Integer type-specifier */
+#define BTI ":_i:"
+/** BCON internal 12 int64 64-bit Integer type-specifier */
+#define BTL ":_l:"
+
+/** BCON internal 02 cstring* string interpolation type-specifier */
+#define BTPS ":Ps:"
+/** BCON internal 01 double* Floating point interpolation type-specifier */
+#define BTPF ":Pf:"
+/** BCON internal 07 cstring* ObjectId interpolation type-specifier */
+#define BTPO ":Po:"
+/** BCON internal 08 int* Boolean interpolation type-specifier */
+#define BTPB ":Pb:"
+/** BCON internal 09 int64* UTC datetime interpolation type-specifier */
+#define BTPT ":Pt:"
+/** BCON internal 0E cstring* Symbol interpolation type-specifier */
+#define BTPX ":Px:"
+/** BCON internal 10 int32* 64-bit Integer interpolation type-specifier */
+#define BTPI ":Pi:"
+/** BCON internal 12 int64* 64-bit Integer interpolation type-specifier */
+#define BTPL ":Pl:"
+
+/** BCON internal 03 union bcon * Embedded document interpolation type-specifier */
+#define BTPD ":PD:"
+/** BCON internal 04 union bcon * Array interpolation type-specifier */
+#define BTPA ":PA:"
+
+/** BCON 02 cstring string value */
+#define BS(v) BTS, { .s = (v) }
+/** BCON 01 double Floating point value */
+#define BF(v) BTF, { .f = (v) }
+/** BCON 07 cstring ObjectId value */
+#define BO(v) BTO, { .o = (v) }
+/** BCON 08 int Boolean value */
+#define BB(v) BTB, { .b = (v) }
+/** BCON 09 int64 UTC datetime value */
+#define BT(v) BTT, { .t = (v) }
+/** BCON 0A Null value */
+#define BNULL BTN, { .v = ("") }
+/** BCON 0E cstring Symbol value */
+#define BX(v) BTX, { .x = (v) }
+/** BCON 10 int32 32-bit Integer value */
+#define BI(v) BTI, { .i = (v) }
+/** BCON 12 int64 64-bit Integer value */
+#define BL(v) BTL, { .l = (v) }
+
+/** BCON 02 cstring* string interpolation value */
+#define BPS(v) BTPS, { .Ps = (v) }
+/** BCON 01 double* Floating point interpolation value */
+#define BPF(v) BTPF, { .Pf = (v) }
+/** BCON 07 cstring* ObjectId interpolation value */
+#define BPO(v) BTPO, { .Po = (v) }
+/** BCON 08 int* Boolean interpolation value */
+#define BPB(v) BTPB, { .Pb = (v) }
+/** BCON 09 int64* UTC datetime value */
+#define BPT(v) BTPT, { .Pt = (v) }
+/** BCON 0E cstring* Symbol interpolation value */
+#define BPX(v) BTPX, { .Px = (v) }
+/** BCON 10 int32* 32-bit Integer interpolation value */
+#define BPI(v) BTPI, { .Pi = (v) }
+/** BCON 12 int64* 64-bit Integer interpolation value */
+#define BPL(v) BTPL, { .Pl = (v) }
+/** BCON 03 union bcon * Embedded document interpolation value */
+#define BPD(v) BTPD, { .PD = (v) }
+/** BCON 04 union bcon * Array interpolation value */
+#define BPA(v) BTPA, { .PA = (v) }
+
+/*
+ * References on codes used for types
+ * http://en.wikipedia.org/wiki/Name_mangling
+ * http://www.agner.org/optimize/calling_conventions.pdf (page 25)
+ */
+
+typedef enum bcon_error_t {
+ BCON_OK = 0, /**< OK return code */
+ BCON_ERROR, /**< ERROR return code */
+ BCON_DOCUMENT_INCOMPLETE, /**< bcon document or nesting incomplete */
+ BCON_BSON_ERROR /**< bson finish error */
+} bcon_error_t;
+
+extern char *bcon_errstr[]; /**< bcon_error_t text messages */
+
+/**
+ * Append a BCON object to a BSON object.
+ *
+ * @param b a BSON object
+ * @param bc a BCON object
+ */
+MONGO_EXPORT bcon_error_t bson_append_bcon(bson *b, const bcon *bc);
+
+/**
+ * Generate a BSON object from a BCON object.
+ *
+ * @param b a BSON object
+ * @param bc a BCON object
+ */
+MONGO_EXPORT bcon_error_t bson_from_bcon( bson *b, const bcon *bc );
+
+/**
+ * Print a string representation of a BCON object.
+ *
+ * @param bc the BCON object to print.
+ */
+MONGO_EXPORT void bcon_print( const bcon *bc );
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+MONGO_EXTERN_C_END
+
+typedef enum bcon_token_t {
+ Token_Default, Token_End, Token_Typespec,
+ Token_OpenBrace, Token_CloseBrace, Token_OpenBracket, Token_CloseBracket,
+ Token_EOD
+} bcon_token_t;
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif
View
5 src/bson.c
@@ -782,9 +782,10 @@ MONGO_EXPORT int bson_append_code_n( bson *b, const char *name, const char *valu
MONGO_EXPORT int bson_append_code_w_scope_n( bson *b, const char *name,
const char *code, int len, const bson *scope ) {
+ int sl, size;
if ( !scope ) return BSON_ERROR;
- int sl = len + 1;
- int size = 4 + 4 + sl + bson_size( scope );
+ sl = len + 1;
+ size = 4 + 4 + sl + bson_size( scope );
if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR )
return BSON_ERROR;
bson_append32( b, &size );
View
301 test/bcon_test.c
@@ -0,0 +1,301 @@
+/* bcon_test.c */
+
+/* Copyright 2009-2012 10gen 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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+#include "bcon.h"
+
+int verbose = 0;
+
+int bcon_token(char *s);
+
+void test_bcon_token() {
+ assert(Token_Default == bcon_token(":_i:X"));
+ assert(Token_Typespec == bcon_token(":_i:"));
+ assert(Token_OpenBrace == bcon_token("{"));
+ assert(Token_CloseBrace == bcon_token("}"));
+ assert(Token_OpenBracket == bcon_token("["));
+ assert(Token_CloseBracket == bcon_token("]"));
+ assert(Token_EOD == bcon_token(0));
+}
+
+void test_bson_from_bcon(const bcon *bc, bcon_error_t bc_err, int bv_err ) {
+ bcon_error_t ret;
+ bson b[1];
+ if ( verbose ) { putchar('\t'); bcon_print(bc); putchar('\n'); }
+ ret = bson_from_bcon( b, bc );
+ if (ret != bc_err) {
+ printf("test_bson_from_bcon ret:%d(%s) != bc_err:%d(%s)\n", ret, bcon_errstr[ret], bc_err, bcon_errstr[bc_err]);
+ }
+ assert( ret == bc_err );
+ assert( b->err == bv_err );
+ if ( verbose )
+ bson_print(b);
+ bson_destroy( b );
+}
+
+void test_basic_types() {
+ bcon basic_types[] = {"string", BS("a string"), "f(double)", BF(3.14159), "boolean", BB(1), "time", BT(time(0)), "null", BNULL, "symbol", BX("a symbol"), "int", BI(123), "long", BL(456789L), BEND};
+ test_bson_from_bcon( basic_types, BCON_OK, BSON_VALID );
+}
+
+void test_basic_interpolation() {
+ char *s = "a_string";
+ double f = 3.14159;
+ bson_bool_t bb = 1;
+ time_t t = time(0);
+ char *x = "a symbol";
+ int i = 123;
+ long l = 456789L;
+ bcon basic_interpolation[] = {"string", BPS(&s), "f(double)", BPF(&f), "boolean", BPB(&bb), "time", BPT(&t), "symbol", BPX(&x), "int", BPI(&i), "long", BPL(&l), BEND};
+ test_bson_from_bcon( basic_interpolation, BCON_OK, BSON_VALID );
+}
+
+void test_oid_and_interpolation() {
+ char *oid_s = "010203040506070809101112";
+ bcon oid_bc[] = { "_id", BO(""), "user_id", BO("010203040506070809101112"), "admin_id", BPO(&oid_s), BEND };
+ if ( verbose ) { putchar('\t'); bcon_print( oid_bc ); putchar('\n'); }
+ test_bson_from_bcon( oid_bc, BCON_OK, BSON_VALID );;
+}
+
+void test_invalid_structure() {
+ bcon bc_incomplete[] = { "k0", BEND };
+ test_bson_from_bcon( bc_incomplete, BCON_DOCUMENT_INCOMPLETE, BSON_VALID );
+}
+
+void test_problematic_structure() {
+ bcon bc_incomplete[] = { "k0", BEND };
+ test_bson_from_bcon( bc_incomplete, BCON_DOCUMENT_INCOMPLETE, BSON_VALID );
+ bcon bc_bracket_brace[] = { "k0", "v0", "k1", "{", "k11", "v11", "]", "v12", "}", BEND };
+ test_bson_from_bcon( bc_bracket_brace, BCON_OK, BSON_VALID ); /* key for now */
+ bcon bc_brace_bracket[] = { "k0", "v0", "k1", "[", "k11", "v11", "}", "]", BEND };
+ test_bson_from_bcon( bc_brace_bracket, BCON_OK, BSON_VALID ); /* key for now */
+}
+
+void test_valid_structure() {
+ bcon bc_key_value[] = { "k0", "v0", BEND };
+ test_bson_from_bcon( bc_key_value, BCON_OK, BSON_VALID );
+ bcon bc_key_spec_value[] = { "k0", ":_s:", "v0", BEND };
+ test_bson_from_bcon( bc_key_spec_value, BCON_OK, BSON_VALID );
+ bcon bc_key_value_2[] = { "k0", "v0", "k1", "v1", BEND };
+ test_bson_from_bcon( bc_key_value_2, BCON_OK, BSON_VALID );
+ bcon bc_embedded[] = { "k0", "v0", "k1", "{", "k10", "v10", "k11", "v11", "}", "k2", "v2", BEND };
+ test_bson_from_bcon( bc_embedded, BCON_OK, BSON_VALID );
+ bcon bc_embedded_2[] = { "k0", "v0", "k1", "{", "k10", "v10", "k11", "{", "k110", "v110", "}", "k12", "v12", "}", "k2", "v2", BEND };
+ test_bson_from_bcon( bc_embedded_2, BCON_OK, BSON_VALID );
+ bcon bc_array[] = { "k0", "v0", "k1", "[", "v10", "v11", "v12", "]", "k2", "v2", BEND };
+ test_bson_from_bcon( bc_array, BCON_OK, BSON_VALID );
+ bcon bc_array_with_type[] = { "k0", "v0", "k1", "[", "v10", BI(123), BL(456789), "v12", "]", "k2", "v2", BEND };
+ test_bson_from_bcon( bc_array_with_type, BCON_OK, BSON_VALID );
+ bcon bc_array_2[] = { "k0", "v0", "k1", "[", "v10", "v11", "[", "v120", "v121", "]", "v13", "]", "k2", "v2", BEND };
+ test_bson_from_bcon( bc_array_2, BCON_OK, BSON_VALID );
+ bcon bc_doc_array[] = { "k0", "v0", "k1", "{", "k10", "v10", "k11", "[", "v110", "v111", "]", "k12", "v12", "}", "k2", "v2", BEND };
+ test_bson_from_bcon( bc_doc_array, BCON_OK, BSON_VALID );
+ bcon bc_array_doc[] = { "k0", "v0", "k1", "[", "v10", "v11", "{", "k120", "v120", "k121", "v121", "}", "v13", "]", "k2", "v2", BEND };
+ test_bson_from_bcon( bc_array_doc, BCON_OK, BSON_VALID );
+}
+
+void test_high_order_interpolation() {
+ bcon bc_child_doc[] = { "k10", "v10", "k11", "v11", BEND };
+ bcon bc_parent_doc[] = { "k0", "v0", "k1", BPD(bc_child_doc), "k2", "v2", BEND };
+ test_bson_from_bcon( bc_parent_doc, BCON_OK, BSON_VALID );
+ bcon bc_child_array[] = { "k10", "v10", "k11", "v11", BEND };
+ bcon bc_parent_doc_array[] = { "k0", "v0", "k1", BPA(bc_child_array), "k2", "v2", BEND };
+ test_bson_from_bcon( bc_parent_doc_array, BCON_OK, BSON_VALID );
+}
+
+void test_example_hello_world() {
+ bcon_error_t ret;
+ bson b[1];
+
+ /* JSON {"hello": "world"} */
+
+ bcon hello[] = {"hello", "world", BEND};
+ test_bson_from_bcon( hello, BCON_OK, BSON_VALID );
+
+ if ( verbose )
+ printf("\t--------\n");
+
+ bson_init( b );
+ bson_append_string( b, "hello", "world" );
+ ret = bson_finish( b );
+ if ( verbose )
+ bson_print( b );
+ bson_destroy( b );
+}
+
+void test_example_awesome() {
+ bcon_error_t ret;
+ bson b[1];
+
+ /* JSON {"BSON": ["awesome", 5.05, 1986]} */
+
+ bcon awesome[] = { "BSON", "[", "awesome", BF(5.05), BI(1986), "]", BEND };
+ test_bson_from_bcon( awesome, BCON_OK, BSON_VALID );
+
+ if (verbose )
+ printf("\t--------\n");
+
+ bson_init( b );
+ bson_append_start_array( b, "BSON" );
+ bson_append_string( b, "0", "awesome" );
+ bson_append_double( b, "1", 5.05 );
+ bson_append_int( b, "2", 1986 );
+ bson_append_finish_array( b );
+ ret = bson_finish( b );
+ if ( verbose )
+ bson_print( b );
+ bson_destroy( b );
+}
+
+void test_example_wikipedia_bcon(size_t iterations) {
+ bcon_error_t ret;
+ size_t i;
+ bson b[1];
+ bcon wikipedia[] = {
+ "firstName", "John",
+ "lastName" , "Smith",
+ "age" , BI(25),
+ "address" ,
+ "{",
+ "streetAddress", "21 2nd Street",
+ "city" , "New York",
+ "state" , "NY",
+ "postalCode" , "10021",
+ "}",
+ "phoneNumber",
+ "[",
+ "{",
+ "type" , "home",
+ "number", "212 555-1234",
+ "}",
+ "{",
+ "type" , "fax",
+ "number", "646 555-4567",
+ "}",
+ "]",
+ BEND
+ };
+ for (i = 0; i < iterations; i++) {
+ ret = bson_from_bcon( b, wikipedia );
+ bson_destroy( b );
+ }
+ assert(ret == BCON_OK);
+}
+
+void test_example_wikipedia_bson(size_t iterations) {
+ bcon_error_t ret;
+ size_t i;
+ bson b[1];
+ for (i = 0; i < iterations; i++) {
+ bson_init( b );
+ bson_append_string( b, "firstName", "John" );
+ bson_append_string( b, "lastName" , "Smith" );
+ bson_append_int( b, "age" , 25);
+ bson_append_start_object( b, "address" );
+ bson_append_string( b, "streetAddress", "21 2nd Street" );
+ bson_append_string( b, "city" , "New York" );
+ bson_append_string( b, "state" , "NY" );
+ bson_append_string( b, "postalCode" , "10021" );
+ bson_append_finish_object( b );
+ bson_append_start_array( b, "phoneNumber" );
+ bson_append_start_object( b, "0" );
+ bson_append_string( b, "type" , "home" );
+ bson_append_string( b, "number", "212 555-1234" );
+ bson_append_finish_object( b );
+ bson_append_start_object( b, "1" );
+ bson_append_string( b, "type" , "fax" );
+ bson_append_string( b, "number", "646 555-4567" );
+ bson_append_finish_object( b );
+ bson_append_finish_array( b );
+ ret = bson_finish( b );
+ bson_destroy( b );
+ }
+ assert(ret == BSON_OK);
+}
+
+void test_example_wikipedia() {
+ bson b[1];
+ /*
+ http://en.wikipedia.org/wiki/JSON
+ {
+ "firstName": "John",
+ "lastName" : "Smith",
+ "age" : 25,
+ "address" :
+ {
+ "streetAddress": "21 2nd Street",
+ "city" : "New York",
+ "state" : "NY",
+ "postalCode" : "10021"
+ },
+ "phoneNumber":
+ [
+ {
+ "type" : "home",
+ "number": "212 555-1234"
+ },
+ {
+ "type" : "fax",
+ "number": "646 555-4567"
+ }
+ ]
+ }
+ */
+/*
+ extern char *benchmark_report_delim;
+ benchmark_report_delim = "";
+ benchmark(stdout, "test_example_wikipedia_bcon", test_example_wikipedia_bcon, 1, 1, 1.0, 10);
+ printf("\n\t--------\n");
+ benchmark_report_delim = "";
+ benchmark(stdout, "test_example_wikipedia_bson", test_example_wikipedia_bson, 1, 1, 1.0, 10);
+ putchar('\n');
+ */
+}
+
+#define NAME_VALUE(x) { #x, x }
+
+struct test_suite {
+ char *name;
+ void (*fn)();
+} test_suite[] = {
+ NAME_VALUE(test_bcon_token),
+ NAME_VALUE(test_basic_types),
+ NAME_VALUE(test_basic_interpolation),
+ NAME_VALUE(test_oid_and_interpolation),
+ NAME_VALUE(test_invalid_structure),
+ NAME_VALUE(test_valid_structure),
+ NAME_VALUE(test_problematic_structure),
+ NAME_VALUE(test_high_order_interpolation),
+ NAME_VALUE(test_example_hello_world),
+ NAME_VALUE(test_example_awesome),
+ /* NAME_VALUE(test_example_wikipedia), */
+};
+
+int main(int argc, char **argv) {
+ int i;
+ if (argc > 1)
+ verbose = 1;
+ for (i = 0; i < sizeof(test_suite)/sizeof(struct test_suite); i++) {
+ if ( verbose )
+ printf("%s:\n", test_suite[i].name);
+ (*test_suite[i].fn)();
+ }
+ return 0;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.