From 1360be3b26a4fe8a3ece98440df5a82253ac23ba Mon Sep 17 00:00:00 2001 From: Hrutvik Kanabar Date: Tue, 25 Oct 2022 10:14:37 +0000 Subject: 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 `"..."/` 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. --- prog/encoding.go | 70 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 21 deletions(-) (limited to 'prog/encoding.go') 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 { -- cgit mrf-deployment