Skip to content

Commit 92298a2

Browse files
committed
Add support for round-tripping of buffers in JSON
1 parent 1b91e53 commit 92298a2

File tree

3 files changed

+70
-13
lines changed

3 files changed

+70
-13
lines changed

VM/src/cjson/lua_cjson.cpp

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,22 @@ static void json_append_tagged_quaternion(strbuf_t *json, const float *a, bool t
875875
strbuf_append_string(json, tight ? "\"" : ">\"");
876876
}
877877

878+
static void json_append_buffer(lua_State *l, strbuf_t *json, int lindex)
879+
{
880+
size_t buf_len = 0;
881+
void *data = luaL_checkbuffer(l, lindex, &buf_len);
882+
883+
// Need to use something that will automatically de-alloc in case we hit
884+
// a memory limit appending to the buffer.
885+
std::vector<char> encode_buf((size_t)apr_base64_encode_len(buf_len));
886+
size_t encoded_len = apr_base64_encode_binary(encode_buf.data(), (const uint8_t *)data, buf_len);
887+
if (encoded_len > 0)
888+
{
889+
// exclude the trailing null
890+
strbuf_append_mem(json, encode_buf.data(), encoded_len - 1);
891+
}
892+
}
893+
878894
// Helper to parse a hex character to its value (returns -1 on error)
879895
static inline int hex_char_to_int(char c) {
880896
if (c >= '0' && c <= '9') return c - '0';
@@ -1025,10 +1041,16 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
10251041
json_append_tagged_float(json, lua_tonumber(l, -2), cfg->encode_number_precision);
10261042
break;
10271043
case LUA_TVECTOR: {
1028-
const float *a = lua_tovector(l, -2);
1044+
const float* a = lua_tovector(l, -2);
10291045
json_append_tagged_vector(json, a, cfg->sl_tight_encoding);
10301046
break;
10311047
}
1048+
case LUA_TBUFFER: {
1049+
strbuf_append_string(json, "\"!d");
1050+
json_append_buffer(l, json, -2);
1051+
strbuf_append_char(json, '"');
1052+
break;
1053+
}
10321054
case LUA_TUSERDATA: {
10331055
int tag = lua_userdatatag(l, -2);
10341056
if (tag == UTAG_UUID) {
@@ -1220,18 +1242,10 @@ static int json_append_data(lua_State *l, json_config_t *cfg,
12201242
}
12211243
case LUA_TBUFFER: {
12221244
strbuf_append_char(json, '"');
1223-
size_t buf_len = 0;
1224-
void *data = luaL_checkbuffer(l, -1, &buf_len);
1225-
1226-
// Need to use something that will automatically de-alloc in case we hit
1227-
// a memory limit appending to the buffer.
1228-
std::vector<char> encode_buf((size_t)apr_base64_encode_len(buf_len));
1229-
size_t encoded_len = apr_base64_encode_binary(encode_buf.data(), (const uint8_t *)data, buf_len);
1230-
if (encoded_len > 0)
1231-
{
1232-
// exclude the trailing null
1233-
strbuf_append_mem(json, encode_buf.data(), encoded_len - 1);
1234-
}
1245+
if (cfg->sl_tagged_types)
1246+
strbuf_append_string(json, "!d");
1247+
1248+
json_append_buffer(l, json, -1);
12351249
strbuf_append_char(json, '"');
12361250
break;
12371251
}
@@ -1535,6 +1549,25 @@ static bool json_parse_tagged_string(lua_State *l, const char *str, size_t len)
15351549
luaL_error(l, "malformed tagged boolean: %s", str);
15361550
return false;
15371551

1552+
case 'd':
1553+
{
1554+
// Buffer (data): !d<base64 data>
1555+
1556+
if (payload_len > 0)
1557+
{
1558+
std::vector<uint8_t> decode_buf(payload_len);
1559+
size_t new_len = apr_base64_decode_binary(decode_buf.data(), payload);
1560+
if (new_len > 0)
1561+
{
1562+
void* buf_data = lua_newbuffer(l, new_len);
1563+
memcpy(buf_data, decode_buf.data(), new_len);
1564+
return true;
1565+
}
1566+
}
1567+
lua_newbuffer(l, 0);
1568+
return true;
1569+
}
1570+
15381571
default:
15391572
luaL_error(l, "unknown tag '!%c' in string: %s", tag, str);
15401573
return false; // unreachable

fuzz/corpus/json/buffer_key_value

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"!dAQI=":"something","whatever":"!dAQIDBA=="}

tests/conformance/lljson.lua

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,29 @@ assert(complex_decoded.str == complex.str)
259259
assert(complex_decoded.bang_str == complex.bang_str)
260260
assert(complex_decoded.nested.inner_vec == complex.nested.inner_vec)
261261

262+
-- Tagged buffers: !d with base64 data
263+
local buf = buffer.create(4)
264+
buffer.writeu8(buf, 0, 0x1)
265+
buffer.writeu8(buf, 2, 0xff)
266+
assert(lljson.slencode(buf) == '"!dAQD/AA=="')
267+
268+
-- Round-trip buffer
269+
local buf_decoded = lljson.sldecode('"!dAQD/AA=="')
270+
assert(buffer.len(buf_decoded) == 4)
271+
assert(buffer.readu8(buf_decoded, 0) == 0x1)
272+
assert(buffer.readu8(buf_decoded, 2) == 0xff)
273+
274+
-- Empty buffer
275+
local empty_buf = buffer.create(0)
276+
assert(lljson.slencode(empty_buf) == '"!d"')
277+
local empty_decoded = lljson.sldecode('"!d"')
278+
assert(buffer.len(empty_decoded) == 0)
279+
280+
-- Buffer as object key
281+
local buf_key_table = {[buf] = "data"}
282+
local buf_key_json = lljson.slencode(buf_key_table)
283+
assert(buf_key_json == '{"!dAQD/AA==":"data"}')
284+
262285
-- ============================================
263286
-- Tight encoding mode (second arg = true)
264287
-- ============================================

0 commit comments

Comments
 (0)