From c1d2e38840c0596efd10e97ff5a9edaa20d5457e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?=
Date: Wed, 14 Sep 2022 04:29:31 +0200 Subject: [PATCH] xP: generate our own JSON marshallers For non-trivial types, which are expensive to serialize with encoding/json's struct reflection. --- xC-gen-proto-go.awk | 67 +++++++++++++++++++++++++++++++++++++++++---- xP/xP.go | 6 ++-- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/xC-gen-proto-go.awk b/xC-gen-proto-go.awk index 4220ccc..eb08477 100644 --- a/xC-gen-proto-go.awk +++ b/xC-gen-proto-go.awk @@ -206,13 +206,14 @@ function codegen_enum(name, cg, gotype, fields) { print "}" print "" + CodegenIsMarshaler[name] = 1 fields = cg["marshal"] sub(/,\n$/, ":", fields) gsub(/\n/, "\n\t", fields) print "func (v " gotype ") MarshalJSON() ([]byte, error) {" print "\tswitch v {" print indent("case " fields) - print "\t\treturn json.Marshal(v.String())" + print "\t\treturn []byte(`\"` + v.String() + `\"`), nil" print "\t}" print "\treturn json.Marshal(int(v))" print "}" @@ -252,7 +253,50 @@ function codegen_enum(name, cg, gotype, fields) { delete cg[i] } +function codegen_struct_field_marshal(d, cg, camel, f, marshal) { + camel = snaketocamel(d["name"]) + f = "s." camel + + # Complex types are json.Marshalers, there's no need to json.Marshal(&f). + if (!d["isarray"]) { + if (CodegenIsMarshaler[d["type"]]) + marshal = f ".MarshalJSON()" + else + marshal = "json.Marshal(" f ")" + + append(cg, "marshal", + "\tb = append(b, `,\"" decapitalize(camel) "\":`...)\n" \ + "\tif j, err := " marshal "; err != nil {\n" \ + "\t\treturn nil, err\n" \ + "\t} else {\n" \ + "\t\tb = append(b, j...)\n" \ + "\t}\n") + return + } + + if (CodegenIsMarshaler[d["type"]]) + marshal = f "[i].MarshalJSON()" + else + marshal = "json.Marshal(" f "[i])" + + append(cg, "marshal", + "\tb = append(b, `,\"" decapitalize(camel) "\":[`...)\n" \ + "\tfor i := 0; i < len(" f "); i++ {\n" \ + "\t\tif i > 0 {\n" \ + "\t\t\tb = append(b, ',')\n" \ + "\t\t}\n" \ + "\t\tif j, err := " marshal "; err != nil {\n" \ + "\t\t\treturn nil, err\n" \ + "\t\t} else {\n" \ + "\t\t\tb = append(b, j...)\n" \ + "\t\t}\n" \ + "\t}\n" \ + "\tb = append(b, ']')\n") +} + function codegen_struct_field(d, cg, camel, f, serialize, deserialize) { + codegen_struct_field_marshal(d, cg) + camel = snaketocamel(d["name"]) f = "s." camel serialize = CodegenSerialize[d["type"]] @@ -303,6 +347,8 @@ function codegen_struct_field(d, cg, camel, f, serialize, deserialize) { } function codegen_struct_tag(d, cg, camel, f) { + codegen_struct_field_marshal(d, cg) + camel = snaketocamel(d["name"]) f = "s." camel append(cg, "fields", "\t" camel " " CodegenGoType[d["type"]] \ @@ -315,6 +361,16 @@ function codegen_struct(name, cg, gotype) { gotype = PrefixCamel name print "type " gotype " struct {\n" cg["fields"] "}\n" + if (cg["marshal"]) { + CodegenIsMarshaler[name] = 1 + print "func (s *" gotype ") MarshalJSON() ([]byte, error) {" + print "\tb := []byte{}" + print cg["marshal"] "\tb[0] = '{'" + print "\treturn append(b, '}'), nil" + print "}" + print "" + } + if (cg["serialize"]) { print "func (s *" gotype ") AppendTo(data []byte) ([]byte, bool) {" print "\tok := true" @@ -362,15 +418,15 @@ function codegen_union_struct(name, casename, cg, scg, structname, init) { "\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \ "\t\ts := " init "\n" \ "\t\terr = json.Unmarshal(data, &s)\n" \ - "\t\tu.Interface = s\n") + "\t\tu.Interface = &s\n") append(cg, "serialize", - "\tcase " CodegenGoType[structname] ":\n" \ + "\tcase *" CodegenGoType[structname] ":\n" \ indent(sprintf(CodegenSerialize[structname], "union"))) append(cg, "deserialize", "\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \ "\t\ts := " init "\n" \ indent(sprintf(CodegenDeserialize[structname], "s")) \ - "\t\tu.Interface = s\n") + "\t\tu.Interface = &s\n") } function codegen_union(name, cg, gotype, tagfield, tagvar) { @@ -381,8 +437,9 @@ function codegen_union(name, cg, gotype, tagfield, tagvar) { print "" # This cannot be a pointer method, it wouldn't work recursively. + CodegenIsMarshaler[name] = 1 print "func (u " gotype ") MarshalJSON() ([]byte, error) {" - print "\treturn json.Marshal(u.Interface)" + print "\treturn u.Interface.(json.Marshaler).MarshalJSON()" print "}" print "" diff --git a/xP/xP.go b/xP/xP.go index dc5aa5e..2d6e096 100644 --- a/xP/xP.go +++ b/xP/xP.go @@ -50,7 +50,7 @@ func relayReadJSON(conn net.Conn) []byte { return nil } - j, err := json.Marshal(&m) + j, err := m.MarshalJSON() if err != nil { log.Println("Event marshalling failed: " + err.Error()) return nil @@ -126,7 +126,7 @@ func clientWriteJSON(ctx context.Context, ws *websocket.Conn, j []byte) bool { } func clientWriteError(ctx context.Context, ws *websocket.Conn, err error) bool { - j, err := json.Marshal(&RelayEventMessage{ + j, err := (&RelayEventMessage{ EventSeq: 0, Data: RelayEventData{ Interface: RelayEventDataError{ @@ -135,7 +135,7 @@ func clientWriteError(ctx context.Context, ws *websocket.Conn, err error) bool { Error: err.Error(), }, }, - }) + }).MarshalJSON() if err != nil { log.Println("Event marshalling failed: " + err.Error()) return false