Permalink
Browse files

bson: Add bson_cursor_find().

Add a new function: bson_cursor_find(), which takes a cursor and a key
name, and attempts to find the key in the BSON object, starting at the
current cursor position, and wrapping over, if so need be (but only
once).

As a bonus, bson_find() and bson_cursor_find_next() were reimplemented
as thin wrappers around the same _bson_cursor_find() that
bson_cursor_find() uses.
  • Loading branch information...
1 parent a4f62c3 commit 2d0382f9cbc84a0081a797e835f88953b44d860f @algernon committed Jun 26, 2011
Showing with 111 additions and 49 deletions.
  1. +5 −0 NEWS
  2. +55 −49 src/bson.c
  3. +13 −0 src/bson.h
  4. +1 −0 src/libmongo-client.ver
  5. +1 −0 tests/Makefile.am
  6. +36 −0 tests/unit/bson/bson_cursor_find.c
View
5 NEWS
@@ -15,6 +15,11 @@ discovered too.
** New feature: Index handling functions
Implemented some helper functions to create and delete indexes.
+** New function: bson_cursor_find()
+Combining the powers of bson_find() and bson_cursor_find_next(), this
+new function can find a key anywhere in a BSON object, yet, maintains
+the ability to continue a previous scan.
+
* 0.1.1 - <2011-06-16 Thu>
** Cursor-based query iterator API
View
@@ -890,53 +890,22 @@ bson_cursor_next (bson_cursor *c)
return TRUE;
}
-gboolean
-bson_cursor_find_next (bson_cursor *c, const gchar *name)
-{
- const gchar *ckey;
- size_t cpos, cvalue_pos;
- gint32 name_len;
-
- if (!c || !name)
- return FALSE;
-
- name_len = strlen(name);
-
- ckey = c->key;
- cpos = c->pos;
- cvalue_pos = c->value_pos;
-
- while (bson_cursor_next (c))
- {
- gint32 key_len = strlen (bson_cursor_key (c));
-
- if (!memcmp (bson_cursor_key (c), name,
- (name_len <= key_len) ? name_len : key_len))
- return TRUE;
- }
-
- c->key = ckey;
- c->pos = cpos;
- c->value_pos = cvalue_pos;
-
- return FALSE;
-}
-
-bson_cursor *
-bson_find (const bson *b, const gchar *name)
+static inline gboolean
+_bson_cursor_find (const bson *b, const gchar *name, size_t start_pos,
+ gboolean wrap_over, bson_cursor *dest_c)
{
- gint32 pos = sizeof (guint32), bs;
+ gint32 pos = start_pos, bs, end_pos = bson_size (b) - 1;
const guint8 *d;
gint32 name_len;
-
- if (bson_size (b) == -1 || !name)
- return NULL;
+ gboolean wrapped = FALSE;
name_len = strlen (name);
d = bson_data (b);
- while (pos < bson_size (b) - 1)
+ find_start:
+
+ while (pos < end_pos)
{
bson_type t = (bson_type) d[pos];
const gchar *key = (gchar *) &d[pos + 1];
@@ -945,23 +914,60 @@ bson_find (const bson *b, const gchar *name)
if (!memcmp (key, name, (name_len <= key_len) ? name_len : key_len))
{
- bson_cursor *c;
-
- c = (bson_cursor *)g_new0 (bson_cursor, 1);
+ dest_c->obj = b;
+ dest_c->key = key;
+ dest_c->pos = pos;
+ dest_c->value_pos = value_pos;
- c->obj = b;
- c->key = key;
- c->pos = pos;
- c->value_pos = value_pos;
-
- return c;
+ return TRUE;
}
bs = _bson_get_block_size (t, &d[value_pos]);
if (bs == -1)
- return NULL;
+ return FALSE;
pos = value_pos + bs;
}
+ if (!wrapped && wrap_over)
+ {
+ pos = sizeof (gint32);
+ end_pos = start_pos;
+ wrapped = TRUE;
+ goto find_start;
+ }
+
+ 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, 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, 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), FALSE, c))
+ return c;
+ bson_cursor_free (c);
return NULL;
}
View
@@ -616,6 +616,19 @@ gboolean bson_cursor_next (bson_cursor *c);
*/
gboolean bson_cursor_find_next (bson_cursor *c, const gchar *name);
+/** Move the cursor to a given key
+ *
+ * Like bson_cursor_find_next(), this function will start scanning the
+ * BSON object at the current position. If the key is not found after
+ * it, it will wrap over and search up to the original position.
+ *
+ * @param c is the cursor to move.
+ * @param name is the key name to position to.
+ *
+ * @returns TRUE on success, FALSE otherwise.
+ */
+gboolean bson_cursor_find (bson_cursor *c, const gchar *name);
+
/** Determine the type of the current element.
*
* @param c is the cursor pointing at the appropriate element.
View
@@ -16,5 +16,6 @@ LMC_0.1.1 {
} LMC_0.1.0;
LMC_0.1.2 {
+ bson_cursor_find;
mongo_sync_cmd_index_*;
} LMC_0.1.1;
View
@@ -34,6 +34,7 @@ bson_unit_tests = \
unit/bson/bson_find \
unit/bson/bson_cursor_next \
unit/bson/bson_cursor_find_next \
+ unit/bson/bson_cursor_find \
unit/bson/bson_cursor_type \
unit/bson/bson_cursor_type_as_string \
unit/bson/bson_cursor_key \
@@ -0,0 +1,36 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_find (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ b = test_bson_generate_full ();
+ c = bson_find (b, "TRUE");
+
+ ok (bson_cursor_find (c, NULL) == FALSE,
+ "bson_cursor_find() should fail with a NULL key");
+ ok (bson_cursor_find (NULL, "int32") == FALSE,
+ "bson_cursor_find() should fail with a NULL cursor");
+
+ ok (bson_cursor_find (c, "sex") == TRUE,
+ "bson_cursor_find() works");
+
+ ok (bson_cursor_find (c, "str") == TRUE,
+ "bson_cursor_find() should wrap over if neccessary");
+
+ ok (bson_cursor_find (c, "-invalid-key-") == FALSE,
+ "bson_cursor_find() should fail when the key is not found");
+
+ ok (bson_cursor_find (c, "int64") == TRUE,
+ "bson_cursor_find() works, even after a previous failure");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (6, bson_cursor_find);

0 comments on commit 2d0382f

Please sign in to comment.