5 Commits

7 changed files with 27 additions and 6 deletions

View File

@@ -30,6 +30,7 @@ PDL allows defining a protocol using HOPP and TAPE.
| []\<TYPE\> | OTA | * | Array of any type[^1] | []\<TYPE\> | OTA | * | Array of any type[^1]
| Table | KTV | * | Table with undefined schema | Table | KTV | * | Table with undefined schema
| {...} | KTV | * | Table with defined schema | {...} | KTV | * | Table with defined schema
| Any | * | * | Value of an undefined type
[^1]: Excluding SI and SBA. I5 and U5 cannot be used in an array, but String and [^1]: Excluding SI and SBA. I5 and U5 cannot be used in an array, but String and
Buffer are simply forced to use their "long" variant. Buffer are simply forced to use their "long" variant.

View File

@@ -627,7 +627,7 @@ func (this *Generator) generateDecodeValue(typ Type, typeName, valueSource, tagS
case TypeTable: case TypeTable:
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)* // KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
nn, err := this.iprintf( nn, err := this.iprintf(
"nn, err = tape.DecodeAny(decoder, %s, %s)\n", "nn, err = tape.DecodeAnyInto(decoder, %s, %s)\n",
valueSource, tagSource) valueSource, tagSource)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
nn, err = this.generateErrorCheck() nn, err = this.generateErrorCheck()

View File

@@ -116,6 +116,7 @@ func (this *parser) parseType() (Type, error) {
case "String": return TypeString { }, this.Next() case "String": return TypeString { }, this.Next()
case "Buffer": return TypeBuffer { }, this.Next() case "Buffer": return TypeBuffer { }, this.Next()
case "Table": return TypeTable { }, this.Next() case "Table": return TypeTable { }, this.Next()
case "Any": return TypeAny { }, this.Next()
} }
return this.parseTypeNamed() return this.parseTypeNamed()
case TokenLBracket: case TokenLBracket:

View File

@@ -31,6 +31,7 @@ func TestParse(test *testing.T) {
0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } }, 0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } },
}, },
} }
correct.Types["Anything"] = TypeAny { }
test.Log("CORRECT:", &correct) test.Log("CORRECT:", &correct)
got, err := ParseReader("test.pdl", strings.NewReader(` got, err := ParseReader("test.pdl", strings.NewReader(`
@@ -48,6 +49,8 @@ func TestParse(test *testing.T) {
0001 Bio String, 0001 Bio String,
0002 Followers U32, 0002 Followers U32,
} }
Anything Any
`)) `))
if err != nil { test.Fatal(parse.Format(err)) } if err != nil { test.Fatal(parse.Format(err)) }
test.Log("GOT: ", got) test.Log("GOT: ", got)

View File

@@ -99,6 +99,12 @@ func (typ TypeNamed) String() string {
return typ.Name return typ.Name
} }
type TypeAny struct { }
func (typ TypeAny) String() string {
return "Any"
}
func HashType(typ Type) [16]byte { func HashType(typ Type) [16]byte {
// TODO: if we ever want to make the compiler more efficient, this would // TODO: if we ever want to make the compiler more efficient, this would
// be a good place to start, complex string concatenation in a hot path // be a good place to start, complex string concatenation in a hot path

View File

@@ -86,9 +86,9 @@ func EncodeAny(encoder *Encoder, value any, tag Tag) (n int, err error) {
return n, fmt.Errorf("cannot encode type %T", value) return n, fmt.Errorf("cannot encode type %T", value)
} }
// DecodeAny decodes data and places it into destination, which must be a // DecodeAnyInto decodes data and places it into destination, which must be a
// pointer to a supported type. See [EncodeAny] for a list of supported types. // pointer to a supported type. See [EncodeAny] for a list of supported types.
func DecodeAny(decoder *Decoder, destination any, tag Tag) (n int, err error) { func DecodeAnyInto(decoder *Decoder, destination any, tag Tag) (n int, err error) {
reflectDestination := reflect.ValueOf(destination) reflectDestination := reflect.ValueOf(destination)
if reflectDestination.Kind() != reflect.Pointer { if reflectDestination.Kind() != reflect.Pointer {
return n, fmt.Errorf("expected pointer destination, not %v", destination) return n, fmt.Errorf("expected pointer destination, not %v", destination)
@@ -96,6 +96,16 @@ func DecodeAny(decoder *Decoder, destination any, tag Tag) (n int, err error) {
return decodeAny(decoder, reflectDestination.Elem(), tag) return decodeAny(decoder, reflectDestination.Elem(), tag)
} }
// DecodeAny is like [DecodeAnyInto], but it automatically creates the
// destination from the tag and data.
func DecodeAny(decoder *Decoder, tag Tag) (value any, n int, err error) {
destination, err := skeletonValue(decoder, tag)
if err != nil { return nil, n, err }
nn, err := DecodeAnyInto(decoder, destination, tag)
n += nn; if err != nil { return nil, n, err }
return destination, n, err
}
// unknownSlicePlaceholder is inserted by skeletonValue and informs the program // unknownSlicePlaceholder is inserted by skeletonValue and informs the program
// that the destination for the slice needs to be generated based on the item // that the destination for the slice needs to be generated based on the item
// tag in the OTA. // tag in the OTA.

View File

@@ -104,7 +104,7 @@ func TestDecodeWrongType(test *testing.T) {
// integers should only assign to other integers // integers should only assign to other integers
if index > 8 { if index > 8 {
cas := func(destination any) { cas := func(destination any) {
n, err := DecodeAny(NewDecoder(bytes.NewBuffer(data[1:])), destination, Tag(data[0])) n, err := DecodeAnyInto(NewDecoder(bytes.NewBuffer(data[1:])), destination, Tag(data[0]))
if err != nil { test.Fatalf("error: %v | n: %d", err, n) } if err != nil { test.Fatalf("error: %v | n: %d", err, n) }
reflectValue := reflect.ValueOf(destination).Elem() reflectValue := reflect.ValueOf(destination).Elem()
if reflectValue.CanInt() { if reflectValue.CanInt() {
@@ -138,7 +138,7 @@ func TestDecodeWrongType(test *testing.T) {
{ var dest uint64; cas(&dest) } { var dest uint64; cas(&dest) }
} }
arrayCase := func(destination any) { arrayCase := func(destination any) {
n, err := DecodeAny(NewDecoder(bytes.NewBuffer(data[1:])), destination, Tag(data[0])) n, err := DecodeAnyInto(NewDecoder(bytes.NewBuffer(data[1:])), destination, Tag(data[0]))
if err != nil { test.Fatalf("error: %v | n: %d", err, n) } if err != nil { test.Fatalf("error: %v | n: %d", err, n) }
reflectDestination := reflect.ValueOf(destination) reflectDestination := reflect.ValueOf(destination)
reflectValue := reflectDestination.Elem() reflectValue := reflectDestination.Elem()
@@ -256,7 +256,7 @@ func decAny(data []byte) (Tag, any, int, error) {
destination := map[uint16] any { } destination := map[uint16] any { }
tag, err := TagAny(destination) tag, err := TagAny(destination)
if err != nil { return 0, nil, 0, err } if err != nil { return 0, nil, 0, err }
n, err := DecodeAny(NewDecoder(bytes.NewBuffer(data)), &destination, tag) n, err := DecodeAnyInto(NewDecoder(bytes.NewBuffer(data)), &destination, tag)
if err != nil { return 0, nil, n, err } if err != nil { return 0, nil, n, err }
return tag, destination, n, nil return tag, destination, n, nil
} }