static mrb_value mrb_memcached_set(mrb_state *mrb, mrb_value self) { mrb_value key, val; mrb_int expr = 0; mrb_memcached_data *data = DATA_PTR(self); memcached_return mrt; mrb_get_args(mrb, "oo|i", &key, &val, &expr); switch (mrb_type(key)) { case MRB_TT_STRING: break; case MRB_TT_SYMBOL: key = mrb_obj_as_string(mrb, key); break; default: mrb_raise(mrb, E_RUNTIME_ERROR, "memcached key type is string or symbol"); } val = mrb_obj_as_string(mrb, val); mrt = memcached_set(data->mst, RSTRING_PTR(key), RSTRING_LEN(key), RSTRING_PTR(val), RSTRING_LEN(val), (time_t)expr, (uint32_t)0); if (mrt != MEMCACHED_SUCCESS && mrt != MEMCACHED_BUFFERED) { // set failed return mrb_nil_value(); } return mrb_fixnum_value(mrt); }
static mrb_value range_to_s(mrb_state *mrb, mrb_value range) { mrb_value str, str2; struct RRange *r = mrb_range_ptr(range); str = mrb_obj_as_string(mrb, r->edges->beg); str2 = mrb_obj_as_string(mrb, r->edges->end); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); mrb_str_append(mrb, str, str2); return str; }
static mrb_value mrb_vedis_set(mrb_state *mrb, mrb_value self) { int ret; vedis *vstore = DATA_PTR(self); mrb_value key_obj, val_obj; const char *key = NULL; mrb_get_args(mrb, "oo", &key_obj, &val_obj); switch (mrb_type(key_obj)) { case MRB_TT_STRING: key = RSTRING_PTR(key_obj); break; case MRB_TT_SYMBOL: key = mrb_sym2name(mrb, mrb_obj_to_sym(mrb, key_obj)); break; default: mrb_raise(mrb, E_RUNTIME_ERROR, "vedis key type is string or symbol"); } val_obj = mrb_obj_as_string(mrb, val_obj); ret = vedis_kv_store(vstore, key, strlen(key), RSTRING_PTR(val_obj), RSTRING_LEN(val_obj)); if (ret != VEDIS_OK) { mrb_vedis_error(mrb, vstore, 0); } return val_obj; }
static mrb_value mrb_vedis_append_s(mrb_state *mrb, mrb_value key_obj, mrb_value val_obj, vedis *vstore) { int ret; const char *key = NULL; size_t key_len; switch (mrb_type(key_obj)) { case MRB_TT_STRING: key = RSTRING_PTR(key_obj); key_len = RSTRING_LEN(key_obj); break; case MRB_TT_SYMBOL: key = mrb_sym2name(mrb, mrb_obj_to_sym(mrb, key_obj)); key_len = strlen(key); break; default: mrb_raise(mrb, E_RUNTIME_ERROR, "vedis key type is string or symbol"); } val_obj = mrb_obj_as_string(mrb, val_obj); ret = vedis_kv_append(vstore, key, key_len, RSTRING_PTR(val_obj), RSTRING_LEN(val_obj)); if (ret != VEDIS_OK) { mrb_vedis_error(mrb, vstore, 0); } return mrb_true_value(); }
static mrb_value mrb_memcached_get(mrb_state *mrb, mrb_value self) { mrb_value key; char *val; size_t len; uint32_t flags; memcached_return mrt; mrb_memcached_data *data = DATA_PTR(self); mrb_get_args(mrb, "o", &key); switch (mrb_type(key)) { case MRB_TT_STRING: break; case MRB_TT_SYMBOL: key = mrb_obj_as_string(mrb, key); break; default: mrb_raise(mrb, E_RUNTIME_ERROR, "memcached key type is string or symbol"); } val = memcached_get(data->mst, RSTRING_PTR(key), RSTRING_LEN(key), &len, &flags, &(mrt)); if (mrt != MEMCACHED_SUCCESS && mrt != MEMCACHED_BUFFERED) { free(val); // value not found return mrb_nil_value(); } return mrb_str_new(mrb, val, len); }
static mrb_value exc_inspect(mrb_state *mrb, mrb_value exc) { RString *str; mrb_value mesg = mrb_attr_get(exc, mrb->intern2("mesg", 4)); mrb_value file = mrb_attr_get(exc, mrb->intern2("file", 4)); mrb_value line = mrb_attr_get(exc, mrb->intern2("line", 4)); RString *mesg_ptr = mesg.is_nil() ? nullptr : mesg.ptr<RString>(); if (!file.is_nil() && !line.is_nil()) { assert(file.is_string()); str = file.ptr<RString>()->dup(); str->str_buf_cat(":",1); str->str_cat(mrb_obj_as_string(mrb,line)); str->str_buf_cat(": ",2); if (mesg_ptr && mesg_ptr->len > 0) { str->str_cat(mesg_ptr); str->str_buf_cat(" (",2); } str->str_buf_cat(mrb_obj_classname(mrb, exc)); if (mesg_ptr && mesg_ptr->len > 0) { str->str_buf_cat(")",1); } } else { str = RString::create(mrb,mrb_obj_classname(mrb, exc)); str->str_buf_cat(": ",2); if (mesg_ptr && mesg_ptr->len > 0) { str->str_cat(mesg_ptr); } else { str->str_buf_cat(mrb_obj_classname(mrb, exc)); } } return str->wrap(); }
MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep) { if (!mrb_nil_p(sep)) { sep = mrb_obj_as_string(mrb, sep); } return join_ary(mrb, ary, sep, mrb_ary_new(mrb)); }
static mrb_value ngx_http_mruby_rputs(mrb_state *mrb, mrb_value self) { mrb_value argv; ngx_buf_t *b; ngx_http_mruby_rputs_chain_list_t *chain; u_char *str; ngx_str_t ns; ngx_http_request_t *r; ngx_http_mruby_ctx_t *ctx; r = ngx_http_mruby_get_request(); ctx = ngx_http_get_module_ctx(r, ngx_http_mruby_module); mrb_get_args(mrb, "o", &argv); argv = mrb_obj_as_string(mrb, argv); ns.data = (u_char *)RSTRING_PTR(argv); ns.len = RSTRING_LEN(argv); if (ns.len == 0) { return self; } if (ctx->rputs_chain == NULL) { chain = ngx_pcalloc(r->pool, sizeof(ngx_http_mruby_rputs_chain_list_t)); chain->out = ngx_alloc_chain_link(r->pool); chain->last = &chain->out; } else { chain = ctx->rputs_chain; (*chain->last)->next = ngx_alloc_chain_link(r->pool); chain->last = &(*chain->last)->next; } b = ngx_calloc_buf(r->pool); (*chain->last)->buf = b; (*chain->last)->next = NULL; if ((str = ngx_pnalloc(r->pool, ns.len + 1)) == NULL) { return self; } ngx_memcpy(str, ns.data, ns.len); str[ns.len] = '\0'; (*chain->last)->buf->pos = str; (*chain->last)->buf->last = str + ns.len; (*chain->last)->buf->memory = 1; ctx->rputs_chain = chain; if (r->headers_out.content_length_n == -1) { r->headers_out.content_length_n += ns.len + 1; } else { r->headers_out.content_length_n += ns.len; } return self; }
static mrb_value join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) { mrb_int i; mrb_value result, val, tmp; /* check recursive */ for (i=0; i<RARRAY_LEN(list); i++) { if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) { mrb_raise(mrb, E_ARGUMENT_ERROR, "recursive array join"); } } mrb_ary_push(mrb, list, ary); result = mrb_str_buf_new(mrb, 64); for (i=0; i<RARRAY_LEN(ary); i++) { if (i > 0 && !mrb_nil_p(sep)) { mrb_str_cat_str(mrb, result, sep); } val = RARRAY_PTR(ary)[i]; switch (mrb_type(val)) { case MRB_TT_ARRAY: ary_join: val = join_ary(mrb, val, sep, list); /* fall through */ case MRB_TT_STRING: str_join: mrb_str_cat_str(mrb, result, val); break; default: if (!mrb_immediate_p(val)) { tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { val = tmp; goto str_join; } tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); if (!mrb_nil_p(tmp)) { val = tmp; goto ary_join; } } val = mrb_obj_as_string(mrb, val); goto str_join; } } mrb_ary_pop(mrb, list); return result; }
static mrb_value ngx_http_mruby_variable_set(mrb_state *mrb, mrb_value self) { char *k; mrb_value o; mrb_get_args(mrb, "zo", &k, &o); o = mrb_obj_as_string(mrb, o); return ngx_http_mruby_variable_set_internal(mrb, self, k, o); }
VCL_STRING vmod_exec(VRT_CTX, struct vmod_priv *priv, VCL_STRING code) { mrb_state *mrb = (mrb_state*)priv->priv; mrb_value v = mrb_code_exec(mrb, code); if(!mrb_string_p(v)) { v = mrb_obj_as_string(mrb,v); } return RSTRING_PTR(v); }
static mrb_value ngx_http_mruby_log(mrb_state *mrb, mrb_value self) { mrb_value *argv; mrb_value msg; mrb_int argc; mrb_int log_level; ngx_http_request_t *r; r = ngx_http_mruby_get_request(); mrb_get_args(mrb, "*", &argv, &argc); if (argc != 2) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s: argument is not 2" , MODULE_NAME , __FUNCTION__ ); return self; } if (mrb_type(argv[0]) != MRB_TT_FIXNUM) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s: argv[0] is not integer" , MODULE_NAME , __FUNCTION__ ); return self; } log_level = mrb_fixnum(argv[0]); if (log_level < 0) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s: log level is not positive number" , MODULE_NAME , __FUNCTION__ ); return self; } msg = mrb_obj_as_string(mrb, argv[1]); ngx_log_error((ngx_uint_t)log_level, r->connection->log, 0, "%s", RSTRING_PTR(msg)); return self; }
static void p(mrb_state *mrb, mrb_value obj) { mrb_value val; val = mrb_funcall(mrb, obj, "inspect", 0); if (!mrb_string_p(val)) { val = mrb_obj_as_string(mrb, obj); } fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout); putc('\n', stdout); }
static mrb_value ngx_http_mruby_parse_http_time(mrb_state *mrb, mrb_value self) { mrb_value mrb_http_time; ngx_str_t http_time; mrb_get_args(mrb, "o", &mrb_http_time); mrb_http_time = mrb_obj_as_string(mrb, mrb_http_time); http_time.data = (u_char *)RSTRING_PTR(mrb_http_time); http_time.len = RSTRING_LEN(mrb_http_time); return mrb_fixnum_value(ngx_http_parse_time(http_time.data, http_time.len)); }
static mrb_value exc_inspect(mrb_state *mrb, mrb_value exc) { mrb_value str; str = mrb_str_new2(mrb, mrb_obj_classname(mrb, exc)); exc = mrb_obj_as_string(mrb, exc); if (RSTRING_LEN(exc) > 0) { mrb_str_cat2(mrb, str, ": "); mrb_str_append(mrb, str, exc); } return str; }
ngx_int_t ngx_http_mruby_run_body_filter(ngx_http_request_t *r, ngx_http_mruby_state_t *state, ngx_http_mruby_code_t *code, ngx_flag_t cached) { ngx_http_mruby_ctx_t *ctx; mrb_value ARGV, mrb_result; size_t body_length; ctx = ngx_http_get_module_ctx(r, ngx_http_mruby_module); ngx_http_mruby_push_request(r); state->ai = mrb_gc_arena_save(state->mrb); ARGV = mrb_ary_new_capa(state->mrb, 1); mrb_ary_push(state->mrb, ARGV, mrb_str_new(state->mrb, (char *)ctx->filter_ctx.body, ctx->filter_ctx.body_length)); mrb_define_global_const(state->mrb, "ARGV", ARGV); mrb_result = mrb_run(state->mrb, code->proc, mrb_top_self(state->mrb)); if (state->mrb->exc) { ngx_http_mruby_raise_error(state, code, r); mrb_gc_arena_restore(state->mrb, state->ai); //mrb_gc_protect(state->mrb, ctx->table); if (!cached) { ngx_http_mruby_irep_clean(state, code); } return NGX_ERROR; } mrb_result = mrb_obj_as_string(state->mrb, mrb_result); body_length = RSTRING_LEN(mrb_result); if (body_length != 0) { ctx->filter_ctx.body = (u_char *)RSTRING_PTR(mrb_result); ctx->filter_ctx.body_length = body_length; } mrb_gc_arena_restore(state->mrb, state->ai); //mrb_gc_protect(state->mrb, ctx->table); if (!cached) { ngx_http_mruby_irep_clean(state, code); } return NGX_OK; }
static mrb_value ngx_http_mruby_base64_encode(mrb_state *mrb, mrb_value self) { mrb_value mrb_src; ngx_str_t src, dst; mrb_get_args(mrb, "o", &mrb_src); mrb_src = mrb_obj_as_string(mrb, mrb_src); src.data = (u_char *)RSTRING_PTR(mrb_src); src.len = RSTRING_LEN(mrb_src); dst.len = ngx_base64_encoded_length(src.len); dst.data = mrb_malloc(mrb, dst.len + 1); ngx_encode_base64(&dst, &src); return mrb_str_new(mrb, (char *)dst.data, dst.len); }
static mrb_value exc_inspect(mrb_state *mrb, mrb_value exc) { mrb_value str, klass; klass = mrb_str_new2(mrb, mrb_obj_classname(mrb, exc)); exc = mrb_obj_as_string(mrb, exc); if (RSTRING_LEN(exc) == 0) { return klass; } str = mrb_str_new2(mrb, "#<"); mrb_str_append(mrb, str, klass); mrb_str_cat2(mrb, str, ": "); mrb_str_append(mrb, str, exc); mrb_str_cat2(mrb, str, ">"); return str; }
void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) { const struct types *type = builtin_types; struct RString *s; int xt; /*if (x == Qundef) { //mrb_bug("undef leaked to the Ruby space"); printf ("undef leaked to the Ruby space\n"); }*/ xt = mrb_type(x); if ((xt != t) || (xt == MRB_TT_DATA)) { while (type->type < MRB_TT_MAXDEFINE) { if (type->type == t) { const char *etype; if (mrb_nil_p(x)) { etype = "nil"; } else if (mrb_type(x) == MRB_TT_FIXNUM) { etype = "Fixnum"; } else if (mrb_type(x) == MRB_TT_SYMBOL) { etype = "Symbol"; } else if (mrb_special_const_p(x)) { s = mrb_str_ptr(mrb_obj_as_string(mrb, x)); etype = s->buf; } else { etype = mrb_obj_classname(mrb, x); } mrb_raise(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)", etype, type->name); } type++; } /*mrb_bug("unknown type 0x%x", t);*/ printf ("unknown type 0x%x (0x%x given)", t, mrb_type(x)); } }
static mrb_value exc_inspect(mrb_state *mrb, mrb_value exc) { mrb_value str, mesg, file, line; mrb_bool append_mesg; mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg")); file = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "file")); line = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "line")); append_mesg = !mrb_nil_p(mesg); if (append_mesg) { mesg = mrb_obj_as_string(mrb, mesg); append_mesg = RSTRING_LEN(mesg) > 0; } if (!mrb_nil_p(file) && !mrb_nil_p(line)) { str = mrb_str_dup(mrb, file); mrb_str_cat_lit(mrb, str, ":"); mrb_str_append(mrb, str, line); mrb_str_cat_lit(mrb, str, ": "); if (append_mesg) { mrb_str_cat_str(mrb, str, mesg); mrb_str_cat_lit(mrb, str, " ("); } mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc)); if (append_mesg) { mrb_str_cat_lit(mrb, str, ")"); } } else { const char *cname = mrb_obj_classname(mrb, exc); str = mrb_str_new_cstr(mrb, cname); mrb_str_cat_lit(mrb, str, ": "); if (append_mesg) { mrb_str_cat_str(mrb, str, mesg); } else { mrb_str_cat_cstr(mrb, str, cname); } } return str; }
ngx_int_t ngx_http_mruby_run_args(ngx_http_request_t *r, ngx_http_mruby_state_t *state, ngx_http_mruby_code_t *code, ngx_flag_t cached, ngx_http_variable_value_t *args, size_t nargs, ngx_str_t *result) { ngx_uint_t i; mrb_value ARGV, mrb_result; state->ai = mrb_gc_arena_save(state->mrb); ARGV = mrb_ary_new_capa(state->mrb, nargs); for (i = 0; i < nargs; i++) { mrb_ary_push(state->mrb, ARGV, mrb_str_new(state->mrb, (char *)args[i].data, args[i].len)); } mrb_define_global_const(state->mrb, "ARGV", ARGV); ngx_http_mruby_push_request(r); mrb_result = mrb_run(state->mrb, code->proc, mrb_top_self(state->mrb)); if (state->mrb->exc) { ngx_http_mruby_raise_error(state, code, r); mrb_gc_arena_restore(state->mrb, state->ai); //mrb_gc_protect(state->mrb, ctx->table); if (!cached) { ngx_http_mruby_irep_clean(state, code); } return NGX_ERROR; } mrb_result = mrb_obj_as_string(state->mrb, mrb_result); result->data = (u_char *)RSTRING_PTR(mrb_result); result->len = RSTRING_LEN(mrb_result); mrb_gc_arena_restore(state->mrb, state->ai); //mrb_gc_protect(state->mrb, ctx->table); if (!cached) { ngx_http_mruby_irep_clean(state, code); } return NGX_OK; }
static void p(mrb_state *mrb, mrb_value obj, int prompt) { mrb_value val; val = mrb_funcall(mrb, obj, "inspect", 0); if (prompt) { if (!mrb->exc) { fputs(" => ", stdout); } else { val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); } } if (!mrb_string_p(val)) { val = mrb_obj_as_string(mrb, obj); } fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout); putc('\n', stdout); }
static mrb_value ngx_http_mruby_variable_method_missing(mrb_state *mrb, mrb_value self) { mrb_value name, *a; int alen, len; mrb_value s_name; char *c_name; mrb_get_args(mrb, "n*", &name, &a, &alen); s_name = mrb_sym2str(mrb, mrb_symbol(name)); c_name = mrb_str_to_cstr(mrb, s_name); len = ngx_strlen(c_name); if (c_name[len - 1] == '=') { a[0] = mrb_obj_as_string(mrb, a[0]); return ngx_http_mruby_variable_set_internal(mrb, self, strtok(c_name, "="), a[0]); } return ngx_http_mruby_variable_get(mrb, self, c_name); }
static mrb_value ngx_stream_mrb_errlogger(mrb_state *mrb, mrb_value self) { mrb_value msg; mrb_int log_level; ngx_stream_mruby_internal_ctx_t *ictx = mrb->ud; ngx_stream_session_t *s = ictx->s; if (s == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't use logger at this phase. only use at session stream phase"); } mrb_get_args(mrb, "io", &log_level, &msg); if (log_level < 0) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "%s ERROR %s: log level is not positive number", MODULE_NAME, __func__); return self; } msg = mrb_obj_as_string(mrb, msg); ngx_log_error((ngx_uint_t)log_level, s->connection->log, 0, "%s", mrb_str_to_cstr(mrb, msg)); return self; }
void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) { const struct types *type = builtin_types; struct RString *s; enum mrb_vtype xt; xt = mrb_type(x); if ((xt != t) || (xt == MRB_TT_DATA)) { while (type->type < MRB_TT_MAXDEFINE) { if (type->type == t) { const char *etype; if (mrb_nil_p(x)) { etype = "nil"; } else if (mrb_fixnum_p(x)) { etype = "Fixnum"; } else if (mrb_type(x) == MRB_TT_SYMBOL) { etype = "Symbol"; } else if (mrb_special_const_p(x)) { s = mrb_str_ptr(mrb_obj_as_string(mrb, x)); etype = s->ptr; } else { etype = mrb_obj_classname(mrb, x); } mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)", mrb_str_new_cstr(mrb, etype), mrb_str_new_cstr(mrb, type->name)); } type++; } mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %S (%S given)", mrb_fixnum_value(t), mrb_fixnum_value(mrb_type(x))); } }
mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep) { sep = mrb_obj_as_string(mrb, sep); return join_ary(mrb, ary, sep, mrb_ary_new(mrb)); }
mrb_value mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt) { const char *p, *end; char *buf; mrb_int blen; mrb_int bsiz; mrb_value result; mrb_int n; mrb_int width; mrb_int prec; int flags = FNONE; int nextarg = 1; int posarg = 0; mrb_value nextvalue; mrb_value tmp; mrb_value str; mrb_value hash = mrb_undef_value(); #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \ } #define CHECK_FOR_FLAGS(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \ } ++argc; --argv; mrb_string_value(mrb, &fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0; bsiz = 120; result = mrb_str_buf_new(mrb, bsiz); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); for (; p < end; p++) { const char *t; mrb_sym id = 0; for (t = p; t < end && *t != '%'; t++) ; PUSH(p, t - p); if (t >= end) goto sprint_exit; /* end of fmt string */ p = t + 1; /* skip `%' */ width = prec = -1; nextvalue = mrb_undef_value(); retry: switch (*p) { default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1)); break; case ' ': CHECK_FOR_FLAGS(flags); flags |= FSPACE; p++; goto retry; case '#': CHECK_FOR_FLAGS(flags); flags |= FSHARP; p++; goto retry; case '+': CHECK_FOR_FLAGS(flags); flags |= FPLUS; p++; goto retry; case '-': CHECK_FOR_FLAGS(flags); flags |= FMINUS; p++; goto retry; case '0': CHECK_FOR_FLAGS(flags); flags |= FZERO; p++; goto retry; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; GETNUM(n, width); if (*p == '$') { if (!mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(n)); } nextvalue = GETPOSARG(n); p++; goto retry; } CHECK_FOR_WIDTH(flags); width = n; flags |= FWIDTH; goto retry; case '<': case '{': { const char *start = p; char term = (*p == '<') ? '>' : '}'; mrb_value symname; for (; p < end && *p != term; ) p++; if (id) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>", mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id)); } symname = mrb_str_new(mrb, start + 1, p - start - 1); id = mrb_intern_str(mrb, symname); nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (int)(p - start + 1)); if (mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1)); } if (term == '}') goto format_s; p++; goto retry; } case '*': CHECK_FOR_WIDTH(flags); flags |= FWIDTH; GETASTER(width); if (width < 0) { flags |= FMINUS; width = -width; } p++; goto retry; case '.': if (flags & FPREC0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "precision given twice"); } flags |= FPREC|FPREC0; prec = 0; p++; if (*p == '*') { GETASTER(prec); if (prec < 0) { /* ignore negative precision */ flags &= ~FPREC; } p++; goto retry; } GETNUM(prec, precision); goto retry; case '\n': case '\0': p--; case '%': if (flags != FNONE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %"); } PUSH("%", 1); break; case 'c': { mrb_value val = GETARG(); mrb_value tmp; unsigned int c; tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { if (RSTRING_LEN(tmp) != 1 ) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } c = RSTRING_PTR(tmp)[0]; n = 1; } else { c = mrb_fixnum(val); n = 1; } if (n <= 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } if (!(flags & FWIDTH)) { CHECK(n); buf[blen] = c; blen += n; } else if ((flags & FMINUS)) { CHECK(n); buf[blen] = c; blen += n; FILL(' ', width-1); } else { FILL(' ', width-1); CHECK(n); buf[blen] = c; blen += n; } } break; case 's': case 'p': format_s: { mrb_value arg = GETARG(); mrb_int len; mrb_int slen; if (*p == 'p') arg = mrb_inspect(mrb, arg); str = mrb_obj_as_string(mrb, arg); len = RSTRING_LEN(str); RSTRING_LEN(result) = blen; if (flags&(FPREC|FWIDTH)) { slen = RSTRING_LEN(str); if (slen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence"); } if ((flags&FPREC) && (prec < slen)) { char *p = RSTRING_PTR(str) + prec; slen = prec; len = p - RSTRING_PTR(str); } /* need to adjust multi-byte string pos */ if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { CHECK(width); while (width--) { buf[blen++] = ' '; } } CHECK(len); memcpy(&buf[blen], RSTRING_PTR(str), len); blen += len; if (flags&FMINUS) { CHECK(width); while (width--) { buf[blen++] = ' '; } } break; } } PUSH(RSTRING_PTR(str), len); } break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': { mrb_value val = GETARG(); char fbuf[32], nbuf[64], *s; const char *prefix = NULL; int sign = 0, dots = 0; char sc = 0; mrb_int v = 0, org_v = 0; int base; mrb_int len; switch (*p) { case 'd': case 'i': case 'u': sign = 1; break; case 'o': case 'x': case 'X': case 'b': case 'B': if (flags&(FPLUS|FSPACE)) sign = 1; break; default: break; } if (flags & FSHARP) { switch (*p) { case 'o': prefix = "0"; break; case 'x': prefix = "0x"; break; case 'X': prefix = "0X"; break; case 'b': prefix = "0b"; break; case 'B': prefix = "0B"; break; default: break; } } bin_retry: switch (mrb_type(val)) { case MRB_TT_FLOAT: if (FIXABLE(mrb_float(val))) { val = mrb_fixnum_value((mrb_int)mrb_float(val)); goto bin_retry; } val = mrb_flt2big(mrb, mrb_float(val)); if (mrb_fixnum_p(val)) goto bin_retry; break; case MRB_TT_STRING: val = mrb_str_to_inum(mrb, val, 0, TRUE); goto bin_retry; case MRB_TT_FIXNUM: v = mrb_fixnum(val); break; default: val = mrb_Integer(mrb, val); goto bin_retry; } switch (*p) { case 'o': base = 8; break; case 'x': case 'X': base = 16; break; case 'b': case 'B': base = 2; break; case 'u': case 'd': case 'i': default: base = 10; break; } if (base == 2) { org_v = v; if ( v < 0 && !sign ) { val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); dots = 1; } else { val = mrb_fix2str(mrb, mrb_fixnum_value(v), base); } v = mrb_fixnum(mrb_str_to_inum(mrb, val, 10, 0/*Qfalse*/)); } if (sign) { char c = *p; if (c == 'i') c = 'd'; /* %d and %i are identical */ if (base == 2) c = 'd'; if (v < 0) { v = -v; sc = '-'; width--; } else if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } snprintf(fbuf, sizeof(fbuf), "%%l%c", c); snprintf(nbuf, sizeof(nbuf), fbuf, v); s = nbuf; } else { char c = *p; if (c == 'X') c = 'x'; if (base == 2) c = 'd'; s = nbuf; if (v < 0) { dots = 1; } snprintf(fbuf, sizeof(fbuf), "%%l%c", c); snprintf(++s, sizeof(nbuf) - 1, fbuf, v); if (v < 0) { char d; s = remove_sign_bits(s, base); switch (base) { case 16: d = 'f'; break; case 8: d = '7'; break; case 2: d = '1'; break; default: d = 0; break; } if (d && *s != d) { *--s = d; } } } { size_t size; size = strlen(s); /* PARANOID: assert(size <= MRB_INT_MAX) */ len = (mrb_int)size; } if (dots) { prec -= 2; width -= 2; } if (*p == 'X') { char *pp = s; int c; while ((c = (int)(unsigned char)*pp) != 0) { *pp = toupper(c); pp++; } } if (prefix && !prefix[1]) { /* octal */ if (dots) { prefix = NULL; } else if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } else if ((flags & FPREC) && (prec > len)) { prefix = NULL; } } else if (len == 1 && *s == '0') { prefix = NULL; } if (prefix) { size_t size; size = strlen(prefix); /* PARANOID: assert(size <= MRB_INT_MAX). * this check is absolutely paranoid. */ width -= (mrb_int)size; } if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) { prec = width; width = 0; } else { if (prec < len) { if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0; prec = len; } width -= prec; } if (!(flags&FMINUS)) { CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } if (sc) PUSH(&sc, 1); if (prefix) { int plen = (int)strlen(prefix); PUSH(prefix, plen); } CHECK(prec - len); if (dots) PUSH("..", 2); if (v < 0 || (base == 2 && org_v < 0)) { char c = sign_bits(base, p); while (len < prec--) { buf[blen++] = c; } } else if ((flags & (FMINUS|FPREC)) != FMINUS) { char c = '0'; while (len < prec--) { buf[blen++] = c; } } PUSH(s, len); CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } break; case 'f': case 'g': case 'G': case 'e': case 'E': case 'a': case 'A': { mrb_value val = GETARG(); double fval; int i, need = 6; char fbuf[32]; fval = mrb_float(mrb_Float(mrb, val)); if (isnan(fval) || isinf(fval)) { const char *expr; const int elen = 3; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = elen; if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS)) need++; if ((flags & FWIDTH) && need < width) need = width; CHECK(need + 1); n = snprintf(&buf[blen], need + 1, "%*s", need, ""); if (flags & FMINUS) { if (!isnan(fval) && fval < 0.0) buf[blen++] = '-'; else if (flags & FPLUS) buf[blen++] = '+'; else if (flags & FSPACE) blen++; memcpy(&buf[blen], expr, elen); } else { if (!isnan(fval) && fval < 0.0) buf[blen + need - elen - 1] = '-'; else if (flags & FPLUS) buf[blen + need - elen - 1] = '+'; else if ((flags & FSPACE) && need > width) blen++; memcpy(&buf[blen + need - elen], expr, elen); } blen += strlen(&buf[blen]); break; } fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec); need = 0; if (*p != 'e' && *p != 'E') { i = INT_MIN; frexp(fval, &i); if (i > 0) need = BIT_DIGITS(i); } need += (flags&FPREC) ? prec : 6; if ((flags&FWIDTH) && need < width) need = width; need += 20; CHECK(need); n = snprintf(&buf[blen], need, fbuf, fval); blen += n; } break; } flags = FNONE; } sprint_exit: #if 0 /* XXX - We cannot validate the number of arguments if (digit)$ style used. */ if (posarg >= 0 && nextarg < argc) { const char *mesg = "too many arguments for format string"; if (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg); if (mrb_test(ruby_verbose)) mrb_warn("%s", mesg); } #endif mrb_str_resize(mrb, result, blen); return result; }
mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj) { return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0, 0)); }
mrb_value mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fmt) { const char *p, *end; char *buf; mrb_int blen; mrb_int bsiz; mrb_value result; mrb_int n; mrb_int width; mrb_int prec; int nextarg = 1; int posarg = 0; mrb_value nextvalue; mrb_value str; mrb_value hash = mrb_undef_value(); #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \ } #define CHECK_FOR_FLAGS(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \ } ++argc; --argv; mrb_to_str(mrb, fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0; bsiz = 120; result = mrb_str_new_capa(mrb, bsiz); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); for (; p < end; p++) { const char *t; mrb_sym id = 0; int flags = FNONE; for (t = p; t < end && *t != '%'; t++) ; if (t + 1 == end) ++t; PUSH(p, t - p); if (t >= end) goto sprint_exit; /* end of fmt string */ p = t + 1; /* skip '%' */ width = prec = -1; nextvalue = mrb_undef_value(); retry: switch (*p) { default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1)); break; case ' ': CHECK_FOR_FLAGS(flags); flags |= FSPACE; p++; goto retry; case '#': CHECK_FOR_FLAGS(flags); flags |= FSHARP; p++; goto retry; case '+': CHECK_FOR_FLAGS(flags); flags |= FPLUS; p++; goto retry; case '-': CHECK_FOR_FLAGS(flags); flags |= FMINUS; p++; goto retry; case '0': CHECK_FOR_FLAGS(flags); flags |= FZERO; p++; goto retry; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; GETNUM(n, width); if (*p == '$') { if (!mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(n)); } nextvalue = GETPOSARG(n); p++; goto retry; } CHECK_FOR_WIDTH(flags); width = n; flags |= FWIDTH; goto retry; case '<': case '{': { const char *start = p; char term = (*p == '<') ? '>' : '}'; mrb_value symname; for (; p < end && *p != term; ) p++; if (id) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>", mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id)); } symname = mrb_str_new(mrb, start + 1, p - start - 1); id = mrb_intern_str(mrb, symname); nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (mrb_int)(p - start + 1)); if (mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1)); } if (term == '}') goto format_s; p++; goto retry; } case '*': CHECK_FOR_WIDTH(flags); flags |= FWIDTH; GETASTER(width); if (width < 0) { flags |= FMINUS; width = -width; } p++; goto retry; case '.': if (flags & FPREC0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "precision given twice"); } flags |= FPREC|FPREC0; prec = 0; p++; if (*p == '*') { GETASTER(prec); if (prec < 0) { /* ignore negative precision */ flags &= ~FPREC; } p++; goto retry; } GETNUM(prec, precision); goto retry; case '\n': case '\0': p--; /* fallthrough */ case '%': if (flags != FNONE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %"); } PUSH("%", 1); break; case 'c': { mrb_value val = GETARG(); mrb_value tmp; char *c; tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { if (RSTRING_LEN(tmp) != 1) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } } else if (mrb_fixnum_p(val)) { mrb_int n = mrb_fixnum(val); if (n < 0x80) { char buf[1]; buf[0] = (char)n; tmp = mrb_str_new(mrb, buf, 1); } else { tmp = mrb_funcall(mrb, val, "chr", 0); mrb_check_type(mrb, tmp, MRB_TT_STRING); } } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } c = RSTRING_PTR(tmp); n = RSTRING_LEN(tmp); if (!(flags & FWIDTH)) { PUSH(c, n); } else if ((flags & FMINUS)) { PUSH(c, n); if (width>0) FILL(' ', width-1); } else { if (width>0) FILL(' ', width-1); PUSH(c, n); } } break; case 's': case 'p': format_s: { mrb_value arg = GETARG(); mrb_int len; mrb_int slen; if (*p == 'p') arg = mrb_inspect(mrb, arg); str = mrb_obj_as_string(mrb, arg); len = RSTRING_LEN(str); if (RSTRING(result)->flags & MRB_STR_EMBED) { mrb_int tmp_n = len; RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK; RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT; } else { RSTRING(result)->as.heap.len = blen; } if (flags&(FPREC|FWIDTH)) { slen = RSTRING_LEN(str); if (slen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence"); } if ((flags&FPREC) && (prec < slen)) { char *p = RSTRING_PTR(str) + prec; slen = prec; len = (mrb_int)(p - RSTRING_PTR(str)); } /* need to adjust multi-byte string pos */ if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { FILL(' ', width); } PUSH(RSTRING_PTR(str), len); if (flags&FMINUS) { FILL(' ', width); } break; } } PUSH(RSTRING_PTR(str), len); } break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': { mrb_value val = GETARG(); char nbuf[68], *s; const char *prefix = NULL; int sign = 0, dots = 0; char sc = 0; mrb_int v = 0; int base; mrb_int len; if (flags & FSHARP) { switch (*p) { case 'o': prefix = "0"; break; case 'x': prefix = "0x"; break; case 'X': prefix = "0X"; break; case 'b': prefix = "0b"; break; case 'B': prefix = "0B"; break; default: break; } } bin_retry: switch (mrb_type(val)) { #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: val = mrb_flo_to_fixnum(mrb, val); if (mrb_fixnum_p(val)) goto bin_retry; break; #endif case MRB_TT_STRING: val = mrb_str_to_inum(mrb, val, 0, TRUE); goto bin_retry; case MRB_TT_FIXNUM: v = mrb_fixnum(val); break; default: val = mrb_Integer(mrb, val); goto bin_retry; } switch (*p) { case 'o': base = 8; break; case 'x': case 'X': base = 16; break; case 'b': case 'B': base = 2; break; case 'u': case 'd': case 'i': sign = 1; default: base = 10; break; } if (sign) { if (v >= 0) { if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } } else { sc = '-'; width--; } mrb_assert(base == 10); snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v); s = nbuf; if (v < 0) s++; /* skip minus sign */ } else { s = nbuf; if (v < 0) { dots = 1; } switch (base) { case 2: if (v < 0) { val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); } else { val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); } strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); break; case 8: snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v); break; case 16: snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v); break; } if (v < 0) { char d; s = remove_sign_bits(s, base); switch (base) { case 16: d = 'f'; break; case 8: d = '7'; break; case 2: d = '1'; break; default: d = 0; break; } if (d && *s != d) { *--s = d; } } } { size_t size; size = strlen(s); /* PARANOID: assert(size <= MRB_INT_MAX) */ len = (mrb_int)size; } if (*p == 'X') { char *pp = s; int c; while ((c = (int)(unsigned char)*pp) != 0) { *pp = toupper(c); pp++; } } if (prefix && !prefix[1]) { /* octal */ if (dots) { prefix = NULL; } else if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } else if ((flags & FPREC) && (prec > len)) { prefix = NULL; } } else if (len == 1 && *s == '0') { prefix = NULL; } if (prefix) { size_t size; size = strlen(prefix); /* PARANOID: assert(size <= MRB_INT_MAX). * this check is absolutely paranoid. */ width -= (mrb_int)size; } if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) { prec = width; width = 0; } else { if (prec < len) { if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0; prec = len; } width -= prec; } if (!(flags&FMINUS) && width > 0) { FILL(' ', width); width = 0; } if (sc) PUSH(&sc, 1); if (prefix) { int plen = (int)strlen(prefix); PUSH(prefix, plen); } if (dots) { prec -= 2; width -= 2; PUSH("..", 2); } if (prec > len) { CHECK(prec - len); if ((flags & (FMINUS|FPREC)) != FMINUS) { char c = '0'; FILL(c, prec - len); } else if (v < 0) { char c = sign_bits(base, p); FILL(c, prec - len); } } PUSH(s, len); if (width > 0) { FILL(' ', width); } } break; #ifndef MRB_WITHOUT_FLOAT case 'f': case 'g': case 'G': case 'e': case 'E': case 'a': case 'A': { mrb_value val = GETARG(); double fval; mrb_int i; mrb_int need = 6; char fbuf[32]; int frexp_result; fval = mrb_float(mrb_Float(mrb, val)); if (!isfinite(fval)) { const char *expr; const mrb_int elen = 3; char sign = '\0'; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = elen; if (!isnan(fval) && fval < 0.0) sign = '-'; else if (flags & (FPLUS|FSPACE)) sign = (flags & FPLUS) ? '+' : ' '; if (sign) ++need; if ((flags & FWIDTH) && need < width) need = width; if (need < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big"); } FILL(' ', need); if (flags & FMINUS) { if (sign) buf[blen - need--] = sign; memcpy(&buf[blen - need], expr, elen); } else { if (sign) buf[blen - elen - 1] = sign; memcpy(&buf[blen - elen], expr, elen); } break; } fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec); need = 0; if (*p != 'e' && *p != 'E') { i = INT_MIN; frexp(fval, &frexp_result); i = (mrb_int)frexp_result; if (i > 0) need = BIT_DIGITS(i); } if (need > MRB_INT_MAX - ((flags&FPREC) ? prec : 6)) { too_big_width: mrb_raise(mrb, E_ARGUMENT_ERROR, (width > prec ? "width too big" : "prec too big")); } need += (flags&FPREC) ? prec : 6; if ((flags&FWIDTH) && need < width) need = width; if (need > MRB_INT_MAX - 20) { goto too_big_width; } need += 20; CHECK(need); n = snprintf(&buf[blen], need, fbuf, fval); if (n < 0 || n >= need) { mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error"); } blen += n; } break; #endif } flags = FNONE; } sprint_exit: #if 0 /* XXX - We cannot validate the number of arguments if (digit)$ style used. */ if (posarg >= 0 && nextarg < argc) { const char *mesg = "too many arguments for format string"; if (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg); if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%S", mrb_str_new_cstr(mrb, mesg)); } #endif mrb_str_resize(mrb, result, blen); return result; }
int value_to_node(mrb_state *mrb, yaml_document_t *document, mrb_value value) { int node; switch (mrb_type(value)) { case MRB_TT_ARRAY: { mrb_int len = mrb_ary_len(mrb, value); mrb_int i; int ai = mrb_gc_arena_save(mrb); node = yaml_document_add_sequence(document, NULL, YAML_ANY_SEQUENCE_STYLE); for (i = 0; i < len; i++) { mrb_value child = mrb_ary_ref(mrb, value, i); int child_node = value_to_node(mrb, document, child); /* Add the child to the sequence */ yaml_document_append_sequence_item(document, node, child_node); mrb_gc_arena_restore(mrb, ai); } break; } case MRB_TT_HASH: { /* Iterating a list of keys is slow, but it only * requires use of the interface defined in `hash.h`. */ mrb_value keys = mrb_hash_keys(mrb, value); mrb_int len = mrb_ary_len(mrb, keys); mrb_int i; int ai = mrb_gc_arena_save(mrb); node = yaml_document_add_mapping(document, NULL, YAML_ANY_MAPPING_STYLE); for (i = 0; i < len; i++) { mrb_value key = mrb_ary_ref(mrb, keys, i); mrb_value child = mrb_hash_get(mrb, value, key); int key_node = value_to_node(mrb, document, key); int child_node = value_to_node(mrb, document, child); /* Add the key/value pair to the mapping */ yaml_document_append_mapping_pair(document, node, key_node, child_node); mrb_gc_arena_restore(mrb, ai); } break; } default: { if (mrb_nil_p(value)) { /* http://yaml.org/type/null.html Canonical form */ value = mrb_str_new_lit(mrb, "~"); } else { /* Equivalent to `obj = obj#to_s` */ value = mrb_obj_as_string(mrb, value); } /* Fallthrough */ } case MRB_TT_STRING: { yaml_scalar_style_t style = YAML_ANY_SCALAR_STYLE; if (RSTRING_LEN(value) == 0) { /* If the String is empty, it may be reloaded as a nil instead of an * empty string, to avoid that place a quoted string instead */ style = YAML_SINGLE_QUOTED_SCALAR_STYLE; } yaml_char_t *value_chars = (unsigned char *) RSTRING_PTR(value); node = yaml_document_add_scalar(document, NULL, value_chars, RSTRING_LEN(value), style); break; } } return node; }