mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 20:36:16 +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;
 | 
						|
}
 |