ipld-eth-server/vendor/github.com/polydawn/refmt/pretty/prettyEncoder.go

244 lines
5.7 KiB
Go

package pretty
import (
"encoding/hex"
"fmt"
"io"
"strconv"
. "github.com/polydawn/refmt/tok"
)
func NewEncoder(wr io.Writer) *Encoder {
return &Encoder{
wr: wr,
stack: make([]phase, 0, 10),
}
}
func (d *Encoder) Reset() {
d.stack = d.stack[0:0]
d.current = phase_anyExpectValue
}
/*
A pretty.Encoder is a TokenSink that emits pretty-printed stuff.
The default behavior is color coded with ANSI escape sequences, so it's
snazzy looking on your terminal.
*/
type Encoder struct {
wr io.Writer
// Stack, tracking how many array and map opens are outstanding.
// (Values are only 'phase_mapExpectKeyOrEnd' and 'phase_arrExpectValueOrEnd'.)
stack []phase
current phase // shortcut to value at end of stack
// Spare memory, for use in operations on leaf nodes (e.g. temp space for an int serialization).
scratch [64]byte
}
type phase int
const (
phase_anyExpectValue phase = iota
phase_mapExpectKeyOrEnd
phase_mapExpectValue
phase_arrExpectValueOrEnd
)
func (d *Encoder) Step(tok *Token) (done bool, err error) {
switch d.current {
case phase_anyExpectValue:
switch tok.Type {
case TMapOpen:
d.pushPhase(phase_mapExpectKeyOrEnd)
d.emitMapOpen(tok)
return false, nil
case TArrOpen:
d.pushPhase(phase_arrExpectValueOrEnd)
d.emitArrOpen(tok)
return false, nil
case TMapClose:
return true, fmt.Errorf("unexpected mapClose; expected start of value")
case TArrClose:
return true, fmt.Errorf("unexpected arrClose; expected start of value")
default:
d.emitValue(tok)
d.wr.Write(wordBreak)
return true, nil
}
case phase_mapExpectKeyOrEnd:
switch tok.Type {
case TMapOpen:
return true, fmt.Errorf("unexpected mapOpen; expected start of key or end of map")
case TArrOpen:
return true, fmt.Errorf("unexpected arrOpen; expected start of key or end of map")
case TMapClose:
d.emitMapClose(tok)
return d.popPhase()
case TArrClose:
return true, fmt.Errorf("unexpected arrClose; expected start of key or end of map")
default:
switch tok.Type {
case TString, TInt, TUint:
d.wr.Write(indentWord(len(d.stack)))
d.emitValue(tok)
d.wr.Write(wordColon)
d.current = phase_mapExpectValue
return false, nil
default:
return true, fmt.Errorf("unexpected token of type %T; expected map key", *tok)
}
}
case phase_mapExpectValue:
switch tok.Type {
case TMapOpen:
d.pushPhase(phase_mapExpectKeyOrEnd)
d.emitMapOpen(tok)
return false, nil
case TArrOpen:
d.pushPhase(phase_arrExpectValueOrEnd)
d.emitArrOpen(tok)
return false, nil
case TMapClose:
return true, fmt.Errorf("unexpected mapClose; expected start of value")
case TArrClose:
return true, fmt.Errorf("unexpected arrClose; expected start of value")
default:
d.current = phase_mapExpectKeyOrEnd
d.emitValue(tok)
d.wr.Write(wordBreak)
return false, nil
}
case phase_arrExpectValueOrEnd:
switch tok.Type {
case TMapOpen:
d.pushPhase(phase_mapExpectKeyOrEnd)
d.emitMapOpen(tok)
return false, nil
case TArrOpen:
d.pushPhase(phase_arrExpectValueOrEnd)
d.emitArrOpen(tok)
return false, nil
case TMapClose:
return true, fmt.Errorf("unexpected mapClose; expected start of value or end of array")
case TArrClose:
d.emitArrClose(tok)
return d.popPhase()
default:
d.wr.Write(indentWord(len(d.stack)))
d.emitValue(tok)
d.wr.Write(wordBreak)
return false, nil
}
default:
panic("Unreachable")
}
}
func (d *Encoder) pushPhase(p phase) {
d.current = p
d.stack = append(d.stack, d.current)
}
// Pop a phase from the stack; return 'true' if stack now empty.
func (d *Encoder) popPhase() (bool, error) {
n := len(d.stack) - 1
if n == 0 {
return true, nil
}
if n < 0 { // the state machines are supposed to have already errored better
panic("prettyEncoder stack overpopped")
}
d.current = d.stack[n-1]
d.stack = d.stack[0:n]
return false, nil
}
func (d *Encoder) emitMapOpen(tok *Token) {
if tok.Tagged {
d.wr.Write(wordTag)
d.wr.Write([]byte(strconv.Itoa(tok.Tag)))
d.wr.Write(wordTagClose)
}
d.wr.Write(wordMapOpenPt1)
if tok.Length < 0 {
d.wr.Write(wordUnknownLen)
} else {
d.wr.Write([]byte(strconv.Itoa(tok.Length)))
}
d.wr.Write(wordMapOpenPt2)
d.wr.Write(wordBreak)
}
func (d *Encoder) emitMapClose(tok *Token) {
d.wr.Write(indentWord(len(d.stack) - 1))
d.wr.Write(wordMapClose)
d.wr.Write(wordBreak)
}
func (d *Encoder) emitArrOpen(tok *Token) {
if tok.Tagged {
d.wr.Write(wordTag)
d.wr.Write([]byte(strconv.Itoa(tok.Tag)))
d.wr.Write(wordTagClose)
}
d.wr.Write(wordArrOpenPt1)
if tok.Length < 0 {
d.wr.Write(wordUnknownLen)
} else {
d.wr.Write([]byte(strconv.Itoa(tok.Length)))
}
d.wr.Write(wordArrOpenPt2)
d.wr.Write(wordBreak)
}
func (d *Encoder) emitArrClose(tok *Token) {
d.wr.Write(indentWord(len(d.stack) - 1))
d.wr.Write(wordArrClose)
d.wr.Write(wordBreak)
}
func (d *Encoder) emitValue(tok *Token) {
if tok.Tagged {
d.wr.Write(wordTag)
d.wr.Write([]byte(strconv.Itoa(tok.Tag)))
d.wr.Write(wordTagClose)
}
switch tok.Type {
case TNull:
d.wr.Write(wordNull)
case TString:
d.emitString(tok.Str)
case TBytes:
dst := make([]byte, hex.EncodedLen(len(tok.Bytes)))
hex.Encode(dst, tok.Bytes)
d.wr.Write(dst)
case TBool:
switch tok.Bool {
case true:
d.wr.Write(wordTrue)
case false:
d.wr.Write(wordFalse)
}
case TInt:
b := strconv.AppendInt(d.scratch[:0], tok.Int, 10)
d.wr.Write(b)
case TUint:
b := strconv.AppendUint(d.scratch[:0], tok.Uint, 10)
d.wr.Write(b)
case TFloat64:
b := strconv.AppendFloat(d.scratch[:0], tok.Float64, 'f', 6, 64)
d.wr.Write(b)
default:
panic(fmt.Errorf("TODO finish more pretty.Encoder primitives support: unhandled token %s", tok))
}
}
func (d *Encoder) writeByte(b byte) {
d.scratch[0] = b
d.wr.Write(d.scratch[0:1])
}