gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

compact.go (6929B)


      1 package encoder
      2 
      3 import (
      4 	"bytes"
      5 	"fmt"
      6 	"strconv"
      7 	"unsafe"
      8 
      9 	"github.com/goccy/go-json/internal/errors"
     10 )
     11 
     12 var (
     13 	isWhiteSpace = [256]bool{
     14 		' ':  true,
     15 		'\n': true,
     16 		'\t': true,
     17 		'\r': true,
     18 	}
     19 	isHTMLEscapeChar = [256]bool{
     20 		'<': true,
     21 		'>': true,
     22 		'&': true,
     23 	}
     24 	nul = byte('\000')
     25 )
     26 
     27 func Compact(buf *bytes.Buffer, src []byte, escape bool) error {
     28 	if len(src) == 0 {
     29 		return errors.ErrUnexpectedEndOfJSON("", 0)
     30 	}
     31 	buf.Grow(len(src))
     32 	dst := buf.Bytes()
     33 
     34 	ctx := TakeRuntimeContext()
     35 	ctxBuf := ctx.Buf[:0]
     36 	ctxBuf = append(append(ctxBuf, src...), nul)
     37 	ctx.Buf = ctxBuf
     38 
     39 	if err := compactAndWrite(buf, dst, ctxBuf, escape); err != nil {
     40 		ReleaseRuntimeContext(ctx)
     41 		return err
     42 	}
     43 	ReleaseRuntimeContext(ctx)
     44 	return nil
     45 }
     46 
     47 func compactAndWrite(buf *bytes.Buffer, dst []byte, src []byte, escape bool) error {
     48 	dst, err := compact(dst, src, escape)
     49 	if err != nil {
     50 		return err
     51 	}
     52 	if _, err := buf.Write(dst); err != nil {
     53 		return err
     54 	}
     55 	return nil
     56 }
     57 
     58 func compact(dst, src []byte, escape bool) ([]byte, error) {
     59 	buf, cursor, err := compactValue(dst, src, 0, escape)
     60 	if err != nil {
     61 		return nil, err
     62 	}
     63 	if err := validateEndBuf(src, cursor); err != nil {
     64 		return nil, err
     65 	}
     66 	return buf, nil
     67 }
     68 
     69 func validateEndBuf(src []byte, cursor int64) error {
     70 	for {
     71 		switch src[cursor] {
     72 		case ' ', '\t', '\n', '\r':
     73 			cursor++
     74 			continue
     75 		case nul:
     76 			return nil
     77 		}
     78 		return errors.ErrSyntax(
     79 			fmt.Sprintf("invalid character '%c' after top-level value", src[cursor]),
     80 			cursor+1,
     81 		)
     82 	}
     83 }
     84 
     85 func skipWhiteSpace(buf []byte, cursor int64) int64 {
     86 LOOP:
     87 	if isWhiteSpace[buf[cursor]] {
     88 		cursor++
     89 		goto LOOP
     90 	}
     91 	return cursor
     92 }
     93 
     94 func compactValue(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) {
     95 	for {
     96 		switch src[cursor] {
     97 		case ' ', '\t', '\n', '\r':
     98 			cursor++
     99 			continue
    100 		case '{':
    101 			return compactObject(dst, src, cursor, escape)
    102 		case '}':
    103 			return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor)
    104 		case '[':
    105 			return compactArray(dst, src, cursor, escape)
    106 		case ']':
    107 			return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor)
    108 		case '"':
    109 			return compactString(dst, src, cursor, escape)
    110 		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
    111 			return compactNumber(dst, src, cursor)
    112 		case 't':
    113 			return compactTrue(dst, src, cursor)
    114 		case 'f':
    115 			return compactFalse(dst, src, cursor)
    116 		case 'n':
    117 			return compactNull(dst, src, cursor)
    118 		default:
    119 			return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor)
    120 		}
    121 	}
    122 }
    123 
    124 func compactObject(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) {
    125 	if src[cursor] == '{' {
    126 		dst = append(dst, '{')
    127 	} else {
    128 		return nil, 0, errors.ErrExpected("expected { character for object value", cursor)
    129 	}
    130 	cursor = skipWhiteSpace(src, cursor+1)
    131 	if src[cursor] == '}' {
    132 		dst = append(dst, '}')
    133 		return dst, cursor + 1, nil
    134 	}
    135 	var err error
    136 	for {
    137 		cursor = skipWhiteSpace(src, cursor)
    138 		dst, cursor, err = compactString(dst, src, cursor, escape)
    139 		if err != nil {
    140 			return nil, 0, err
    141 		}
    142 		cursor = skipWhiteSpace(src, cursor)
    143 		if src[cursor] != ':' {
    144 			return nil, 0, errors.ErrExpected("colon after object key", cursor)
    145 		}
    146 		dst = append(dst, ':')
    147 		dst, cursor, err = compactValue(dst, src, cursor+1, escape)
    148 		if err != nil {
    149 			return nil, 0, err
    150 		}
    151 		cursor = skipWhiteSpace(src, cursor)
    152 		switch src[cursor] {
    153 		case '}':
    154 			dst = append(dst, '}')
    155 			cursor++
    156 			return dst, cursor, nil
    157 		case ',':
    158 			dst = append(dst, ',')
    159 		default:
    160 			return nil, 0, errors.ErrExpected("comma after object value", cursor)
    161 		}
    162 		cursor++
    163 	}
    164 }
    165 
    166 func compactArray(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) {
    167 	if src[cursor] == '[' {
    168 		dst = append(dst, '[')
    169 	} else {
    170 		return nil, 0, errors.ErrExpected("expected [ character for array value", cursor)
    171 	}
    172 	cursor = skipWhiteSpace(src, cursor+1)
    173 	if src[cursor] == ']' {
    174 		dst = append(dst, ']')
    175 		return dst, cursor + 1, nil
    176 	}
    177 	var err error
    178 	for {
    179 		dst, cursor, err = compactValue(dst, src, cursor, escape)
    180 		if err != nil {
    181 			return nil, 0, err
    182 		}
    183 		cursor = skipWhiteSpace(src, cursor)
    184 		switch src[cursor] {
    185 		case ']':
    186 			dst = append(dst, ']')
    187 			cursor++
    188 			return dst, cursor, nil
    189 		case ',':
    190 			dst = append(dst, ',')
    191 		default:
    192 			return nil, 0, errors.ErrExpected("comma after array value", cursor)
    193 		}
    194 		cursor++
    195 	}
    196 }
    197 
    198 func compactString(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) {
    199 	if src[cursor] != '"' {
    200 		return nil, 0, errors.ErrInvalidCharacter(src[cursor], "string", cursor)
    201 	}
    202 	start := cursor
    203 	for {
    204 		cursor++
    205 		c := src[cursor]
    206 		if escape {
    207 			if isHTMLEscapeChar[c] {
    208 				dst = append(dst, src[start:cursor]...)
    209 				dst = append(dst, `\u00`...)
    210 				dst = append(dst, hex[c>>4], hex[c&0xF])
    211 				start = cursor + 1
    212 			} else if c == 0xE2 && cursor+2 < int64(len(src)) && src[cursor+1] == 0x80 && src[cursor+2]&^1 == 0xA8 {
    213 				dst = append(dst, src[start:cursor]...)
    214 				dst = append(dst, `\u202`...)
    215 				dst = append(dst, hex[src[cursor+2]&0xF])
    216 				cursor += 2
    217 				start = cursor + 3
    218 			}
    219 		}
    220 		switch c {
    221 		case '\\':
    222 			cursor++
    223 			if src[cursor] == nul {
    224 				return nil, 0, errors.ErrUnexpectedEndOfJSON("string", int64(len(src)))
    225 			}
    226 		case '"':
    227 			cursor++
    228 			return append(dst, src[start:cursor]...), cursor, nil
    229 		case nul:
    230 			return nil, 0, errors.ErrUnexpectedEndOfJSON("string", int64(len(src)))
    231 		}
    232 	}
    233 }
    234 
    235 func compactNumber(dst, src []byte, cursor int64) ([]byte, int64, error) {
    236 	start := cursor
    237 	for {
    238 		cursor++
    239 		if floatTable[src[cursor]] {
    240 			continue
    241 		}
    242 		break
    243 	}
    244 	num := src[start:cursor]
    245 	if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&num)), 64); err != nil {
    246 		return nil, 0, err
    247 	}
    248 	dst = append(dst, num...)
    249 	return dst, cursor, nil
    250 }
    251 
    252 func compactTrue(dst, src []byte, cursor int64) ([]byte, int64, error) {
    253 	if cursor+3 >= int64(len(src)) {
    254 		return nil, 0, errors.ErrUnexpectedEndOfJSON("true", cursor)
    255 	}
    256 	if !bytes.Equal(src[cursor:cursor+4], []byte(`true`)) {
    257 		return nil, 0, errors.ErrInvalidCharacter(src[cursor], "true", cursor)
    258 	}
    259 	dst = append(dst, "true"...)
    260 	cursor += 4
    261 	return dst, cursor, nil
    262 }
    263 
    264 func compactFalse(dst, src []byte, cursor int64) ([]byte, int64, error) {
    265 	if cursor+4 >= int64(len(src)) {
    266 		return nil, 0, errors.ErrUnexpectedEndOfJSON("false", cursor)
    267 	}
    268 	if !bytes.Equal(src[cursor:cursor+5], []byte(`false`)) {
    269 		return nil, 0, errors.ErrInvalidCharacter(src[cursor], "false", cursor)
    270 	}
    271 	dst = append(dst, "false"...)
    272 	cursor += 5
    273 	return dst, cursor, nil
    274 }
    275 
    276 func compactNull(dst, src []byte, cursor int64) ([]byte, int64, error) {
    277 	if cursor+3 >= int64(len(src)) {
    278 		return nil, 0, errors.ErrUnexpectedEndOfJSON("null", cursor)
    279 	}
    280 	if !bytes.Equal(src[cursor:cursor+4], []byte(`null`)) {
    281 		return nil, 0, errors.ErrInvalidCharacter(src[cursor], "null", cursor)
    282 	}
    283 	dst = append(dst, "null"...)
    284 	cursor += 4
    285 	return dst, cursor, nil
    286 }