|
@@ -0,0 +1,488 @@
|
|
|
|
+#include "uthash.h"
|
|
|
|
+
|
|
|
|
+#include "store_htable.h"
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Callback type for key comparison.
|
|
|
|
+ */
|
|
|
|
+typedef bool (*LSUP_key_eq_fn_t)(
|
|
|
|
+ const LSUP_Key spok[], const LSUP_Key luk[]);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+typedef struct triple_entry_t {
|
|
|
|
+ LSUP_TripleKey key;
|
|
|
|
+ UT_hash_handle hh;
|
|
|
|
+} TripleEntry;
|
|
|
|
+
|
|
|
|
+typedef struct idx_entry_t {
|
|
|
|
+ LSUP_Key key;
|
|
|
|
+ LSUP_Buffer * sterm;
|
|
|
|
+ UT_hash_handle hh;
|
|
|
|
+} IndexEntry;
|
|
|
|
+
|
|
|
|
+typedef struct ht_store_t {
|
|
|
|
+ TripleEntry * keys; // Triple keys (set).
|
|
|
|
+ IndexEntry * idx; // Dictionary of keys to serialized terms.
|
|
|
|
+} HTStore;
|
|
|
|
+
|
|
|
|
+typedef struct ht_iterator_t {
|
|
|
|
+ HTStore * store; // Store being iterated.
|
|
|
|
+ size_t i; // Lookup cursor.
|
|
|
|
+ LSUP_Key luk[3]; // 0÷3 lookup keys.
|
|
|
|
+ LSUP_key_eq_fn_t eq_fn; // Equality function to test triples.
|
|
|
|
+ int rc; // Return code for *next* result.
|
|
|
|
+ TripleEntry * spok; // Retrieved SPO key.
|
|
|
|
+} HTIterator;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Identity hashing function.
|
|
|
|
+ *
|
|
|
|
+ * Since the key is already a strong hash, reuse it for bucket allocation.
|
|
|
|
+ */
|
|
|
|
+#define HASH_FUNCTION (s,len,hashv) (hashv) = (unsigned)(s)
|
|
|
|
+
|
|
|
|
+/* * * CALLBACKS * * */
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Dummy callback for queries with all parameters unbound. Returns true.
|
|
|
|
+*/
|
|
|
|
+static bool lookup_none_eq_fn(
|
|
|
|
+ const LSUP_Key spok[], const LSUP_Key luk[])
|
|
|
|
+{ return true; }
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Keyset lookup for S key.
|
|
|
|
+ */
|
|
|
|
+static bool lookup_sk_eq_fn(
|
|
|
|
+ const LSUP_Key spok[], const LSUP_Key luk[])
|
|
|
|
+{ return spok[0] == luk[0]; }
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Keyset lookup for P key.
|
|
|
|
+ */
|
|
|
|
+static bool lookup_pk_eq_fn(
|
|
|
|
+ const LSUP_Key spok[], const LSUP_Key luk[])
|
|
|
|
+{ return spok[1] == luk[0]; }
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Keyset lookup for O key.
|
|
|
|
+ */
|
|
|
|
+static bool lookup_ok_eq_fn(
|
|
|
|
+ const LSUP_Key spok[], const LSUP_Key luk[])
|
|
|
|
+{ return spok[2] == luk[0]; }
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Keyset lookup for S and P keys.
|
|
|
|
+ */
|
|
|
|
+static bool lookup_spk_eq_fn(
|
|
|
|
+ const LSUP_Key spok[], const LSUP_Key luk[])
|
|
|
|
+{ return spok[0] == luk[0] && spok[1] == luk[1]; }
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Keyset lookup for S and O keys.
|
|
|
|
+ */
|
|
|
|
+static bool lookup_sok_eq_fn(
|
|
|
|
+ const LSUP_Key spok[], const LSUP_Key luk[])
|
|
|
|
+{ return spok[0] == luk[0] && spok[2] == luk[1]; }
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Keyset lookup for P and O keys.
|
|
|
|
+ */
|
|
|
|
+static bool lookup_pok_eq_fn(
|
|
|
|
+ const LSUP_Key spok[], const LSUP_Key luk[])
|
|
|
|
+{ return spok[1] == luk[0] && spok[2] == luk[1]; }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* * * Other prototypes. * * */
|
|
|
|
+
|
|
|
|
+static inline LSUP_rc htiter_next_key (HTIterator *it);
|
|
|
|
+static inline LSUP_rc tkey_to_strp (
|
|
|
|
+ const HTStore *store, const LSUP_Key spok[], LSUP_SerTriple *sspo);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* * * API * * */
|
|
|
|
+
|
|
|
|
+HTStore *
|
|
|
|
+LSUP_htstore_new (void)
|
|
|
|
+{
|
|
|
|
+ HTStore *ht = malloc (sizeof (*ht));
|
|
|
|
+ if (UNLIKELY (!ht)) return NULL;
|
|
|
|
+
|
|
|
|
+ ht->keys = NULL;
|
|
|
|
+ ht->idx = NULL;
|
|
|
|
+
|
|
|
|
+ return ht;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+HTStore *
|
|
|
|
+LSUP_htstore_copy (const HTStore *src)
|
|
|
|
+{
|
|
|
|
+ HTStore *dest = LSUP_htstore_new();
|
|
|
|
+ if (UNLIKELY (!dest)) return NULL;
|
|
|
|
+
|
|
|
|
+ if (LSUP_htstore_copy_contents (dest, src) < 0) {
|
|
|
|
+ LSUP_htstore_free (dest);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return dest;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+LSUP_rc
|
|
|
|
+LSUP_htstore_copy_contents (HTStore *dest, const HTStore *src)
|
|
|
|
+{
|
|
|
|
+ TripleEntry *dest_trp = malloc (
|
|
|
|
+ HASH_COUNT (src->keys) * sizeof (*dest_trp));
|
|
|
|
+ if (UNLIKELY (!dest_trp)) return LSUP_MEM_ERR;
|
|
|
|
+
|
|
|
|
+ IndexEntry *dest_idx = malloc (
|
|
|
|
+ HASH_COUNT (src->idx) * sizeof (*dest_idx));
|
|
|
|
+ if (UNLIKELY (!dest_idx)) {
|
|
|
|
+ free (dest_trp);
|
|
|
|
+ return LSUP_MEM_ERR;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ TripleEntry *src_trp;
|
|
|
|
+ IndexEntry *src_idx;
|
|
|
|
+ unsigned i;
|
|
|
|
+
|
|
|
|
+ // Copy triple data.
|
|
|
|
+ for (
|
|
|
|
+ src_trp = src->keys, i = 0; src_trp != NULL;
|
|
|
|
+ src_trp = src->keys->hh.next
|
|
|
|
+ ) {
|
|
|
|
+ memcpy (dest_trp + i, src_trp, sizeof (*dest_trp));
|
|
|
|
+ HASH_ADD (hh, dest->keys, key, TRP_KLEN, dest_trp + i);
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Copy index data.
|
|
|
|
+ for (
|
|
|
|
+ src_idx = src->idx, i = 0; src_idx != NULL;
|
|
|
|
+ src_idx = src->idx->hh.next
|
|
|
|
+ ) {
|
|
|
|
+ memcpy (dest_idx + i, src_idx, sizeof (*dest_idx));
|
|
|
|
+ HASH_ADD (hh, dest->idx, key, KLEN, dest_idx + i);
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+HTStore *
|
|
|
|
+LSUP_htstore_bool_op(
|
|
|
|
+ const LSUP_bool_op op, const HTStore *s1, const HTStore *s2)
|
|
|
|
+{
|
|
|
|
+ HTStore *dest;
|
|
|
|
+ TripleEntry *trp_cur;
|
|
|
|
+
|
|
|
|
+ dest = LSUP_htstore_new();
|
|
|
|
+
|
|
|
|
+ if (UNLIKELY (
|
|
|
|
+ op != LSUP_BOOL_UNION
|
|
|
|
+ && op != LSUP_BOOL_SUBTRACTION
|
|
|
|
+ && op != LSUP_BOOL_INTERSECTION
|
|
|
|
+ && op != LSUP_BOOL_XOR)) {
|
|
|
|
+ fprintf (stderr, "Operation not supported.\n");
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (op == LSUP_BOOL_UNION) {
|
|
|
|
+ dest = LSUP_htstore_copy (s1);
|
|
|
|
+ if (UNLIKELY (!dest) || LSUP_htstore_copy_contents (dest, s2) < 0)
|
|
|
|
+ goto fail;
|
|
|
|
+
|
|
|
|
+ return dest;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LSUP_SerTriple *sspo = STRP_DUMMY;
|
|
|
|
+
|
|
|
|
+ LSUP_HTIterator *it = LSUP_htstore_add_init(dest);
|
|
|
|
+
|
|
|
|
+ if (op == LSUP_BOOL_XOR) {
|
|
|
|
+ // Add triples from s2 if not found in s1.
|
|
|
|
+ for (
|
|
|
|
+ trp_cur = s2->keys; trp_cur != NULL;
|
|
|
|
+ trp_cur = s2->keys->hh.next
|
|
|
|
+ ) {
|
|
|
|
+ TripleEntry *ins = NULL;
|
|
|
|
+ HASH_FIND (hh, s1->keys, trp_cur->key, TRP_KLEN, ins);
|
|
|
|
+
|
|
|
|
+ if (!ins) {
|
|
|
|
+ tkey_to_strp (s2, trp_cur->key, sspo);
|
|
|
|
+ LSUP_htstore_add_iter (it, sspo);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (
|
|
|
|
+ trp_cur = s1->keys; trp_cur != NULL;
|
|
|
|
+ trp_cur = s1->keys->hh.next
|
|
|
|
+ ) {
|
|
|
|
+ TripleEntry *ins = NULL;
|
|
|
|
+ HASH_FIND (hh, s2->keys, trp_cur->key, TRP_KLEN, ins);
|
|
|
|
+
|
|
|
|
+ // For XOR and subtraction, add if not found.
|
|
|
|
+ // For intersection, add if found.
|
|
|
|
+ if ((op == LSUP_BOOL_INTERSECTION) ^ (ins == NULL)) {
|
|
|
|
+ tkey_to_strp (s1, trp_cur->key, sspo);
|
|
|
|
+ LSUP_htstore_add_iter (it, sspo);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ LSUP_striple_free (sspo);
|
|
|
|
+ LSUP_htstore_add_done (it);
|
|
|
|
+
|
|
|
|
+ return dest;
|
|
|
|
+
|
|
|
|
+fail:
|
|
|
|
+ LSUP_htstore_free (dest);
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+LSUP_htstore_free (HTStore *ht)
|
|
|
|
+{
|
|
|
|
+ IndexEntry *idx_entry, *idx_tmp;
|
|
|
|
+ HASH_ITER (hh, ht->idx, idx_entry, idx_tmp) {
|
|
|
|
+ HASH_DEL (ht->idx, idx_entry);
|
|
|
|
+ free (idx_entry);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ TripleEntry *trp_entry, *trp_tmp;
|
|
|
|
+ HASH_ITER (hh, ht->keys, trp_entry, trp_tmp) {
|
|
|
|
+ HASH_DEL (ht->keys, trp_entry);
|
|
|
|
+ free (trp_entry);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ free (ht);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+size_t
|
|
|
|
+LSUP_htstore_size (LSUP_HTStore *ht)
|
|
|
|
+{ return HASH_COUNT (ht->keys); }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+LSUP_HTIterator *
|
|
|
|
+LSUP_htstore_add_init (HTStore *store)
|
|
|
|
+{
|
|
|
|
+ HTIterator *it = malloc (sizeof (HTIterator));
|
|
|
|
+ if (UNLIKELY (!it)) return NULL;
|
|
|
|
+
|
|
|
|
+ it->store = store;
|
|
|
|
+ it->i = 0;
|
|
|
|
+
|
|
|
|
+ return it;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+LSUP_rc
|
|
|
|
+LSUP_htstore_add_iter (HTIterator *it, const LSUP_SerTriple *sspo)
|
|
|
|
+{
|
|
|
|
+ LSUP_rc rc = LSUP_NOACTION;
|
|
|
|
+ LSUP_TripleKey spok = NULL_TRP;
|
|
|
|
+
|
|
|
|
+ // Add terms to index.
|
|
|
|
+ for (int i = 0; i < 3; i++) {
|
|
|
|
+ spok[i] = LSUP_buffer_hash (LSUP_striple_pos (sspo, i));
|
|
|
|
+ TRACE ("Indexing term key %lu\n", spok[i]);
|
|
|
|
+
|
|
|
|
+ IndexEntry *ins = NULL;
|
|
|
|
+ HASH_FIND (hh, it->store->idx, spok + i, KLEN, ins);
|
|
|
|
+ if (ins == NULL) {
|
|
|
|
+ ins = malloc (sizeof (*ins));
|
|
|
|
+ if (UNLIKELY (!ins)) return LSUP_MEM_ERR;
|
|
|
|
+ ins->key = spok[i];
|
|
|
|
+ ins->sterm = LSUP_striple_pos (sspo, i);
|
|
|
|
+ HASH_ADD (hh, it->store->idx, key, KLEN, ins);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Add triple.
|
|
|
|
+ TRACE ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
|
|
|
|
+
|
|
|
|
+ TripleEntry *k_ins = NULL;
|
|
|
|
+ HASH_FIND (hh, it->store->keys, spok, TRP_KLEN, k_ins);
|
|
|
|
+ if (k_ins == NULL) {
|
|
|
|
+ k_ins = malloc (sizeof (*k_ins));
|
|
|
|
+ if (UNLIKELY (!k_ins)) return LSUP_MEM_ERR;
|
|
|
|
+
|
|
|
|
+ memcpy (k_ins->key, spok, TRP_KLEN);
|
|
|
|
+ HASH_ADD (hh, it->store->keys, key, TRP_KLEN, k_ins);
|
|
|
|
+
|
|
|
|
+ it->i++;
|
|
|
|
+ rc = LSUP_OK;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+LSUP_htstore_add_done (HTIterator *it)
|
|
|
|
+{ free (it); }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+LSUP_rc
|
|
|
|
+LSUP_htstore_remove(
|
|
|
|
+ LSUP_HTStore *store, const LSUP_SerTriple *sspo, size_t *ct)
|
|
|
|
+{
|
|
|
|
+ LSUP_HTIterator *it = LSUP_htstore_lookup (store, sspo, ct);
|
|
|
|
+ if (UNLIKELY (!it)) return LSUP_DB_ERR;
|
|
|
|
+
|
|
|
|
+ *ct = 0;
|
|
|
|
+ while (htiter_next_key (it)) {
|
|
|
|
+ HASH_DEL (store->keys, it->spok);
|
|
|
|
+ free (it->spok);
|
|
|
|
+ //if (UNLIKELY (rc < 0)) return rc;
|
|
|
|
+
|
|
|
|
+ (*ct) ++;
|
|
|
|
+ }
|
|
|
|
+ // TODO clean up orphan indices in separate (async, scheduled) function.
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+HTIterator *
|
|
|
|
+LSUP_htstore_lookup (HTStore *store, const LSUP_SerTriple *sspo, size_t *ct)
|
|
|
|
+{
|
|
|
|
+ HTIterator *it = malloc (sizeof (*it));
|
|
|
|
+ if (UNLIKELY (!it)) return NULL;
|
|
|
|
+
|
|
|
|
+ it->store = store;
|
|
|
|
+ //it->cur = 0;
|
|
|
|
+ it->rc = LSUP_END;
|
|
|
|
+
|
|
|
|
+ if (HASH_COUNT (store->keys) == 0) return it;
|
|
|
|
+
|
|
|
|
+ LSUP_TripleKey spok = {
|
|
|
|
+ LSUP_buffer_hash (sspo->s),
|
|
|
|
+ LSUP_buffer_hash (sspo->p),
|
|
|
|
+ LSUP_buffer_hash (sspo->o),
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // s p o
|
|
|
|
+ if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
|
|
|
|
+ memcpy (it->luk, spok, sizeof (LSUP_TripleKey));
|
|
|
|
+ it->eq_fn = NULL;
|
|
|
|
+
|
|
|
|
+ } else if (spok[0] != NULL_KEY) {
|
|
|
|
+ it->luk[0] = spok[0];
|
|
|
|
+
|
|
|
|
+ // s p ?
|
|
|
|
+ if (spok[1] != NULL_KEY) {
|
|
|
|
+ it->luk[1] = spok[1];
|
|
|
|
+ it->eq_fn = lookup_spk_eq_fn;
|
|
|
|
+
|
|
|
|
+ // s ? o
|
|
|
|
+ } else if (spok[2] != NULL_KEY) {
|
|
|
|
+ it->luk[1] = spok[2];
|
|
|
|
+ it->eq_fn = lookup_sok_eq_fn;
|
|
|
|
+
|
|
|
|
+ // s ? ?
|
|
|
|
+ } else {
|
|
|
|
+ it->eq_fn = lookup_sk_eq_fn;
|
|
|
|
+ }
|
|
|
|
+ //it->cur = 0;
|
|
|
|
+
|
|
|
|
+ } else if (spok[1] != NULL_KEY) {
|
|
|
|
+ it->luk[0] = spok[1];
|
|
|
|
+
|
|
|
|
+ // ? p o
|
|
|
|
+ if (spok[2] != NULL_KEY) {
|
|
|
|
+ it->luk[1] = spok[2];
|
|
|
|
+ it->eq_fn = lookup_pok_eq_fn;
|
|
|
|
+
|
|
|
|
+ // ? p ?
|
|
|
|
+ } else it->eq_fn = lookup_pk_eq_fn;
|
|
|
|
+
|
|
|
|
+ // ? ? o
|
|
|
|
+ } else if (spok[2] != NULL_KEY) {
|
|
|
|
+ it->luk[0] = spok[2];
|
|
|
|
+ it->eq_fn = lookup_ok_eq_fn;
|
|
|
|
+
|
|
|
|
+ // ? ? ?
|
|
|
|
+ } else it->eq_fn = lookup_none_eq_fn;
|
|
|
|
+
|
|
|
|
+ it->spok = it->store->keys->hh.next;
|
|
|
|
+ it->rc = it->spok == NULL ? LSUP_END : LSUP_OK;
|
|
|
|
+
|
|
|
|
+ return it;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+LSUP_htiter_free (LSUP_HTIterator *it)
|
|
|
|
+{ free (it); }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+size_t
|
|
|
|
+LSUP_htiter_cur (LSUP_HTIterator *it)
|
|
|
|
+{ return it->i; }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+LSUP_rc
|
|
|
|
+LSUP_htiter_next (HTIterator *it, LSUP_SerTriple *sspo)
|
|
|
|
+{
|
|
|
|
+ LSUP_rc rc = htiter_next_key (it);
|
|
|
|
+ if (UNLIKELY (rc != LSUP_OK)) return rc;
|
|
|
|
+
|
|
|
|
+ IndexEntry *cur;
|
|
|
|
+
|
|
|
|
+ HASH_FIND (hh, it->store->idx, it->spok + 0, KLEN, cur);
|
|
|
|
+ sspo->s = cur->sterm;
|
|
|
|
+ HASH_FIND (hh, it->store->idx, it->spok + 1, KLEN, cur);
|
|
|
|
+ sspo->p = cur->sterm;
|
|
|
|
+ HASH_FIND (hh, it->store->idx, it->spok + 2, KLEN, cur);
|
|
|
|
+ sspo->o = cur->sterm;
|
|
|
|
+
|
|
|
|
+ if (!sspo->s || !sspo->p || !sspo->o) {
|
|
|
|
+ sspo = NULL;
|
|
|
|
+ return LSUP_DB_ERR;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* * * Statics * * */
|
|
|
|
+
|
|
|
|
+static inline LSUP_rc
|
|
|
|
+htiter_next_key (HTIterator *it)
|
|
|
|
+{
|
|
|
|
+ // Loop as long as there are entries, and until a match is found.
|
|
|
|
+ for (;;) {
|
|
|
|
+ if (it->rc != LSUP_OK) return it->rc;
|
|
|
|
+ if (it->eq_fn (it->spok->key, it->luk)) return LSUP_OK;
|
|
|
|
+
|
|
|
|
+ TripleEntry *cur = NULL, *tmp = NULL;
|
|
|
|
+ HASH_ITER (hh, it->store->keys, cur, tmp);
|
|
|
|
+ if (cur != NULL) {
|
|
|
|
+ it->spok = cur;
|
|
|
|
+ it->rc = LSUP_OK;
|
|
|
|
+ } else it->rc = LSUP_NORESULT;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+inline static LSUP_rc
|
|
|
|
+tkey_to_strp (
|
|
|
|
+ const HTStore *store, const LSUP_Key spok[], LSUP_SerTriple *sspo)
|
|
|
|
+{
|
|
|
|
+ for (unsigned i = 0; i < 3; i++) {
|
|
|
|
+ IndexEntry *cur = NULL;
|
|
|
|
+ HASH_FIND (hh, store->idx, spok + i, KLEN, cur);
|
|
|
|
+ if (!UNLIKELY (cur)) return LSUP_ERROR;
|
|
|
|
+
|
|
|
|
+ memcpy (LSUP_striple_pos (sspo, i), cur->sterm, sizeof (*cur->sterm));
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|