Compare commits
20 Commits
5d0b95d59a
...
message-si
| Author | SHA1 | Date | |
|---|---|---|---|
| 12fbfa6293 | |||
| 44fb561758 | |||
| 04c352fad6 | |||
| 0ea7e222cc | |||
| ae79a32309 | |||
| e28ab4dc6b | |||
| 80161b37f7 | |||
| 9d40b81e00 | |||
| 80c7d25c73 | |||
| 743a5d4ae0 | |||
| ea17e354a3 | |||
| 4dc8a30ebd | |||
| 15c5f0b2b8 | |||
| 087b6b6690 | |||
| 77bfc45fea | |||
| de6099fadc | |||
| 0097dbeedd | |||
| 2db7ff88c2 | |||
| 4fd15c79a4 | |||
| d6f6a3485c |
@@ -4,6 +4,7 @@ import "os"
|
|||||||
import "fmt"
|
import "fmt"
|
||||||
import "strings"
|
import "strings"
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
|
import "git.tebibyte.media/sashakoshka/goparse"
|
||||||
import "git.tebibyte.media/sashakoshka/hopp/generate"
|
import "git.tebibyte.media/sashakoshka/hopp/generate"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -18,7 +19,7 @@ func main() {
|
|||||||
input, err := os.Open(source)
|
input, err := os.Open(source)
|
||||||
handleErr(1, err)
|
handleErr(1, err)
|
||||||
defer input.Close()
|
defer input.Close()
|
||||||
protocol, err := generate.ParseReader(input)
|
protocol, err := generate.ParseReader(source, input)
|
||||||
handleErr(1, err)
|
handleErr(1, err)
|
||||||
|
|
||||||
absDestination, err := filepath.Abs(destination)
|
absDestination, err := filepath.Abs(destination)
|
||||||
@@ -30,14 +31,18 @@ func main() {
|
|||||||
|
|
||||||
output, err := os.Create(destination)
|
output, err := os.Create(destination)
|
||||||
handleErr(1, err)
|
handleErr(1, err)
|
||||||
err = protocol.Generate(output, packageName)
|
generator := generate.Generator {
|
||||||
|
Output: output,
|
||||||
|
PackageName: packageName,
|
||||||
|
}
|
||||||
|
_, err = generator.Generate(protocol)
|
||||||
handleErr(1, err)
|
handleErr(1, err)
|
||||||
fmt.Fprintf(os.Stderr, "%s: OK\n", name)
|
fmt.Fprintf(os.Stderr, "%s: OK\n", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleErr(code int, err error) {
|
func handleErr(code int, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], err)
|
fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], parse.Format(err))
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package hopp
|
|||||||
|
|
||||||
import "io"
|
import "io"
|
||||||
import "net"
|
import "net"
|
||||||
// import "time"
|
import "time"
|
||||||
|
|
||||||
const defaultSizeLimit int64 = 1024 * 1024 // 1 megabyte
|
const defaultSizeLimit int64 = 1024 * 1024 // 1 megabyte
|
||||||
|
|
||||||
@@ -23,8 +23,13 @@ type Conn interface {
|
|||||||
// be called in a loop to avoid the connection locking up.
|
// be called in a loop to avoid the connection locking up.
|
||||||
AcceptTrans() (Trans, error)
|
AcceptTrans() (Trans, error)
|
||||||
|
|
||||||
|
// SetDeadline operates is [net.Conn.SetDeadline] but for OpenTrans
|
||||||
|
// and AcceptTrans calls.
|
||||||
|
SetDeadline(t time.Time) error
|
||||||
// SetSizeLimit sets a limit (in bytes) for how large messages can be.
|
// SetSizeLimit sets a limit (in bytes) for how large messages can be.
|
||||||
// By default, this limit is 1 megabyte.
|
// By default, this limit is 1 megabyte. Note that this is only
|
||||||
|
// enforced when sending and receiving byte slices, and it does not
|
||||||
|
// apply to [Trans.SendWriter] or [Trans.ReceiveReader].
|
||||||
SetSizeLimit(limit int64)
|
SetSizeLimit(limit int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,8 +44,6 @@ type Trans interface {
|
|||||||
// ID returns the transaction ID. This must not change, and it must be
|
// ID returns the transaction ID. This must not change, and it must be
|
||||||
// unique within the connection. This method is safe for concurrent use.
|
// unique within the connection. This method is safe for concurrent use.
|
||||||
ID() int64
|
ID() int64
|
||||||
|
|
||||||
// TODO: add methods for setting send and receive deadlines
|
|
||||||
|
|
||||||
// Send sends a message. This method is not safe for concurrent use.
|
// Send sends a message. This method is not safe for concurrent use.
|
||||||
Send(method uint16, data []byte) error
|
Send(method uint16, data []byte) error
|
||||||
@@ -57,4 +60,12 @@ type Trans interface {
|
|||||||
// previously opened through this function will be discarded. This
|
// previously opened through this function will be discarded. This
|
||||||
// method is not safe for concurrent use, and neither is its result.
|
// method is not safe for concurrent use, and neither is its result.
|
||||||
ReceiveReader() (method uint16, data io.Reader, err error)
|
ReceiveReader() (method uint16, data io.Reader, err error)
|
||||||
|
|
||||||
|
// See the documentation for [net.Conn.SetDeadline].
|
||||||
|
SetDeadline(time.Time) error
|
||||||
|
// TODO
|
||||||
|
// // See the documentation for [net.Conn.SetReadDeadline].
|
||||||
|
// SetReadDeadline(t time.Time) error
|
||||||
|
// // See the documentation for [net.Conn.SetWriteDeadline].
|
||||||
|
// SetWriteDeadline(t time.Time) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,13 +317,22 @@ func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource stri
|
|||||||
if typ.Signed {
|
if typ.Signed {
|
||||||
prefix = "WriteInt"
|
prefix = "WriteInt"
|
||||||
}
|
}
|
||||||
nn, err := this.iprintf("nn, err = encoder.%s%d(%s)\n", prefix, typ.Bits, valueSource)
|
nn, err := this.iprintf("nn, err = encoder.%s%d(", prefix, typ.Bits)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.generateType(typ) // TODO: cast like this for
|
||||||
|
// every type
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.printf("(%s))\n", valueSource)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.generateErrorCheck()
|
nn, err = this.generateErrorCheck()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
case TypeFloat:
|
case TypeFloat:
|
||||||
// FP: <value: FloatN>
|
// FP: <value: FloatN>
|
||||||
nn, err := this.iprintf("nn, err = encoder.WriteFloat%d(%s)\n", typ.Bits, valueSource)
|
nn, err := this.iprintf("nn, err = encoder.WriteFloat%d(", typ.Bits)
|
||||||
|
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", valueSource)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.generateErrorCheck()
|
nn, err = this.generateErrorCheck()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
@@ -334,7 +343,14 @@ func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource stri
|
|||||||
case TypeBuffer:
|
case TypeBuffer:
|
||||||
// SBA: <data: U8>*
|
// SBA: <data: U8>*
|
||||||
// LBA: <length: UN> <data: U8>*
|
// LBA: <length: UN> <data: U8>*
|
||||||
nn, err := this.iprintf("if %s.Is(tape.LBA) {\n", tagSource)
|
nn, err := this.iprintf("if len(%s) > tape.MaxStructureLength {\n", valueSource)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
this.push()
|
||||||
|
nn, err = this.iprintf("return n, tape.ErrTooLong\n")
|
||||||
|
this.pop()
|
||||||
|
nn, err = this.iprintf("}\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf("if %s.Is(tape.LBA) {\n", tagSource)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
this.push()
|
this.push()
|
||||||
nn, err = this.iprintf(
|
nn, err = this.iprintf(
|
||||||
@@ -346,14 +362,20 @@ func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource stri
|
|||||||
this.pop()
|
this.pop()
|
||||||
nn, err = this.iprintf("}\n")
|
nn, err = this.iprintf("}\n")
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
|
|
||||||
nn, err = this.iprintf("nn, err = encoder.Write([]byte(%s))\n", valueSource)
|
nn, err = this.iprintf("nn, err = encoder.Write([]byte(%s))\n", valueSource)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.generateErrorCheck()
|
nn, err = this.generateErrorCheck()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
case TypeArray:
|
case TypeArray:
|
||||||
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
||||||
nn, err := this.iprintf(
|
nn, err := this.iprintf("if len(%s) > tape.MaxStructureLength {\n", valueSource)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
this.push()
|
||||||
|
nn, err = this.iprintf("return n, tape.ErrTooLong\n")
|
||||||
|
this.pop()
|
||||||
|
nn, err = this.iprintf("}\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf(
|
||||||
"nn, err = encoder.WriteUintN(uint64(len(%s)), %s.CN())\n",
|
"nn, err = encoder.WriteUintN(uint64(len(%s)), %s.CN())\n",
|
||||||
valueSource, tagSource)
|
valueSource, tagSource)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
@@ -408,7 +430,14 @@ func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource stri
|
|||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
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("if len(%s) > tape.MaxStructureLength {\n", valueSource)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
this.push()
|
||||||
|
nn, err = this.iprintf("return n, tape.ErrTooLong\n")
|
||||||
|
this.pop()
|
||||||
|
nn, err = this.iprintf("}\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf(
|
||||||
"nn, err = tape.EncodeAny(encoder, %s, %s)\n",
|
"nn, err = tape.EncodeAny(encoder, %s, %s)\n",
|
||||||
valueSource, tagSource)
|
valueSource, tagSource)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
@@ -416,7 +445,14 @@ func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource stri
|
|||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
case TypeTableDefined:
|
case TypeTableDefined:
|
||||||
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
||||||
nn, err := this.iprintf(
|
nn, err := this.iprintf("if %d > tape.MaxStructureLength {\n", len(typ.Fields))
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
this.push()
|
||||||
|
nn, err = this.iprintf("return n, tape.ErrTooLong\n")
|
||||||
|
this.pop()
|
||||||
|
nn, err = this.iprintf("}\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf(
|
||||||
"nn, err = encoder.WriteUintN(%d, %s.CN())\n",
|
"nn, err = encoder.WriteUintN(%d, %s.CN())\n",
|
||||||
len(typ.Fields), tagSource)
|
len(typ.Fields), tagSource)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
@@ -482,24 +518,57 @@ func (this *Generator) generateDecodeValue(typ Type, typeName, valueSource, tagS
|
|||||||
// LI/LSI: <value: IntN>
|
// LI/LSI: <value: IntN>
|
||||||
if typ.Bits <= 5 {
|
if typ.Bits <= 5 {
|
||||||
// SI stores the value in the tag
|
// SI stores the value in the tag
|
||||||
nn, err := this.iprintf("*%s = uint8(%s.CN())\n", valueSource, tagSource)
|
if typeName == "" {
|
||||||
n += nn; if err != nil { return n, err }
|
nn, err := this.iprintf("*%s = uint8(%s.CN())\n", valueSource, tagSource)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
} else {
|
||||||
|
nn, err := this.iprintf("*%s = %s(%s.CN())\n", valueSource, typeName, tagSource)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
prefix := "ReadUint"
|
prefix := "ReadUint"
|
||||||
if typ.Signed {
|
if typ.Signed {
|
||||||
prefix = "ReadInt"
|
prefix = "ReadInt"
|
||||||
}
|
}
|
||||||
nn, err := this.iprintf("*%s, nn, err = decoder.%s%d()\n", valueSource, prefix, typ.Bits)
|
destinationVar := this.newTemporaryVar("destination")
|
||||||
|
nn, err := this.iprintf("var %s ", destinationVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.generateType(typ)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.print("\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf("%s, nn, err = decoder.%s%d()\n", destinationVar, prefix, typ.Bits)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.generateErrorCheck()
|
nn, err = this.generateErrorCheck()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
|
if typeName == "" {
|
||||||
|
nn, err := this.iprintf("*%s = %s\n", valueSource, destinationVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
} else {
|
||||||
|
nn, err := this.iprintf("*%s = %s(%s)\n", valueSource, typeName, destinationVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
}
|
||||||
case TypeFloat:
|
case TypeFloat:
|
||||||
// FP: <value: FloatN>
|
// FP: <value: FloatN>
|
||||||
nn, err := this.iprintf("*%s, nn, err = decoder.ReadFloat%d()\n", valueSource, typ.Bits)
|
destinationVar := this.newTemporaryVar("destination")
|
||||||
|
nn, err := this.iprintf("var %s ", destinationVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.generateType(typ)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.print("\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf("%s, nn, err = decoder.ReadFloat%d()\n", destinationVar, typ.Bits)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.generateErrorCheck()
|
nn, err = this.generateErrorCheck()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
|
if typeName == "" {
|
||||||
|
nn, err := this.iprintf("*%s = %s\n", valueSource, destinationVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
} else {
|
||||||
|
nn, err := this.iprintf("*%s = %s(%s)\n", valueSource, typeName, destinationVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
}
|
||||||
case TypeString, TypeBuffer:
|
case TypeString, TypeBuffer:
|
||||||
// SBA: <data: U8>*
|
// SBA: <data: U8>*
|
||||||
// LBA: <length: UN> <data: U8>*
|
// LBA: <length: UN> <data: U8>*
|
||||||
@@ -526,17 +595,29 @@ func (this *Generator) generateDecodeValue(typ Type, typeName, valueSource, tagS
|
|||||||
this.pop()
|
this.pop()
|
||||||
nn, err = this.iprintf("}\n")
|
nn, err = this.iprintf("}\n")
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.iprintf("buffer := make([]byte, int(%s))\n", lengthVar)
|
nn, err = this.iprintf("if %s > uint64(tape.MaxStructureLength) {\n", lengthVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
this.push()
|
||||||
|
nn, err = this.iprintf("return n, tape.ErrTooLong\n")
|
||||||
|
this.pop()
|
||||||
|
nn, err = this.iprintf("}\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf("buffer := make([]byte, %s)\n", lengthVar)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.iprintf("nn, err = decoder.Read(buffer)\n")
|
nn, err = this.iprintf("nn, err = decoder.Read(buffer)\n")
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.generateErrorCheck()
|
nn, err = this.generateErrorCheck()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
if _, ok := typ.(TypeString); ok {
|
if typeName == "" {
|
||||||
nn, err = this.iprintf("*%s = string(buffer)\n", valueSource)
|
if _, ok := typ.(TypeString); ok {
|
||||||
n += nn; if err != nil { return n, err }
|
nn, err = this.iprintf("*%s = string(buffer)\n", valueSource)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
} else {
|
||||||
|
nn, err = this.iprintf("*%s = buffer\n", valueSource)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
nn, err = this.iprintf("*%s = buffer\n", valueSource)
|
nn, err = this.iprintf("*%s = %s(buffer)\n", valueSource, typeName)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
}
|
}
|
||||||
case TypeArray:
|
case TypeArray:
|
||||||
@@ -623,6 +704,13 @@ func (this *Generator) generateDecodeBranch(hash [16]byte, typ Type, typeName st
|
|||||||
lengthVar := this.newTemporaryVar("length")
|
lengthVar := this.newTemporaryVar("length")
|
||||||
nn, err := this.iprintf("var %s uint64\n", lengthVar)
|
nn, err := this.iprintf("var %s uint64\n", lengthVar)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf("if %s > uint64(tape.MaxStructureLength) {\n", lengthVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
this.push()
|
||||||
|
nn, err = this.iprintf("return n, tape.ErrTooLong\n")
|
||||||
|
this.pop()
|
||||||
|
nn, err = this.iprintf("}\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.iprintf("%s, nn, err = decoder.ReadUintN(int(tag.CN()))\n", lengthVar)
|
nn, err = this.iprintf("%s, nn, err = decoder.ReadUintN(int(tag.CN()))\n", lengthVar)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.generateErrorCheck()
|
nn, err = this.generateErrorCheck()
|
||||||
@@ -692,6 +780,13 @@ func (this *Generator) generateDecodeBranch(hash [16]byte, typ Type, typeName st
|
|||||||
lengthVar := this.newTemporaryVar("length")
|
lengthVar := this.newTemporaryVar("length")
|
||||||
nn, err := this.iprintf("var %s uint64\n", lengthVar)
|
nn, err := this.iprintf("var %s uint64\n", lengthVar)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = this.iprintf("if %s > uint64(tape.MaxStructureLength) {\n", lengthVar)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
this.push()
|
||||||
|
nn, err = this.iprintf("return n, tape.ErrTooLong\n")
|
||||||
|
this.pop()
|
||||||
|
nn, err = this.iprintf("}\n")
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.iprintf("%s, nn, err = decoder.ReadUintN(int(tag.CN()))\n", lengthVar)
|
nn, err = this.iprintf("%s, nn, err = decoder.ReadUintN(int(tag.CN()))\n", lengthVar)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
nn, err = this.generateErrorCheck()
|
nn, err = this.generateErrorCheck()
|
||||||
@@ -704,7 +799,7 @@ func (this *Generator) generateDecodeBranch(hash [16]byte, typ Type, typeName st
|
|||||||
// problems
|
// problems
|
||||||
|
|
||||||
// read fields
|
// read fields
|
||||||
nn, err = this.iprintf("for _ = range int(%s) {\n", lengthVar)
|
nn, err = this.iprintf("for _ = range %s {\n", lengthVar)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
this.push()
|
this.push()
|
||||||
// read field header
|
// read field header
|
||||||
@@ -848,10 +943,10 @@ func (this *Generator) generateTag(typ Type, source string) (n int, err error) {
|
|||||||
nn, err := this.printf("tape.FP.WithCN(%d)", bitsToCN(typ.Bits))
|
nn, err := this.printf("tape.FP.WithCN(%d)", bitsToCN(typ.Bits))
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
case TypeString:
|
case TypeString:
|
||||||
nn, err := this.printf("tape.StringTag(%s)", source)
|
nn, err := this.printf("tape.StringTag(string(%s))", source)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
case TypeBuffer:
|
case TypeBuffer:
|
||||||
nn, err := this.printf("tape.BufferTag(%s)", source)
|
nn, err := this.printf("tape.BufferTag([]byte(%s))", source)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
case TypeArray:
|
case TypeArray:
|
||||||
nn, err := this.printf("tape.OTA.WithCN(tape.IntBytes(uint64(len(%s))))", source)
|
nn, err := this.printf("tape.OTA.WithCN(tape.IntBytes(uint64(len(%s))))", source)
|
||||||
|
|||||||
@@ -71,17 +71,16 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateRunEncode(test *testing.T) {
|
func TestGenerateRunEncodeDecode(test *testing.T) {
|
||||||
testGenerateRun(test, &exampleProtocol, "encode", `
|
testGenerateRun(test, &exampleProtocol, "encode-decode", `
|
||||||
// imports
|
// imports
|
||||||
`, `
|
`, `
|
||||||
// test case
|
|
||||||
log.Println("MessageConnect")
|
log.Println("MessageConnect")
|
||||||
messageConnect := MessageConnect {
|
messageConnect := MessageConnect {
|
||||||
Name: "rarity",
|
Name: "rarity",
|
||||||
Password: "gems",
|
Password: "gems",
|
||||||
}
|
}
|
||||||
testEncode(
|
testEncodeDecode(
|
||||||
&messageConnect,
|
&messageConnect,
|
||||||
tu.S(0xE1, 0x02).AddVar(
|
tu.S(0xE1, 0x02).AddVar(
|
||||||
[]byte { 0x00, 0x00, 0x86, 'r', 'a', 'r', 'i', 't', 'y' },
|
[]byte { 0x00, 0x00, 0x86, 'r', 'a', 'r', 'i', 't', 'y' },
|
||||||
@@ -107,7 +106,7 @@ func TestGenerateRunEncode(test *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testEncode(
|
testEncodeDecode(
|
||||||
&messageUserList,
|
&messageUserList,
|
||||||
tu.S(0xE1, 0x01, 0x00, 0x00,
|
tu.S(0xE1, 0x01, 0x00, 0x00,
|
||||||
0xC1, 0x03, 0xE1,
|
0xC1, 0x03, 0xE1,
|
||||||
@@ -129,11 +128,11 @@ func TestGenerateRunEncode(test *testing.T) {
|
|||||||
messagePulse := MessagePulse {
|
messagePulse := MessagePulse {
|
||||||
Index: 9,
|
Index: 9,
|
||||||
Offset: -0x3521,
|
Offset: -0x3521,
|
||||||
X: 45.389,
|
X: 45.375,
|
||||||
Y: 294.1,
|
Y: 294.1,
|
||||||
Z: 384729384.234892034,
|
Z: 384729384.234892034,
|
||||||
}
|
}
|
||||||
testEncode(
|
testEncodeDecode(
|
||||||
&messagePulse,
|
&messagePulse,
|
||||||
tu.S(0xE1, 0x05).AddVar(
|
tu.S(0xE1, 0x05).AddVar(
|
||||||
[]byte { 0x00, 0x00, 0x09 },
|
[]byte { 0x00, 0x00, 0x09 },
|
||||||
@@ -154,7 +153,7 @@ func TestGenerateRunEncode(test *testing.T) {
|
|||||||
uint8s(6),
|
uint8s(6),
|
||||||
uint8s(35),
|
uint8s(35),
|
||||||
}
|
}
|
||||||
testEncode(
|
testEncodeDecode(
|
||||||
&messageNestedArray,
|
&messageNestedArray,
|
||||||
tu.S(0xC1, 0x02, 0xC1,
|
tu.S(0xC1, 0x02, 0xC1,
|
||||||
0x06, 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
0x06, 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
||||||
@@ -180,7 +179,7 @@ func TestGenerateRunEncode(test *testing.T) {
|
|||||||
NI32: -0x10E134C9,
|
NI32: -0x10E134C9,
|
||||||
NI64: -0x639109BC10E134C9,
|
NI64: -0x639109BC10E134C9,
|
||||||
}
|
}
|
||||||
testEncode(
|
testEncodeDecode(
|
||||||
&messageIntegers,
|
&messageIntegers,
|
||||||
tu.S(0xE1, 13).AddVar(
|
tu.S(0xE1, 13).AddVar(
|
||||||
[]byte { 0x00, 0x00, 0x13 },
|
[]byte { 0x00, 0x00, 0x13 },
|
||||||
@@ -200,130 +199,183 @@ func TestGenerateRunEncode(test *testing.T) {
|
|||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateRunDecode(test *testing.T) {
|
func TestGenerateRunDecodeWrongType(test *testing.T) {
|
||||||
testGenerateRun(test, &exampleProtocol, "decode", `
|
protocol := defaultProtocol()
|
||||||
|
protocol.Messages[0x0000] = Message {
|
||||||
|
Name: "Uint5",
|
||||||
|
Type: TypeInt { Bits: 5 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0001] = Message {
|
||||||
|
Name: "Uint8",
|
||||||
|
Type: TypeInt { Bits: 8 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0002] = Message {
|
||||||
|
Name: "Uint16",
|
||||||
|
Type: TypeInt { Bits: 16 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0003] = Message {
|
||||||
|
Name: "Uint32",
|
||||||
|
Type: TypeInt { Bits: 32 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0004] = Message {
|
||||||
|
Name: "Uint64",
|
||||||
|
Type: TypeInt { Bits: 64 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0005] = Message {
|
||||||
|
Name: "Int8",
|
||||||
|
Type: TypeInt { Bits: 8 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0006] = Message {
|
||||||
|
Name: "Int16",
|
||||||
|
Type: TypeInt { Bits: 16 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0007] = Message {
|
||||||
|
Name: "Int32",
|
||||||
|
Type: TypeInt { Bits: 32 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0008] = Message {
|
||||||
|
Name: "Int64",
|
||||||
|
Type: TypeInt { Bits: 64 },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x0009] = Message {
|
||||||
|
Name: "String",
|
||||||
|
Type: TypeString { },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x000A] = Message {
|
||||||
|
Name: "Buffer",
|
||||||
|
Type: TypeBuffer { },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x000B] = Message {
|
||||||
|
Name: "StringArray",
|
||||||
|
Type: TypeArray { Element: TypeString { } },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x000C] = Message {
|
||||||
|
Name: "Table",
|
||||||
|
Type: TypeTable { },
|
||||||
|
}
|
||||||
|
protocol.Messages[0x000D] = Message {
|
||||||
|
Name: "TableDefined",
|
||||||
|
Type: TypeTableDefined {
|
||||||
|
Fields: map[uint16] Field {
|
||||||
|
0x0000: Field { Name: "Name", Type: TypeString { } },
|
||||||
|
0x0001: Field { Name: "Password", Type: TypeString { } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testGenerateRun(test, &protocol, "decode-wrong-type", `
|
||||||
// imports
|
// imports
|
||||||
`, `
|
`, `
|
||||||
log.Println("MessageConnect")
|
datas := [][]byte {
|
||||||
messageConnect := MessageConnect {
|
/* int8 */ []byte { byte(tape.LSI.WithCN(0)), 0x45 },
|
||||||
Name: "rarity",
|
/* int16 */ []byte { byte(tape.LSI.WithCN(1)), 0x45, 0x67 },
|
||||||
Password: "gems",
|
/* int32 */ []byte { byte(tape.LSI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB },
|
||||||
}
|
/* int64 */ []byte { byte(tape.LSI.WithCN(7)), 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 },
|
||||||
testDecode(
|
/* uint5 */ []byte { byte(tape.SI.WithCN(12)) },
|
||||||
&messageConnect,
|
/* uint8 */ []byte { byte(tape.LI.WithCN(0)), 0x45 },
|
||||||
tu.S(0xE1, 0x02).AddVar(
|
/* uint16 */ []byte { byte(tape.LI.WithCN(1)), 0x45, 0x67 },
|
||||||
[]byte { 0x00, 0x00, 0x86, 'r', 'a', 'r', 'i', 't', 'y' },
|
/* uint32 */ []byte { byte(tape.LI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB },
|
||||||
[]byte { 0x00, 0x01, 0x84, 'g', 'e', 'm', 's' },
|
/* uint64 */ []byte { byte(tape.LI.WithCN(7)), 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 },
|
||||||
))
|
/* string */ []byte { byte(tape.SBA.WithCN(7)), 'p', 'u', 'p', 'e', 'v', 'e', 'r' },
|
||||||
log.Println("MessageUserList")
|
/* []byte */ []byte { byte(tape.SBA.WithCN(5)), 'b', 'l', 'a', 'r', 'g' },
|
||||||
messageUserList := MessageUserList {
|
/* []string */ []byte {
|
||||||
Users: []User {
|
byte(tape.OTA.WithCN(0)), 2, byte(tape.LBA.WithCN(0)),
|
||||||
User {
|
0x08, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23,
|
||||||
Name: "rarity",
|
0x05, 0x11, 0x11, 0x11, 0x11, 0x11,
|
||||||
Bio: "asdjads",
|
},
|
||||||
Followers: 0x324,
|
/* map[uint16] any */ []byte {
|
||||||
},
|
byte(tape.KTV.WithCN(0)), 2,
|
||||||
User {
|
0x02, 0x23, byte(tape.LSI.WithCN(1)), 0x45, 0x67,
|
||||||
Name: "deez nuts",
|
0x02, 0x23, byte(tape.LI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB,
|
||||||
Bio: "logy",
|
|
||||||
Followers: 0x8000,
|
|
||||||
},
|
|
||||||
User {
|
|
||||||
Name: "creekflow",
|
|
||||||
Bio: "im creekflow",
|
|
||||||
Followers: 0x3894,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testEncode(
|
|
||||||
&messageUserList,
|
|
||||||
tu.S(0xE1, 0x01, 0x00, 0x00,
|
for index, data := range datas {
|
||||||
0xC1, 0x03, 0xE1,
|
log.Printf("data %2d %v [%s]", index, tape.Tag(data[0]), tu.HexBytes(data[1:]))
|
||||||
).Add(0x03).AddVar(
|
// integers should only assign to other integers
|
||||||
[]byte { 0x00, 0x00, 0x86, 'r', 'a', 'r', 'i', 't', 'y' },
|
if index > 8 {
|
||||||
[]byte { 0x00, 0x01, 0x87, 'a', 's', 'd', 'j', 'a', 'd', 's' },
|
cas := func(destination Message) {
|
||||||
[]byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x03, 0x24 },
|
n, err := destination.Decode(tape.NewDecoder(bytes.NewBuffer(data)))
|
||||||
).Add(0x03).AddVar(
|
if err != nil { log.Fatalf("error: %v | n: %d", err, n) }
|
||||||
[]byte { 0x00, 0x00, 0x89, 'd', 'e', 'e', 'z', ' ', 'n', 'u', 't', 's' },
|
reflectValue := reflect.ValueOf(destination).Elem()
|
||||||
[]byte { 0x00, 0x01, 0x84, 'l', 'o', 'g', 'y' },
|
if reflectValue.CanInt() {
|
||||||
[]byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x80, 0x00 },
|
if reflectValue.Int() != 0 {
|
||||||
).Add(0x03).AddVar(
|
log.Fatalf(
|
||||||
[]byte { 0x00, 0x00, 0x89, 'c', 'r', 'e', 'e', 'k', 'f', 'l', 'o', 'w' },
|
"destination not zero: %v",
|
||||||
[]byte { 0x00, 0x01, 0x8C, 'i', 'm', ' ', 'c', 'r', 'e', 'e', 'k', 'f',
|
reflectValue.Elem().Interface())
|
||||||
'l', 'o', 'w' },
|
}
|
||||||
[]byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x38, 0x94 },
|
} else {
|
||||||
))
|
if reflectValue.Uint() != 0 {
|
||||||
log.Println("MessagePulse")
|
log.Fatalf(
|
||||||
messagePulse := MessagePulse {
|
"destination not zero: %v",
|
||||||
Index: 9,
|
reflectValue.Elem().Interface())
|
||||||
Offset: -0x3521,
|
}
|
||||||
X: 45.389,
|
}
|
||||||
Y: 294.1,
|
if n != len(data) {
|
||||||
Z: 384729384.234892034,
|
log.Fatalf("n not equal: %d != %d", n, len(data))
|
||||||
}
|
}
|
||||||
testEncode(
|
}
|
||||||
&messagePulse,
|
log.Println("- MessageInt8")
|
||||||
tu.S(0xE1, 0x05).AddVar(
|
{ var dest MessageInt8; cas(&dest) }
|
||||||
[]byte { 0x00, 0x00, 0x09 },
|
log.Println("- MessageInt16")
|
||||||
[]byte { 0x00, 0x01, 0x41, 0xCA, 0xDF },
|
{ var dest MessageInt16; cas(&dest) }
|
||||||
[]byte { 0x00, 0x02, 0x61, 0x51, 0xAC },
|
log.Println("- MessageInt32")
|
||||||
[]byte { 0x00, 0x03, 0x63, 0x43, 0x93, 0x0C, 0xCD },
|
{ var dest MessageInt32; cas(&dest) }
|
||||||
[]byte { 0x00, 0x04, 0x67, 0x41, 0xB6, 0xEE, 0x81, 0x28, 0x3C, 0x21, 0xE2 },
|
log.Println("- MessageInt64")
|
||||||
))
|
{ var dest MessageInt64; cas(&dest) }
|
||||||
log.Println("MessageNestedArray")
|
log.Println("- MessageUint8")
|
||||||
uint8s := func(n int) []uint8 {
|
{ var dest MessageUint8; cas(&dest) }
|
||||||
array := make([]uint8, n)
|
log.Println("- MessageUint16")
|
||||||
for index := range array {
|
{ var dest MessageUint16; cas(&dest) }
|
||||||
array[index] = uint8(index + 1) | 0xF0
|
log.Println("- MessageUint32")
|
||||||
|
{ var dest MessageUint32; cas(&dest) }
|
||||||
|
log.Println("- MessageUint64")
|
||||||
|
{ var dest MessageUint64; cas(&dest) }
|
||||||
|
}
|
||||||
|
arrayCase := func(destination Message) {
|
||||||
|
n, err := destination.Decode(tape.NewDecoder(bytes.NewBuffer(data)),)
|
||||||
|
if err != nil { log.Fatalf("error: %v | n: %d", err, n) }
|
||||||
|
reflectDestination := reflect.ValueOf(destination)
|
||||||
|
reflectValue := reflectDestination.Elem()
|
||||||
|
if reflectValue.Len() != 0 {
|
||||||
|
log.Fatalf("len(destination) not zero: %v", reflectValue.Interface())
|
||||||
|
}
|
||||||
|
if n != len(data) {
|
||||||
|
log.Fatalf("n not equal: %d != %d", n, len(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
anyCase := func(destination Message) {
|
||||||
|
n, err := destination.Decode(tape.NewDecoder(bytes.NewBuffer(data)),)
|
||||||
|
if err != nil { log.Fatalf("error: %v | n: %d", err, n) }
|
||||||
|
reflectDestination := reflect.ValueOf(destination)
|
||||||
|
reflectValue := reflectDestination.Elem()
|
||||||
|
if reflectValue == reflect.Zero(reflectValue.Type()) {
|
||||||
|
log.Fatalf("len(destination) not zero: %v", reflectValue.Interface())
|
||||||
|
}
|
||||||
|
if n != len(data) {
|
||||||
|
log.Fatalf("n not equal: %d != %d", n, len(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SBA/LBA types should only assign to other SBA/LBA types
|
||||||
|
if index != 9 && index != 10 {
|
||||||
|
log.Println("- MessageString")
|
||||||
|
{ var dest MessageString; arrayCase(&dest) }
|
||||||
|
log.Println("- MessageBuffer")
|
||||||
|
{ var dest MessageBuffer; arrayCase(&dest) }
|
||||||
|
}
|
||||||
|
// arrays should only assign to other arrays
|
||||||
|
if index != 11 {
|
||||||
|
log.Println("- MessageStringArray")
|
||||||
|
{ var dest MessageStringArray; arrayCase(&dest) }
|
||||||
|
}
|
||||||
|
// tables should only assign to other tables
|
||||||
|
if index != 12 {
|
||||||
|
log.Println("- MessageTable")
|
||||||
|
{ var dest = make(MessageTable); arrayCase(&dest) }
|
||||||
|
log.Println("- MessageTableDefined")
|
||||||
|
{ var dest MessageTableDefined; anyCase(&dest) }
|
||||||
}
|
}
|
||||||
return array
|
|
||||||
}
|
}
|
||||||
messageNestedArray := MessageNestedArray {
|
|
||||||
uint8s(6),
|
|
||||||
uint8s(35),
|
|
||||||
}
|
|
||||||
testEncode(
|
|
||||||
&messageNestedArray,
|
|
||||||
tu.S(0xC1, 0x02, 0xC1,
|
|
||||||
0x06, 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
|
||||||
35, 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
|
||||||
0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC,
|
|
||||||
0xFD, 0xFE, 0xFF, 0xF0, 0xF1, 0xF2,
|
|
||||||
0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
|
|
||||||
0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE,
|
|
||||||
0xFF, 0xF0, 0xF1, 0xF2, 0xF3))
|
|
||||||
log.Println("MessageIntegers")
|
|
||||||
messageIntegers := MessageIntegers {
|
|
||||||
U5: 0x13,
|
|
||||||
U8: 0xC9,
|
|
||||||
U16: 0x34C9,
|
|
||||||
U32: 0x10E134C9,
|
|
||||||
U64: 0x639109BC10E134C9,
|
|
||||||
I8: 0x35,
|
|
||||||
I16: 0x34C9,
|
|
||||||
I32: 0x10E134C9,
|
|
||||||
I64: 0x639109BC10E134C9,
|
|
||||||
NI8: -0x35,
|
|
||||||
NI16: -0x34C9,
|
|
||||||
NI32: -0x10E134C9,
|
|
||||||
NI64: -0x639109BC10E134C9,
|
|
||||||
}
|
|
||||||
testEncode(
|
|
||||||
&messageIntegers,
|
|
||||||
tu.S(0xE1, 13).AddVar(
|
|
||||||
[]byte { 0x00, 0x00, 0x13 },
|
|
||||||
[]byte { 0x00, 0x01, 0x20, 0xC9 },
|
|
||||||
[]byte { 0x00, 0x02, 0x21, 0x34, 0xC9 },
|
|
||||||
[]byte { 0x00, 0x03, 0x23, 0x10, 0xE1, 0x34, 0xC9 },
|
|
||||||
[]byte { 0x00, 0x04, 0x27, 0x63, 0x91, 0x09, 0xBC, 0x10, 0xE1, 0x34, 0xC9 },
|
|
||||||
[]byte { 0x00, 0x06, 0x40, 0x35 },
|
|
||||||
[]byte { 0x00, 0x07, 0x41, 0x34, 0xC9 },
|
|
||||||
[]byte { 0x00, 0x08, 0x43, 0x10, 0xE1, 0x34, 0xC9 },
|
|
||||||
[]byte { 0x00, 0x09, 0x47, 0x63, 0x91, 0x09, 0xBC, 0x10, 0xE1, 0x34, 0xC9 },
|
|
||||||
[]byte { 0x00, 0x0B, 0x40, 0xCB },
|
|
||||||
[]byte { 0x00, 0x0C, 0x41, 0xCB, 0x37 },
|
|
||||||
[]byte { 0x00, 0x0D, 0x43, 0xEF, 0x1E, 0xCB, 0x37 },
|
|
||||||
[]byte { 0x00, 0x0E, 0x47, 0x9C, 0x6E, 0xF6, 0x43, 0xEF, 0x1E, 0xCB, 0x37 },
|
|
||||||
))
|
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,42 @@ func testGenerateRun(test *testing.T, protocol *Protocol, title, imports, testCa
|
|||||||
log.Fatalln("not equal")
|
log.Fatalln("not equal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: possibly combine the two above functions into this one,
|
||||||
|
// also take a data parameter here (snake)
|
||||||
|
func testEncodeDecode(message Message, data tu.Snake) {buffer := bytes.Buffer { }
|
||||||
|
log.Println("encoding:")
|
||||||
|
encoder := tape.NewEncoder(&buffer)
|
||||||
|
n, err := message.Encode(encoder)
|
||||||
|
if err != nil { log.Fatalf("at %d: %v\n", n, err) }
|
||||||
|
encoder.Flush()
|
||||||
|
got := buffer.Bytes()
|
||||||
|
log.Printf("got: [%s]", tu.HexBytes(got))
|
||||||
|
log.Println("correct:", data)
|
||||||
|
if n != len(got) {
|
||||||
|
log.Fatalf("n incorrect: %d != %d\n", n, len(got))
|
||||||
|
}
|
||||||
|
if ok, n := data.Check(got); !ok {
|
||||||
|
log.Fatalln("not equal at", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("decoding:")
|
||||||
|
destination := reflect.New(reflect.ValueOf(message).Elem().Type()).Interface().(Message)
|
||||||
|
flat := data.Flatten()
|
||||||
|
log.Println("before: ", destination)
|
||||||
|
decoder := tape.NewDecoder(bytes.NewBuffer(flat))
|
||||||
|
n, err = destination.Decode(decoder)
|
||||||
|
if err != nil { log.Fatalf("at %d: %v\n", n, err) }
|
||||||
|
log.Println("got: ", destination)
|
||||||
|
log.Println("correct:", message)
|
||||||
|
if n != len(flat) {
|
||||||
|
log.Fatalf("n incorrect: %d != %d\n", n, len(flat))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(destination, message) {
|
||||||
|
log.Fatalln("not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
`
|
`
|
||||||
fmt.Fprintf(
|
fmt.Fprintf(
|
||||||
mainFile, "package main\n%s\nfunc main() {\n%s\n%s\n%s\n}\n%s",
|
mainFile, "package main\n%s\nfunc main() {\n%s\n%s\n%s\n}\n%s",
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ func defaultProtocol() Protocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseReader(reader io.Reader) (*Protocol, error) {
|
func ParseReader(fileName string, reader io.Reader) (*Protocol, error) {
|
||||||
lx, err := Lex("test.pdl", reader)
|
lx, err := Lex(fileName, reader)
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
return Parse(lx)
|
return Parse(lx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func TestParse(test *testing.T) {
|
|||||||
}
|
}
|
||||||
test.Log("CORRECT:", &correct)
|
test.Log("CORRECT:", &correct)
|
||||||
|
|
||||||
got, err := ParseReader(strings.NewReader(`
|
got, err := ParseReader("test.pdl", strings.NewReader(`
|
||||||
M0000 Connect {
|
M0000 Connect {
|
||||||
0000 Name String,
|
0000 Name String,
|
||||||
0001 Password String,
|
0001 Password String,
|
||||||
|
|||||||
75
metadapta.go
75
metadapta.go
@@ -1,9 +1,11 @@
|
|||||||
package hopp
|
package hopp
|
||||||
|
|
||||||
import "io"
|
import "io"
|
||||||
|
import "os"
|
||||||
import "fmt"
|
import "fmt"
|
||||||
import "net"
|
import "net"
|
||||||
import "sync"
|
import "sync"
|
||||||
|
import "time"
|
||||||
import "sync/atomic"
|
import "sync/atomic"
|
||||||
import "git.tebibyte.media/sashakoshka/go-util/sync"
|
import "git.tebibyte.media/sashakoshka/go-util/sync"
|
||||||
|
|
||||||
@@ -108,6 +110,10 @@ func (this *a) AcceptTrans() (Trans, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *a) SetDeadline(t time.Time) error {
|
||||||
|
return this.underlying.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *a) SetSizeLimit(limit int64) {
|
func (this *a) SetSizeLimit(limit int64) {
|
||||||
this.sizeLimit = limit
|
this.sizeLimit = limit
|
||||||
}
|
}
|
||||||
@@ -212,6 +218,10 @@ type transA struct {
|
|||||||
currentWriter io.Closer
|
currentWriter io.Closer
|
||||||
writeBuffer []byte
|
writeBuffer []byte
|
||||||
closed atomic.Bool
|
closed atomic.Bool
|
||||||
|
closeErr error
|
||||||
|
|
||||||
|
deadline *time.Timer
|
||||||
|
deadlineLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *transA) Close() error {
|
func (this *transA) Close() error {
|
||||||
@@ -221,6 +231,11 @@ func (this *transA) Close() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *transA) closeWithError(err error) error {
|
||||||
|
this.closeErr = err
|
||||||
|
return this.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func (this *transA) closeDontUnlist() (err error) {
|
func (this *transA) closeDontUnlist() (err error) {
|
||||||
// MUST be goroutine safe
|
// MUST be goroutine safe
|
||||||
this.incoming.Close()
|
this.incoming.Close()
|
||||||
@@ -269,9 +284,9 @@ func (this *transA) Receive() (method uint16, data []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *transA) ReceiveReader() (uint16, io.Reader, error) {
|
func (this *transA) ReceiveReader() (uint16, io.Reader, error) {
|
||||||
// if the transaction has been closed, return an io.EOF
|
// if the transaction has been closed, return an appropriate error.
|
||||||
if this.closed.Load() {
|
if err := this.errIfClosed(); err != nil {
|
||||||
return 0, nil, io.EOF
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// drain previous reader if necessary
|
// drain previous reader if necessary
|
||||||
@@ -289,6 +304,54 @@ func (this *transA) ReceiveReader() (uint16, io.Reader, error) {
|
|||||||
return method, reader, nil
|
return method, reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *transA) SetDeadline(t time.Time) error {
|
||||||
|
this.deadlineLock.Lock()
|
||||||
|
defer this.deadlineLock.Unlock()
|
||||||
|
|
||||||
|
if t == (time.Time { }) {
|
||||||
|
if this.deadline != nil {
|
||||||
|
this.deadline.Stop()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
until := time.Until(t)
|
||||||
|
if this.deadline == nil {
|
||||||
|
this.deadline.Reset(until)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
this.deadline = time.AfterFunc(until, func () {
|
||||||
|
this.closeWithError(os.ErrDeadlineExceeded)
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// func (this *transA) SetReadDeadline(t time.Time) error {
|
||||||
|
// // TODO
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (this *transA) SetWriteDeadline(t time.Time) error {
|
||||||
|
// // TODO
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (this *transA) errIfClosed() error {
|
||||||
|
if !this.closed.Load() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return this.bestErr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *transA) bestErr() error {
|
||||||
|
if this.parent.err != nil {
|
||||||
|
return this.parent.err
|
||||||
|
}
|
||||||
|
if this.closeErr != nil {
|
||||||
|
return this.closeErr
|
||||||
|
}
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
type readerA struct {
|
type readerA struct {
|
||||||
parent *transA
|
parent *transA
|
||||||
leftover []byte
|
leftover []byte
|
||||||
@@ -319,11 +382,7 @@ func (this *readerA) pull() (uint16, error) {
|
|||||||
// close and return error on failure
|
// close and return error on failure
|
||||||
this.eof = true
|
this.eof = true
|
||||||
this.parent.Close()
|
this.parent.Close()
|
||||||
if this.parent.parent.err == nil {
|
return 0, fmt.Errorf("could not receive message: %w", this.parent.bestErr())
|
||||||
return 0, fmt.Errorf("could not receive message: %w", io.EOF)
|
|
||||||
} else {
|
|
||||||
return 0, this.parent.parent.err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *readerA) Read(buffer []byte) (int, error) {
|
func (this *readerA) Read(buffer []byte) (int, error) {
|
||||||
|
|||||||
13
metadaptb.go
13
metadaptb.go
@@ -2,6 +2,7 @@ package hopp
|
|||||||
|
|
||||||
import "io"
|
import "io"
|
||||||
import "net"
|
import "net"
|
||||||
|
import "time"
|
||||||
import "bytes"
|
import "bytes"
|
||||||
import "errors"
|
import "errors"
|
||||||
import "context"
|
import "context"
|
||||||
@@ -50,6 +51,10 @@ func (this *b) SetSizeLimit(limit int64) {
|
|||||||
this.sizeLimit = limit
|
this.sizeLimit = limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *b) SetDeadline(t time.Time) error {
|
||||||
|
return this.underlying.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *b) newTrans(underlying Stream) *transB {
|
func (this *b) newTrans(underlying Stream) *transB {
|
||||||
return &transB {
|
return &transB {
|
||||||
sizeLimit: this.sizeLimit,
|
sizeLimit: this.sizeLimit,
|
||||||
@@ -124,6 +129,10 @@ func (this *transB) receiveReader() (uint16, int64, io.Reader, error) {
|
|||||||
return method, size, data, nil
|
return method, size, data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *transB) SetDeadline(t time.Time) error {
|
||||||
|
return this.underlying.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
type writerB struct {
|
type writerB struct {
|
||||||
parent *transB
|
parent *transB
|
||||||
buffer bytes.Buffer
|
buffer bytes.Buffer
|
||||||
@@ -149,12 +158,16 @@ type MultiConn interface {
|
|||||||
AcceptStream(context.Context) (Stream, error)
|
AcceptStream(context.Context) (Stream, error)
|
||||||
// OpenStream opens a new stream.
|
// OpenStream opens a new stream.
|
||||||
OpenStream() (Stream, error)
|
OpenStream() (Stream, error)
|
||||||
|
// See the documentation for [net.Conn.SetDeadline].
|
||||||
|
SetDeadline(time.Time) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream represents a single stream returned by a [MultiConn].
|
// Stream represents a single stream returned by a [MultiConn].
|
||||||
type Stream interface {
|
type Stream interface {
|
||||||
// See documentation for [net.Conn].
|
// See documentation for [net.Conn].
|
||||||
io.ReadWriteCloser
|
io.ReadWriteCloser
|
||||||
|
// See the documentation for [net.Conn.SetDeadline].
|
||||||
|
SetDeadline(time.Time) error
|
||||||
// ID returns the stream ID
|
// ID returns the stream ID
|
||||||
ID() int64
|
ID() int64
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,9 +46,16 @@ func EncodeAny(encoder *Encoder, value any, tag Tag) (n int, err error) {
|
|||||||
case reflect.Uint32: return encoder.WriteUint32(uint32(reflectValue.Uint()))
|
case reflect.Uint32: return encoder.WriteUint32(uint32(reflectValue.Uint()))
|
||||||
case reflect.Int64: return encoder.WriteInt64(int64(reflectValue.Int()))
|
case reflect.Int64: return encoder.WriteInt64(int64(reflectValue.Int()))
|
||||||
case reflect.Uint64: return encoder.WriteUint64(uint64(reflectValue.Uint()))
|
case reflect.Uint64: return encoder.WriteUint64(uint64(reflectValue.Uint()))
|
||||||
case reflect.String: return EncodeAny(encoder, []byte(reflectValue.String()), tag)
|
case reflect.String:
|
||||||
|
if reflectValue.Len() > MaxStructureLength {
|
||||||
|
return 0, ErrTooLong
|
||||||
|
}
|
||||||
|
return EncodeAny(encoder, []byte(reflectValue.String()), tag)
|
||||||
}
|
}
|
||||||
if reflectValue.CanConvert(reflect.TypeOf(dummyBuffer)) {
|
if reflectValue.CanConvert(reflect.TypeOf(dummyBuffer)) {
|
||||||
|
if reflectValue.Len() > MaxStructureLength {
|
||||||
|
return 0, ErrTooLong
|
||||||
|
}
|
||||||
if tag.Is(LBA) {
|
if tag.Is(LBA) {
|
||||||
nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1)
|
nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
@@ -64,8 +71,13 @@ func EncodeAny(encoder *Encoder, value any, tag Tag) (n int, err error) {
|
|||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
return encodeAnySlice(encoder, value, tag)
|
return encodeAnySlice(encoder, value, tag)
|
||||||
// case reflect.Array:
|
// case reflect.Array:
|
||||||
|
// TODO: we can encode arrays. but can we decode into them?
|
||||||
|
// that's the fucken question. maybe we just do the first
|
||||||
// return encodeAnySlice(encoder, reflect.ValueOf(value).Slice(0, reflectType.Len()).Interface(), tag)
|
// return encodeAnySlice(encoder, reflect.ValueOf(value).Slice(0, reflectType.Len()).Interface(), tag)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
|
if reflectValue.Len() > MaxStructureLength {
|
||||||
|
return 0, ErrTooLong
|
||||||
|
}
|
||||||
if reflectType.Key() == reflect.TypeOf(uint16(0)) {
|
if reflectType.Key() == reflect.TypeOf(uint16(0)) {
|
||||||
return encodeAnyMap(encoder, value, tag)
|
return encodeAnyMap(encoder, value, tag)
|
||||||
}
|
}
|
||||||
@@ -133,7 +145,11 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
|
|||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
case SBA:
|
case SBA:
|
||||||
// SBA: <data: U8>*
|
// SBA: <data: U8>*
|
||||||
buffer := make([]byte, tag.CN())
|
length := tag.CN()
|
||||||
|
if length > MaxStructureLength {
|
||||||
|
return 0, ErrTooLong
|
||||||
|
}
|
||||||
|
buffer := make([]byte, length)
|
||||||
nn, err := decoder.Read(buffer)
|
nn, err := decoder.Read(buffer)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
setByteArray(destination, buffer)
|
setByteArray(destination, buffer)
|
||||||
@@ -141,6 +157,9 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
|
|||||||
// LBA: <length: UN> <data: U8>*
|
// LBA: <length: UN> <data: U8>*
|
||||||
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
|
if length > uint64(MaxStructureLength) {
|
||||||
|
return 0, ErrTooLong
|
||||||
|
}
|
||||||
buffer := make([]byte, length)
|
buffer := make([]byte, length)
|
||||||
nn, err = decoder.Read(buffer)
|
nn, err = decoder.Read(buffer)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
@@ -149,10 +168,15 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
|
|||||||
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
||||||
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
|
if length > uint64(MaxStructureLength) {
|
||||||
|
return 0, ErrTooLong
|
||||||
|
}
|
||||||
|
lengthCast, err := Uint64ToIntSafe(length)
|
||||||
|
if err != nil { return n, err }
|
||||||
oneTag, nn, err := decoder.ReadTag()
|
oneTag, nn, err := decoder.ReadTag()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
if destination.Cap() < int(length) {
|
if destination.Cap() < lengthCast {
|
||||||
destination.Grow(int(length) - destination.Cap())
|
destination.Grow(lengthCast - destination.Cap())
|
||||||
}
|
}
|
||||||
// skip the rest of the array if the one tag doesn't
|
// skip the rest of the array if the one tag doesn't
|
||||||
// match up with the destination
|
// match up with the destination
|
||||||
@@ -165,7 +189,7 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil { return n, err }
|
if err != nil { return n, err }
|
||||||
destination.SetLen(int(length))
|
destination.SetLen(lengthCast)
|
||||||
for index := range length {
|
for index := range length {
|
||||||
nn, err := decodeAny(decoder, destination.Index(int(index)), oneTag)
|
nn, err := decodeAny(decoder, destination.Index(int(index)), oneTag)
|
||||||
n += nn
|
n += nn
|
||||||
@@ -179,6 +203,9 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
|
|||||||
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
||||||
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
|
if length > uint64(MaxStructureLength) {
|
||||||
|
return 0, ErrTooLong
|
||||||
|
}
|
||||||
destination.Clear()
|
destination.Clear()
|
||||||
for _ = range length {
|
for _ = range length {
|
||||||
key, nn, err := decoder.ReadUint16()
|
key, nn, err := decoder.ReadUint16()
|
||||||
|
|||||||
12
tape/error.go
Normal file
12
tape/error.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package tape
|
||||||
|
|
||||||
|
// Error enumerates common errors in this package.
|
||||||
|
type Error string; const (
|
||||||
|
ErrTooLong Error = "data structure too long"
|
||||||
|
ErrTooLarge Error = "number too large"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (err Error) Error() string {
|
||||||
|
return string(err)
|
||||||
|
}
|
||||||
26
tape/limits.go
Normal file
26
tape/limits.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package tape
|
||||||
|
|
||||||
|
// MaxStructureLength determines how long a TAPE data structure can be. This
|
||||||
|
// applies to:
|
||||||
|
//
|
||||||
|
// - OTA
|
||||||
|
// - SBA/LBA
|
||||||
|
// - KTV
|
||||||
|
//
|
||||||
|
// By default it is set at 2^20 (about a million).
|
||||||
|
// You shouldn't need to change this. If you do, it should only be set once at
|
||||||
|
// the start of the program.
|
||||||
|
var MaxStructureLength = 1024 * 1024
|
||||||
|
|
||||||
|
// MaxInt is the maximum value an int can hold. This varies depending on the
|
||||||
|
// system.
|
||||||
|
const MaxInt int = int(^uint(0) >> 1)
|
||||||
|
|
||||||
|
// Uint64ToIntSafe casts the input to an int if it can be done without overflow,
|
||||||
|
// or returns an error otherwise.
|
||||||
|
func Uint64ToIntSafe(input uint64) (int, error) {
|
||||||
|
if input > uint64(MaxInt) {
|
||||||
|
return 0, ErrTooLarge
|
||||||
|
}
|
||||||
|
return int(input), nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user