From b88ef53b781466532d2059bd538ba9a3e141a6c4 Mon Sep 17 00:00:00 2001 From: engin0223 Date: Sat, 27 Jun 2026 12:58:51 +0300 Subject: [PATCH] fix(thrift): preserve inner object structure for LIST, SET, and MAP to ensure correct round-tripping --- src/core/lib/Thrift.mjs | 23 +++++++++--- tests/operations/tests/Thrift.mjs | 58 +++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/core/lib/Thrift.mjs b/src/core/lib/Thrift.mjs index 28212cf1..64549158 100644 --- a/src/core/lib/Thrift.mjs +++ b/src/core/lib/Thrift.mjs @@ -193,29 +193,34 @@ export function readBinaryType(data, offset, type) { case 13: { // MAP const keyType = data.getUint8(offset++); const valType = data.getUint8(offset++); + const keyTypeName = getBinaryTypeName(keyType); + const valTypeName = getBinaryTypeName(valType); const mapSize = data.getInt32(offset); offset += 4; - value = []; + const elements = []; for (let i = 0; i < mapSize; i++) { const k = readBinaryType(data, offset, keyType); offset = k.offset; const v = readBinaryType(data, offset, valType); offset = v.offset; - value.push({ key: k.value, val: v.value }); + elements.push({ key: k.value, val: v.value }); } + value = { keyType: keyTypeName, valType: valTypeName, elements: elements }; break; } case 14: // SET case 15: { // LIST const elemType = data.getUint8(offset++); + const elemTypeName = getBinaryTypeName(elemType); const listSize = data.getInt32(offset); offset += 4; - value = []; + const elements = []; for (let i = 0; i < listSize; i++) { const elem = readBinaryType(data, offset, elemType); - value.push(elem.value); + elements.push(elem.value); offset = elem.offset; } + value = { elementType: elemTypeName, elements: elements }; break; } default: @@ -298,10 +303,18 @@ export function readCompactType(data, offset, type) { value = data.getInt8(offset++); break; case 4: // I16 + varintParsed = readVarint(data, offset); + value = Number(fromZigZag(varintParsed.value)); + offset = varintParsed.offset; + break; case 5: // I32 + varintParsed = readVarint(data, offset); + value = Number(fromZigZag(varintParsed.value)); + offset = varintParsed.offset; + break; case 6: // I64 varintParsed = readVarint(data, offset); - value = fromZigZag(varintParsed.value); // Decodes ZigZag + value = fromZigZag(varintParsed.value).toString(); offset = varintParsed.offset; break; case 7: // DOUBLE diff --git a/tests/operations/tests/Thrift.mjs b/tests/operations/tests/Thrift.mjs index fd1b29e8..6b47ede3 100644 --- a/tests/operations/tests/Thrift.mjs +++ b/tests/operations/tests/Thrift.mjs @@ -28,7 +28,7 @@ TestRegister.addTests([ // STOP: 00 expectedOutput: "08 00 01 00 00 05 39 0b 00 02 00 00 00 04 54 65 73 74 02 00 03 01 00", recipeConfig: [ - { op: "Thrift Serialize", args: ["TBinaryProtocol"] }, + { op: "Thrift Serialize", args: [] }, { op: "To Hex", args: ["Space", 0] } ] }, @@ -50,7 +50,30 @@ TestRegister.addTests([ // STOP: 00 expectedOutput: "0f 00 01 08 00 00 00 02 00 00 00 0a 00 00 00 14 00", recipeConfig: [ - { op: "Thrift Serialize", args: ["TBinaryProtocol"] }, + { op: "Thrift Serialize", args: [] }, + { op: "To Hex", args: ["Space", 0] } + ] + }, + + { + name: "Thrift Serialize: TBinaryProtocol (Set of BINARY)", + input: JSON.stringify({ + "field_1": { + "type": "SET", + "value": { + "elementType": "BINARY", + "elements": ["a", "b"] + } + } + }), + // Hex breakdown: + // Field 1 (SET): 0e 00 01 + // Element Type (BINARY = 0b), Size (2 = 00 00 00 02) + // Values: 00 00 00 01 61, 00 00 00 01 62 + // STOP: 00 + expectedOutput: "0e 00 01 0b 00 00 00 02 00 00 00 01 61 00 00 00 01 62 00", + recipeConfig: [ + { op: "Thrift Serialize", args: [] }, { op: "To Hex", args: ["Space", 0] } ] }, @@ -77,7 +100,10 @@ TestRegister.addTests([ expectedOutput: formatJson({ "field_1": { "type": "LIST", - "value": [10, 20] + "value": { + "elementType": "I32", + "elements": [10, 20] + } } }), recipeConfig: [ @@ -132,5 +158,31 @@ TestRegister.addTests([ { "op": "Thrift Serialize", "args": [] }, { "op": "To Hex", "args": ["Space", 0] } ] + }, + { + name: "Thrift Deserialize/Serialize: TBinaryProtocol Set round-trip", + // Validates that a TBinaryProtocal SET is successfully deserialised + // and that the result can be Serialized back into the original binary data + input: "0e 00 01 0b 00 00 00 02 00 00 00 01 61 00 00 00 01 62 00", + expectedOutput: "0e 00 01 0b 00 00 00 02 00 00 00 01 61 00 00 00 01 62 00", + recipeConfig: [ + { "op": "From Hex", "args": ["Auto"] }, + { "op": "Thrift Deserialize", "args": ["TBinaryProtocol"] }, + { "op": "Thrift Serialize", "args": [] }, + { "op": "To Hex", "args": ["Space", 0] } + ] + }, + { + name: "Thrift Deserialize/Serialize: TBinaryProtocol Map round-trip", + // Validates that a TBinaryProtocal MAP is successfully deserialised + // and that the result can be Serialized back into the original binary data + input: "0d 00 01 08 0b 00 00 00 01 00 00 00 01 00 00 00 01 61 00", + expectedOutput: "0d 00 01 08 0b 00 00 00 01 00 00 00 01 00 00 00 01 61 00", + recipeConfig: [ + { "op": "From Hex", "args": ["Auto"] }, + { "op": "Thrift Deserialize", "args": ["TBinaryProtocol"] }, + { "op": "Thrift Serialize", "args": [] }, + { "op": "To Hex", "args": ["Space", 0] } + ] } ]);