summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-08-01 00:18:59 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-08-01 00:44:08 -0600
commit14bb92b622d5cd56829d0d9f04a201c7260ff9f9 (patch)
tree2a808d1e1f6acd8315e6306beeaf61f33890f3aa
parent0ee3e4eb97aba57735b1278e2afb3df5e7d7ee56 (diff)
wip
-rw-r--r--lib/lowmemjson.stock/borrowed_tags_test.go28
-rw-r--r--lib/lowmemjson/base64.go121
-rw-r--r--lib/lowmemjson/base64_test.go44
-rw-r--r--lib/lowmemjson/decode.go221
-rw-r--r--lib/lowmemjson/encode.go13
-rw-r--r--lib/lowmemjson/reencode.go2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c032
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae22
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed42
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf316839960262
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b072
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf212
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b42
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e92
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be917235936802
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab32
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d432
28 files changed, 387 insertions, 86 deletions
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 <lukeshu@lukeshu.com>
+//
+// 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 <lukeshu@lukeshu.com>
+//
+// 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")