[PATCH v6 2/4] lib: implement JSON tree class for json library

Vladimir Davydov vdavydov.dev at gmail.com
Mon Dec 10 20:36:58 MSK 2018


On Thu, Dec 06, 2018 at 11:42:29AM +0300, Kirill Shcherbatov wrote:
> New JSON tree class would store JSON paths for tuple fields
> for registered non-plain indexes. It is a hierarchical data
> structure that organize JSON nodes produced by parser.
> Class provides API to lookup node by path and iterate over the
> tree.
> JSON Indexes patch require such functionality to make lookup
> for tuple_fields by path, make initialization of field map and
> build vynyl_stmt msgpack for secondary index via JSON tree
> iteration.
> 
> Needed for #1012
> ---
>  src/lib/json/CMakeLists.txt |   1 +
>  src/lib/json/json.c         | 257 ++++++++++++++++++++++++++++++++
>  src/lib/json/json.h         | 287 +++++++++++++++++++++++++++++++++++-
>  test/unit/json_path.c       | 243 +++++++++++++++++++++++++++++-
>  test/unit/json_path.result  |  60 +++++++-
>  5 files changed, 845 insertions(+), 3 deletions(-)

I cleaned up the patch and pushed it to 2.1.

Here's the list of my changes:
 - Forbid root = NULL in json_tree_lookup_path.
 - Make loop variable first in foreach macros argument list.
 - Use int instead of uint32_t unless uint32_t is specifically
   required.
 - Set max_child_idx to -1 if children array is empty to avoid
   ambiguity.
 - Fix child index allocation for JSON_TOKEN_STR.
 - Rework json_tree_add and json_tree_del to make them more
   straightforward.
 - Add comments and cleanup code.

Here's the incremental diff:

diff --git a/src/lib/json/json.c b/src/lib/json/json.c
index 65169b04..6f3cccc4 100644
--- a/src/lib/json/json.c
+++ b/src/lib/json/json.c
@@ -30,12 +30,19 @@
  */
 
 #include "json.h"
-#include "third_party/PMurHash.h"
+
+#include <assert.h>
 #include <ctype.h>
 #include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unicode/uchar.h>
 #include <unicode/utf8.h>
+
 #include "trivia/util.h"
+#include "third_party/PMurHash.h"
 
 /**
  * Read a single symbol from a string starting from an offset.
@@ -245,7 +252,9 @@ json_lexer_next_token(struct json_lexer *lexer, struct json_token *token)
 	}
 }
 
-/** Compare JSON tokens keys. */
+/**
+ * Compare JSON token keys.
+ */
 static int
 json_token_cmp(const struct json_token *a, const struct json_token *b)
 {
@@ -279,14 +288,18 @@ json_token_cmp(const struct json_token *a, const struct json_token *b)
 
 static const uint32_t hash_seed = 13U;
 
-/** Compute the hash value of a JSON token. */
+/**
+ * Compute the hash value of a JSON token.
+ *
+ * See the comment to json_tree::hash for implementation details.
+ */
 static uint32_t
 json_token_hash(struct json_token *token)
 {
 	uint32_t h = token->parent->hash;
 	uint32_t carry = 0;
 	const void *data;
-	uint32_t data_size;
+	int data_size;
 	if (token->type == JSON_TOKEN_STR) {
 		data = token->str;
 		data_size = token->len;
@@ -306,6 +319,8 @@ json_tree_create(struct json_tree *tree)
 	memset(tree, 0, sizeof(struct json_tree));
 	tree->root.hash = hash_seed;
 	tree->root.type = JSON_TOKEN_END;
+	tree->root.max_child_idx = -1;
+	tree->root.sibling_idx = -1;
 	tree->hash = mh_json_new();
 	return tree->hash == NULL ? -1 : 0;
 }
@@ -313,16 +328,10 @@ json_tree_create(struct json_tree *tree)
 static void
 json_token_destroy(struct json_token *token)
 {
-#ifndef NDEBUG
-	/* Token mustn't have JSON subtree. */
-	struct json_token *iter;
-	uint32_t nodes = 0;
-	json_tree_foreach_preorder(token, iter)
-		nodes++;
-	assert(nodes == 0);
-#endif /* NDEBUG */
-
+ 	assert(token->max_child_idx == -1);
+	assert(token->sibling_idx == -1);
 	free(token->children);
+	token->children = NULL;
 }
 
 void
@@ -347,7 +356,7 @@ json_tree_lookup_slowpath(struct json_tree *tree, struct json_token *parent,
 	if (id == mh_end(tree->hash))
 		return NULL;
 	struct json_token **entry = mh_json_node(tree->hash, id);
-	assert(entry != NULL);
+	assert(entry != NULL && *entry != NULL);
 	return *entry;
 }
 
@@ -355,89 +364,90 @@ int
 json_tree_add(struct json_tree *tree, struct json_token *parent,
 	      struct json_token *token)
 {
-	int ret = 0;
-	token->parent = parent;
-	uint32_t hash = json_token_hash(token);
 	assert(json_tree_lookup(tree, parent, token) == NULL);
-	uint32_t insert_idx = token->type == JSON_TOKEN_NUM ?
-			      (uint32_t)token->num :
-			      parent->children_capacity;
+	token->parent = parent;
+	token->children = NULL;
+	token->children_capacity = 0;
+	token->max_child_idx = -1;
+	token->hash = json_token_hash(token);
+	int insert_idx = token->type == JSON_TOKEN_NUM ?
+			 (int)token->num : parent->max_child_idx + 1;
+	/*
+	 * Dynamically grow the children array if necessary.
+	 */
 	if (insert_idx >= parent->children_capacity) {
-		uint32_t new_size = parent->children_capacity == 0 ?
-				    8 : 2 * parent->children_capacity;
+		int new_size = parent->children_capacity == 0 ?
+			       8 : 2 * parent->children_capacity;
 		while (insert_idx >= new_size)
 			new_size *= 2;
-		struct json_token **children =
-			realloc(parent->children, new_size*sizeof(void *));
-		if (children == NULL) {
-			ret = -1;
-			goto end;
-		}
+		struct json_token **children = realloc(parent->children,
+						new_size * sizeof(void *));
+		if (children == NULL)
+			return -1; /* out of memory */
 		memset(children + parent->children_capacity, 0,
-		       (new_size - parent->children_capacity)*sizeof(void *));
+		       (new_size - parent->children_capacity) * sizeof(void *));
 		parent->children = children;
 		parent->children_capacity = new_size;
 	}
+	/*
+	 * Insert the token into the hash (only for tokens representing
+	 * JSON map entries, see the comment to json_tree::hash).
+	 */
+	if (token->type == JSON_TOKEN_STR) {
+		mh_int_t id = mh_json_put(tree->hash,
+			(const struct json_token **)&token, NULL, NULL);
+		if (id == mh_end(tree->hash))
+			return -1; /* out of memory */
+	}
+	/*
+	 * Success, now we can insert the new token into its parent's
+	 * children array.
+	 */
 	assert(parent->children[insert_idx] == NULL);
 	parent->children[insert_idx] = token;
 	parent->max_child_idx = MAX(parent->max_child_idx, insert_idx);
 	token->sibling_idx = insert_idx;
-	token->hash = hash;
-	token->parent = parent;
-	if (token->type != JSON_TOKEN_STR)
-		goto end;
-	/*
-	 * Add string token to json_tree hash to make lookup
-	 * by name.
-	 */
-	mh_int_t rc =
-		mh_json_put(tree->hash, (const struct json_token **)&token,
-			    NULL, NULL);
-	if (rc == mh_end(tree->hash)) {
-		parent->children[insert_idx] = NULL;
-		ret = -1;
-		goto end;
-	}
-end:
 	assert(json_tree_lookup(tree, parent, token) == token);
-	return ret;
+	return 0;
 }
 
 void
 json_tree_del(struct json_tree *tree, struct json_token *token)
 {
 	struct json_token *parent = token->parent;
+	assert(token->sibling_idx >= 0);
+	assert(parent->children[token->sibling_idx] == token);
 	assert(json_tree_lookup(tree, parent, token) == token);
-	struct json_token **child_slot = &parent->children[token->sibling_idx];
-	assert(child_slot != NULL && *child_slot == token);
-	*child_slot = NULL;
-	/* The max_child_idx field may require update. */
-	if (token->sibling_idx == parent->max_child_idx &&
-	    parent->max_child_idx > 0) {
-		uint32_t idx = token->sibling_idx - 1;
-		while (idx > 0 && parent->children[idx] == 0)
-			idx--;
-		parent->max_child_idx = idx;
+	/*
+	 * Clear the entry corresponding to this token in parent's
+	 * children array and update max_child_idx if necessary.
+	 */
+	parent->children[token->sibling_idx] = NULL;
+	token->sibling_idx = -1;
+	while (parent->max_child_idx >= 0 &&
+	       parent->children[parent->max_child_idx] == NULL)
+		parent->max_child_idx--;
+	/*
+	 * Remove the token from the hash (only for tokens representing
+	 * JSON map entries, see the comment to json_tree::hash).
+	 */
+	if (token->type == JSON_TOKEN_STR) {
+		mh_int_t id = mh_json_find(tree->hash, token, NULL);
+		assert(id != mh_end(tree->hash));
+		mh_json_del(tree->hash, id, NULL);
 	}
-	if (token->type != JSON_TOKEN_STR)
-		goto end;
-	/* Remove string token from json_tree hash. */
-	mh_int_t id = mh_json_find(tree->hash, token, NULL);
-	assert(id != mh_end(tree->hash));
-	mh_json_del(tree->hash, id, NULL);
 	json_token_destroy(token);
-end:
 	assert(json_tree_lookup(tree, parent, token) == NULL);
 }
 
 struct json_token *
-json_tree_lookup_path(struct json_tree *tree, struct json_token *parent,
-		      const char *path, uint32_t path_len, uint32_t index_base)
+json_tree_lookup_path(struct json_tree *tree, struct json_token *root,
+		      const char *path, int path_len, int index_base)
 {
 	int rc;
 	struct json_lexer lexer;
 	struct json_token token;
-	struct json_token *ret = parent != NULL ? parent : &tree->root;
+	struct json_token *ret = root;
 	json_lexer_create(&lexer, path, path_len, index_base);
 	while ((rc = json_lexer_next_token(&lexer, &token)) == 0 &&
 	       token.type != JSON_TOKEN_END && ret != NULL) {
@@ -448,6 +458,11 @@ json_tree_lookup_path(struct json_tree *tree, struct json_token *parent,
 	return ret;
 }
 
+/**
+ * Return the child of @parent following @pos or NULL if @pos
+ * points to the last child in the children array. If @pos is
+ * NULL, this function returns the first child.
+ */
 static struct json_token *
 json_tree_child_next(struct json_token *parent, struct json_token *pos)
 {
@@ -455,12 +470,16 @@ json_tree_child_next(struct json_token *parent, struct json_token *pos)
 	struct json_token **arr = parent->children;
 	if (arr == NULL)
 		return NULL;
-	uint32_t idx = pos != NULL ? pos->sibling_idx + 1 : 0;
+	int idx = pos != NULL ? pos->sibling_idx + 1 : 0;
 	while (idx <= parent->max_child_idx && arr[idx] == NULL)
 		idx++;
 	return idx <= parent->max_child_idx ? arr[idx] : NULL;
 }
 
+/**
+ * Return the leftmost descendant of the tree rooted at @pos
+ * or NULL if the tree is empty.
+ */
 static struct json_token *
 json_tree_leftmost(struct json_token *pos)
 {
diff --git a/src/lib/json/json.h b/src/lib/json/json.h
index 4ff2b9c6..8aa2765f 100644
--- a/src/lib/json/json.h
+++ b/src/lib/json/json.h
@@ -30,12 +30,18 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+#include <stddef.h>
+#include <stdint.h>
 #include "trivia/util.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#ifndef typeof
+#define typeof __typeof__
+#endif
+
 /**
  * Lexer for JSON paths:
  * <field>, <.field>, <[123]>, <['field']> and their combinations.
@@ -84,26 +90,33 @@ struct json_token {
 		/** Index value. */
 		uint64_t num;
 	};
+	/** Pointer to the parent token in a JSON tree. */
+	struct json_token *parent;
 	/**
-	 * Hash value of the token. Used for lookups in a JSON tree.
-	 * For more details, see the comment to json_tree::hash_table.
-	 */
-	uint32_t hash;
-	/**
-	 * Array of child records. Indexes in this array
-	 * match [token.num] index for JSON_TOKEN_NUM type and
-	 * are allocated sequentially for JSON_TOKEN_STR child
+	 * Array of child tokens in a JSON tree. Indexes in this
+	 * array match [token.num] index for JSON_TOKEN_NUM type
+	 * and are allocated sequentially for JSON_TOKEN_STR child
 	 * tokens.
 	 */
 	struct json_token **children;
 	/** Allocation size of children array. */
-	uint32_t children_capacity;
-	/** Max occupied index in the children array. */
-	uint32_t max_child_idx;
-	/** Index of node in parent children array. */
-	uint32_t sibling_idx;
-	/** Pointer to parent node. */
-	struct json_token *parent;
+	int children_capacity;
+	/**
+	 * Max occupied index in the children array or -1 if
+	 * the children array is empty.
+	 */
+	int max_child_idx;
+	/**
+	 * Index of this token in parent's children array or -1
+	 * if the token was removed from a JSON tree or represents
+	 * a JSON tree root.
+	 */
+	int sibling_idx;
+	/**
+	 * Hash value of the token. Used for lookups in a JSON tree.
+	 * For more details, see the comment to json_tree::hash.
+	 */
+	uint32_t hash;
 };
 
 struct mh_json_t;
@@ -222,32 +235,43 @@ json_lexer_create(struct json_lexer *lexer, const char *src, int src_len,
 int
 json_lexer_next_token(struct json_lexer *lexer, struct json_token *token);
 
-/** Create a JSON tree object to manage data relations. */
+/**
+ * Initialize a new empty JSON tree.
+ *
+ * Returns 0 on success, -1 on memory allocation error.
+ */
 int
 json_tree_create(struct json_tree *tree);
 
 /**
- * Destroy JSON tree object. This routine doesn't destroy attached
- * subtree so it should be called at the end of manual destroy.
+ * Destroy a JSON tree.
+ *
+ * Note, this routine expects the tree to be empty - the caller
+ * is supposed to use json_tree_foreach_safe() and json_tree_del()
+ * to dismantle the tree before calling this function.
  */
 void
 json_tree_destroy(struct json_tree *tree);
 
-/** Internal function, use json_tree_lookup instead. */
+/**
+ * Internal function, use json_tree_lookup() instead.
+ */
 struct json_token *
 json_tree_lookup_slowpath(struct json_tree *tree, struct json_token *parent,
 			  const struct json_token *token);
 
 /**
- * Look up a token in a tree at position specified with parent.
+ * Look up a token in a JSON tree given a parent token and a key.
+ *
+ * Returns NULL if not found.
  */
 static inline struct json_token *
 json_tree_lookup(struct json_tree *tree, struct json_token *parent,
 		 const struct json_token *token)
 {
 	struct json_token *ret = NULL;
-	if (token->type == JSON_TOKEN_NUM) {
-		ret = likely(token->num < parent->children_capacity) ?
+	if (likely(token->type == JSON_TOKEN_NUM)) {
+		ret = (int)token->num < parent->children_capacity ?
 		      parent->children[token->num] : NULL;
 	} else {
 		ret = json_tree_lookup_slowpath(tree, parent, token);
@@ -256,147 +280,171 @@ json_tree_lookup(struct json_tree *tree, struct json_token *parent,
 }
 
 /**
- * Append token to the given parent position in a JSON tree. The
- * parent mustn't have a child with such content.
+ * Insert a token into a JSON tree at a given position.
+ *
+ * The token key (json_token::type and num/str,len) must be set,
+ * e.g. by json_lexer_next_token(). The caller must ensure that
+ * no token with the same key is linked to the same parent, e.g.
+ * with json_tree_lookup().
+ *
+ * Returns 0 on success, -1 on memory allocation error.
  */
 int
 json_tree_add(struct json_tree *tree, struct json_token *parent,
 	      struct json_token *token);
 
 /**
- * Delete a JSON tree token at the given parent position in JSON
- * tree. Token entry shouldn't have subtree.
+ * Delete a token from a JSON tree.
+ *
+ * The token must be linked to the tree (see json_tree_add())
+ * and must not have any children.
  */
 void
 json_tree_del(struct json_tree *tree, struct json_token *token);
 
-/** Lookup child by path in JSON tree. */
+/**
+ * Look up a token in a JSON tree by path.
+ *
+ * The path is relative to the given root token. In order to
+ * look up a token by absolute path, pass json_tree::root.
+ * The index_base is passed to json_lexer used for tokenizing
+ * the path, see json_lexer_create() for more details.
+ *
+ * Returns NULL if no token is found or the path is invalid.
+ */
 struct json_token *
-json_tree_lookup_path(struct json_tree *tree, struct json_token *parent,
-		      const char *path, uint32_t path_len, uint32_t index_base);
+json_tree_lookup_path(struct json_tree *tree, struct json_token *root,
+		      const char *path, int path_len, int index_base);
 
-/** Do pre order traversal in JSON tree. */
+/**
+ * Perform pre-order traversal in a JSON subtree rooted
+ * at a given node.
+ *
+ * To start a new traversal, pass NULL for @pos.
+ * Returns @root at the first iteration.
+ * Returns NULL when traversal is over.
+ */
 struct json_token *
 json_tree_preorder_next(struct json_token *root, struct json_token *pos);
 
-/** Do post order traversal in JSON tree. */
+/**
+ * Perform post-order traversal in a JSON subtree rooted
+ * at a given node.
+ *
+ * To start a new traversal, pass NULL for @pos.
+ * Returns @root at the last iteration.
+ * Returns NULL when traversal is over.
+ */
 struct json_token *
 json_tree_postorder_next(struct json_token *root, struct json_token *pos);
 
-#ifndef typeof
-#define typeof __typeof__
-#endif
+/**
+ * Perform pre-order JSON tree traversal.
+ * Note, this function does not visit the root node.
+ * See also json_tree_preorder_next().
+ */
+#define json_tree_foreach_preorder(pos, root)				     \
+	for ((pos) = json_tree_preorder_next((root), (root));		     \
+	     (pos) != NULL;						     \
+	     (pos) = json_tree_preorder_next((root), (pos)))
 
-/** Return container entry by json_tree_node node. */
-#define json_tree_entry(node, type, member) \
-	container_of((node), type, member)
 /**
- * Return container entry by json_tree_node or NULL if
- * node is NULL.
+ * Perform post-order JSON tree traversal.
+ * Note, this function does not visit the root node.
+ * See also json_tree_postorder_next().
  */
-#define json_tree_entry_safe(node, type, member) ({			     \
-	(node) != NULL ? json_tree_entry((node), type, member) : NULL;	     \
-})
+#define json_tree_foreach_postorder(pos, root)				     \
+	for ((pos) = json_tree_postorder_next((root), NULL);		     \
+	     (pos) != (root);						     \
+	     (pos) = json_tree_postorder_next((root), (pos)))
 
-/** Do entry pre order traversal in JSON tree */
-#define json_tree_preorder_next_entry(root, node, type, member) ({	     \
-	struct json_token *__next =					     \
-		json_tree_preorder_next((root), (node));		     \
-	json_tree_entry_safe(__next, type, member);			     \
-})
+/**
+ * Perform post-order JSON tree traversal safe against node removal.
+ * Note, this function does not visit the root node.
+ * See also json_tree_postorder_next().
+ */
+#define json_tree_foreach_safe(pos, root, tmp)				     \
+	for ((pos) = json_tree_postorder_next((root), NULL);		     \
+	     (pos) != (root) &&						     \
+	     ((tmp) = json_tree_postorder_next((root), (pos))) != NULL;	     \
+	     (pos) = (tmp))
 
 /**
- * Do entry post order traversal in JSON tree.
- * This cycle doesn't visit root node.
+ * Return a container of a json_tree_token.
  */
-#define json_tree_postorder_next_entry(root, node, type, member) ({	     \
-	struct json_token *__next =					     \
-		json_tree_postorder_next((root), (node));		     \
-	json_tree_entry_safe(__next, type, member);			     \
-})
+#define json_tree_entry(token, type, member)				     \
+	container_of((token), type, member)
 
 /**
- * Lookup in tree by path and return entry.
- * This cycle doesn't visit root node.
+ * Return a container of a json_tree_token or NULL if @token is NULL.
  */
-#define json_tree_lookup_path_entry(tree, parent, path, path_len,	     \
-				    index_base, type, member) ({	     \
-	struct json_token *__node =					     \
-		json_tree_lookup_path((tree), (parent), path, path_len,	     \
-				      index_base);			     \
-	json_tree_entry_safe(__node, type, member);			     \
-})
+#define json_tree_entry_safe(token, type, member)			     \
+	((token) != NULL ? json_tree_entry((token), type, member) : NULL);   \
 
-/** Lookup in tree by token and return entry. */
+/**
+ * Container-aware wrapper around json_tree_lookup().
+ */
 #define json_tree_lookup_entry(tree, parent, token, type, member) ({	     \
-	struct json_token *__node =					     \
-		json_tree_lookup((tree), (parent), token);		     \
-	json_tree_entry_safe(__node, type, member);			     \
+	struct json_token *ret = json_tree_lookup((tree), (parent), (token));\
+	json_tree_entry_safe(ret, type, member);			     \
 })
 
 /**
- * Do pre-order traversal in JSON tree.
- * This cycle doesn't visit root node.
+ * Container-aware wrapper around json_tree_lookup_path().
  */
-#define json_tree_foreach_preorder(root, node)				     \
-	for ((node) = json_tree_preorder_next((root), (root));		     \
-	     (node) != NULL;						     \
-	     (node) = json_tree_preorder_next((root), (node)))
+#define json_tree_lookup_path_entry(tree, root, path, path_len, index_base,  \
+				    type, member) ({			     \
+	struct json_token *ret = json_tree_lookup_path((tree), (root),	     \
+					(path), (path_len), (index_base));   \
+	json_tree_entry_safe(ret, type, member);			     \
+})
 
 /**
- * Do post-order traversal in JSON tree.
- * This cycle doesn't visit root node.
+ * Container-aware wrapper around json_tree_preorder_next().
  */
-#define json_tree_foreach_postorder(root, node)				     \
-	for ((node) = json_tree_postorder_next((root), NULL);		     \
-	     (node) != (root);						     \
-	     (node) = json_tree_postorder_next((root), (node)))
+#define json_tree_preorder_next_entry(root, pos, type, member) ({	     \
+	struct json_token *next = json_tree_preorder_next((root), (pos));    \
+	json_tree_entry_safe(next, type, member);			     \
+})
 
 /**
- * Do safe post-order traversal in JSON tree.
- * May be used for destructors.
- * This cycle doesn't visit root node.
+ * Container-aware wrapper around json_tree_postorder_next().
  */
-#define json_tree_foreach_safe(root, node, tmp)				     \
-	for ((node) = json_tree_postorder_next((root), NULL);		     \
-	     (node) != (root) &&					     \
-	     ((tmp) =  json_tree_postorder_next((root), (node)));	     \
-	     (node) = (tmp))
+#define json_tree_postorder_next_entry(root, pos, type, member) ({	     \
+	struct json_token *next = json_tree_postorder_next((root), (pos));   \
+	json_tree_entry_safe(next, type, member);			     \
+})
 
 /**
- * Do post-order traversal in JSON tree and return entry.
- * This cycle doesn't visit root node.
+ * Container-aware version of json_tree_foreach_preorder().
  */
-#define json_tree_foreach_entry_preorder(root, node, type, member)	     \
-	for ((node) = json_tree_preorder_next_entry((root), (root),	     \
-						    type, member);	     \
-	     (node) != NULL;						     \
-	     (node) = json_tree_preorder_next_entry((root), &(node)->member, \
-						    type, member))
+#define json_tree_foreach_entry_preorder(pos, root, type, member)	     \
+	for ((pos) = json_tree_preorder_next_entry((root), (root),	     \
+						   type, member);	     \
+	     (pos) != NULL;						     \
+	     (pos) = json_tree_preorder_next_entry((root), &(pos)->member,   \
+						   type, member))
 
 /**
- * Do pre-order traversal in JSON tree and return entry.
- * This cycle doesn't visit root node.
+ * Container-aware version of json_tree_foreach_postorder().
  */
-#define json_tree_foreach_entry_postorder(root, node, type, member)	     \
-	for ((node) = json_tree_postorder_next_entry((root), NULL, type,     \
-						     member);		     \
-	     &(node)->member != (root);					     \
-	     (node) = json_tree_postorder_next_entry((root), &(node)->member,\
+#define json_tree_foreach_entry_postorder(pos, root, type, member)	     \
+	for ((pos) = json_tree_postorder_next_entry((root), NULL,	     \
+						    type, member);	     \
+	     &(pos)->member != (root);					     \
+	     (pos) = json_tree_postorder_next_entry((root), &(pos)->member,  \
 						     type, member))
 
 /**
- * Do secure post-order traversal in JSON tree and return entry.
- * This cycle doesn't visit root node.
+ * Container-aware version of json_tree_foreach_safe().
  */
-#define json_tree_foreach_entry_safe(root, node, type, member, tmp)	     \
-	for ((node) = json_tree_postorder_next_entry((root), NULL,	     \
-						     type, member);	     \
-	     &(node)->member != (root) &&				     \
-	     ((tmp) =  json_tree_postorder_next_entry((root),		     \
-						      &(node)->member,	     \
-						      type, member));	     \
-	     (node) = (tmp))
+#define json_tree_foreach_entry_safe(pos, root, type, member, tmp)	     \
+	for ((pos) = json_tree_postorder_next_entry((root), NULL,	     \
+						    type, member);	     \
+	     &(pos)->member != (root) &&				     \
+	     ((tmp) = json_tree_postorder_next_entry((root), &(pos)->member, \
+						     type, member)) != NULL; \
+	     (pos) = (tmp))
 
 #ifdef __cplusplus
 }
diff --git a/test/unit/json_path.c b/test/unit/json_path.c
index 04d36688..b72865e4 100644
--- a/test/unit/json_path.c
+++ b/test/unit/json_path.c
@@ -250,17 +250,17 @@ test_tree()
 			     &records_idx);
 	is(node, &records[5], "add path '%s'", path4_copy);
 
-	node = json_tree_lookup_path_entry(&tree, NULL, path1, strlen(path1),
-					   JSON_INDEX_BASE, struct test_struct,
-					   node);
+	node = json_tree_lookup_path_entry(&tree, &tree.root, path1,
+					   strlen(path1), JSON_INDEX_BASE,
+					   struct test_struct, node);
 	is(node, &records[1], "lookup path '%s'", path1);
 
-	node = json_tree_lookup_path_entry(&tree, NULL, path2, strlen(path2),
-					   JSON_INDEX_BASE, struct test_struct,
-					   node);
+	node = json_tree_lookup_path_entry(&tree, &tree.root, path2,
+					   strlen(path2), JSON_INDEX_BASE,
+					   struct test_struct, node);
 	is(node, &records[3], "lookup path '%s'", path2);
 
-	node = json_tree_lookup_path_entry(&tree, NULL, path_unregistered,
+	node = json_tree_lookup_path_entry(&tree, &tree.root, path_unregistered,
 					   strlen(path_unregistered),
 					   JSON_INDEX_BASE, struct test_struct,
 					   node);
@@ -274,7 +274,7 @@ test_tree()
 	int cnt = sizeof(tokens_preorder)/sizeof(tokens_preorder[0]);
 	int idx = 0;
 
-	json_tree_foreach_preorder(&tree.root, token) {
+	json_tree_foreach_preorder(token, &tree.root) {
 		if (idx >= cnt)
 			break;
 		struct test_struct *t1 =
@@ -294,7 +294,7 @@ test_tree()
 		 &records[3].node, &records[2].node, &records[0].node};
 	cnt = sizeof(tree_nodes_postorder)/sizeof(tree_nodes_postorder[0]);
 	idx = 0;
-	json_tree_foreach_postorder(&tree.root, token) {
+	json_tree_foreach_postorder(token, &tree.root) {
 		if (idx >= cnt)
 			break;
 		struct test_struct *t1 =
@@ -310,7 +310,7 @@ test_tree()
 	is(idx, cnt, "records iterated count %d of %d", idx, cnt);
 
 	idx = 0;
-	json_tree_foreach_safe(&tree.root, token, tmp) {
+	json_tree_foreach_safe(token, &tree.root, tmp) {
 		if (idx >= cnt)
 			break;
 		struct test_struct *t1 =
@@ -326,7 +326,7 @@ test_tree()
 	is(idx, cnt, "records iterated count %d of %d", idx, cnt);
 
 	idx = 0;
-	json_tree_foreach_entry_preorder(&tree.root, node, struct test_struct,
+	json_tree_foreach_entry_preorder(node, &tree.root, struct test_struct,
 					 node) {
 		if (idx >= cnt)
 			break;
@@ -341,7 +341,7 @@ test_tree()
 	is(idx, cnt, "records iterated count %d of %d", idx, cnt);
 
 	idx = 0;
-	json_tree_foreach_entry_postorder(&tree.root, node, struct test_struct,
+	json_tree_foreach_entry_postorder(node, &tree.root, struct test_struct,
 					  node) {
 		if (idx >= cnt)
 			break;
@@ -362,21 +362,21 @@ test_tree()
 	is(records[3].node.max_child_idx, 1, "max_child_index %d expected of %d",
 	   records[3].node.max_child_idx, 1);
 	json_tree_del(&tree, &records[4].node);
-	is(records[3].node.max_child_idx, 0, "max_child_index %d expected of %d",
-	   records[3].node.max_child_idx, 0);
-	node = json_tree_lookup_path_entry(&tree, NULL, path3, strlen(path3),\
-					   JSON_INDEX_BASE, struct test_struct,
-					   node);
+	is(records[3].node.max_child_idx, -1, "max_child_index %d expected of %d",
+	   records[3].node.max_child_idx, -1);
+	node = json_tree_lookup_path_entry(&tree, &tree.root, path3,
+					   strlen(path3), JSON_INDEX_BASE,
+					   struct test_struct, node);
 	is(node, NULL, "lookup removed path '%s'", path3);
 
-	node = json_tree_lookup_path_entry(&tree, NULL, path4, strlen(path4),
-					   JSON_INDEX_BASE, struct test_struct,
-					   node);
+	node = json_tree_lookup_path_entry(&tree, &tree.root, path4,
+					   strlen(path4), JSON_INDEX_BASE,
+					   struct test_struct, node);
 	is(node, NULL, "lookup removed path '%s'", path4);
 
-	node = json_tree_lookup_path_entry(&tree, NULL, path2, strlen(path2),
-					   JSON_INDEX_BASE, struct test_struct,
-					   node);
+	node = json_tree_lookup_path_entry(&tree, &tree.root, path2,
+					   strlen(path2), JSON_INDEX_BASE,
+					   struct test_struct, node);
 	is(node, &records[3], "lookup path was not corrupted '%s'", path2);
 
 	const struct json_token *tree_nodes_postorder_new[] =
@@ -385,7 +385,7 @@ test_tree()
 	cnt = sizeof(tree_nodes_postorder_new) /
 	      sizeof(tree_nodes_postorder_new[0]);
 	idx = 0;
-	json_tree_foreach_entry_safe(&tree.root, node, struct test_struct,
+	json_tree_foreach_entry_safe(node, &tree.root, struct test_struct,
 				     node, node_tmp) {
 		if (idx >= cnt)
 			break;
diff --git a/test/unit/json_path.result b/test/unit/json_path.result
index 0ee970c8..ee23e70c 100644
--- a/test/unit/json_path.result
+++ b/test/unit/json_path.result
@@ -147,7 +147,7 @@ ok 2 - subtests
     ok 43 - records iterated count 6 of 6
     ok 44 - max_child_index 7 expected of 7
     ok 45 - max_child_index 1 expected of 1
-    ok 46 - max_child_index 0 expected of 0
+    ok 46 - max_child_index -1 expected of -1
     ok 47 - lookup removed path '[1][20].file[2]'
     ok 48 - lookup removed path '[1][20].file[8]'
     ok 49 - lookup path was not corrupted '[1][20].file'



More information about the Tarantool-patches mailing list