github.com/urso/go-structform@v0.0.2/json/visitor.go (about)

     1  package json
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"math"
     7  	"strconv"
     8  	"unicode/utf8"
     9  
    10  	structform "github.com/urso/go-structform"
    11  )
    12  
    13  // Visitor implements the structform.Visitor interface, json encoding the
    14  // structure being visited
    15  type Visitor struct {
    16  	w writer
    17  
    18  	scratch [64]byte
    19  
    20  	first   boolStack
    21  	inArray boolStack
    22  }
    23  
    24  type boolStack struct {
    25  	stack   []bool
    26  	stack0  [32]bool
    27  	current bool
    28  }
    29  
    30  var _ structform.Visitor = &Visitor{}
    31  
    32  type writer struct {
    33  	out io.Writer
    34  }
    35  
    36  func (w writer) write(b []byte) error {
    37  	_, err := w.out.Write(b)
    38  	return err
    39  }
    40  
    41  func NewVisitor(out io.Writer) *Visitor {
    42  	v := &Visitor{w: writer{out}}
    43  	return v
    44  }
    45  
    46  func (vs *Visitor) writeByte(b byte) error {
    47  	vs.scratch[0] = b
    48  	return vs.w.write(vs.scratch[:1])
    49  }
    50  
    51  func (vs *Visitor) writeString(s string) error {
    52  	return vs.w.write(str2Bytes(s))
    53  }
    54  
    55  func (vs *Visitor) OnObjectStart(_ int, _ structform.BaseType) error {
    56  	if err := vs.tryElemNext(); err != nil {
    57  		return err
    58  	}
    59  
    60  	vs.first.push(true)
    61  	vs.inArray.push(false)
    62  	return vs.writeByte('{')
    63  }
    64  
    65  func (vs *Visitor) OnObjectFinished() error {
    66  	vs.first.pop()
    67  	vs.inArray.pop()
    68  	return vs.writeByte('}')
    69  }
    70  
    71  func (vs *Visitor) OnKeyRef(s []byte) error {
    72  	if err := vs.onFieldNext(); err != nil {
    73  		return err
    74  	}
    75  
    76  	err := vs.OnStringRef(s)
    77  	if err == nil {
    78  		err = vs.writeByte(':')
    79  	}
    80  	return err
    81  }
    82  
    83  func (vs *Visitor) OnKey(s string) error {
    84  	if err := vs.onFieldNext(); err != nil {
    85  		return err
    86  	}
    87  
    88  	err := vs.OnString(s)
    89  	if err == nil {
    90  		err = vs.writeByte(':')
    91  	}
    92  	return err
    93  }
    94  
    95  func (vs *Visitor) onFieldNext() error {
    96  	if vs.first.current {
    97  		vs.first.current = false
    98  		return nil
    99  	}
   100  	return vs.writeByte(',')
   101  }
   102  
   103  func (vs *Visitor) OnArrayStart(_ int, _ structform.BaseType) error {
   104  	if err := vs.tryElemNext(); err != nil {
   105  		return err
   106  	}
   107  
   108  	vs.first.push(true)
   109  	vs.inArray.push(true)
   110  	return vs.writeByte('[')
   111  }
   112  
   113  func (vs *Visitor) OnArrayFinished() error {
   114  	vs.first.pop()
   115  	vs.inArray.pop()
   116  	return vs.writeByte(']')
   117  }
   118  
   119  func (vs *Visitor) tryElemNext() error {
   120  	if !vs.inArray.current {
   121  		return nil
   122  	}
   123  
   124  	if vs.first.current {
   125  		vs.first.current = false
   126  		return nil
   127  	}
   128  	return vs.w.write(commaSymbol)
   129  }
   130  
   131  var hex = "0123456789abcdef"
   132  
   133  func (vs *Visitor) OnStringRef(s []byte) error {
   134  	return vs.OnString(bytes2Str(s))
   135  }
   136  
   137  func (vs *Visitor) OnString(s string) error {
   138  	if err := vs.tryElemNext(); err != nil {
   139  		return err
   140  	}
   141  
   142  	vs.writeByte('"')
   143  	start := 0
   144  	for i := 0; i < len(s); {
   145  		if b := s[i]; b < utf8.RuneSelf {
   146  			if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
   147  				i++
   148  				continue
   149  			}
   150  			if start < i {
   151  				vs.writeString(s[start:i])
   152  			}
   153  			switch b {
   154  			case '\\', '"':
   155  				vs.scratch[0], vs.scratch[1] = '\\', b
   156  				vs.w.write(vs.scratch[:2])
   157  			case '\n':
   158  				vs.scratch[0], vs.scratch[1] = '\\', 'n'
   159  				vs.w.write(vs.scratch[:2])
   160  			case '\r':
   161  				vs.scratch[0], vs.scratch[1] = '\\', 'r'
   162  				vs.w.write(vs.scratch[:2])
   163  			case '\t':
   164  				vs.scratch[0], vs.scratch[1] = '\\', 't'
   165  				vs.w.write(vs.scratch[:2])
   166  			default:
   167  				// This vsodes bytes < 0x20 except for \n and \r,
   168  				// as well as <, > and &. The latter are escaped because they
   169  				// can lead to security holes when user-controlled strings
   170  				// are rendered into JSON and served to some browsers.
   171  				vs.scratch[0], vs.scratch[1], vs.scratch[2], vs.scratch[3] = '\\', 'u', '0', '0'
   172  				vs.scratch[4] = hex[b>>4]
   173  				vs.scratch[5] = hex[b&0xF]
   174  				vs.w.write(vs.scratch[:6])
   175  			}
   176  			i++
   177  			start = i
   178  			continue
   179  		}
   180  		c, size := utf8.DecodeRuneInString(s[i:])
   181  		if c == utf8.RuneError && size == 1 {
   182  			if start < i {
   183  				vs.writeString(s[start:i])
   184  			}
   185  			vs.w.write(invalidCharSym)
   186  			i += size
   187  			start = i
   188  			continue
   189  		}
   190  		// U+2028 is LINE SEPARATOR.
   191  		// U+2029 is PARAGRAPH SEPARATOR.
   192  		// They are both technically valid characters in JSON strings,
   193  		// but don't work in JSONP, which has to be evaluated as JavaScript,
   194  		// and can lead to security holes there. It is valid JSON to
   195  		// escape them, so we do so unconditionally.
   196  		// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
   197  		if c == '\u2028' || c == '\u2029' {
   198  			if start < i {
   199  				vs.writeString(s[start:i])
   200  			}
   201  			vs.writeString(`\u202`)
   202  			vs.writeByte(hex[c&0xF])
   203  			i += size
   204  			start = i
   205  			continue
   206  		}
   207  		i += size
   208  	}
   209  	if start < len(s) {
   210  		vs.writeString(s[start:])
   211  	}
   212  	vs.writeByte('"')
   213  	return nil
   214  }
   215  
   216  func (vs *Visitor) OnBool(b bool) error {
   217  	if err := vs.tryElemNext(); err != nil {
   218  		return err
   219  	}
   220  
   221  	var err error
   222  	if b {
   223  		err = vs.w.write(trueSymbol)
   224  	} else {
   225  		err = vs.w.write(falseSymbol)
   226  	}
   227  	return err
   228  }
   229  
   230  func (vs *Visitor) OnNil() error {
   231  	if err := vs.tryElemNext(); err != nil {
   232  		return err
   233  	}
   234  
   235  	err := vs.w.write(nullSymbol)
   236  	return err
   237  }
   238  
   239  func (vs *Visitor) OnInt8(i int8) error {
   240  	return vs.onInt(int64(i))
   241  }
   242  
   243  func (vs *Visitor) OnInt16(i int16) error {
   244  	return vs.onInt(int64(i))
   245  }
   246  
   247  func (vs *Visitor) OnInt32(i int32) error {
   248  	return vs.onInt(int64(i))
   249  }
   250  
   251  func (vs *Visitor) OnInt64(i int64) error {
   252  	return vs.onInt(i)
   253  }
   254  
   255  func (vs *Visitor) OnInt(i int) error {
   256  	return vs.onInt(int64(i))
   257  }
   258  
   259  func (vs *Visitor) onInt(v int64) error {
   260  	if err := vs.tryElemNext(); err != nil {
   261  		return err
   262  	}
   263  
   264  	/*
   265  		b := strconv.AppendInt(vs.scratch[:0], i, 10)
   266  		_, err := vs.w.Write(b)
   267  	*/
   268  	vs.onNumber(v < 0, uint64(v))
   269  	return nil
   270  }
   271  
   272  func (vs *Visitor) OnUint8(u uint8) error {
   273  	return vs.onUint(uint64(u))
   274  }
   275  
   276  func (vs *Visitor) OnByte(b byte) error {
   277  	return vs.onUint(uint64(b))
   278  }
   279  
   280  func (vs *Visitor) OnUint16(u uint16) error {
   281  	return vs.onUint(uint64(u))
   282  }
   283  
   284  func (vs *Visitor) OnUint32(u uint32) error {
   285  	return vs.onUint(uint64(u))
   286  }
   287  
   288  func (vs *Visitor) OnUint64(u uint64) error {
   289  	return vs.onUint(u)
   290  }
   291  
   292  func (vs *Visitor) OnUint(u uint) error {
   293  	return vs.onUint(uint64(u))
   294  }
   295  
   296  func (vs *Visitor) onUint(u uint64) error {
   297  	if err := vs.tryElemNext(); err != nil {
   298  		return err
   299  	}
   300  
   301  	return vs.onNumber(false, u)
   302  	/*
   303  		b := strconv.AppendUint(vs.scratch[:0], u, 10)
   304  		_, err := vs.w.Write(b)
   305  		return err
   306  	*/
   307  }
   308  
   309  func (vs *Visitor) onNumber(neg bool, u uint64) error {
   310  	if neg {
   311  		u = -u
   312  	}
   313  	i := len(vs.scratch)
   314  
   315  	// common case: use constants for / because
   316  	// the compiler can optimize it into a multiply+shift
   317  	if ^uintptr(0)>>32 == 0 {
   318  		for u > uint64(^uintptr(0)) {
   319  			q := u / 1e9
   320  			us := uintptr(u - q*1e9) // us % 1e9 fits into a uintptr
   321  			for j := 9; j > 0; j-- {
   322  				i--
   323  				qs := us / 10
   324  				vs.scratch[i] = byte(us - qs*10 + '0')
   325  				us = qs
   326  			}
   327  			u = q
   328  		}
   329  	}
   330  
   331  	// u guaranteed to fit into a uintptr
   332  	us := uintptr(u)
   333  	for us >= 10 {
   334  		i--
   335  		q := us / 10
   336  		vs.scratch[i] = byte(us - q*10 + '0')
   337  		us = q
   338  	}
   339  	// u < 10
   340  	i--
   341  	vs.scratch[i] = byte(us + '0')
   342  
   343  	if neg {
   344  		i--
   345  		vs.scratch[i] = '-'
   346  	}
   347  	return vs.w.write(vs.scratch[i:])
   348  }
   349  
   350  func (vs *Visitor) OnFloat32(f float32) error {
   351  	return vs.onFloat(float64(f), 32)
   352  }
   353  
   354  func (vs *Visitor) OnFloat64(f float64) error {
   355  	return vs.onFloat(f, 64)
   356  }
   357  
   358  func (vs *Visitor) onFloat(f float64, bits int) error {
   359  	if err := vs.tryElemNext(); err != nil {
   360  		return err
   361  	}
   362  
   363  	if math.IsInf(f, 0) || math.IsNaN(f) {
   364  		return fmt.Errorf("unsupported float value: %v", f)
   365  	}
   366  
   367  	b := strconv.AppendFloat(vs.scratch[:0], f, 'g', -1, bits)
   368  	err := vs.w.write(b)
   369  	return err
   370  }
   371  
   372  func (s *boolStack) init() {
   373  	s.stack = s.stack0[:0]
   374  }
   375  
   376  func (s *boolStack) push(b bool) {
   377  	s.stack = append(s.stack, s.current)
   378  	s.current = b
   379  }
   380  
   381  func (s *boolStack) pop() {
   382  	if len(s.stack) == 0 {
   383  		panic("pop from empty stack")
   384  	}
   385  
   386  	last := len(s.stack) - 1
   387  	s.current = s.stack[last]
   388  	s.stack = s.stack[:last]
   389  }