bug-recutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-recutils] [PATCH 03/13] src,torture: imple ment index tree scans.


From: Michał Masłowski
Subject: [bug-recutils] [PATCH 03/13] src,torture: imple ment index tree scans.
Date: Mon, 20 Aug 2012 18:21:24 +0200

---
 ChangeLog                                  |  25 ++
 src/rec-idx-tree.c                         | 491 ++++++++++++++++++++++++++++-
 src/rec.h                                  |  28 ++
 torture/Makefile.am                        |   1 +
 torture/rec-idx-tree/rec-idx-tree-scan.c   | 196 ++++++++++++
 torture/rec-idx-tree/tsuite-rec-idx-tree.c |   2 +
 6 files changed, 740 insertions(+), 3 deletions(-)
 create mode 100644 torture/rec-idx-tree/rec-idx-tree-scan.c

diff --git a/ChangeLog b/ChangeLog
index d401c5d..93012af 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2012-08-12  Michał Masłowski  <address@hidden>
+
+       src,torture: implement index tree scans.
+       * src/rec-idx-tree.c (struct rec_idx_tree_s): New fields parser,
+       rset and root.
+       (struct rec_idx_tree_iterator_s): New structure.
+       (REC_IDX_TREE_NONFIRST_MASK): New constant.
+       (rec_idx_tree_new): Initialize the new fields.
+       (rec_idx_tree_set_rset): New function.
+       (rec_idx_tree_scan): Likewise.
+       (rec_idx_tree_scan_interval): Likewise.
+       (rec_idx_tree_scan_common): Likewise.
+       (rec_idx_tree_get_depth): Likewise.
+       (rec_idx_tree_skip_and_compare_key): Likewise.
+       (rec_idx_tree_iter_next): Likewise.
+       (rec_idx_tree_iter_dispose): Likewise.
+       * src/rec.h: Add prototypes for rec_idx_tree_set_rset,
+       rec_idx_tree_scan and rec_idx_tree_scan_interval.
+
+       * torture/Makefile.am (REC_IDX_TREE_TSUITE): Add
+       rec-idx-tree-scan.c.
+       * torture/rec-idx-tree/rec-idx-tree-scan.c: New file.
+       * torture/rec-idx-tree/tsuite-rec-idx-tree.c: Add
+       test_rec_idx_tree_scan.
+
 2012-08-08  Michał Masłowski  <address@hidden>
 
        src,torture: implement initial index tree support without nodes.
diff --git a/src/rec-idx-tree.c b/src/rec-idx-tree.c
index 3d92bf7..f106082 100644
--- a/src/rec-idx-tree.c
+++ b/src/rec-idx-tree.c
@@ -43,6 +43,26 @@ struct rec_idx_tree_s
   const uint8_t *end;  /* first byte after it */
   const char **names;  /* array of key field names */
   rec_idx_tree_key_t *types;  /* array of key field types */
+  rec_parser_t parser;  /* parser for records */
+  rec_rset_t rset;  /* rset for records */
+  const uint8_t *root;  /* start of the root node */
+};
+
+/* Internal object storing record iterator state. */
+
+struct rec_idx_tree_iterator_s
+{
+  rec_idx_tree_t tree;
+  rec_record_t record;  /* previous one, to be freed later */
+  const uint8_t *next;  /* next key in the node */
+  const uint8_t *end;  /* root must be less than this */
+  size_t line;  /* next record position */
+  size_t character;
+  const void **right;  /* maximum comparison key to iterate */
+  size_t right_ind;  /* highest index to iterate */
+  int n_entries;  /* number of entries in the node */
+  bool first_only;
+  bool is_end;
 };
 
 /* Type of the basic tree index.  */
@@ -57,8 +77,51 @@ struct rec_idx_tree_header_s
 };
 
 /* Then a sequence of uint8_t type and null-terminated name follow.
-   After them is padding to eight octets just before the first
-   node.  */
+   After them is padding to eight octets just before the root node.
+
+   A B+tree variant with variable number of keys in a node is used.
+
+   This is the nonleaf node format:
+   - uint64_t number of entries
+   - uint64_t offset in the index tree of the subtree for the first
+     key
+   - uint64_t number of first records (i.e. ones mapped from a key
+     based only on the first value for each field name, as used for
+     sorting and interval searches) in that subtree
+   - the lowest key for the second subtree (a sequence of values of
+     format depending on their type, i.e. null-terminated strings for
+     REC_IDX_KEY_STRING)
+   - padding to eight octets
+   - offsets, numbers of first records, keys and padding for following
+     entries (no key is stated after the numbers for the last
+     subtree).
+
+   Each subtree has higher lowest key than the highest key in the
+   preceding subtree.
+
+   This is the leaf node format:
+   - uint64_t number of entries
+   - uint64_t line number of the record to which the key is mapped,
+     with highest bit set if this is not a first record as defined
+     above
+   - uint64_t character offset of the record
+   - key for which the preceding numbers are stated
+   - padding to eight octets
+   - line, character numbers, keys and padding for following entries
+   - uint64_t offset of the next leaf or 0 if this is the last one.
+
+   Keys in a node are sorted in order depending on their types.
+*/
+
+#define REC_IDX_TREE_NONFIRST_MASK INT64_C(0x8000000000000000)
+
+/* Static functions defined in this file.  */
+
+static rec_iter_t rec_idx_tree_scan_common (rec_idx_tree_t idx_tree, const 
void **left, const void **right, size_t left_ind, size_t right_ind, bool 
first_only);
+static size_t rec_idx_tree_get_depth (rec_idx_tree_t idx_tree);
+static int rec_idx_tree_skip_and_compare_key (rec_idx_tree_t tree, const 
uint8_t **in_tree, const void **given);
+static bool rec_idx_tree_iter_next (void *data, rec_record_t *record);
+static void rec_idx_tree_iter_dispose (void *data);
 
 /*
  * Public functions.
@@ -95,6 +158,8 @@ rec_idx_tree_new (const uint8_t *bytes,
   /* Set basic fields and allocate the per-field arrays.  */
   res->buffer = bytes;
   res->end = bytes + size;
+  res->parser = NULL;
+  res->rset = NULL;
   n_fields = rec_idx_tree_get_num_fields (res);
 
   res->names = malloc (sizeof (res->names[0]) * n_fields);
@@ -119,7 +184,7 @@ rec_idx_tree_new (const uint8_t *bytes,
   while ((next_byte != NULL)
          && (next_byte < res->end - 2) && (current_field < n_fields))
     {
-      next_byte++;
+      next_byte++;  /* skip '\0' */
       res->types[current_field] = *next_byte;
 
       if (res->types[current_field] > REC_IDX_KEY_STRING)
@@ -141,10 +206,28 @@ rec_idx_tree_new (const uint8_t *bytes,
       return NULL;
     }
 
+  next_byte++;  /* skip '\0' */
+
+  /* Save the start of the root node after padding. */
+  if ((next_byte - bytes) % 8)
+    {
+      next_byte += 8 - (next_byte - bytes) % 8;
+    }
+  res->root = next_byte;
+
   return res;
 }
 
 void
+rec_idx_tree_set_rset (rec_idx_tree_t idx_tree,
+                       rec_parser_t parser,
+                       rec_rset_t rset)
+{
+  idx_tree->parser = parser;
+  idx_tree->rset = rset;
+}
+
+void
 rec_idx_tree_destroy (rec_idx_tree_t idx_tree)
 {
   free (idx_tree->names);
@@ -180,4 +263,406 @@ rec_idx_tree_get_field_type (rec_idx_tree_t idx_tree, 
size_t n)
   return idx_tree->types[n];
 }
 
+rec_iter_t
+rec_idx_tree_scan (rec_idx_tree_t idx_tree,
+                   const void **left, const void **right)
+{
+  return rec_idx_tree_scan_common (idx_tree, left, right, 0, SIZE_MAX, false);
+}
+
+rec_iter_t
+rec_idx_tree_scan_interval (rec_idx_tree_t idx_tree, size_t left, size_t right)
+{
+  return rec_idx_tree_scan_common (idx_tree, NULL, NULL, left, right, true);
+}
+
+/*
+ * Private functions.
+ */
+
+static rec_iter_t
+rec_idx_tree_scan_common (rec_idx_tree_t idx_tree,
+                          const void **left, const void **right,
+                          size_t left_ind, size_t right_ind, bool first_only)
+{
+  size_t depth;
+  const uint8_t *root;
+  const uint64_t *node;
+  int n_entries, comparison;
+  size_t child, size, character, line;
+  bool empty;
+  struct rec_idx_tree_iterator_s *iter_state;
+  rec_iter_t iterator;
+
+  root = idx_tree->root;
+  depth = rec_idx_tree_get_depth (idx_tree);
+
+  /* Traverse the tree downward until finding the first possibly
+     useful leaf. */
+  while (depth > 0)
+    {
+      depth--;
+
+      if ((root >= idx_tree->end)
+          || (root < idx_tree->root))
+        {
+          return NULL;  /* Invalid node position. */
+        }
+
+      node = (const uint64_t*) root;
+      n_entries = (int) node[0];
+      node++;
+      empty = true;
+
+      while (n_entries > 0)
+        {
+          n_entries--;
+
+          if (root + sizeof (node[0]) * 2 >= idx_tree->end)
+            {
+              return NULL;  /* truncated index */
+            }
+
+          child = (size_t) node[0];  /* offset of subtree */
+          size = (size_t) node[1];
+          root = (const uint8_t*) &node[2];  /* skip to key */
+
+          /* Compare the key if there is one. */
+          if (n_entries > 0)
+            {
+              comparison = rec_idx_tree_skip_and_compare_key (idx_tree,
+                                                              &root, left);
+              if (!root)
+                {
+                  return NULL;  /* truncated index */
+                }
+            }
+          else
+            {
+              comparison = 1;  /* don't skip the last tree */
+            }
+
+          /* Check if we should skip the tree. */
+          if ((comparison <= 0) || (left_ind >= size))
+            {
+              if (left_ind >= size)
+                {
+                  left_ind -= size;
+                }
+              else
+                {
+                  left_ind = 0;
+                }
+              right_ind -= size;
+              continue;
+            }
+          else if (comparison > 0)
+            {
+              /* If a record is found, it's in this subtree.  */
+              empty = false;
+              root = idx_tree->buffer + child;
+            }
+          break;
+        }
+
+      if (empty)
+        {
+          /* Skipped the whole tree.  */
+          return rec_iter_new (NULL, NULL, NULL);
+        }
+    }
+
+  if (root + sizeof (node[0]) > idx_tree->end)
+    {
+      return NULL;  /* truncated index */
+    }
+
+  /* Now root points to the beginning of a leaf node which must
+     contain the first record position we want to iterate, unless the
+     output is empty (assuming correct code and index).  So find the
+     first element >= left and make an iterator starting the work
+     there.  */
+  node = (const uint64_t *) root;
+  n_entries = node[0];
+  root = (const uint8_t*) &node[1];
+
+  while (n_entries > 0)
+    {
+      n_entries--;
+
+      if (root + sizeof(node[0]) * 2 > idx_tree->end)
+        {
+          return NULL;  /* truncated index */
+        }
+
+      node = (const uint64_t *) root;
+      line = (size_t) node[0];
+      character = (size_t) node[1];
+      root = (const uint8_t *) &node[2];
+      comparison = rec_idx_tree_skip_and_compare_key (idx_tree, &root, left);
+      if (!root)
+        {
+          return NULL;  /* truncated index */
+        }
+
+      if ((comparison < 0) || (left_ind > 0)
+          || ((line & REC_IDX_TREE_NONFIRST_MASK) && first_only))
+        {
+          /* Skip the record, it has too small key or index.  */
+          left_ind--;
+          right_ind--;
+          continue;
+        }
+
+      /* We could have parsed the last entry of the node.  In that
+         case go to the start of the next one so the iterator always
+         starts at a leaf node entry.  */
+      if (n_entries == 0)
+        {
+          node = (const uint64_t*) root;
+
+          if (node[0] != 0)
+            {
+              root = idx_tree->buffer + node[0];
+              if (root + sizeof (node[0]) > idx_tree->end)
+                {
+                  return NULL;  /* truncated index */
+                }
+
+              n_entries = node[1];
+              root = (const uint8_t*) &node[2];
+            }
+          else
+            {
+              /* We found the last record of the tree, not looking
+                 further.  */
+              root = NULL;
+            }
+        }
+
+      /* Prepare the iterator. */
+      iter_state = malloc (sizeof (*iter_state));
+      if (!iter_state)
+        {
+          return NULL;
+        }
+
+      iterator = rec_iter_new (iter_state, rec_idx_tree_iter_next,
+                               rec_idx_tree_iter_dispose);
+      if (!iterator)
+        {
+          free (iter_state);
+          return NULL;
+        }
+
+      iter_state->tree = idx_tree;
+      iter_state->record = NULL;
+      iter_state->next = root;
+      iter_state->end = idx_tree->end;
+      iter_state->line = line;
+      iter_state->character = character;
+      iter_state->right = right;
+      iter_state->right_ind = right_ind;
+      iter_state->n_entries = n_entries;
+      iter_state->first_only = first_only;
+      iter_state->is_end = (root == NULL);
+      return iterator;
+    }
+  return rec_iter_new (NULL, NULL, NULL);  /* no records found */
+}
+
+static size_t
+rec_idx_tree_get_depth (rec_idx_tree_t idx_tree)
+{
+  struct rec_idx_tree_header_s *header =
+    (struct rec_idx_tree_header_s *) idx_tree->buffer;
+  return (size_t) header->depth;
+}
+
+/* Return a negative, zero or positive value if *in_tree is less,
+   equal or greater than specified in given.  Advance *in_tree after
+   the whole key, or set it to NULL if the index is known to be
+   invalid.  Pass NULL as given to just advance *in_tree.  */
+
+static int
+rec_idx_tree_skip_and_compare_key (rec_idx_tree_t tree,
+                                   const uint8_t **in_tree, const void **given)
+{
+  size_t n_fields, n, limit;
+  int comparison = 0;
+
+  n_fields = rec_idx_tree_get_num_fields (tree);
+  for (n = 0; n < n_fields; n++)
+    {
+      limit = tree->end - *in_tree;  /* maximal key size */
+      switch (tree->types[n])
+        {
+        case REC_IDX_KEY_NONE:
+          /* Does not occur.  */
+          break;
+        case REC_IDX_KEY_STRING:
+          if ((comparison == 0) && (given[n] != NULL))
+            {
+              comparison = strncmp ((const char *) *in_tree,
+                                    (const char *) (given[n]), limit);
+            }
+
+          *in_tree = memchr (*in_tree, '\0', limit);
+          if (in_tree == NULL)
+            {
+              return comparison;
+            }
+
+          (*in_tree)++;
+          break;
+        }
+    }
+
+  /* Skip padding after the key.  */
+  if ((*in_tree - tree->buffer) % 8)
+    {
+      *in_tree += 8 - (*in_tree - tree->buffer) % 8;
+    }
+  return comparison;
+}
+
+static bool
+rec_idx_tree_iter_next (void *data, rec_record_t *record)
+{
+  struct rec_idx_tree_iterator_s *iter =
+    (struct rec_idx_tree_iterator_s *) data;
+  uint64_t line_field;
+  const uint64_t *node;
+  int comparison;
+
+  /* Destroy the previous record. */
+  if (iter->record)
+    {
+      rec_record_destroy (iter->record);
+      iter->record = NULL;
+    }
+
+  if (iter->is_end)
+    {
+      return false;
+    }
+
+  /* Update the count of records iterated. */
+  if (iter->right_ind == 0)
+    {
+      iter->is_end = true;
+    }
+  else
+    {
+      iter->right_ind--;
+    }
+
+  /* Parse the current record. */
+  if (!rec_parser_seek (iter->tree->parser, iter->line, iter->character)
+      || !rec_parse_record (iter->tree->parser, &(iter->record)))
+    {
+      /* Error. */
+      *record = NULL;
+      return false;
+    }
+
+  rec_record_set_container (iter->record, iter->tree->rset);
+
+  if (iter->next == NULL)
+    {
+      iter->is_end = true;
+    }
+  else
+    {
+      /* Find the next record. */
+      while (iter->n_entries > 0)
+        {
+          iter->n_entries--;
+
+          if (iter->next + sizeof (node[0]) * 2 > iter->end)
+            {
+              /* Truncated index. */
+              *record = NULL;
+              return false;
+            }
+
+          node = (const uint64_t*) iter->next;
+          line_field = node[0];
+          iter->line = (size_t) (line_field & ~REC_IDX_TREE_NONFIRST_MASK);
+          iter->character = (size_t) node[1];
+          iter->next = (const uint8_t*) &node[2];
+          comparison = rec_idx_tree_skip_and_compare_key (iter->tree,
+                                                          &(iter->next),
+                                                      iter->right);
+
+          if (!iter->next)
+            {
+              *record = NULL;
+              return false;
+            }
+
+          if (comparison > 0)
+            {
+              iter->is_end = true;
+            }
+
+          if (iter->n_entries == 0)
+            {
+              /* Go to the next node.  */
+              if (iter->next + sizeof (node[0]) > iter->end)
+                {
+                  *record = NULL;
+                  return false;
+                }
+
+              node = (const uint64_t *) iter->next;
+
+              if (node[0] == INT64_C(0))
+                {
+                  /* This is a different condition than iter->is_end:
+                     there is only one next record.  */
+                  iter->next = NULL;
+                }
+              else
+                {
+                  iter->next = iter->tree->buffer + node[0];
+                  if (iter->next + sizeof (node[0]) > iter->end)
+                    {
+                      *record = NULL;
+                      return false;
+                    }
+
+                  node = (const uint64_t *) iter->next;
+                  iter->n_entries = (size_t) node[0];
+                  iter->next += sizeof (node[0]);
+                }
+
+            }
+
+          if (!((line_field & REC_IDX_TREE_NONFIRST_MASK) && iter->first_only))
+            {
+              /* We won't skip the record just found.  */
+              break;
+            }
+        }
+    }
+
+  *record = iter->record;
+  return true;
+}
+
+static void
+rec_idx_tree_iter_dispose (void *data)
+{
+  struct rec_idx_tree_iterator_s *iter =
+    (struct rec_idx_tree_iterator_s *) data;
+
+  if (iter->record)
+    {
+      rec_record_destroy (iter->record);
+    }
+
+  free (iter);
+}
+
 /* End of rec-idx-tree.c */
diff --git a/src/rec.h b/src/rec.h
index 980bf56..548e439 100644
--- a/src/rec.h
+++ b/src/rec.h
@@ -2480,6 +2480,12 @@ typedef enum rec_idx_tree_key_e rec_idx_tree_key_t;
 
 rec_idx_tree_t rec_idx_tree_new (const uint8_t *bytes, int64_t type, size_t 
size);
 
+/* Set rset and parser used by the index tree.  Records found by
+   searches will be parsed by the given parser and considered elements
+   of the specified rset.  */
+
+void rec_idx_tree_set_rset (rec_idx_tree_t idx_tree, rec_parser_t parser, 
rec_rset_t rset);
+
 /* Destroy an index tree object.  */
 
 void rec_idx_tree_destroy (rec_idx_tree_t idx_tree);
@@ -2504,6 +2510,28 @@ const char *rec_idx_tree_get_field_name (rec_idx_tree_t 
idx_tree, size_t n);
 
 rec_idx_tree_key_t rec_idx_tree_get_field_type (rec_idx_tree_t idx_tree, 
size_t n);
 
+/**************** Searching index trees ******************************/
+
+/* Return an iterator over all records in the index mapped from keys
+   between (inclusive) left and right.  Records mapped from multiple
+   keys in the specified range will be duplicated.
+
+   The left and right arguments are arrays of size equal to the key
+   type length containing values of key fields of representations
+   depending on their types.  For REC_IDX_KEY_STRING fields the value
+   is a const char * pointing to the field value.
+
+   NULL is returned on error.
+*/
+
+rec_iter_t rec_idx_tree_scan (rec_idx_tree_t idx_tree, const void **left, 
const void **right);
+
+/* Return an interator over records having indices in the tree in the
+   interval of [left, right].  Only the first occurrence of each
+   record in the tree might be used.  NULL is returned on error.  */
+
+rec_iter_t rec_idx_tree_scan_interval (rec_idx_tree_t idx_tree, size_t left, 
size_t right);
+
 #endif /* !GNU_REC_H */
 
 /* End of rec.h */
diff --git a/torture/Makefile.am b/torture/Makefile.am
index 0ac2d86..38d9e6d 100644
--- a/torture/Makefile.am
+++ b/torture/Makefile.am
@@ -148,6 +148,7 @@ REC_ITER_TSUITE = rec-iter/rec-iter-mset.c \
                   rec-iter/tsuite-rec-iter.c
 
 REC_IDX_TREE_TSUITE = rec-idx-tree/rec-idx-tree-new.c \
+                      rec-idx-tree/rec-idx-tree-scan.c \
                       rec-idx-tree/tsuite-rec-idx-tree.c
 
 runtests_SOURCES = runtests.c \
diff --git a/torture/rec-idx-tree/rec-idx-tree-scan.c 
b/torture/rec-idx-tree/rec-idx-tree-scan.c
new file mode 100644
index 0000000..108b746
--- /dev/null
+++ b/torture/rec-idx-tree/rec-idx-tree-scan.c
@@ -0,0 +1,196 @@
+/* -*- mode: C -*-
+ *
+ *       File:         rec-idx-tree-scan.c
+ *       Date:         Thu Aug 9 20:24:08 2012
+ *
+ *       GNU recutils - rec_idx_tree_scan unit tests.
+ *
+ */
+
+/* Copyright (C) 2012 Michał Masłowski */
+
+/* This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <check.h>
+
+#include <rec.h>
+
+/* Header and key description for a simple index tree. */
+static const uint8_t sample_header[] =
+  {
+    1 /* second rset */, 0 /* only leaf node */,
+    2 /* two fields in the key */,
+    1 /* string */, 'f', 'o', 'o', '\0',
+    1 /* string */, 'b', 'a', 'r', '\0',
+    0, 0, 0 /* padding */
+  };
+
+/*-
+ * Test: rec_idx_tree_scan_one_node
+ * Unit: rec_idx_tree_scan
+ * Description:
+ * + Iterate records from a single-node tree.
+ */
+START_TEST(rec_idx_tree_scan_one_node)
+{
+  rec_idx_tree_t tree;
+  uint8_t buffer[1024];
+  uint64_t *node;
+  rec_rset_t rset;
+  rec_parser_t parser;
+  rec_iter_t iter;
+  rec_record_t record;
+  /* This isn't related to the keys in the index and is shorter.  */
+  static const char recfile[] = "a: 1\n\na: 2\n\na: 3\n";
+  static const char* left[] = {"abc", "def"};
+  static const char* right[] = {"bcd", "efg"};
+
+  memcpy (buffer, sample_header, sizeof (sample_header));
+  rset = rec_rset_new ();
+  fail_if (!rset);
+  parser = rec_parser_new_str (recfile, "dummy");
+  fail_if (!parser);
+
+  /* Prepare the node.  */
+  node = (uint64_t *) &buffer[sizeof (sample_header)];
+  node[0] = 3;  /* number of entries */
+  node[1] = 1;  /* line */
+  node[2] = 0;  /* character */
+  memcpy (node + 3, "abc\0def\0", 8); /* key field values */
+  node[4] = 3;
+  node[5] = 6;
+  memcpy (node + 6, "bcd\0ef\0\0", 8);
+  node[7] = 5;
+  node[8] = 12;
+  memcpy (node + 9, "cde\0fgh\0", 8);
+  node[10] = 0;  /* next node */
+
+  /* Open the tree. */
+  tree = rec_idx_tree_new (buffer, 1 /* the type */, sizeof(buffer));
+  fail_if (!tree);
+  rec_idx_tree_set_rset (tree, parser, rset);
+  iter = rec_idx_tree_scan (tree, (const void **) left, (const void **) right);
+  fail_if (!iter);
+
+  /* Read the records. */
+  fail_if (!rec_iter_next (iter, &record));
+  fail_if (!rec_record_contains_field (record, "a", "1"));
+  fail_if (!rec_iter_next (iter, &record));
+  fail_if (!rec_record_contains_field (record, "a", "2"));
+  fail_if (rec_iter_next (iter, &record));
+
+  /* Close. */
+  rec_iter_destroy (iter);
+  rec_idx_tree_destroy (tree);
+  rec_parser_destroy (parser);
+  rec_rset_destroy (rset);
+}
+END_TEST
+
+/*-
+ * Test: rec_idx_tree_scan_tree
+ * Unit: rec_idx_tree_scan
+ * Description:
+ * + Iterate a record from a tree having a nonleaf node.
+ */
+START_TEST(rec_idx_tree_scan_tree)
+{
+  rec_idx_tree_t tree;
+  uint8_t buffer[1024];
+  uint64_t *node;
+  rec_rset_t rset;
+  rec_parser_t parser;
+  rec_iter_t iter;
+  rec_record_t record;
+  /* This isn't related to the keys in the index and is shorter.  */
+  static const char recfile[] = "a: 1\n\na: 2\n\na: 3\n";
+  static const char* left[] = {"abc", "def"};
+  static const char* right[] = {"bcd", "efg"};
+
+  memcpy (buffer, sample_header, sizeof (sample_header));
+  buffer[1] = 1;  /* depth */
+  rset = rec_rset_new ();
+  fail_if (!rset);
+  parser = rec_parser_new_str (recfile, "dummy");
+  fail_if (!parser);
+
+  /* Prepare tree nodes, one root and two non-root.  */
+  /* root */
+  node = (uint64_t *) &buffer[sizeof (sample_header)];
+  node[0] = 2;  /* number of entries */
+  /* offset of the first child */
+  node[1] = sizeof (sample_header) + sizeof (uint64_t) * 6;
+  node[2] = 1;  /* number of first records */
+  memcpy (node + 3, "bcd\0ef\0\0", 8);  /* key field values */
+  /* offset of the second child */
+  node[4] = sizeof (sample_header) + sizeof (uint64_t) * 11;
+  node[5] = 2;  /* number of first records */
+  /* first child */
+  node[6] = 1;  /* number of entries */
+  node[7] = 1;  /* line */
+  node[8] = 0;  /* character */
+  memcpy (node + 9, "abc\0def\0", 8); /* key field values */
+  node[10] = sizeof (sample_header) + sizeof (uint64_t) * 11;  /* next node */
+  /* second child */
+  node[11] = 2;
+  node[12] = 3;
+  node[13] = 6;
+  memcpy (node + 14, "bcd\0ef\0\0", 8);
+  node[15] = 5;
+  node[16] = 12;
+  memcpy (node + 17, "cde\0fgh\0", 8);
+  node[18] = 0;  /* next node */
+
+  /* Open the tree. */
+  tree = rec_idx_tree_new (buffer, 1 /* the type */, sizeof(buffer));
+  fail_if (!tree);
+  rec_idx_tree_set_rset (tree, parser, rset);
+  iter = rec_idx_tree_scan (tree, (const void **) left, (const void **) right);
+  fail_if (!iter);
+
+  /* Read the records. */
+  fail_if (!rec_iter_next (iter, &record));
+  fail_if (!rec_record_contains_field (record, "a", "1"));
+  fail_if (!rec_iter_next (iter, &record));
+  fail_if (!rec_record_contains_field (record, "a", "2"));
+  fail_if (rec_iter_next (iter, &record));
+
+  /* Close. */
+  rec_iter_destroy (iter);
+  rec_idx_tree_destroy (tree);
+  rec_parser_destroy (parser);
+  rec_rset_destroy (rset);
+}
+END_TEST
+
+/*
+ * Test creation function
+ */
+TCase *
+test_rec_idx_tree_scan (void)
+{
+  TCase *tc = tcase_create ("rec_idx_tree_scan");
+  tcase_add_test (tc, rec_idx_tree_scan_one_node);
+  tcase_add_test (tc, rec_idx_tree_scan_tree);
+
+  return tc;
+}
+
+/* End of rec-idx-tree-scan.c */
diff --git a/torture/rec-idx-tree/tsuite-rec-idx-tree.c 
b/torture/rec-idx-tree/tsuite-rec-idx-tree.c
index aae8873..0ea6d45 100644
--- a/torture/rec-idx-tree/tsuite-rec-idx-tree.c
+++ b/torture/rec-idx-tree/tsuite-rec-idx-tree.c
@@ -27,6 +27,7 @@
 #include <check.h>
 
 extern TCase *test_rec_idx_tree_new (void);
+extern TCase *test_rec_idx_tree_scan (void);
 
 Suite *
 tsuite_rec_idx_tree ()
@@ -35,6 +36,7 @@ tsuite_rec_idx_tree ()
 
   s = suite_create ("rec-idx-tree");
   suite_add_tcase (s, test_rec_idx_tree_new ());
+  suite_add_tcase (s, test_rec_idx_tree_scan ());
 
   return s;
 }
-- 
1.7.11.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]