From: Vladimir Davydov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: tarantool-patches@dev.tarantool.org
Subject: [Tarantool-patches] [PATCH 11/20] net.box: rewrite response decoder in C
Date: Fri, 23 Jul 2021 14:07:21 +0300 [thread overview]
Message-ID: <6c3509125f24f6276be678d6b8f1ac264631d048.1627024646.git.vdavydov@tarantool.org> (raw)
In-Reply-To: <cover.1627024646.git.vdavydov@tarantool.org>
This patch moves method_decoder table from Lua to C. This is a step
towards rewriting performance-critical parts of net.box in C.
---
src/box/lua/net_box.c | 235 +++++++++++++++++++++++++++++++---------
src/box/lua/net_box.lua | 43 +-------
2 files changed, 189 insertions(+), 89 deletions(-)
diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c
index 49030aabea69..c0c3725e5350 100644
--- a/src/box/lua/net_box.c
+++ b/src/box/lua/net_box.c
@@ -678,6 +678,79 @@ netbox_encode_method(struct lua_State *L)
return 0;
}
+/**
+ * This function handles a response that is supposed to have an empty body
+ * (e.g. IPROTO_PING result). It doesn't decode anything per se. Instead it
+ * simply pushes nil to Lua stack and advances the data ptr to data_end.
+ */
+static void
+netbox_decode_nil(struct lua_State *L, const char **data,
+ const char *data_end, struct tuple_format *format)
+{
+ (void) format;
+ *data = data_end;
+ lua_pushnil(L);
+}
+
+/**
+ * This helper skips a MessagePack map header and IPROTO_DATA key so that
+ * *data points to the actual response content.
+ */
+static void
+netbox_skip_to_data(const char **data)
+{
+ assert(mp_typeof(**data) == MP_MAP);
+ uint32_t map_size = mp_decode_map(data);
+ /* Until 2.0 body has no keys except DATA. */
+ assert(map_size == 1);
+ (void) map_size;
+ uint32_t key = mp_decode_uint(data);
+ assert(key == IPROTO_DATA);
+ (void) key;
+}
+
+/**
+ * Decode Tarantool response body consisting of single
+ * IPROTO_DATA key into Lua table.
+ * @param L Lua stack to push result on.
+ * @param data MessagePack.
+ * @retval Lua table
+ */
+static void
+netbox_decode_table(struct lua_State *L, const char **data,
+ const char *data_end, struct tuple_format *format)
+{
+ (void) data_end;
+ (void) format;
+ netbox_skip_to_data(data);
+ luamp_decode(L, cfg, data);
+}
+
+/**
+ * Same as netbox_decode_table, but only decodes the first element of the
+ * table, skipping the rest. Used to decode index.count() call result.
+ * @param L Lua stack to push result on.
+ * @param data MessagePack.
+ * @retval count or nil.
+ */
+static void
+netbox_decode_value(struct lua_State *L, const char **data,
+ const char *data_end, struct tuple_format *format)
+{
+ (void) data_end;
+ (void) format;
+ netbox_skip_to_data(data);
+ uint32_t count = mp_decode_array(data);
+ for (uint32_t i = 0; i < count; ++i) {
+ if (i == 0)
+ luamp_decode(L, cfg, data);
+ else
+ mp_next(data);
+ }
+ if (count == 0)
+ lua_pushnil(L);
+}
+
/**
* Decode IPROTO_DATA into tuples array.
* @param L Lua stack to push result on.
@@ -704,31 +777,45 @@ netbox_decode_data(struct lua_State *L, const char **data,
/**
* Decode Tarantool response body consisting of single
* IPROTO_DATA key into array of tuples.
- * @param Lua stack[1] Raw MessagePack pointer.
- * @retval Tuples array and position of the body end.
+ * @param L Lua stack to push result on.
+ * @param data MessagePack.
+ * @retval Tuples array.
*/
-static int
-netbox_decode_select(struct lua_State *L)
+static void
+netbox_decode_select(struct lua_State *L, const char **data,
+ const char *data_end, struct tuple_format *format)
{
- uint32_t ctypeid;
- assert(lua_gettop(L) == 3);
- struct tuple_format *format;
- if (lua_type(L, 3) == LUA_TCDATA)
- format = lbox_check_tuple_format(L, 3);
- else
- format = tuple_format_runtime;
- const char *data = *(const char **)luaL_checkcdata(L, 1, &ctypeid);
- assert(mp_typeof(*data) == MP_MAP);
- uint32_t map_size = mp_decode_map(&data);
- /* Until 2.0 body has no keys except DATA. */
- assert(map_size == 1);
- (void) map_size;
- uint32_t key = mp_decode_uint(&data);
- assert(key == IPROTO_DATA);
- (void) key;
- netbox_decode_data(L, &data, format);
- *(const char **)luaL_pushcdata(L, ctypeid) = data;
- return 2;
+ (void) data_end;
+ netbox_skip_to_data(data);
+ netbox_decode_data(L, data, format);
+}
+
+/**
+ * Same as netbox_decode_select, but only decodes the first tuple of the array,
+ * skipping the rest.
+ * @param L Lua stack to push result on.
+ * @param data MessagePack.
+ * @retval Tuple or nil.
+ */
+static void
+netbox_decode_tuple(struct lua_State *L, const char **data,
+ const char *data_end, struct tuple_format *format)
+{
+ (void) data_end;
+ netbox_skip_to_data(data);
+ uint32_t count = mp_decode_array(data);
+ for (uint32_t i = 0; i < count; ++i) {
+ const char *begin = *data;
+ mp_next(data);
+ if (i > 0)
+ continue;
+ struct tuple *tuple = box_tuple_new(format, begin, *data);
+ if (tuple == NULL)
+ luaT_error(L);
+ luaT_pushtuple(L, tuple);
+ }
+ if (count == 0)
+ lua_pushnil(L);
}
/** Decode optional (i.e. may be present in response) metadata fields. */
@@ -847,28 +934,29 @@ netbox_decode_sql_info(struct lua_State *L, const char **data)
}
}
-static int
-netbox_decode_execute(struct lua_State *L)
+static void
+netbox_decode_execute(struct lua_State *L, const char **data,
+ const char *data_end, struct tuple_format *format)
{
- uint32_t ctypeid;
- const char *data = *(const char **)luaL_checkcdata(L, 1, &ctypeid);
- assert(mp_typeof(*data) == MP_MAP);
- uint32_t map_size = mp_decode_map(&data);
+ (void) data_end;
+ (void) format;
+ assert(mp_typeof(**data) == MP_MAP);
+ uint32_t map_size = mp_decode_map(data);
int rows_index = 0, meta_index = 0, info_index = 0;
for (uint32_t i = 0; i < map_size; ++i) {
- uint32_t key = mp_decode_uint(&data);
+ uint32_t key = mp_decode_uint(data);
switch(key) {
case IPROTO_DATA:
- netbox_decode_data(L, &data, tuple_format_runtime);
+ netbox_decode_data(L, data, tuple_format_runtime);
rows_index = i - map_size;
break;
case IPROTO_METADATA:
- netbox_decode_metadata(L, &data);
+ netbox_decode_metadata(L, data);
meta_index = i - map_size;
break;
default:
assert(key == IPROTO_SQL_INFO);
- netbox_decode_sql_info(L, &data);
+ netbox_decode_sql_info(L, data);
info_index = i - map_size;
break;
}
@@ -885,42 +973,41 @@ netbox_decode_execute(struct lua_State *L)
assert(meta_index == 0);
assert(rows_index == 0);
}
- *(const char **)luaL_pushcdata(L, ctypeid) = data;
- return 2;
}
-static int
-netbox_decode_prepare(struct lua_State *L)
+static void
+netbox_decode_prepare(struct lua_State *L, const char **data,
+ const char *data_end, struct tuple_format *format)
{
- uint32_t ctypeid;
- const char *data = *(const char **)luaL_checkcdata(L, 1, &ctypeid);
- assert(mp_typeof(*data) == MP_MAP);
- uint32_t map_size = mp_decode_map(&data);
+ (void) data_end;
+ (void) format;
+ assert(mp_typeof(**data) == MP_MAP);
+ uint32_t map_size = mp_decode_map(data);
int stmt_id_idx = 0, meta_idx = 0, bind_meta_idx = 0,
bind_count_idx = 0;
uint32_t stmt_id = 0;
for (uint32_t i = 0; i < map_size; ++i) {
- uint32_t key = mp_decode_uint(&data);
+ uint32_t key = mp_decode_uint(data);
switch(key) {
case IPROTO_STMT_ID: {
- stmt_id = mp_decode_uint(&data);
+ stmt_id = mp_decode_uint(data);
luaL_pushuint64(L, stmt_id);
stmt_id_idx = i - map_size;
break;
}
case IPROTO_METADATA: {
- netbox_decode_metadata(L, &data);
+ netbox_decode_metadata(L, data);
meta_idx = i - map_size;
break;
}
case IPROTO_BIND_METADATA: {
- netbox_decode_metadata(L, &data);
+ netbox_decode_metadata(L, data);
bind_meta_idx = i - map_size;
break;
}
default: {
assert(key == IPROTO_BIND_COUNT);
- uint32_t bind_count = mp_decode_uint(&data);
+ uint32_t bind_count = mp_decode_uint(data);
luaL_pushuint64(L, bind_count);
bind_count_idx = i - map_size;
break;
@@ -940,8 +1027,58 @@ netbox_decode_prepare(struct lua_State *L)
lua_pushvalue(L, meta_idx - 1);
lua_setfield(L, -2, "metadata");
}
+}
- *(const char **)luaL_pushcdata(L, ctypeid) = data;
+/*
+ * Decodes a response body for the specified method. Pushes the result and the
+ * end of the decoded data to Lua stack.
+ *
+ * Takes the following arguments:
+ * - method: a value from the netbox_method enumeration
+ * - data: pointer to the data to decode (char ptr)
+ * - data_end: pointer to the end of the data (char ptr)
+ * - format: tuple format to use for decoding the body or nil
+ */
+static int
+netbox_decode_method(struct lua_State *L)
+{
+ typedef void (*method_decoder_f)(struct lua_State *L, const char **data,
+ const char *data_end,
+ struct tuple_format *format);
+ static method_decoder_f method_decoder[] = {
+ [NETBOX_PING] = netbox_decode_nil,
+ [NETBOX_CALL_16] = netbox_decode_select,
+ [NETBOX_CALL_17] = netbox_decode_table,
+ [NETBOX_EVAL] = netbox_decode_table,
+ [NETBOX_INSERT] = netbox_decode_tuple,
+ [NETBOX_REPLACE] = netbox_decode_tuple,
+ [NETBOX_DELETE] = netbox_decode_tuple,
+ [NETBOX_UPDATE] = netbox_decode_tuple,
+ [NETBOX_UPSERT] = netbox_decode_nil,
+ [NETBOX_SELECT] = netbox_decode_select,
+ [NETBOX_EXECUTE] = netbox_decode_execute,
+ [NETBOX_PREPARE] = netbox_decode_prepare,
+ [NETBOX_UNPREPARE] = netbox_decode_nil,
+ [NETBOX_GET] = netbox_decode_tuple,
+ [NETBOX_MIN] = netbox_decode_tuple,
+ [NETBOX_MAX] = netbox_decode_tuple,
+ [NETBOX_COUNT] = netbox_decode_value,
+ [NETBOX_INJECT] = netbox_decode_table,
+ };
+ enum netbox_method method = lua_tointeger(L, 1);
+ assert(method < netbox_method_MAX);
+ uint32_t ctypeid;
+ const char *data = *(const char **)luaL_checkcdata(L, 2, &ctypeid);
+ assert(ctypeid == CTID_CHAR_PTR || ctypeid == CTID_CONST_CHAR_PTR);
+ const char *data_end = *(const char **)luaL_checkcdata(L, 3, &ctypeid);
+ assert(ctypeid == CTID_CHAR_PTR || ctypeid == CTID_CONST_CHAR_PTR);
+ struct tuple_format *format;
+ if (!lua_isnil(L, 4))
+ format = lbox_check_tuple_format(L, 4);
+ else
+ format = tuple_format_runtime;
+ method_decoder[method](L, &data, data_end, format);
+ *(const char **)luaL_pushcdata(L, CTID_CONST_CHAR_PTR) = data;
return 2;
}
@@ -952,10 +1089,8 @@ luaopen_net_box(struct lua_State *L)
{ "encode_auth", netbox_encode_auth },
{ "encode_method", netbox_encode_method },
{ "decode_greeting",netbox_decode_greeting },
+ { "decode_method", netbox_decode_method },
{ "communicate", netbox_communicate },
- { "decode_select", netbox_decode_select },
- { "decode_execute", netbox_decode_execute },
- { "decode_prepare", netbox_decode_prepare },
{ NULL, NULL}
};
/* luaL_register_module polutes _G */
diff --git a/src/box/lua/net_box.lua b/src/box/lua/net_box.lua
index bb844184fa01..9e41d6c0844b 100644
--- a/src/box/lua/net_box.lua
+++ b/src/box/lua/net_box.lua
@@ -26,6 +26,7 @@ local communicate = internal.communicate
local encode_auth = internal.encode_auth
local encode_method = internal.encode_method
local decode_greeting = internal.decode_greeting
+local decode_method = internal.decode_method
local TIMEOUT_INFINITY = 500 * 365 * 86400
local VSPACE_ID = 281
@@ -83,21 +84,6 @@ error_unref(struct error *e);
-- utility tables
local is_final_state = {closed = 1, error = 1}
-local function decode_nil(raw_data, raw_data_end) -- luacheck: no unused args
- return nil, raw_data_end
-end
-local function decode_data(raw_data)
- local response, raw_end = decode(raw_data)
- return response[IPROTO_DATA_KEY], raw_end
-end
-local function decode_tuple(raw_data, raw_data_end, format) -- luacheck: no unused args
- local response, raw_end = internal.decode_select(raw_data, nil, format)
- return response[1], raw_end
-end
-local function decode_count(raw_data)
- local response, raw_end = decode(raw_data)
- return response[IPROTO_DATA_KEY][1], raw_end
-end
local function decode_push(raw_data)
local response, raw_end = decode(raw_data)
return response[IPROTO_DATA_KEY][1], raw_end
@@ -111,27 +97,6 @@ local function version_at_least(peer_version_id, major, minor, patch)
return peer_version_id >= version_id(major, minor, patch)
end
-local method_decoder = {
- [M_PING] = decode_nil,
- [M_CALL_16] = internal.decode_select,
- [M_CALL_17] = decode_data,
- [M_EVAL] = decode_data,
- [M_INSERT] = decode_tuple,
- [M_REPLACE] = decode_tuple,
- [M_DELETE] = decode_tuple,
- [M_UPDATE] = decode_tuple,
- [M_UPSERT] = decode_nil,
- [M_SELECT] = internal.decode_select,
- [M_EXECUTE] = internal.decode_execute,
- [M_PREPARE] = internal.decode_prepare,
- [M_UNPREPARE] = decode_nil,
- [M_GET] = decode_tuple,
- [M_MIN] = decode_tuple,
- [M_MAX] = decode_tuple,
- [M_COUNT] = decode_count,
- [M_INJECT] = decode_data,
-}
-
local function decode_error(raw_data)
local ptr = ffi.new('const char *[1]', raw_data)
local err = ffi.C.error_unpack_unsafe(ptr)
@@ -642,9 +607,9 @@ local function create_transport(host, port, user, password, callback,
local real_end
-- Decode xrow.body[DATA] to Lua objects
if status == IPROTO_OK_KEY then
- request.response, real_end =
- method_decoder[request.method](body_rpos, body_end,
- request.format)
+ request.response, real_end = decode_method(request.method,
+ body_rpos, body_end,
+ request.format)
assert(real_end == body_end, "invalid body length")
requests[id] = nil
request.id = nil
--
2.25.1
next prev parent reply other threads:[~2021-07-23 11:13 UTC|newest]
Thread overview: 80+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-07-23 11:07 [Tarantool-patches] [PATCH 00/20] Rewrite performance critical parts of net.box " Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 01/20] net.box: fix console connection breakage when request is discarded Vladimir Davydov via Tarantool-patches
2021-07-28 22:49 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-29 10:40 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 02/20] net.box: wake up wait_result callers " Vladimir Davydov via Tarantool-patches
2021-07-29 10:47 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 03/20] net.box: do not check worker_fiber in request:result, is_ready Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 04/20] net.box: remove decode_push from method_decoder table Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 05/20] net.box: use decode_tuple instead of decode_get Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 06/20] net.box: rename request.ctx to request.format Vladimir Davydov via Tarantool-patches
2021-07-28 22:49 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-29 10:54 ` Vladimir Davydov via Tarantool-patches
2021-07-29 22:39 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-30 8:15 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 07/20] net.box: use integer id instead of method name Vladimir Davydov via Tarantool-patches
2021-07-28 22:50 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-29 11:30 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 08/20] net.box: remove useless encode optimization Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 09/20] net.box: rewrite request encoder in C Vladimir Davydov via Tarantool-patches
2021-07-28 22:51 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-29 14:08 ` Vladimir Davydov via Tarantool-patches
2021-07-29 14:10 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 10/20] lua/utils: make char ptr Lua CTIDs public Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` Vladimir Davydov via Tarantool-patches [this message]
2021-07-27 14:07 ` [Tarantool-patches] [PATCH 11/20] net.box: rewrite response decoder in C Cyrill Gorcunov via Tarantool-patches
2021-07-27 14:14 ` Vladimir Davydov via Tarantool-patches
2021-07-29 22:39 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-30 8:44 ` Vladimir Davydov via Tarantool-patches
2021-07-30 22:12 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-02 7:36 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 12/20] net.box: rewrite error " Vladimir Davydov via Tarantool-patches
2021-07-30 22:13 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-02 8:00 ` Vladimir Davydov via Tarantool-patches
2021-08-02 21:47 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 13/20] net.box: rewrite send_and_recv_{iproto, console} " Vladimir Davydov via Tarantool-patches
2021-08-02 21:49 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-03 15:44 ` Vladimir Davydov via Tarantool-patches
2021-08-03 23:06 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-04 13:56 ` Vladimir Davydov via Tarantool-patches
2021-08-04 21:18 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-05 8:37 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 14/20] net.box: rename netbox_{prepare, encode}_request to {begin, end} Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 15/20] net.box: rewrite request implementation in C Vladimir Davydov via Tarantool-patches
2021-08-02 21:54 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-04 12:30 ` Vladimir Davydov via Tarantool-patches
2021-08-04 15:35 ` Vladimir Davydov via Tarantool-patches
2021-08-04 16:14 ` Vladimir Davydov via Tarantool-patches
2021-08-04 21:20 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-05 12:46 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 16/20] net.box: store next_request_id in C code Vladimir Davydov via Tarantool-patches
2021-08-03 23:06 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-04 16:25 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 17/20] net.box: rewrite console handlers in C Vladimir Davydov via Tarantool-patches
2021-08-03 23:07 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-05 11:53 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 18/20] net.box: rewrite iproto " Vladimir Davydov via Tarantool-patches
2021-08-03 23:08 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-05 11:54 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 19/20] net.box: merge new_id, new_request and encode_method Vladimir Davydov via Tarantool-patches
2021-08-03 23:08 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-05 11:55 ` Vladimir Davydov via Tarantool-patches
2021-07-23 11:07 ` [Tarantool-patches] [PATCH 20/20] net.box: do not create request object in Lua for sync requests Vladimir Davydov via Tarantool-patches
2021-08-03 23:09 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-05 12:23 ` Vladimir Davydov via Tarantool-patches
2021-07-23 12:48 ` [Tarantool-patches] [PATCH 00/20] Rewrite performance critical parts of net.box in C Vladimir Davydov via Tarantool-patches
2021-07-26 7:26 ` Kirill Yukhin via Tarantool-patches
2021-07-27 9:59 ` Vladimir Davydov via Tarantool-patches
2021-07-28 22:51 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-29 11:33 ` Vladimir Davydov via Tarantool-patches
2021-07-29 15:23 ` Vladimir Davydov via Tarantool-patches
2021-07-29 22:38 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-30 10:04 ` Vladimir Davydov via Tarantool-patches
2021-07-29 22:40 ` Vladislav Shpilevoy via Tarantool-patches
2021-07-30 8:16 ` Vladimir Davydov via Tarantool-patches
2021-08-03 23:05 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-04 12:40 ` Vladimir Davydov via Tarantool-patches
2021-08-05 20:59 ` Vladislav Shpilevoy via Tarantool-patches
2021-08-09 11:22 ` Igor Munkin via Tarantool-patches
2021-08-09 11:48 ` Vitaliia Ioffe via Tarantool-patches
2021-08-09 13:56 ` Vladimir Davydov via Tarantool-patches
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6c3509125f24f6276be678d6b8f1ac264631d048.1627024646.git.vdavydov@tarantool.org \
--to=tarantool-patches@dev.tarantool.org \
--cc=vdavydov@tarantool.org \
--subject='Re: [Tarantool-patches] [PATCH 11/20] net.box: rewrite response decoder in C' \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox