summaryrefslogtreecommitdiff
path: root/reencode_compactwsifunder.go
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-02-14 11:44:36 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-02-16 22:56:31 -0700
commitd19e2c6884c2d409fcc828c870f1839ee84f38cb (patch)
tree3a61b0c070a5db186e2c49fe70dff6f40431124e /reencode_compactwsifunder.go
parent6f8e7db1ac5ddd21b8e3fcc39a1e30fde9b62c3a (diff)
reencode: Factor into separate modules
Diffstat (limited to 'reencode_compactwsifunder.go')
-rw-r--r--reencode_compactwsifunder.go106
1 files changed, 106 insertions, 0 deletions
diff --git a/reencode_compactwsifunder.go b/reencode_compactwsifunder.go
new file mode 100644
index 0000000..2349104
--- /dev/null
+++ b/reencode_compactwsifunder.go
@@ -0,0 +1,106 @@
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package lowmemjson
+
+import (
+ "bytes"
+
+ "git.lukeshu.com/go/lowmemjson/internal/jsonparse"
+)
+
+type reEncodeCompactWSIfUnder struct {
+ out reEncoderModule
+
+ // CompactWSIfUnder runs uses reEncodeCompactWScauses for
+ // individual elements if doing so would cause that element to
+ // be under this number of bytes.
+ //
+ // This has O(2^min(CompactWSIfUnder, depth)) time overhead,
+ // so set with caution.
+ CompactWSIfUnder int
+
+ // state
+ compactor reEncodeWrite
+ compacted bytes.Buffer
+ full []handleRuneCall
+ endWhenStackSize int
+}
+
+var _ reEncoderModule = (*reEncodeCompactWSIfUnder)(nil)
+
+type handleRuneCall struct {
+ c rune
+ t jsonparse.RuneType
+ escape BackslashEscapeMode
+ stackSize int
+}
+
+func (enc *reEncodeCompactWSIfUnder) reset() {
+ enc.compactor = reEncodeWrite{}
+ enc.compacted.Reset()
+ enc.full = enc.full[:0]
+ enc.endWhenStackSize = 0
+}
+
+func (enc *reEncodeCompactWSIfUnder) PopWriteBarrier() {
+ enc.out.PopWriteBarrier()
+}
+
+func (enc *reEncodeCompactWSIfUnder) HandleRune(c rune, t jsonparse.RuneType, escape BackslashEscapeMode, stackSize int) error {
+ if enc.compactor.out == nil { // not speculating
+ switch t {
+ case jsonparse.RuneTypeObjectBeg, jsonparse.RuneTypeArrayBeg: // start speculating
+ enc.endWhenStackSize = stackSize - 1
+ enc.compactor = reEncodeWrite{
+ out: &enc.compacted,
+ }
+ enc.full = append(enc.full, handleRuneCall{
+ c: c,
+ t: t,
+ escape: escape,
+ stackSize: stackSize,
+ })
+ return enc.compactor.HandleRune(c, t, escape, stackSize)
+ default:
+ return enc.out.HandleRune(c, t, escape, stackSize)
+ }
+ } else { // speculating
+ enc.full = append(enc.full, handleRuneCall{
+ c: c,
+ t: t,
+ escape: escape,
+ stackSize: stackSize,
+ })
+ if t != jsonparse.RuneTypeSpace {
+ if err := enc.compactor.HandleRune(c, t, escape, stackSize); err != nil {
+ return err
+ }
+ }
+ switch {
+ case enc.compacted.Len() >= enc.CompactWSIfUnder: // stop speculating; use indent
+ buf := append([]handleRuneCall(nil), enc.full...)
+ enc.reset()
+ if err := enc.out.HandleRune(buf[0].c, buf[0].t, buf[0].escape, buf[0].stackSize); err != nil {
+ return err
+ }
+ for _, tuple := range buf[1:] {
+ if err := enc.HandleRune(tuple.c, tuple.t, tuple.escape, tuple.stackSize); err != nil {
+ return err
+ }
+ }
+ case stackSize == enc.endWhenStackSize: // stop speculating; use compact
+ for _, tuple := range enc.full {
+ if tuple.t == jsonparse.RuneTypeSpace {
+ continue
+ }
+ if err := enc.out.HandleRune(tuple.c, tuple.t, tuple.escape, tuple.stackSize); err != nil {
+ return err
+ }
+ }
+ enc.reset()
+ }
+ return nil
+ }
+}