From be9ecf785952ab7515155cb495e8e0da76b93a38 Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Sat, 24 Apr 2021 11:18:18 -0500 Subject: [PATCH] fix: Support repeated string enums. (#284) * fix: Support repeated string enums. * Fix packed encoding. Co-authored-by: kanziw --- .../simple-string-enums/simple-test.ts | 6 ++- integration/simple-string-enums/simple.bin | Bin 483 -> 595 bytes integration/simple-string-enums/simple.proto | 1 + integration/simple-string-enums/simple.ts | 36 +++++++++++++++++- src/main.ts | 15 ++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/integration/simple-string-enums/simple-test.ts b/integration/simple-string-enums/simple-test.ts index 0dfba76cb..5d8afda9c 100644 --- a/integration/simple-string-enums/simple-test.ts +++ b/integration/simple-string-enums/simple-test.ts @@ -2,13 +2,17 @@ import { Simple, StateEnum } from './simple'; describe('simple-string-enums', () => { it('encodes', () => { - const s1: Simple = { name: 'a', state: StateEnum.ON }; + const s1: Simple = { name: 'a', state: StateEnum.ON, states: [StateEnum.ON, StateEnum.OFF] }; const b = Simple.encode(s1).finish(); const s2 = Simple.decode(b); expect(s2).toMatchInlineSnapshot(` Object { "name": "a", "state": "ON", + "states": Array [ + "ON", + "OFF", + ], } `); }); diff --git a/integration/simple-string-enums/simple.bin b/integration/simple-string-enums/simple.bin index 5e2779ea94f37a3d86fd1daf84d44ccaa5828bdd..e3a54581912f1631f05f2d94fcfbd84014acaa0b 100644 GIT binary patch delta 326 zcmX|-OK!q25QgpXBk{zER12g65}>X?t+FU*-~w=g7KueA9uWyr7nOR2<{;g33053| zTXfZNaM=0t|ILiYS8au#T*5#ubi%ItS zkOz6`Z|A$?b`ga7^TqrDY9|>>>)#QN>hsrkf>JU7Cp3)FqVga;VN04Q>9oX&x`UPk z(Qt%_pqP_}rJT+}F|v%xkeVw7%n5|dmw2EE!L;;7lhLNZq%65-ydm1YspBLjQ5-}9 WdwU_Z1QIQ%Ao^WwY*xDYt=T_as3`vc delta 213 zcmX}myA8rH6oujI>j(CUN#PDCkf;$&X2>E)6iA>%K#9Z(cm`%*2YMQMmY~E54&|r+ z6z8el@;aG5B>Zu^Z+DwjduUEgFh4EoSk>{iba#dcipa;;T$uPdUjWt=lqD|!NKGP0 zt4kD6nwWxgT*5>> 3) { @@ -86,6 +93,16 @@ export const Simple = { case 4: message.state = stateEnumFromJSON(reader.int32()); break; + case 5: + if ((tag & 7) === 2) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.states.push(stateEnumFromJSON(reader.int32())); + } + } else { + message.states.push(stateEnumFromJSON(reader.int32())); + } + break; default: reader.skipType(tag & 7); break; @@ -96,6 +113,7 @@ export const Simple = { fromJSON(object: any): Simple { const message = { ...baseSimple } as Simple; + message.states = []; if (object.name !== undefined && object.name !== null) { message.name = String(object.name); } else { @@ -106,6 +124,11 @@ export const Simple = { } else { message.state = StateEnum.UNKNOWN; } + if (object.states !== undefined && object.states !== null) { + for (const e of object.states) { + message.states.push(stateEnumFromJSON(e)); + } + } return message; }, @@ -113,11 +136,17 @@ export const Simple = { const obj: any = {}; message.name !== undefined && (obj.name = message.name); message.state !== undefined && (obj.state = stateEnumToJSON(message.state)); + if (message.states) { + obj.states = message.states.map((e) => stateEnumToJSON(e)); + } else { + obj.states = []; + } return obj; }, fromPartial(object: DeepPartial): Simple { const message = { ...baseSimple } as Simple; + message.states = []; if (object.name !== undefined && object.name !== null) { message.name = object.name; } else { @@ -128,6 +157,11 @@ export const Simple = { } else { message.state = StateEnum.UNKNOWN; } + if (object.states !== undefined && object.states !== null) { + for (const e of object.states) { + message.states.push(e); + } + } return message; }, }; diff --git a/src/main.ts b/src/main.ts index d4b976718..802855779 100644 --- a/src/main.ts +++ b/src/main.ts @@ -793,7 +793,22 @@ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorP ${writeSnippet('v!')}; } `); + } else if (isEnum(field) && options.stringEnums) { + // This is a lot like the `else` clause, but we wrap `fooToNumber` around it. + // Ideally we'd reuse `writeSnippet` here, but `writeSnippet` has the `writer.uint32(tag)` + // embedded inside of it, and we want to drop that so that we can encode it packed + // (i.e. just one tag and multiple values). + const tag = ((field.number << 3) | 2) >>> 0; + const toNumber = getEnumMethod(typeMap, field.typeName, 'ToNumber'); + chunks.push(code` + writer.uint32(${tag}).fork(); + for (const v of message.${fieldName}) { + writer.${toReaderCall(field)}(${toNumber}(v)); + } + writer.ldelim(); + `); } else { + // Ideally we'd reuse `writeSnippet` but it has tagging embedded inside of it. const tag = ((field.number << 3) | 2) >>> 0; chunks.push(code` writer.uint32(${tag}).fork();