corso/src/cmd/jsondebug/decoder/manifst_decoder.go
Ashlie Martinez 1cc3247721 Interface/code for running benchmarks on buffers
Setup things so we can remove the time it takes to read the input data.
2023-03-31 16:50:09 -07:00

301 lines
6.4 KiB
Go

package decoder
import (
"bytes"
"encoding/json"
"io"
"github.com/pkg/errors"
"github.com/alcionai/corso/src/cmd/jsondebug/common"
)
var (
_ common.ManifestDecoder = Array{}
_ common.ByteManifestDecoder = Array{}
_ common.ManifestDecoder = ArrayFull{}
_ common.ByteManifestDecoder = ArrayFull{}
_ common.ManifestDecoder = Map{}
_ common.ByteManifestDecoder = Map{}
)
type Array struct{}
func (d Array) Decode(r io.Reader, gcStats bool) error {
_, err := DecodeManifestArray(r)
return err
}
func (d Array) DecodeBytes(data []byte, gcStats bool) error {
r := bytes.NewReader(data)
_, err := DecodeManifestArray(r)
return err
}
func DecodeManifestArray(r io.Reader) (common.Manifest, error) {
var (
dec = json.NewDecoder(r)
res = common.Manifest{}
)
if err := expectDelimToken(dec, objectOpen); err != nil {
return res, err
}
// Need to manually decode fields here since we can't reuse the stdlib
// decoder due to memory issues.
if err := parseManifestFields(dec, &res); err != nil {
return res, err
}
// Consumes closing object curly brace after we're done. Don't need to check
// for EOF because json.Decode only guarantees decoding the next JSON item in
// the stream so this follows that.
return res, expectDelimToken(dec, objectClose)
}
func parseManifestFields(dec *json.Decoder, res *common.Manifest) error {
var seen bool
for dec.More() {
l, err := stringToken(dec)
if err != nil {
return err
}
// Only have `entries` field right now. This is stricter than the current
// JSON decoder in the stdlib.
if l != "entries" {
return errors.Errorf("unexpected field name %s", l)
} else if seen {
return errors.New("repeated Entries field")
}
seen = true
if err := decodeArray(dec, &res.Entries); err != nil {
return err
}
}
return nil
}
type ArrayFull struct{}
func (d ArrayFull) Decode(r io.Reader, gcStats bool) error {
_, err := d.decodeManifestArray(r)
return err
}
func (d ArrayFull) DecodeBytes(data []byte, gcStats bool) error {
r := bytes.NewReader(data)
_, err := d.decodeManifestArray(r)
return err
}
func (d ArrayFull) decodeManifestArray(r io.Reader) (common.Manifest, error) {
var (
dec = json.NewDecoder(r)
res = common.Manifest{}
)
if err := expectDelimToken(dec, objectOpen); err != nil {
return res, err
}
// Need to manually decode fields here since we can't reuse the stdlib
// decoder due to memory issues.
if err := d.parseManifestEntries(dec, &res); err != nil {
return res, err
}
// Consumes closing object curly brace after we're done. Don't need to check
// for EOF because json.Decode only guarantees decoding the next JSON item in
// the stream so this follows that.
return res, expectDelimToken(dec, objectClose)
}
func (d ArrayFull) parseManifestEntries(dec *json.Decoder, res *common.Manifest) error {
var seen bool
for dec.More() {
l, err := stringToken(dec)
if err != nil {
return err
}
// Only have `entries` field right now. This is stricter than the current
// JSON decoder in the stdlib.
if l != "entries" {
return errors.Errorf("unexpected field name %s", l)
} else if seen {
return errors.New("repeated Entries field")
}
seen = true
if err := expectDelimToken(dec, arrayOpen); err != nil {
return err
}
for dec.More() {
ent, err := d.parseManifestFields(dec)
if err != nil {
return err
}
res.Entries = append(res.Entries, ent)
}
if err := expectDelimToken(dec, arrayClose); err != nil {
return err
}
}
return nil
}
func (d ArrayFull) parseManifestFields(dec *json.Decoder) (*common.ManifestEntry, error) {
if err := expectDelimToken(dec, objectOpen); err != nil {
return nil, err
}
var (
seen = map[string]struct{}{}
res = &common.ManifestEntry{}
)
for dec.More() {
l, err := stringToken(dec)
if err != nil {
return nil, err
}
if _, ok := seen[l]; ok {
return nil, errors.Errorf("repeated field %s", l)
}
switch l {
case "id":
err = dec.Decode(&res.ID)
case "labels":
err = dec.Decode(&res.Labels)
case "modified":
err = dec.Decode(&res.ModTime)
case "deleted":
err = dec.Decode(&res.Deleted)
case "data":
err = dec.Decode(&res.Content)
default:
if _, err := dec.Token(); err != nil {
return nil, errors.Wrapf(err, "consuming value for unexpected field %s", l)
}
continue
}
seen[l] = struct{}{}
if err != nil {
return nil, errors.Wrapf(err, "decoding value for field %s", l)
}
}
if err := expectDelimToken(dec, objectClose); err != nil {
return nil, err
}
return res, nil
}
type Map struct{}
func (d Map) Decode(r io.Reader, gcStats bool) error {
_, err := d.decodeManifestArray(r)
return err
}
func (d Map) DecodeBytes(data []byte, gcStats bool) error {
r := bytes.NewReader(data)
_, err := d.decodeManifestArray(r)
return err
}
func (d Map) decodeManifestArray(r io.Reader) (common.Manifest, error) {
var (
dec = json.NewDecoder(r)
res = common.Manifest{}
)
if err := expectDelimToken(dec, objectOpen); err != nil {
return res, err
}
// Need to manually decode fields here since we can't reuse the stdlib
// decoder due to memory issues.
if err := d.parseManifestEntries(dec, &res); err != nil {
return res, err
}
// Consumes closing object curly brace after we're done. Don't need to check
// for EOF because json.Decode only guarantees decoding the next JSON item in
// the stream so this follows that.
return res, expectDelimToken(dec, objectClose)
}
func (d Map) parseManifestEntries(dec *json.Decoder, res *common.Manifest) error {
var seen bool
for dec.More() {
l, err := stringToken(dec)
if err != nil {
return err
}
// Only have `entries` field right now. This is stricter than the current
// JSON decoder in the stdlib.
if l != "entries" {
return errors.Errorf("unexpected field name %s", l)
} else if seen {
return errors.New("repeated Entries field")
}
seen = true
if err := expectDelimToken(dec, arrayOpen); err != nil {
return err
}
for dec.More() {
ent := map[string]any{}
if err := dec.Decode(&ent); err != nil {
return err
}
// Give up here, just check how many bytes it needs during benchmarking.
// fmt.Printf("%+v\n", ent)
// return errors.New("exit early")
// me := &common.ManifestEntry{
// ModTime:
// }
} //nolint: wsl
if err := expectDelimToken(dec, arrayClose); err != nil {
return err
}
}
return nil
}