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  }