diff options
Diffstat (limited to 'src/bson.c')
-rw-r--r-- | src/bson.c | 1251 |
1 files changed, 1251 insertions, 0 deletions
diff --git a/src/bson.c b/src/bson.c new file mode 100644 index 0000000..845f6de --- /dev/null +++ b/src/bson.c @@ -0,0 +1,1251 @@ +/* bson.c - libmongo-client's BSON implementation + * Copyright 2011, 2012 Gergely Nagy <algernon@balabit.hu> + * + * 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. + */ + +/** @file src/bson.c + * Implementation of the BSON API. + */ + +#include <glib.h> +#include <errno.h> +#include <string.h> +#include <stdarg.h> + +#include "bson.h" +#include "libmongo-macros.h" +#include "libmongo-private.h" + +/** @internal BSON cursor structure. + */ +struct _bson_cursor +{ + const bson *obj; /**< The BSON object this is a cursor for. */ + const gchar *key; /**< Pointer within the BSON object to the + current key. */ + size_t pos; /**< Position within the BSON object, pointing at the + element type. */ + size_t value_pos; /**< The start of the value within the BSON + object, pointing right after the end of the + key. */ +}; + +/** @internal Append a byte to a BSON stream. + * + * @param b is the BSON stream to append to. + * @param byte is the byte to append. + */ +static inline void +_bson_append_byte (bson *b, const guint8 byte) +{ + b->data = g_byte_array_append (b->data, &byte, sizeof (byte)); +} + +/** @internal Append a 32-bit integer to a BSON stream. + * + * @param b is the BSON stream to append to. + * @param i is the integer to append. + */ +static inline void +_bson_append_int32 (bson *b, const gint32 i) +{ + b->data = g_byte_array_append (b->data, (const guint8 *)&i, sizeof (gint32)); +} + +/** @internal Append a 64-bit integer to a BSON stream. + * + * @param b is the BSON stream to append to. + * @param i is the integer to append. + */ +static inline void +_bson_append_int64 (bson *b, const gint64 i) +{ + b->data = g_byte_array_append (b->data, (const guint8 *)&i, sizeof (gint64)); +} + +/** @internal Append an element header to a BSON stream. + * + * The element header is a single byte, signaling the type of the + * element, followed by a NULL-terminated C string: the key (element) + * name. + * + * @param b is the BSON object to append to. + * @param type is the element type to append. + * @param name is the key name. + * + * @returns TRUE on success, FALSE otherwise. + */ +static inline gboolean +_bson_append_element_header (bson *b, bson_type type, const gchar *name) +{ + if (!name || !b) + return FALSE; + + if (b->finished) + return FALSE; + + _bson_append_byte (b, (guint8) type); + b->data = g_byte_array_append (b->data, (const guint8 *)name, + strlen (name) + 1); + + return TRUE; +} + +/** @internal Append a string-like element to a BSON object. + * + * There are a few string-like elements in the BSON spec that differ + * only in type, not in structure. This convenience function is used + * to append them with the appropriate type. + * + * @param b is the BSON object to append to. + * @param type is the string-like type to append. + * @param name is the key name. + * @param val is the value to append. + * @param length is the length of the value. + * + * @note Passing @a -1 as length will use the full length of @a + * val. + * + * @returns TRUE on success, FALSE otherwise. + */ +static gboolean +_bson_append_string_element (bson *b, bson_type type, const gchar *name, + const gchar *val, gint32 length) +{ + size_t len; + + if (!val || !length || length < -1) + return FALSE; + + len = (length != -1) ? (size_t)length + 1: strlen (val) + 1; + + if (!_bson_append_element_header (b, type, name)) + return FALSE; + + _bson_append_int32 (b, GINT32_TO_LE (len)); + + b->data = g_byte_array_append (b->data, (const guint8 *)val, len - 1); + _bson_append_byte (b, 0); + + return TRUE; +} + +/** @internal Append a document-like element to a BSON object. + * + * Arrays and documents are both similar, and differ very little: + * different type, and arrays have restrictions on key names (which + * are not enforced by this library). + * + * This convenience function can append both types. + * + * @param b is the BSON object to append to. + * @param type is the document-like type to append. + * @param name is the key name. + * @param doc is the document-like object to append. + * + * @note The @a doc must be a finished BSON object. + * + * @returns TRUE on success, FALSE otherwise. + */ +static gboolean +_bson_append_document_element (bson *b, bson_type type, const gchar *name, + const bson *doc) +{ + if (bson_size (doc) < 0) + return FALSE; + + if (!_bson_append_element_header (b, type, name)) + return FALSE; + + b->data = g_byte_array_append (b->data, bson_data (doc), bson_size (doc)); + return TRUE; +} + +/** @internal Append a 64-bit integer to a BSON object. + * + * @param b is the BSON object to append to. + * @param type is the int64-like type to append. + * @param name is the key name. + * @param i is the 64-bit value to append. + * + * @returns TRUE on success, FALSE otherwise. + */ +static inline gboolean +_bson_append_int64_element (bson *b, bson_type type, const gchar *name, + gint64 i) +{ + if (!_bson_append_element_header (b, type, name)) + return FALSE; + + _bson_append_int64 (b, GINT64_TO_LE (i)); + return TRUE; +} + +/******************** + * Public interface * + ********************/ + +const gchar * +bson_type_as_string (bson_type type) +{ + switch (type) + { + case BSON_TYPE_NONE: + return "BSON_TYPE_NONE"; + case BSON_TYPE_DOUBLE: + return "BSON_TYPE_DOUBLE"; + case BSON_TYPE_STRING: + return "BSON_TYPE_STRING"; + case BSON_TYPE_DOCUMENT: + return "BSON_TYPE_DOCUMENT"; + case BSON_TYPE_ARRAY: + return "BSON_TYPE_ARRAY"; + case BSON_TYPE_BINARY: + return "BSON_TYPE_BINARY"; + case BSON_TYPE_UNDEFINED: + return "BSON_TYPE_UNDEFINED"; + case BSON_TYPE_OID: + return "BSON_TYPE_OID"; + case BSON_TYPE_BOOLEAN: + return "BSON_TYPE_BOOLEAN"; + case BSON_TYPE_UTC_DATETIME: + return "BSON_TYPE_UTC_DATETIME"; + case BSON_TYPE_NULL: + return "BSON_TYPE_NULL"; + case BSON_TYPE_REGEXP: + return "BSON_TYPE_REGEXP"; + case BSON_TYPE_DBPOINTER: + return "BSON_TYPE_DBPOINTER"; + case BSON_TYPE_JS_CODE: + return "BSON_TYPE_JS_CODE"; + case BSON_TYPE_SYMBOL: + return "BSON_TYPE_SYMBOL"; + case BSON_TYPE_JS_CODE_W_SCOPE: + return "BSON_TYPE_JS_CODE_W_SCOPE"; + case BSON_TYPE_INT32: + return "BSON_TYPE_INT32"; + case BSON_TYPE_TIMESTAMP: + return "BSON_TYPE_TIMESTAMP"; + case BSON_TYPE_INT64: + return "BSON_TYPE_INT64"; + case BSON_TYPE_MIN: + return "BSON_TYPE_MIN"; + case BSON_TYPE_MAX: + return "BSON_TYPE_MAX"; + default: + return NULL; + } +} + +bson * +bson_new (void) +{ + return bson_new_sized (0); +} + +bson * +bson_new_sized (gint32 size) +{ + bson *b = g_new0 (bson, 1); + + b->data = g_byte_array_sized_new (size + sizeof (gint32) + sizeof (guint8)); + _bson_append_int32 (b, 0); + + return b; +} + +bson * +bson_new_from_data (const guint8 *data, gint32 size) +{ + bson *b; + + if (!data || size <= 0) + return NULL; + + b = g_new0 (bson, 1); + b->data = g_byte_array_sized_new (size + sizeof (guint8)); + b->data = g_byte_array_append (b->data, data, size); + + return b; +} + +/** @internal Add a single element of any type to a BSON object. + * + * Used internally by bson_build() and bson_build_full(), this + * function adds a single element of any supported type to the target + * BSON object. + * + * @param b is the target BSON object. + * @param type is the element type to add. + * @param name is the key name. + * @param free_after signals whether to free the values after adding + * them. + * @param ap is the list of remaining parameters. + * + * @returns TRUE in @a single_result on success, FALSE otherwise. + */ +#define _bson_build_add_single(b,type,name,free_after,ap) \ + { \ + single_result = TRUE; \ + switch (type) \ + { \ + case BSON_TYPE_NONE: \ + case BSON_TYPE_UNDEFINED: \ + case BSON_TYPE_DBPOINTER: \ + single_result = FALSE; \ + break; \ + case BSON_TYPE_MIN: \ + case BSON_TYPE_MAX: \ + default: \ + single_result = FALSE; \ + break; \ + case BSON_TYPE_DOUBLE: \ + { \ + gdouble d = (gdouble)va_arg (ap, gdouble); \ + bson_append_double (b, name, d); \ + break; \ + } \ + case BSON_TYPE_STRING: \ + { \ + gchar *s = (gchar *)va_arg (ap, gpointer); \ + gint32 l = (gint32)va_arg (ap, gint32); \ + bson_append_string (b, name, s, l); \ + if (free_after) \ + g_free (s); \ + break; \ + } \ + case BSON_TYPE_DOCUMENT: \ + { \ + bson *d = (bson *)va_arg (ap, gpointer); \ + if (free_after && bson_size (d) < 0) \ + bson_finish (d); \ + bson_append_document (b, name, d); \ + if (free_after) \ + bson_free (d); \ + break; \ + } \ + case BSON_TYPE_ARRAY: \ + { \ + bson *d = (bson *)va_arg (ap, gpointer); \ + if (free_after && bson_size (d) < 0) \ + bson_finish (d); \ + bson_append_array (b, name, d); \ + if (free_after) \ + bson_free (d); \ + break; \ + } \ + case BSON_TYPE_BINARY: \ + { \ + bson_binary_subtype s = \ + (bson_binary_subtype)va_arg (ap, guint); \ + guint8 *d = (guint8 *)va_arg (ap, gpointer); \ + gint32 l = (gint32)va_arg (ap, gint32); \ + bson_append_binary (b, name, s, d, l); \ + if (free_after) \ + g_free (d); \ + break; \ + } \ + case BSON_TYPE_OID: \ + { \ + guint8 *oid = (guint8 *)va_arg (ap, gpointer); \ + bson_append_oid (b, name, oid); \ + if (free_after) \ + g_free (oid); \ + break; \ + } \ + case BSON_TYPE_BOOLEAN: \ + { \ + gboolean v = (gboolean)va_arg (ap, guint); \ + bson_append_boolean (b, name, v); \ + break; \ + } \ + case BSON_TYPE_UTC_DATETIME: \ + { \ + gint64 ts = (gint64)va_arg (ap, gint64); \ + bson_append_utc_datetime (b, name, ts); \ + break; \ + } \ + case BSON_TYPE_NULL: \ + { \ + bson_append_null (b, name); \ + break; \ + } \ + case BSON_TYPE_REGEXP: \ + { \ + gchar *r = (gchar *)va_arg (ap, gpointer); \ + gchar *o = (gchar *)va_arg (ap, gpointer); \ + bson_append_regex (b, name, r, o); \ + if (free_after) \ + { \ + g_free (r); \ + g_free (o); \ + } \ + break; \ + } \ + case BSON_TYPE_JS_CODE: \ + { \ + gchar *s = (gchar *)va_arg (ap, gpointer); \ + gint32 l = (gint32)va_arg (ap, gint32); \ + bson_append_javascript (b, name, s, l); \ + if (free_after) \ + g_free (s); \ + break; \ + } \ + case BSON_TYPE_SYMBOL: \ + { \ + gchar *s = (gchar *)va_arg (ap, gpointer); \ + gint32 l = (gint32)va_arg (ap, gint32); \ + bson_append_symbol (b, name, s, l); \ + if (free_after) \ + g_free (s); \ + break; \ + } \ + case BSON_TYPE_JS_CODE_W_SCOPE: \ + { \ + gchar *s = (gchar *)va_arg (ap, gpointer); \ + gint32 l = (gint32)va_arg (ap, gint32); \ + bson *scope = (bson *)va_arg (ap, gpointer); \ + if (free_after && bson_size (scope) < 0) \ + bson_finish (scope); \ + bson_append_javascript_w_scope (b, name, s, l, scope); \ + if (free_after) \ + bson_free (scope); \ + break; \ + } \ + case BSON_TYPE_INT32: \ + { \ + gint32 l = (gint32)va_arg (ap, gint32); \ + bson_append_int32 (b, name, l); \ + break; \ + } \ + case BSON_TYPE_TIMESTAMP: \ + { \ + gint64 ts = (gint64)va_arg (ap, gint64); \ + bson_append_timestamp (b, name, ts); \ + break; \ + } \ + case BSON_TYPE_INT64: \ + { \ + gint64 l = (gint64)va_arg (ap, gint64); \ + bson_append_int64 (b, name, l); \ + break; \ + } \ + } \ + } + +bson * +bson_build (bson_type type, const gchar *name, ...) +{ + va_list ap; + bson_type t; + const gchar *n; + bson *b; + gboolean single_result; + + b = bson_new (); + va_start (ap, name); + _bson_build_add_single (b, type, name, FALSE, ap); + + if (!single_result) + { + bson_free (b); + va_end (ap); + return NULL; + } + + while ((t = (bson_type)va_arg (ap, gint))) + { + n = (const gchar *)va_arg (ap, gpointer); + _bson_build_add_single (b, t, n, FALSE, ap); + if (!single_result) + { + bson_free (b); + va_end (ap); + return NULL; + } + } + va_end (ap); + + return b; +} + +bson * +bson_build_full (bson_type type, const gchar *name, gboolean free_after, ...) +{ + va_list ap; + bson_type t; + const gchar *n; + gboolean f; + bson *b; + gboolean single_result; + + b = bson_new (); + va_start (ap, free_after); + _bson_build_add_single (b, type, name, free_after, ap); + if (!single_result) + { + bson_free (b); + va_end (ap); + return NULL; + } + + while ((t = (bson_type)va_arg (ap, gint))) + { + n = (const gchar *)va_arg (ap, gpointer); + f = (gboolean)va_arg (ap, gint); + _bson_build_add_single (b, t, n, f, ap); + if (!single_result) + { + bson_free (b); + va_end (ap); + return NULL; + } + } + va_end (ap); + + return b; +} + +gboolean +bson_finish (bson *b) +{ + gint32 *i; + + if (!b) + return FALSE; + + if (b->finished) + return TRUE; + + _bson_append_byte (b, 0); + + i = (gint32 *) (&b->data->data[0]); + *i = GINT32_TO_LE ((gint32) (b->data->len)); + + b->finished = TRUE; + + return TRUE; +} + +gint32 +bson_size (const bson *b) +{ + if (!b) + return -1; + + if (b->finished) + return b->data->len; + else + return -1; +} + +const guint8 * +bson_data (const bson *b) +{ + if (!b) + return NULL; + + if (b->finished) + return b->data->data; + else + return NULL; +} + +gboolean +bson_reset (bson *b) +{ + if (!b) + return FALSE; + + b->finished = FALSE; + g_byte_array_set_size (b->data, 0); + _bson_append_int32 (b, 0); + + return TRUE; +} + +void +bson_free (bson *b) +{ + if (!b) + return; + + if (b->data) + g_byte_array_free (b->data, TRUE); + g_free (b); +} + +gboolean +bson_validate_key (const gchar *key, gboolean forbid_dots, + gboolean no_dollar) +{ + if (!key) + { + errno = EINVAL; + return FALSE; + } + errno = 0; + + if (no_dollar && key[0] == '$') + return FALSE; + + if (forbid_dots && strchr (key, '.') != NULL) + return FALSE; + + return TRUE; +} + +/* + * Append elements + */ + +gboolean +bson_append_double (bson *b, const gchar *name, gdouble val) +{ + gdouble d = GDOUBLE_TO_LE (val); + + if (!_bson_append_element_header (b, BSON_TYPE_DOUBLE, name)) + return FALSE; + + b->data = g_byte_array_append (b->data, (const guint8 *)&d, sizeof (val)); + return TRUE; +} + +gboolean +bson_append_string (bson *b, const gchar *name, const gchar *val, + gint32 length) +{ + return _bson_append_string_element (b, BSON_TYPE_STRING, name, val, length); +} + +gboolean +bson_append_document (bson *b, const gchar *name, const bson *doc) +{ + return _bson_append_document_element (b, BSON_TYPE_DOCUMENT, name, doc); +} + +gboolean +bson_append_array (bson *b, const gchar *name, const bson *array) +{ + return _bson_append_document_element (b, BSON_TYPE_ARRAY, name, array); +} + +gboolean +bson_append_binary (bson *b, const gchar *name, bson_binary_subtype subtype, + const guint8 *data, gint32 size) +{ + if (!data || !size || size <= 0) + return FALSE; + + if (!_bson_append_element_header (b, BSON_TYPE_BINARY, name)) + return FALSE; + + _bson_append_int32 (b, GINT32_TO_LE (size)); + _bson_append_byte (b, (guint8)subtype); + + b->data = g_byte_array_append (b->data, data, size); + return TRUE; +} + +gboolean +bson_append_oid (bson *b, const gchar *name, const guint8 *oid) +{ + if (!oid) + return FALSE; + + if (!_bson_append_element_header (b, BSON_TYPE_OID, name)) + return FALSE; + + b->data = g_byte_array_append (b->data, oid, 12); + return TRUE; +} + +gboolean +bson_append_boolean (bson *b, const gchar *name, gboolean value) +{ + if (!_bson_append_element_header (b, BSON_TYPE_BOOLEAN, name)) + return FALSE; + + _bson_append_byte (b, (guint8)value); + return TRUE; +} + +gboolean +bson_append_utc_datetime (bson *b, const gchar *name, gint64 ts) +{ + return _bson_append_int64_element (b, BSON_TYPE_UTC_DATETIME, name, ts); +} + +gboolean +bson_append_null (bson *b, const gchar *name) +{ + return _bson_append_element_header (b, BSON_TYPE_NULL, name); +} + +gboolean +bson_append_regex (bson *b, const gchar *name, const gchar *regexp, + const gchar *options) +{ + if (!regexp || !options) + return FALSE; + + if (!_bson_append_element_header (b, BSON_TYPE_REGEXP, name)) + return FALSE; + + b->data = g_byte_array_append (b->data, (const guint8 *)regexp, + strlen (regexp) + 1); + b->data = g_byte_array_append (b->data, (const guint8 *)options, + strlen (options) + 1); + + return TRUE; +} + +gboolean +bson_append_javascript (bson *b, const gchar *name, const gchar *js, + gint32 len) +{ + return _bson_append_string_element (b, BSON_TYPE_JS_CODE, name, js, len); +} + +gboolean +bson_append_symbol (bson *b, const gchar *name, const gchar *symbol, + gint32 len) +{ + return _bson_append_string_element (b, BSON_TYPE_SYMBOL, name, symbol, len); +} + +gboolean +bson_append_javascript_w_scope (bson *b, const gchar *name, + const gchar *js, gint32 len, + const bson *scope) +{ + gint size; + size_t length; + + if (!js || !scope || bson_size (scope) < 0 || len < -1) + return FALSE; + + if (!_bson_append_element_header (b, BSON_TYPE_JS_CODE_W_SCOPE, name)) + return FALSE; + + length = (len != -1) ? (size_t)len + 1: strlen (js) + 1; + + size = length + sizeof (gint32) + sizeof (gint32) + bson_size (scope); + + _bson_append_int32 (b, GINT32_TO_LE (size)); + + /* Append the JS code */ + _bson_append_int32 (b, GINT32_TO_LE (length)); + b->data = g_byte_array_append (b->data, (const guint8 *)js, length - 1); + _bson_append_byte (b, 0); + + /* Append the scope */ + b->data = g_byte_array_append (b->data, bson_data (scope), + bson_size (scope)); + + return TRUE; +} + +gboolean +bson_append_int32 (bson *b, const gchar *name, gint32 i) +{ + if (!_bson_append_element_header (b, BSON_TYPE_INT32, name)) + return FALSE; + + _bson_append_int32 (b, GINT32_TO_LE (i)); + return TRUE; + } + +gboolean +bson_append_timestamp (bson *b, const gchar *name, gint64 ts) +{ + return _bson_append_int64_element (b, BSON_TYPE_TIMESTAMP, name, ts); +} + +gboolean +bson_append_int64 (bson *b, const gchar *name, gint64 i) +{ + return _bson_append_int64_element (b, BSON_TYPE_INT64, name, i); +} + +/* + * Find & retrieve data + */ +bson_cursor * +bson_cursor_new (const bson *b) +{ + bson_cursor *c; + + if (bson_size (b) == -1) + return NULL; + + c = (bson_cursor *)g_new0 (bson_cursor, 1); + c->obj = b; + + return c; +} + +void +bson_cursor_free (bson_cursor *c) +{ + g_free (c); +} + +/** @internal Figure out the block size of a given type. + * + * Provided a #bson_type and some raw data, figures out the length of + * the block, counted from rigth after the element name's position. + * + * @param type is the type of object we need the size for. + * @param data is the raw data (starting right after the element's + * name). + * + * @returns The size of the block, or -1 on error. + */ +static gint32 +_bson_get_block_size (bson_type type, const guint8 *data) +{ + glong l; + + switch (type) + { + case BSON_TYPE_STRING: + case BSON_TYPE_JS_CODE: + case BSON_TYPE_SYMBOL: + return bson_stream_doc_size (data, 0) + sizeof (gint32); + case BSON_TYPE_DOCUMENT: + case BSON_TYPE_ARRAY: + case BSON_TYPE_JS_CODE_W_SCOPE: + return bson_stream_doc_size (data, 0); + case BSON_TYPE_DOUBLE: + return sizeof (gdouble); + case BSON_TYPE_BINARY: + return bson_stream_doc_size (data, 0) + + sizeof (gint32) + sizeof (guint8); + case BSON_TYPE_OID: + return 12; + case BSON_TYPE_BOOLEAN: + return 1; + case BSON_TYPE_UTC_DATETIME: + case BSON_TYPE_TIMESTAMP: + case BSON_TYPE_INT64: + return sizeof (gint64); + case BSON_TYPE_NULL: + case BSON_TYPE_UNDEFINED: + case BSON_TYPE_MIN: + case BSON_TYPE_MAX: + return 0; + case BSON_TYPE_REGEXP: + l = strlen((gchar *)data); + return l + strlen((gchar *)(data + l + 1)) + 2; + case BSON_TYPE_INT32: + return sizeof (gint32); + case BSON_TYPE_DBPOINTER: + return bson_stream_doc_size (data, 0) + sizeof (gint32) + 12; + case BSON_TYPE_NONE: + default: + return -1; + } +} + +gboolean +bson_cursor_next (bson_cursor *c) +{ + const guint8 *d; + gint32 pos, bs; + + if (!c) + return FALSE; + + d = bson_data (c->obj); + + if (c->pos == 0) + pos = sizeof (guint32); + else + { + bs = _bson_get_block_size (bson_cursor_type (c), d + c->value_pos); + if (bs == -1) + return FALSE; + pos = c->value_pos + bs; + } + + if (pos >= bson_size (c->obj) - 1) + return FALSE; + + c->pos = pos; + c->key = (gchar *) &d[c->pos + 1]; + c->value_pos = c->pos + strlen (c->key) + 2; + + return TRUE; +} + +static inline gboolean +_bson_cursor_find (const bson *b, const gchar *name, size_t start_pos, + gint32 end_pos, gboolean wrap_over, bson_cursor *dest_c) +{ + gint32 pos = start_pos, bs; + const guint8 *d; + gint32 name_len; + + name_len = strlen (name); + + d = bson_data (b); + + while (pos < end_pos) + { + bson_type t = (bson_type) d[pos]; + const gchar *key = (gchar *) &d[pos + 1]; + gint32 key_len = strlen (key); + gint32 value_pos = pos + key_len + 2; + + if (key_len == name_len && memcmp (key, name, key_len) == 0) + { + dest_c->obj = b; + dest_c->key = key; + dest_c->pos = pos; + dest_c->value_pos = value_pos; + + return TRUE; + } + bs = _bson_get_block_size (t, &d[value_pos]); + if (bs == -1) + return FALSE; + pos = value_pos + bs; + } + + if (wrap_over) + return _bson_cursor_find (b, name, sizeof (gint32), start_pos, + FALSE, dest_c); + + return FALSE; +} + +gboolean +bson_cursor_find (bson_cursor *c, const gchar *name) +{ + if (!c || !name) + return FALSE; + + return _bson_cursor_find (c->obj, name, c->pos, bson_size (c->obj) - 1, + TRUE, c); +} + +gboolean +bson_cursor_find_next (bson_cursor *c, const gchar *name) +{ + if (!c || !name) + return FALSE; + + return _bson_cursor_find (c->obj, name, c->pos, bson_size (c->obj) - 1, + FALSE, c); +} + +bson_cursor * +bson_find (const bson *b, const gchar *name) +{ + bson_cursor *c; + + if (bson_size (b) == -1 || !name) + return NULL; + + c = bson_cursor_new (b); + if (_bson_cursor_find (b, name, sizeof (gint32), bson_size (c->obj) - 1, + FALSE, c)) + return c; + bson_cursor_free (c); + return NULL; +} + +bson_type +bson_cursor_type (const bson_cursor *c) +{ + if (!c || c->pos < sizeof (gint32)) + return BSON_TYPE_NONE; + + return (bson_type)(bson_data (c->obj)[c->pos]); +} + +const gchar * +bson_cursor_type_as_string (const bson_cursor *c) +{ + if (!c || c->pos < sizeof (gint32)) + return NULL; + + return bson_type_as_string (bson_cursor_type (c)); +} + +const gchar * +bson_cursor_key (const bson_cursor *c) +{ + if (!c) + return NULL; + + return c->key; +} + +/** @internal Convenience macro to verify a cursor's type. + * + * Verifies that the cursor's type is the same as the type requested + * by the caller, and returns FALSE if there is a mismatch. + */ +#define BSON_CURSOR_CHECK_TYPE(c,type) \ + if (bson_cursor_type(c) != type) \ + return FALSE; + +gboolean +bson_cursor_get_string (const bson_cursor *c, const gchar **dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_STRING); + + *dest = (gchar *)(bson_data (c->obj) + c->value_pos + sizeof (gint32)); + + return TRUE; +} + +gboolean +bson_cursor_get_double (const bson_cursor *c, gdouble *dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_DOUBLE); + + memcpy (dest, bson_data (c->obj) + c->value_pos, sizeof (gdouble)); + *dest = GDOUBLE_FROM_LE (*dest); + + return TRUE; +} + +gboolean +bson_cursor_get_document (const bson_cursor *c, bson **dest) +{ + bson *b; + gint32 size; + + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_DOCUMENT); + + size = bson_stream_doc_size (bson_data(c->obj), c->value_pos) - + sizeof (gint32) - 1; + b = bson_new_sized (size); + b->data = g_byte_array_append (b->data, + bson_data (c->obj) + c->value_pos + + sizeof (gint32), size); + bson_finish (b); + + *dest = b; + + return TRUE; +} + +gboolean +bson_cursor_get_array (const bson_cursor *c, bson **dest) +{ + bson *b; + gint32 size; + + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_ARRAY); + + size = bson_stream_doc_size (bson_data(c->obj), c->value_pos) - + sizeof (gint32) - 1; + b = bson_new_sized (size); + b->data = g_byte_array_append (b->data, + bson_data (c->obj) + c->value_pos + + sizeof (gint32), size); + bson_finish (b); + + *dest = b; + + return TRUE; +} + +gboolean +bson_cursor_get_binary (const bson_cursor *c, + bson_binary_subtype *subtype, + const guint8 **data, gint32 *size) +{ + if (!subtype || !size || !data) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_BINARY); + + *size = bson_stream_doc_size (bson_data(c->obj), c->value_pos); + *subtype = (bson_binary_subtype)(bson_data (c->obj)[c->value_pos + + sizeof (gint32)]); + *data = (guint8 *)(bson_data (c->obj) + c->value_pos + sizeof (gint32) + 1); + + return TRUE; +} + +gboolean +bson_cursor_get_oid (const bson_cursor *c, const guint8 **dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_OID); + + *dest = (guint8 *)(bson_data (c->obj) + c->value_pos); + + return TRUE; +} + +gboolean +bson_cursor_get_boolean (const bson_cursor *c, gboolean *dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_BOOLEAN); + + *dest = (gboolean)(bson_data (c->obj) + c->value_pos)[0]; + + return TRUE; +} + +gboolean +bson_cursor_get_utc_datetime (const bson_cursor *c, + gint64 *dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_UTC_DATETIME); + + memcpy (dest, bson_data (c->obj) + c->value_pos, sizeof (gint64)); + *dest = GINT64_FROM_LE (*dest); + + return TRUE; +} + +gboolean +bson_cursor_get_regex (const bson_cursor *c, const gchar **regex, + const gchar **options) +{ + if (!regex || !options) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_REGEXP); + + *regex = (gchar *)(bson_data (c->obj) + c->value_pos); + *options = (gchar *)(*regex + strlen(*regex) + 1); + + return TRUE; +} + +gboolean +bson_cursor_get_javascript (const bson_cursor *c, const gchar **dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_JS_CODE); + + *dest = (gchar *)(bson_data (c->obj) + c->value_pos + sizeof (gint32)); + + return TRUE; +} + +gboolean +bson_cursor_get_symbol (const bson_cursor *c, const gchar **dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_SYMBOL); + + *dest = (gchar *)(bson_data (c->obj) + c->value_pos + sizeof (gint32)); + + return TRUE; +} + +gboolean +bson_cursor_get_javascript_w_scope (const bson_cursor *c, + const gchar **js, + bson **scope) +{ + bson *b; + gint32 size, docpos; + + if (!js || !scope) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_JS_CODE_W_SCOPE); + + docpos = bson_stream_doc_size (bson_data (c->obj), + c->value_pos + sizeof (gint32)) + + sizeof (gint32) * 2; + size = bson_stream_doc_size (bson_data (c->obj), c->value_pos + docpos) - + sizeof (gint32) - 1; + b = bson_new_sized (size); + b->data = g_byte_array_append (b->data, + bson_data (c->obj) + c->value_pos + docpos + + sizeof (gint32), size); + bson_finish (b); + + *scope = b; + *js = (gchar *)(bson_data (c->obj) + c->value_pos + sizeof (gint32) * 2); + + return TRUE; +} + +gboolean +bson_cursor_get_int32 (const bson_cursor *c, gint32 *dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_INT32); + + memcpy (dest, bson_data (c->obj) + c->value_pos, sizeof (gint32)); + *dest = GINT32_FROM_LE (*dest); + + return TRUE; +} + +gboolean +bson_cursor_get_timestamp (const bson_cursor *c, gint64 *dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_TIMESTAMP); + + memcpy (dest, bson_data (c->obj) + c->value_pos, sizeof (gint64)); + *dest = GINT64_FROM_LE (*dest); + + return TRUE; +} + +gboolean +bson_cursor_get_int64 (const bson_cursor *c, gint64 *dest) +{ + if (!dest) + return FALSE; + + BSON_CURSOR_CHECK_TYPE (c, BSON_TYPE_INT64); + + memcpy (dest, bson_data (c->obj) + c->value_pos, sizeof (gint64)); + *dest = GINT64_FROM_LE (*dest); + + return TRUE; +} |