Skip to content

Commit

Permalink
Change implementation of tuple_to_string
Browse files Browse the repository at this point in the history
Old variant converted tuple to lua table, then encoded to yaml
New variant encodes tuple to yaml.

Relates #128

Results of benchmarking with:

	old: 1m27.555s
	new: 0m48.738s
	Acceleration 44%
  • Loading branch information
ilmarkov committed Jun 19, 2017
1 parent 6b9a07a commit 1bbfd60
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 5 deletions.
18 changes: 18 additions & 0 deletions src/box/lua/tuple.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#include "lua/msgpack.h" /* luamp_encode_XXX() */
#include "diag.h" /* diag_set() */
#include <small/ibuf.h>
#include <small/region.h>
#include <fiber.h>

#include "box/tuple.h"
#include "box/errcode.h"
Expand Down Expand Up @@ -358,6 +360,21 @@ lbox_tuple_transform(struct lua_State *L)
return 1;
}


static int
lbox_tuple_to_string(struct lua_State *L)
{
struct tuple *tuple = lua_checktuple(L, 1);
char *res = tuple_to_yaml(tuple);
size_t used = region_used(&fiber()->gc);
if (!res) {
luaT_error(L);
}
lua_pushstring(L, res);
region_truncate(&fiber()->gc, used);
return 1;
}

void
luaT_pushtuple(struct lua_State *L, box_tuple_t *tuple)
{
Expand All @@ -376,6 +393,7 @@ luaT_pushtuple(struct lua_State *L, box_tuple_t *tuple)

static const struct luaL_Reg lbox_tuple_meta[] = {
{"__gc", lbox_tuple_gc},
{"tostring", lbox_tuple_to_string},
{"slice", lbox_tuple_slice},
{"transform", lbox_tuple_transform},
{NULL, NULL}
Expand Down
6 changes: 1 addition & 5 deletions src/box/lua/tuple.lua
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,7 @@ ffi.metatype(tuple_t, {
__len = function(tuple)
return builtin.box_tuple_field_count(tuple)
end;
__tostring = function(tuple)
-- Unpack tuple, call yaml.encode, remove yaml header and footer
-- 5 = '---\n\n' (header), -6 = '\n...\n' (footer)
return yaml.encode(methods.totable(tuple)):sub(5, -6)
end;
__tostring = internal.tuple.tostring;
__index = function(tuple, key)
if type(key) == "number" then
return tuple_field(tuple, key)
Expand Down
10 changes: 10 additions & 0 deletions src/box/tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,16 @@ tuple_to_obuf(struct tuple *tuple, struct obuf *buf);
ssize_t
tuple_to_buf(const struct tuple *tuple, char *buf, size_t size);

/**
* Convert tuple to yaml string
*
* \param tuple tuple
* \retval NULL in case of error written in diag
* \retval pointer to string allocated on fiber()->gc region
*/
char *
tuple_to_yaml(const struct tuple *tuple);

#if defined(__cplusplus)
} /* extern "C" */

Expand Down
231 changes: 231 additions & 0 deletions src/box/tuple_convert.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
*/
#include "tuple.h"
#include "iobuf.h"
#include <msgpuck/msgpuck.h>
#include <yaml.h>
#include "third_party/base64.h"
#include <small/region.h>
#include "fiber.h"
#include <trivia/util.h>

int
tuple_to_obuf(struct tuple *tuple, struct obuf *buf)
Expand All @@ -53,3 +59,228 @@ tuple_to_buf(const struct tuple *tuple, char *buf, size_t size)
}
return bsize;
}

int
append_output(void *arg, unsigned char *buf, size_t len)
{
(void) arg;
char *buf_out = region_alloc(&fiber()->gc, len);
if (!buf_out) {
diag_set(OutOfMemory, len , "region_alloc", "append_output");
return 0;
}
memcpy(buf_out, buf, len);
return 1;
}

static int
encode_node(yaml_emitter_t *emitter, const char **data);

static int
encode_table(yaml_emitter_t *emitter, const char **data)
{
yaml_event_t ev;
yaml_mapping_style_t yaml_style = YAML_FLOW_MAPPING_STYLE;
if (!yaml_mapping_start_event_initialize(&ev, NULL, NULL, 0, yaml_style)
|| !yaml_emitter_emit(emitter, &ev)) {
diag_set(SystemError, "failed to init event libyaml");
return 0;
}

uint32_t size = mp_decode_map(data);
for (uint32_t i = 0; i < size; i++) {
if (!encode_node(emitter, data))
return 0;
if (!encode_node(emitter, data))
return 0;
}

if (!yaml_mapping_end_event_initialize(&ev) ||
!yaml_emitter_emit(emitter, &ev)) {
diag_set(SystemError, "failed to end event libyaml");
return 0;
}

return 1;
}


static int
encode_array(yaml_emitter_t *emitter, const char **data)
{
yaml_event_t ev;
yaml_sequence_style_t yaml_style = YAML_FLOW_SEQUENCE_STYLE;
if (!yaml_sequence_start_event_initialize(&ev, NULL, NULL, 0,
yaml_style) ||
!yaml_emitter_emit(emitter, &ev)) {
diag_set(SystemError, "failed to init event libyaml");
return 0;
}

uint32_t size = mp_decode_array(data);
for (uint32_t i = 0; i < size; i++) {
if (!encode_node(emitter, data))
return 0;
}

if (!yaml_sequence_end_event_initialize(&ev) ||
!yaml_emitter_emit(emitter, &ev)) {
diag_set(SystemError, "failed to end event libyaml");
return 0;
}

return 1;
}

static int
encode_node(yaml_emitter_t *emitter, const char **data)
{
size_t len = 0;
const char *str = "";
yaml_char_t *tag = NULL;
yaml_event_t ev;
yaml_scalar_style_t style = YAML_PLAIN_SCALAR_STYLE;
int is_binary = 0;
char buf[FPCONV_G_FMT_BUFSIZE];
char *binary_encode = NULL;
int type = mp_typeof(**data);
switch(type) {
case MP_UINT:
len = snprintf(buf, sizeof(buf) - 1, "%llu",
(unsigned long long) mp_decode_uint(data));
buf[len] = 0;
str = buf;
break;
case MP_INT:
len = snprintf(buf, sizeof(buf) - 1, "%lld",
(long long) mp_decode_int(data));
buf[len] = 0;
str = buf;
break;
case MP_FLOAT:
fpconv_g_fmt(buf, mp_decode_float(data), ENCODE_NUMBER_PRECISION);
str = buf;
len = strlen(buf);
break;
case MP_DOUBLE:
fpconv_g_fmt(buf, mp_decode_double(data), ENCODE_NUMBER_PRECISION);
str = buf;
len = strlen(buf);
break;
case MP_ARRAY:
return encode_array(emitter, data);
case MP_MAP:
return encode_table(emitter, data);
case MP_STR:
len = mp_decode_strl(data);
str = *data;
style = YAML_ANY_SCALAR_STYLE;
if (check_utf8((const yaml_char_t *) str, len)) {
style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
*data += len;
break;
}
case MP_BIN:
if (type != MP_STR) {
len = mp_decode_binl(data);
}
/* Binary or not UTF8 */
is_binary = 1;
binary_encode = (char *) malloc(base64_bufsize(len));
if (!binary_encode) {
diag_set(OutOfMemory, base64_bufsize(len),
"malloc", "encode_node");
return 0;
}
base64_encode(str, len, binary_encode, base64_bufsize(len));
str = binary_encode;
tag = (yaml_char_t *) "binary";
*data += len;
break;
case MP_BOOL:
if (mp_decode_bool(data)) {
str = "true";
len = 4;
} else {
str = "false";
len = 5;
}
break;
case MP_NIL:
mp_decode_nil(data);
style = YAML_PLAIN_SCALAR_STYLE;
str = "null";
len = 4;
break;
case MP_EXT:
mp_next(data);
style = YAML_PLAIN_SCALAR_STYLE;
str = "null";
len = 4;
break;
default:
unreachable();
}
int ret_val = 1;
if (!yaml_scalar_event_initialize(&ev, NULL, tag, (unsigned char *)str,
len, !is_binary, !is_binary, style) ||
!yaml_emitter_emit(emitter, &ev)) {
diag_set(OutOfMemory, len, "yaml_scalar_event_initialize",
"encode_node");
ret_val = 0;
}

if (is_binary) {
free(binary_encode);
}
return ret_val;
}

char *
tuple_to_yaml(const struct tuple *tuple)
{
const char *data = tuple_data(tuple);
yaml_emitter_t emitter;
yaml_event_t ev;

size_t used = region_used(&fiber()->gc);

if (!yaml_emitter_initialize(&emitter)) {
diag_set(SystemError, "failed to init libyaml");
return NULL;
}
yaml_emitter_set_unicode(&emitter, 1);
yaml_emitter_set_indent(&emitter, 2);
yaml_emitter_set_width(&emitter, 2);
yaml_emitter_set_break(&emitter, YAML_LN_BREAK);
yaml_emitter_set_output(&emitter, &append_output, NULL);

if (!yaml_stream_start_event_initialize(&ev, YAML_UTF8_ENCODING) ||
!yaml_emitter_emit(&emitter, &ev) ||
!yaml_document_start_event_initialize(&ev, NULL, NULL, NULL, 1) ||
!yaml_emitter_emit(&emitter, &ev)
) {
diag_set(SystemError, "failed to init event libyaml");
return NULL;
}
if (!encode_node(&emitter, &data)) {
return NULL;
}
if (!yaml_document_end_event_initialize(&ev, 1) ||
!yaml_emitter_emit(&emitter, &ev) ||
!yaml_stream_end_event_initialize(&ev) ||
!yaml_emitter_emit(&emitter, &ev) ||
!yaml_emitter_flush(&emitter)) {
diag_set(SystemError, "failed to end event libyaml");
return NULL;
}
yaml_emitter_delete(&emitter);
size_t total_len = region_used(&fiber()->gc) - used;
char *buf = (char *) region_join(&fiber()->gc, total_len);
if (!buf) {
diag_set(OutOfMemory, total_len, "region_join", "tuple_to_yaml");
return NULL;
}
buf[total_len - 1] = 0;
return buf;
}

0 comments on commit 1bbfd60

Please sign in to comment.