+/* This is based on the hash algorithm from gdbm */
+static unsigned int hash_key(TDB_DATA *key)
+{
+ uint32_t value; /* Used to compute the hash value. */
+ uint32_t i; /* Used to cycle through random values. */
+
+ /* Set the initial value from the key size. */
+ for (value = 0x238F13AF ^ key->dsize, i=0; i < key->dsize; i++)
+ value = (value + (key->dptr[i] << (i*5 % 24)));
+
+ return (1103515243 * value + 12345);
+}
+
+/* A trivial traversal is one which doesn't terminate early and only
+ * plays with its own record. We can reliably replay these even if
+ * traverse order changes. */
+static bool is_trivial_traverse(struct op op[], unsigned int end)
+{
+ unsigned int i;
+ TDB_DATA cur = tdb_null;
+
+ if (op[end].ret != 0)
+ return false;
+
+ for (i = 0; i < end; i++) {
+ if (!op[i].key.dptr)
+ continue;
+ if (op[i].op == OP_TDB_TRAVERSE)
+ cur = op[i].key;
+ if (!key_eq(cur, op[i].key))
+ return false;
+ }
+ return true;
+}
+
+static void analyze_traverse(struct op op[], unsigned int end)
+{
+ int i;
+ struct traverse *trav = talloc(op, struct traverse);
+
+ trav->num = 0;
+ trav->end = end;
+ for (i = end-1; i >= 0; i--) {
+ if (op[i].op == OP_TDB_TRAVERSE)
+ trav->num++;
+ if (op[i].op != OP_TDB_TRAVERSE_READ_START
+ && op[i].op != OP_TDB_TRAVERSE_START)
+ continue;
+ if (op[i].trav)
+ continue;
+ break;
+ }
+
+ if (i < 0)
+ errx(1, "Line %u: no traversal start found", end+1);
+
+ op[i].trav = trav;
+
+ if (is_trivial_traverse(op+i, end-i)) {
+ /* Fill in a plentiful hash table. */
+ op[i].trav->hash = talloc_zero_array(op[i].trav,
+ struct traverse_hash,
+ trav->num * 2);
+ for (; i < end; i++) {
+ unsigned int h;
+ if (op[i].op != OP_TDB_TRAVERSE)
+ continue;
+ h = hash_key(&op[i].key) % (trav->num * 2);
+ while (trav->hash[h].index)
+ h = (h + 1) % (trav->num * 2);
+ trav->hash[h].index = i+1;
+ trav->hash[h].key = op[i].key;
+ }
+ } else
+ trav->hash = NULL;
+}
+