<commit_before>/* -*- c-basic-offset: 2 -*- */
/* Copyright(C) 2009-2012 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-1301  USA
*/

#ifndef GROONGA_IN_H
#include "groonga_in.h"
#endif /* GROONGA_IN_H */

#include <string.h>
#include "str.h"
#include "db.h"
#include "util.h"
#include "output.h"

#define LEVELS (&ctx->impl->levels)
#define DEPTH (GRN_BULK_VSIZE(LEVELS)>>2)
#define CURR_LEVEL (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1))) : 0)
#define INCR_DEPTH(i) GRN_UINT32_PUT(ctx, LEVELS, i)
#define DECR_DEPTH (DEPTH ? grn_bulk_truncate(ctx, LEVELS, GRN_BULK_VSIZE(LEVELS) - sizeof(uint32_t)) : 0)
#define INCR_LENGTH (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1)) += 2) : 0)

static void
put_delimiter(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
{
  uint32_t level = CURR_LEVEL;
  switch (output_type) {
  case GRN_CONTENT_JSON:
    if (level < 2) { return; }
    GRN_TEXT_PUTC(ctx, outbuf, ((level & 3) == 3) ? ':' : ',');
    // if (DEPTH == 1 && ((level & 3) != 3)) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); }
    break;
  case GRN_CONTENT_XML:
    if (!DEPTH) { return; }
    GRN_TEXT_PUTC(ctx, outbuf, '\n');
    break;
  case GRN_CONTENT_TSV:
    if (level < 2) { return; }
    if (DEPTH <= 2) {
      GRN_TEXT_PUTC(ctx, outbuf, ((level & 3) == 3) ? '\t' : '\n');
    } else {
      GRN_TEXT_PUTC(ctx, outbuf, '\t');
    }
  case GRN_CONTENT_MSGPACK :
    // do nothing
    break;
  case GRN_CONTENT_NONE:
    break;
  }
}

void
grn_output_array_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                      const char *name, int nelements)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTC(ctx, outbuf, '[');
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTC(ctx, outbuf, '<');
    GRN_TEXT_PUTS(ctx, outbuf, name);
    GRN_TEXT_PUTC(ctx, outbuf, '>');
    grn_vector_add_element(ctx, &ctx->impl->names, name, strlen(name), 0, GRN_DB_SHORT_TEXT);
    break;
  case GRN_CONTENT_TSV:
    if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "[\t"); }
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    if (nelements < 0) {
      GRN_LOG(ctx, GRN_LOG_DEBUG,
              "grn_output_array_open nelements (%d) for <%s>",
              nelements,
              name);
    }
    msgpack_pack_array(&ctx->impl->msgpacker, nelements);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_DEPTH(0);
}

void
grn_output_array_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
{
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTC(ctx, outbuf, ']');
    break;
  case GRN_CONTENT_TSV:
    if (DEPTH > 3) {
      if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); }
      GRN_TEXT_PUTC(ctx, outbuf, ']');
    }
    break;
  case GRN_CONTENT_XML:
    {
      const char *name;
      unsigned int name_len = grn_vector_pop_element(ctx, &ctx->impl->names, &name, NULL, NULL);
      GRN_TEXT_PUTS(ctx, outbuf, "</");
      GRN_TEXT_PUT(ctx, outbuf, name, name_len);
      GRN_TEXT_PUTC(ctx, outbuf, '>');
    }
    break;
  case GRN_CONTENT_MSGPACK :
    // do nothing
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  DECR_DEPTH;
  INCR_LENGTH;
}

void
grn_output_map_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                    const char *name, int nelements)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTS(ctx, outbuf, "{");
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTC(ctx, outbuf, '<');
    GRN_TEXT_PUTS(ctx, outbuf, name);
    GRN_TEXT_PUTC(ctx, outbuf, '>');
    grn_vector_add_element(ctx, &ctx->impl->names, name, strlen(name), 0, GRN_DB_SHORT_TEXT);
    break;
  case GRN_CONTENT_TSV:
    if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "{\t"); }
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    if (nelements < 0) {
      GRN_LOG(ctx, GRN_LOG_DEBUG,
              "grn_output_map_open nelements (%d) for <%s>",
              nelements,
              name);
    }
    msgpack_pack_map(&ctx->impl->msgpacker, nelements / 2);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_DEPTH(1);
}

void
grn_output_map_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
{
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTS(ctx, outbuf, "}");
    break;
  case GRN_CONTENT_TSV:
    if (DEPTH > 3) {
      if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); }
      GRN_TEXT_PUTC(ctx, outbuf, '}');
    }
    break;
  case GRN_CONTENT_XML:
    {
      const char *name;
      unsigned int name_len = grn_vector_pop_element(ctx, &ctx->impl->names, &name, NULL, NULL);
      GRN_TEXT_PUTS(ctx, outbuf, "</");
      GRN_TEXT_PUT(ctx, outbuf, name, name_len);
      GRN_TEXT_PUTC(ctx, outbuf, '>');
    }
    break;
  case GRN_CONTENT_MSGPACK :
    // do nothing
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  DECR_DEPTH;
  INCR_LENGTH;
}

void
grn_output_int32(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_itoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_TSV:
    grn_text_itoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
    grn_text_itoa(ctx, outbuf, value);
    GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_int32(&ctx->impl->msgpacker, value);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_int64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_lltoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_TSV:
    grn_text_lltoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
    grn_text_lltoa(ctx, outbuf, value);
    GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_int64(&ctx->impl->msgpacker, value);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_uint64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_ulltoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_TSV:
    grn_text_ulltoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
    grn_text_ulltoa(ctx, outbuf, value);
    GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_uint64(&ctx->impl->msgpacker, value);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_float(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, double value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_ftoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_TSV:
    grn_text_ftoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<FLOAT>");
    grn_text_ftoa(ctx, outbuf, value);
    GRN_TEXT_PUTS(ctx, outbuf, "</FLOAT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_double(&ctx->impl->msgpacker, value);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_str(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
               const char *value, size_t value_len)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_esc(ctx, outbuf, value, value_len);
    break;
  case GRN_CONTENT_TSV:
    grn_text_esc(ctx, outbuf, value, value_len);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<TEXT>");
    grn_text_escape_xml(ctx, outbuf, value, value_len);
    GRN_TEXT_PUTS(ctx, outbuf, "</TEXT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_raw(&ctx->impl->msgpacker, value_len);
    msgpack_pack_raw_body(&ctx->impl->msgpacker, value, value_len);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_cstr(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                const char *value)
{
  grn_output_str(ctx, outbuf, output_type, value, strlen(value));
}

void
grn_output_bool(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_bool value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
    break;
  case GRN_CONTENT_TSV:
    GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<BOOL>");
    GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
    GRN_TEXT_PUTS(ctx, outbuf, "</BOOL>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    if (value) {
      msgpack_pack_true(&ctx->impl->msgpacker);
    } else {
      msgpack_pack_false(&ctx->impl->msgpacker);
    }
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

static inline void
grn_output_bulk_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                     const char *value, size_t value_len)
{
  if (value_len == sizeof(grn_id) && *(grn_id *)value == GRN_ID_NIL) {
    put_delimiter(ctx, outbuf, output_type);
    switch (output_type) {
    case GRN_CONTENT_JSON:
      GRN_TEXT_PUTS(ctx, outbuf, "null");
      break;
    case GRN_CONTENT_TSV:
      break;
    case GRN_CONTENT_XML:
      GRN_TEXT_PUTS(ctx, outbuf, "<NULL/>");
      break;
    case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
      msgpack_pack_nil(&ctx->impl->msgpacker);
#endif
      break;
    case GRN_CONTENT_NONE:
      break;
    }
    INCR_LENGTH;
  } else {
    grn_output_str(ctx, outbuf, output_type, value, value_len);
  }
}

void
grn_output_time(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
{
  double dv = value;
  dv /= 1000000.0;
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_ftoa(ctx, outbuf, dv);
    break;
  case GRN_CONTENT_TSV:
    grn_text_ftoa(ctx, outbuf, dv);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<DATE>");
    grn_text_ftoa(ctx, outbuf, dv);
    GRN_TEXT_PUTS(ctx, outbuf, "</DATE>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_double(&ctx->impl->msgpacker, dv);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_geo_point(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                     grn_geo_point *value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    if (value) {
      GRN_TEXT_PUTC(ctx, outbuf, '"');
      grn_text_itoa(ctx, outbuf, value->latitude);
      GRN_TEXT_PUTC(ctx, outbuf, 'x');
      grn_text_itoa(ctx, outbuf, value->longitude);
      GRN_TEXT_PUTC(ctx, outbuf, '"');
    } else {
      GRN_TEXT_PUTS(ctx, outbuf, "null");
    }
    break;
  case GRN_CONTENT_TSV:
    if (value) {
      GRN_TEXT_PUTC(ctx, outbuf, '"');
      grn_text_itoa(ctx, outbuf, value->latitude);
      GRN_TEXT_PUTC(ctx, outbuf, 'x');
      grn_text_itoa(ctx, outbuf, value->longitude);
      GRN_TEXT_PUTC(ctx, outbuf, '"');
    } else {
      GRN_TEXT_PUTS(ctx, outbuf, "\"\"");
    }
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<GEO_POINT>");
    if (value) {
      grn_text_itoa(ctx, outbuf, value->latitude);
      GRN_TEXT_PUTC(ctx, outbuf, 'x');
      grn_text_itoa(ctx, outbuf, value->longitude);
    }
    GRN_TEXT_PUTS(ctx, outbuf, "</GEO_POINT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    if (value) {
      grn_obj buf;
      GRN_TEXT_INIT(&buf, 0);
      grn_text_itoa(ctx, &buf, value->latitude);
      GRN_TEXT_PUTC(ctx, &buf, 'x');
      grn_text_itoa(ctx, &buf, value->longitude);
      msgpack_pack_raw(&ctx->impl->msgpacker, GRN_TEXT_LEN(&buf));
      msgpack_pack_raw_body(&ctx->impl->msgpacker,
                            GRN_TEXT_VALUE(&buf),
                            GRN_TEXT_LEN(&buf));
      grn_obj_close(ctx, &buf);
    } else {
      msgpack_pack_nil(&ctx->impl->msgpacker);
    }
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

static void
grn_text_atoj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
              grn_obj *obj, grn_id id)
{
  int vs;
  grn_obj buf;
  if (obj->header.type == GRN_ACCESSOR) {
    grn_accessor *a = (grn_accessor *)obj;
    GRN_TEXT_INIT(&buf, 0);
    for (;;) {
      buf.header.domain = grn_obj_get_range(ctx, obj);
      GRN_BULK_REWIND(&buf);
      switch (a->action) {
      case GRN_ACCESSOR_GET_ID :
        GRN_UINT32_PUT(ctx, &buf, id);
        buf.header.domain = GRN_DB_UINT32;
        break;
      case GRN_ACCESSOR_GET_KEY :
        grn_table_get_key2(ctx, a->obj, id, &buf);
        buf.header.domain = DB_OBJ(a->obj)->header.domain;
        break;
      case GRN_ACCESSOR_GET_VALUE :
        grn_obj_get_value(ctx, a->obj, id, &buf);
        buf.header.domain = GRN_DB_INT32; /* fix me */
        break;
      case GRN_ACCESSOR_GET_SCORE :
        grn_obj_get_value(ctx, a->obj, id, &buf);
        {
          grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
          GRN_INT32_PUT(ctx, &buf, ri->score);
        }
        buf.header.domain = GRN_DB_INT32;
        break;
      case GRN_ACCESSOR_GET_NSUBRECS :
        {
          grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
          GRN_INT32_PUT(ctx, &buf, ri->n_subrecs);
        }
        buf.header.domain = GRN_DB_INT32;
        break;
      case GRN_ACCESSOR_GET_COLUMN_VALUE :
        if ((a->obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
          if (a->next) {
            grn_id *idp;
            grn_obj_get_value(ctx, a->obj, id, &buf);
            idp = (grn_id *)GRN_BULK_HEAD(&buf);
            vs = GRN_BULK_VSIZE(&buf) / sizeof(grn_id);
            grn_output_array_open(ctx, outbuf, output_type, "COLUMN", vs);
            for (; vs--; idp++) {
              grn_text_atoj(ctx, outbuf, output_type, (grn_obj *)a->next, *idp);
            }
            grn_output_array_close(ctx, outbuf, output_type);
          } else {
            grn_text_atoj(ctx, outbuf, output_type, a->obj, id);
          }
          goto exit;
        } else {
          grn_obj_get_value(ctx, a->obj, id, &buf);
        }
        break;
      case GRN_ACCESSOR_GET_DB_OBJ :
        /* todo */
        break;
      case GRN_ACCESSOR_LOOKUP :
        /* todo */
        break;
      case GRN_ACCESSOR_FUNCALL :
        /* todo */
        break;
      }
      if (a->next) {
        a = a->next;
        if (GRN_BULK_VSIZE(&buf) >= sizeof(grn_id)) {
          id = *((grn_id *)GRN_BULK_HEAD(&buf));
        } else {
          id = GRN_ID_NIL;
        }
      } else {
        break;
      }
    }
  } else {
    switch (obj->header.type) {
    case GRN_COLUMN_FIX_SIZE :
      GRN_VALUE_FIX_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
      break;
    case GRN_COLUMN_VAR_SIZE :
      if ((obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
        grn_obj *range = grn_ctx_at(ctx, DB_OBJ(obj)->range);
        if (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
          GRN_VALUE_VAR_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
        } else {
          GRN_VALUE_FIX_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
        }
      } else {
        GRN_VALUE_VAR_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
      }
      break;
    case GRN_COLUMN_INDEX :
      GRN_UINT32_INIT(&buf, 0);
      break;
    default:
      GRN_TEXT_INIT(&buf, 0);
      break;
    }
    grn_obj_get_value(ctx, obj, id, &buf);
  }
  grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
exit :
  grn_obj_close(ctx, &buf);
}

static void
grn_text_atoj_o(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                grn_obj *obj, grn_obj *id)
{
  grn_id *idp = (grn_id *)GRN_BULK_HEAD(id);
  uint32_t ids = GRN_BULK_VSIZE(id);
  for (;;) {
    if (ids < sizeof(grn_id)) {
      ERR(GRN_INVALID_ARGUMENT, "invalid id.");
      return;
    }
    if (obj->header.type == GRN_ACCESSOR_VIEW) {
      uint32_t n;
      grn_accessor_view *v = (grn_accessor_view *)obj;
      n = *idp;
      if (n >= v->naccessors) {
        ERR(GRN_INVALID_ARGUMENT, "invalid id");
        return;
      }
      if (!(obj = v->accessors[n])) { return ; }
      idp++;
      ids -= sizeof(grn_id);
    } else {
      break;
    }
  }
  grn_text_atoj(ctx, outbuf, output_type, obj, *idp);
}

static inline void
grn_output_bulk(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                grn_obj *bulk, grn_obj_format *format)
{
  grn_obj buf;
  GRN_TEXT_INIT(&buf, 0);
  switch (bulk->header.domain) {
  case GRN_DB_VOID :
    grn_output_bulk_void(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk));
    break;
  case GRN_DB_SHORT_TEXT :
  case GRN_DB_TEXT :
  case GRN_DB_LONG_TEXT :
    grn_output_str(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk));
    break;
  case GRN_DB_BOOL :
    grn_output_bool(ctx, outbuf, output_type,
                    GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0);
    break;
  case GRN_DB_INT8 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_INT8_VALUE(bulk) : 0);
    break;
  case GRN_DB_UINT8 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0);
    break;
  case GRN_DB_INT16 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_INT16_VALUE(bulk) : 0);
    break;
  case GRN_DB_UINT16 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_UINT16_VALUE(bulk) : 0);
    break;
  case GRN_DB_INT32 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_INT32_VALUE(bulk) : 0);
    break;
  case GRN_DB_UINT32 :
    grn_output_int64(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_UINT32_VALUE(bulk) : 0);
    break;
  case GRN_DB_INT64 :
    grn_output_int64(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0);
    break;
  case GRN_DB_UINT64 :
    grn_output_uint64(ctx, outbuf, output_type,
                      GRN_BULK_VSIZE(bulk) ? GRN_UINT64_VALUE(bulk) : 0);
    break;
  case GRN_DB_FLOAT :
    grn_output_float(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_FLOAT_VALUE(bulk) : 0);
    break;
  case GRN_DB_TIME :
    grn_output_time(ctx, outbuf, output_type,
                    GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0);
    break;
  case GRN_DB_TOKYO_GEO_POINT :
  case GRN_DB_WGS84_GEO_POINT :
    grn_output_geo_point(ctx, outbuf, output_type,
                         GRN_BULK_VSIZE(bulk) ? (grn_geo_point *)GRN_BULK_HEAD(bulk) : NULL);
    break;
  default :
    if (format) {
      int j;
      int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
      grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
      if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
        grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", ncolumns);
        for (j = 0; j < ncolumns; j++) {
          grn_id range_id;
          grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
          GRN_BULK_REWIND(&buf);
          grn_column_name_(ctx, columns[j], &buf);
          grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
          /* column range */
          range_id = grn_obj_get_range(ctx, columns[j]);
          if (range_id == GRN_ID_NIL) {
            GRN_TEXT_PUTS(ctx, outbuf, "null");
          } else {
            int name_len;
            grn_obj *range_obj;
            char name_buf[GRN_TABLE_MAX_KEY_SIZE];

            range_obj = grn_ctx_at(ctx, range_id);
            name_len = grn_obj_name(ctx, range_obj, name_buf,
                                    GRN_TABLE_MAX_KEY_SIZE);
            GRN_BULK_REWIND(&buf);
            GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
            grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
          }
          grn_output_array_close(ctx, outbuf, output_type);
        }
        grn_output_array_close(ctx, outbuf, output_type);
      }
      grn_output_array_open(ctx, outbuf, output_type, "HIT", ncolumns);
      for (j = 0; j < ncolumns; j++) {
        grn_text_atoj_o(ctx, outbuf, output_type, columns[j], bulk);
      }
      grn_output_array_close(ctx, outbuf, output_type);
    } else {
      grn_obj *table = grn_ctx_at(ctx, bulk->header.domain);
      grn_id id = *((grn_id *)GRN_BULK_HEAD(bulk));
      if (table && table->header.type != GRN_TABLE_NO_KEY) {
        grn_obj *accessor = grn_obj_column(ctx, table, "_key", 4);
        if (accessor) {
          if (id == GRN_ID_NIL) {
            grn_obj_reinit_for(ctx, &buf, accessor);
          } else {
            grn_obj_get_value(ctx, accessor, id, &buf);
          }
          grn_obj_unlink(ctx, accessor);
        }
        grn_output_obj(ctx, outbuf, output_type, &buf, format);
      } else {
        grn_output_int64(ctx, outbuf, output_type, id);
      }
    }
    break;
  }
  GRN_OBJ_FIN(ctx, &buf);
}

static inline void
grn_output_uvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                   grn_obj *uvector, grn_obj_format *format)
{
  grn_obj buf;
  GRN_TEXT_INIT(&buf, 0);
  if (format) {
    int i, j;
    grn_id *v = (grn_id *)GRN_BULK_HEAD(uvector), *ve = (grn_id *)GRN_BULK_CURR(uvector);
    int ncolumns = GRN_BULK_VSIZE(&format->columns) / sizeof(grn_obj *);
    grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
    grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", -1);
    grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1);
    grn_text_itoa(ctx, outbuf, ve - v);
    grn_output_array_close(ctx, outbuf, output_type);
    if (v < ve) {
      if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
        grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", -1);
        for (j = 0; j < ncolumns; j++) {
          grn_id range_id;
          grn_output_array_open(ctx, outbuf, output_type, "COLUMN", -1);
          GRN_BULK_REWIND(&buf);
          grn_column_name_(ctx, columns[j], &buf);
          grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
          /* column range */
          range_id = grn_obj_get_range(ctx, columns[j]);
          if (range_id == GRN_ID_NIL) {
            GRN_TEXT_PUTS(ctx, outbuf, "null");
          } else {
            int name_len;
            grn_obj *range_obj;
            char name_buf[GRN_TABLE_MAX_KEY_SIZE];

            range_obj = grn_ctx_at(ctx, range_id);
            name_len = grn_obj_name(ctx, range_obj, name_buf,
                                    GRN_TABLE_MAX_KEY_SIZE);
            GRN_BULK_REWIND(&buf);
            GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
            grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
          }
          grn_output_array_close(ctx, outbuf, output_type);
        }
        grn_output_array_close(ctx, outbuf, output_type);
      }
      for (i = 0;; i++) {
        grn_output_array_open(ctx, outbuf, output_type, "HITS", -1);
        for (j = 0; j < ncolumns; j++) {
          GRN_BULK_REWIND(&buf);
          grn_obj_get_value(ctx, columns[j], *v, &buf);
          grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
        }
        grn_output_array_close(ctx, outbuf, output_type);
        v++;
        if (v < ve) {

        } else {
          break;
        }
      }
    }
    grn_output_array_close(ctx, outbuf, output_type);
  } else {
    grn_obj *range = grn_ctx_at(ctx, uvector->header.domain);
    if (range && range->header.type == GRN_TYPE) {
      int value_size = ((struct _grn_type *)range)->obj.range;
      char *v = (char *)GRN_BULK_HEAD(uvector),
        *ve = (char *)GRN_BULK_CURR(uvector);
      grn_output_array_open(ctx, outbuf, output_type, "VECTOR", -1);
      if (v < ve) {
        for (;;) {
          grn_obj value;
          GRN_OBJ_INIT(&value, GRN_BULK, 0, uvector->header.domain);
          grn_bulk_write_from(ctx, &value, v, 0, value_size);
          grn_output_obj(ctx, outbuf, output_type, &value, NULL);

          v += value_size;
          if (v < ve) {

          } else {
            break;
          }
        }
      }
      grn_output_array_close(ctx, outbuf, output_type);
    } else {
      grn_id *v = (grn_id *)GRN_BULK_HEAD(uvector),
        *ve = (grn_id *)GRN_BULK_CURR(uvector);
      grn_output_array_open(ctx, outbuf, output_type, "VECTOR", ve - v);
      if (v < ve) {
        grn_obj key;
        GRN_OBJ_INIT(&key, GRN_BULK, 0, range->header.domain);
        for (;;) {
          if (range->header.type != GRN_TABLE_NO_KEY) {
            grn_table_get_key2(ctx, range, *v, &key);
            grn_output_obj(ctx, outbuf, output_type, &key, NULL);
            GRN_BULK_REWIND(&key);
          } else {
            grn_obj id;
            GRN_UINT32_INIT(&id, 0);
            GRN_UINT32_SET(ctx, &id, *v);
            grn_output_obj(ctx, outbuf, output_type, &id, NULL);
            GRN_OBJ_FIN(ctx, &id);
          }
          v++;
          if (v < ve) {

          } else {
            break;
          }
        }
        GRN_OBJ_FIN(ctx, &key);
      }
      grn_output_array_close(ctx, outbuf, output_type);
    }
  }
  GRN_OBJ_FIN(ctx, &buf);
}

static inline void
grn_output_vector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                  grn_obj *vector, grn_obj_format *format)
{
  if (vector->header.domain == GRN_DB_VOID) {
    ERR(GRN_INVALID_ARGUMENT, "invalid obj->header.domain");
  }
  if (format) {
    ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
        "cannot print GRN_VECTOR using grn_obj_format");
  } else {
    unsigned int i, n;
    grn_obj value;
    GRN_VOID_INIT(&value);
    n = grn_vector_size(ctx, vector);
    grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n);
    for (i = 0; i < n; i++) {
      const char *_value;
      unsigned int weight, length;
      grn_id domain;

      length = grn_vector_get_element(ctx, vector, i,
                                      &_value, &weight, &domain);
      if (domain != GRN_DB_VOID) {
        grn_obj_reinit(ctx, &value, domain, 0);
      } else {
        grn_obj_reinit(ctx, &value, vector->header.domain, 0);
      }
      grn_bulk_write(ctx, &value, _value, length);
      grn_output_obj(ctx, outbuf, output_type, &value, NULL);
    }
    grn_output_array_close(ctx, outbuf, output_type);
    GRN_OBJ_FIN(ctx, &value);
  }
}

static inline void
grn_output_pvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                   grn_obj *pvector, grn_obj_format *format)
{
  if (format) {
    ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
        "cannot print GRN_PVECTOR using grn_obj_format");
  } else {
    unsigned int i, n;
    grn_output_array_open(ctx, outbuf, output_type, "VECTOR", -1);
    n = GRN_BULK_VSIZE(pvector) / sizeof(grn_obj *);
    for (i = 0; i < n; i++) {
      grn_obj *value;

      value = GRN_PTR_VALUE_AT(pvector, i);
      grn_output_obj(ctx, outbuf, output_type, value, NULL);
    }
    grn_output_array_close(ctx, outbuf, output_type);
  }
}

static inline void
grn_output_table_header(grn_ctx *ctx, grn_obj *outbuf,
                        grn_content_type output_type,
                        grn_obj *table, grn_obj_format *format)
{
    grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1);
    if (output_type == GRN_CONTENT_XML) {
      grn_text_itoa(ctx, outbuf, format->nhits);
    } else {
      grn_output_int32(ctx, outbuf, output_type, format->nhits);
    }
    grn_output_array_close(ctx, outbuf, output_type);
}

static inline int
count_n_elements_in_expression(grn_ctx *ctx, grn_obj *expression)
{
  int n_elements = 0;
  grn_bool is_first_comma = GRN_TRUE;
  grn_expr *expr = (grn_expr *)expression;
  grn_expr_code *code;
  grn_expr_code *code_end = expr->codes + expr->codes_curr;

  for (code = expr->codes; code < code_end; code++) {
    if (code->op == GRN_OP_COMMA) {
      n_elements++;
      if (is_first_comma) {
        n_elements++;
        is_first_comma = GRN_FALSE;
      }
    }
  }

  return n_elements;
}

static inline int
count_used_n_codes(grn_ctx *ctx, grn_expr_code *start, grn_expr_code *target)
{
  int n_codes;
  int i, n_args;
  grn_bool have_proc_push_code = GRN_FALSE;
  grn_expr_code *sub_code;

  if (start == target) {
    return 0;
  }

  n_args = target->nargs;
  if (target->op == GRN_OP_CALL) {
    if (!target->value) {
      have_proc_push_code = GRN_TRUE;
    }
  } else {
    if (target->value) {
      n_args--;
      if (n_args == 0) {
        return 1;
      }
    }
  }

  n_codes = 1;
  sub_code = target - 1;
  for (i = 0; i < n_args; i++) {
    int sub_n_codes;
    sub_n_codes = count_used_n_codes(ctx, start, sub_code);
    n_codes += sub_n_codes;
    sub_code -= sub_n_codes;
    if (sub_code < start) {
      /* TODO: report error */
      return 0;
    }
  }

  if (have_proc_push_code) {
    n_codes++;
    sub_code--;
    if (sub_code < start) {
      /* TODO: report error */
      return 0;
    }
  }

  return n_codes;
}

static inline void
grn_output_table_column(grn_ctx *ctx, grn_obj *outbuf,
                        grn_content_type output_type,
                        grn_obj *column, grn_obj *buf)
{
  grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
  if (column) {
    grn_id range_id;
    GRN_BULK_REWIND(buf);
    grn_column_name_(ctx, column, buf);
    grn_output_obj(ctx, outbuf, output_type, buf, NULL);
    range_id = grn_obj_get_range(ctx, column);
    if (range_id == GRN_ID_NIL) {
      grn_output_cstr(ctx, outbuf, output_type, "null");
    } else {
      int name_len;
      grn_obj *range_obj;
      char name_buf[GRN_TABLE_MAX_KEY_SIZE];

      range_obj = grn_ctx_at(ctx, range_id);
      name_len = grn_obj_name(ctx, range_obj, name_buf,
                              GRN_TABLE_MAX_KEY_SIZE);
      GRN_BULK_REWIND(buf);
      GRN_TEXT_PUT(ctx, buf, name_buf, name_len);
      grn_output_obj(ctx, outbuf, output_type, buf, NULL);
    }
  } else {
    grn_output_cstr(ctx, outbuf, output_type, "");
    grn_output_cstr(ctx, outbuf, output_type, "");
  }
  grn_output_array_close(ctx, outbuf, output_type);
}

static inline void
grn_output_table_columns_by_expression(grn_ctx *ctx, grn_obj *outbuf,
                                       grn_content_type output_type,
                                       grn_obj *table, grn_obj_format *format,
                                       grn_obj *buf)
{
  int n_elements;
  int previous_comma_offset = -1;
  grn_bool is_first_comma = GRN_TRUE;
  grn_bool have_comma = GRN_FALSE;
  grn_expr *expr = (grn_expr *)format->expression;
  grn_expr_code *code;
  grn_expr_code *code_end = expr->codes + expr->codes_curr;

  n_elements = count_n_elements_in_expression(ctx, format->expression);

  grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", n_elements);
  for (code = expr->codes; code < code_end; code++) {
    int code_start_offset;

    if (code->op != GRN_OP_COMMA) {
      continue;
    }

    have_comma = GRN_TRUE;
    code_start_offset = previous_comma_offset + 1;
    if (is_first_comma) {
      int code_end_offset;
      int n_used_code;

      grn_output_table_column(ctx, outbuf, output_type,
                              expr->codes[0].value, buf);

      code_end_offset = code - expr->codes - code_start_offset - 1;
      n_used_code = count_used_n_codes(ctx,
                                       expr->codes,
                                       expr->codes + code_end_offset);
      code_start_offset = code_end_offset - n_used_code + 1;
      is_first_comma = GRN_FALSE;
    }

    grn_output_table_column(ctx, outbuf, output_type,
                            expr->codes[code_start_offset].value, buf);
    previous_comma_offset = code - expr->codes;
  }

  if (!have_comma && expr->codes_curr > 0) {
    grn_output_table_column(ctx, outbuf, output_type,
                            expr->codes[0].value, buf);
  }

  grn_output_array_close(ctx, outbuf, output_type);
}

static inline void
grn_output_table_columns_by_columns(grn_ctx *ctx, grn_obj *outbuf,
                                    grn_content_type output_type,
                                    grn_obj *table, grn_obj_format *format,
                                    grn_obj *buf)
{
  int i;
  int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
  grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);

  grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", ncolumns);
  for (i = 0; i < ncolumns; i++) {
    grn_output_table_column(ctx, outbuf, output_type, columns[i], buf);
  }
  grn_output_array_close(ctx, outbuf, output_type);
}

static inline void
grn_output_table_columns(grn_ctx *ctx, grn_obj *outbuf,
                         grn_content_type output_type,
                         grn_obj *table, grn_obj_format *format)
{
  grn_obj buf;

  GRN_TEXT_INIT(&buf, 0);
  if (format->expression) {
    grn_output_table_columns_by_expression(ctx, outbuf, output_type,
                                           table, format, &buf);
  } else {
    grn_output_table_columns_by_columns(ctx, outbuf, output_type,
                                        table, format, &buf);
  }
  GRN_OBJ_FIN(ctx, &buf);
}

static inline void
grn_output_table_record_by_expression(grn_ctx *ctx, grn_obj *outbuf,
                                      grn_content_type output_type,
                                      grn_obj *expression)
{
  grn_obj *result;
  result = grn_expr_exec(ctx, expression, 0);
  if (result) {
    grn_output_obj(ctx, outbuf, output_type, result, NULL);
  } else {
    grn_output_cstr(ctx, outbuf, output_type, ctx->errbuf);
  }
}

static inline void
grn_output_table_records_by_expression(grn_ctx *ctx, grn_obj *outbuf,
                                       grn_content_type output_type,
                                       grn_table_cursor *tc,
                                       grn_obj_format *format)
{
  int n_elements = 0;
  grn_obj *record;
  grn_expr *expr = (grn_expr *)format->expression;
  grn_expr_code *code;
  grn_expr_code *code_end = expr->codes + expr->codes_curr;

  n_elements = count_n_elements_in_expression(ctx, format->expression);
  record = grn_expr_get_var_by_offset(ctx, format->expression, 0);
  while (!grn_table_cursor_next_o(ctx, tc, record)) {
    int previous_comma_offset = -1;
    grn_bool is_first_comma = GRN_TRUE;
    grn_bool have_comma = GRN_FALSE;
    grn_output_array_open(ctx, outbuf, output_type, "HIT", n_elements);
    for (code = expr->codes; code < code_end; code++) {
      if (code->op == GRN_OP_COMMA) {
        int code_start_offset = previous_comma_offset + 1;
        int code_end_offset = code - expr->codes - code_start_offset;
        int original_codes_curr = expr->codes_curr;

        have_comma = GRN_TRUE;
        if (is_first_comma) {
          expr->codes_curr = count_used_n_codes(ctx,
                                                expr->codes,
                                                expr->codes + expr->codes_curr);
          grn_output_table_record_by_expression(ctx, outbuf, output_type,
                                                format->expression);
          code_start_offset = expr->codes_curr;
          code_end_offset -= expr->codes_curr;
          is_first_comma = GRN_FALSE;
        }
        expr->codes += code_start_offset;
        expr->codes_curr = code_end_offset;
        grn_output_table_record_by_expression(ctx, outbuf, output_type,
                                              format->expression);
        expr->codes -= code_start_offset;
        expr->codes_curr = original_codes_curr;
        previous_comma_offset = code - expr->codes;
      }
    }

    if (!have_comma && expr->codes_curr > 0) {
      grn_output_table_record_by_expression(ctx, outbuf, output_type,
                                            format->expression);
    }

    grn_output_array_close(ctx, outbuf, output_type);
    GRN_BULK_REWIND(record);
  }
}

static inline void
grn_output_table_records_by_columns(grn_ctx *ctx, grn_obj *outbuf,
                                    grn_content_type output_type,
                                    grn_table_cursor *tc,
                                    grn_obj_format *format)
{
  int i;
  grn_obj id;
  int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
  grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
  GRN_TEXT_INIT(&id, 0);
  while (!grn_table_cursor_next_o(ctx, tc, &id)) {
    grn_output_array_open(ctx, outbuf, output_type, "HIT", ncolumns);
    for (i = 0; i < ncolumns; i++) {
      grn_text_atoj_o(ctx, outbuf, output_type, columns[i], &id);
    }
    grn_output_array_close(ctx, outbuf, output_type);
  }
  GRN_OBJ_FIN(ctx, &id);
}

static inline void
grn_output_table_records(grn_ctx *ctx, grn_obj *outbuf,
                         grn_content_type output_type,
                         grn_obj *table, grn_obj_format *format)
{
  grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
                                               format->offset, format->limit,
                                               GRN_CURSOR_ASCENDING);
  if (tc) {
    if (format->expression) {
      grn_output_table_records_by_expression(ctx, outbuf, output_type,
                                             tc, format);
    } else {
      grn_output_table_records_by_columns(ctx, outbuf, output_type,
                                          tc, format);
    }
    grn_table_cursor_close(ctx, tc);
  } else {
    ERRCLR(ctx);
  }
}

static inline void
grn_output_table(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                 grn_obj *table, grn_obj_format *format)
{
  grn_obj buf;
  GRN_TEXT_INIT(&buf, 0);
  if (format) {
    int resultset_size = 1;
    /* resultset: [NHITS, (COLUMNS), (HITS)] */
    if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
      resultset_size++;
    }
    resultset_size += format->limit;
    grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", resultset_size);
    grn_output_table_header(ctx, outbuf, output_type, table, format);
    if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
      grn_output_table_columns(ctx, outbuf, output_type, table, format);
    }
    grn_output_table_records(ctx, outbuf, output_type, table, format);
    grn_output_array_close(ctx, outbuf, output_type);
  } else {
    int i;
    grn_obj *column = grn_obj_column(ctx, table, "_key", 4);
    grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
                                                 0, -1, GRN_CURSOR_ASCENDING);
    grn_output_array_open(ctx, outbuf, output_type, "HIT", -1);
    if (tc) {
      grn_obj id;
      GRN_TEXT_INIT(&id, 0);
      for (i = 0; !grn_table_cursor_next_o(ctx, tc, &id); i++) {
        /* todo:
           grn_text_atoj_o(ctx, outbuf, output_type, column, &id);
        */
        GRN_BULK_REWIND(&buf);
        grn_obj_get_value_o(ctx, column, &id, &buf);
        grn_text_esc(ctx, outbuf, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf));
      }
      GRN_OBJ_FIN(ctx, &id);
      grn_table_cursor_close(ctx, tc);
    }
    grn_output_array_close(ctx, outbuf, output_type);
    grn_obj_unlink(ctx, column);
  }
  GRN_OBJ_FIN(ctx, &buf);
}

void
grn_output_obj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
               grn_obj *obj, grn_obj_format *format)
{
  grn_obj buf;
  GRN_TEXT_INIT(&buf, 0);
  switch (obj->header.type) {
  case GRN_BULK :
    grn_output_bulk(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_UVECTOR :
    grn_output_uvector(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_VECTOR :
    grn_output_vector(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_PVECTOR :
    grn_output_pvector(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_TABLE_HASH_KEY :
  case GRN_TABLE_PAT_KEY :
  case GRN_TABLE_NO_KEY :
  case GRN_TABLE_VIEW :
    grn_output_table(ctx, outbuf, output_type, obj, format);
    break;
  }
  GRN_OBJ_FIN(ctx, &buf);
}

typedef enum {
  XML_START,
  XML_START_ELEMENT,
  XML_END_ELEMENT,<commit_msg>Support GRN_VOID output<commit_after>/* -*- c-basic-offset: 2 -*- */
/* Copyright(C) 2009-2012 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-1301  USA
*/

#ifndef GROONGA_IN_H
#include "groonga_in.h"
#endif /* GROONGA_IN_H */

#include <string.h>
#include "str.h"
#include "db.h"
#include "util.h"
#include "output.h"

#define LEVELS (&ctx->impl->levels)
#define DEPTH (GRN_BULK_VSIZE(LEVELS)>>2)
#define CURR_LEVEL (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1))) : 0)
#define INCR_DEPTH(i) GRN_UINT32_PUT(ctx, LEVELS, i)
#define DECR_DEPTH (DEPTH ? grn_bulk_truncate(ctx, LEVELS, GRN_BULK_VSIZE(LEVELS) - sizeof(uint32_t)) : 0)
#define INCR_LENGTH (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1)) += 2) : 0)

static void
put_delimiter(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
{
  uint32_t level = CURR_LEVEL;
  switch (output_type) {
  case GRN_CONTENT_JSON:
    if (level < 2) { return; }
    GRN_TEXT_PUTC(ctx, outbuf, ((level & 3) == 3) ? ':' : ',');
    // if (DEPTH == 1 && ((level & 3) != 3)) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); }
    break;
  case GRN_CONTENT_XML:
    if (!DEPTH) { return; }
    GRN_TEXT_PUTC(ctx, outbuf, '\n');
    break;
  case GRN_CONTENT_TSV:
    if (level < 2) { return; }
    if (DEPTH <= 2) {
      GRN_TEXT_PUTC(ctx, outbuf, ((level & 3) == 3) ? '\t' : '\n');
    } else {
      GRN_TEXT_PUTC(ctx, outbuf, '\t');
    }
  case GRN_CONTENT_MSGPACK :
    // do nothing
    break;
  case GRN_CONTENT_NONE:
    break;
  }
}

void
grn_output_array_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                      const char *name, int nelements)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTC(ctx, outbuf, '[');
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTC(ctx, outbuf, '<');
    GRN_TEXT_PUTS(ctx, outbuf, name);
    GRN_TEXT_PUTC(ctx, outbuf, '>');
    grn_vector_add_element(ctx, &ctx->impl->names, name, strlen(name), 0, GRN_DB_SHORT_TEXT);
    break;
  case GRN_CONTENT_TSV:
    if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "[\t"); }
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    if (nelements < 0) {
      GRN_LOG(ctx, GRN_LOG_DEBUG,
              "grn_output_array_open nelements (%d) for <%s>",
              nelements,
              name);
    }
    msgpack_pack_array(&ctx->impl->msgpacker, nelements);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_DEPTH(0);
}

void
grn_output_array_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
{
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTC(ctx, outbuf, ']');
    break;
  case GRN_CONTENT_TSV:
    if (DEPTH > 3) {
      if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); }
      GRN_TEXT_PUTC(ctx, outbuf, ']');
    }
    break;
  case GRN_CONTENT_XML:
    {
      const char *name;
      unsigned int name_len = grn_vector_pop_element(ctx, &ctx->impl->names, &name, NULL, NULL);
      GRN_TEXT_PUTS(ctx, outbuf, "</");
      GRN_TEXT_PUT(ctx, outbuf, name, name_len);
      GRN_TEXT_PUTC(ctx, outbuf, '>');
    }
    break;
  case GRN_CONTENT_MSGPACK :
    // do nothing
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  DECR_DEPTH;
  INCR_LENGTH;
}

void
grn_output_map_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                    const char *name, int nelements)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTS(ctx, outbuf, "{");
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTC(ctx, outbuf, '<');
    GRN_TEXT_PUTS(ctx, outbuf, name);
    GRN_TEXT_PUTC(ctx, outbuf, '>');
    grn_vector_add_element(ctx, &ctx->impl->names, name, strlen(name), 0, GRN_DB_SHORT_TEXT);
    break;
  case GRN_CONTENT_TSV:
    if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "{\t"); }
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    if (nelements < 0) {
      GRN_LOG(ctx, GRN_LOG_DEBUG,
              "grn_output_map_open nelements (%d) for <%s>",
              nelements,
              name);
    }
    msgpack_pack_map(&ctx->impl->msgpacker, nelements / 2);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_DEPTH(1);
}

void
grn_output_map_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
{
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTS(ctx, outbuf, "}");
    break;
  case GRN_CONTENT_TSV:
    if (DEPTH > 3) {
      if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); }
      GRN_TEXT_PUTC(ctx, outbuf, '}');
    }
    break;
  case GRN_CONTENT_XML:
    {
      const char *name;
      unsigned int name_len = grn_vector_pop_element(ctx, &ctx->impl->names, &name, NULL, NULL);
      GRN_TEXT_PUTS(ctx, outbuf, "</");
      GRN_TEXT_PUT(ctx, outbuf, name, name_len);
      GRN_TEXT_PUTC(ctx, outbuf, '>');
    }
    break;
  case GRN_CONTENT_MSGPACK :
    // do nothing
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  DECR_DEPTH;
  INCR_LENGTH;
}

void
grn_output_int32(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_itoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_TSV:
    grn_text_itoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
    grn_text_itoa(ctx, outbuf, value);
    GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_int32(&ctx->impl->msgpacker, value);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_int64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_lltoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_TSV:
    grn_text_lltoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
    grn_text_lltoa(ctx, outbuf, value);
    GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_int64(&ctx->impl->msgpacker, value);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_uint64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_ulltoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_TSV:
    grn_text_ulltoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
    grn_text_ulltoa(ctx, outbuf, value);
    GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_uint64(&ctx->impl->msgpacker, value);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_float(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, double value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_ftoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_TSV:
    grn_text_ftoa(ctx, outbuf, value);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<FLOAT>");
    grn_text_ftoa(ctx, outbuf, value);
    GRN_TEXT_PUTS(ctx, outbuf, "</FLOAT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_double(&ctx->impl->msgpacker, value);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_str(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
               const char *value, size_t value_len)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_esc(ctx, outbuf, value, value_len);
    break;
  case GRN_CONTENT_TSV:
    grn_text_esc(ctx, outbuf, value, value_len);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<TEXT>");
    grn_text_escape_xml(ctx, outbuf, value, value_len);
    GRN_TEXT_PUTS(ctx, outbuf, "</TEXT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_raw(&ctx->impl->msgpacker, value_len);
    msgpack_pack_raw_body(&ctx->impl->msgpacker, value, value_len);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_cstr(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                const char *value)
{
  grn_output_str(ctx, outbuf, output_type, value, strlen(value));
}

void
grn_output_bool(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_bool value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
    break;
  case GRN_CONTENT_TSV:
    GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<BOOL>");
    GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
    GRN_TEXT_PUTS(ctx, outbuf, "</BOOL>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    if (value) {
      msgpack_pack_true(&ctx->impl->msgpacker);
    } else {
      msgpack_pack_false(&ctx->impl->msgpacker);
    }
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

static inline void
grn_output_null(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    GRN_TEXT_PUTS(ctx, outbuf, "null");
    break;
  case GRN_CONTENT_TSV:
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<NULL/>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_nil(&ctx->impl->msgpacker);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

static inline void
grn_output_bulk_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                     const char *value, size_t value_len)
{
  if (value_len == sizeof(grn_id) && *(grn_id *)value == GRN_ID_NIL) {
    grn_output_null(ctx, outbuf, output_type);
  } else {
    grn_output_str(ctx, outbuf, output_type, value, value_len);
  }
}

void
grn_output_time(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
{
  double dv = value;
  dv /= 1000000.0;
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    grn_text_ftoa(ctx, outbuf, dv);
    break;
  case GRN_CONTENT_TSV:
    grn_text_ftoa(ctx, outbuf, dv);
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<DATE>");
    grn_text_ftoa(ctx, outbuf, dv);
    GRN_TEXT_PUTS(ctx, outbuf, "</DATE>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    msgpack_pack_double(&ctx->impl->msgpacker, dv);
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

void
grn_output_geo_point(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                     grn_geo_point *value)
{
  put_delimiter(ctx, outbuf, output_type);
  switch (output_type) {
  case GRN_CONTENT_JSON:
    if (value) {
      GRN_TEXT_PUTC(ctx, outbuf, '"');
      grn_text_itoa(ctx, outbuf, value->latitude);
      GRN_TEXT_PUTC(ctx, outbuf, 'x');
      grn_text_itoa(ctx, outbuf, value->longitude);
      GRN_TEXT_PUTC(ctx, outbuf, '"');
    } else {
      GRN_TEXT_PUTS(ctx, outbuf, "null");
    }
    break;
  case GRN_CONTENT_TSV:
    if (value) {
      GRN_TEXT_PUTC(ctx, outbuf, '"');
      grn_text_itoa(ctx, outbuf, value->latitude);
      GRN_TEXT_PUTC(ctx, outbuf, 'x');
      grn_text_itoa(ctx, outbuf, value->longitude);
      GRN_TEXT_PUTC(ctx, outbuf, '"');
    } else {
      GRN_TEXT_PUTS(ctx, outbuf, "\"\"");
    }
    break;
  case GRN_CONTENT_XML:
    GRN_TEXT_PUTS(ctx, outbuf, "<GEO_POINT>");
    if (value) {
      grn_text_itoa(ctx, outbuf, value->latitude);
      GRN_TEXT_PUTC(ctx, outbuf, 'x');
      grn_text_itoa(ctx, outbuf, value->longitude);
    }
    GRN_TEXT_PUTS(ctx, outbuf, "</GEO_POINT>");
    break;
  case GRN_CONTENT_MSGPACK :
#ifdef WITH_MESSAGE_PACK
    if (value) {
      grn_obj buf;
      GRN_TEXT_INIT(&buf, 0);
      grn_text_itoa(ctx, &buf, value->latitude);
      GRN_TEXT_PUTC(ctx, &buf, 'x');
      grn_text_itoa(ctx, &buf, value->longitude);
      msgpack_pack_raw(&ctx->impl->msgpacker, GRN_TEXT_LEN(&buf));
      msgpack_pack_raw_body(&ctx->impl->msgpacker,
                            GRN_TEXT_VALUE(&buf),
                            GRN_TEXT_LEN(&buf));
      grn_obj_close(ctx, &buf);
    } else {
      msgpack_pack_nil(&ctx->impl->msgpacker);
    }
#endif
    break;
  case GRN_CONTENT_NONE:
    break;
  }
  INCR_LENGTH;
}

static void
grn_text_atoj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
              grn_obj *obj, grn_id id)
{
  int vs;
  grn_obj buf;
  if (obj->header.type == GRN_ACCESSOR) {
    grn_accessor *a = (grn_accessor *)obj;
    GRN_TEXT_INIT(&buf, 0);
    for (;;) {
      buf.header.domain = grn_obj_get_range(ctx, obj);
      GRN_BULK_REWIND(&buf);
      switch (a->action) {
      case GRN_ACCESSOR_GET_ID :
        GRN_UINT32_PUT(ctx, &buf, id);
        buf.header.domain = GRN_DB_UINT32;
        break;
      case GRN_ACCESSOR_GET_KEY :
        grn_table_get_key2(ctx, a->obj, id, &buf);
        buf.header.domain = DB_OBJ(a->obj)->header.domain;
        break;
      case GRN_ACCESSOR_GET_VALUE :
        grn_obj_get_value(ctx, a->obj, id, &buf);
        buf.header.domain = GRN_DB_INT32; /* fix me */
        break;
      case GRN_ACCESSOR_GET_SCORE :
        grn_obj_get_value(ctx, a->obj, id, &buf);
        {
          grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
          GRN_INT32_PUT(ctx, &buf, ri->score);
        }
        buf.header.domain = GRN_DB_INT32;
        break;
      case GRN_ACCESSOR_GET_NSUBRECS :
        {
          grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
          GRN_INT32_PUT(ctx, &buf, ri->n_subrecs);
        }
        buf.header.domain = GRN_DB_INT32;
        break;
      case GRN_ACCESSOR_GET_COLUMN_VALUE :
        if ((a->obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
          if (a->next) {
            grn_id *idp;
            grn_obj_get_value(ctx, a->obj, id, &buf);
            idp = (grn_id *)GRN_BULK_HEAD(&buf);
            vs = GRN_BULK_VSIZE(&buf) / sizeof(grn_id);
            grn_output_array_open(ctx, outbuf, output_type, "COLUMN", vs);
            for (; vs--; idp++) {
              grn_text_atoj(ctx, outbuf, output_type, (grn_obj *)a->next, *idp);
            }
            grn_output_array_close(ctx, outbuf, output_type);
          } else {
            grn_text_atoj(ctx, outbuf, output_type, a->obj, id);
          }
          goto exit;
        } else {
          grn_obj_get_value(ctx, a->obj, id, &buf);
        }
        break;
      case GRN_ACCESSOR_GET_DB_OBJ :
        /* todo */
        break;
      case GRN_ACCESSOR_LOOKUP :
        /* todo */
        break;
      case GRN_ACCESSOR_FUNCALL :
        /* todo */
        break;
      }
      if (a->next) {
        a = a->next;
        if (GRN_BULK_VSIZE(&buf) >= sizeof(grn_id)) {
          id = *((grn_id *)GRN_BULK_HEAD(&buf));
        } else {
          id = GRN_ID_NIL;
        }
      } else {
        break;
      }
    }
  } else {
    switch (obj->header.type) {
    case GRN_COLUMN_FIX_SIZE :
      GRN_VALUE_FIX_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
      break;
    case GRN_COLUMN_VAR_SIZE :
      if ((obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
        grn_obj *range = grn_ctx_at(ctx, DB_OBJ(obj)->range);
        if (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
          GRN_VALUE_VAR_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
        } else {
          GRN_VALUE_FIX_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
        }
      } else {
        GRN_VALUE_VAR_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
      }
      break;
    case GRN_COLUMN_INDEX :
      GRN_UINT32_INIT(&buf, 0);
      break;
    default:
      GRN_TEXT_INIT(&buf, 0);
      break;
    }
    grn_obj_get_value(ctx, obj, id, &buf);
  }
  grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
exit :
  grn_obj_close(ctx, &buf);
}

static void
grn_text_atoj_o(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                grn_obj *obj, grn_obj *id)
{
  grn_id *idp = (grn_id *)GRN_BULK_HEAD(id);
  uint32_t ids = GRN_BULK_VSIZE(id);
  for (;;) {
    if (ids < sizeof(grn_id)) {
      ERR(GRN_INVALID_ARGUMENT, "invalid id.");
      return;
    }
    if (obj->header.type == GRN_ACCESSOR_VIEW) {
      uint32_t n;
      grn_accessor_view *v = (grn_accessor_view *)obj;
      n = *idp;
      if (n >= v->naccessors) {
        ERR(GRN_INVALID_ARGUMENT, "invalid id");
        return;
      }
      if (!(obj = v->accessors[n])) { return ; }
      idp++;
      ids -= sizeof(grn_id);
    } else {
      break;
    }
  }
  grn_text_atoj(ctx, outbuf, output_type, obj, *idp);
}

static inline void
grn_output_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                grn_obj *bulk, grn_obj_format *format)
{
  grn_output_null(ctx, outbuf, output_type);
}

static inline void
grn_output_bulk(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                grn_obj *bulk, grn_obj_format *format)
{
  grn_obj buf;
  GRN_TEXT_INIT(&buf, 0);
  switch (bulk->header.domain) {
  case GRN_DB_VOID :
    grn_output_bulk_void(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk));
    break;
  case GRN_DB_SHORT_TEXT :
  case GRN_DB_TEXT :
  case GRN_DB_LONG_TEXT :
    grn_output_str(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk));
    break;
  case GRN_DB_BOOL :
    grn_output_bool(ctx, outbuf, output_type,
                    GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0);
    break;
  case GRN_DB_INT8 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_INT8_VALUE(bulk) : 0);
    break;
  case GRN_DB_UINT8 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0);
    break;
  case GRN_DB_INT16 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_INT16_VALUE(bulk) : 0);
    break;
  case GRN_DB_UINT16 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_UINT16_VALUE(bulk) : 0);
    break;
  case GRN_DB_INT32 :
    grn_output_int32(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_INT32_VALUE(bulk) : 0);
    break;
  case GRN_DB_UINT32 :
    grn_output_int64(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_UINT32_VALUE(bulk) : 0);
    break;
  case GRN_DB_INT64 :
    grn_output_int64(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0);
    break;
  case GRN_DB_UINT64 :
    grn_output_uint64(ctx, outbuf, output_type,
                      GRN_BULK_VSIZE(bulk) ? GRN_UINT64_VALUE(bulk) : 0);
    break;
  case GRN_DB_FLOAT :
    grn_output_float(ctx, outbuf, output_type,
                     GRN_BULK_VSIZE(bulk) ? GRN_FLOAT_VALUE(bulk) : 0);
    break;
  case GRN_DB_TIME :
    grn_output_time(ctx, outbuf, output_type,
                    GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0);
    break;
  case GRN_DB_TOKYO_GEO_POINT :
  case GRN_DB_WGS84_GEO_POINT :
    grn_output_geo_point(ctx, outbuf, output_type,
                         GRN_BULK_VSIZE(bulk) ? (grn_geo_point *)GRN_BULK_HEAD(bulk) : NULL);
    break;
  default :
    if (format) {
      int j;
      int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
      grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
      if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
        grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", ncolumns);
        for (j = 0; j < ncolumns; j++) {
          grn_id range_id;
          grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
          GRN_BULK_REWIND(&buf);
          grn_column_name_(ctx, columns[j], &buf);
          grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
          /* column range */
          range_id = grn_obj_get_range(ctx, columns[j]);
          if (range_id == GRN_ID_NIL) {
            GRN_TEXT_PUTS(ctx, outbuf, "null");
          } else {
            int name_len;
            grn_obj *range_obj;
            char name_buf[GRN_TABLE_MAX_KEY_SIZE];

            range_obj = grn_ctx_at(ctx, range_id);
            name_len = grn_obj_name(ctx, range_obj, name_buf,
                                    GRN_TABLE_MAX_KEY_SIZE);
            GRN_BULK_REWIND(&buf);
            GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
            grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
          }
          grn_output_array_close(ctx, outbuf, output_type);
        }
        grn_output_array_close(ctx, outbuf, output_type);
      }
      grn_output_array_open(ctx, outbuf, output_type, "HIT", ncolumns);
      for (j = 0; j < ncolumns; j++) {
        grn_text_atoj_o(ctx, outbuf, output_type, columns[j], bulk);
      }
      grn_output_array_close(ctx, outbuf, output_type);
    } else {
      grn_obj *table = grn_ctx_at(ctx, bulk->header.domain);
      grn_id id = *((grn_id *)GRN_BULK_HEAD(bulk));
      if (table && table->header.type != GRN_TABLE_NO_KEY) {
        grn_obj *accessor = grn_obj_column(ctx, table, "_key", 4);
        if (accessor) {
          if (id == GRN_ID_NIL) {
            grn_obj_reinit_for(ctx, &buf, accessor);
          } else {
            grn_obj_get_value(ctx, accessor, id, &buf);
          }
          grn_obj_unlink(ctx, accessor);
        }
        grn_output_obj(ctx, outbuf, output_type, &buf, format);
      } else {
        grn_output_int64(ctx, outbuf, output_type, id);
      }
    }
    break;
  }
  GRN_OBJ_FIN(ctx, &buf);
}

static inline void
grn_output_uvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                   grn_obj *uvector, grn_obj_format *format)
{
  grn_obj buf;
  GRN_TEXT_INIT(&buf, 0);
  if (format) {
    int i, j;
    grn_id *v = (grn_id *)GRN_BULK_HEAD(uvector), *ve = (grn_id *)GRN_BULK_CURR(uvector);
    int ncolumns = GRN_BULK_VSIZE(&format->columns) / sizeof(grn_obj *);
    grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
    grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", -1);
    grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1);
    grn_text_itoa(ctx, outbuf, ve - v);
    grn_output_array_close(ctx, outbuf, output_type);
    if (v < ve) {
      if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
        grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", -1);
        for (j = 0; j < ncolumns; j++) {
          grn_id range_id;
          grn_output_array_open(ctx, outbuf, output_type, "COLUMN", -1);
          GRN_BULK_REWIND(&buf);
          grn_column_name_(ctx, columns[j], &buf);
          grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
          /* column range */
          range_id = grn_obj_get_range(ctx, columns[j]);
          if (range_id == GRN_ID_NIL) {
            GRN_TEXT_PUTS(ctx, outbuf, "null");
          } else {
            int name_len;
            grn_obj *range_obj;
            char name_buf[GRN_TABLE_MAX_KEY_SIZE];

            range_obj = grn_ctx_at(ctx, range_id);
            name_len = grn_obj_name(ctx, range_obj, name_buf,
                                    GRN_TABLE_MAX_KEY_SIZE);
            GRN_BULK_REWIND(&buf);
            GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
            grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
          }
          grn_output_array_close(ctx, outbuf, output_type);
        }
        grn_output_array_close(ctx, outbuf, output_type);
      }
      for (i = 0;; i++) {
        grn_output_array_open(ctx, outbuf, output_type, "HITS", -1);
        for (j = 0; j < ncolumns; j++) {
          GRN_BULK_REWIND(&buf);
          grn_obj_get_value(ctx, columns[j], *v, &buf);
          grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
        }
        grn_output_array_close(ctx, outbuf, output_type);
        v++;
        if (v < ve) {

        } else {
          break;
        }
      }
    }
    grn_output_array_close(ctx, outbuf, output_type);
  } else {
    grn_obj *range = grn_ctx_at(ctx, uvector->header.domain);
    if (range && range->header.type == GRN_TYPE) {
      int value_size = ((struct _grn_type *)range)->obj.range;
      char *v = (char *)GRN_BULK_HEAD(uvector),
        *ve = (char *)GRN_BULK_CURR(uvector);
      grn_output_array_open(ctx, outbuf, output_type, "VECTOR", -1);
      if (v < ve) {
        for (;;) {
          grn_obj value;
          GRN_OBJ_INIT(&value, GRN_BULK, 0, uvector->header.domain);
          grn_bulk_write_from(ctx, &value, v, 0, value_size);
          grn_output_obj(ctx, outbuf, output_type, &value, NULL);

          v += value_size;
          if (v < ve) {

          } else {
            break;
          }
        }
      }
      grn_output_array_close(ctx, outbuf, output_type);
    } else {
      grn_id *v = (grn_id *)GRN_BULK_HEAD(uvector),
        *ve = (grn_id *)GRN_BULK_CURR(uvector);
      grn_output_array_open(ctx, outbuf, output_type, "VECTOR", ve - v);
      if (v < ve) {
        grn_obj key;
        GRN_OBJ_INIT(&key, GRN_BULK, 0, range->header.domain);
        for (;;) {
          if (range->header.type != GRN_TABLE_NO_KEY) {
            grn_table_get_key2(ctx, range, *v, &key);
            grn_output_obj(ctx, outbuf, output_type, &key, NULL);
            GRN_BULK_REWIND(&key);
          } else {
            grn_obj id;
            GRN_UINT32_INIT(&id, 0);
            GRN_UINT32_SET(ctx, &id, *v);
            grn_output_obj(ctx, outbuf, output_type, &id, NULL);
            GRN_OBJ_FIN(ctx, &id);
          }
          v++;
          if (v < ve) {

          } else {
            break;
          }
        }
        GRN_OBJ_FIN(ctx, &key);
      }
      grn_output_array_close(ctx, outbuf, output_type);
    }
  }
  GRN_OBJ_FIN(ctx, &buf);
}

static inline void
grn_output_vector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                  grn_obj *vector, grn_obj_format *format)
{
  if (vector->header.domain == GRN_DB_VOID) {
    ERR(GRN_INVALID_ARGUMENT, "invalid obj->header.domain");
  }
  if (format) {
    ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
        "cannot print GRN_VECTOR using grn_obj_format");
  } else {
    unsigned int i, n;
    grn_obj value;
    GRN_VOID_INIT(&value);
    n = grn_vector_size(ctx, vector);
    grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n);
    for (i = 0; i < n; i++) {
      const char *_value;
      unsigned int weight, length;
      grn_id domain;

      length = grn_vector_get_element(ctx, vector, i,
                                      &_value, &weight, &domain);
      if (domain != GRN_DB_VOID) {
        grn_obj_reinit(ctx, &value, domain, 0);
      } else {
        grn_obj_reinit(ctx, &value, vector->header.domain, 0);
      }
      grn_bulk_write(ctx, &value, _value, length);
      grn_output_obj(ctx, outbuf, output_type, &value, NULL);
    }
    grn_output_array_close(ctx, outbuf, output_type);
    GRN_OBJ_FIN(ctx, &value);
  }
}

static inline void
grn_output_pvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                   grn_obj *pvector, grn_obj_format *format)
{
  if (format) {
    ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
        "cannot print GRN_PVECTOR using grn_obj_format");
  } else {
    unsigned int i, n;
    grn_output_array_open(ctx, outbuf, output_type, "VECTOR", -1);
    n = GRN_BULK_VSIZE(pvector) / sizeof(grn_obj *);
    for (i = 0; i < n; i++) {
      grn_obj *value;

      value = GRN_PTR_VALUE_AT(pvector, i);
      grn_output_obj(ctx, outbuf, output_type, value, NULL);
    }
    grn_output_array_close(ctx, outbuf, output_type);
  }
}

static inline void
grn_output_table_header(grn_ctx *ctx, grn_obj *outbuf,
                        grn_content_type output_type,
                        grn_obj *table, grn_obj_format *format)
{
    grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1);
    if (output_type == GRN_CONTENT_XML) {
      grn_text_itoa(ctx, outbuf, format->nhits);
    } else {
      grn_output_int32(ctx, outbuf, output_type, format->nhits);
    }
    grn_output_array_close(ctx, outbuf, output_type);
}

static inline int
count_n_elements_in_expression(grn_ctx *ctx, grn_obj *expression)
{
  int n_elements = 0;
  grn_bool is_first_comma = GRN_TRUE;
  grn_expr *expr = (grn_expr *)expression;
  grn_expr_code *code;
  grn_expr_code *code_end = expr->codes + expr->codes_curr;

  for (code = expr->codes; code < code_end; code++) {
    if (code->op == GRN_OP_COMMA) {
      n_elements++;
      if (is_first_comma) {
        n_elements++;
        is_first_comma = GRN_FALSE;
      }
    }
  }

  return n_elements;
}

static inline int
count_used_n_codes(grn_ctx *ctx, grn_expr_code *start, grn_expr_code *target)
{
  int n_codes;
  int i, n_args;
  grn_bool have_proc_push_code = GRN_FALSE;
  grn_expr_code *sub_code;

  if (start == target) {
    return 0;
  }

  n_args = target->nargs;
  if (target->op == GRN_OP_CALL) {
    if (!target->value) {
      have_proc_push_code = GRN_TRUE;
    }
  } else {
    if (target->value) {
      n_args--;
      if (n_args == 0) {
        return 1;
      }
    }
  }

  n_codes = 1;
  sub_code = target - 1;
  for (i = 0; i < n_args; i++) {
    int sub_n_codes;
    sub_n_codes = count_used_n_codes(ctx, start, sub_code);
    n_codes += sub_n_codes;
    sub_code -= sub_n_codes;
    if (sub_code < start) {
      /* TODO: report error */
      return 0;
    }
  }

  if (have_proc_push_code) {
    n_codes++;
    sub_code--;
    if (sub_code < start) {
      /* TODO: report error */
      return 0;
    }
  }

  return n_codes;
}

static inline void
grn_output_table_column(grn_ctx *ctx, grn_obj *outbuf,
                        grn_content_type output_type,
                        grn_obj *column, grn_obj *buf)
{
  grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
  if (column) {
    grn_id range_id;
    GRN_BULK_REWIND(buf);
    grn_column_name_(ctx, column, buf);
    grn_output_obj(ctx, outbuf, output_type, buf, NULL);
    range_id = grn_obj_get_range(ctx, column);
    if (range_id == GRN_ID_NIL) {
      grn_output_cstr(ctx, outbuf, output_type, "null");
    } else {
      int name_len;
      grn_obj *range_obj;
      char name_buf[GRN_TABLE_MAX_KEY_SIZE];

      range_obj = grn_ctx_at(ctx, range_id);
      name_len = grn_obj_name(ctx, range_obj, name_buf,
                              GRN_TABLE_MAX_KEY_SIZE);
      GRN_BULK_REWIND(buf);
      GRN_TEXT_PUT(ctx, buf, name_buf, name_len);
      grn_output_obj(ctx, outbuf, output_type, buf, NULL);
    }
  } else {
    grn_output_cstr(ctx, outbuf, output_type, "");
    grn_output_cstr(ctx, outbuf, output_type, "");
  }
  grn_output_array_close(ctx, outbuf, output_type);
}

static inline void
grn_output_table_columns_by_expression(grn_ctx *ctx, grn_obj *outbuf,
                                       grn_content_type output_type,
                                       grn_obj *table, grn_obj_format *format,
                                       grn_obj *buf)
{
  int n_elements;
  int previous_comma_offset = -1;
  grn_bool is_first_comma = GRN_TRUE;
  grn_bool have_comma = GRN_FALSE;
  grn_expr *expr = (grn_expr *)format->expression;
  grn_expr_code *code;
  grn_expr_code *code_end = expr->codes + expr->codes_curr;

  n_elements = count_n_elements_in_expression(ctx, format->expression);

  grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", n_elements);
  for (code = expr->codes; code < code_end; code++) {
    int code_start_offset;

    if (code->op != GRN_OP_COMMA) {
      continue;
    }

    have_comma = GRN_TRUE;
    code_start_offset = previous_comma_offset + 1;
    if (is_first_comma) {
      int code_end_offset;
      int n_used_code;

      grn_output_table_column(ctx, outbuf, output_type,
                              expr->codes[0].value, buf);

      code_end_offset = code - expr->codes - code_start_offset - 1;
      n_used_code = count_used_n_codes(ctx,
                                       expr->codes,
                                       expr->codes + code_end_offset);
      code_start_offset = code_end_offset - n_used_code + 1;
      is_first_comma = GRN_FALSE;
    }

    grn_output_table_column(ctx, outbuf, output_type,
                            expr->codes[code_start_offset].value, buf);
    previous_comma_offset = code - expr->codes;
  }

  if (!have_comma && expr->codes_curr > 0) {
    grn_output_table_column(ctx, outbuf, output_type,
                            expr->codes[0].value, buf);
  }

  grn_output_array_close(ctx, outbuf, output_type);
}

static inline void
grn_output_table_columns_by_columns(grn_ctx *ctx, grn_obj *outbuf,
                                    grn_content_type output_type,
                                    grn_obj *table, grn_obj_format *format,
                                    grn_obj *buf)
{
  int i;
  int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
  grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);

  grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", ncolumns);
  for (i = 0; i < ncolumns; i++) {
    grn_output_table_column(ctx, outbuf, output_type, columns[i], buf);
  }
  grn_output_array_close(ctx, outbuf, output_type);
}

static inline void
grn_output_table_columns(grn_ctx *ctx, grn_obj *outbuf,
                         grn_content_type output_type,
                         grn_obj *table, grn_obj_format *format)
{
  grn_obj buf;

  GRN_TEXT_INIT(&buf, 0);
  if (format->expression) {
    grn_output_table_columns_by_expression(ctx, outbuf, output_type,
                                           table, format, &buf);
  } else {
    grn_output_table_columns_by_columns(ctx, outbuf, output_type,
                                        table, format, &buf);
  }
  GRN_OBJ_FIN(ctx, &buf);
}

static inline void
grn_output_table_record_by_expression(grn_ctx *ctx, grn_obj *outbuf,
                                      grn_content_type output_type,
                                      grn_obj *expression)
{
  grn_obj *result;
  result = grn_expr_exec(ctx, expression, 0);
  if (result) {
    grn_output_obj(ctx, outbuf, output_type, result, NULL);
  } else {
    grn_output_cstr(ctx, outbuf, output_type, ctx->errbuf);
  }
}

static inline void
grn_output_table_records_by_expression(grn_ctx *ctx, grn_obj *outbuf,
                                       grn_content_type output_type,
                                       grn_table_cursor *tc,
                                       grn_obj_format *format)
{
  int n_elements = 0;
  grn_obj *record;
  grn_expr *expr = (grn_expr *)format->expression;
  grn_expr_code *code;
  grn_expr_code *code_end = expr->codes + expr->codes_curr;

  n_elements = count_n_elements_in_expression(ctx, format->expression);
  record = grn_expr_get_var_by_offset(ctx, format->expression, 0);
  while (!grn_table_cursor_next_o(ctx, tc, record)) {
    int previous_comma_offset = -1;
    grn_bool is_first_comma = GRN_TRUE;
    grn_bool have_comma = GRN_FALSE;
    grn_output_array_open(ctx, outbuf, output_type, "HIT", n_elements);
    for (code = expr->codes; code < code_end; code++) {
      if (code->op == GRN_OP_COMMA) {
        int code_start_offset = previous_comma_offset + 1;
        int code_end_offset = code - expr->codes - code_start_offset;
        int original_codes_curr = expr->codes_curr;

        have_comma = GRN_TRUE;
        if (is_first_comma) {
          expr->codes_curr = count_used_n_codes(ctx,
                                                expr->codes,
                                                expr->codes + expr->codes_curr);
          grn_output_table_record_by_expression(ctx, outbuf, output_type,
                                                format->expression);
          code_start_offset = expr->codes_curr;
          code_end_offset -= expr->codes_curr;
          is_first_comma = GRN_FALSE;
        }
        expr->codes += code_start_offset;
        expr->codes_curr = code_end_offset;
        grn_output_table_record_by_expression(ctx, outbuf, output_type,
                                              format->expression);
        expr->codes -= code_start_offset;
        expr->codes_curr = original_codes_curr;
        previous_comma_offset = code - expr->codes;
      }
    }

    if (!have_comma && expr->codes_curr > 0) {
      grn_output_table_record_by_expression(ctx, outbuf, output_type,
                                            format->expression);
    }

    grn_output_array_close(ctx, outbuf, output_type);
    GRN_BULK_REWIND(record);
  }
}

static inline void
grn_output_table_records_by_columns(grn_ctx *ctx, grn_obj *outbuf,
                                    grn_content_type output_type,
                                    grn_table_cursor *tc,
                                    grn_obj_format *format)
{
  int i;
  grn_obj id;
  int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
  grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
  GRN_TEXT_INIT(&id, 0);
  while (!grn_table_cursor_next_o(ctx, tc, &id)) {
    grn_output_array_open(ctx, outbuf, output_type, "HIT", ncolumns);
    for (i = 0; i < ncolumns; i++) {
      grn_text_atoj_o(ctx, outbuf, output_type, columns[i], &id);
    }
    grn_output_array_close(ctx, outbuf, output_type);
  }
  GRN_OBJ_FIN(ctx, &id);
}

static inline void
grn_output_table_records(grn_ctx *ctx, grn_obj *outbuf,
                         grn_content_type output_type,
                         grn_obj *table, grn_obj_format *format)
{
  grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
                                               format->offset, format->limit,
                                               GRN_CURSOR_ASCENDING);
  if (tc) {
    if (format->expression) {
      grn_output_table_records_by_expression(ctx, outbuf, output_type,
                                             tc, format);
    } else {
      grn_output_table_records_by_columns(ctx, outbuf, output_type,
                                          tc, format);
    }
    grn_table_cursor_close(ctx, tc);
  } else {
    ERRCLR(ctx);
  }
}

static inline void
grn_output_table(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
                 grn_obj *table, grn_obj_format *format)
{
  grn_obj buf;
  GRN_TEXT_INIT(&buf, 0);
  if (format) {
    int resultset_size = 1;
    /* resultset: [NHITS, (COLUMNS), (HITS)] */
    if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
      resultset_size++;
    }
    resultset_size += format->limit;
    grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", resultset_size);
    grn_output_table_header(ctx, outbuf, output_type, table, format);
    if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
      grn_output_table_columns(ctx, outbuf, output_type, table, format);
    }
    grn_output_table_records(ctx, outbuf, output_type, table, format);
    grn_output_array_close(ctx, outbuf, output_type);
  } else {
    int i;
    grn_obj *column = grn_obj_column(ctx, table, "_key", 4);
    grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
                                                 0, -1, GRN_CURSOR_ASCENDING);
    grn_output_array_open(ctx, outbuf, output_type, "HIT", -1);
    if (tc) {
      grn_obj id;
      GRN_TEXT_INIT(&id, 0);
      for (i = 0; !grn_table_cursor_next_o(ctx, tc, &id); i++) {
        /* todo:
           grn_text_atoj_o(ctx, outbuf, output_type, column, &id);
        */
        GRN_BULK_REWIND(&buf);
        grn_obj_get_value_o(ctx, column, &id, &buf);
        grn_text_esc(ctx, outbuf, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf));
      }
      GRN_OBJ_FIN(ctx, &id);
      grn_table_cursor_close(ctx, tc);
    }
    grn_output_array_close(ctx, outbuf, output_type);
    grn_obj_unlink(ctx, column);
  }
  GRN_OBJ_FIN(ctx, &buf);
}

void
grn_output_obj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
               grn_obj *obj, grn_obj_format *format)
{
  grn_obj buf;
  GRN_TEXT_INIT(&buf, 0);
  switch (obj->header.type) {
  case GRN_VOID :
    grn_output_void(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_BULK :
    grn_output_bulk(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_UVECTOR :
    grn_output_uvector(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_VECTOR :
    grn_output_vector(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_PVECTOR :
    grn_output_pvector(ctx, outbuf, output_type, obj, format);
    break;
  case GRN_TABLE_HASH_KEY :
  case GRN_TABLE_PAT_KEY :
  case GRN_TABLE_NO_KEY :
  case GRN_TABLE_VIEW :
    grn_output_table(ctx, outbuf, output_type, obj, format);
    break;
  }
  GRN_OBJ_FIN(ctx, &buf);
}

typedef enum {
  XML_START,
  XML_START_ELEMENT,
  XML_END_ELEMENT,
{'fraction_non_alphanumeric': 0.11613173652694611, 'fraction_numerical': 0.005964071856287425, 'mean_word_length': 2.72373349982162, 'pattern_counts': {'":': 0, '<': 103, '<?xml version=': 0, '>': 391, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 84, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2071179', 'n_tokens_mistral': 32756, 'n_tokens_neox': 32160, 'n_words': 5883}