mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			3674 lines
		
	
	
	
		
			110 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3674 lines
		
	
	
	
		
			110 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -*- c-basic-offset: 2 -*- */
 | |
| /*
 | |
|   Copyright(C) 2009-2017 Brazil
 | |
| 
 | |
|   This library is free software; you can redistribute it and/or
 | |
|   modify it under the terms of the GNU Lesser General Public
 | |
|   License version 2.1 as published by the Free Software Foundation.
 | |
| 
 | |
|   This library 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
 | |
|   Lesser General Public License for more details.
 | |
| 
 | |
|   You should have received a copy of the GNU Lesser General Public
 | |
|   License along with this library; if not, write to the Free Software
 | |
|   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
 | |
| */
 | |
| #include "grn.h"
 | |
| #include <string.h>
 | |
| #include <limits.h>
 | |
| #include "grn_pat.h"
 | |
| #include "grn_output.h"
 | |
| #include "grn_util.h"
 | |
| #include "grn_normalizer.h"
 | |
| 
 | |
| #define GRN_PAT_DELETED (GRN_ID_MAX + 1)
 | |
| 
 | |
| #define GRN_PAT_SEGMENT_SIZE 0x400000
 | |
| #define W_OF_KEY_IN_A_SEGMENT 22
 | |
| #define W_OF_PAT_IN_A_SEGMENT 18
 | |
| #define W_OF_SIS_IN_A_SEGMENT 19
 | |
| #define KEY_MASK_IN_A_SEGMENT 0x3fffff
 | |
| #define PAT_MASK_IN_A_SEGMENT 0x3ffff
 | |
| #define SIS_MASK_IN_A_SEGMENT 0x7ffff
 | |
| #define SEG_NOT_ASSIGNED 0xffff
 | |
| #define GRN_PAT_MAX_SEGMENT 0x1000
 | |
| #define GRN_PAT_MDELINFOS (GRN_PAT_NDELINFOS - 1)
 | |
| 
 | |
| #define GRN_PAT_BIN_KEY 0x70000
 | |
| 
 | |
| typedef struct {
 | |
|   grn_id lr[2];
 | |
|   /*
 | |
|     lr[0]: the left node.
 | |
|     lr[1]: the right node.
 | |
| 
 | |
|     The left node has 0 at the nth bit at the nth byte.
 | |
|     The right node has 1 at the nth bit at the nth byte.
 | |
|     'check' value indicate 'at the nth bit at the nth byte'.
 | |
| 
 | |
|     The both available nodes has larger check value rather
 | |
|     than the current node.
 | |
| 
 | |
|     The first node (PAT_AT(pat, GRN_ID_NIL, node)) has only
 | |
|     the right node and the node is the start point.
 | |
|    */
 | |
|   uint32_t key;
 | |
|   /*
 | |
|     PAT_IMD(node) == 0: key bytes offset in memory map.
 | |
|     PAT_IMD(node) == 1: the key bytes.
 | |
|    */
 | |
|   uint16_t check;
 | |
|   /*
 | |
|     nth byte: 12, nth bit: 3, terminated: 1
 | |
| 
 | |
|     nth byte is different in key bytes: (check >> 4): max == 4095
 | |
|     the left most byte is the 0th byte and the right most byte is the 11th byte.
 | |
| 
 | |
|     nth bit is different in nth byte: ((check >> 1) & 0b111)
 | |
|     the left most bit is the 0th bit and the right most bit is the 7th bit.
 | |
| 
 | |
|     terminated: (check & 0b1)
 | |
|     terminated == 1: key is terminated.
 | |
|    */
 | |
|   uint16_t bits;
 | |
|   /* length: 13, immediate: 1, deleting: 1 */
 | |
| } pat_node;
 | |
| 
 | |
| #define PAT_DELETING  (1<<1)
 | |
| #define PAT_IMMEDIATE (1<<2)
 | |
| 
 | |
| #define PAT_DEL(x) ((x)->bits & PAT_DELETING)
 | |
| #define PAT_IMD(x) ((x)->bits & PAT_IMMEDIATE)
 | |
| #define PAT_LEN(x) (((x)->bits >> 3) + 1)
 | |
| #define PAT_CHK(x) ((x)->check)
 | |
| #define PAT_DEL_ON(x) ((x)->bits |= PAT_DELETING)
 | |
| #define PAT_IMD_ON(x) ((x)->bits |= PAT_IMMEDIATE)
 | |
| #define PAT_DEL_OFF(x) ((x)->bits &= ~PAT_DELETING)
 | |
| #define PAT_IMD_OFF(x) ((x)->bits &= ~PAT_IMMEDIATE)
 | |
| #define PAT_LEN_SET(x,v) ((x)->bits = ((x)->bits & ((1<<3) - 1))|(((v) - 1) << 3))
 | |
| #define PAT_CHK_SET(x,v) ((x)->check = (v))
 | |
| 
 | |
| typedef struct {
 | |
|   grn_id children;
 | |
|   grn_id sibling;
 | |
| } sis_node;
 | |
| 
 | |
| enum {
 | |
|   segment_key = 0,
 | |
|   segment_pat = 1,
 | |
|   segment_sis = 2
 | |
| };
 | |
| 
 | |
| void grn_p_pat_node(grn_ctx *ctx, grn_pat *pat, pat_node *node);
 | |
| 
 | |
| /* error utilities */
 | |
| inline static int
 | |
| grn_pat_name(grn_ctx *ctx, grn_pat *pat, char *buffer, int buffer_size)
 | |
| {
 | |
|   int name_size;
 | |
| 
 | |
|   if (DB_OBJ(pat)->id == GRN_ID_NIL) {
 | |
|     grn_strcpy(buffer, buffer_size, "(anonymous)");
 | |
|     name_size = strlen(buffer);
 | |
|   } else {
 | |
|     name_size = grn_obj_name(ctx, (grn_obj *)pat, buffer, buffer_size);
 | |
|   }
 | |
| 
 | |
|   return name_size;
 | |
| }
 | |
| 
 | |
| /* bit operation */
 | |
| 
 | |
| #define nth_bit(key,n,l) ((((key)[(n)>>4]) >> (7 - (((n)>>1) & 7))) & 1)
 | |
| 
 | |
| /* segment operation */
 | |
| 
 | |
| /* patricia array operation */
 | |
| 
 | |
| #define PAT_AT(pat,id,n) do {\
 | |
|   int flags = 0;\
 | |
|   GRN_IO_ARRAY_AT(pat->io, segment_pat, id, &flags, n);\
 | |
| } while (0)
 | |
| 
 | |
| inline static pat_node *
 | |
| pat_get(grn_ctx *ctx, grn_pat *pat, grn_id id)
 | |
| {
 | |
|   pat_node *res;
 | |
|   int flags = GRN_TABLE_ADD;
 | |
|   if (id > GRN_ID_MAX) { return NULL; }
 | |
|   GRN_IO_ARRAY_AT(pat->io, segment_pat, id, &flags, res);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| /* sis operation */
 | |
| 
 | |
| inline static sis_node *
 | |
| sis_at(grn_ctx *ctx, grn_pat *pat, grn_id id)
 | |
| {
 | |
|   sis_node *res;
 | |
|   int flags = 0;
 | |
|   if (id > GRN_ID_MAX) { return NULL; }
 | |
|   GRN_IO_ARRAY_AT(pat->io, segment_sis, id, &flags, res);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| inline static sis_node *
 | |
| sis_get(grn_ctx *ctx, grn_pat *pat, grn_id id)
 | |
| {
 | |
|   sis_node *res;
 | |
|   int flags = GRN_TABLE_ADD;
 | |
|   if (id > GRN_ID_MAX) { return NULL; }
 | |
|   GRN_IO_ARRAY_AT(pat->io, segment_sis, id, &flags, res);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| #define MAX_LEVEL 16
 | |
| 
 | |
| static void
 | |
| sis_collect(grn_ctx *ctx, grn_pat *pat, grn_hash *h, grn_id id, uint32_t level)
 | |
| {
 | |
|   uint32_t *offset;
 | |
|   sis_node *sl = sis_at(ctx, pat, id);
 | |
|   if (sl) {
 | |
|     grn_id sid = sl->children;
 | |
|     while (sid && sid != id) {
 | |
|       if (grn_hash_add(ctx, h, &sid, sizeof(grn_id), (void **) &offset, NULL)) {
 | |
|         *offset = level;
 | |
|         if (level < MAX_LEVEL) { sis_collect(ctx, pat, h, sid, level + 1); }
 | |
|         if (!(sl = sis_at(ctx, pat, sid))) { break; }
 | |
|         sid = sl->sibling;
 | |
|       } else {
 | |
|         /* todo : must be handled */
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* key operation */
 | |
| 
 | |
| #define KEY_AT(pat,pos,ptr,addp) do {\
 | |
|   int flags = addp;\
 | |
|   GRN_IO_ARRAY_AT(pat->io, segment_key, pos, &flags, ptr);\
 | |
| } while (0)
 | |
| 
 | |
| inline static uint32_t
 | |
| key_put(grn_ctx *ctx, grn_pat *pat, const uint8_t *key, uint32_t len)
 | |
| {
 | |
|   uint32_t res, ts;
 | |
| //  if (len >= GRN_PAT_SEGMENT_SIZE) { return 0; /* error */ }
 | |
|   res = pat->header->curr_key;
 | |
|   if (res < GRN_PAT_MAX_TOTAL_KEY_SIZE &&
 | |
|       len > GRN_PAT_MAX_TOTAL_KEY_SIZE - res) {
 | |
|     char name[GRN_TABLE_MAX_KEY_SIZE];
 | |
|     int name_size;
 | |
|     name_size = grn_pat_name(ctx, pat, name, GRN_TABLE_MAX_KEY_SIZE);
 | |
|     ERR(GRN_NOT_ENOUGH_SPACE,
 | |
|         "[pat][key][put] total key size is over: <%.*s>: "
 | |
|         "max=%u: current=%u: new key size=%u",
 | |
|         name_size, name,
 | |
|         GRN_PAT_MAX_TOTAL_KEY_SIZE,
 | |
|         res,
 | |
|         len);
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   ts = (res + len) >> W_OF_KEY_IN_A_SEGMENT;
 | |
|   if (res >> W_OF_KEY_IN_A_SEGMENT != ts) {
 | |
|     res = pat->header->curr_key = ts << W_OF_KEY_IN_A_SEGMENT;
 | |
|   }
 | |
|   {
 | |
|     uint8_t *dest;
 | |
|     KEY_AT(pat, res, dest, GRN_TABLE_ADD);
 | |
|     if (!dest) {
 | |
|       char name[GRN_TABLE_MAX_KEY_SIZE];
 | |
|       int name_size;
 | |
|       name_size = grn_pat_name(ctx, pat, name, GRN_TABLE_MAX_KEY_SIZE);
 | |
|       ERR(GRN_NO_MEMORY_AVAILABLE,
 | |
|           "[pat][key][put] failed to allocate memory for new key: <%.*s>: "
 | |
|           "new offset:%u key size:%u",
 | |
|           name_size, name,
 | |
|           res,
 | |
|           len);
 | |
|       return 0;
 | |
|     }
 | |
|     grn_memcpy(dest, key, len);
 | |
|   }
 | |
|   pat->header->curr_key += len;
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| inline static uint8_t *
 | |
| pat_node_get_key(grn_ctx *ctx, grn_pat *pat, pat_node *n)
 | |
| {
 | |
|   if (PAT_IMD(n)) {
 | |
|     return (uint8_t *) &n->key;
 | |
|   } else {
 | |
|     uint8_t *res;
 | |
|     KEY_AT(pat, n->key, res, 0);
 | |
|     return res;
 | |
|   }
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| pat_node_set_key(grn_ctx *ctx, grn_pat *pat, pat_node *n, const uint8_t *key, uint32_t len)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   if (!key || !len) { return GRN_INVALID_ARGUMENT; }
 | |
|   PAT_LEN_SET(n, len);
 | |
|   if (len <= sizeof(uint32_t)) {
 | |
|     PAT_IMD_ON(n);
 | |
|     grn_memcpy(&n->key, key, len);
 | |
|     rc = GRN_SUCCESS;
 | |
|   } else {
 | |
|     PAT_IMD_OFF(n);
 | |
|     n->key = key_put(ctx, pat, key, len);
 | |
|     rc = ctx->rc;
 | |
|   }
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| /* delinfo operation */
 | |
| 
 | |
| enum {
 | |
|   /* The delinfo is currently not used. */
 | |
|   DL_EMPTY = 0,
 | |
|   /*
 | |
|    * stat->d refers to a deleting node (in a tree).
 | |
|    * The deletion requires an additional operation.
 | |
|    */
 | |
|   DL_PHASE1,
 | |
|   /*
 | |
|    * stat->d refers to a deleted node (not in a tree).
 | |
|    * The node is pending for safety.
 | |
|    */
 | |
|   DL_PHASE2
 | |
| };
 | |
| 
 | |
| inline static grn_pat_delinfo *
 | |
| delinfo_search(grn_pat *pat, grn_id id)
 | |
| {
 | |
|   int i;
 | |
|   grn_pat_delinfo *di;
 | |
|   for (i = (pat->header->curr_del2) & GRN_PAT_MDELINFOS;
 | |
|        i != pat->header->curr_del;
 | |
|        i = (i + 1) & GRN_PAT_MDELINFOS) {
 | |
|     di = &pat->header->delinfos[i];
 | |
|     if (di->stat != DL_PHASE1) { continue; }
 | |
|     if (di->ld == id) { return di; }
 | |
|     if (di->d == id) { return di; }
 | |
|   }
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| delinfo_turn_2(grn_ctx *ctx, grn_pat *pat, grn_pat_delinfo *di)
 | |
| {
 | |
|   grn_id d, *p = NULL;
 | |
|   pat_node *ln, *dn;
 | |
|   // grn_log("delinfo_turn_2> di->d=%d di->ld=%d stat=%d", di->d, di->ld, di->stat);
 | |
|   if (di->stat != DL_PHASE1) {
 | |
|     return GRN_SUCCESS;
 | |
|   }
 | |
|   PAT_AT(pat, di->ld, ln);
 | |
|   if (!ln) {
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
|   d = di->d;
 | |
|   if (!d) {
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
|   PAT_AT(pat, d, dn);
 | |
|   if (!dn) {
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
|   PAT_DEL_OFF(ln);
 | |
|   PAT_DEL_OFF(dn);
 | |
|   {
 | |
|     grn_id *p0;
 | |
|     pat_node *rn;
 | |
|     int c0 = -1, c;
 | |
|     uint32_t len = PAT_LEN(dn) * 16;
 | |
|     const uint8_t *key = pat_node_get_key(ctx, pat, dn);
 | |
|     if (!key) {
 | |
|       return GRN_INVALID_ARGUMENT;
 | |
|     }
 | |
|     PAT_AT(pat, 0, rn);
 | |
|     p0 = &rn->lr[1];
 | |
|     for (;;) {
 | |
|       grn_id r = *p0;
 | |
|       if (!r) {
 | |
|         break;
 | |
|       }
 | |
|       if (r == d) {
 | |
|         p = p0;
 | |
|         break;
 | |
|       }
 | |
|       PAT_AT(pat, r, rn);
 | |
|       if (!rn) {
 | |
|         return GRN_FILE_CORRUPT;
 | |
|       }
 | |
|       c = PAT_CHK(rn);
 | |
|       if ((int) c <= (int) c0 || (int) len <= (int) c) {
 | |
|         break;
 | |
|       }
 | |
|       if (c & 1) {
 | |
|         p0 = (c + 1 < (int) len) ? &rn->lr[1] : &rn->lr[0];
 | |
|       } else {
 | |
|         p0 = &rn->lr[nth_bit((uint8_t *)key, c, len)];
 | |
|       }
 | |
|       c0 = c;
 | |
|     }
 | |
|   }
 | |
|   if (p) {
 | |
|     PAT_CHK_SET(ln, PAT_CHK(dn));
 | |
|     ln->lr[1] = dn->lr[1];
 | |
|     ln->lr[0] = dn->lr[0];
 | |
|     *p = di->ld;
 | |
|   } else {
 | |
|     /* debug */
 | |
|     int j;
 | |
|     grn_id dd;
 | |
|     grn_pat_delinfo *ddi;
 | |
|     GRN_LOG(ctx, GRN_LOG_DEBUG, "failed to find d=%d", d);
 | |
|     for (j = (pat->header->curr_del2 + 1) & GRN_PAT_MDELINFOS;
 | |
|          j != pat->header->curr_del;
 | |
|          j = (j + 1) & GRN_PAT_MDELINFOS) {
 | |
|       ddi = &pat->header->delinfos[j];
 | |
|       if (ddi->stat != DL_PHASE1) { continue; }
 | |
|       PAT_AT(pat, ddi->ld, ln);
 | |
|       if (!ln) { continue; }
 | |
|       if (!(dd = ddi->d)) { continue; }
 | |
|       if (d == ddi->ld) {
 | |
|         GRN_LOG(ctx, GRN_LOG_DEBUG, "found!!!, d(%d) become ld of (%d)", d, dd);
 | |
|       }
 | |
|     }
 | |
|     /* debug */
 | |
|   }
 | |
|   di->stat = DL_PHASE2;
 | |
|   di->d = d;
 | |
|   // grn_log("delinfo_turn_2< di->d=%d di->ld=%d", di->d, di->ld);
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| delinfo_turn_3(grn_ctx *ctx, grn_pat *pat, grn_pat_delinfo *di)
 | |
| {
 | |
|   pat_node *dn;
 | |
|   uint32_t size;
 | |
|   if (di->stat != DL_PHASE2) { return GRN_SUCCESS; }
 | |
|   PAT_AT(pat, di->d, dn);
 | |
|   if (!dn) { return GRN_INVALID_ARGUMENT; }
 | |
|   if (di->shared) {
 | |
|     PAT_IMD_ON(dn);
 | |
|     size = 0;
 | |
|   } else {
 | |
|     if (PAT_IMD(dn)) {
 | |
|       size = 0;
 | |
|     } else {
 | |
|       size = PAT_LEN(dn);
 | |
|     }
 | |
|   }
 | |
|   di->stat = DL_EMPTY;
 | |
|   //  dn->lr[1] = GRN_PAT_DELETED;
 | |
|   dn->lr[0] = pat->header->garbages[size];
 | |
|   pat->header->garbages[size] = di->d;
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| inline static grn_pat_delinfo *
 | |
| delinfo_new(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   grn_pat_delinfo *res = &pat->header->delinfos[pat->header->curr_del];
 | |
|   uint32_t n = (pat->header->curr_del + 1) & GRN_PAT_MDELINFOS;
 | |
|   int gap = ((n + GRN_PAT_NDELINFOS - pat->header->curr_del2) & GRN_PAT_MDELINFOS)
 | |
|             - (GRN_PAT_NDELINFOS / 2);
 | |
|   while (gap-- > 0) {
 | |
|     if (delinfo_turn_2(ctx, pat, &pat->header->delinfos[pat->header->curr_del2])) {
 | |
|       GRN_LOG(ctx, GRN_LOG_CRIT, "d2 failed: %d", pat->header->delinfos[pat->header->curr_del2].ld);
 | |
|     }
 | |
|     pat->header->curr_del2 = (pat->header->curr_del2 + 1) & GRN_PAT_MDELINFOS;
 | |
|   }
 | |
|   if ((int) n == (int) pat->header->curr_del3) {
 | |
|     if (delinfo_turn_3(ctx, pat, &pat->header->delinfos[pat->header->curr_del3])) {
 | |
|       GRN_LOG(ctx, GRN_LOG_CRIT, "d3 failed: %d", pat->header->delinfos[pat->header->curr_del3].ld);
 | |
|     }
 | |
|     pat->header->curr_del3 = (pat->header->curr_del3 + 1) & GRN_PAT_MDELINFOS;
 | |
|   }
 | |
|   pat->header->curr_del = n;
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| /* pat operation */
 | |
| 
 | |
| inline static grn_pat *
 | |
| _grn_pat_create(grn_ctx *ctx, grn_pat *pat,
 | |
|                 const char *path, uint32_t key_size,
 | |
|                 uint32_t value_size, uint32_t flags) {
 | |
|   grn_io *io;
 | |
|   pat_node *node0;
 | |
|   struct grn_pat_header *header;
 | |
|   uint32_t entry_size, w_of_element;
 | |
|   grn_encoding encoding = ctx->encoding;
 | |
|   if (flags & GRN_OBJ_KEY_WITH_SIS) {
 | |
|     entry_size = sizeof(sis_node) + value_size;
 | |
|   } else {
 | |
|     entry_size = value_size;
 | |
|   }
 | |
|   for (w_of_element = 0; (1 << w_of_element) < entry_size; w_of_element++) {
 | |
|     /* nop */
 | |
|   }
 | |
|   {
 | |
|     grn_io_array_spec array_spec[3];
 | |
|     array_spec[segment_key].w_of_element = 0;
 | |
|     array_spec[segment_key].max_n_segments = 0x400;
 | |
|     array_spec[segment_pat].w_of_element = 4;
 | |
|     array_spec[segment_pat].max_n_segments = 1 << (30 - (22 - 4));
 | |
|     array_spec[segment_sis].w_of_element = w_of_element;
 | |
|     array_spec[segment_sis].max_n_segments = 1 << (30 - (22 - w_of_element));
 | |
|     io = grn_io_create_with_array(ctx, path, sizeof(struct grn_pat_header),
 | |
|                                   GRN_PAT_SEGMENT_SIZE, grn_io_auto, 3, array_spec);
 | |
|   }
 | |
|   if (!io) { return NULL; }
 | |
|   if (encoding == GRN_ENC_DEFAULT) { encoding = grn_gctx.encoding; }
 | |
|   header = grn_io_header(io);
 | |
|   grn_io_set_type(io, GRN_TABLE_PAT_KEY);
 | |
|   header->flags = flags;
 | |
|   header->encoding = encoding;
 | |
|   header->key_size = key_size;
 | |
|   header->value_size = value_size;
 | |
|   header->n_entries = 0;
 | |
|   header->curr_rec = 0;
 | |
|   header->curr_key = 0;
 | |
|   header->curr_del = 0;
 | |
|   header->curr_del2 = 0;
 | |
|   header->curr_del3 = 0;
 | |
|   header->n_garbages = 0;
 | |
|   header->tokenizer = GRN_ID_NIL;
 | |
|   if (header->flags & GRN_OBJ_KEY_NORMALIZE) {
 | |
|     header->flags &= ~GRN_OBJ_KEY_NORMALIZE;
 | |
|     pat->normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
 | |
|     header->normalizer = grn_obj_id(ctx, pat->normalizer);
 | |
|   } else {
 | |
|     pat->normalizer = NULL;
 | |
|     header->normalizer = GRN_ID_NIL;
 | |
|   }
 | |
|   header->truncated = GRN_FALSE;
 | |
|   GRN_PTR_INIT(&(pat->token_filters), GRN_OBJ_VECTOR, GRN_ID_NIL);
 | |
|   pat->io = io;
 | |
|   pat->header = header;
 | |
|   pat->key_size = key_size;
 | |
|   pat->value_size = value_size;
 | |
|   pat->tokenizer = NULL;
 | |
|   pat->encoding = encoding;
 | |
|   pat->obj.header.flags = header->flags;
 | |
|   if (!(node0 = pat_get(ctx, pat, 0))) {
 | |
|     grn_io_close(ctx, io);
 | |
|     return NULL;
 | |
|   }
 | |
|   node0->lr[1] = 0;
 | |
|   node0->lr[0] = 0;
 | |
|   node0->key = 0;
 | |
|   return pat;
 | |
| }
 | |
| 
 | |
| grn_pat *
 | |
| grn_pat_create(grn_ctx *ctx, const char *path, uint32_t key_size,
 | |
|                uint32_t value_size, uint32_t flags)
 | |
| {
 | |
|   grn_pat *pat;
 | |
|   if (!(pat = GRN_CALLOC(sizeof(grn_pat)))) {
 | |
|     return NULL;
 | |
|   }
 | |
|   GRN_DB_OBJ_SET_TYPE(pat, GRN_TABLE_PAT_KEY);
 | |
|   if (!_grn_pat_create(ctx, pat, path, key_size, value_size, flags)) {
 | |
|     GRN_FREE(pat);
 | |
|     return NULL;
 | |
|   }
 | |
|   pat->cache = NULL;
 | |
|   pat->cache_size = 0;
 | |
|   pat->is_dirty = GRN_FALSE;
 | |
|   CRITICAL_SECTION_INIT(pat->lock);
 | |
|   return pat;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  grn_pat_cache_enable() and grn_pat_cache_disable() are not thread-safe.
 | |
|  So far, they can be used only from single threaded programs.
 | |
|  */
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_cache_enable(grn_ctx *ctx, grn_pat *pat, uint32_t cache_size)
 | |
| {
 | |
|   if (pat->cache || pat->cache_size) {
 | |
|     ERR(GRN_INVALID_ARGUMENT, "cache is already enabled");
 | |
|     return ctx->rc;
 | |
|   }
 | |
|   if (cache_size & (cache_size - 1)) {
 | |
|     ERR(GRN_INVALID_ARGUMENT, "cache_size(%u) must be a power of two", cache_size);
 | |
|     return ctx->rc;
 | |
|   }
 | |
|   if (!(pat->cache = GRN_CALLOC(cache_size * sizeof(grn_id)))) {
 | |
|     return ctx->rc;
 | |
|   }
 | |
|   pat->cache_size = cache_size;
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_pat_cache_disable(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   if (pat->cache) {
 | |
|     GRN_FREE(pat->cache);
 | |
|     pat->cache_size = 0;
 | |
|     pat->cache = NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| grn_pat *
 | |
| grn_pat_open(grn_ctx *ctx, const char *path)
 | |
| {
 | |
|   grn_io *io;
 | |
|   grn_pat *pat;
 | |
|   pat_node *node0;
 | |
|   struct grn_pat_header *header;
 | |
|   uint32_t io_type;
 | |
|   io = grn_io_open(ctx, path, grn_io_auto);
 | |
|   if (!io) { return NULL; }
 | |
|   header = grn_io_header(io);
 | |
|   io_type = grn_io_get_type(io);
 | |
|   if (io_type != GRN_TABLE_PAT_KEY) {
 | |
|     ERR(GRN_INVALID_FORMAT, "[table][pat] file type must be %#04x: <%#04x>",
 | |
|         GRN_TABLE_PAT_KEY, io_type);
 | |
|     grn_io_close(ctx, io);
 | |
|     return NULL;
 | |
|   }
 | |
|   if (!(pat = GRN_MALLOC(sizeof(grn_pat)))) {
 | |
|     grn_io_close(ctx, io);
 | |
|     return NULL;
 | |
|   }
 | |
|   GRN_DB_OBJ_SET_TYPE(pat, GRN_TABLE_PAT_KEY);
 | |
|   pat->io = io;
 | |
|   pat->header = header;
 | |
|   pat->key_size = header->key_size;
 | |
|   pat->value_size = header->value_size;
 | |
|   pat->encoding = header->encoding;
 | |
|   pat->tokenizer = grn_ctx_at(ctx, header->tokenizer);
 | |
|   if (header->flags & GRN_OBJ_KEY_NORMALIZE) {
 | |
|     header->flags &= ~GRN_OBJ_KEY_NORMALIZE;
 | |
|     pat->normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
 | |
|     header->normalizer = grn_obj_id(ctx, pat->normalizer);
 | |
|   } else {
 | |
|     pat->normalizer = grn_ctx_at(ctx, header->normalizer);
 | |
|   }
 | |
|   GRN_PTR_INIT(&(pat->token_filters), GRN_OBJ_VECTOR, GRN_ID_NIL);
 | |
|   pat->obj.header.flags = header->flags;
 | |
|   PAT_AT(pat, 0, node0);
 | |
|   if (!node0) {
 | |
|     grn_io_close(ctx, io);
 | |
|     GRN_FREE(pat);
 | |
|     return NULL;
 | |
|   }
 | |
|   pat->cache = NULL;
 | |
|   pat->cache_size = 0;
 | |
|   pat->is_dirty = GRN_FALSE;
 | |
|   CRITICAL_SECTION_INIT(pat->lock);
 | |
|   return pat;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * grn_pat_error_if_truncated() logs an error and returns its error code if
 | |
|  * a pat is truncated by another process.
 | |
|  * Otherwise, this function returns GRN_SUCCESS.
 | |
|  * Note that `ctx` and `pat` must be valid.
 | |
|  *
 | |
|  * FIXME: A pat should be reopened if possible.
 | |
|  */
 | |
| static grn_rc
 | |
| grn_pat_error_if_truncated(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   if (pat->header->truncated) {
 | |
|     ERR(GRN_FILE_CORRUPT,
 | |
|         "pat is truncated, please unmap or reopen the database");
 | |
|     return GRN_FILE_CORRUPT;
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_close(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   grn_rc rc;
 | |
| 
 | |
|   CRITICAL_SECTION_FIN(pat->lock);
 | |
| 
 | |
|   if (pat->is_dirty) {
 | |
|     uint32_t n_dirty_opens;
 | |
|     GRN_ATOMIC_ADD_EX(&(pat->header->n_dirty_opens), -1, n_dirty_opens);
 | |
|   }
 | |
| 
 | |
|   if ((rc = grn_io_close(ctx, pat->io))) {
 | |
|     ERR(rc, "grn_io_close failed");
 | |
|   } else {
 | |
|     grn_pvector_fin(ctx, &pat->token_filters);
 | |
|     if (pat->cache) { grn_pat_cache_disable(ctx, pat); }
 | |
|     GRN_FREE(pat);
 | |
|   }
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_remove(grn_ctx *ctx, const char *path)
 | |
| {
 | |
|   if (!path) {
 | |
|     ERR(GRN_INVALID_ARGUMENT, "path is null");
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
|   return grn_io_remove(ctx, path);
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_truncate(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   const char *io_path;
 | |
|   char *path;
 | |
|   uint32_t key_size, value_size, flags;
 | |
| 
 | |
|   rc = grn_pat_error_if_truncated(ctx, pat);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   if ((io_path = grn_io_path(pat->io)) && *io_path != '\0') {
 | |
|     if (!(path = GRN_STRDUP(io_path))) {
 | |
|       ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_path);
 | |
|       return GRN_NO_MEMORY_AVAILABLE;
 | |
|     }
 | |
|   } else {
 | |
|     path = NULL;
 | |
|   }
 | |
|   key_size = pat->key_size;
 | |
|   value_size = pat->value_size;
 | |
|   flags = pat->obj.header.flags;
 | |
|   if (path) {
 | |
|     pat->header->truncated = GRN_TRUE;
 | |
|   }
 | |
|   if ((rc = grn_io_close(ctx, pat->io))) { goto exit; }
 | |
|   grn_pvector_fin(ctx, &pat->token_filters);
 | |
|   pat->io = NULL;
 | |
|   if (path && (rc = grn_io_remove(ctx, path))) { goto exit; }
 | |
|   if (!_grn_pat_create(ctx, pat, path, key_size, value_size, flags)) {
 | |
|     rc = GRN_UNKNOWN_ERROR;
 | |
|   }
 | |
|   if (pat->cache && pat->cache_size) {
 | |
|     memset(pat->cache, 0, pat->cache_size * sizeof(grn_id));
 | |
|   }
 | |
| exit:
 | |
|   if (path) { GRN_FREE(path); }
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| inline static grn_id
 | |
| _grn_pat_add(grn_ctx *ctx, grn_pat *pat, const uint8_t *key, uint32_t size, uint32_t *new, uint32_t *lkey)
 | |
| {
 | |
|   grn_id r, r0, *p0, *p1 = NULL;
 | |
|   pat_node *rn, *rn0;
 | |
|   int c, c0 = -1, c1 = -1, len;
 | |
|   uint32_t cache_id = 0;
 | |
| 
 | |
|   *new = 0;
 | |
|   if (pat->cache) {
 | |
|     const uint8_t *p = key;
 | |
|     uint32_t length = size;
 | |
|     for (cache_id = 0; length--; p++) { cache_id = (cache_id * 37) + *p; }
 | |
|     cache_id &= (pat->cache_size - 1);
 | |
|     if (pat->cache[cache_id]) {
 | |
|       PAT_AT(pat, pat->cache[cache_id], rn);
 | |
|       if (rn) {
 | |
|         const uint8_t *k = pat_node_get_key(ctx, pat, rn);
 | |
|         if (k && size == PAT_LEN(rn) && !memcmp(k, key, size)) {
 | |
|           return pat->cache[cache_id];
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   len = (int)size * 16;
 | |
|   PAT_AT(pat, 0, rn0);
 | |
|   p0 = &rn0->lr[1];
 | |
|   if (*p0) {
 | |
|     uint32_t size2;
 | |
|     int xor, mask;
 | |
|     const uint8_t *s, *d;
 | |
|     for (;;) {
 | |
|       if (!(r0 = *p0)) {
 | |
|         if (!(s = pat_node_get_key(ctx, pat, rn0))) { return GRN_ID_NIL; }
 | |
|         size2 = PAT_LEN(rn0);
 | |
|         break;
 | |
|       }
 | |
|       PAT_AT(pat, r0, rn0);
 | |
|       if (!rn0) { return GRN_ID_NIL; }
 | |
|       if (c0 < rn0->check && rn0->check < len) {
 | |
|         c1 = c0; c0 = rn0->check;
 | |
|         p1 = p0;
 | |
|         if (c0 & 1) {
 | |
|           p0 = (c0 + 1 < len) ? &rn0->lr[1] : &rn0->lr[0];
 | |
|         } else {
 | |
|           p0 = &rn0->lr[nth_bit(key, c0, len)];
 | |
|         }
 | |
|       } else {
 | |
|         if (!(s = pat_node_get_key(ctx, pat, rn0))) { return GRN_ID_NIL; }
 | |
|         size2 = PAT_LEN(rn0);
 | |
|         if (size == size2 && !memcmp(s, key, size)) {
 | |
|           if (pat->cache) { pat->cache[cache_id] = r0; }
 | |
|           return r0;
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     {
 | |
|       uint32_t min = size > size2 ? size2 : size;
 | |
|       for (c = 0, d = key; min && *s == *d; c += 16, s++, d++, min--);
 | |
|       if (min) {
 | |
|         for (xor = *s ^ *d, mask = 0x80; !(xor & mask); mask >>= 1, c += 2);
 | |
|       } else {
 | |
|         c--;
 | |
|       }
 | |
|     }
 | |
|     if (c == c0 && !*p0) {
 | |
|       if (c < len - 2) { c += 2; }
 | |
|     } else {
 | |
|       if (c < c0) {
 | |
|         if (c > c1) {
 | |
|           p0 = p1;
 | |
|         } else {
 | |
|           PAT_AT(pat, 0, rn0);
 | |
|           p0 = &rn0->lr[1];
 | |
|           while ((r0 = *p0)) {
 | |
|             PAT_AT(pat, r0, rn0);
 | |
|             if (!rn0) { return GRN_ID_NIL; }
 | |
|             c0 = PAT_CHK(rn0);
 | |
|             if (c < c0) { break; }
 | |
|             if (c0 & 1) {
 | |
|               p0 = (c0 + 1 < len) ? &rn0->lr[1] : &rn0->lr[0];
 | |
|             } else {
 | |
|               p0 = &rn0->lr[nth_bit(key, c0, len)];
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if (c >= len) { return GRN_ID_NIL; }
 | |
|   } else {
 | |
|     c = len - 2;
 | |
|   }
 | |
|   {
 | |
|     uint32_t size2 = size > sizeof(uint32_t) ? size : 0;
 | |
|     if (*lkey && size2) {
 | |
|       if (pat->header->garbages[0]) {
 | |
|         r = pat->header->garbages[0];
 | |
|         PAT_AT(pat, r, rn);
 | |
|         if (!rn) { return GRN_ID_NIL; }
 | |
|         pat->header->n_entries++;
 | |
|         pat->header->n_garbages--;
 | |
|         pat->header->garbages[0] = rn->lr[0];
 | |
|       } else {
 | |
|         r = pat->header->curr_rec + 1;
 | |
|         rn = pat_get(ctx, pat, r);
 | |
|         if (!rn) { return GRN_ID_NIL; }
 | |
|         pat->header->curr_rec = r;
 | |
|         pat->header->n_entries++;
 | |
|       }
 | |
|       PAT_IMD_OFF(rn);
 | |
|       PAT_LEN_SET(rn, size);
 | |
|       rn->key = *lkey;
 | |
|     } else {
 | |
|       if (pat->header->garbages[size2]) {
 | |
|         uint8_t *keybuf;
 | |
|         r = pat->header->garbages[size2];
 | |
|         PAT_AT(pat, r, rn);
 | |
|         if (!rn) { return GRN_ID_NIL; }
 | |
|         if (!(keybuf = pat_node_get_key(ctx, pat, rn))) { return GRN_ID_NIL; }
 | |
|         pat->header->n_entries++;
 | |
|         pat->header->n_garbages--;
 | |
|         pat->header->garbages[size2] = rn->lr[0];
 | |
|         PAT_LEN_SET(rn, size);
 | |
|         grn_memcpy(keybuf, key, size);
 | |
|       } else {
 | |
|         r = pat->header->curr_rec + 1;
 | |
|         rn = pat_get(ctx, pat, r);
 | |
|         if (!rn) { return GRN_ID_NIL; }
 | |
|         if (pat_node_set_key(ctx, pat, rn, key, size)) { return GRN_ID_NIL; }
 | |
|         pat->header->curr_rec = r;
 | |
|         pat->header->n_entries++;
 | |
|       }
 | |
|       *lkey = rn->key;
 | |
|     }
 | |
|   }
 | |
|   PAT_CHK_SET(rn, c);
 | |
|   PAT_DEL_OFF(rn);
 | |
|   if ((c & 1) ? (c + 1 < len) : nth_bit(key, c, len)) {
 | |
|     rn->lr[1] = r;
 | |
|     rn->lr[0] = *p0;
 | |
|   } else {
 | |
|     rn->lr[1] = *p0;
 | |
|     rn->lr[0] = r;
 | |
|   }
 | |
|   // smp_wmb();
 | |
|   *p0 = r;
 | |
|   *new = 1;
 | |
|   if (pat->cache) { pat->cache[cache_id] = r; }
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| inline static grn_bool
 | |
| chop(grn_ctx *ctx, grn_pat *pat, const char **key, const char *end, uint32_t *lkey)
 | |
| {
 | |
|   size_t len = grn_charlen(ctx, *key, end);
 | |
|   if (len) {
 | |
|     *lkey += len;
 | |
|     *key += len;
 | |
|     return (end - *key) > 0;
 | |
|   } else {
 | |
|     return GRN_FALSE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| #define MAX_FIXED_KEY_SIZE (sizeof(int64_t))
 | |
| 
 | |
| #define KEY_NEEDS_CONVERT(pat,size) \
 | |
|   (!((pat)->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) && (size_t) (size) <= MAX_FIXED_KEY_SIZE)
 | |
| 
 | |
| #define KEY_ENC(pat,keybuf,key,size) do {\
 | |
|   switch ((pat)->obj.header.flags & GRN_OBJ_KEY_MASK) {\
 | |
|   case GRN_OBJ_KEY_UINT :\
 | |
|     if (((pat)->obj.header.domain != GRN_DB_TOKYO_GEO_POINT) &&\
 | |
|         ((pat)->obj.header.domain != GRN_DB_WGS84_GEO_POINT)) {\
 | |
|       grn_hton((keybuf), (key), (size));\
 | |
|       break;\
 | |
|     }\
 | |
|   case GRN_OBJ_KEY_GEO_POINT :\
 | |
|     grn_gton((keybuf), (key), (size));\
 | |
|     break;\
 | |
|   case GRN_OBJ_KEY_INT :\
 | |
|     grn_hton((keybuf), (key), (size));\
 | |
|     *((uint8_t *)(keybuf)) ^= 0x80;\
 | |
|     break;\
 | |
|   case GRN_OBJ_KEY_FLOAT :\
 | |
|     if ((size) == sizeof(int64_t)) {\
 | |
|       int64_t v = *(int64_t *)(key);\
 | |
|       v ^= ((v >> 63)|(1ULL << 63));\
 | |
|       grn_hton((keybuf), &v, (size));\
 | |
|     }\
 | |
|     break;\
 | |
|   }\
 | |
| } while (0)
 | |
| 
 | |
| #define KEY_DEC(pat,keybuf,key,size) do {\
 | |
|   switch ((pat)->obj.header.flags & GRN_OBJ_KEY_MASK) {\
 | |
|   case GRN_OBJ_KEY_UINT :\
 | |
|     if (((pat)->obj.header.domain != GRN_DB_TOKYO_GEO_POINT) &&\
 | |
|         ((pat)->obj.header.domain != GRN_DB_WGS84_GEO_POINT)) {\
 | |
|       grn_ntoh((keybuf), (key), (size));\
 | |
|       break;\
 | |
|     }\
 | |
|   case GRN_OBJ_KEY_GEO_POINT :\
 | |
|     grn_ntog((keybuf), (key), (size));\
 | |
|     break;\
 | |
|   case GRN_OBJ_KEY_INT :\
 | |
|     grn_ntohi((keybuf), (key), (size));\
 | |
|     break;\
 | |
|   case GRN_OBJ_KEY_FLOAT :\
 | |
|     if ((size) == sizeof(int64_t)) {\
 | |
|       int64_t v;\
 | |
|       grn_hton(&v, (key), (size));\
 | |
|       *((int64_t *)(keybuf)) = v ^ ((((int64_t)(v^(1ULL<<63)))>> 63)|(1ULL<<63)); \
 | |
|     }\
 | |
|     break;\
 | |
|   }\
 | |
| } while (0)
 | |
| 
 | |
| #define KEY_ENCODE(pat,keybuf,key,size) do {\
 | |
|   if (KEY_NEEDS_CONVERT(pat,size)) {\
 | |
|     KEY_ENC((pat), (keybuf), (key), (size));\
 | |
|     (key) = (keybuf);\
 | |
|   }\
 | |
| } while (0)
 | |
| 
 | |
| grn_id
 | |
| grn_pat_add(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size,
 | |
|             void **value, int *added)
 | |
| {
 | |
|   uint32_t new, lkey = 0;
 | |
|   grn_id r0;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return GRN_ID_NIL;
 | |
|   }
 | |
|   if (!key || !key_size) { return GRN_ID_NIL; }
 | |
|   if (key_size > GRN_TABLE_MAX_KEY_SIZE) {
 | |
|     ERR(GRN_INVALID_ARGUMENT, "too long key: (%u)", key_size);
 | |
|     return GRN_ID_NIL;
 | |
|   }
 | |
|   KEY_ENCODE(pat, keybuf, key, key_size);
 | |
|   r0 = _grn_pat_add(ctx, pat, (uint8_t *)key, key_size, &new, &lkey);
 | |
|   if (r0 == GRN_ID_NIL) { return GRN_ID_NIL; }
 | |
|   if (added) { *added = new; }
 | |
|   if (r0 && (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) &&
 | |
|       (*((uint8_t *)key) & 0x80)) { // todo: refine!!
 | |
|     sis_node *sl, *sr;
 | |
|     grn_id l = r0, r;
 | |
|     if (new && (sl = sis_get(ctx, pat, l))) {
 | |
|       const char *sis = key, *end = sis + key_size;
 | |
|       sl->children = l;
 | |
|       sl->sibling = 0;
 | |
|       while (chop(ctx, pat, &sis, end, &lkey)) {
 | |
|         if (!(*sis & 0x80)) { break; }
 | |
|         if (!(r = _grn_pat_add(ctx, pat, (uint8_t *)sis, end - sis, &new, &lkey))) {
 | |
|           break;
 | |
|         }
 | |
|         if (!(sr = sis_get(ctx, pat, r))) { break; }
 | |
|         if (new) {
 | |
|           sl->sibling = r;
 | |
|           sr->children = l;
 | |
|           sr->sibling = 0;
 | |
|         } else {
 | |
|           sl->sibling = sr->children;
 | |
|           sr->children = l;
 | |
|           break;
 | |
|         }
 | |
|         l = r;
 | |
|         sl = sr;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (r0 && value) {
 | |
|     byte *v = (byte *)sis_get(ctx, pat, r0);
 | |
|     if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
 | |
|       *value = v + sizeof(sis_node);
 | |
|     } else {
 | |
|       *value = v;
 | |
|     }
 | |
|   }
 | |
|   return r0;
 | |
| }
 | |
| 
 | |
| inline static grn_id
 | |
| _grn_pat_get(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size, void **value)
 | |
| {
 | |
|   grn_id r;
 | |
|   pat_node *rn;
 | |
|   int c0 = -1, c;
 | |
|   uint32_t len = key_size * 16;
 | |
|   PAT_AT(pat, 0, rn);
 | |
|   for (r = rn->lr[1]; r;) {
 | |
|     PAT_AT(pat, r, rn);
 | |
|     if (!rn) { break; /* corrupt? */ }
 | |
|     c = PAT_CHK(rn);
 | |
|     if ((int) len <= c) { break; }
 | |
|     if (c <= c0) {
 | |
|       const uint8_t *k = pat_node_get_key(ctx, pat, rn);
 | |
|       if (k && key_size == PAT_LEN(rn) && !memcmp(k, key, key_size)) {
 | |
|         if (value) {
 | |
|           byte *v = (byte *)sis_get(ctx, pat, r);
 | |
|           if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
 | |
|             *value = v + sizeof(sis_node);
 | |
|           } else {
 | |
|             *value = v;
 | |
|           }
 | |
|         }
 | |
|         return r;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     if (c & 1) {
 | |
|       r = (c + 1 < (int) len) ? rn->lr[1] : rn->lr[0];
 | |
|     } else {
 | |
|       r = rn->lr[nth_bit((uint8_t *)key, c, len)];
 | |
|     }
 | |
|     c0 = c;
 | |
|   }
 | |
|   return GRN_ID_NIL;
 | |
| }
 | |
| 
 | |
| grn_id
 | |
| grn_pat_get(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size, void **value)
 | |
| {
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return GRN_ID_NIL;
 | |
|   }
 | |
|   KEY_ENCODE(pat, keybuf, key, key_size);
 | |
|   return _grn_pat_get(ctx, pat, key, key_size, value);
 | |
| }
 | |
| 
 | |
| grn_id
 | |
| grn_pat_nextid(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
 | |
| {
 | |
|   grn_id r = GRN_ID_NIL;
 | |
|   if (pat && key) {
 | |
|     if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|       return GRN_ID_NIL;
 | |
|     }
 | |
|     if (!(r = pat->header->garbages[key_size > sizeof(uint32_t) ? key_size : 0])) {
 | |
|       r = pat->header->curr_rec + 1;
 | |
|     }
 | |
|   }
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| static void
 | |
| get_tc(grn_ctx *ctx, grn_pat *pat, grn_hash *h, pat_node *rn)
 | |
| {
 | |
|   grn_id id;
 | |
|   pat_node *node;
 | |
|   id = rn->lr[1];
 | |
|   if (id) {
 | |
|     PAT_AT(pat, id, node);
 | |
|     if (node) {
 | |
|       if (PAT_CHK(node) > PAT_CHK(rn)) {
 | |
|         get_tc(ctx, pat, h, node);
 | |
|       } else {
 | |
|         grn_hash_add(ctx, h, &id, sizeof(grn_id), NULL, NULL);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   id = rn->lr[0];
 | |
|   if (id) {
 | |
|     PAT_AT(pat, id, node);
 | |
|     if (node) {
 | |
|       if (PAT_CHK(node) > PAT_CHK(rn)) {
 | |
|         get_tc(ctx, pat, h, node);
 | |
|       } else {
 | |
|         grn_hash_add(ctx, h, &id, sizeof(grn_id), NULL, NULL);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_prefix_search(grn_ctx *ctx, grn_pat *pat,
 | |
|                       const void *key, uint32_t key_size, grn_hash *h)
 | |
| {
 | |
|   int c0 = -1, c;
 | |
|   const uint8_t *k;
 | |
|   uint32_t len = key_size * 16;
 | |
|   grn_id r;
 | |
|   pat_node *rn;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   grn_rc rc = grn_pat_error_if_truncated(ctx, pat);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   KEY_ENCODE(pat, keybuf, key, key_size);
 | |
|   PAT_AT(pat, 0, rn);
 | |
|   r = rn->lr[1];
 | |
|   while (r) {
 | |
|     PAT_AT(pat, r, rn);
 | |
|     if (!rn) { return GRN_FILE_CORRUPT; }
 | |
|     c = PAT_CHK(rn);
 | |
|     if (c0 < c && c < (int) len - 1) {
 | |
|       if (c & 1) {
 | |
|         r = (c + 1 < (int) len) ? rn->lr[1] : rn->lr[0];
 | |
|       } else {
 | |
|         r = rn->lr[nth_bit((uint8_t *)key, c, len)];
 | |
|       }
 | |
|       c0 = c;
 | |
|       continue;
 | |
|     }
 | |
|     if (!(k = pat_node_get_key(ctx, pat, rn))) { break; }
 | |
|     if (PAT_LEN(rn) < key_size) { break; }
 | |
|     if (!memcmp(k, key, key_size)) {
 | |
|       if (c >= (int) len - 1) {
 | |
|         get_tc(ctx, pat, h, rn);
 | |
|       } else {
 | |
|         grn_hash_add(ctx, h, &r, sizeof(grn_id), NULL, NULL);
 | |
|       }
 | |
|       return GRN_SUCCESS;
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   return GRN_END_OF_DATA;
 | |
| }
 | |
| 
 | |
| grn_hash *
 | |
| grn_pat_prefix_search2(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
 | |
| {
 | |
|   grn_hash *h;
 | |
|   if (!pat || !key) { return NULL; }
 | |
|   if ((h = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, 0))) {
 | |
|     if (grn_pat_prefix_search(ctx, pat, key, key_size, h)) {
 | |
|       grn_hash_close(ctx, h);
 | |
|       h = NULL;
 | |
|     }
 | |
|   }
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_suffix_search(grn_ctx *ctx, grn_pat *pat,
 | |
|                       const void *key, uint32_t key_size, grn_hash *h)
 | |
| {
 | |
|   grn_id r;
 | |
|   if ((r = grn_pat_get(ctx, pat, key, key_size, NULL))) {
 | |
|     uint32_t *offset;
 | |
|     if (grn_hash_add(ctx, h, &r, sizeof(grn_id), (void **) &offset, NULL)) {
 | |
|       *offset = 0;
 | |
|       if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) { sis_collect(ctx, pat, h, r, 1); }
 | |
|       return GRN_SUCCESS;
 | |
|     }
 | |
|   }
 | |
|   return GRN_END_OF_DATA;
 | |
| }
 | |
| 
 | |
| grn_hash *
 | |
| grn_pat_suffix_search2(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
 | |
| {
 | |
|   grn_hash *h;
 | |
|   if (!pat || !key) { return NULL; }
 | |
|   if ((h = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(uint32_t), 0))) {
 | |
|     if (grn_pat_suffix_search(ctx, pat, key, key_size, h)) {
 | |
|       grn_hash_close(ctx, h);
 | |
|       h = NULL;
 | |
|     }
 | |
|   }
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| grn_id
 | |
| grn_pat_lcp_search(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
 | |
| {
 | |
|   pat_node *rn;
 | |
|   grn_id r, r2 = GRN_ID_NIL;
 | |
|   uint32_t len = key_size * 16;
 | |
|   int c0 = -1, c;
 | |
|   if (!pat || !key) {
 | |
|     return GRN_ID_NIL;
 | |
|   }
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return GRN_ID_NIL;
 | |
|   }
 | |
|   if (!(pat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE)) { return GRN_ID_NIL; }
 | |
|   PAT_AT(pat, 0, rn);
 | |
|   for (r = rn->lr[1]; r;) {
 | |
|     PAT_AT(pat, r, rn);
 | |
|     if (!rn) { break; /* corrupt? */ }
 | |
|     c = PAT_CHK(rn);
 | |
|     if (c <= c0) {
 | |
|       if (PAT_LEN(rn) <= key_size) {
 | |
|         uint8_t *p = pat_node_get_key(ctx, pat, rn);
 | |
|         if (!p) { break; }
 | |
|         if (!memcmp(p, key, PAT_LEN(rn))) { return r; }
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     if ((int) len <= c) { break; }
 | |
|     if (c & 1) {
 | |
|       uint8_t *p;
 | |
|       pat_node *rn0;
 | |
|       grn_id r0 = rn->lr[0];
 | |
|       PAT_AT(pat, r0, rn0);
 | |
|       if (!rn0) { break; /* corrupt? */ }
 | |
|       p = pat_node_get_key(ctx, pat, rn0);
 | |
|       if (!p) { break; }
 | |
|       if (PAT_LEN(rn0) <= key_size && !memcmp(p, key, PAT_LEN(rn0))) { r2 = r0; }
 | |
|       r = (c + 1 < (int) len) ? rn->lr[1] : rn->lr[0];
 | |
|     } else {
 | |
|       r = rn->lr[nth_bit((uint8_t *)key, c, len)];
 | |
|     }
 | |
|     c0 = c;
 | |
|   }
 | |
|   return r2;
 | |
| }
 | |
| 
 | |
| static grn_id
 | |
| common_prefix_pat_node_get(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
 | |
| {
 | |
|   int c0 = -1, c;
 | |
|   const uint8_t *k;
 | |
|   uint32_t len = key_size * 16;
 | |
|   grn_id r;
 | |
|   pat_node *rn;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
| 
 | |
|   KEY_ENCODE(pat, keybuf, key, key_size);
 | |
|   PAT_AT(pat, 0, rn);
 | |
|   r = rn->lr[1];
 | |
|   while (r) {
 | |
|     PAT_AT(pat, r, rn);
 | |
|     if (!rn) { return GRN_ID_NIL; }
 | |
|     c = PAT_CHK(rn);
 | |
|     if (c0 < c && c < (int) len - 1) {
 | |
|       if (c & 1) {
 | |
|         r = (c + 1 < (int) len) ? rn->lr[1] : rn->lr[0];
 | |
|       } else {
 | |
|         r = rn->lr[nth_bit((uint8_t *)key, c, len)];
 | |
|       }
 | |
|       c0 = c;
 | |
|       continue;
 | |
|     }
 | |
|     if (!(k = pat_node_get_key(ctx, pat, rn))) { break; }
 | |
|     if (PAT_LEN(rn) < key_size) { break; }
 | |
|     if (!memcmp(k, key, key_size)) {
 | |
|       return r;
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   return GRN_ID_NIL;
 | |
| }
 | |
| 
 | |
| typedef struct {
 | |
|   grn_id id;
 | |
|   uint16_t distance;
 | |
| } fuzzy_heap_node;
 | |
| 
 | |
| typedef struct {
 | |
|   int n_entries;
 | |
|   int limit;
 | |
|   fuzzy_heap_node *nodes;
 | |
| } fuzzy_heap;
 | |
| 
 | |
| static inline fuzzy_heap *
 | |
| fuzzy_heap_open(grn_ctx *ctx, int max)
 | |
| {
 | |
|   fuzzy_heap *h = GRN_MALLOC(sizeof(fuzzy_heap));
 | |
|   if (!h) { return NULL; }
 | |
|   h->nodes = GRN_MALLOC(sizeof(fuzzy_heap_node) * max);
 | |
|   if (!h->nodes) {
 | |
|     GRN_FREE(h);
 | |
|     return NULL;
 | |
|   }
 | |
|   h->n_entries = 0;
 | |
|   h->limit = max;
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| static inline grn_bool
 | |
| fuzzy_heap_push(grn_ctx *ctx, fuzzy_heap *h, grn_id id, uint16_t distance)
 | |
| {
 | |
|   int n, n2;
 | |
|   fuzzy_heap_node node = {id, distance};
 | |
|   fuzzy_heap_node node2;
 | |
|   if (h->n_entries >= h->limit) {
 | |
|     int max = h->limit * 2;
 | |
|     fuzzy_heap_node *nodes = GRN_REALLOC(h->nodes, sizeof(fuzzy_heap) * max);
 | |
|     if (!h) {
 | |
|       return GRN_FALSE;
 | |
|     }
 | |
|     h->limit = max;
 | |
|     h->nodes = nodes;
 | |
|   }
 | |
|   h->nodes[h->n_entries] = node;
 | |
|   n = h->n_entries++;
 | |
|   while (n) {
 | |
|     n2 = (n - 1) >> 1;
 | |
|     if (h->nodes[n2].distance <= h->nodes[n].distance) { break; }
 | |
|     node2 = h->nodes[n];
 | |
|     h->nodes[n] = h->nodes[n2];
 | |
|     h->nodes[n2] = node2;
 | |
|     n = n2;
 | |
|   }
 | |
|   return GRN_TRUE;
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| fuzzy_heap_close(grn_ctx *ctx, fuzzy_heap *h)
 | |
| {
 | |
|   GRN_FREE(h->nodes);
 | |
|   GRN_FREE(h);
 | |
| }
 | |
| 
 | |
| #define DIST(ox,oy) (dists[((lx + 1) * (oy)) + (ox)])
 | |
| 
 | |
| inline static uint16_t
 | |
| calc_edit_distance_by_offset(grn_ctx *ctx,
 | |
|                              const char *sx, const char *ex,
 | |
|                              const char *sy, const char *ey,
 | |
|                              uint16_t *dists, uint32_t lx,
 | |
|                              uint32_t offset, uint32_t max_distance,
 | |
|                              grn_bool *can_transition, int flags)
 | |
| {
 | |
|   uint32_t cx, cy, x, y;
 | |
|   const char *px, *py;
 | |
| 
 | |
|   /* Skip already calculated rows */
 | |
|   for (py = sy, y = 1; py < ey && (cy = grn_charlen(ctx, py, ey)); py += cy, y++) {
 | |
|     if (py - sy >= offset) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   for (; py < ey && (cy = grn_charlen(ctx, py, ey)); py += cy, y++) {
 | |
|     /* children nodes will be no longer smaller than max distance
 | |
|      * with only insertion costs.
 | |
|      * This is end of row on allocated memory. */
 | |
|     if (y > lx + max_distance) {
 | |
|       *can_transition = GRN_FALSE;
 | |
|       return max_distance + 1;
 | |
|     }
 | |
| 
 | |
|     for (px = sx, x = 1; px < ex && (cx = grn_charlen(ctx, px, ex)); px += cx, x++) {
 | |
|       if (cx == cy && !memcmp(px, py, cx)) {
 | |
|         DIST(x, y) = DIST(x - 1, y - 1);
 | |
|       } else {
 | |
|         uint32_t a, b, c;
 | |
|         a = DIST(x - 1, y) + 1;
 | |
|         b = DIST(x, y - 1) + 1;
 | |
|         c = DIST(x - 1, y - 1) + 1;
 | |
|         DIST(x, y) = ((a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c));
 | |
|         if (flags & GRN_TABLE_FUZZY_SEARCH_WITH_TRANSPOSITION &&
 | |
|             x > 1 && y > 1 &&
 | |
|             cx == cy &&
 | |
|             memcmp(px, py - cy, cx) == 0 &&
 | |
|             memcmp(px - cx, py, cx) == 0) {
 | |
|           uint32_t t = DIST(x - 2, y - 2) + 1;
 | |
|           DIST(x, y) = ((DIST(x, y) < t) ? DIST(x, y) : t);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (lx) {
 | |
|     /* If there is no cell which is smaller than equal to max distance on end of row,
 | |
|      * children nodes will be no longer smaller than max distance */
 | |
|     *can_transition = GRN_FALSE;
 | |
|     for (x = 1; x <= lx; x++) {
 | |
|       if (DIST(x, y - 1) <= max_distance) {
 | |
|         *can_transition = GRN_TRUE;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return DIST(lx, y - 1);
 | |
| }
 | |
| 
 | |
| typedef struct {
 | |
|   const char *key;
 | |
|   int key_length;
 | |
|   grn_bool can_transition;
 | |
| } fuzzy_node;
 | |
| 
 | |
| inline static void
 | |
| _grn_pat_fuzzy_search(grn_ctx *ctx, grn_pat *pat, grn_id id,
 | |
|                       const char *key, uint32_t key_size,
 | |
|                       uint16_t *dists, uint32_t lx,
 | |
|                       int last_check, fuzzy_node *last_node,
 | |
|                       uint32_t max_distance, int flags, fuzzy_heap *heap)
 | |
| {
 | |
|   pat_node *node = NULL;
 | |
|   int check, len;
 | |
|   const char *k;
 | |
|   uint32_t offset = 0;
 | |
| 
 | |
|   PAT_AT(pat, id, node);
 | |
|   if (!node) {
 | |
|     return;
 | |
|   }
 | |
|   check = PAT_CHK(node);
 | |
|   len = PAT_LEN(node);
 | |
|   k = pat_node_get_key(ctx, pat, node);
 | |
| 
 | |
|   if (check > last_check) {
 | |
|     if (len >= last_node->key_length &&
 | |
|         !memcmp(k, last_node->key, last_node->key_length)) {
 | |
|       if (last_node->can_transition == GRN_FALSE) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|     _grn_pat_fuzzy_search(ctx, pat, node->lr[0],
 | |
|                           key, key_size, dists, lx,
 | |
|                           check, last_node,
 | |
|                           max_distance, flags, heap);
 | |
| 
 | |
|     _grn_pat_fuzzy_search(ctx, pat, node->lr[1],
 | |
|                           key, key_size, dists, lx,
 | |
|                           check, last_node,
 | |
|                           max_distance, flags, heap);
 | |
|   } else {
 | |
|     if (id) {
 | |
|       /* Set already calculated common prefix length */
 | |
|       if (len >= last_node->key_length &&
 | |
|           !memcmp(k, last_node->key, last_node->key_length)) {
 | |
|         if (last_node->can_transition == GRN_FALSE) {
 | |
|           return;
 | |
|         }
 | |
|         offset = last_node->key_length;
 | |
|       } else {
 | |
|         if (last_node->can_transition == GRN_FALSE) {
 | |
|           last_node->can_transition = GRN_TRUE;
 | |
|         }
 | |
|         if (last_node->key_length) {
 | |
|           const char *kp = k;
 | |
|           const char *ke = k + len;
 | |
|           const char *p = last_node->key;
 | |
|           const char *e = last_node->key + last_node->key_length;
 | |
|           int lp;
 | |
|           for (;p < e && kp < ke && (lp = grn_charlen(ctx, p, e));
 | |
|                p += lp, kp += lp) {
 | |
|             if (p + lp <= e && kp + lp <= ke && memcmp(p, kp, lp)) {
 | |
|               break;
 | |
|             }
 | |
|           }
 | |
|           offset = kp - k;
 | |
|         }
 | |
|       }
 | |
|       if (len - offset) {
 | |
|         uint16_t distance;
 | |
|         distance =
 | |
|           calc_edit_distance_by_offset(ctx,
 | |
|                                        key, key + key_size,
 | |
|                                        k, k + len,
 | |
|                                        dists, lx,
 | |
|                                        offset, max_distance,
 | |
|                                        &(last_node->can_transition), flags);
 | |
|         if (distance <= max_distance) {
 | |
|           fuzzy_heap_push(ctx, heap, id, distance);
 | |
|         }
 | |
|       }
 | |
|       last_node->key = k;
 | |
|       last_node->key_length = len;
 | |
|     }
 | |
|   }
 | |
|   return;
 | |
| }
 | |
| 
 | |
| #define HEAP_SIZE 256
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_fuzzy_search(grn_ctx *ctx, grn_pat *pat,
 | |
|                      const void *key, uint32_t key_size,
 | |
|                      grn_fuzzy_search_optarg *args, grn_hash *h)
 | |
| {
 | |
|   pat_node *node;
 | |
|   grn_id id;
 | |
|   uint16_t *dists;
 | |
|   uint32_t lx, len, x, y, i;
 | |
|   const char *s = key;
 | |
|   const char *e = (const char *)key + key_size;
 | |
|   fuzzy_node last_node;
 | |
|   fuzzy_heap *heap;
 | |
|   uint32_t max_distance = 1;
 | |
|   uint32_t max_expansion = 0;
 | |
|   uint32_t prefix_match_size = 0;
 | |
|   int flags = 0;
 | |
|   grn_rc rc = grn_pat_error_if_truncated(ctx, pat);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   if (args) {
 | |
|     max_distance = args->max_distance;
 | |
|     max_expansion = args->max_expansion;
 | |
|     prefix_match_size = args->prefix_match_size;
 | |
|     flags = args->flags;
 | |
|   }
 | |
|   if (key_size > GRN_TABLE_MAX_KEY_SIZE ||
 | |
|       max_distance > GRN_TABLE_MAX_KEY_SIZE ||
 | |
|       prefix_match_size > key_size) {
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
| 
 | |
|   heap = fuzzy_heap_open(ctx, HEAP_SIZE);
 | |
|   if (!heap) {
 | |
|     return GRN_NO_MEMORY_AVAILABLE;
 | |
|   }
 | |
| 
 | |
|   PAT_AT(pat, GRN_ID_NIL, node);
 | |
|   id = node->lr[1];
 | |
| 
 | |
|   if (prefix_match_size) {
 | |
|     grn_id tid;
 | |
|     tid = common_prefix_pat_node_get(ctx, pat, key, prefix_match_size);
 | |
|     if (tid != GRN_ID_NIL) {
 | |
|       id = tid;
 | |
|     } else {
 | |
|       return GRN_END_OF_DATA;
 | |
|     }
 | |
|   }
 | |
|   for (lx = 0; s < e && (len = grn_charlen(ctx, s, e)); s += len) {
 | |
|     lx++;
 | |
|   }
 | |
|   dists = GRN_MALLOC((lx + 1) * (lx + max_distance + 1) * sizeof(uint16_t));
 | |
|   if (!dists) {
 | |
|     return GRN_NO_MEMORY_AVAILABLE;
 | |
|   }
 | |
| 
 | |
|   for (x = 0; x <= lx; x++) { DIST(x, 0) = x; }
 | |
|   for (y = 0; y <= lx + max_distance ; y++) { DIST(0, y) = y; }
 | |
| 
 | |
|   last_node.key = NULL;
 | |
|   last_node.key_length = 0;
 | |
|   last_node.can_transition = GRN_TRUE;
 | |
|   _grn_pat_fuzzy_search(ctx, pat, id,
 | |
|                         key, key_size, dists, lx,
 | |
|                         -1, &last_node, max_distance, flags, heap);
 | |
|   GRN_FREE(dists);
 | |
|   for (i = 0; i < (uint32_t) heap->n_entries; i++) {
 | |
|     if (max_expansion > 0 && i >= max_expansion) {
 | |
|       break;
 | |
|     }
 | |
|     if (DB_OBJ(h)->header.flags & GRN_OBJ_WITH_SUBREC) {
 | |
|       grn_rset_recinfo *ri;
 | |
|       if (grn_hash_add(ctx, h, &(heap->nodes[i].id), sizeof(grn_id), (void **)&ri, NULL)) {
 | |
|         ri->score = max_distance - heap->nodes[i].distance + 1;
 | |
|       }
 | |
|     } else {
 | |
|       grn_hash_add(ctx, h, &(heap->nodes[i].id), sizeof(grn_id), NULL, NULL);
 | |
|     }
 | |
|   }
 | |
|   fuzzy_heap_close(ctx, heap);
 | |
|   if (grn_hash_size(ctx, h)) {
 | |
|     return GRN_SUCCESS;
 | |
|   } else {
 | |
|     return GRN_END_OF_DATA;
 | |
|   }
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| _grn_pat_del(grn_ctx *ctx, grn_pat *pat, const char *key, uint32_t key_size, int shared,
 | |
|              grn_table_delete_optarg *optarg)
 | |
| {
 | |
|   grn_pat_delinfo *di;
 | |
|   pat_node *rn, *rn0 = NULL, *rno = NULL;
 | |
|   int c = -1, c0 = -1, ch;
 | |
|   uint32_t len = key_size * 16;
 | |
|   grn_id r, otherside, *proot, *p, *p0 = NULL;
 | |
| 
 | |
|   /* delinfo_new() must be called before searching for rn. */
 | |
|   di = delinfo_new(ctx, pat);
 | |
|   di->shared = shared;
 | |
| 
 | |
|   /*
 | |
|    * Search a patricia tree for a given key.
 | |
|    * If the key exists, get its output node.
 | |
|    *
 | |
|    * rn, rn0: the output node and its previous node.
 | |
|    * rno: the other side of rn (the other destination of rn0).
 | |
|    * c, c0: checks of rn0 and its previous node.
 | |
|    * p, p0: pointers to transitions (IDs) that refer to rn and rn0.
 | |
|    */
 | |
|   PAT_AT(pat, 0, rn);
 | |
|   proot = p = &rn->lr[1];
 | |
|   for (;;) {
 | |
|     r = *p;
 | |
|     if (!r) {
 | |
|       return GRN_INVALID_ARGUMENT;
 | |
|     }
 | |
|     PAT_AT(pat, r, rn);
 | |
|     if (!rn) {
 | |
|       return GRN_FILE_CORRUPT;
 | |
|     }
 | |
|     ch = PAT_CHK(rn);
 | |
|     if ((int) len <= ch) {
 | |
|       return GRN_INVALID_ARGUMENT;
 | |
|     }
 | |
|     if (c >= ch) {
 | |
|       /* Output node found. */
 | |
|       const uint8_t *k = pat_node_get_key(ctx, pat, rn);
 | |
|       if (!k) {
 | |
|         return GRN_INVALID_ARGUMENT;
 | |
|       }
 | |
|       if (key_size != PAT_LEN(rn) || memcmp(k, key, key_size)) {
 | |
|         return GRN_INVALID_ARGUMENT;
 | |
|       }
 | |
|       /* Given key found. */
 | |
|       break;
 | |
|     }
 | |
|     c0 = c;
 | |
|     p0 = p;
 | |
|     c = ch;
 | |
|     if (c & 1) {
 | |
|       p = (c + 1 < (int) len) ? &rn->lr[1] : &rn->lr[0];
 | |
|     } else {
 | |
|       p = &rn->lr[nth_bit((uint8_t *)key, c, len)];
 | |
|     }
 | |
|     rn0 = rn;
 | |
|   }
 | |
|   if (optarg && optarg->func &&
 | |
|       !optarg->func(ctx, (grn_obj *)pat, r, optarg->func_arg)) {
 | |
|     return GRN_SUCCESS;
 | |
|   }
 | |
|   if (rn0->lr[0] == rn0->lr[1]) {
 | |
|     GRN_LOG(ctx, GRN_LOG_DEBUG, "*p0 (%d), rn0->lr[0] == rn0->lr[1] (%d)",
 | |
|             *p0, rn0->lr[0]);
 | |
|     return GRN_FILE_CORRUPT;
 | |
|   }
 | |
|   otherside = (rn0->lr[1] == r) ? rn0->lr[0] : rn0->lr[1];
 | |
|   if (otherside) {
 | |
|     PAT_AT(pat, otherside, rno);
 | |
|     if (!rno) {
 | |
|       return GRN_FILE_CORRUPT;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (rn == rn0) {
 | |
|     /* The last transition (p) is a self-loop. */
 | |
|     di->stat = DL_PHASE2;
 | |
|     di->d = r;
 | |
|     if (otherside) {
 | |
|       if (c0 < PAT_CHK(rno) && PAT_CHK(rno) <= c) {
 | |
|         /* To keep rno as an output node, its check is set to zero. */
 | |
|         if (!delinfo_search(pat, otherside)) {
 | |
|           GRN_LOG(ctx, GRN_LOG_DEBUG, "no delinfo found %d", otherside);
 | |
|         }
 | |
|         PAT_CHK_SET(rno, 0);
 | |
|       }
 | |
|       if (proot == p0 && !rno->check) {
 | |
|         /*
 | |
|          * Update rno->lr because the first node, rno becomes the new first
 | |
|          * node, is not an output node even if its check is zero.
 | |
|          */
 | |
|         const uint8_t *k = pat_node_get_key(ctx, pat, rno);
 | |
|         int direction = k ? (*k >> 7) : 1;
 | |
|         rno->lr[direction] = otherside;
 | |
|         rno->lr[!direction] = 0;
 | |
|       }
 | |
|     }
 | |
|     *p0 = otherside;
 | |
|   } else if ((!rn->lr[0] && rn->lr[1] == r) ||
 | |
|              (!rn->lr[1] && rn->lr[0] == r)) {
 | |
|     /* The output node has only a disabled self-loop. */
 | |
|     di->stat = DL_PHASE2;
 | |
|     di->d = r;
 | |
|     *p = 0;
 | |
|   } else {
 | |
|     /* The last transition (p) is not a self-loop. */
 | |
|     grn_pat_delinfo *ldi = NULL, *ddi = NULL;
 | |
|     if (PAT_DEL(rn)) {
 | |
|       ldi = delinfo_search(pat, r);
 | |
|     }
 | |
|     if (PAT_DEL(rn0)) {
 | |
|       ddi = delinfo_search(pat, *p0);
 | |
|     }
 | |
|     if (ldi) {
 | |
|       PAT_DEL_OFF(rn);
 | |
|       di->stat = DL_PHASE2;
 | |
|       if (ddi) {
 | |
|         PAT_DEL_OFF(rn0);
 | |
|         ddi->stat = DL_PHASE2;
 | |
|         if (ddi == ldi) {
 | |
|           if (r != ddi->ld) {
 | |
|             GRN_LOG(ctx, GRN_LOG_ERROR, "r(%d) != ddi->ld(%d)", r, ddi->ld);
 | |
|           }
 | |
|           di->d = r;
 | |
|         } else {
 | |
|           ldi->ld = ddi->ld;
 | |
|           di->d = r;
 | |
|         }
 | |
|       } else {
 | |
|         PAT_DEL_ON(rn0);
 | |
|         ldi->ld = *p0;
 | |
|         di->d = r;
 | |
|       }
 | |
|     } else {
 | |
|       PAT_DEL_ON(rn);
 | |
|       if (ddi) {
 | |
|         if (ddi->d != *p0) {
 | |
|           GRN_LOG(ctx, GRN_LOG_ERROR, "ddi->d(%d) != *p0(%d)", ddi->d, *p0);
 | |
|         }
 | |
|         PAT_DEL_OFF(rn0);
 | |
|         ddi->stat = DL_PHASE2;
 | |
|         di->stat = DL_PHASE1;
 | |
|         di->ld = ddi->ld;
 | |
|         di->d = r;
 | |
|         /*
 | |
|         PAT_DEL_OFF(rn0);
 | |
|         ddi->d = r;
 | |
|         di->stat = DL_PHASE2;
 | |
|         di->d = *p0;
 | |
|         */
 | |
|       } else {
 | |
|         PAT_DEL_ON(rn0);
 | |
|         di->stat = DL_PHASE1;
 | |
|         di->ld = *p0;
 | |
|         di->d = r;
 | |
|         // grn_log("pat_del d=%d ld=%d stat=%d", r, *p0, DL_PHASE1);
 | |
|       }
 | |
|     }
 | |
|     if (*p0 == otherside) {
 | |
|       /* The previous node (*p0) has a self-loop (rn0 == rno). */
 | |
|       PAT_CHK_SET(rno, 0);
 | |
|       if (proot == p0) {
 | |
|         /*
 | |
|          * Update rno->lr because the first node, rno becomes the new first
 | |
|          * node, is not an output node even if its check is zero.
 | |
|          */
 | |
|         const uint8_t *k = pat_node_get_key(ctx, pat, rno);
 | |
|         int direction = k ? (*k >> 7) : 1;
 | |
|         rno->lr[direction] = otherside;
 | |
|         rno->lr[!direction] = 0;
 | |
|       }
 | |
|     } else {
 | |
|       if (otherside) {
 | |
|         if (c0 < PAT_CHK(rno) && PAT_CHK(rno) <= c) {
 | |
|           /* To keep rno as an output node, its check is set to zero. */
 | |
|           if (!delinfo_search(pat, otherside)) {
 | |
|             GRN_LOG(ctx, GRN_LOG_ERROR, "no delinfo found %d", otherside);
 | |
|           }
 | |
|           PAT_CHK_SET(rno, 0);
 | |
|         }
 | |
|         if (proot == p0 && !rno->check) {
 | |
|           /*
 | |
|            * Update rno->lr because the first node, rno becomes the new first
 | |
|            * node, is not an output node even if its check is zero.
 | |
|            */
 | |
|           const uint8_t *k = pat_node_get_key(ctx, pat, rno);
 | |
|           int direction = k ? (*k >> 7) : 1;
 | |
|           rno->lr[direction] = otherside;
 | |
|           rno->lr[!direction] = 0;
 | |
|         }
 | |
|       }
 | |
|       *p0 = otherside;
 | |
|     }
 | |
|   }
 | |
|   pat->header->n_entries--;
 | |
|   pat->header->n_garbages++;
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| static grn_rc
 | |
| _grn_pat_delete(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size,
 | |
|                 grn_table_delete_optarg *optarg)
 | |
| {
 | |
|   if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
 | |
|     grn_id id = grn_pat_get(ctx, pat, key, key_size, NULL);
 | |
|     if (id && grn_pat_delete_with_sis(ctx, pat, id, optarg)) {
 | |
|       return GRN_SUCCESS;
 | |
|     }
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
|   return _grn_pat_del(ctx, pat, key, key_size, 0, optarg);
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_delete(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size,
 | |
|                grn_table_delete_optarg *optarg)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   if (!pat || !key || !key_size) { return GRN_INVALID_ARGUMENT; }
 | |
|   rc = grn_pat_error_if_truncated(ctx, pat);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   KEY_ENCODE(pat, keybuf, key, key_size);
 | |
|   return _grn_pat_delete(ctx, pat, key, key_size, optarg);
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| grn_pat_size(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   if (!pat) { return GRN_INVALID_ARGUMENT; }
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return 0;
 | |
|   }
 | |
|   return pat->header->n_entries;
 | |
| }
 | |
| 
 | |
| const char *
 | |
| _grn_pat_key(grn_ctx *ctx, grn_pat *pat, grn_id id, uint32_t *key_size)
 | |
| {
 | |
|   pat_node *node;
 | |
|   uint8_t *key;
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     *key_size = 0;
 | |
|     return NULL;
 | |
|   }
 | |
|   PAT_AT(pat, id, node);
 | |
|   if (!node) {
 | |
|     *key_size = 0;
 | |
|     return NULL;
 | |
|   }
 | |
|   key = pat_node_get_key(ctx, pat, node);
 | |
|   if (key) {
 | |
|     *key_size = PAT_LEN(node);
 | |
|   } else {
 | |
|     *key_size = 0;
 | |
|   }
 | |
|   return (const char *)key;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_delete_by_id(grn_ctx *ctx, grn_pat *pat, grn_id id,
 | |
|                      grn_table_delete_optarg *optarg)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   if (!pat || !id) { return GRN_INVALID_ARGUMENT; }
 | |
|   rc = grn_pat_error_if_truncated(ctx, pat);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   {
 | |
|     uint32_t key_size;
 | |
|     const char *key = _grn_pat_key(ctx, pat, id, &key_size);
 | |
|     return _grn_pat_delete(ctx, pat, key, key_size, optarg);
 | |
|   }
 | |
| }
 | |
| 
 | |
| int
 | |
| grn_pat_get_key(grn_ctx *ctx, grn_pat *pat, grn_id id, void *keybuf, int bufsize)
 | |
| {
 | |
|   int len;
 | |
|   uint8_t *key;
 | |
|   pat_node *node;
 | |
|   if (!pat) { return 0; }
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return 0;
 | |
|   }
 | |
|   if (!id) { return 0; }
 | |
|   PAT_AT(pat, id, node);
 | |
|   if (!node) { return 0; }
 | |
|   if (!(key = pat_node_get_key(ctx, pat, node))) { return 0; }
 | |
|   len = PAT_LEN(node);
 | |
|   if (keybuf && bufsize >= len) {
 | |
|     if (KEY_NEEDS_CONVERT(pat, len)) {
 | |
|       KEY_DEC(pat, keybuf, key, len);
 | |
|     } else {
 | |
|       grn_memcpy(keybuf, key, len);
 | |
|     }
 | |
|   }
 | |
|   return len;
 | |
| }
 | |
| 
 | |
| int
 | |
| grn_pat_get_key2(grn_ctx *ctx, grn_pat *pat, grn_id id, grn_obj *bulk)
 | |
| {
 | |
|   uint32_t len;
 | |
|   uint8_t *key;
 | |
|   pat_node *node;
 | |
|   if (!pat) { return GRN_INVALID_ARGUMENT; }
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return 0;
 | |
|   }
 | |
|   if (!id) { return 0; }
 | |
|   PAT_AT(pat, id, node);
 | |
|   if (!node) { return 0; }
 | |
|   if (!(key = pat_node_get_key(ctx, pat, node))) { return 0; }
 | |
|   len = PAT_LEN(node);
 | |
|   if (KEY_NEEDS_CONVERT(pat, len)) {
 | |
|     if (bulk->header.impl_flags & GRN_OBJ_REFER) {
 | |
|       GRN_TEXT_INIT(bulk, 0);
 | |
|     }
 | |
|     if (!grn_bulk_reserve(ctx, bulk, len)) {
 | |
|       char *curr = GRN_BULK_CURR(bulk);
 | |
|       KEY_DEC(pat, curr, key, len);
 | |
|       grn_bulk_truncate(ctx, bulk, GRN_BULK_VSIZE(bulk) + len);
 | |
|     }
 | |
|   } else {
 | |
|     if (bulk->header.impl_flags & GRN_OBJ_REFER) {
 | |
|       bulk->u.b.head = (char *)key;
 | |
|       bulk->u.b.curr = (char *)key + len;
 | |
|     } else {
 | |
|       grn_bulk_write(ctx, bulk, (char *)key, len);
 | |
|     }
 | |
|   }
 | |
|   return len;
 | |
| }
 | |
| 
 | |
| int
 | |
| grn_pat_get_value(grn_ctx *ctx, grn_pat *pat, grn_id id, void *valuebuf)
 | |
| {
 | |
|   int value_size;
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return 0;
 | |
|   }
 | |
|   value_size = (int)pat->value_size;
 | |
|   if (value_size) {
 | |
|     byte *v = (byte *)sis_at(ctx, pat, id);
 | |
|     if (v) {
 | |
|       if (valuebuf) {
 | |
|         if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
 | |
|           grn_memcpy(valuebuf, v + sizeof(sis_node), value_size);
 | |
|         } else {
 | |
|           grn_memcpy(valuebuf, v, value_size);
 | |
|         }
 | |
|       }
 | |
|       return value_size;
 | |
|     }
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| const char *
 | |
| grn_pat_get_value_(grn_ctx *ctx, grn_pat *pat, grn_id id, uint32_t *size)
 | |
| {
 | |
|   const char *value = NULL;
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return NULL;
 | |
|   }
 | |
|   if ((*size = pat->value_size)) {
 | |
|     if ((value = (const char *)sis_at(ctx, pat, id))
 | |
|         && (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS)) {
 | |
|       value += sizeof(sis_node);
 | |
|     }
 | |
|   }
 | |
|   return value;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_set_value(grn_ctx *ctx, grn_pat *pat, grn_id id,
 | |
|                   const void *value, int flags)
 | |
| {
 | |
|   grn_rc rc = grn_pat_error_if_truncated(ctx, pat);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   if (value) {
 | |
|     uint32_t value_size = pat->value_size;
 | |
|     if (value_size) {
 | |
|       byte *v = (byte *)sis_get(ctx, pat, id);
 | |
|       if (v) {
 | |
|         if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) { v += sizeof(sis_node); }
 | |
|         switch ((flags & GRN_OBJ_SET_MASK)) {
 | |
|         case GRN_OBJ_SET :
 | |
|           grn_memcpy(v, value, value_size);
 | |
|           return GRN_SUCCESS;
 | |
|         case GRN_OBJ_INCR :
 | |
|           switch (value_size) {
 | |
|           case sizeof(int32_t) :
 | |
|             *((int32_t *)v) += *((int32_t *)value);
 | |
|             return GRN_SUCCESS;
 | |
|           case sizeof(int64_t) :
 | |
|             *((int64_t *)v) += *((int64_t *)value);
 | |
|             return GRN_SUCCESS;
 | |
|           default :
 | |
|             return GRN_INVALID_ARGUMENT;
 | |
|           }
 | |
|           break;
 | |
|         case GRN_OBJ_DECR :
 | |
|           switch (value_size) {
 | |
|           case sizeof(int32_t) :
 | |
|             *((int32_t *)v) -= *((int32_t *)value);
 | |
|             return GRN_SUCCESS;
 | |
|           case sizeof(int64_t) :
 | |
|             *((int64_t *)v) -= *((int64_t *)value);
 | |
|             return GRN_SUCCESS;
 | |
|           default :
 | |
|             return GRN_INVALID_ARGUMENT;
 | |
|           }
 | |
|           break;
 | |
|         default :
 | |
|           // todo : support other types.
 | |
|           return GRN_INVALID_ARGUMENT;
 | |
|         }
 | |
|       } else {
 | |
|         return GRN_NO_MEMORY_AVAILABLE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return GRN_INVALID_ARGUMENT;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_info(grn_ctx *ctx, grn_pat *pat, int *key_size, unsigned int *flags,
 | |
|              grn_encoding *encoding, unsigned int *n_entries, unsigned int *file_size)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   ERRCLR(NULL);
 | |
|   if (!pat) { return GRN_INVALID_ARGUMENT; }
 | |
|   rc = grn_pat_error_if_truncated(ctx, pat);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   if (key_size) { *key_size = pat->key_size; }
 | |
|   if (flags) { *flags = pat->obj.header.flags; }
 | |
|   if (encoding) { *encoding = pat->encoding; }
 | |
|   if (n_entries) { *n_entries = pat->header->n_entries; }
 | |
|   if (file_size) {
 | |
|     uint64_t tmp = 0;
 | |
|     if ((rc = grn_io_size(ctx, pat->io, &tmp))) {
 | |
|       return rc;
 | |
|     }
 | |
|     *file_size = (unsigned int) tmp; /* FIXME: inappropriate cast */
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| int
 | |
| grn_pat_delete_with_sis(grn_ctx *ctx, grn_pat *pat, grn_id id,
 | |
|                         grn_table_delete_optarg *optarg)
 | |
| {
 | |
|   int level = 0, shared;
 | |
|   const char *key = NULL, *_key;
 | |
|   sis_node *sp, *ss = NULL, *si;
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return 0;
 | |
|   }
 | |
|   si = sis_at(ctx, pat, id);
 | |
|   while (id) {
 | |
|     pat_node *rn;
 | |
|     uint32_t key_size;
 | |
|     if ((si && si->children && si->children != id) ||
 | |
|         (optarg && optarg->func &&
 | |
|          !optarg->func(ctx, (grn_obj *)pat, id, optarg->func_arg))) {
 | |
|       break;
 | |
|     }
 | |
|     PAT_AT(pat, id, rn);
 | |
|     if (!(_key = (char *)pat_node_get_key(ctx, pat, rn))) { return 0; }
 | |
|     if (_key == key) {
 | |
|       shared = 1;
 | |
|     } else {
 | |
|       key = _key;
 | |
|       shared = 0;
 | |
|     }
 | |
|     key_size = PAT_LEN(rn);
 | |
|     if (key && key_size) { _grn_pat_del(ctx, pat, key, key_size, shared, NULL); }
 | |
|     if (si) {
 | |
|       grn_id *p, sid;
 | |
|       uint32_t lkey = 0;
 | |
|       if ((*key & 0x80) && chop(ctx, pat, &key, key + key_size, &lkey)) {
 | |
|         if ((sid = grn_pat_get(ctx, pat, key, key_size - lkey, NULL)) &&
 | |
|             (ss = sis_at(ctx, pat, sid))) {
 | |
|           for (p = &ss->children; *p && *p != sid; p = &sp->sibling) {
 | |
|             if (*p == id) {
 | |
|               *p = si->sibling;
 | |
|               break;
 | |
|             }
 | |
|             if (!(sp = sis_at(ctx, pat, *p))) { break; }
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         sid = GRN_ID_NIL;
 | |
|       }
 | |
|       si->sibling = 0;
 | |
|       si->children = 0;
 | |
|       id = sid;
 | |
|       si = ss;
 | |
|     } else {
 | |
|       id = GRN_ID_NIL;
 | |
|     }
 | |
|     level++;
 | |
|   }
 | |
|   if (level) {
 | |
|     uint32_t lkey = 0;
 | |
|     while (id && key) {
 | |
|       uint32_t key_size;
 | |
|       if (_grn_pat_key(ctx, pat, id, &key_size) != key) { break; }
 | |
|       {
 | |
|         pat_node *rn;
 | |
|         PAT_AT(pat, id, rn);
 | |
|         if (!rn) { break; }
 | |
|         if (lkey) {
 | |
|           rn->key = lkey;
 | |
|         } else {
 | |
|           pat_node_set_key(ctx, pat, rn, (uint8_t *)key, key_size);
 | |
|           lkey = rn->key;
 | |
|         }
 | |
|       }
 | |
|       {
 | |
|         const char *end = key + key_size;
 | |
|         if (!((*key & 0x80) && chop(ctx, pat, &key, end, &lkey))) { break; }
 | |
|         id = grn_pat_get(ctx, pat, key, end - key, NULL);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return level;
 | |
| }
 | |
| 
 | |
| grn_id
 | |
| grn_pat_next(grn_ctx *ctx, grn_pat *pat, grn_id id)
 | |
| {
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return GRN_ID_NIL;
 | |
|   }
 | |
|   while (++id <= pat->header->curr_rec) {
 | |
|     uint32_t key_size;
 | |
|     const char *key = _grn_pat_key(ctx, pat, id, &key_size);
 | |
|     if (id == grn_pat_get(ctx, pat, key, key_size, NULL)) {
 | |
|       return id;
 | |
|     }
 | |
|   }
 | |
|   return GRN_ID_NIL;
 | |
| }
 | |
| 
 | |
| grn_id
 | |
| grn_pat_at(grn_ctx *ctx, grn_pat *pat, grn_id id)
 | |
| {
 | |
|   uint32_t key_size;
 | |
|   const char *key = _grn_pat_key(ctx, pat, id, &key_size);
 | |
|   if (key && (id == _grn_pat_get(ctx, pat, key, key_size, NULL))) { return id; }
 | |
|   return GRN_ID_NIL;
 | |
| }
 | |
| 
 | |
| grn_id
 | |
| grn_pat_curr_id(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return GRN_ID_NIL;
 | |
|   }
 | |
|   return pat->header->curr_rec;
 | |
| }
 | |
| 
 | |
| int
 | |
| grn_pat_scan(grn_ctx *ctx, grn_pat *pat, const char *str, unsigned int str_len,
 | |
|              grn_pat_scan_hit *sh, unsigned int sh_size, const char **rest)
 | |
| {
 | |
|   int n = 0;
 | |
|   grn_id tid;
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return 0;
 | |
|   }
 | |
|   if (pat->normalizer) {
 | |
|     int flags =
 | |
|       GRN_STRING_REMOVE_BLANK |
 | |
|       GRN_STRING_WITH_TYPES |
 | |
|       GRN_STRING_WITH_CHECKS;
 | |
|     grn_obj *nstr = grn_string_open(ctx, str, str_len,
 | |
|                                     pat->normalizer, flags);
 | |
|     if (nstr) {
 | |
|       const short *cp = grn_string_get_checks(ctx, nstr);
 | |
|       const unsigned char *tp = grn_string_get_types(ctx, nstr);
 | |
|       unsigned int offset = 0, offset0 = 0;
 | |
|       unsigned int normalized_length_in_bytes;
 | |
|       const char *sp, *se;
 | |
|       grn_string_get_normalized(ctx, nstr, &sp, &normalized_length_in_bytes,
 | |
|                                 NULL);
 | |
|       se = sp + normalized_length_in_bytes;
 | |
|       while (n < (int) sh_size) {
 | |
|         if ((tid = grn_pat_lcp_search(ctx, pat, sp, se - sp))) {
 | |
|           const char *key;
 | |
|           uint32_t len;
 | |
|           int first_key_char_len;
 | |
|           key = _grn_pat_key(ctx, pat, tid, &len);
 | |
|           sh[n].id = tid;
 | |
|           sh[n].offset = (*cp > 0) ? offset : offset0;
 | |
|           first_key_char_len = grn_charlen(ctx, key, key + len);
 | |
|           if (sh[n].offset > 0 &&
 | |
|               GRN_CHAR_IS_BLANK(tp[-1]) &&
 | |
|               ((first_key_char_len == 1 && key[0] != ' ') ||
 | |
|                first_key_char_len > 1)){
 | |
|             /* Remove leading spaces. */
 | |
|             const char *original_str = str + sh[n].offset;
 | |
|             while (grn_charlen(ctx, original_str, str + str_len) == 1 &&
 | |
|                    original_str[0] == ' ') {
 | |
|               original_str++;
 | |
|               sh[n].offset++;
 | |
|             }
 | |
|           }
 | |
|           {
 | |
|             grn_bool blank_in_alnum = GRN_FALSE;
 | |
|             const unsigned char *start_tp = tp;
 | |
|             const unsigned char *blank_in_alnum_check_tp;
 | |
|             while (len--) {
 | |
|               if (*cp > 0) { offset0 = offset; offset += *cp; tp++; }
 | |
|               sp++; cp++;
 | |
|             }
 | |
|             sh[n].length = offset - sh[n].offset;
 | |
|             for (blank_in_alnum_check_tp = start_tp + 1;
 | |
|                  blank_in_alnum_check_tp < tp;
 | |
|                  blank_in_alnum_check_tp++) {
 | |
| #define GRN_CHAR_IS_ALNUM(char_type)                         \
 | |
|               (GRN_CHAR_TYPE(char_type) == GRN_CHAR_ALPHA || \
 | |
|                GRN_CHAR_TYPE(char_type) == GRN_CHAR_DIGIT)
 | |
|               if (GRN_CHAR_IS_BLANK(blank_in_alnum_check_tp[0]) &&
 | |
|                   GRN_CHAR_IS_ALNUM(blank_in_alnum_check_tp[-1]) &&
 | |
|                   (blank_in_alnum_check_tp + 1) < tp &&
 | |
|                   GRN_CHAR_IS_ALNUM(blank_in_alnum_check_tp[1])) {
 | |
|                 blank_in_alnum = GRN_TRUE;
 | |
|               }
 | |
| #undef GRN_CHAR_IS_ALNUM
 | |
|             }
 | |
|             if (!blank_in_alnum) {
 | |
|               n++;
 | |
|             }
 | |
|           }
 | |
|         } else {
 | |
|           if (*cp > 0) { offset0 = offset; offset += *cp; tp++; }
 | |
|           do {
 | |
|             sp++; cp++;
 | |
|           } while (sp < se && !*cp);
 | |
|         }
 | |
|         if (se <= sp) { offset = str_len; break; }
 | |
|       }
 | |
|       if (rest) {
 | |
|         grn_string_get_original(ctx, nstr, rest, NULL);
 | |
|         *rest += offset;
 | |
|       }
 | |
|       grn_obj_close(ctx, nstr);
 | |
|     } else {
 | |
|       n = -1;
 | |
|       if (rest) { *rest = str; }
 | |
|     }
 | |
|   } else {
 | |
|     uint32_t len;
 | |
|     const char *sp, *se = str + str_len;
 | |
|     for (sp = str; sp < se && n < (int) sh_size; sp += len) {
 | |
|       if ((tid = grn_pat_lcp_search(ctx, pat, sp, se - sp))) {
 | |
|         _grn_pat_key(ctx, pat, tid, &len);
 | |
|         sh[n].id = tid;
 | |
|         sh[n].offset = sp - str;
 | |
|         sh[n].length = len;
 | |
|         n++;
 | |
|       } else {
 | |
|         len = grn_charlen(ctx, sp, se);
 | |
|       }
 | |
|       if (!len) { break; }
 | |
|     }
 | |
|     if (rest) { *rest = sp; }
 | |
|   }
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| #define INITIAL_SIZE 512
 | |
| 
 | |
| inline static void
 | |
| push(grn_pat_cursor *c, grn_id id, uint16_t check)
 | |
| {
 | |
|   grn_ctx *ctx = c->ctx;
 | |
|   grn_pat_cursor_entry *se;
 | |
|   if (c->size <= c->sp) {
 | |
|     if (c->ss) {
 | |
|       uint32_t size = c->size * 4;
 | |
|       grn_pat_cursor_entry *ss = GRN_REALLOC(c->ss, size);
 | |
|       if (!ss) { return; /* give up */ }
 | |
|       c->ss = ss;
 | |
|       c->size = size;
 | |
|     } else {
 | |
|       if (!(c->ss = GRN_MALLOC(sizeof(grn_pat_cursor_entry) * INITIAL_SIZE))) {
 | |
|         return; /* give up */
 | |
|       }
 | |
|       c->size = INITIAL_SIZE;
 | |
|     }
 | |
|   }
 | |
|   se = &c->ss[c->sp++];
 | |
|   se->id = id;
 | |
|   se->check = check;
 | |
| }
 | |
| 
 | |
| inline static grn_pat_cursor_entry *
 | |
| pop(grn_pat_cursor *c)
 | |
| {
 | |
|   return c->sp ? &c->ss[--c->sp] : NULL;
 | |
| }
 | |
| 
 | |
| static grn_id
 | |
| grn_pat_cursor_next_by_id(grn_ctx *ctx, grn_pat_cursor *c)
 | |
| {
 | |
|   grn_pat *pat = c->pat;
 | |
|   int dir = (c->obj.header.flags & GRN_CURSOR_DESCENDING) ? -1 : 1;
 | |
|   while (c->curr_rec != c->tail) {
 | |
|     c->curr_rec += dir;
 | |
|     if (pat->header->n_garbages) {
 | |
|       uint32_t key_size;
 | |
|       const void *key = _grn_pat_key(ctx, pat, c->curr_rec, &key_size);
 | |
|       if (_grn_pat_get(ctx, pat, key, key_size, NULL) != c->curr_rec) {
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
|     c->rest--;
 | |
|     return c->curr_rec;
 | |
|   }
 | |
|   return GRN_ID_NIL;
 | |
| }
 | |
| 
 | |
| grn_id
 | |
| grn_pat_cursor_next(grn_ctx *ctx, grn_pat_cursor *c)
 | |
| {
 | |
|   pat_node *node;
 | |
|   grn_pat_cursor_entry *se;
 | |
|   if (!c->rest) { return GRN_ID_NIL; }
 | |
|   if ((c->obj.header.flags & GRN_CURSOR_BY_ID)) {
 | |
|     return grn_pat_cursor_next_by_id(ctx, c);
 | |
|   }
 | |
|   while ((se = pop(c))) {
 | |
|     grn_id id = se->id;
 | |
|     int check = se->check, ch;
 | |
|     while (id) {
 | |
|       PAT_AT(c->pat, id, node);
 | |
|       if (!node) {
 | |
|         break;
 | |
|       }
 | |
|       ch = PAT_CHK(node);
 | |
|       if (ch > check) {
 | |
|         if (c->obj.header.flags & GRN_CURSOR_DESCENDING) {
 | |
|           push(c, node->lr[0], ch);
 | |
|           id = node->lr[1];
 | |
|         } else {
 | |
|           push(c, node->lr[1], ch);
 | |
|           id = node->lr[0];
 | |
|         }
 | |
|         check = ch;
 | |
|         continue;
 | |
|       } else {
 | |
|         if (id == c->tail) {
 | |
|           c->sp = 0;
 | |
|         } else {
 | |
|           if (!c->curr_rec && c->tail) {
 | |
|             uint32_t lmin, lmax;
 | |
|             pat_node *nmin, *nmax;
 | |
|             const uint8_t *kmin, *kmax;
 | |
|             if (c->obj.header.flags & GRN_CURSOR_DESCENDING) {
 | |
|               PAT_AT(c->pat, c->tail, nmin);
 | |
|               PAT_AT(c->pat, id, nmax);
 | |
|             } else {
 | |
|               PAT_AT(c->pat, id, nmin);
 | |
|               PAT_AT(c->pat, c->tail, nmax);
 | |
|             }
 | |
|             lmin = PAT_LEN(nmin);
 | |
|             lmax = PAT_LEN(nmax);
 | |
|             kmin = pat_node_get_key(ctx, c->pat, nmin);
 | |
|             kmax = pat_node_get_key(ctx, c->pat, nmax);
 | |
|             if ((lmin < lmax) ?
 | |
|                 (memcmp(kmin, kmax, lmin) > 0) :
 | |
|                 (memcmp(kmin, kmax, lmax) >= 0)) {
 | |
|               c->sp = 0;
 | |
|               break;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         c->curr_rec = id;
 | |
|         c->rest--;
 | |
|         return id;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return GRN_ID_NIL;
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_pat_cursor_close(grn_ctx *ctx, grn_pat_cursor *c)
 | |
| {
 | |
|   GRN_ASSERT(c->ctx == ctx);
 | |
|   if (c->ss) { GRN_FREE(c->ss); }
 | |
|   GRN_FREE(c);
 | |
| }
 | |
| 
 | |
| inline static int
 | |
| bitcmp(const void *s1, const void *s2, int offset, int length)
 | |
| {
 | |
|   int r, rest = length + (offset & 7) - 8, bl = offset >> 3, mask = 0xff >> (offset & 7);
 | |
|   unsigned char *a = (unsigned char *)s1 + bl, *b = (unsigned char *)s2 + bl;
 | |
|   if (rest <= 0) {
 | |
|     mask &= 0xff << -rest;
 | |
|     return (*a & mask) - (*b & mask);
 | |
|   }
 | |
|   if ((r = (*a & mask) - (*b & mask))) { return r; }
 | |
|   a++; b++;
 | |
|   if ((bl = rest >> 3)) {
 | |
|     if ((r = memcmp(a, b, bl))) { return r; }
 | |
|     a += bl; b += bl;
 | |
|   }
 | |
|   mask = 0xff << (8 - (rest & 7));
 | |
|   return (*a & mask) - (*b & mask);
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| set_cursor_prefix(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
 | |
|                   const void *key, uint32_t key_size, int flags)
 | |
| {
 | |
|   int c0 = -1, ch;
 | |
|   const uint8_t *k;
 | |
|   uint32_t len, byte_len;
 | |
|   grn_id id;
 | |
|   pat_node *node;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   if (flags & GRN_CURSOR_SIZE_BY_BIT) {
 | |
|     len = key_size * 2;
 | |
|     byte_len = key_size >> 3;
 | |
|   } else {
 | |
|     len = key_size * 16;
 | |
|     byte_len = key_size;
 | |
|   }
 | |
|   KEY_ENCODE(pat, keybuf, key, byte_len);
 | |
|   PAT_AT(pat, 0, node);
 | |
|   id = node->lr[1];
 | |
|   while (id) {
 | |
|     PAT_AT(pat, id, node);
 | |
|     if (!node) { return GRN_FILE_CORRUPT; }
 | |
|     ch = PAT_CHK(node);
 | |
|     if (c0 < ch && ch < (int) len - 1) {
 | |
|       if (ch & 1) {
 | |
|         id = (ch + 1 < (int) len) ? node->lr[1] : node->lr[0];
 | |
|       } else {
 | |
|         id = node->lr[nth_bit((uint8_t *)key, ch, len)];
 | |
|       }
 | |
|       c0 = ch;
 | |
|       continue;
 | |
|     }
 | |
|     if (!(k = pat_node_get_key(ctx, pat, node))) { break; }
 | |
|     if (PAT_LEN(node) < byte_len) { break; }
 | |
|     if ((flags & GRN_CURSOR_SIZE_BY_BIT)
 | |
|         ? !bitcmp(k, key, 0, key_size)
 | |
|         : !memcmp(k, key, key_size)) {
 | |
|       if (c0 < ch) {
 | |
|         if (flags & GRN_CURSOR_DESCENDING) {
 | |
|           if ((ch > (int) len - 1) || !(flags & GRN_CURSOR_GT)) {
 | |
|             push(c, node->lr[0], ch);
 | |
|           }
 | |
|           push(c, node->lr[1], ch);
 | |
|         } else {
 | |
|           push(c, node->lr[1], ch);
 | |
|           if ((ch > (int) len - 1) || !(flags & GRN_CURSOR_GT)) {
 | |
|             push(c, node->lr[0], ch);
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         if (PAT_LEN(node) * 16 > len || !(flags & GRN_CURSOR_GT)) {
 | |
|           push(c, id, ch);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| set_cursor_near(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
 | |
|                 uint32_t min_size, const void *key, int flags)
 | |
| {
 | |
|   grn_id id;
 | |
|   pat_node *node;
 | |
|   const uint8_t *k;
 | |
|   int r, check = -1, ch;
 | |
|   uint32_t min = min_size * 16;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   KEY_ENCODE(pat, keybuf, key, pat->key_size);
 | |
|   PAT_AT(pat, 0, node);
 | |
|   for (id = node->lr[1]; id;) {
 | |
|     PAT_AT(pat, id, node);
 | |
|     if (!node) { return GRN_FILE_CORRUPT; }
 | |
|     ch = PAT_CHK(node);
 | |
|     if (ch <= check) {
 | |
|       if (check >= (int) min) { push(c, id, check); }
 | |
|       break;
 | |
|     }
 | |
|     if ((check += 2) < ch) {
 | |
|       if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
 | |
|       if ((r = bitcmp(key, k, check >> 1, (ch - check) >> 1))) {
 | |
|         if (ch >= (int) min) {
 | |
|           push(c, node->lr[1], ch);
 | |
|           push(c, node->lr[0], ch);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     check = ch;
 | |
|     if (nth_bit((uint8_t *)key, check, pat->key_size)) {
 | |
|       if (check >= (int) min) { push(c, node->lr[0], check); }
 | |
|       id = node->lr[1];
 | |
|     } else {
 | |
|       if (check >= (int) min) { push(c, node->lr[1], check); }
 | |
|       id = node->lr[0];
 | |
|     }
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| set_cursor_common_prefix(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
 | |
|                          uint32_t min_size, const void *key, uint32_t key_size, int flags)
 | |
| {
 | |
|   grn_id id;
 | |
|   pat_node *node;
 | |
|   const uint8_t *k;
 | |
|   int check = -1, ch;
 | |
|   uint32_t len = key_size * 16;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   KEY_ENCODE(pat, keybuf, key, key_size);
 | |
|   PAT_AT(pat, 0, node);
 | |
|   for (id = node->lr[1]; id;) {
 | |
|     PAT_AT(pat, id, node);
 | |
|     if (!node) { return GRN_FILE_CORRUPT; }
 | |
|     ch = PAT_CHK(node);
 | |
|     if (ch <= check) {
 | |
|       if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
 | |
|       {
 | |
|         uint32_t l = PAT_LEN(node);
 | |
|         if (min_size <= l && l <= key_size) {
 | |
|           if (!memcmp(key, k, l)) { push(c, id, check); }
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     check = ch;
 | |
|     if ((int) len <= check) { break; }
 | |
|     if (check & 1) {
 | |
|       grn_id id0 = node->lr[0];
 | |
|       pat_node *node0;
 | |
|       PAT_AT(pat, id0, node0);
 | |
|       if (!node0) { return GRN_FILE_CORRUPT; }
 | |
|       if (!(k = pat_node_get_key(ctx, pat, node0))) { return GRN_FILE_CORRUPT; }
 | |
|       {
 | |
|         uint32_t l = PAT_LEN(node0);
 | |
|         if (memcmp(key, k, l)) { break; }
 | |
|         if (min_size <= l) {
 | |
|           push(c, id0, check);
 | |
|         }
 | |
|       }
 | |
|       id = node->lr[1];
 | |
|     } else {
 | |
|       id = node->lr[nth_bit((uint8_t *)key, check, len)];
 | |
|     }
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| set_cursor_ascend(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
 | |
|                   const void *key, uint32_t key_size, int flags)
 | |
| {
 | |
|   grn_id id;
 | |
|   pat_node *node;
 | |
|   const uint8_t *k;
 | |
|   int r, check = -1, ch, c2;
 | |
|   uint32_t len = key_size * 16;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   KEY_ENCODE(pat, keybuf, key, key_size);
 | |
|   PAT_AT(pat, 0, node);
 | |
|   for (id = node->lr[1]; id;) {
 | |
|     PAT_AT(pat, id, node);
 | |
|     if (!node) { return GRN_FILE_CORRUPT; }
 | |
|     ch = PAT_CHK(node);
 | |
|     if (ch <= check) {
 | |
|       if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
 | |
|       {
 | |
|         uint32_t l = PAT_LEN(node);
 | |
|         if (l == key_size) {
 | |
|           if (flags & GRN_CURSOR_GT) {
 | |
|             if (memcmp(key, k, l) < 0) { push(c, id, check); }
 | |
|           } else {
 | |
|             if (memcmp(key, k, l) <= 0) { push(c, id, check); }
 | |
|           }
 | |
|         } else if (l < key_size) {
 | |
|           if (memcmp(key, k, l) < 0) { push(c, id, check); }
 | |
|         } else {
 | |
|           if (memcmp(key, k, key_size) <= 0) { push(c, id, check); }
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     c2 = (int) len < ch ? (int) len : ch;
 | |
|     if ((check += 2) < c2) {
 | |
|       if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
 | |
|       if ((r = bitcmp(key, k, check >> 1, ((c2 + 1) >> 1) - (check >> 1)))) {
 | |
|         if (r < 0) {
 | |
|           push(c, node->lr[1], ch);
 | |
|           push(c, node->lr[0], ch);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     check = ch;
 | |
|     if ((int) len <= check) {
 | |
|       push(c, node->lr[1], ch);
 | |
|       push(c, node->lr[0], ch);
 | |
|       break;
 | |
|     }
 | |
|     if (check & 1) {
 | |
|       if (check + 1 < (int) len) {
 | |
|         id = node->lr[1];
 | |
|       } else {
 | |
|         push(c, node->lr[1], check);
 | |
|         id = node->lr[0];
 | |
|       }
 | |
|     } else {
 | |
|       if (nth_bit((uint8_t *)key, check, len)) {
 | |
|         id = node->lr[1];
 | |
|       } else {
 | |
|         push(c, node->lr[1], check);
 | |
|         id = node->lr[0];
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| inline static grn_rc
 | |
| set_cursor_descend(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
 | |
|                    const void *key, uint32_t key_size, int flags)
 | |
| {
 | |
|   grn_id id;
 | |
|   pat_node *node;
 | |
|   const uint8_t *k;
 | |
|   int r, check = -1, ch, c2;
 | |
|   uint32_t len = key_size * 16;
 | |
|   uint8_t keybuf[MAX_FIXED_KEY_SIZE];
 | |
|   KEY_ENCODE(pat, keybuf, key, key_size);
 | |
|   PAT_AT(pat, 0, node);
 | |
|   for (id = node->lr[1]; id;) {
 | |
|     PAT_AT(pat, id, node);
 | |
|     if (!node) { return GRN_FILE_CORRUPT; }
 | |
|     ch = PAT_CHK(node);
 | |
|     if (ch <= check) {
 | |
|       if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
 | |
|       {
 | |
|         uint32_t l = PAT_LEN(node);
 | |
|         if (l <= key_size) {
 | |
|           if ((flags & GRN_CURSOR_LT) && l == key_size) {
 | |
|             if (memcmp(key, k, l) > 0) { push(c, id, check); }
 | |
|           } else {
 | |
|             if (memcmp(key, k, l) >= 0) { push(c, id, check); }
 | |
|           }
 | |
|         } else {
 | |
|           if (memcmp(key, k, key_size) > 0) { push(c, id, check); }
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     c2 = (int) len < ch ? (int) len : ch;
 | |
|     if ((check += 2) < c2) {
 | |
|       if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
 | |
|       if ((r = bitcmp(key, k, check >> 1, ((c2 + 1) >> 1) - (check >> 1)))) {
 | |
|         if (r >= 0) {
 | |
|           push(c, node->lr[0], ch);
 | |
|           push(c, node->lr[1], ch);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     check = ch;
 | |
|     if ((int) len <= check) { break; }
 | |
|     if (check & 1) {
 | |
|       if (check + 1 < (int) len) {
 | |
|         push(c, node->lr[0], check);
 | |
|         id = node->lr[1];
 | |
|       } else {
 | |
|         id = node->lr[0];
 | |
|       }
 | |
|     } else {
 | |
|       if (nth_bit((uint8_t *)key, check, len)) {
 | |
|         push(c, node->lr[0], check);
 | |
|         id = node->lr[1];
 | |
|       } else {
 | |
|         id = node->lr[0];
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| static grn_pat_cursor *
 | |
| grn_pat_cursor_open_by_id(grn_ctx *ctx, grn_pat *pat,
 | |
|                           const void *min, uint32_t min_size,
 | |
|                           const void *max, uint32_t max_size,
 | |
|                           int offset, int limit, int flags)
 | |
| {
 | |
|   int dir;
 | |
|   grn_pat_cursor *c;
 | |
|   if (!pat || !ctx) { return NULL; }
 | |
|   if (!(c = GRN_MALLOCN(grn_pat_cursor, 1))) { return NULL; }
 | |
|   GRN_DB_OBJ_SET_TYPE(c, GRN_CURSOR_TABLE_PAT_KEY);
 | |
|   c->pat = pat;
 | |
|   c->ctx = ctx;
 | |
|   c->obj.header.flags = flags;
 | |
|   c->obj.header.domain = GRN_ID_NIL;
 | |
|   c->size = 0;
 | |
|   c->sp = 0;
 | |
|   c->ss = NULL;
 | |
|   c->tail = 0;
 | |
|   if (flags & GRN_CURSOR_DESCENDING) {
 | |
|     dir = -1;
 | |
|     if (max) {
 | |
|       if (!(c->curr_rec = grn_pat_get(ctx, pat, max, max_size, NULL))) {
 | |
|         c->tail = GRN_ID_NIL;
 | |
|         goto exit;
 | |
|       }
 | |
|       if (!(flags & GRN_CURSOR_LT)) { c->curr_rec++; }
 | |
|     } else {
 | |
|       c->curr_rec = pat->header->curr_rec + 1;
 | |
|     }
 | |
|     if (min) {
 | |
|       if (!(c->tail = grn_pat_get(ctx, pat, min, min_size, NULL))) {
 | |
|         c->curr_rec = GRN_ID_NIL;
 | |
|         goto exit;
 | |
|       }
 | |
|       if ((flags & GRN_CURSOR_GT)) { c->tail++; }
 | |
|     } else {
 | |
|       c->tail = GRN_ID_NIL + 1;
 | |
|     }
 | |
|     if (c->curr_rec < c->tail) { c->tail = c->curr_rec; }
 | |
|   } else {
 | |
|     dir = 1;
 | |
|     if (min) {
 | |
|       if (!(c->curr_rec = grn_pat_get(ctx, pat, min, min_size, NULL))) {
 | |
|         c->tail = GRN_ID_NIL;
 | |
|         goto exit;
 | |
|       }
 | |
|       if (!(flags & GRN_CURSOR_GT)) { c->curr_rec--; }
 | |
|     } else {
 | |
|       c->curr_rec = GRN_ID_NIL;
 | |
|     }
 | |
|     if (max) {
 | |
|       if (!(c->tail = grn_pat_get(ctx, pat, max, max_size, NULL))) {
 | |
|         c->curr_rec = GRN_ID_NIL;
 | |
|         goto exit;
 | |
|       }
 | |
|       if ((flags & GRN_CURSOR_LT)) { c->tail--; }
 | |
|     } else {
 | |
|       c->tail = pat->header->curr_rec;
 | |
|     }
 | |
|     if (c->tail < c->curr_rec) { c->tail = c->curr_rec; }
 | |
|   }
 | |
|   if (pat->header->n_garbages) {
 | |
|     while (offset && c->curr_rec != c->tail) {
 | |
|       uint32_t key_size;
 | |
|       const void *key;
 | |
|       c->curr_rec += dir;
 | |
|       key = _grn_pat_key(ctx, pat, c->curr_rec, &key_size);
 | |
|       if (_grn_pat_get(ctx, pat, key, key_size, NULL) == c->curr_rec) {
 | |
|         offset--;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     if ((int) (dir * (c->tail - c->curr_rec)) < offset) {
 | |
|       c->curr_rec = c->tail;
 | |
|     } else {
 | |
|       c->curr_rec += dir * offset;
 | |
|     }
 | |
|   }
 | |
|   c->rest = (limit < 0) ? GRN_ID_MAX : limit;
 | |
| exit :
 | |
|   return c;
 | |
| }
 | |
| 
 | |
| static grn_rc set_cursor_rk(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
 | |
|                             const void *key, uint32_t key_size, int flags);
 | |
| 
 | |
| grn_pat_cursor *
 | |
| grn_pat_cursor_open(grn_ctx *ctx, grn_pat *pat,
 | |
|                     const void *min, uint32_t min_size,
 | |
|                     const void *max, uint32_t max_size,
 | |
|                     int offset, int limit, int flags)
 | |
| {
 | |
|   grn_id id;
 | |
|   pat_node *node;
 | |
|   grn_pat_cursor *c;
 | |
|   if (!pat || !ctx) { return NULL; }
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return NULL;
 | |
|   }
 | |
|   if ((flags & GRN_CURSOR_BY_ID)) {
 | |
|     return grn_pat_cursor_open_by_id(ctx, pat, min, min_size, max, max_size,
 | |
|                                      offset, limit, flags);
 | |
|   }
 | |
|   if (!(c = GRN_MALLOCN(grn_pat_cursor, 1))) { return NULL; }
 | |
|   GRN_DB_OBJ_SET_TYPE(c, GRN_CURSOR_TABLE_PAT_KEY);
 | |
|   c->pat = pat;
 | |
|   c->ctx = ctx;
 | |
|   c->size = 0;
 | |
|   c->sp = 0;
 | |
|   c->ss = NULL;
 | |
|   c->tail = 0;
 | |
|   c->rest = GRN_ID_MAX;
 | |
|   c->curr_rec = GRN_ID_NIL;
 | |
|   c->obj.header.domain = GRN_ID_NIL;
 | |
|   if (flags & GRN_CURSOR_PREFIX) {
 | |
|     if (max && max_size) {
 | |
|       if ((pat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE)) {
 | |
|         set_cursor_common_prefix(ctx, pat, c, min_size, max, max_size, flags);
 | |
|       } else {
 | |
|         set_cursor_near(ctx, pat, c, min_size, max, flags);
 | |
|       }
 | |
|       goto exit;
 | |
|     } else {
 | |
|       if (min && min_size) {
 | |
|         if (flags & GRN_CURSOR_RK) {
 | |
|           set_cursor_rk(ctx, pat, c, min, min_size, flags);
 | |
|         } else {
 | |
|           set_cursor_prefix(ctx, pat, c, min, min_size, flags);
 | |
|         }
 | |
|         goto exit;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (flags & GRN_CURSOR_DESCENDING) {
 | |
|     if (min && min_size) {
 | |
|       set_cursor_ascend(ctx, pat, c, min, min_size, flags);
 | |
|       c->obj.header.flags = GRN_CURSOR_ASCENDING;
 | |
|       c->tail = grn_pat_cursor_next(ctx, c);
 | |
|       c->sp = 0;
 | |
|       if (!c->tail) { goto exit; }
 | |
|     }
 | |
|     if (max && max_size) {
 | |
|       set_cursor_descend(ctx, pat, c, max, max_size, flags);
 | |
|     } else {
 | |
|       PAT_AT(pat, 0, node);
 | |
|       if (!node) {
 | |
|         grn_pat_cursor_close(ctx, c);
 | |
|         return NULL;
 | |
|       }
 | |
|       if ((id = node->lr[1])) {
 | |
|         PAT_AT(pat, id, node);
 | |
|         if (node) {
 | |
|           int ch = PAT_CHK(node);
 | |
|           push(c, node->lr[0], ch);
 | |
|           push(c, node->lr[1], ch);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     if (max && max_size) {
 | |
|       set_cursor_descend(ctx, pat, c, max, max_size, flags);
 | |
|       c->obj.header.flags = GRN_CURSOR_DESCENDING;
 | |
|       c->tail = grn_pat_cursor_next(ctx, c);
 | |
|       c->sp = 0;
 | |
|       if (!c->tail) { goto exit; }
 | |
|     }
 | |
|     if (min && min_size) {
 | |
|       set_cursor_ascend(ctx, pat, c, min, min_size, flags);
 | |
|     } else {
 | |
|       PAT_AT(pat, 0, node);
 | |
|       if (!node) {
 | |
|         grn_pat_cursor_close(ctx, c);
 | |
|         return NULL;
 | |
|       }
 | |
|       if ((id = node->lr[1])) {
 | |
|         PAT_AT(pat, id, node);
 | |
|         if (node) {
 | |
|           int ch = PAT_CHK(node);
 | |
|           push(c, node->lr[1], ch);
 | |
|           push(c, node->lr[0], ch);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| exit :
 | |
|   c->obj.header.flags = flags;
 | |
|   c->curr_rec = GRN_ID_NIL;
 | |
|   while (offset--) { grn_pat_cursor_next(ctx, c); }
 | |
|   c->rest = (limit < 0) ? GRN_ID_MAX : limit;
 | |
|   return c;
 | |
| }
 | |
| 
 | |
| int
 | |
| grn_pat_cursor_get_key(grn_ctx *ctx, grn_pat_cursor *c, void **key)
 | |
| {
 | |
|   *key = c->curr_key;
 | |
|   return grn_pat_get_key(ctx, c->pat, c->curr_rec, *key, GRN_TABLE_MAX_KEY_SIZE);
 | |
| }
 | |
| 
 | |
| int
 | |
| grn_pat_cursor_get_value(grn_ctx *ctx, grn_pat_cursor *c, void **value)
 | |
| {
 | |
|   int value_size = (int)c->pat->value_size;
 | |
|   if (value_size) {
 | |
|     byte *v = (byte *)sis_at(ctx, c->pat, c->curr_rec);
 | |
|     if (v) {
 | |
|       if (c->pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
 | |
|         *value = v + sizeof(sis_node);
 | |
|       } else {
 | |
|         *value = v;
 | |
|       }
 | |
|     } else {
 | |
|       *value = NULL;
 | |
|     }
 | |
|   }
 | |
|   return value_size;
 | |
| }
 | |
| 
 | |
| int
 | |
| grn_pat_cursor_get_key_value(grn_ctx *ctx, grn_pat_cursor *c,
 | |
|                              void **key, uint32_t *key_size, void **value)
 | |
| {
 | |
|   int value_size = (int)c->pat->value_size;
 | |
|   if (key_size) {
 | |
|     *key_size = (uint32_t) grn_pat_get_key(ctx, c->pat, c->curr_rec, c->curr_key,
 | |
|                                            GRN_TABLE_MAX_KEY_SIZE);
 | |
|     if (key) { *key = c->curr_key; }
 | |
|   }
 | |
|   if (value && value_size) {
 | |
|     byte *v = (byte *)sis_at(ctx, c->pat, c->curr_rec);
 | |
|     if (v) {
 | |
|       if (c->pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
 | |
|         *value = v + sizeof(sis_node);
 | |
|       } else {
 | |
|         *value = v;
 | |
|       }
 | |
|     } else {
 | |
|       *value = NULL;
 | |
|     }
 | |
|   }
 | |
|   return value_size;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_cursor_set_value(grn_ctx *ctx, grn_pat_cursor *c,
 | |
|                          const void *value, int flags)
 | |
| {
 | |
|   return grn_pat_set_value(ctx, c->pat, c->curr_rec, value, flags);
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_cursor_delete(grn_ctx *ctx, grn_pat_cursor *c,
 | |
|                       grn_table_delete_optarg *optarg)
 | |
| {
 | |
|   return grn_pat_delete_by_id(ctx, c->pat, c->curr_rec, optarg);
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_pat_check(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   char buf[8];
 | |
|   struct grn_pat_header *h = pat->header;
 | |
|   if (grn_pat_error_if_truncated(ctx, pat) != GRN_SUCCESS) {
 | |
|     return;
 | |
|   }
 | |
|   GRN_OUTPUT_ARRAY_OPEN("RESULT", 1);
 | |
|   GRN_OUTPUT_MAP_OPEN("SUMMARY", 23);
 | |
|   GRN_OUTPUT_CSTR("flags");
 | |
|   grn_itoh(h->flags, buf, 8);
 | |
|   GRN_OUTPUT_STR(buf, 8);
 | |
|   GRN_OUTPUT_CSTR("key size");
 | |
|   GRN_OUTPUT_INT64(h->key_size);
 | |
|   GRN_OUTPUT_CSTR("value_size");
 | |
|   GRN_OUTPUT_INT64(h->value_size);
 | |
|   GRN_OUTPUT_CSTR("tokenizer");
 | |
|   GRN_OUTPUT_INT64(h->tokenizer);
 | |
|   GRN_OUTPUT_CSTR("normalizer");
 | |
|   GRN_OUTPUT_INT64(h->normalizer);
 | |
|   GRN_OUTPUT_CSTR("n_entries");
 | |
|   GRN_OUTPUT_INT64(h->n_entries);
 | |
|   GRN_OUTPUT_CSTR("curr_rec");
 | |
|   GRN_OUTPUT_INT64(h->curr_rec);
 | |
|   GRN_OUTPUT_CSTR("curr_key");
 | |
|   GRN_OUTPUT_INT64(h->curr_key);
 | |
|   GRN_OUTPUT_CSTR("curr_del");
 | |
|   GRN_OUTPUT_INT64(h->curr_del);
 | |
|   GRN_OUTPUT_CSTR("curr_del2");
 | |
|   GRN_OUTPUT_INT64(h->curr_del2);
 | |
|   GRN_OUTPUT_CSTR("curr_del3");
 | |
|   GRN_OUTPUT_INT64(h->curr_del3);
 | |
|   GRN_OUTPUT_CSTR("n_garbages");
 | |
|   GRN_OUTPUT_INT64(h->n_garbages);
 | |
|   GRN_OUTPUT_MAP_CLOSE();
 | |
|   GRN_OUTPUT_ARRAY_CLOSE();
 | |
| }
 | |
| 
 | |
| /* utilities */
 | |
| void
 | |
| grn_p_pat_node(grn_ctx *ctx, grn_pat *pat, pat_node *node)
 | |
| {
 | |
|   uint8_t *key = NULL;
 | |
| 
 | |
|   if (!node) {
 | |
|     printf("#<pat_node:(null)>\n");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (PAT_IMD(node)) {
 | |
|     key = (uint8_t *)&(node->key);
 | |
|   } else {
 | |
|     KEY_AT(pat, node->key, key, 0);
 | |
|   }
 | |
| 
 | |
|   printf("#<pat_node:%p "
 | |
|          "left:%u "
 | |
|          "right:%u "
 | |
|          "deleting:%s "
 | |
|          "immediate:%s "
 | |
|          "length:%u "
 | |
|          "nth-byte:%u "
 | |
|          "nth-bit:%u "
 | |
|          "terminated:%s "
 | |
|          "key:<%.*s>"
 | |
|          ">\n",
 | |
|          node,
 | |
|          node->lr[0],
 | |
|          node->lr[1],
 | |
|          PAT_DEL(node) ? "true" : "false",
 | |
|          PAT_IMD(node) ? "true" : "false",
 | |
|          PAT_LEN(node),
 | |
|          PAT_CHK(node) >> 4,
 | |
|          (PAT_CHK(node) >> 1) & 0x7,
 | |
|          (PAT_CHK(node) & 0x1) ? "true" : "false",
 | |
|          PAT_LEN(node),
 | |
|          (char *)key);
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_pat_inspect_check(grn_ctx *ctx, grn_obj *buf, int check)
 | |
| {
 | |
|   GRN_TEXT_PUTS(ctx, buf, "{");
 | |
|   grn_text_lltoa(ctx, buf, check >> 4);
 | |
|   GRN_TEXT_PUTS(ctx, buf, ",");
 | |
|   grn_text_lltoa(ctx, buf, (check >> 1) & 7);
 | |
|   GRN_TEXT_PUTS(ctx, buf, ",");
 | |
|   grn_text_lltoa(ctx, buf, check & 1);
 | |
|   GRN_TEXT_PUTS(ctx, buf, "}");
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_pat_inspect_node(grn_ctx *ctx, grn_pat *pat, grn_id id, int check,
 | |
|                      grn_obj *key_buf, int indent, const char *prefix,
 | |
|                      grn_obj *buf)
 | |
| {
 | |
|   pat_node *node = NULL;
 | |
|   int i, c;
 | |
| 
 | |
|   PAT_AT(pat, id, node);
 | |
|   c = PAT_CHK(node);
 | |
| 
 | |
|   for (i = 0; i < indent; i++) {
 | |
|     GRN_TEXT_PUTC(ctx, buf, ' ');
 | |
|   }
 | |
|   GRN_TEXT_PUTS(ctx, buf, prefix);
 | |
|   grn_text_lltoa(ctx, buf, id);
 | |
|   grn_pat_inspect_check(ctx, buf, c);
 | |
| 
 | |
|   if (c > check) {
 | |
|     GRN_TEXT_PUTS(ctx, buf, "\n");
 | |
|     grn_pat_inspect_node(ctx, pat, node->lr[0], c, key_buf,
 | |
|                          indent + 2, "L:", buf);
 | |
|     GRN_TEXT_PUTS(ctx, buf, "\n");
 | |
|     grn_pat_inspect_node(ctx, pat, node->lr[1], c, key_buf,
 | |
|                          indent + 2, "R:", buf);
 | |
|   } else if (id) {
 | |
|     int key_size;
 | |
|     uint8_t *key;
 | |
| 
 | |
|     key_size = PAT_LEN(node);
 | |
|     GRN_BULK_REWIND(key_buf);
 | |
|     grn_bulk_space(ctx, key_buf, key_size);
 | |
|     grn_pat_get_key(ctx, pat, id, GRN_BULK_HEAD(key_buf), key_size);
 | |
|     GRN_TEXT_PUTS(ctx, buf, "(");
 | |
|     grn_inspect(ctx, buf, key_buf);
 | |
|     GRN_TEXT_PUTS(ctx, buf, ")");
 | |
| 
 | |
|     GRN_TEXT_PUTS(ctx, buf, "[");
 | |
|     key = pat_node_get_key(ctx, pat, node);
 | |
|     for (i = 0; i < key_size; i++) {
 | |
|       int j;
 | |
|       uint8_t byte = key[i];
 | |
|       if (i != 0) {
 | |
|         GRN_TEXT_PUTS(ctx, buf, " ");
 | |
|       }
 | |
|       for (j = 0; j < 8; j++) {
 | |
|         grn_text_lltoa(ctx, buf, (byte >> (7 - j)) & 1);
 | |
|       }
 | |
|     }
 | |
|     GRN_TEXT_PUTS(ctx, buf, "]");
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_pat_inspect_nodes(grn_ctx *ctx, grn_pat *pat, grn_obj *buf)
 | |
| {
 | |
|   pat_node *node;
 | |
|   grn_obj key_buf;
 | |
| 
 | |
|   GRN_TEXT_PUTS(ctx, buf, "{");
 | |
|   PAT_AT(pat, GRN_ID_NIL, node);
 | |
|   if (node->lr[1]) {
 | |
|     GRN_TEXT_PUTS(ctx, buf, "\n");
 | |
|     GRN_OBJ_INIT(&key_buf, GRN_BULK, 0, pat->obj.header.domain);
 | |
|     grn_pat_inspect_node(ctx, pat, node->lr[1], -1, &key_buf, 0, "", buf);
 | |
|     GRN_OBJ_FIN(ctx, &key_buf);
 | |
|     GRN_TEXT_PUTS(ctx, buf, "\n");
 | |
|   }
 | |
|   GRN_TEXT_PUTS(ctx, buf, "}");
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_pat_cursor_inspect_entries(grn_ctx *ctx, grn_pat_cursor *c, grn_obj *buf)
 | |
| {
 | |
|   uint i;
 | |
|   GRN_TEXT_PUTS(ctx, buf, "[");
 | |
|   for (i = 0; i < c->sp; i++) {
 | |
|     grn_pat_cursor_entry *e = c->ss + i;
 | |
|     if (i != 0) {
 | |
|       GRN_TEXT_PUTS(ctx, buf, ", ");
 | |
|     }
 | |
|     GRN_TEXT_PUTS(ctx, buf, "[");
 | |
|     grn_text_lltoa(ctx, buf, e->id);
 | |
|     GRN_TEXT_PUTS(ctx, buf, ",");
 | |
|     grn_pat_inspect_check(ctx, buf, e->check);
 | |
|     GRN_TEXT_PUTS(ctx, buf, "]");
 | |
|   }
 | |
|   GRN_TEXT_PUTS(ctx, buf, "]");
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_pat_cursor_inspect(grn_ctx *ctx, grn_pat_cursor *c, grn_obj *buf)
 | |
| {
 | |
|   GRN_TEXT_PUTS(ctx, buf, "#<cursor:pat:");
 | |
|   grn_inspect_name(ctx, buf, (grn_obj *)(c->pat));
 | |
| 
 | |
|   GRN_TEXT_PUTS(ctx, buf, " ");
 | |
|   GRN_TEXT_PUTS(ctx, buf, "current:");
 | |
|   grn_text_lltoa(ctx, buf, c->curr_rec);
 | |
| 
 | |
|   GRN_TEXT_PUTS(ctx, buf, " ");
 | |
|   GRN_TEXT_PUTS(ctx, buf, "tail:");
 | |
|   grn_text_lltoa(ctx, buf, c->tail);
 | |
| 
 | |
|   GRN_TEXT_PUTS(ctx, buf, " ");
 | |
|   GRN_TEXT_PUTS(ctx, buf, "flags:");
 | |
|   if (c->obj.header.flags & GRN_CURSOR_PREFIX) {
 | |
|     GRN_TEXT_PUTS(ctx, buf, "prefix");
 | |
|   } else {
 | |
|     if (c->obj.header.flags & GRN_CURSOR_DESCENDING) {
 | |
|       GRN_TEXT_PUTS(ctx, buf, "descending");
 | |
|     } else {
 | |
|       GRN_TEXT_PUTS(ctx, buf, "ascending");
 | |
|     }
 | |
|     GRN_TEXT_PUTS(ctx, buf, "|");
 | |
|     if (c->obj.header.flags & GRN_CURSOR_GT) {
 | |
|       GRN_TEXT_PUTS(ctx, buf, "greater-than");
 | |
|     } else {
 | |
|       GRN_TEXT_PUTS(ctx, buf, "greater");
 | |
|     }
 | |
|     GRN_TEXT_PUTS(ctx, buf, "|");
 | |
|     if (c->obj.header.flags & GRN_CURSOR_LT) {
 | |
|       GRN_TEXT_PUTS(ctx, buf, "less-than");
 | |
|     } else {
 | |
|       GRN_TEXT_PUTS(ctx, buf, "less");
 | |
|     }
 | |
|     if (c->obj.header.flags & GRN_CURSOR_BY_ID) {
 | |
|       GRN_TEXT_PUTS(ctx, buf, "|by-id");
 | |
|     }
 | |
|     if (c->obj.header.flags & GRN_CURSOR_BY_KEY) {
 | |
|       GRN_TEXT_PUTS(ctx, buf, "|by-key");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   GRN_TEXT_PUTS(ctx, buf, " ");
 | |
|   GRN_TEXT_PUTS(ctx, buf, "rest:");
 | |
|   grn_text_lltoa(ctx, buf, c->rest);
 | |
| 
 | |
|   GRN_TEXT_PUTS(ctx, buf, " ");
 | |
|   GRN_TEXT_PUTS(ctx, buf, "entries:");
 | |
|   grn_pat_cursor_inspect_entries(ctx, c, buf);
 | |
| 
 | |
|   GRN_TEXT_PUTS(ctx, buf, ">");
 | |
| }
 | |
| 
 | |
| typedef struct {
 | |
|   uint8_t code;
 | |
|   uint8_t next;
 | |
|   uint8_t emit;
 | |
|   uint8_t attr;
 | |
| } rk_tree_node;
 | |
| 
 | |
| static uint16_t rk_str_idx[] = {
 | |
|   0x0003, 0x0006, 0x0009, 0x000c, 0x0012, 0x0015, 0x0018, 0x001e, 0x0024, 0x002a,
 | |
|   0x0030, 0x0036, 0x003c, 0x0042, 0x0048, 0x004e, 0x0054, 0x005a, 0x0060, 0x0066,
 | |
|   0x006c, 0x0072, 0x0078, 0x007e, 0x0084, 0x008a, 0x0090, 0x0096, 0x009c, 0x00a2,
 | |
|   0x00a8, 0x00ae, 0x00b4, 0x00ba, 0x00c0, 0x00c3, 0x00c6, 0x00c9, 0x00cc, 0x00cf,
 | |
|   0x00d2, 0x00d5, 0x00db, 0x00e1, 0x00e7, 0x00ea, 0x00f0, 0x00f6, 0x00fc, 0x00ff,
 | |
|   0x0105, 0x0108, 0x010e, 0x0111, 0x0114, 0x0117, 0x011a, 0x011d, 0x0120, 0x0123,
 | |
|   0x0129, 0x012f, 0x0135, 0x013b, 0x013e, 0x0144, 0x014a, 0x0150, 0x0156, 0x0159,
 | |
|   0x015c, 0x015f, 0x0162, 0x0165, 0x0168, 0x016b, 0x016e, 0x0171, 0x0177, 0x017d,
 | |
|   0x0183, 0x0189, 0x018c, 0x0192, 0x0198, 0x019e, 0x01a1, 0x01a4, 0x01aa, 0x01b0,
 | |
|   0x01b6, 0x01bc, 0x01bf, 0x01c2, 0x01c8, 0x01ce, 0x01d1, 0x01d7, 0x01dd, 0x01e0,
 | |
|   0x01e6, 0x01e9, 0x01ef, 0x01f2, 0x01f5, 0x01fb, 0x0201, 0x0207, 0x020d, 0x0213,
 | |
|   0x0216, 0x0219, 0x021c, 0x021f, 0x0222, 0x0225, 0x0228, 0x022e, 0x0234, 0x023a,
 | |
|   0x023d, 0x0243, 0x0249, 0x024f, 0x0252, 0x0258, 0x025e, 0x0264, 0x0267, 0x026d,
 | |
|   0x0273, 0x0279, 0x027f, 0x0285, 0x0288, 0x028b, 0x028e, 0x0291, 0x0294, 0x0297,
 | |
|   0x029a, 0x029d, 0x02a0, 0x02a3, 0x02a9, 0x02af, 0x02b5, 0x02b8, 0x02bb, 0x02be,
 | |
|   0x02c1, 0x02c4, 0x02c7, 0x02ca, 0x02cd, 0x02d0, 0x02d3, 0x02d6, 0x02dc, 0x02e2,
 | |
|   0x02e8, 0x02eb, 0x02ee, 0x02f1, 0x02f4, 0x02f7, 0x02fa, 0x02fd, 0x0300, 0x0303,
 | |
|   0x0309, 0x030c, 0x0312, 0x0318, 0x031e, 0x0324, 0x0327, 0x032a, 0x032d
 | |
| };
 | |
| static char rk_str[] = {
 | |
|   0xe3, 0x82, 0xa1, 0xe3, 0x82, 0xa2, 0xe3, 0x82, 0xa3, 0xe3, 0x82, 0xa4, 0xe3,
 | |
|   0x82, 0xa4, 0xe3, 0x82, 0xa7, 0xe3, 0x82, 0xa5, 0xe3, 0x82, 0xa6, 0xe3, 0x82,
 | |
|   0xa6, 0xe3, 0x82, 0xa2, 0xe3, 0x82, 0xa6, 0xe3, 0x82, 0xa3, 0xe3, 0x82, 0xa6,
 | |
|   0xe3, 0x82, 0xa4, 0xe3, 0x82, 0xa6, 0xe3, 0x82, 0xa6, 0xe3, 0x82, 0xa6, 0xe3,
 | |
|   0x82, 0xa7, 0xe3, 0x82, 0xa6, 0xe3, 0x82, 0xa8, 0xe3, 0x82, 0xa6, 0xe3, 0x82,
 | |
|   0xaa, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa0, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa1,
 | |
|   0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa2, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa3, 0xe3,
 | |
|   0x82, 0xa6, 0xe3, 0x83, 0xa4, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa5, 0xe3, 0x82,
 | |
|   0xa6, 0xe3, 0x83, 0xa6, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xa6,
 | |
|   0xe3, 0x83, 0xa8, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa9, 0xe3, 0x82, 0xa6, 0xe3,
 | |
|   0x83, 0xaa, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xab, 0xe3, 0x82, 0xa6, 0xe3, 0x83,
 | |
|   0xac, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xad, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xae,
 | |
|   0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xaf, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xb0, 0xe3,
 | |
|   0x82, 0xa6, 0xe3, 0x83, 0xb1, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xb2, 0xe3, 0x82,
 | |
|   0xa6, 0xe3, 0x83, 0xb3, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xbc, 0xe3, 0x82, 0xa7,
 | |
|   0xe3, 0x82, 0xa8, 0xe3, 0x82, 0xa9, 0xe3, 0x82, 0xaa, 0xe3, 0x82, 0xab, 0xe3,
 | |
|   0x82, 0xac, 0xe3, 0x82, 0xad, 0xe3, 0x82, 0xad, 0xe3, 0x83, 0xa3, 0xe3, 0x82,
 | |
|   0xad, 0xe3, 0x83, 0xa5, 0xe3, 0x82, 0xad, 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xae,
 | |
|   0xe3, 0x82, 0xae, 0xe3, 0x83, 0xa3, 0xe3, 0x82, 0xae, 0xe3, 0x83, 0xa5, 0xe3,
 | |
|   0x82, 0xae, 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xaf, 0xe3, 0x82, 0xaf, 0xe3, 0x82,
 | |
|   0xa1, 0xe3, 0x82, 0xb0, 0xe3, 0x82, 0xb0, 0xe3, 0x82, 0xa1, 0xe3, 0x82, 0xb1,
 | |
|   0xe3, 0x82, 0xb2, 0xe3, 0x82, 0xb3, 0xe3, 0x82, 0xb4, 0xe3, 0x82, 0xb5, 0xe3,
 | |
|   0x82, 0xb6, 0xe3, 0x82, 0xb7, 0xe3, 0x82, 0xb7, 0xe3, 0x82, 0xa7, 0xe3, 0x82,
 | |
|   0xb7, 0xe3, 0x83, 0xa3, 0xe3, 0x82, 0xb7, 0xe3, 0x83, 0xa5, 0xe3, 0x82, 0xb7,
 | |
|   0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xb8, 0xe3, 0x82, 0xb8, 0xe3, 0x82, 0xa7, 0xe3,
 | |
|   0x82, 0xb8, 0xe3, 0x83, 0xa3, 0xe3, 0x82, 0xb8, 0xe3, 0x83, 0xa5, 0xe3, 0x82,
 | |
|   0xb8, 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xb9, 0xe3, 0x82, 0xba, 0xe3, 0x82, 0xbb,
 | |
|   0xe3, 0x82, 0xbc, 0xe3, 0x82, 0xbd, 0xe3, 0x82, 0xbe, 0xe3, 0x82, 0xbf, 0xe3,
 | |
|   0x83, 0x80, 0xe3, 0x83, 0x81, 0xe3, 0x83, 0x81, 0xe3, 0x82, 0xa7, 0xe3, 0x83,
 | |
|   0x81, 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0x81, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x81,
 | |
|   0xe3, 0x83, 0xa7, 0xe3, 0x83, 0x82, 0xe3, 0x83, 0x82, 0xe3, 0x83, 0xa3, 0xe3,
 | |
|   0x83, 0x82, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x82, 0xe3, 0x83, 0xa7, 0xe3, 0x83,
 | |
|   0x83, 0xe3, 0x83, 0x84, 0xe3, 0x83, 0x84, 0xe3, 0x82, 0xa1, 0xe3, 0x83, 0x84,
 | |
|   0xe3, 0x82, 0xa3, 0xe3, 0x83, 0x84, 0xe3, 0x82, 0xa7, 0xe3, 0x83, 0x84, 0xe3,
 | |
|   0x82, 0xa9, 0xe3, 0x83, 0x85, 0xe3, 0x83, 0x86, 0xe3, 0x83, 0x86, 0xe3, 0x82,
 | |
|   0xa3, 0xe3, 0x83, 0x86, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x87, 0xe3, 0x83, 0x87,
 | |
|   0xe3, 0x82, 0xa3, 0xe3, 0x83, 0x87, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x88, 0xe3,
 | |
|   0x83, 0x88, 0xe3, 0x82, 0xa5, 0xe3, 0x83, 0x89, 0xe3, 0x83, 0x89, 0xe3, 0x82,
 | |
|   0xa5, 0xe3, 0x83, 0x8a, 0xe3, 0x83, 0x8b, 0xe3, 0x83, 0x8b, 0xe3, 0x82, 0xa3,
 | |
|   0xe3, 0x83, 0x8b, 0xe3, 0x82, 0xa7, 0xe3, 0x83, 0x8b, 0xe3, 0x83, 0xa3, 0xe3,
 | |
|   0x83, 0x8b, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x8b, 0xe3, 0x83, 0xa7, 0xe3, 0x83,
 | |
|   0x8c, 0xe3, 0x83, 0x8d, 0xe3, 0x83, 0x8e, 0xe3, 0x83, 0x8f, 0xe3, 0x83, 0x90,
 | |
|   0xe3, 0x83, 0x91, 0xe3, 0x83, 0x92, 0xe3, 0x83, 0x92, 0xe3, 0x83, 0xa3, 0xe3,
 | |
|   0x83, 0x92, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x92, 0xe3, 0x83, 0xa7, 0xe3, 0x83,
 | |
|   0x93, 0xe3, 0x83, 0x93, 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0x93, 0xe3, 0x83, 0xa5,
 | |
|   0xe3, 0x83, 0x93, 0xe3, 0x83, 0xa7, 0xe3, 0x83, 0x94, 0xe3, 0x83, 0x94, 0xe3,
 | |
|   0x83, 0xa3, 0xe3, 0x83, 0x94, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x94, 0xe3, 0x83,
 | |
|   0xa7, 0xe3, 0x83, 0x95, 0xe3, 0x83, 0x95, 0xe3, 0x82, 0xa1, 0xe3, 0x83, 0x95,
 | |
|   0xe3, 0x82, 0xa3, 0xe3, 0x83, 0x95, 0xe3, 0x82, 0xa7, 0xe3, 0x83, 0x95, 0xe3,
 | |
|   0x82, 0xa9, 0xe3, 0x83, 0x95, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x96, 0xe3, 0x83,
 | |
|   0x97, 0xe3, 0x83, 0x98, 0xe3, 0x83, 0x99, 0xe3, 0x83, 0x9a, 0xe3, 0x83, 0x9b,
 | |
|   0xe3, 0x83, 0x9c, 0xe3, 0x83, 0x9d, 0xe3, 0x83, 0x9e, 0xe3, 0x83, 0x9f, 0xe3,
 | |
|   0x83, 0x9f, 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0x9f, 0xe3, 0x83, 0xa5, 0xe3, 0x83,
 | |
|   0x9f, 0xe3, 0x83, 0xa7, 0xe3, 0x83, 0xa0, 0xe3, 0x83, 0xa1, 0xe3, 0x83, 0xa2,
 | |
|   0xe3, 0x83, 0xa3, 0xe3, 0x83, 0xa4, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0xa6, 0xe3,
 | |
|   0x83, 0xa7, 0xe3, 0x83, 0xa8, 0xe3, 0x83, 0xa9, 0xe3, 0x83, 0xaa, 0xe3, 0x83,
 | |
|   0xaa, 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0xaa, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0xaa,
 | |
|   0xe3, 0x83, 0xa7, 0xe3, 0x83, 0xab, 0xe3, 0x83, 0xac, 0xe3, 0x83, 0xad, 0xe3,
 | |
|   0x83, 0xae, 0xe3, 0x83, 0xaf, 0xe3, 0x83, 0xb0, 0xe3, 0x83, 0xb1, 0xe3, 0x83,
 | |
|   0xb2, 0xe3, 0x83, 0xb3, 0xe3, 0x83, 0xb3, 0xe3, 0x83, 0xbc, 0xe3, 0x83, 0xb4,
 | |
|   0xe3, 0x83, 0xb4, 0xe3, 0x82, 0xa1, 0xe3, 0x83, 0xb4, 0xe3, 0x82, 0xa3, 0xe3,
 | |
|   0x83, 0xb4, 0xe3, 0x82, 0xa7, 0xe3, 0x83, 0xb4, 0xe3, 0x82, 0xa9, 0xe3, 0x83,
 | |
|   0xb5, 0xe3, 0x83, 0xb6, 0xe3, 0x83, 0xbc
 | |
| };
 | |
| static uint16_t rk_tree_idx[] = {
 | |
|   0x001b, 0x0022, 0x0025, 0x0028, 0x002d, 0x0030, 0x0039, 0x003b, 0x003c, 0x003f,
 | |
|   0x0046, 0x0047, 0x004f, 0x0050, 0x0053, 0x005a, 0x005d, 0x0064, 0x0067, 0x006f,
 | |
|   0x0070, 0x0073, 0x007d, 0x007f, 0x0081, 0x0082, 0x0083, 0x0088, 0x008f, 0x0092,
 | |
|   0x00af, 0x00b5, 0x00bc, 0x00bf, 0x00c6, 0x00c9, 0x00d1, 0x00d6, 0x00da, 0x00e4,
 | |
|   0x00e6, 0x00eb, 0x00ec, 0x00f0, 0x00f6, 0x00fc, 0x00fe, 0x0108, 0x010a, 0x010c,
 | |
|   0x010d, 0x010e, 0x0113, 0x0118, 0x011f, 0x0123, 0x0125, 0x0164, 0x0180, 0x0183,
 | |
|   0x0199, 0x01ad
 | |
| };
 | |
| static rk_tree_node rk_tree[] = {
 | |
|   {0x2d, 0x00, 0xb2, 0x01}, {0x61, 0x00, 0x01, 0x01}, {0x62, 0x01, 0xff, 0x01},
 | |
|   {0x63, 0x03, 0xff, 0x01}, {0x64, 0x06, 0xff, 0x01}, {0x65, 0x00, 0x24, 0x01},
 | |
|   {0x66, 0x0a, 0xff, 0x01}, {0x67, 0x0c, 0xff, 0x01}, {0x68, 0x0f, 0xff, 0x01},
 | |
|   {0x69, 0x00, 0x03, 0x01}, {0x6a, 0x11, 0xff, 0x01}, {0x6b, 0x13, 0xff, 0x01},
 | |
|   {0x6c, 0x16, 0xff, 0x01}, {0x6d, 0x1c, 0xff, 0x01}, {0x6e, 0x1e, 0xff, 0x01},
 | |
|   {0x6f, 0x00, 0x26, 0x01}, {0x70, 0x20, 0xff, 0x01}, {0x72, 0x22, 0xff, 0x01},
 | |
|   {0x73, 0x24, 0xff, 0x01}, {0x74, 0x27, 0xff, 0x01}, {0x75, 0x00, 0x06, 0x01},
 | |
|   {0x76, 0x2c, 0xff, 0x01}, {0x77, 0x2d, 0xff, 0x01}, {0x78, 0x2f, 0xff, 0x01},
 | |
|   {0x79, 0x35, 0xff, 0x01}, {0x7a, 0x36, 0xff, 0x01}, {0xe3, 0x38, 0xff, 0x01},
 | |
|   {0x61, 0x00, 0x72, 0x01}, {0x62, 0x01, 0x56, 0x01}, {0x65, 0x00, 0x89, 0x01},
 | |
|   {0x69, 0x00, 0x78, 0x01}, {0x6f, 0x00, 0x8c, 0x01}, {0x75, 0x00, 0x86, 0x01},
 | |
|   {0x79, 0x02, 0xff, 0x00}, {0x61, 0x00, 0x79, 0x01}, {0x6f, 0x00, 0x7b, 0x01},
 | |
|   {0x75, 0x00, 0x7a, 0x01}, {0x63, 0x03, 0x56, 0x01}, {0x68, 0x04, 0xff, 0x01},
 | |
|   {0x79, 0x05, 0xff, 0x01}, {0x61, 0x00, 0x4f, 0x00}, {0x65, 0x00, 0x4e, 0x00},
 | |
|   {0x69, 0x00, 0x4d, 0x01}, {0x6f, 0x00, 0x51, 0x00}, {0x75, 0x00, 0x50, 0x00},
 | |
|   {0x61, 0x00, 0x4f, 0x01}, {0x6f, 0x00, 0x51, 0x01}, {0x75, 0x00, 0x50, 0x01},
 | |
|   {0x61, 0x00, 0x4c, 0x01}, {0x64, 0x06, 0x56, 0x01}, {0x65, 0x00, 0x60, 0x01},
 | |
|   {0x68, 0x07, 0xff, 0x00}, {0x69, 0x00, 0x61, 0x00}, {0x6f, 0x00, 0x65, 0x01},
 | |
|   {0x75, 0x00, 0x5c, 0x01}, {0x77, 0x08, 0xff, 0x00}, {0x79, 0x09, 0xff, 0x01},
 | |
|   {0x69, 0x00, 0x61, 0x01}, {0x75, 0x00, 0x62, 0x01}, {0x75, 0x00, 0x66, 0x01},
 | |
|   {0x61, 0x00, 0x53, 0x01}, {0x6f, 0x00, 0x55, 0x01}, {0x75, 0x00, 0x54, 0x01},
 | |
|   {0x61, 0x00, 0x81, 0x00}, {0x65, 0x00, 0x83, 0x00}, {0x66, 0x0a, 0x56, 0x01},
 | |
|   {0x69, 0x00, 0x82, 0x00}, {0x6f, 0x00, 0x84, 0x00}, {0x75, 0x00, 0x80, 0x01},
 | |
|   {0x79, 0x0b, 0xff, 0x00}, {0x75, 0x00, 0x85, 0x01}, {0x61, 0x00, 0x28, 0x01},
 | |
|   {0x65, 0x00, 0x36, 0x01}, {0x67, 0x0c, 0x56, 0x01}, {0x69, 0x00, 0x2d, 0x01},
 | |
|   {0x6f, 0x00, 0x38, 0x01}, {0x75, 0x00, 0x33, 0x01}, {0x77, 0x0d, 0xff, 0x00},
 | |
|   {0x79, 0x0e, 0xff, 0x00}, {0x61, 0x00, 0x34, 0x01}, {0x61, 0x00, 0x2e, 0x01},
 | |
|   {0x6f, 0x00, 0x30, 0x01}, {0x75, 0x00, 0x2f, 0x01}, {0x61, 0x00, 0x71, 0x01},
 | |
|   {0x65, 0x00, 0x88, 0x01}, {0x68, 0x0f, 0x56, 0x01}, {0x69, 0x00, 0x74, 0x01},
 | |
|   {0x6f, 0x00, 0x8b, 0x01}, {0x75, 0x00, 0x80, 0x01}, {0x79, 0x10, 0xff, 0x00},
 | |
|   {0x61, 0x00, 0x75, 0x01}, {0x6f, 0x00, 0x77, 0x01}, {0x75, 0x00, 0x76, 0x01},
 | |
|   {0x61, 0x00, 0x42, 0x00}, {0x65, 0x00, 0x41, 0x00}, {0x69, 0x00, 0x40, 0x01},
 | |
|   {0x6a, 0x11, 0x56, 0x01}, {0x6f, 0x00, 0x44, 0x00}, {0x75, 0x00, 0x43, 0x00},
 | |
|   {0x79, 0x12, 0xff, 0x00}, {0x61, 0x00, 0x42, 0x01}, {0x6f, 0x00, 0x44, 0x01},
 | |
|   {0x75, 0x00, 0x43, 0x01}, {0x61, 0x00, 0x27, 0x01}, {0x65, 0x00, 0x35, 0x01},
 | |
|   {0x69, 0x00, 0x29, 0x01}, {0x6b, 0x13, 0x56, 0x01}, {0x6f, 0x00, 0x37, 0x01},
 | |
|   {0x75, 0x00, 0x31, 0x01}, {0x77, 0x14, 0xff, 0x00}, {0x79, 0x15, 0xff, 0x00},
 | |
|   {0x61, 0x00, 0x32, 0x01}, {0x61, 0x00, 0x2a, 0x01}, {0x6f, 0x00, 0x2c, 0x01},
 | |
|   {0x75, 0x00, 0x2b, 0x01}, {0x61, 0x00, 0x00, 0x01}, {0x65, 0x00, 0x23, 0x01},
 | |
|   {0x69, 0x00, 0x02, 0x01}, {0x6b, 0x17, 0xff, 0x01}, {0x6c, 0x16, 0x56, 0x01},
 | |
|   {0x6f, 0x00, 0x25, 0x01}, {0x74, 0x18, 0xff, 0x01}, {0x75, 0x00, 0x05, 0x01},
 | |
|   {0x77, 0x1a, 0xff, 0x01}, {0x79, 0x1b, 0xff, 0x01}, {0x61, 0x00, 0xb0, 0x01},
 | |
|   {0x65, 0x00, 0xb1, 0x01}, {0x73, 0x19, 0xff, 0x00}, {0x75, 0x00, 0x56, 0x01},
 | |
|   {0x75, 0x00, 0x56, 0x01}, {0x61, 0x00, 0xa4, 0x01}, {0x61, 0x00, 0x96, 0x01},
 | |
|   {0x65, 0x00, 0x23, 0x01}, {0x69, 0x00, 0x02, 0x01}, {0x6f, 0x00, 0x9a, 0x01},
 | |
|   {0x75, 0x00, 0x98, 0x01}, {0x61, 0x00, 0x8e, 0x01}, {0x65, 0x00, 0x94, 0x01},
 | |
|   {0x69, 0x00, 0x8f, 0x01}, {0x6d, 0x1c, 0x56, 0x01}, {0x6f, 0x00, 0x95, 0x01},
 | |
|   {0x75, 0x00, 0x93, 0x01}, {0x79, 0x1d, 0xff, 0x00}, {0x61, 0x00, 0x90, 0x01},
 | |
|   {0x6f, 0x00, 0x92, 0x01}, {0x75, 0x00, 0x91, 0x01}, {0x00, 0x00, 0xa9, 0x01},
 | |
|   {0x27, 0x00, 0xa9, 0x00}, {0x2d, 0x00, 0xaa, 0x00}, {0x61, 0x00, 0x67, 0x01},
 | |
|   {0x62, 0x01, 0xa9, 0x00}, {0x63, 0x03, 0xa9, 0x00}, {0x64, 0x06, 0xa9, 0x00},
 | |
|   {0x65, 0x00, 0x6f, 0x01}, {0x66, 0x0a, 0xa9, 0x00}, {0x67, 0x0c, 0xa9, 0x00},
 | |
|   {0x68, 0x0f, 0xa9, 0x00}, {0x69, 0x00, 0x68, 0x01}, {0x6a, 0x11, 0xa9, 0x00},
 | |
|   {0x6b, 0x13, 0xa9, 0x00}, {0x6c, 0x16, 0xa9, 0x00}, {0x6d, 0x1c, 0xa9, 0x00},
 | |
|   {0x6e, 0x00, 0xa9, 0x00}, {0x6f, 0x00, 0x70, 0x01}, {0x70, 0x20, 0xa9, 0x00},
 | |
|   {0x72, 0x22, 0xa9, 0x00}, {0x73, 0x24, 0xa9, 0x00}, {0x74, 0x27, 0xa9, 0x00},
 | |
|   {0x75, 0x00, 0x6e, 0x01}, {0x76, 0x2c, 0xa9, 0x00}, {0x77, 0x2d, 0xa9, 0x00},
 | |
|   {0x78, 0x2f, 0xa9, 0x00}, {0x79, 0x1f, 0xff, 0x00}, {0x7a, 0x36, 0xa9, 0x00},
 | |
|   {0xe3, 0x38, 0xa9, 0x00}, {0x00, 0x00, 0xa9, 0x01}, {0x61, 0x00, 0x6b, 0x01},
 | |
|   {0x65, 0x00, 0x6a, 0x01}, {0x69, 0x00, 0x69, 0x01}, {0x6f, 0x00, 0x6d, 0x01},
 | |
|   {0x75, 0x00, 0x6c, 0x01}, {0x61, 0x00, 0x73, 0x01}, {0x65, 0x00, 0x8a, 0x01},
 | |
|   {0x69, 0x00, 0x7c, 0x01}, {0x6f, 0x00, 0x8d, 0x01}, {0x70, 0x20, 0x56, 0x01},
 | |
|   {0x75, 0x00, 0x87, 0x01}, {0x79, 0x21, 0xff, 0x00}, {0x61, 0x00, 0x7d, 0x01},
 | |
|   {0x6f, 0x00, 0x7f, 0x01}, {0x75, 0x00, 0x7e, 0x01}, {0x61, 0x00, 0x9c, 0x01},
 | |
|   {0x65, 0x00, 0xa2, 0x01}, {0x69, 0x00, 0x9d, 0x01}, {0x6f, 0x00, 0xa3, 0x01},
 | |
|   {0x72, 0x22, 0x56, 0x01}, {0x75, 0x00, 0xa1, 0x01}, {0x79, 0x23, 0xff, 0x00},
 | |
|   {0x61, 0x00, 0x9e, 0x01}, {0x6f, 0x00, 0xa0, 0x01}, {0x75, 0x00, 0x9f, 0x01},
 | |
|   {0x61, 0x00, 0x39, 0x01}, {0x65, 0x00, 0x47, 0x01}, {0x68, 0x25, 0xff, 0x00},
 | |
|   {0x69, 0x00, 0x3b, 0x01}, {0x6f, 0x00, 0x49, 0x01}, {0x73, 0x24, 0x56, 0x01},
 | |
|   {0x75, 0x00, 0x45, 0x01}, {0x79, 0x26, 0xff, 0x00}, {0x61, 0x00, 0x3d, 0x00},
 | |
|   {0x65, 0x00, 0x3c, 0x00}, {0x69, 0x00, 0x3b, 0x01}, {0x6f, 0x00, 0x3f, 0x00},
 | |
|   {0x75, 0x00, 0x3e, 0x00}, {0x61, 0x00, 0x3d, 0x01}, {0x65, 0x00, 0x3c, 0x01},
 | |
|   {0x6f, 0x00, 0x3f, 0x01}, {0x75, 0x00, 0x3e, 0x01}, {0x61, 0x00, 0x4b, 0x01},
 | |
|   {0x65, 0x00, 0x5d, 0x01}, {0x68, 0x28, 0xff, 0x00}, {0x69, 0x00, 0x4d, 0x01},
 | |
|   {0x6f, 0x00, 0x63, 0x01}, {0x73, 0x29, 0xff, 0x00}, {0x74, 0x27, 0x56, 0x01},
 | |
|   {0x75, 0x00, 0x57, 0x01}, {0x77, 0x2a, 0xff, 0x00}, {0x79, 0x2b, 0xff, 0x00},
 | |
|   {0x69, 0x00, 0x5e, 0x01}, {0x75, 0x00, 0x5f, 0x01}, {0x61, 0x00, 0x58, 0x00},
 | |
|   {0x65, 0x00, 0x5a, 0x00}, {0x69, 0x00, 0x59, 0x00}, {0x6f, 0x00, 0x5b, 0x00},
 | |
|   {0x75, 0x00, 0x57, 0x01}, {0x75, 0x00, 0x64, 0x01}, {0x61, 0x00, 0x4f, 0x01},
 | |
|   {0x65, 0x00, 0x4e, 0x01}, {0x6f, 0x00, 0x51, 0x01}, {0x75, 0x00, 0x50, 0x01},
 | |
|   {0x61, 0x00, 0xac, 0x00}, {0x65, 0x00, 0xae, 0x00}, {0x69, 0x00, 0xad, 0x00},
 | |
|   {0x6f, 0x00, 0xaf, 0x00}, {0x75, 0x00, 0xab, 0x01}, {0x76, 0x2c, 0x56, 0x01},
 | |
|   {0x61, 0x00, 0xa5, 0x01}, {0x65, 0x00, 0x0b, 0x01}, {0x69, 0x00, 0x08, 0x01},
 | |
|   {0x6f, 0x00, 0xa8, 0x01}, {0x77, 0x2d, 0x56, 0x01}, {0x79, 0x2e, 0xff, 0x01},
 | |
|   {0x65, 0x00, 0xa7, 0x01}, {0x69, 0x00, 0xa6, 0x01}, {0x61, 0x00, 0x00, 0x01},
 | |
|   {0x65, 0x00, 0x23, 0x01}, {0x69, 0x00, 0x02, 0x01}, {0x6b, 0x30, 0xff, 0x01},
 | |
|   {0x6f, 0x00, 0x25, 0x01}, {0x74, 0x31, 0xff, 0x01}, {0x75, 0x00, 0x05, 0x01},
 | |
|   {0x77, 0x33, 0xff, 0x01}, {0x78, 0x2f, 0x56, 0x01}, {0x79, 0x34, 0xff, 0x01},
 | |
|   {0x61, 0x00, 0xb0, 0x01}, {0x65, 0x00, 0xb1, 0x01}, {0x73, 0x32, 0xff, 0x00},
 | |
|   {0x75, 0x00, 0x56, 0x01}, {0x75, 0x00, 0x56, 0x01}, {0x61, 0x00, 0xa4, 0x01},
 | |
|   {0x61, 0x00, 0x96, 0x01}, {0x65, 0x00, 0x23, 0x01}, {0x69, 0x00, 0x02, 0x01},
 | |
|   {0x6f, 0x00, 0x9a, 0x01}, {0x75, 0x00, 0x98, 0x01}, {0x61, 0x00, 0x97, 0x01},
 | |
|   {0x65, 0x00, 0x04, 0x01}, {0x6f, 0x00, 0x9b, 0x01}, {0x75, 0x00, 0x99, 0x01},
 | |
|   {0x79, 0x35, 0x56, 0x01}, {0x61, 0x00, 0x3a, 0x01}, {0x65, 0x00, 0x48, 0x01},
 | |
|   {0x69, 0x00, 0x40, 0x01}, {0x6f, 0x00, 0x4a, 0x01}, {0x75, 0x00, 0x46, 0x01},
 | |
|   {0x79, 0x37, 0xff, 0x00}, {0x7a, 0x36, 0x56, 0x01}, {0x61, 0x00, 0x42, 0x01},
 | |
|   {0x65, 0x00, 0x41, 0x01}, {0x6f, 0x00, 0x44, 0x01}, {0x75, 0x00, 0x43, 0x01},
 | |
|   {0x81, 0x39, 0xff, 0x01}, {0x82, 0x3d, 0xff, 0x01}, {0x81, 0x00, 0x00, 0x01},
 | |
|   {0x82, 0x00, 0x01, 0x01}, {0x83, 0x00, 0x02, 0x01}, {0x84, 0x00, 0x03, 0x01},
 | |
|   {0x85, 0x00, 0x05, 0x01}, {0x86, 0x3a, 0xff, 0x01}, {0x87, 0x00, 0x23, 0x01},
 | |
|   {0x88, 0x00, 0x24, 0x01}, {0x89, 0x00, 0x25, 0x01}, {0x8a, 0x00, 0x26, 0x01},
 | |
|   {0x8b, 0x00, 0x27, 0x01}, {0x8c, 0x00, 0x28, 0x01}, {0x8d, 0x00, 0x29, 0x01},
 | |
|   {0x8e, 0x00, 0x2d, 0x01}, {0x8f, 0x00, 0x31, 0x01}, {0x90, 0x00, 0x33, 0x01},
 | |
|   {0x91, 0x00, 0x35, 0x01}, {0x92, 0x00, 0x36, 0x01}, {0x93, 0x00, 0x37, 0x01},
 | |
|   {0x94, 0x00, 0x38, 0x01}, {0x95, 0x00, 0x39, 0x01}, {0x96, 0x00, 0x3a, 0x01},
 | |
|   {0x97, 0x00, 0x3b, 0x01}, {0x98, 0x00, 0x40, 0x01}, {0x99, 0x00, 0x45, 0x01},
 | |
|   {0x9a, 0x00, 0x46, 0x01}, {0x9b, 0x00, 0x47, 0x01}, {0x9c, 0x00, 0x48, 0x01},
 | |
|   {0x9d, 0x00, 0x49, 0x01}, {0x9e, 0x00, 0x4a, 0x01}, {0x9f, 0x00, 0x4b, 0x01},
 | |
|   {0xa0, 0x00, 0x4c, 0x01}, {0xa1, 0x00, 0x4d, 0x01}, {0xa2, 0x00, 0x52, 0x01},
 | |
|   {0xa3, 0x00, 0x56, 0x01}, {0xa4, 0x00, 0x57, 0x01}, {0xa5, 0x00, 0x5c, 0x01},
 | |
|   {0xa6, 0x00, 0x5d, 0x01}, {0xa7, 0x00, 0x60, 0x01}, {0xa8, 0x00, 0x63, 0x01},
 | |
|   {0xa9, 0x00, 0x65, 0x01}, {0xaa, 0x00, 0x67, 0x01}, {0xab, 0x00, 0x68, 0x01},
 | |
|   {0xac, 0x00, 0x6e, 0x01}, {0xad, 0x00, 0x6f, 0x01}, {0xae, 0x00, 0x70, 0x01},
 | |
|   {0xaf, 0x00, 0x71, 0x01}, {0xb0, 0x00, 0x72, 0x01}, {0xb1, 0x00, 0x73, 0x01},
 | |
|   {0xb2, 0x00, 0x74, 0x01}, {0xb3, 0x00, 0x78, 0x01}, {0xb4, 0x00, 0x7c, 0x01},
 | |
|   {0xb5, 0x00, 0x80, 0x01}, {0xb6, 0x00, 0x86, 0x01}, {0xb7, 0x00, 0x87, 0x01},
 | |
|   {0xb8, 0x00, 0x88, 0x01}, {0xb9, 0x00, 0x89, 0x01}, {0xba, 0x00, 0x8a, 0x01},
 | |
|   {0xbb, 0x00, 0x8b, 0x01}, {0xbc, 0x00, 0x8c, 0x01}, {0xbd, 0x00, 0x8d, 0x01},
 | |
|   {0xbe, 0x00, 0x8e, 0x01}, {0xbf, 0x00, 0x8f, 0x01}, {0x00, 0x00, 0x06, 0x00},
 | |
|   {0x2d, 0x00, 0x22, 0x00}, {0x61, 0x00, 0x07, 0x00}, {0x62, 0x01, 0x06, 0x00},
 | |
|   {0x63, 0x03, 0x06, 0x00}, {0x64, 0x06, 0x06, 0x00}, {0x65, 0x00, 0x0c, 0x00},
 | |
|   {0x66, 0x0a, 0x06, 0x00}, {0x67, 0x0c, 0x06, 0x00}, {0x68, 0x0f, 0x06, 0x00},
 | |
|   {0x69, 0x00, 0x09, 0x00}, {0x6a, 0x11, 0x06, 0x00}, {0x6b, 0x13, 0x06, 0x00},
 | |
|   {0x6c, 0x16, 0x06, 0x00}, {0x6d, 0x1c, 0x06, 0x00}, {0x6e, 0x1e, 0x06, 0x00},
 | |
|   {0x6f, 0x00, 0x0d, 0x00}, {0x70, 0x20, 0x06, 0x00}, {0x72, 0x22, 0x06, 0x00},
 | |
|   {0x73, 0x24, 0x06, 0x00}, {0x74, 0x27, 0x06, 0x00}, {0x75, 0x00, 0x0a, 0x00},
 | |
|   {0x76, 0x2c, 0x06, 0x00}, {0x77, 0x2d, 0x06, 0x00}, {0x78, 0x2f, 0x06, 0x00},
 | |
|   {0x79, 0x35, 0x06, 0x00}, {0x7a, 0x36, 0x06, 0x00}, {0xe3, 0x3b, 0xff, 0x01},
 | |
|   {0x00, 0x00, 0x06, 0x00}, {0x81, 0x39, 0x06, 0x00}, {0x82, 0x3c, 0xff, 0x01},
 | |
|   {0x00, 0x00, 0x06, 0x01}, {0x80, 0x00, 0x0e, 0x00}, {0x81, 0x00, 0x0f, 0x00},
 | |
|   {0x82, 0x00, 0x10, 0x00}, {0x83, 0x00, 0x11, 0x00}, {0x84, 0x00, 0x12, 0x00},
 | |
|   {0x85, 0x00, 0x13, 0x00}, {0x86, 0x00, 0x14, 0x00}, {0x87, 0x00, 0x15, 0x00},
 | |
|   {0x88, 0x00, 0x16, 0x00}, {0x89, 0x00, 0x17, 0x00}, {0x8a, 0x00, 0x18, 0x00},
 | |
|   {0x8b, 0x00, 0x19, 0x00}, {0x8c, 0x00, 0x1a, 0x00}, {0x8d, 0x00, 0x1b, 0x00},
 | |
|   {0x8e, 0x00, 0x1c, 0x00}, {0x8f, 0x00, 0x1d, 0x00}, {0x90, 0x00, 0x1e, 0x00},
 | |
|   {0x91, 0x00, 0x1f, 0x00}, {0x92, 0x00, 0x20, 0x00}, {0x93, 0x00, 0x21, 0x00},
 | |
|   {0x9b, 0x00, 0xab, 0x01}, {0x80, 0x00, 0x93, 0x01}, {0x81, 0x00, 0x94, 0x01},
 | |
|   {0x82, 0x00, 0x95, 0x01}, {0x83, 0x00, 0x96, 0x01}, {0x84, 0x00, 0x97, 0x01},
 | |
|   {0x85, 0x00, 0x98, 0x01}, {0x86, 0x00, 0x99, 0x01}, {0x87, 0x00, 0x9a, 0x01},
 | |
|   {0x88, 0x00, 0x9b, 0x01}, {0x89, 0x00, 0x9c, 0x01}, {0x8a, 0x00, 0x9d, 0x01},
 | |
|   {0x8b, 0x00, 0xa1, 0x01}, {0x8c, 0x00, 0xa2, 0x01}, {0x8d, 0x00, 0xa3, 0x01},
 | |
|   {0x8e, 0x00, 0xa4, 0x01}, {0x8f, 0x00, 0xa5, 0x01}, {0x90, 0x00, 0xa6, 0x01},
 | |
|   {0x91, 0x00, 0xa7, 0x01}, {0x92, 0x00, 0xa8, 0x01}, {0x93, 0x00, 0xa9, 0x01}
 | |
| };
 | |
| 
 | |
| static rk_tree_node *
 | |
| rk_lookup(uint8_t state, uint8_t code)
 | |
| {
 | |
|   if (state < sizeof(rk_tree_idx)/sizeof(uint16_t)) {
 | |
|     uint16_t ns = state ? rk_tree_idx[state - 1] : 0;
 | |
|     uint16_t ne = rk_tree_idx[state];
 | |
|     while (ns < ne) {
 | |
|       uint16_t m = (ns + ne)>>1;
 | |
|       rk_tree_node *rn = &rk_tree[m];
 | |
|       if (rn->code == code) { return rn; }
 | |
|       if (rn->code < code) {
 | |
|         ns = m + 1;
 | |
|       } else {
 | |
|         ne = m;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| static uint32_t
 | |
| rk_emit(rk_tree_node *rn, char **str)
 | |
| {
 | |
|   if (rn && rn->emit != 0xff) {
 | |
|     uint16_t pos = rn->emit ? rk_str_idx[rn->emit - 1] :  0;
 | |
|     *str = &rk_str[pos];
 | |
|     return (uint32_t)(rk_str_idx[rn->emit] - pos);
 | |
|   } else {
 | |
|     *str = NULL;
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| #define RK_OUTPUT(e,l) do {\
 | |
|   if (oc < oe) {\
 | |
|     uint32_t l_ = (oc + (l) < oe) ? (l) : (oe - oc);\
 | |
|     grn_memcpy(oc, (e), l_);\
 | |
|     oc += l_;\
 | |
|     ic_ = ic;\
 | |
|   }\
 | |
| } while (0)
 | |
| 
 | |
| static uint32_t
 | |
| rk_conv(const char *str, uint32_t str_len, uint8_t *buf, uint32_t buf_size, uint8_t *statep)
 | |
| {
 | |
|   uint32_t l;
 | |
|   uint8_t state = 0;
 | |
|   rk_tree_node *rn;
 | |
|   char *e;
 | |
|   uint8_t *oc = buf, *oe = oc + buf_size;
 | |
|   const uint8_t *ic = (uint8_t *)str, *ic_ = ic, *ie = ic + str_len;
 | |
|   while (ic < ie) {
 | |
|     if ((rn = rk_lookup(state, *ic))) {
 | |
|       ic++;
 | |
|       if ((l = rk_emit(rn, &e))) { RK_OUTPUT(e, l); }
 | |
|       state = rn->next;
 | |
|     } else {
 | |
|       if (!state) { ic++; }
 | |
|       if (ic_ < ic) { RK_OUTPUT(ic_, ic - ic_); }
 | |
|       state = 0;
 | |
|     }
 | |
|   }
 | |
| #ifdef FLUSH_UNRESOLVED_INPUT
 | |
|   if ((rn = rk_lookup(state, 0))) {
 | |
|     if ((l = rk_emit(rn, &e))) { RK_OUTPUT(e, l); }
 | |
|     state = rn->next;
 | |
|   } else {
 | |
|     if (ic_ < ic) { RK_OUTPUT(ic_, ic - ic_); }
 | |
|   }
 | |
| #endif /* FLUSH_UNRESOLVED_INPUT */
 | |
|   *statep = state;
 | |
|   return oc - buf;
 | |
| }
 | |
| 
 | |
| static grn_id
 | |
| sub_search(grn_ctx *ctx, grn_pat *pat, grn_id id,
 | |
|            int *c0, uint8_t *key, uint32_t key_len)
 | |
| {
 | |
|   pat_node *pn;
 | |
|   uint32_t len = key_len * 16;
 | |
|   if (!key_len) { return id; }
 | |
|   PAT_AT(pat, id, pn);
 | |
|   while (pn) {
 | |
|     int ch;
 | |
|     ch = PAT_CHK(pn);
 | |
|     if (*c0 < ch && ch < (int) len - 1) {
 | |
|       if (ch & 1) {
 | |
|         id = (ch + 1 < (int) len) ? pn->lr[1] : pn->lr[0];
 | |
|       } else {
 | |
|         id = pn->lr[nth_bit(key, ch, len)];
 | |
|       }
 | |
|       *c0 = ch;
 | |
|       PAT_AT(pat, id, pn);
 | |
|     } else {
 | |
|       const uint8_t *k = pat_node_get_key(ctx, pat, pn);
 | |
|       return (k && key_len <= PAT_LEN(pn) && !memcmp(k, key, key_len)) ? id : GRN_ID_NIL;
 | |
|     }
 | |
|   }
 | |
|   return GRN_ID_NIL;
 | |
| }
 | |
| 
 | |
| static void
 | |
| search_push(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
 | |
|             uint8_t *key, uint32_t key_len, uint8_t state, grn_id id, int c0, int flags)
 | |
| {
 | |
|   if (state) {
 | |
|     int step;
 | |
|     uint16_t ns, ne;
 | |
|     if (flags & GRN_CURSOR_DESCENDING) {
 | |
|       ns = rk_tree_idx[state - 1];
 | |
|       ne = rk_tree_idx[state];
 | |
|       step = 1;
 | |
|     } else {
 | |
|       ns = rk_tree_idx[state] - 1;
 | |
|       ne = rk_tree_idx[state - 1] - 1;
 | |
|       step = -1;
 | |
|     }
 | |
|     for (; ns != ne; ns += step) {
 | |
|       rk_tree_node *rn = &rk_tree[ns];
 | |
|       if (rn->attr) {
 | |
|         char *e;
 | |
|         uint32_t l = rk_emit(rn, &e);
 | |
|         if (l) {
 | |
|           if (l + key_len <= GRN_TABLE_MAX_KEY_SIZE) {
 | |
|             int ch = c0;
 | |
|             grn_id i;
 | |
|             grn_memcpy(key + key_len, e, l);
 | |
|             if ((i = sub_search(ctx, pat, id, &ch, key, key_len + l))) {
 | |
|               search_push(ctx, pat, c, key, key_len + l, rn->next, i, ch, flags);
 | |
|             }
 | |
|           }
 | |
|         } else {
 | |
|           search_push(ctx, pat, c, key, key_len, rn->next, id, c0, flags);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     pat_node *pn;
 | |
|     PAT_AT(pat, id, pn);
 | |
|     if (pn) {
 | |
|       int ch = PAT_CHK(pn);
 | |
|       uint32_t len = key_len * 16;
 | |
|       if (c0 < ch) {
 | |
|         if (flags & GRN_CURSOR_DESCENDING) {
 | |
|           if ((ch > (int) len - 1) || !(flags & GRN_CURSOR_GT)) { push(c, pn->lr[0], ch); }
 | |
|           push(c, pn->lr[1], ch);
 | |
|         } else {
 | |
|           push(c, pn->lr[1], ch);
 | |
|           if ((ch > (int) len - 1) || !(flags & GRN_CURSOR_GT)) { push(c, pn->lr[0], ch); }
 | |
|         }
 | |
|       } else {
 | |
|         if (PAT_LEN(pn) * 16 > len || !(flags & GRN_CURSOR_GT)) { push(c, id, ch); }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static grn_rc
 | |
| set_cursor_rk(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
 | |
|                   const void *key, uint32_t key_len, int flags)
 | |
| {
 | |
|   grn_id id;
 | |
|   uint8_t state;
 | |
|   pat_node *pn;
 | |
|   int c0 = -1;
 | |
|   uint32_t len, byte_len;
 | |
|   uint8_t keybuf[GRN_TABLE_MAX_KEY_SIZE];
 | |
|   if (flags & GRN_CURSOR_SIZE_BY_BIT) { return GRN_OPERATION_NOT_SUPPORTED; }
 | |
|   byte_len = rk_conv(key, key_len, keybuf, GRN_TABLE_MAX_KEY_SIZE, &state);
 | |
|   len = byte_len * 16;
 | |
|   PAT_AT(pat, 0, pn);
 | |
|   id = pn->lr[1];
 | |
|   if ((id = sub_search(ctx, pat, id, &c0, keybuf, byte_len))) {
 | |
|     search_push(ctx, pat, c, keybuf, byte_len, state, id, c0, flags);
 | |
|   }
 | |
|   return ctx->rc;
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| grn_pat_total_key_size(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   return pat->header->curr_key;
 | |
| }
 | |
| 
 | |
| grn_bool
 | |
| grn_pat_is_key_encoded(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   grn_obj *domain;
 | |
|   uint32_t key_size;
 | |
| 
 | |
|   domain = grn_ctx_at(ctx, pat->obj.header.domain);
 | |
|   if (grn_obj_is_type(ctx, domain)) {
 | |
|     key_size = grn_type_size(ctx, domain);
 | |
|   } else {
 | |
|     key_size = sizeof(grn_id);
 | |
|   }
 | |
| 
 | |
|   return KEY_NEEDS_CONVERT(pat, key_size);
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_dirty(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   grn_rc rc = GRN_SUCCESS;
 | |
| 
 | |
|   CRITICAL_SECTION_ENTER(pat->lock);
 | |
|   if (!pat->is_dirty) {
 | |
|     uint32_t n_dirty_opens;
 | |
|     pat->is_dirty = GRN_TRUE;
 | |
|     GRN_ATOMIC_ADD_EX(&(pat->header->n_dirty_opens), 1, n_dirty_opens);
 | |
|     rc = grn_io_flush(ctx, pat->io);
 | |
|   }
 | |
|   CRITICAL_SECTION_LEAVE(pat->lock);
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| grn_bool
 | |
| grn_pat_is_dirty(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   return pat->header->n_dirty_opens > 0;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_clean(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   grn_rc rc = GRN_SUCCESS;
 | |
| 
 | |
|   CRITICAL_SECTION_ENTER(pat->lock);
 | |
|   if (pat->is_dirty) {
 | |
|     uint32_t n_dirty_opens;
 | |
|     pat->is_dirty = GRN_FALSE;
 | |
|     GRN_ATOMIC_ADD_EX(&(pat->header->n_dirty_opens), -1, n_dirty_opens);
 | |
|     rc = grn_io_flush(ctx, pat->io);
 | |
|   }
 | |
|   CRITICAL_SECTION_LEAVE(pat->lock);
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_pat_clear_dirty(grn_ctx *ctx, grn_pat *pat)
 | |
| {
 | |
|   grn_rc rc = GRN_SUCCESS;
 | |
| 
 | |
|   CRITICAL_SECTION_ENTER(pat->lock);
 | |
|   pat->is_dirty = GRN_FALSE;
 | |
|   pat->header->n_dirty_opens = 0;
 | |
|   rc = grn_io_flush(ctx, pat->io);
 | |
|   CRITICAL_SECTION_LEAVE(pat->lock);
 | |
| 
 | |
|   return rc;
 | |
| }
 | 
