git.lukeshu.com/go/lowmemjson@v0.3.9-0.20230723050957-72f6d13f6fb2/reencode_compactwsifunder.go (about) 1 // Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> 2 // 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 package lowmemjson 6 7 import ( 8 "bytes" 9 10 "git.lukeshu.com/go/lowmemjson/internal/jsonparse" 11 ) 12 13 type reEncodeCompactWSIfUnder struct { 14 out reEncoderModule 15 16 // CompactWSIfUnder runs uses reEncodeCompactWScauses for 17 // individual elements if doing so would cause that element to 18 // be under this number of bytes. 19 // 20 // This has O(2^min(CompactWSIfUnder, depth)) time overhead, 21 // so set with caution. 22 CompactWSIfUnder int 23 24 // state 25 compactor reEncodeWrite 26 compacted bytes.Buffer 27 full []handleRuneCall 28 endWhenStackSize int 29 } 30 31 var _ reEncoderModule = (*reEncodeCompactWSIfUnder)(nil) 32 33 type handleRuneCall struct { 34 c rune 35 t jsonparse.RuneType 36 escape BackslashEscapeMode 37 stackSize int 38 } 39 40 func (enc *reEncodeCompactWSIfUnder) reset() { 41 enc.compactor = reEncodeWrite{} 42 enc.compacted.Reset() 43 enc.full = enc.full[:0] 44 enc.endWhenStackSize = 0 45 } 46 47 func (enc *reEncodeCompactWSIfUnder) PopWriteBarrier() { 48 enc.out.PopWriteBarrier() 49 } 50 51 func (enc *reEncodeCompactWSIfUnder) HandleRune(c rune, t jsonparse.RuneType, escape BackslashEscapeMode, stackSize int) error { 52 if enc.compactor.out == nil { // not speculating 53 switch t { 54 case jsonparse.RuneTypeObjectBeg, jsonparse.RuneTypeArrayBeg: // start speculating 55 enc.endWhenStackSize = stackSize - 1 56 enc.compactor = reEncodeWrite{ 57 out: &enc.compacted, 58 } 59 enc.full = append(enc.full, handleRuneCall{ 60 c: c, 61 t: t, 62 escape: escape, 63 stackSize: stackSize, 64 }) 65 return enc.compactor.HandleRune(c, t, escape, stackSize) 66 default: 67 return enc.out.HandleRune(c, t, escape, stackSize) 68 } 69 } else { // speculating 70 enc.full = append(enc.full, handleRuneCall{ 71 c: c, 72 t: t, 73 escape: escape, 74 stackSize: stackSize, 75 }) 76 if t != jsonparse.RuneTypeSpace { 77 if err := enc.compactor.HandleRune(c, t, escape, stackSize); err != nil { 78 return err 79 } 80 } 81 switch { 82 case enc.compacted.Len() >= enc.CompactWSIfUnder: // stop speculating; use indent 83 buf := append([]handleRuneCall(nil), enc.full...) 84 enc.reset() 85 if err := enc.out.HandleRune(buf[0].c, buf[0].t, buf[0].escape, buf[0].stackSize); err != nil { 86 return err 87 } 88 for _, tuple := range buf[1:] { 89 if err := enc.HandleRune(tuple.c, tuple.t, tuple.escape, tuple.stackSize); err != nil { 90 return err 91 } 92 } 93 case stackSize == enc.endWhenStackSize: // stop speculating; use compact 94 for _, tuple := range enc.full { 95 if tuple.t == jsonparse.RuneTypeSpace { 96 continue 97 } 98 if err := enc.out.HandleRune(tuple.c, tuple.t, tuple.escape, tuple.stackSize); err != nil { 99 return err 100 } 101 } 102 enc.reset() 103 } 104 return nil 105 } 106 }