|
|
|
|
@@ -6,6 +6,7 @@ import "maps"
|
|
|
|
|
import "math"
|
|
|
|
|
import "slices"
|
|
|
|
|
import "strings"
|
|
|
|
|
import "encoding/hex"
|
|
|
|
|
import "git.tebibyte.media/sashakoshka/hopp/tape"
|
|
|
|
|
|
|
|
|
|
const imports =
|
|
|
|
|
@@ -33,6 +34,19 @@ type Message interface {
|
|
|
|
|
// Method returns the method code of the message.
|
|
|
|
|
Method() uint16
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// canAssign determines if data from the given source tag can be assigned to
|
|
|
|
|
// a Go type represented by destination. It is designed to receive destination
|
|
|
|
|
// values from [generate.Generator.generateCanAssign]. The eventual Go type and
|
|
|
|
|
// the destination tag must come from the same (or hash-equivalent) PDL type.
|
|
|
|
|
func canAssign(destination, source tape.Tag) bool {
|
|
|
|
|
if destination.Is(source) { return true }
|
|
|
|
|
if (destination == tape.SBA || destination == tape.LBA) &&
|
|
|
|
|
(source == tape.SBA || source == tape.LBA) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
// Generator converts protocols into Go code.
|
|
|
|
|
@@ -46,6 +60,14 @@ type Generator struct {
|
|
|
|
|
nestingLevel int
|
|
|
|
|
temporaryVar int
|
|
|
|
|
protocol *Protocol
|
|
|
|
|
|
|
|
|
|
decodeBranchRequestQueue []decodeBranchRequest
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type decodeBranchRequest struct {
|
|
|
|
|
hash [16]byte
|
|
|
|
|
typ Type
|
|
|
|
|
name string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (this *Generator) Generate(protocol *Protocol) (n int, err error) {
|
|
|
|
|
@@ -79,6 +101,14 @@ func (this *Generator) Generate(protocol *Protocol) (n int, err error) {
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// request queue
|
|
|
|
|
for {
|
|
|
|
|
hash, typ, name, ok := this.pullDecodeBranchRequest()
|
|
|
|
|
if !ok { break }
|
|
|
|
|
nn, err := this.generateDecodeBranch(hash, typ, name)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -146,7 +176,25 @@ func (this *Generator) generateTypedef(name string, typ Type) (n int, err error)
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.iprintf("var nn int\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateDecodeValue(typ, "this", "tag", "return n, nil")
|
|
|
|
|
|
|
|
|
|
nn, err = this.iprintf("if !(")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateCanAssign(typ, "tag")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.printf(") {\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.iprintf("nn, err = tape.Skim(decoder, tag)\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("return n, nil\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
|
|
|
|
|
nn, err = this.generateDecodeValue(typ, name, "this", "tag")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("return n, nil\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
@@ -217,20 +265,25 @@ func (this *Generator) generateMessage(method uint16, message Message) (n int, e
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
abort := "return n, nil" // TODO: skip value somehow
|
|
|
|
|
nn, err = this.iprintf("if !tag.Is(")
|
|
|
|
|
|
|
|
|
|
nn, err = this.iprintf("if !(")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateTN(message.Type)
|
|
|
|
|
nn, err = this.generateCanAssign(message.Type, "tag")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.printf(") {\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.iprintf("%s\n", abort)
|
|
|
|
|
nn, err = this.iprintf("nn, err = tape.Skim(decoder, tag)\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("return n, nil\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateDecodeValue(message.Type, "this", "tag", abort)
|
|
|
|
|
|
|
|
|
|
nn, err = this.generateDecodeValue(message.Type, this.resolveMessageName(message.Name), "this", "tag")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("return n, nil\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
@@ -419,7 +472,10 @@ func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource stri
|
|
|
|
|
// - n int
|
|
|
|
|
// - err error
|
|
|
|
|
// - nn int
|
|
|
|
|
func (this *Generator) generateDecodeValue(typ Type, valueSource, tagSource, abort string) (n int, err error) {
|
|
|
|
|
//
|
|
|
|
|
// The typeName paramterer is handled in the way described in the documentation
|
|
|
|
|
// for [Generator.generateDecodeBranch].
|
|
|
|
|
func (this *Generator) generateDecodeValue(typ Type, typeName, valueSource, tagSource string) (n int, err error) {
|
|
|
|
|
switch typ := typ.(type) {
|
|
|
|
|
case TypeInt:
|
|
|
|
|
// SI: (none)
|
|
|
|
|
@@ -485,49 +541,7 @@ func (this *Generator) generateDecodeValue(typ Type, valueSource, tagSource, abo
|
|
|
|
|
}
|
|
|
|
|
case TypeArray:
|
|
|
|
|
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
|
|
|
|
lengthVar := this.newTemporaryVar("length")
|
|
|
|
|
nn, err := this.iprintf("var %s uint64\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf(
|
|
|
|
|
"%s, nn, err = decoder.ReadUintN(int(%s.CN()))\n",
|
|
|
|
|
lengthVar, tagSource)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("*%s = make(", valueSource)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateType(typ)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.printf(", int(%s))\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("var itemTag tape.Tag\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("itemTag, nn, err = decoder.ReadTag()\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("if !itemTag.Is(")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateTN(typ.Element)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf(") {\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.iprintf("%s\n", abort)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("for index := range %s {\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.generateDecodeValue(
|
|
|
|
|
typ.Element,
|
|
|
|
|
fmt.Sprintf("(&(*%s)[index])", valueSource),
|
|
|
|
|
"itemTag", abort)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
nn, err := this.generateDecodeBranchCall(typ, typeName, valueSource, tagSource)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
case TypeTable:
|
|
|
|
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
|
|
|
|
@@ -539,72 +553,7 @@ func (this *Generator) generateDecodeValue(typ Type, valueSource, tagSource, abo
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
case TypeTableDefined:
|
|
|
|
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
|
|
|
|
lengthVar := this.newTemporaryVar("length")
|
|
|
|
|
nn, err := this.iprintf("var %s uint64\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf(
|
|
|
|
|
"%s, nn, err = decoder.ReadUintN(int(%s.CN()))\n",
|
|
|
|
|
lengthVar, tagSource)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("for _ = range %s {\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.iprintf("var key uint16\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("key, nn, err = decoder.ReadUint16()\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("var itemTag tape.Tag\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("itemTag, nn, err = decoder.ReadTag()\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("switch key {\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
keys := slices.Collect(maps.Keys(typ.Fields))
|
|
|
|
|
slices.Sort(keys)
|
|
|
|
|
for _, key := range keys {
|
|
|
|
|
field := typ.Fields[key]
|
|
|
|
|
nn, err = this.iprintf("case 0x%04X:\n", key)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
labelVar := this.newTemporaryVar("label")
|
|
|
|
|
fieldAbort := fmt.Sprintf("goto %s", labelVar) // TODO: skip value somehow
|
|
|
|
|
nn, err = this.iprintf("if !itemTag.Is(")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateTN(field.Type)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.printf(") {\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.iprintf("%s\n", fieldAbort)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("{\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.generateDecodeValue(
|
|
|
|
|
field.Type,
|
|
|
|
|
fmt.Sprintf("(&%s.%s)", valueSource, field.Name),
|
|
|
|
|
"itemTag", fieldAbort)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("%s:;\n", labelVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
}
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
nn, err := this.generateDecodeBranchCall(typ, typeName, valueSource, tagSource)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
case TypeNamed:
|
|
|
|
|
// WHATEVER: [WHATEVER]
|
|
|
|
|
@@ -619,6 +568,263 @@ func (this *Generator) generateDecodeValue(typ Type, valueSource, tagSource, abo
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generateDecodeBranchCall generates code to call an aggregate decoder function,
|
|
|
|
|
// for a specified type. The definition of the function is deferred so no
|
|
|
|
|
// duplicates are created. The function overwrites memory pointed to by the
|
|
|
|
|
// variable (or parenthetical statement) specified by valueSource, and the value
|
|
|
|
|
// will be encoded according to the tag stored in the variable (or parenthetical
|
|
|
|
|
// statement) specified by tagSource. the code generated is a BLOCK and expects
|
|
|
|
|
// these variables to be defined:
|
|
|
|
|
//
|
|
|
|
|
// - decoder *tape.Decoder
|
|
|
|
|
// - n int
|
|
|
|
|
// - err error
|
|
|
|
|
// - nn int
|
|
|
|
|
//
|
|
|
|
|
// The typeName paramterer is handled in the way described in the documentation
|
|
|
|
|
// for [Generator.generateDecodeBranch].
|
|
|
|
|
func (this *Generator) generateDecodeBranchCall(typ Type, typeName, valueSource, tagSource string) (n int, err error) {
|
|
|
|
|
hash := HashType(typ)
|
|
|
|
|
nn, err := this.iprintf(
|
|
|
|
|
"nn, err = %s(%s, decoder, %s)\n",
|
|
|
|
|
this.decodeBranchName(hash, typeName), valueSource, tagSource)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pushDecodeBranchRequest(hash, typ, typeName)
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generateDecodeBranch generates an aggregate decoder function definition for a
|
|
|
|
|
// specified type. It assumes that hash == HashType(typ). If typeName is not
|
|
|
|
|
// empty, it will be used as the type in the argument list instead of the result
|
|
|
|
|
// of [Generator.generateType].
|
|
|
|
|
func (this *Generator) generateDecodeBranch(hash [16]byte, typ Type, typeName string) (n int, err error) {
|
|
|
|
|
nn, err := this.iprintf("\nfunc %s(this *", this.decodeBranchName(hash, typeName))
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
if typeName == "" {
|
|
|
|
|
nn, err = this.generateType(typ)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
} else {
|
|
|
|
|
nn, err = this.print(typeName)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
}
|
|
|
|
|
nn, err = this.printf(", decoder *tape.Decoder, tag tape.Tag) (n int, err error) {\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
|
|
|
|
|
nn, err = this.iprintf("var nn int\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
|
|
|
|
|
switch typ := typ.(type) {
|
|
|
|
|
case TypeArray:
|
|
|
|
|
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
|
|
|
|
// read header
|
|
|
|
|
lengthVar := this.newTemporaryVar("length")
|
|
|
|
|
nn, err := this.iprintf("var %s uint64\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("%s, nn, err = decoder.ReadUintN(int(tag.CN()))\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
elementTagVar := this.newTemporaryVar("elementTag")
|
|
|
|
|
nn, err = this.iprintf("var %s tape.Tag\n", elementTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("%s, nn, err = decoder.ReadTag()\n", elementTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
|
|
|
|
|
// abort macro
|
|
|
|
|
abort := func() (n int, err error) {
|
|
|
|
|
// skim entire array
|
|
|
|
|
nn, err = this.iprintf("for _ = range %s {\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.iprintf("nn, err = tape.Skim(decoder, %s)\n", elementTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("return n, nil\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// validate header
|
|
|
|
|
// TODO: here, validate that length is less than the
|
|
|
|
|
// max, whatever that is configured to be. the reason we
|
|
|
|
|
// want to read it here is that we would have to skip
|
|
|
|
|
// the tag anyway so why not.
|
|
|
|
|
nn, err = this.iprintf("if !(")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateCanAssign(typ.Element, elementTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.printf(") {\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = abort()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
|
|
|
|
|
// decode payloads
|
|
|
|
|
nn, err = this.iprintf("*this = make(")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateType(typ)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.printf(", %s)\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("for index := range int(%s) {\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = this.generateDecodeValue(typ.Element, "", "(&(*this)[index])", elementTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
case TypeTableDefined:
|
|
|
|
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
|
|
|
|
// read header
|
|
|
|
|
lengthVar := this.newTemporaryVar("length")
|
|
|
|
|
nn, err := this.iprintf("var %s uint64\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("%s, nn, err = decoder.ReadUintN(int(tag.CN()))\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
|
|
|
|
|
// validate header
|
|
|
|
|
// TODO: here, validate that length is less than the
|
|
|
|
|
// max, whatever that is configured to be. if not, stop
|
|
|
|
|
// ALL decoding. skimming huge big ass data could cause
|
|
|
|
|
// problems
|
|
|
|
|
|
|
|
|
|
// read fields
|
|
|
|
|
nn, err = this.iprintf("for _ = range int(%s) {\n", lengthVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
// read field header
|
|
|
|
|
fieldKeyVar := this.newTemporaryVar("fieldKey")
|
|
|
|
|
nn, err = this.iprintf("var %s uint16\n", fieldKeyVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("%s, nn, err = decoder.ReadUint16()\n", fieldKeyVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
fieldTagVar := this.newTemporaryVar("fieldTag")
|
|
|
|
|
nn, err = this.iprintf("var %s tape.Tag\n", fieldTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("%s, nn, err = decoder.ReadTag()\n", fieldTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateErrorCheck()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
|
|
|
|
|
// abort field macro
|
|
|
|
|
abortField := func() (n int, err error) {
|
|
|
|
|
nn, err = this.iprintf("tape.Skim(decoder, %s)\n", fieldTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.iprintf("continue\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// switch on tag
|
|
|
|
|
nn, err = this.iprintf("switch %s {\n", fieldKeyVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
for _, key := range slices.Sorted(maps.Keys(typ.Fields)) {
|
|
|
|
|
field := typ.Fields[key]
|
|
|
|
|
nn, err = this.iprintf("case 0x%04X:\n", key)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
|
|
|
|
|
// validate field header
|
|
|
|
|
nn, err = this.iprintf("if !(")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateCanAssign(field.Type, fieldTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.printf(") {\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
nn, err = abortField()
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
|
|
|
|
|
// decode payload
|
|
|
|
|
nn, err = this.generateDecodeValue(
|
|
|
|
|
field.Type, "",
|
|
|
|
|
fmt.Sprintf("(&(this.%s))", field.Name), fieldTagVar)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
}
|
|
|
|
|
nn, err = this.iprintf("default:\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.push()
|
|
|
|
|
abortField()
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
|
|
|
|
|
// TODO once options are implemented, have a set of
|
|
|
|
|
// bools for each non-optional field, and check here
|
|
|
|
|
// that they are all true. a counter will not work
|
|
|
|
|
// because if someone specifies a non-optional field
|
|
|
|
|
// twice, they can neglect to specify another
|
|
|
|
|
// non-optional field and we won't even know because the
|
|
|
|
|
// count will still be even. we shouldn't use a map
|
|
|
|
|
// either because its an allocation and its way more
|
|
|
|
|
// memory than just, like 5 bools (on the stack no less)
|
|
|
|
|
default: return n, fmt.Errorf("unexpected type: %T", typ)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nn, err = this.iprintf("return n, nil\n")
|
|
|
|
|
|
|
|
|
|
this.pop()
|
|
|
|
|
nn, err = this.iprintf("}\n")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (this *Generator) decodeBranchName(hash [16]byte, name string) string {
|
|
|
|
|
if name == "" {
|
|
|
|
|
return fmt.Sprintf("decodeBranch_%s", hex.EncodeToString(hash[:]))
|
|
|
|
|
} else {
|
|
|
|
|
return fmt.Sprintf("decodeBranch_%s_%s", hex.EncodeToString(hash[:]), name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pushDecodeBranchRequest pushes a new branch decode function request to the
|
|
|
|
|
// back of the queue, if it is not already in the queue.
|
|
|
|
|
func (this *Generator) pushDecodeBranchRequest(hash [16]byte, typ Type, name string) {
|
|
|
|
|
for _, item := range this.decodeBranchRequestQueue {
|
|
|
|
|
if item.hash == hash && item.name == name { return }
|
|
|
|
|
}
|
|
|
|
|
this.decodeBranchRequestQueue = append(this.decodeBranchRequestQueue, decodeBranchRequest {
|
|
|
|
|
hash: hash,
|
|
|
|
|
typ: typ,
|
|
|
|
|
name: name,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pullDecodeBranchRequest pulls a branch decode function request from the front
|
|
|
|
|
// of the queue.
|
|
|
|
|
func (this *Generator) pullDecodeBranchRequest() (hash [16]byte, typ Type, name string, ok bool) {
|
|
|
|
|
if len(this.decodeBranchRequestQueue) < 1 {
|
|
|
|
|
return [16]byte { }, nil, "", false
|
|
|
|
|
}
|
|
|
|
|
request := this.decodeBranchRequestQueue[0]
|
|
|
|
|
this.decodeBranchRequestQueue = this.decodeBranchRequestQueue[1:]
|
|
|
|
|
return request.hash, request.typ, request.name, true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (this *Generator) generateErrorCheck() (n int, err error) {
|
|
|
|
|
return this.iprintf("n += nn; if err != nil { return n, err }\n")
|
|
|
|
|
}
|
|
|
|
|
@@ -781,6 +987,19 @@ func (this *Generator) generateTypeTableDefined(typ TypeTableDefined) (n int, er
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generateCanAssign generates an expression which checks if the tag specified
|
|
|
|
|
// by tagSource can be assigned to a Go destination generated from typ. The
|
|
|
|
|
// generated code is INLINE.
|
|
|
|
|
func (this *Generator) generateCanAssign(typ Type, tagSource string) (n int, err error) {
|
|
|
|
|
nn, err := this.printf("canAssign(")
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.generateTN(typ)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
nn, err = this.printf(", %s)", tagSource)
|
|
|
|
|
n += nn; if err != nil { return n, err }
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (this *Generator) validateIntBitSize(size int) error {
|
|
|
|
|
switch size {
|
|
|
|
|
case 5, 8, 16, 32, 64: return nil
|
|
|
|
|
|