diff --git a/generate/generate.go b/generate/generate.go index 7919219..3fd2a73 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -34,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. @@ -585,6 +598,7 @@ func (this *Generator) generateDecodeBranch(hash [16]byte, typ Type) (n int, err nn, err = this.iprintf("for _ = range %s {\n", lengthVar) n += nn; if err != nil { return n, err } this.push() + // FIXME: wrong tag var nn, err = this.iprintf("nn, err = tape.Skim(decoder, tag)") n += nn; if err != nil { return n, err } nn, err = this.generateErrorCheck() @@ -597,6 +611,12 @@ func (this *Generator) generateDecodeBranch(hash [16]byte, typ Type) (n int, err return n, nil } + // FIXME: the tag is validated improperly, the result of + // generateTag means basically nothing here because + // there is no data in the destination, we need another + // function that checks if a tag can be assigned to a + // PDL type + // validate header // TODO: here, validate that length is less than the // max, whatever that is configured to be. the reason we @@ -632,7 +652,102 @@ func (this *Generator) generateDecodeBranch(hash [16]byte, typ Type) (n int, err n += nn; if err != nil { return n, err } case TypeTableDefined: // KTV: ( )* - // TODO + // 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 } + indexVar := this.newTemporaryVar("index") + nn, err = this.iprintf("%s := 0\n", indexVar) + 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 index = range %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)", fieldTagVar) + n += nn; if err != nil { return n, err } + nn, err = this.iprintf("continue") + n += nn; if err != nil { return n, err } + return n, nil + } + + // switch on tag + nn, err = this.iprintf("switch %s {\n", fieldTagVar) + n += nn; if err != nil { return n, err } + this.push() + for _, key := range slices.Sorted(maps.Keys(typ.Fields)) { + field := typ.Fields[key] + nn, err = this.iprintf("case %s:\n", fieldTagVar) + 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) } @@ -831,6 +946,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