From 14bb92b622d5cd56829d0d9f04a201c7260ff9f9 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 1 Aug 2022 00:18:59 -0600 Subject: wip --- lib/lowmemjson.stock/borrowed_tags_test.go | 28 +++ lib/lowmemjson/base64.go | 121 +++++++++++ lib/lowmemjson/base64_test.go | 44 ++++ lib/lowmemjson/decode.go | 221 +++++++++++++-------- lib/lowmemjson/encode.go | 13 +- lib/lowmemjson/reencode.go | 2 +- ...ad7f1a4606dc7419750995a57828aa25ea57fe7099d5c03 | 2 + ...5cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a | 2 + ...a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae2 | 2 + ...3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd | 2 + ...775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 | 2 + ...1b66b08f0a4ff81edd7cb53d00dce8ee0eaf31683996026 | 2 + ...be1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d | 2 + ...f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b | 2 + ...920b5202cd1269174416ce32769c7f59376e76b7dd3129c | 2 + ...7433233f3a90099024e580a6ba319ea2bf539880c50bd7c | 2 + ...326638b8915f80863feab0ba0108183b3093934bdc0420c | 2 + ...aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f | 2 + ...7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b07 | 2 + ...5bd136874415dddfff5c586e662f21420caa7a94131a56a | 2 + ...d54218d2ad8112204672cc1fb30be297853616788208a5c | 2 + ...eab217543561dfd8001d4a44f53ceb664aaba86cebfaf21 | 2 + ...77408be5ef9389790e33ed1886073dec445d4cf05bcd4b4 | 2 + ...1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9 | 2 + ...c70baf6d7821a5a6f3a90cabb033575790be91723593680 | 2 + ...9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a | 2 + ...27531927c5c1e65d159b70f39cd161da0dba348c1221ab3 | 2 + ...1a201281dbf6568628b4135c35c811dd9bce97620a75d43 | 2 + 28 files changed, 387 insertions(+), 86 deletions(-) create mode 100644 lib/lowmemjson.stock/borrowed_tags_test.go create mode 100644 lib/lowmemjson/base64.go create mode 100644 lib/lowmemjson/base64_test.go create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c03 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae2 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf31683996026 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b07 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf21 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b4 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be91723593680 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab3 create mode 100644 lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d43 diff --git a/lib/lowmemjson.stock/borrowed_tags_test.go b/lib/lowmemjson.stock/borrowed_tags_test.go new file mode 100644 index 0000000..8ba8ddd --- /dev/null +++ b/lib/lowmemjson.stock/borrowed_tags_test.go @@ -0,0 +1,28 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "testing" +) + +func TestTagParsing(t *testing.T) { + name, opts := parseTag("field,foobar,foo") + if name != "field" { + t.Fatalf("name = %q, want field", name) + } + for _, tt := range []struct { + opt string + want bool + }{ + {"foobar", true}, + {"foo", true}, + {"bar", false}, + } { + if opts.Contains(tt.opt) != tt.want { + t.Errorf("Contains(%q) = %v", tt.opt, !tt.want) + } + } +} diff --git a/lib/lowmemjson/base64.go b/lib/lowmemjson/base64.go new file mode 100644 index 0000000..86fc293 --- /dev/null +++ b/lib/lowmemjson/base64.go @@ -0,0 +1,121 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package lowmemjson + +import ( + "encoding/base64" + "io" + "strings" +) + +type base64Decoder struct { + dst io.Writer + + err error + pos int64 + buf [4]byte + bufLen int +} + +func newBase64Decoder(w io.Writer) io.WriteCloser { + return &base64Decoder{ + dst: w, + } +} + +func (dec *base64Decoder) decodeByte(b byte) (byte, bool) { + const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + n := strings.IndexByte(alphabet, b) + if n < 0 { + return 0, false + } + dec.pos++ + return byte(n), true +} + +func (dec *base64Decoder) decodeTuple(a, b, c, d byte) error { + var decodedLen int + var encoded [4]byte + var ok bool + + if a != '=' { + encoded[0], ok = dec.decodeByte(a) + if !ok { + return base64.CorruptInputError(dec.pos) + } + decodedLen++ + } + if b != '=' { + encoded[1], ok = dec.decodeByte(b) + if !ok { + return base64.CorruptInputError(dec.pos) + } + // do NOT increment decodedLen here + } + if c != '=' { + encoded[2], ok = dec.decodeByte(c) + if !ok { + return base64.CorruptInputError(dec.pos) + } + decodedLen++ + } + if d != '=' { + encoded[3], ok = dec.decodeByte(d) + if !ok { + return base64.CorruptInputError(dec.pos) + } + decodedLen++ + } + + val := 0 | + uint32(encoded[0])<<18 | + uint32(encoded[1])<<12 | + uint32(encoded[2])<<6 | + uint32(encoded[3])<<0 + var decoded [3]byte + decoded[0] = byte(val >> 16) + decoded[1] = byte(val >> 8) + decoded[2] = byte(val >> 0) + + _, err := dec.dst.Write(decoded[:decodedLen]) + return err +} + +func (dec *base64Decoder) Write(dat []byte) (int, error) { + if len(dat) == 0 { + return 0, nil + } + if dec.err != nil { + return 0, dec.err + } + var n int + if dec.bufLen > 0 { + n = copy(dec.buf[dec.bufLen:], dat) + dec.bufLen += n + if dec.bufLen < 4 { + return len(dat), nil + } + if err := dec.decodeTuple(dec.buf[0], dec.buf[1], dec.buf[2], dec.buf[3]); err != nil { + dec.err = err + return 0, dec.err + } + } + for ; n+3 < len(dat); n += 4 { + if err := dec.decodeTuple(dat[n], dat[n+1], dat[n+2], dat[n+3]); err != nil { + dec.err = err + return n, dec.err + } + } + dec.bufLen = copy(dec.buf[:], dat[n:]) + return len(dat), nil +} + +func (dec *base64Decoder) Close() error { + if dec.bufLen == 0 { + return nil + } + copy(dec.buf[:], "====") + return dec.decodeTuple(dec.buf[0], dec.buf[1], dec.buf[2], dec.buf[3]) +} diff --git a/lib/lowmemjson/base64_test.go b/lib/lowmemjson/base64_test.go new file mode 100644 index 0000000..43367af --- /dev/null +++ b/lib/lowmemjson/base64_test.go @@ -0,0 +1,44 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package lowmemjson + +import ( + "bytes" + "encoding/base64" + "testing" + + "github.com/stretchr/testify/require" +) + +func b64encode(t *testing.T, input []byte) []byte { + var encoded bytes.Buffer + enc := base64.NewEncoder(base64.StdEncoding, &encoded) + _, err := enc.Write(input) + require.NoError(t, err) + require.NoError(t, enc.Close()) + return encoded.Bytes() +} + +func b64decode(t *testing.T, input []byte) []byte { + var decoded bytes.Buffer + dec := newBase64Decoder(&decoded) + _, err := dec.Write(input) + require.NoError(t, err) + require.NoError(t, dec.Close()) + return decoded.Bytes() +} + +func FuzzBase64Decoder(f *testing.F) { + f.Fuzz(func(t *testing.T, input []byte) { + encoded := b64encode(t, input) + decoded := b64decode(t, encoded) + t.Logf("input b64 = %q", encoded) + t.Logf("expected decoded = %#v", input) + t.Logf("actual decoded = %#v", decoded) + if !bytes.Equal(input, decoded) { + t.Fail() + } + }) +} diff --git a/lib/lowmemjson/decode.go b/lib/lowmemjson/decode.go index 4873d43..b2eaacf 100644 --- a/lib/lowmemjson/decode.go +++ b/lib/lowmemjson/decode.go @@ -24,7 +24,7 @@ type decodeError struct { } type runeBuffer interface { - *bytes.Buffer | *strings.Builder + io.Writer WriteRune(rune) (int, error) Reset() } @@ -93,7 +93,7 @@ func Decode(r io.RuneScanner, ptr any) (err error) { } }() decodeWS(r) - decode(r, ptrVal) + decode(r, ptrVal.Elem()) return nil } @@ -124,39 +124,39 @@ var kind2bits = map[reflect.Kind]int{ reflect.Float64: 64, } -func decode(r io.RuneScanner, ptrVal reflect.Value) { +func decode(r io.RuneScanner, val reflect.Value) { + typ := val.Type() switch { - case ptrVal.Type() == rawMessagePtrType: + case val.CanAddr() && reflect.PointerTo(typ) == rawMessagePtrType: var buf bytes.Buffer scan(r, &buf) - if err := ptrVal.Interface().(*json.RawMessage).UnmarshalJSON(buf.Bytes()); err != nil { + if err := val.Addr().Interface().(*json.RawMessage).UnmarshalJSON(buf.Bytes()); err != nil { panic(decodeError{err}) } - case ptrVal.Type().Implements(decoderType): - obj := ptrVal.Interface().(Decoder) + case val.CanAddr() && reflect.PointerTo(typ).Implements(decoderType): + obj := val.Addr().Interface().(Decoder) if err := obj.DecodeJSON(r); err != nil { panic(decodeError{err}) } - case ptrVal.Type().Implements(jsonUnmarshalerType): + case val.CanAddr() && reflect.PointerTo(typ).Implements(jsonUnmarshalerType): var buf bytes.Buffer scan(r, &buf) - obj := ptrVal.Interface().(json.Unmarshaler) + obj := val.Addr().Interface().(json.Unmarshaler) if err := obj.UnmarshalJSON(buf.Bytes()); err != nil { panic(decodeError{err}) } - case ptrVal.Type().Implements(textUnmarshalerType): + case val.CanAddr() && reflect.PointerTo(typ).Implements(textUnmarshalerType): var buf bytes.Buffer decodeString(r, &buf) - obj := ptrVal.Interface().(encoding.TextUnmarshaler) + obj := val.Addr().Interface().(encoding.TextUnmarshaler) if err := obj.UnmarshalText(buf.Bytes()); err != nil { panic(decodeError{err}) } default: - typ := ptrVal.Type().Elem() kind := typ.Kind() switch kind { case reflect.Bool: - ptrVal.Elem().SetBool(decodeBool(r)) + val.SetBool(decodeBool(r)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var buf strings.Builder scanNumber(r, &buf) @@ -164,7 +164,7 @@ func decode(r io.RuneScanner, ptrVal reflect.Value) { if err != nil { panic(decodeError{err}) } - ptrVal.Elem().SetInt(n) + val.SetInt(n) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: var buf strings.Builder scanNumber(r, &buf) @@ -172,7 +172,7 @@ func decode(r io.RuneScanner, ptrVal reflect.Value) { if err != nil { panic(decodeError{err}) } - ptrVal.Elem().SetUint(n) + val.SetUint(n) case reflect.Float32, reflect.Float64: var buf strings.Builder scanNumber(r, &buf) @@ -180,23 +180,27 @@ func decode(r io.RuneScanner, ptrVal reflect.Value) { if err != nil { panic(decodeError{err}) } - ptrVal.Elem().SetFloat(n) + val.SetFloat(n) case reflect.String: var buf strings.Builder if typ == numberType { scanNumber(r, &buf) - ptrVal.Elem().SetString(buf.String()) + val.SetString(buf.String()) } else { decodeString(r, &buf) - ptrVal.Elem().SetString(buf.String()) + val.SetString(buf.String()) } case reflect.Interface: - if typ == anyType { - ptrVal.Elem().Set(reflect.ValueOf(decodeAny(r))) + if val.IsNil() { + if typ == anyType { + val.Set(reflect.ValueOf(decodeAny(r))) + } else { + panic(decodeError{&json.UnsupportedTypeError{ + Type: typ, + }}) + } } else { - panic(decodeError{&json.UnsupportedTypeError{ - Type: typ, - }}) + decode(r, val.Elem()) } case reflect.Struct: index := indexStruct(typ) @@ -209,7 +213,7 @@ func decode(r io.RuneScanner, ptrVal reflect.Value) { return } field := index.byPos[idx] - fVal := ptrVal.Elem() + fVal := val for _, idx := range field.Path { if fVal.Kind() == reflect.Pointer { if fVal.IsNil() { @@ -225,58 +229,91 @@ func decode(r io.RuneScanner, ptrVal reflect.Value) { decode(r, fVal.Addr()) }) case reflect.Map: - if ptrVal.Elem().IsNil() { - ptrVal.Elem().Set(reflect.MakeMap(typ)) - } - var nameBuf bytes.Buffer - decodeObject(r, &nameBuf, func() { - nameValTyp := typ.Key() - nameValPtr := reflect.New(nameValTyp) - switch { - case reflect.PointerTo(nameValTyp).Implements(textUnmarshalerType): - obj := ptrVal.Interface().(encoding.TextUnmarshaler) - if err := obj.UnmarshalText(nameBuf.Bytes()); err != nil { - panic(decodeError{err}) - } - default: - switch nameValTyp.Kind() { - case reflect.String: - nameValPtr.Elem().SetString(nameBuf.String()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - n, err := strconv.ParseInt(nameBuf.String(), 10, kind2bits[nameValTyp.Kind()]) - if err != nil { - panic(decodeError{err}) - } - nameValPtr.Elem().SetInt(n) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - n, err := strconv.ParseUint(nameBuf.String(), 10, kind2bits[nameValTyp.Kind()]) - if err != nil { + switch peekRune(r) { + case 'n': + decodeNull(r) + val.Set(reflect.Zero(typ)) + case '{': + if val.IsNil() { + val.Set(reflect.MakeMap(typ)) + } + var nameBuf bytes.Buffer + decodeObject(r, &nameBuf, func() { + nameValTyp := typ.Key() + nameValPtr := reflect.New(nameValTyp) + switch { + case reflect.PointerTo(nameValTyp).Implements(textUnmarshalerType): + obj := nameValPtr.Interface().(encoding.TextUnmarshaler) + if err := obj.UnmarshalText(nameBuf.Bytes()); err != nil { panic(decodeError{err}) } - nameValPtr.Elem().SetUint(n) default: - panic(decodeError{fmt.Errorf("invalid map key type: %v", nameValTyp)}) + switch nameValTyp.Kind() { + case reflect.String: + nameValPtr.Elem().SetString(nameBuf.String()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.ParseInt(nameBuf.String(), 10, kind2bits[nameValTyp.Kind()]) + if err != nil { + panic(decodeError{err}) + } + nameValPtr.Elem().SetInt(n) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.ParseUint(nameBuf.String(), 10, kind2bits[nameValTyp.Kind()]) + if err != nil { + panic(decodeError{err}) + } + nameValPtr.Elem().SetUint(n) + default: + panic(decodeError{fmt.Errorf("invalid map key type: %v", nameValTyp)}) + } } - } - fValPtr := reflect.New(typ.Elem()) - decode(r, fValPtr) + fValPtr := reflect.New(typ.Elem()) + decode(r, fValPtr) - ptrVal.Elem().SetMapIndex(nameValPtr.Elem(), fValPtr.Elem()) - }) + val.SetMapIndex(nameValPtr.Elem(), fValPtr.Elem()) + }) + default: + panic(decodeError{fmt.Errorf("invalid character %q for map value", peekRune(r))}) + } case reflect.Slice: - if ptrVal.Elem().IsNil() { - ptrVal.Elem().Set(reflect.MakeSlice(typ.Elem(), 0, 0)) + switch { + case typ.Elem().Kind() == reflect.Uint8: + var buf bytes.Buffer + dec := newBase64Decoder(&buf) + decodeString(r, dec) + val.Set(reflect.ValueOf(buf.Bytes())) + default: + switch peekRune(r) { + case 'n': + decodeNull(r) + val.Set(reflect.Zero(typ)) + case '[': + if val.IsNil() { + val.Set(reflect.MakeSlice(typ, 0, 0)) + } + decodeArray(r, func() { + mValPtr := reflect.New(typ.Elem()) + decode(r, mValPtr) + val.Set(reflect.Append(val, mValPtr.Elem())) + }) + default: + panic(decodeError{fmt.Errorf("invalid character %q for slice value", peekRune(r))}) + } } + case reflect.Array: + i := 0 decodeArray(r, func() { mValPtr := reflect.New(typ.Elem()) decode(r, mValPtr) - ptrVal.Set(reflect.Append(ptrVal.Elem(), mValPtr.Elem())) + val.Index(i).Set(mValPtr.Elem()) + i++ }) case reflect.Pointer: - val := reflect.New(typ.Elem()) - decode(r, val) - ptrVal.Elem().Set(val) + if val.IsNil() { + val.Set(reflect.New(typ.Elem())) + } + decode(r, val.Elem()) default: panic(decodeError{&json.UnsupportedTypeError{ Type: typ, @@ -337,7 +374,7 @@ func scanNumber(r io.RuneScanner, out io.Writer) { case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': scan(r, out) default: - panic(decodeError{fmt.Errorf("expected a nubmer bug got %c", c)}) + panic(decodeError{fmt.Errorf("expected a nubmer but got %c", c)}) } } @@ -368,17 +405,14 @@ func decodeAny(r io.RuneScanner) any { case 't', 'f': return decodeBool(r) case 'n': - expectRune(r, 'n') - expectRune(r, 'u') - expectRune(r, 'l') - expectRune(r, 'l') + decodeNull(r) return nil default: panic(decodeError{fmt.Errorf("unexpected character: %c", c)}) } } -func decodeObject[bufT runeBuffer](r io.RuneScanner, nameBuf bufT, decodeKVal func()) { +func decodeObject(r io.RuneScanner, nameBuf runeBuffer, decodeKVal func()) { expectRune(r, '{') decodeWS(r) c := readRune(r) @@ -450,40 +484,54 @@ func decodeHex(r io.RuneReader) rune { } } -func decodeString[bufT runeBuffer](r io.RuneScanner, out bufT) { - // No need to check errors from out.WriteRune because 'out' - // guaranteed (by the 'runeBuffer' type constraint) to always - // either a *bytes.Buffer or a *string.Builder, neither of - // which return errors. +func decodeString(r io.RuneScanner, out io.Writer) { expectRune(r, '"') for { c := readRune(r) switch { case 0x0020 <= c && c <= 0x10FFFF && c != '"' && c != '\\': - _, _ = out.WriteRune(c) + if _, err := writeRune(out, c); err != nil { + panic(decodeError{err}) + } case c == '\\': c = readRune(r) switch c { case '"': - _, _ = out.WriteRune('"') + if _, err := writeRune(out, '"'); err != nil { + panic(decodeError{err}) + } case '\\': - _, _ = out.WriteRune('\\') + if _, err := writeRune(out, '\\'); err != nil { + panic(decodeError{err}) + } case 'b': - _, _ = out.WriteRune('\b') + if _, err := writeRune(out, '\b'); err != nil { + panic(decodeError{err}) + } case 'f': - _, _ = out.WriteRune('\f') + if _, err := writeRune(out, '\f'); err != nil { + panic(decodeError{err}) + } case 'n': - _, _ = out.WriteRune('\n') + if _, err := writeRune(out, '\n'); err != nil { + panic(decodeError{err}) + } case 'r': - _, _ = out.WriteRune('\r') + if _, err := writeRune(out, '\r'); err != nil { + panic(decodeError{err}) + } case 't': - _, _ = out.WriteRune('\t') + if _, err := writeRune(out, '\t'); err != nil { + panic(decodeError{err}) + } case 'u': c = decodeHex(r) c = (c << 4) | decodeHex(r) c = (c << 4) | decodeHex(r) c = (c << 4) | decodeHex(r) - _, _ = out.WriteRune(c) + if _, err := writeRune(out, c); err != nil { + panic(decodeError{err}) + } } case c == '"': return @@ -511,3 +559,10 @@ func decodeBool(r io.RuneReader) bool { panic(decodeError{fmt.Errorf("unexpected character: %c", c)}) } } + +func decodeNull(r io.RuneReader) { + expectRune(r, 'n') + expectRune(r, 'u') + expectRune(r, 'l') + expectRune(r, 'l') +} diff --git a/lib/lowmemjson/encode.go b/lib/lowmemjson/encode.go index ca3d406..c967785 100644 --- a/lib/lowmemjson/encode.go +++ b/lib/lowmemjson/encode.go @@ -7,6 +7,7 @@ package lowmemjson import ( "bytes" "encoding" + "encoding/base64" "encoding/json" "io" "reflect" @@ -250,10 +251,18 @@ func encode(w io.Writer, val reflect.Value, quote bool) { encodeWriteByte(w, '}') case reflect.Slice: switch { - case val.Type().Elem().Kind() == reflect.Uint8: - encodeTODO(w, val) case val.IsNil(): encodeWriteString(w, "null") + case val.Type().Elem().Kind() == reflect.Uint8: + encodeWriteByte(w, '"') + enc := base64.NewEncoder(base64.StdEncoding, w) + if _, err := enc.Write(val.Interface().([]byte)); err != nil { + panic(encodeError{err}) + } + if err := enc.Close(); err != nil { + panic(encodeError{err}) + } + encodeWriteByte(w, '"') default: encodeArray(w, val) } diff --git a/lib/lowmemjson/reencode.go b/lib/lowmemjson/reencode.go index ebca619..7517b29 100644 --- a/lib/lowmemjson/reencode.go +++ b/lib/lowmemjson/reencode.go @@ -73,7 +73,7 @@ func (enc *ReEncoder) Write(p []byte) (int, error) { n += size } enc.bufLen = copy(enc.buf[:], p[n:]) - return n, nil + return len(p), nil } func (enc *ReEncoder) Flush() error { diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c03 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c03 new file mode 100644 index 0000000..c3774e7 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c03 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a new file mode 100644 index 0000000..4c861db --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae2 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae2 new file mode 100644 index 0000000..3d32e14 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae2 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("00000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd new file mode 100644 index 0000000..d08ef92 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("00000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 new file mode 100644 index 0000000..a96f559 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf31683996026 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf31683996026 new file mode 100644 index 0000000..87c024d --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf31683996026 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0000000000000000000000000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d new file mode 100644 index 0000000..959401e --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b new file mode 100644 index 0000000..bd1ae59 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000000000000000000000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c new file mode 100644 index 0000000..09e0ad2 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c new file mode 100644 index 0000000..e8000f3 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("00") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c new file mode 100644 index 0000000..aac6b7d --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("00000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f new file mode 100644 index 0000000..f3bf6d9 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("00000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b07 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b07 new file mode 100644 index 0000000..2e7f462 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b07 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("00000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a new file mode 100644 index 0000000..c541f52 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000000000000000000000000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c new file mode 100644 index 0000000..5d56f29 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf21 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf21 new file mode 100644 index 0000000..4b4d59f --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf21 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b4 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b4 new file mode 100644 index 0000000..ef9f9d4 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b4 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9 new file mode 100644 index 0000000..67322c7 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be91723593680 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be91723593680 new file mode 100644 index 0000000..f195330 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be91723593680 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\x04000000000000\r00000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a new file mode 100644 index 0000000..5b0d392 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0000000000000000000000000000000000000000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab3 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab3 new file mode 100644 index 0000000..a389d3c --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab3 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("00000") diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d43 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d43 new file mode 100644 index 0000000..17d10b2 --- /dev/null +++ b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d43 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("000000000000") -- cgit v1.2.3-2-g168b