212 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			212 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| package encoder
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 
 | |
| 	"github.com/goccy/go-json/internal/errors"
 | |
| )
 | |
| 
 | |
| func takeIndentSrcRuntimeContext(src []byte) (*RuntimeContext, []byte) {
 | |
| 	ctx := TakeRuntimeContext()
 | |
| 	buf := ctx.Buf[:0]
 | |
| 	buf = append(append(buf, src...), nul)
 | |
| 	ctx.Buf = buf
 | |
| 	return ctx, buf
 | |
| }
 | |
| 
 | |
| func Indent(buf *bytes.Buffer, src []byte, prefix, indentStr string) error {
 | |
| 	if len(src) == 0 {
 | |
| 		return errors.ErrUnexpectedEndOfJSON("", 0)
 | |
| 	}
 | |
| 
 | |
| 	srcCtx, srcBuf := takeIndentSrcRuntimeContext(src)
 | |
| 	dstCtx := TakeRuntimeContext()
 | |
| 	dst := dstCtx.Buf[:0]
 | |
| 
 | |
| 	dst, err := indentAndWrite(buf, dst, srcBuf, prefix, indentStr)
 | |
| 	if err != nil {
 | |
| 		ReleaseRuntimeContext(srcCtx)
 | |
| 		ReleaseRuntimeContext(dstCtx)
 | |
| 		return err
 | |
| 	}
 | |
| 	dstCtx.Buf = dst
 | |
| 	ReleaseRuntimeContext(srcCtx)
 | |
| 	ReleaseRuntimeContext(dstCtx)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func indentAndWrite(buf *bytes.Buffer, dst []byte, src []byte, prefix, indentStr string) ([]byte, error) {
 | |
| 	dst, err := doIndent(dst, src, prefix, indentStr, false)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if _, err := buf.Write(dst); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return dst, nil
 | |
| }
 | |
| 
 | |
| func doIndent(dst, src []byte, prefix, indentStr string, escape bool) ([]byte, error) {
 | |
| 	buf, cursor, err := indentValue(dst, src, 0, 0, []byte(prefix), []byte(indentStr), escape)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if err := validateEndBuf(src, cursor); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return buf, nil
 | |
| }
 | |
| 
 | |
| func indentValue(
 | |
| 	dst []byte,
 | |
| 	src []byte,
 | |
| 	indentNum int,
 | |
| 	cursor int64,
 | |
| 	prefix []byte,
 | |
| 	indentBytes []byte,
 | |
| 	escape bool) ([]byte, int64, error) {
 | |
| 	for {
 | |
| 		switch src[cursor] {
 | |
| 		case ' ', '\t', '\n', '\r':
 | |
| 			cursor++
 | |
| 			continue
 | |
| 		case '{':
 | |
| 			return indentObject(dst, src, indentNum, cursor, prefix, indentBytes, escape)
 | |
| 		case '}':
 | |
| 			return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor)
 | |
| 		case '[':
 | |
| 			return indentArray(dst, src, indentNum, cursor, prefix, indentBytes, escape)
 | |
| 		case ']':
 | |
| 			return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor)
 | |
| 		case '"':
 | |
| 			return compactString(dst, src, cursor, escape)
 | |
| 		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
 | |
| 			return compactNumber(dst, src, cursor)
 | |
| 		case 't':
 | |
| 			return compactTrue(dst, src, cursor)
 | |
| 		case 'f':
 | |
| 			return compactFalse(dst, src, cursor)
 | |
| 		case 'n':
 | |
| 			return compactNull(dst, src, cursor)
 | |
| 		default:
 | |
| 			return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func indentObject(
 | |
| 	dst []byte,
 | |
| 	src []byte,
 | |
| 	indentNum int,
 | |
| 	cursor int64,
 | |
| 	prefix []byte,
 | |
| 	indentBytes []byte,
 | |
| 	escape bool) ([]byte, int64, error) {
 | |
| 	if src[cursor] == '{' {
 | |
| 		dst = append(dst, '{')
 | |
| 	} else {
 | |
| 		return nil, 0, errors.ErrExpected("expected { character for object value", cursor)
 | |
| 	}
 | |
| 	cursor = skipWhiteSpace(src, cursor+1)
 | |
| 	if src[cursor] == '}' {
 | |
| 		dst = append(dst, '}')
 | |
| 		return dst, cursor + 1, nil
 | |
| 	}
 | |
| 	indentNum++
 | |
| 	var err error
 | |
| 	for {
 | |
| 		dst = append(append(dst, '\n'), prefix...)
 | |
| 		for i := 0; i < indentNum; i++ {
 | |
| 			dst = append(dst, indentBytes...)
 | |
| 		}
 | |
| 		cursor = skipWhiteSpace(src, cursor)
 | |
| 		dst, cursor, err = compactString(dst, src, cursor, escape)
 | |
| 		if err != nil {
 | |
| 			return nil, 0, err
 | |
| 		}
 | |
| 		cursor = skipWhiteSpace(src, cursor)
 | |
| 		if src[cursor] != ':' {
 | |
| 			return nil, 0, errors.ErrSyntax(
 | |
| 				fmt.Sprintf("invalid character '%c' after object key", src[cursor]),
 | |
| 				cursor+1,
 | |
| 			)
 | |
| 		}
 | |
| 		dst = append(dst, ':', ' ')
 | |
| 		dst, cursor, err = indentValue(dst, src, indentNum, cursor+1, prefix, indentBytes, escape)
 | |
| 		if err != nil {
 | |
| 			return nil, 0, err
 | |
| 		}
 | |
| 		cursor = skipWhiteSpace(src, cursor)
 | |
| 		switch src[cursor] {
 | |
| 		case '}':
 | |
| 			dst = append(append(dst, '\n'), prefix...)
 | |
| 			for i := 0; i < indentNum-1; i++ {
 | |
| 				dst = append(dst, indentBytes...)
 | |
| 			}
 | |
| 			dst = append(dst, '}')
 | |
| 			cursor++
 | |
| 			return dst, cursor, nil
 | |
| 		case ',':
 | |
| 			dst = append(dst, ',')
 | |
| 		default:
 | |
| 			return nil, 0, errors.ErrSyntax(
 | |
| 				fmt.Sprintf("invalid character '%c' after object key:value pair", src[cursor]),
 | |
| 				cursor+1,
 | |
| 			)
 | |
| 		}
 | |
| 		cursor++
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func indentArray(
 | |
| 	dst []byte,
 | |
| 	src []byte,
 | |
| 	indentNum int,
 | |
| 	cursor int64,
 | |
| 	prefix []byte,
 | |
| 	indentBytes []byte,
 | |
| 	escape bool) ([]byte, int64, error) {
 | |
| 	if src[cursor] == '[' {
 | |
| 		dst = append(dst, '[')
 | |
| 	} else {
 | |
| 		return nil, 0, errors.ErrExpected("expected [ character for array value", cursor)
 | |
| 	}
 | |
| 	cursor = skipWhiteSpace(src, cursor+1)
 | |
| 	if src[cursor] == ']' {
 | |
| 		dst = append(dst, ']')
 | |
| 		return dst, cursor + 1, nil
 | |
| 	}
 | |
| 	indentNum++
 | |
| 	var err error
 | |
| 	for {
 | |
| 		dst = append(append(dst, '\n'), prefix...)
 | |
| 		for i := 0; i < indentNum; i++ {
 | |
| 			dst = append(dst, indentBytes...)
 | |
| 		}
 | |
| 		dst, cursor, err = indentValue(dst, src, indentNum, cursor, prefix, indentBytes, escape)
 | |
| 		if err != nil {
 | |
| 			return nil, 0, err
 | |
| 		}
 | |
| 		cursor = skipWhiteSpace(src, cursor)
 | |
| 		switch src[cursor] {
 | |
| 		case ']':
 | |
| 			dst = append(append(dst, '\n'), prefix...)
 | |
| 			for i := 0; i < indentNum-1; i++ {
 | |
| 				dst = append(dst, indentBytes...)
 | |
| 			}
 | |
| 			dst = append(dst, ']')
 | |
| 			cursor++
 | |
| 			return dst, cursor, nil
 | |
| 		case ',':
 | |
| 			dst = append(dst, ',')
 | |
| 		default:
 | |
| 			return nil, 0, errors.ErrSyntax(
 | |
| 				fmt.Sprintf("invalid character '%c' after array value", src[cursor]),
 | |
| 				cursor+1,
 | |
| 			)
 | |
| 		}
 | |
| 		cursor++
 | |
| 	}
 | |
| }
 |