From db22828f3064c6ec93650f212c762467a50bcdf2 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Sat, 30 Sep 2023 18:23:38 -0700 Subject: [PATCH 1/2] bump go to 1.20; drop pkg/errors --- .github/workflows/go.yml | 4 +-- decode.go | 33 +++++++++---------- encode.go | 24 +++++++------- errors.go | 8 ++--- go.mod | 4 +-- go.sum | 2 -- reader.go | 29 ++++++++-------- ...8400f8a0a12f2f1952d87f0bae9e950c94a90fabea | 1 - ...a3a5f86c38549135c789b8d97dbbf084d21369685f | 1 - ...8f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca | 1 - writer.go | 7 ++-- 11 files changed, 52 insertions(+), 62 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 411da53..04808c6 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,7 +7,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - go: [1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18] + go: [1.15, 1.16, 1.17, 1.18, 1.19, "1.20", 1.21] os: [ubuntu-latest, windows-latest, macos-latest] steps: @@ -29,4 +29,4 @@ jobs: run: go test ./... - name: Race - run: go test -race ./... + run: go test -race -count 10 ./... diff --git a/decode.go b/decode.go index 16a06b9..ca0d2b2 100644 --- a/decode.go +++ b/decode.go @@ -1,11 +1,10 @@ package ubjson import ( + "errors" "fmt" "io" "reflect" - - "github.com/pkg/errors" ) // MaxCollectionAlloc is the default maximum collection capacity allocation. @@ -48,7 +47,7 @@ func (d *Decoder) DecodeValue(v Value) error { // decodeValue asserts a value's type marker, then decodes the data. func (d *Decoder) decodeValue(m Marker, decodeData func(*Decoder) error) error { if r, err := d.readValType(); err != nil { - return errors.Wrapf(err, "failed trying to read type '%s'", m) + return fmt.Errorf("failed trying to read type '%s': %w", m, err) } else if r != m { return errWrongTypeRead(m, r) } @@ -59,7 +58,7 @@ func (d *Decoder) decodeValue(m Marker, decodeData func(*Decoder) error) error { func (d *Decoder) assertType(m Marker) error { r, err := d.readMarker() if err != nil { - return errors.Wrapf(err, "failed trying to read type '%s'", m) + return fmt.Errorf("failed trying to read type '%s': %w", m, err) } if r != m { return errWrongTypeRead(m, r) @@ -587,7 +586,7 @@ func (d *Decoder) Decode(v interface{}) error { value := reflect.ValueOf(v) if value.Kind() != reflect.Ptr { - return errors.Errorf("can only decode into pointers, not: %s", value.Type()) + return fmt.Errorf("can only decode into pointers, not: %s", value.Type()) } // Containers switch value.Elem().Kind() { @@ -618,7 +617,7 @@ func arrayToArray(arrayPtr reflect.Value) func(*ArrayDecoder) error { elemType := arrayValue.Type().Elem() if ad.Len > 0 { if ad.Len >= 0 && ad.Len != arrayValue.Len() { - return errors.Errorf("unable to decode data length %d into array of length %d", ad.Len, arrayValue.Len()) + return fmt.Errorf("unable to decode data length %d into array of length %d", ad.Len, arrayValue.Len()) } } @@ -649,7 +648,7 @@ func arrayToSlice(slicePtr reflect.Value) func(*ArrayDecoder) error { sliceValue = reflect.Append(sliceValue, elemPtr.Elem()) } } else if ad.Len > ad.MaxCollectionAlloc { - return errors.Errorf("collection exceeds max allocation limit of %d: %d", ad.MaxCollectionAlloc, ad.Len) + return fmt.Errorf("collection exceeds max allocation limit of %d: %d", ad.MaxCollectionAlloc, ad.Len) } else { sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), ad.Len, ad.Len)) @@ -672,7 +671,7 @@ func objectIntoStruct(structPtr reflect.Value) func(*ObjectDecoder) error { for o.NextEntry() { k, err := o.DecodeKey() if err != nil { - return errors.Wrapf(err, "failed to decode key with call #%d", o.count) + return fmt.Errorf("failed to decode key with call #%d: %w", o.count, err) } structValue := structPtr.Elem() f := fieldByName(structValue, k) @@ -680,10 +679,10 @@ func objectIntoStruct(structPtr reflect.Value) func(*ObjectDecoder) error { // Discard value with no matching field. // TODO could be more efficient with custom discardValue() method if _, err := o.decodeInterface(); err != nil { - return errors.Wrapf(err, "failed to discard value for %q with call #%d", k, o.count) + return fmt.Errorf("failed to discard value for %q with call #%d: %w", k, o.count, err) } } else if err := o.Decode(f.Addr().Interface()); err != nil { - return errors.Wrapf(err, "failed to decode value for %q with call #%d", k, o.count) + return fmt.Errorf("failed to decode value for %q with call #%d: %w", k, o.count, err) } } return o.End() @@ -703,7 +702,7 @@ func fieldByName(structValue reflect.Value, k string) reflect.Value { func objectIntoMap(mapPtr reflect.Value) func(*ObjectDecoder) error { return func(o *ObjectDecoder) error { if o.Len > o.MaxCollectionAlloc { - return errors.Errorf("collection exceeds max allocation limit of %d: %d", o.MaxCollectionAlloc, o.Len) + return fmt.Errorf("collection exceeds max allocation limit of %d: %d", o.MaxCollectionAlloc, o.Len) } mapValue := mapPtr.Elem() mapValue.Set(makeMap(mapValue.Type(), o.Len)) @@ -712,13 +711,13 @@ func objectIntoMap(mapPtr reflect.Value) func(*ObjectDecoder) error { for o.NextEntry() { k, err := o.DecodeKey() if err != nil { - return errors.Wrapf(err, "failed to decode key #%d", o.count) + return fmt.Errorf("failed to decode key #%d: %w", o.count, err) } valPtr := reflect.New(elemType) if err := o.Decode(valPtr.Interface()); err != nil { - return errors.Wrapf(err, "failed to decode value #%d", o.count) + return fmt.Errorf("failed to decode value #%d: %w", o.count, err) } mapValue.SetMapIndex(reflect.ValueOf(k), valPtr.Elem()) @@ -731,7 +730,7 @@ func objectIntoMap(mapPtr reflect.Value) func(*ObjectDecoder) error { // either interface{} or a stricter type if the object is strongly typed. func objectAsInterface(o *ObjectDecoder) (interface{}, error) { if o.Len > o.MaxCollectionAlloc { - return nil, errors.Errorf("collection exceeds max allocation limit of %d: %d", o.MaxCollectionAlloc, o.Len) + return nil, fmt.Errorf("collection exceeds max allocation limit of %d: %d", o.MaxCollectionAlloc, o.Len) } if o.ValType == NoOpMarker { return nil, errors.New("No-Op (N) is not a legal strong type") @@ -742,11 +741,11 @@ func objectAsInterface(o *ObjectDecoder) (interface{}, error) { for o.NextEntry() { k, err := o.DecodeKey() if err != nil { - return nil, errors.Wrapf(err, "failed to decode key #%d", o.count) + return nil, fmt.Errorf("failed to decode key #%d: %w", o.count, err) } valPtr := reflect.New(mapType.Elem()) if err := o.Decode(valPtr.Interface()); err != nil { - return nil, errors.Wrapf(err, "failed to decode value #%d", o.count) + return nil, fmt.Errorf("failed to decode value #%d: %w", o.count, err) } mapValue.SetMapIndex(reflect.ValueOf(k), valPtr.Elem()) @@ -774,7 +773,7 @@ func arrayAsInterface(a *ArrayDecoder) (interface{}, error) { sliceValue = reflect.Append(sliceValue, elemPtr.Elem()) } } else if a.Len > a.MaxCollectionAlloc { - return "", errors.Errorf("collection exceeds max allocation limit of %d: %d", a.MaxCollectionAlloc, a.Len) + return "", fmt.Errorf("collection exceeds max allocation limit of %d: %d", a.MaxCollectionAlloc, a.Len) } else { sliceValue = reflect.MakeSlice(sliceType, a.Len, a.Len) diff --git a/encode.go b/encode.go index 4c1aaf9..d10cde4 100644 --- a/encode.go +++ b/encode.go @@ -1,10 +1,10 @@ package ubjson import ( + "errors" + "fmt" "io" "reflect" - - "github.com/pkg/errors" ) // Encoder provides methods for encoding UBJSON data types. @@ -115,7 +115,7 @@ func (e *Encoder) EncodeInt(v int) error { case Int64Marker: return e.EncodeInt64(int64(v)) default: - return errors.Errorf("unsupported marker: %s", string(m)) + return fmt.Errorf("unsupported marker: %s", string(m)) } } @@ -261,7 +261,7 @@ func (a *ArrayEncoder) End() error { return err } } else if a.len != a.count { - return errors.Errorf("unable to end array of length %d after %d elements", a.len, a.count) + return fmt.Errorf("unable to end array of length %d after %d elements", a.len, a.count) } return a.Flush() @@ -339,7 +339,7 @@ func (o *ObjectEncoder) End() error { return err } } else if 2*o.len != o.count { - return errors.Errorf("unable to end map of %d entries after %d", o.len, o.count/2) + return fmt.Errorf("unable to end map of %d entries after %d", o.len, o.count/2) } return o.Flush() @@ -464,7 +464,7 @@ func (e *Encoder) Encode(v interface{}) error { case reflect.Map: if k := value.Type().Key().Kind(); k != reflect.String { - return errors.Errorf("unable to encode map of type %s: key reflect.Kind must be reflect.String but is %s", value.Type(), k) + return fmt.Errorf("unable to encode map of type %s: key reflect.Kind must be reflect.String but is %s", value.Type(), k) } return e.encode(ObjectStartMarker, encodeMap(value)) @@ -478,7 +478,7 @@ func (e *Encoder) Encode(v interface{}) error { return e.Encode(value.Elem().Interface()) } - return errors.Errorf("unable to encode value: %v", v) + return fmt.Errorf("unable to encode value: %v", v) } func encodeArray(arrayValue reflect.Value) func(*Encoder) error { @@ -501,7 +501,7 @@ func encodeArray(arrayValue reflect.Value) func(*Encoder) error { for i := 0; i < arrayValue.Len(); i++ { if err := ae.Encode(arrayValue.Index(i).Interface()); err != nil { - return errors.Wrapf(err, "failed to encode array element %d", i) + return fmt.Errorf("failed to encode array element %d: %w", i, err) } } @@ -533,10 +533,10 @@ func encodeMap(mapValue reflect.Value) func(*Encoder) error { for _, key := range keys { if err := o.EncodeKey(key.String()); err != nil { - return errors.Wrapf(err, "failed to encode key %q", key.String()) + return fmt.Errorf("failed to encode key %q: %w", key.String(), err) } if err := o.Encode(mapValue.MapIndex(key).Interface()); err != nil { - return errors.Wrapf(err, "failed to encode value for key %q", key.String()) + return fmt.Errorf("failed to encode value for key %q: %w", key.String(), err) } } @@ -562,11 +562,11 @@ func encodeStruct(structValue reflect.Value) func(*Encoder) error { panic("invalid cached type info: no index for field " + name) } if err := o.EncodeKey(name); err != nil { - return errors.Wrapf(err, "failed to encode key %q", name) + return fmt.Errorf("failed to encode key %q: %w", name, err) } val := structValue.Field(i).Interface() if err := o.Encode(val); err != nil { - return errors.Wrapf(err, "failed to encode value for key %q", name) + return fmt.Errorf("failed to encode value for key %q: %w", name, err) } } diff --git a/errors.go b/errors.go index 381ac31..817a465 100644 --- a/errors.go +++ b/errors.go @@ -1,15 +1,15 @@ package ubjson -import "github.com/pkg/errors" +import "fmt" func errTooMany(len int) error { - return errors.Errorf("too many calls for container with len %d", len) + return fmt.Errorf("too many calls for container with len %d", len) } func errWrongTypeWrite(exp, got Marker) error { - return errors.Errorf("unable to write element type '%s' to container type '%s'", got, exp) + return fmt.Errorf("unable to write element type '%s' to container type '%s'", got, exp) } func errWrongTypeRead(exp, got Marker) error { - return errors.Errorf("tried to read type '%s' but found type '%s'", exp, got) + return fmt.Errorf("tried to read type '%s' but found type '%s'", exp, got) } diff --git a/go.mod b/go.mod index 97e8910..3128aad 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,3 @@ module github.com/jmank88/ubjson -go 1.18 - -require github.com/pkg/errors v0.8.1 +go 1.20 diff --git a/go.sum b/go.sum index f29ab35..e69de29 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +0,0 @@ -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/reader.go b/reader.go index 30b774d..3eb63c4 100644 --- a/reader.go +++ b/reader.go @@ -3,12 +3,11 @@ package ubjson import ( "bufio" "encoding/binary" + "errors" "fmt" "io" "math" "strconv" - - "github.com/pkg/errors" ) // A reader reads UBJSON types. @@ -85,7 +84,7 @@ func readContainer(r reader) (Marker, int, error) { return 0, 0, err } if l < 0 { - return 0, 0, errors.Errorf("illegal negative container length: %d", l) + return 0, 0, fmt.Errorf("illegal negative container length: %d", l) } return m, l, nil @@ -98,7 +97,7 @@ func readContainer(r reader) (Marker, int, error) { return 0, 0, err } if l < 0 { - return 0, 0, errors.Errorf("illegal negative container length: %d", l) + return 0, 0, fmt.Errorf("illegal negative container length: %d", l) } return 0, l, nil @@ -209,20 +208,20 @@ func (r *binaryReader) readFloat64() (float64, error) { func (r *binaryReader) readString(max int) (string, error) { l, err := readInt(r) if err != nil { - return "", errors.Wrap(err, "failed to read string length prefix") + return "", fmt.Errorf("failed to read string length prefix: %w", err) } switch { case l == 0: return "", nil case l < 0: - return "", errors.Errorf("illegal string length prefix: %d", l) + return "", fmt.Errorf("illegal string length prefix: %d", l) case l > max: - return "", errors.Errorf("string length prefix exceeds max allocation limit of %d: %d", max, l) + return "", fmt.Errorf("string length prefix exceeds max allocation limit of %d: %d", max, l) } b := make([]byte, l) n, err := r.Read(b) if err != nil { - return "", errors.Wrap(err, "failed to read string bytes") + return "", fmt.Errorf("failed to read string bytes: %w", err) } if n != l { return "", fmt.Errorf("failed to read full string length (%d), instead got: %s", l, string(b[:n])) @@ -265,7 +264,7 @@ func (r *blockReader) nextBlock() (string, error) { // The readBlock method reads the next block. func (r *blockReader) readBlock() (string, error) { if _, err := r.ReadBytes('['); err != nil { - return "", errors.Wrap(err, "failed to read block start") + return "", fmt.Errorf("failed to read block start: %w", err) } s, err := r.ReadString(']') if err != nil { @@ -318,7 +317,7 @@ func (r *blockReader) readUInt8() (uint8, error) { } i, err := strconv.ParseUint(s, 10, 8) if err != nil { - return 0, errors.Wrapf(err, "failed to parse uint8") + return 0, fmt.Errorf("failed to parse uint8: %w", err) } return uint8(i), nil } @@ -340,7 +339,7 @@ func (r *blockReader) readBlockedInt(bitSize int) (int64, error) { } i, err := strconv.ParseInt(s, 10, bitSize) if err != nil { - return 0, errors.Wrapf(err, "failed to parse int%d", bitSize) + return 0, fmt.Errorf("failed to parse int%d: %w", bitSize, err) } return i, nil } @@ -376,19 +375,19 @@ func (r *blockReader) readFloat64() (float64, error) { func (r *blockReader) readString(max int) (string, error) { l, err := readInt(r) if err != nil { - return "", errors.Wrap(err, "failed to read string length prefix") + return "", fmt.Errorf("failed to read string length prefix: %w", err) } switch { case l == 0: return "", nil case l < 0: - return "", errors.Errorf("illegal string length prefix: %d", l) + return "", fmt.Errorf("illegal string length prefix: %d", l) case l > max: - return "", errors.Errorf("string length prefix exceeds max allocation limit of %d: %d", max, l) + return "", fmt.Errorf("string length prefix exceeds max allocation limit of %d: %d", max, l) } s, err := r.nextBlock() if err != nil { - return "", errors.Wrap(err, "failed to read string block") + return "", fmt.Errorf("failed to read string block: %w", err) } if len(s) != l { return "", fmt.Errorf("string length %d but prefix %d", len(s), l) diff --git a/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea b/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea index dd2351e..c9fab03 100644 --- a/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea +++ b/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea @@ -1,2 +1 @@ -go test fuzz v1 []byte("[$d#U\x190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") diff --git a/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f b/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f index 3fffd2b..15007ac 100644 --- a/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f +++ b/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f @@ -1,2 +1 @@ -go test fuzz v1 []byte("[$C#U\x010") diff --git a/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca b/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca index 3e0f094..00c6235 100644 --- a/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca +++ b/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca @@ -1,2 +1 @@ -go test fuzz v1 string("[[][$][C][#][U][1][0]") diff --git a/writer.go b/writer.go index c859271..8e963ee 100644 --- a/writer.go +++ b/writer.go @@ -3,12 +3,11 @@ package ubjson import ( "bufio" "encoding/binary" + "errors" "fmt" "io" "math" "strconv" - - "github.com/pkg/errors" ) type writer interface { @@ -120,7 +119,7 @@ func (w *blockWriter) writeChar(v byte) error { // The writeString method writes a length-prefixed UBJSON string. func (w *blockWriter) writeString(s string) error { if err := writeInt(w, len(s)); err != nil { - return errors.Wrap(err, "failed writing string lenth prefix") + return fmt.Errorf("failed writing string lenth prefix: %w", err) } if len(s) > 0 { @@ -204,7 +203,7 @@ func (w *binaryWriter) writeChar(v byte) error { func (w *binaryWriter) writeString(s string) error { if err := writeInt(w, len(s)); err != nil { - return errors.Wrap(err, "failed writing string lenth prefix") + return fmt.Errorf("failed writing string lenth prefix: %w", err) } if len(s) > 0 { From 73bfffff85d04cab620736fbe5aa3adc752d1b49 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Sat, 30 Sep 2023 18:40:46 -0700 Subject: [PATCH 2/2] fix fuzz tests --- fuzz_1.18_test.go | 3 +++ ...89fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea | 1 - ...24f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f | 1 - ...181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea delete mode 100644 testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f delete mode 100644 testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca diff --git a/fuzz_1.18_test.go b/fuzz_1.18_test.go index 62a03c3..6d262c5 100644 --- a/fuzz_1.18_test.go +++ b/fuzz_1.18_test.go @@ -16,6 +16,8 @@ func FuzzBinary(f *testing.F) { for _, c := range cases { f.Add(c.binary) } + f.Add([]byte("[$C#U\x010")) + f.Add([]byte("[$d#U\x190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")) const corpus = "testdata/bin/corpus" if dir, err := testdata.ReadDir(corpus); err != nil { f.Fatal("failed to read corpus dir:", err) @@ -54,6 +56,7 @@ func FuzzBlock(f *testing.F) { for _, c := range cases { f.Add(c.block) } + f.Add("[[][$][C][#][U][1][0]") const corpus = "testdata/block/corpus" if dir, err := testdata.ReadDir(corpus); err != nil { f.Fatal("failed to read corpus dir:", err) diff --git a/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea b/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea deleted file mode 100644 index c9fab03..0000000 --- a/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea +++ /dev/null @@ -1 +0,0 @@ -[]byte("[$d#U\x190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") diff --git a/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f b/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f deleted file mode 100644 index 15007ac..0000000 --- a/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f +++ /dev/null @@ -1 +0,0 @@ -[]byte("[$C#U\x010") diff --git a/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca b/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca deleted file mode 100644 index 00c6235..0000000 --- a/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca +++ /dev/null @@ -1 +0,0 @@ -string("[[][$][C][#][U][1][0]")