indent.go (5164B)
1 package encoder 2 3 import ( 4 "bytes" 5 "fmt" 6 7 "github.com/goccy/go-json/internal/errors" 8 ) 9 10 func takeIndentSrcRuntimeContext(src []byte) (*RuntimeContext, []byte) { 11 ctx := TakeRuntimeContext() 12 buf := ctx.Buf[:0] 13 buf = append(append(buf, src...), nul) 14 ctx.Buf = buf 15 return ctx, buf 16 } 17 18 func Indent(buf *bytes.Buffer, src []byte, prefix, indentStr string) error { 19 if len(src) == 0 { 20 return errors.ErrUnexpectedEndOfJSON("", 0) 21 } 22 23 srcCtx, srcBuf := takeIndentSrcRuntimeContext(src) 24 dstCtx := TakeRuntimeContext() 25 dst := dstCtx.Buf[:0] 26 27 dst, err := indentAndWrite(buf, dst, srcBuf, prefix, indentStr) 28 if err != nil { 29 ReleaseRuntimeContext(srcCtx) 30 ReleaseRuntimeContext(dstCtx) 31 return err 32 } 33 dstCtx.Buf = dst 34 ReleaseRuntimeContext(srcCtx) 35 ReleaseRuntimeContext(dstCtx) 36 return nil 37 } 38 39 func indentAndWrite(buf *bytes.Buffer, dst []byte, src []byte, prefix, indentStr string) ([]byte, error) { 40 dst, err := doIndent(dst, src, prefix, indentStr, false) 41 if err != nil { 42 return nil, err 43 } 44 if _, err := buf.Write(dst); err != nil { 45 return nil, err 46 } 47 return dst, nil 48 } 49 50 func doIndent(dst, src []byte, prefix, indentStr string, escape bool) ([]byte, error) { 51 buf, cursor, err := indentValue(dst, src, 0, 0, []byte(prefix), []byte(indentStr), escape) 52 if err != nil { 53 return nil, err 54 } 55 if err := validateEndBuf(src, cursor); err != nil { 56 return nil, err 57 } 58 return buf, nil 59 } 60 61 func indentValue( 62 dst []byte, 63 src []byte, 64 indentNum int, 65 cursor int64, 66 prefix []byte, 67 indentBytes []byte, 68 escape bool) ([]byte, int64, error) { 69 for { 70 switch src[cursor] { 71 case ' ', '\t', '\n', '\r': 72 cursor++ 73 continue 74 case '{': 75 return indentObject(dst, src, indentNum, cursor, prefix, indentBytes, escape) 76 case '}': 77 return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor) 78 case '[': 79 return indentArray(dst, src, indentNum, cursor, prefix, indentBytes, escape) 80 case ']': 81 return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor) 82 case '"': 83 return compactString(dst, src, cursor, escape) 84 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 85 return compactNumber(dst, src, cursor) 86 case 't': 87 return compactTrue(dst, src, cursor) 88 case 'f': 89 return compactFalse(dst, src, cursor) 90 case 'n': 91 return compactNull(dst, src, cursor) 92 default: 93 return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor) 94 } 95 } 96 } 97 98 func indentObject( 99 dst []byte, 100 src []byte, 101 indentNum int, 102 cursor int64, 103 prefix []byte, 104 indentBytes []byte, 105 escape bool) ([]byte, int64, error) { 106 if src[cursor] == '{' { 107 dst = append(dst, '{') 108 } else { 109 return nil, 0, errors.ErrExpected("expected { character for object value", cursor) 110 } 111 cursor = skipWhiteSpace(src, cursor+1) 112 if src[cursor] == '}' { 113 dst = append(dst, '}') 114 return dst, cursor + 1, nil 115 } 116 indentNum++ 117 var err error 118 for { 119 dst = append(append(dst, '\n'), prefix...) 120 for i := 0; i < indentNum; i++ { 121 dst = append(dst, indentBytes...) 122 } 123 cursor = skipWhiteSpace(src, cursor) 124 dst, cursor, err = compactString(dst, src, cursor, escape) 125 if err != nil { 126 return nil, 0, err 127 } 128 cursor = skipWhiteSpace(src, cursor) 129 if src[cursor] != ':' { 130 return nil, 0, errors.ErrSyntax( 131 fmt.Sprintf("invalid character '%c' after object key", src[cursor]), 132 cursor+1, 133 ) 134 } 135 dst = append(dst, ':', ' ') 136 dst, cursor, err = indentValue(dst, src, indentNum, cursor+1, prefix, indentBytes, escape) 137 if err != nil { 138 return nil, 0, err 139 } 140 cursor = skipWhiteSpace(src, cursor) 141 switch src[cursor] { 142 case '}': 143 dst = append(append(dst, '\n'), prefix...) 144 for i := 0; i < indentNum-1; i++ { 145 dst = append(dst, indentBytes...) 146 } 147 dst = append(dst, '}') 148 cursor++ 149 return dst, cursor, nil 150 case ',': 151 dst = append(dst, ',') 152 default: 153 return nil, 0, errors.ErrSyntax( 154 fmt.Sprintf("invalid character '%c' after object key:value pair", src[cursor]), 155 cursor+1, 156 ) 157 } 158 cursor++ 159 } 160 } 161 162 func indentArray( 163 dst []byte, 164 src []byte, 165 indentNum int, 166 cursor int64, 167 prefix []byte, 168 indentBytes []byte, 169 escape bool) ([]byte, int64, error) { 170 if src[cursor] == '[' { 171 dst = append(dst, '[') 172 } else { 173 return nil, 0, errors.ErrExpected("expected [ character for array value", cursor) 174 } 175 cursor = skipWhiteSpace(src, cursor+1) 176 if src[cursor] == ']' { 177 dst = append(dst, ']') 178 return dst, cursor + 1, nil 179 } 180 indentNum++ 181 var err error 182 for { 183 dst = append(append(dst, '\n'), prefix...) 184 for i := 0; i < indentNum; i++ { 185 dst = append(dst, indentBytes...) 186 } 187 dst, cursor, err = indentValue(dst, src, indentNum, cursor, prefix, indentBytes, escape) 188 if err != nil { 189 return nil, 0, err 190 } 191 cursor = skipWhiteSpace(src, cursor) 192 switch src[cursor] { 193 case ']': 194 dst = append(append(dst, '\n'), prefix...) 195 for i := 0; i < indentNum-1; i++ { 196 dst = append(dst, indentBytes...) 197 } 198 dst = append(dst, ']') 199 cursor++ 200 return dst, cursor, nil 201 case ',': 202 dst = append(dst, ',') 203 default: 204 return nil, 0, errors.ErrSyntax( 205 fmt.Sprintf("invalid character '%c' after array value", src[cursor]), 206 cursor+1, 207 ) 208 } 209 cursor++ 210 } 211 }