aboutsummaryrefslogtreecommitdiffstats
path: root/prog/encoding.go
diff options
context:
space:
mode:
authorHrutvik Kanabar <hrutvik@google.com>2022-10-25 10:14:37 +0000
committerAleksandr Nogikh <wp32pw@gmail.com>2022-11-21 11:06:14 +0100
commit1360be3b26a4fe8a3ece98440df5a82253ac23ba (patch)
tree3f74edd01515b27685fd7ba888c899c066b835a5 /prog/encoding.go
parentd0405298b24db0e2a6b2abfdc8c7e5ebbe49d1a0 (diff)
prog: introduce new Base64 syntax for data
The new "$..." syntax is read as a Base64 encoding binary data. Note that users cannot specify the size of the Base64 syntax using the `"..."/<size>` notation. When serialising programs to human-readable form, only compressed types (determined by `IsCompressed()`) are represented using the new Base64 notation. Also add a couple of serialisation tests, checking behaviour for compressed and non-compressed types.
Diffstat (limited to 'prog/encoding.go')
-rw-r--r--prog/encoding.go70
1 files changed, 49 insertions, 21 deletions
diff --git a/prog/encoding.go b/prog/encoding.go
index 82c93273a..33983fb47 100644
--- a/prog/encoding.go
+++ b/prog/encoding.go
@@ -147,19 +147,23 @@ func (a *DataArg) serialize(ctx *serializer) {
return
}
data := a.Data()
- // Statically typed data will be padded with 0s during deserialization,
- // so we can strip them here for readability always. For variable-size
- // data we strip trailing 0s only if we strip enough of them.
- sz := len(data)
- for len(data) >= 2 && data[len(data)-1] == 0 && data[len(data)-2] == 0 {
- data = data[:len(data)-1]
- }
- if typ.Varlen() && len(data)+8 >= sz {
- data = data[:sz]
- }
- serializeData(ctx.buf, data, isReadableDataType(typ))
- if typ.Varlen() && sz != len(data) {
- ctx.printf("/%v", sz)
+ if typ.IsCompressed() {
+ serializeCompressedData(ctx.buf, data)
+ } else {
+ // Statically typed data will be padded with 0s during deserialization,
+ // so we can strip them here for readability always. For variable-size
+ // data we strip trailing 0s only if we strip enough of them.
+ sz := len(data)
+ for len(data) >= 2 && data[len(data)-1] == 0 && data[len(data)-2] == 0 {
+ data = data[:len(data)-1]
+ }
+ if typ.Varlen() && len(data)+8 >= sz {
+ data = data[:sz]
+ }
+ serializeData(ctx.buf, data, isReadableDataType(typ))
+ if typ.Varlen() && sz != len(data) {
+ ctx.printf("/%v", sz)
+ }
}
}
@@ -593,7 +597,7 @@ func (p *parser) parseArgString(t Type, dir Dir) (Arg, error) {
p.eatExcessive(true, "wrong string arg")
return t.DefaultArg(dir), nil
}
- data, err := p.deserializeData()
+ data, b64, err := p.deserializeData()
if err != nil {
return nil, err
}
@@ -607,7 +611,7 @@ func (p *parser) parseArgString(t Type, dir Dir) (Arg, error) {
}
}
size := ^uint64(0)
- if p.Char() == '/' {
+ if p.Char() == '/' && !b64 {
p.Parse('/')
sizeStr := p.Ident()
size, err = strconv.ParseUint(sizeStr, 0, 64)
@@ -880,6 +884,14 @@ func serializeData(buf *bytes.Buffer, data []byte, readable bool) {
buf.WriteByte('\'')
}
+func serializeCompressedData(buf *bytes.Buffer, data []byte) {
+ buf.WriteByte('"')
+ buf.WriteByte('$')
+ encoded := EncodeB64(data)
+ buf.Write(encoded)
+ buf.WriteByte('"')
+}
+
func EncodeData(buf *bytes.Buffer, data []byte, readable bool) {
if !readable && isReadableData(data) {
readable = true
@@ -957,10 +969,26 @@ func isReadableData(data []byte) bool {
return true
}
-func (p *parser) deserializeData() ([]byte, error) {
+// Deserialize data, returning the data and whether it was encoded in Base64.
+func (p *parser) deserializeData() ([]byte, bool, error) {
var data []byte
if p.Char() == '"' {
p.Parse('"')
+ if p.Char() == '$' {
+ // Read Base64 data.
+ p.consume()
+ var rawData []byte
+ for p.Char() != '"' {
+ v := p.consume()
+ rawData = append(rawData, v)
+ }
+ decoded, err := DecodeB64(rawData)
+ if err != nil {
+ return nil, false, fmt.Errorf("data arg is corrupt: %v", err)
+ }
+ p.Parse('"')
+ return decoded, true, nil
+ }
val := ""
if p.Char() != '"' {
val = p.Ident()
@@ -969,11 +997,11 @@ func (p *parser) deserializeData() ([]byte, error) {
var err error
data, err = hex.DecodeString(val)
if err != nil {
- return nil, fmt.Errorf("data arg has bad value %q", val)
+ return nil, false, fmt.Errorf("data arg has bad value %q", val)
}
} else {
if p.consume() != '\'' {
- return nil, fmt.Errorf("data arg does not start with \" nor with '")
+ return nil, false, fmt.Errorf("data arg does not start with \" nor with '")
}
for p.Char() != '\'' && p.Char() != 0 {
v := p.consume()
@@ -988,7 +1016,7 @@ func (p *parser) deserializeData() ([]byte, error) {
lo := p.consume()
b, ok := hexToByte(lo, hi)
if !ok {
- return nil, fmt.Errorf("invalid hex \\x%v%v in data arg", hi, lo)
+ return nil, false, fmt.Errorf("invalid hex \\x%v%v in data arg", hi, lo)
}
data = append(data, b)
case 'a':
@@ -1012,12 +1040,12 @@ func (p *parser) deserializeData() ([]byte, error) {
case '\\':
data = append(data, '\\')
default:
- return nil, fmt.Errorf("invalid \\%c escape sequence in data arg", v)
+ return nil, false, fmt.Errorf("invalid \\%c escape sequence in data arg", v)
}
}
p.Parse('\'')
}
- return data, nil
+ return data, false, nil
}
func isPrintable(v byte) bool {