index.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. package pretty
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "sort"
  6. "strconv"
  7. )
  8. type PrettyUtils struct {
  9. Options
  10. }
  11. // Options is Pretty options
  12. type Options struct {
  13. // Width is an max column width for single line arrays
  14. // Default is 80
  15. Width int
  16. // Prefix is a prefix for all lines
  17. // Default is an empty string
  18. Prefix string
  19. // Indent is the nested indentation
  20. // Default is two spaces
  21. Indent string
  22. // SortKeys will sort the keys alphabetically
  23. // Default is false
  24. SortKeys bool
  25. }
  26. // DefaultOptions is the default options for pretty formats.
  27. var defaultOptions = &Options{Width: 80, Prefix: "", Indent: " ", SortKeys: false}
  28. // Pretty converts the input json into a more human readable format where each
  29. // element is on it's own line with clear indentation.
  30. func (p PrettyUtils) Pretty(json []byte) []byte {
  31. return p.PrettyOptions(json, nil)
  32. }
  33. func (p PrettyUtils) GetDefaultOptions() *Options {
  34. return defaultOptions
  35. }
  36. // PrettyOptions is like Pretty but with customized options.
  37. func (p PrettyUtils) PrettyOptions(json []byte, opts *Options) []byte {
  38. if opts == nil {
  39. opts = p.GetDefaultOptions()
  40. }
  41. buf := make([]byte, 0, len(json))
  42. if len(opts.Prefix) != 0 {
  43. buf = append(buf, opts.Prefix...)
  44. }
  45. buf, _, _, _ = appendPrettyAny(buf, json, 0, true,
  46. opts.Width, opts.Prefix, opts.Indent, opts.SortKeys,
  47. 0, 0, -1)
  48. if len(buf) > 0 {
  49. buf = append(buf, '\n')
  50. }
  51. return buf
  52. }
  53. // Ugly removes insignificant space characters from the input json byte slice
  54. // and returns the compacted result.
  55. func (PrettyUtils) Ugly(json []byte) []byte {
  56. buf := make([]byte, 0, len(json))
  57. return ugly(buf, json)
  58. }
  59. // UglyInPlace removes insignificant space characters from the input json
  60. // byte slice and returns the compacted result. This method reuses the
  61. // input json buffer to avoid allocations. Do not use the original bytes
  62. // slice upon return.
  63. func (PrettyUtils) UglyInPlace(json []byte) []byte { return ugly(json, json) }
  64. func ugly(dst, src []byte) []byte {
  65. dst = dst[:0]
  66. for i := 0; i < len(src); i++ {
  67. if src[i] > ' ' {
  68. dst = append(dst, src[i])
  69. if src[i] == '"' {
  70. for i = i + 1; i < len(src); i++ {
  71. dst = append(dst, src[i])
  72. if src[i] == '"' {
  73. j := i - 1
  74. for ; ; j-- {
  75. if src[j] != '\\' {
  76. break
  77. }
  78. }
  79. if (j-i)%2 != 0 {
  80. break
  81. }
  82. }
  83. }
  84. }
  85. }
  86. }
  87. return dst
  88. }
  89. func isNaNOrInf(src []byte) bool {
  90. return src[0] == 'i' || //Inf
  91. src[0] == 'I' || // inf
  92. src[0] == '+' || // +Inf
  93. src[0] == 'N' || // Nan
  94. (src[0] == 'n' && len(src) > 1 && src[1] != 'u') // nan
  95. }
  96. func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
  97. for ; i < len(json); i++ {
  98. if json[i] <= ' ' {
  99. continue
  100. }
  101. if json[i] == '"' {
  102. return appendPrettyString(buf, json, i, nl)
  103. }
  104. if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' || isNaNOrInf(json[i:]) {
  105. return appendPrettyNumber(buf, json, i, nl)
  106. }
  107. if json[i] == '{' {
  108. return appendPrettyObject(buf, json, i, '{', '}', pretty, width, prefix, indent, sortkeys, tabs, nl, max)
  109. }
  110. if json[i] == '[' {
  111. return appendPrettyObject(buf, json, i, '[', ']', pretty, width, prefix, indent, sortkeys, tabs, nl, max)
  112. }
  113. switch json[i] {
  114. case 't':
  115. return append(buf, 't', 'r', 'u', 'e'), i + 4, nl, true
  116. case 'f':
  117. return append(buf, 'f', 'a', 'l', 's', 'e'), i + 5, nl, true
  118. case 'n':
  119. return append(buf, 'n', 'u', 'l', 'l'), i + 4, nl, true
  120. }
  121. }
  122. return buf, i, nl, true
  123. }
  124. type pair struct {
  125. kstart, kend int
  126. vstart, vend int
  127. }
  128. type byKeyVal struct {
  129. sorted bool
  130. json []byte
  131. buf []byte
  132. pairs []pair
  133. }
  134. func (arr *byKeyVal) Len() int {
  135. return len(arr.pairs)
  136. }
  137. func (arr *byKeyVal) Less(i, j int) bool {
  138. if arr.isLess(i, j, byKey) {
  139. return true
  140. }
  141. if arr.isLess(j, i, byKey) {
  142. return false
  143. }
  144. return arr.isLess(i, j, byVal)
  145. }
  146. func (arr *byKeyVal) Swap(i, j int) {
  147. arr.pairs[i], arr.pairs[j] = arr.pairs[j], arr.pairs[i]
  148. arr.sorted = true
  149. }
  150. type byKind int
  151. const (
  152. byKey byKind = 0
  153. byVal byKind = 1
  154. )
  155. type jtype int
  156. const (
  157. jnull jtype = iota
  158. jfalse
  159. jnumber
  160. jstring
  161. jtrue
  162. jjson
  163. )
  164. func getjtype(v []byte) jtype {
  165. if len(v) == 0 {
  166. return jnull
  167. }
  168. switch v[0] {
  169. case '"':
  170. return jstring
  171. case 'f':
  172. return jfalse
  173. case 't':
  174. return jtrue
  175. case 'n':
  176. return jnull
  177. case '[', '{':
  178. return jjson
  179. default:
  180. return jnumber
  181. }
  182. }
  183. func (arr *byKeyVal) isLess(i, j int, kind byKind) bool {
  184. k1 := arr.json[arr.pairs[i].kstart:arr.pairs[i].kend]
  185. k2 := arr.json[arr.pairs[j].kstart:arr.pairs[j].kend]
  186. var v1, v2 []byte
  187. if kind == byKey {
  188. v1 = k1
  189. v2 = k2
  190. } else {
  191. v1 = bytes.TrimSpace(arr.buf[arr.pairs[i].vstart:arr.pairs[i].vend])
  192. v2 = bytes.TrimSpace(arr.buf[arr.pairs[j].vstart:arr.pairs[j].vend])
  193. if len(v1) >= len(k1)+1 {
  194. v1 = bytes.TrimSpace(v1[len(k1)+1:])
  195. }
  196. if len(v2) >= len(k2)+1 {
  197. v2 = bytes.TrimSpace(v2[len(k2)+1:])
  198. }
  199. }
  200. t1 := getjtype(v1)
  201. t2 := getjtype(v2)
  202. if t1 < t2 {
  203. return true
  204. }
  205. if t1 > t2 {
  206. return false
  207. }
  208. if t1 == jstring {
  209. s1 := parsestr(v1)
  210. s2 := parsestr(v2)
  211. return string(s1) < string(s2)
  212. }
  213. if t1 == jnumber {
  214. n1, _ := strconv.ParseFloat(string(v1), 64)
  215. n2, _ := strconv.ParseFloat(string(v2), 64)
  216. return n1 < n2
  217. }
  218. return string(v1) < string(v2)
  219. }
  220. func parsestr(s []byte) []byte {
  221. for i := 1; i < len(s); i++ {
  222. if s[i] == '\\' {
  223. var str string
  224. json.Unmarshal(s, &str)
  225. return []byte(str)
  226. }
  227. if s[i] == '"' {
  228. return s[1:i]
  229. }
  230. }
  231. return nil
  232. }
  233. func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
  234. var ok bool
  235. if width > 0 {
  236. if pretty && open == '[' && max == -1 {
  237. // here we try to create a single line array
  238. max := width - (len(buf) - nl)
  239. if max > 3 {
  240. s1, s2 := len(buf), i
  241. buf, i, _, ok = appendPrettyObject(buf, json, i, '[', ']', false, width, prefix, "", sortkeys, 0, 0, max)
  242. if ok && len(buf)-s1 <= max {
  243. return buf, i, nl, true
  244. }
  245. buf = buf[:s1]
  246. i = s2
  247. }
  248. } else if max != -1 && open == '{' {
  249. return buf, i, nl, false
  250. }
  251. }
  252. buf = append(buf, open)
  253. i++
  254. var pairs []pair
  255. if open == '{' && sortkeys {
  256. pairs = make([]pair, 0, 8)
  257. }
  258. var n int
  259. for ; i < len(json); i++ {
  260. if json[i] <= ' ' {
  261. continue
  262. }
  263. if json[i] == close {
  264. if pretty {
  265. if open == '{' && sortkeys {
  266. buf = sortPairs(json, buf, pairs)
  267. }
  268. if n > 0 {
  269. nl = len(buf)
  270. if buf[nl-1] == ' ' {
  271. buf[nl-1] = '\n'
  272. } else {
  273. buf = append(buf, '\n')
  274. }
  275. }
  276. if buf[len(buf)-1] != open {
  277. buf = appendTabs(buf, prefix, indent, tabs)
  278. }
  279. }
  280. buf = append(buf, close)
  281. return buf, i + 1, nl, open != '{'
  282. }
  283. if open == '[' || json[i] == '"' {
  284. if n > 0 {
  285. buf = append(buf, ',')
  286. if width != -1 && open == '[' {
  287. buf = append(buf, ' ')
  288. }
  289. }
  290. var p pair
  291. if pretty {
  292. nl = len(buf)
  293. if buf[nl-1] == ' ' {
  294. buf[nl-1] = '\n'
  295. } else {
  296. buf = append(buf, '\n')
  297. }
  298. if open == '{' && sortkeys {
  299. p.kstart = i
  300. p.vstart = len(buf)
  301. }
  302. buf = appendTabs(buf, prefix, indent, tabs+1)
  303. }
  304. if open == '{' {
  305. buf, i, nl, _ = appendPrettyString(buf, json, i, nl)
  306. if sortkeys {
  307. p.kend = i
  308. }
  309. buf = append(buf, ':')
  310. if pretty {
  311. buf = append(buf, ' ')
  312. }
  313. }
  314. buf, i, nl, ok = appendPrettyAny(buf, json, i, pretty, width, prefix, indent, sortkeys, tabs+1, nl, max)
  315. if max != -1 && !ok {
  316. return buf, i, nl, false
  317. }
  318. if pretty && open == '{' && sortkeys {
  319. p.vend = len(buf)
  320. if p.kstart > p.kend || p.vstart > p.vend {
  321. // bad data. disable sorting
  322. sortkeys = false
  323. } else {
  324. pairs = append(pairs, p)
  325. }
  326. }
  327. i--
  328. n++
  329. }
  330. }
  331. return buf, i, nl, open != '{'
  332. }
  333. func sortPairs(json, buf []byte, pairs []pair) []byte {
  334. if len(pairs) == 0 {
  335. return buf
  336. }
  337. vstart := pairs[0].vstart
  338. vend := pairs[len(pairs)-1].vend
  339. arr := byKeyVal{false, json, buf, pairs}
  340. sort.Stable(&arr)
  341. if !arr.sorted {
  342. return buf
  343. }
  344. nbuf := make([]byte, 0, vend-vstart)
  345. for i, p := range pairs {
  346. nbuf = append(nbuf, buf[p.vstart:p.vend]...)
  347. if i < len(pairs)-1 {
  348. nbuf = append(nbuf, ',')
  349. nbuf = append(nbuf, '\n')
  350. }
  351. }
  352. return append(buf[:vstart], nbuf...)
  353. }
  354. func appendPrettyString(buf, json []byte, i, nl int) ([]byte, int, int, bool) {
  355. s := i
  356. i++
  357. for ; i < len(json); i++ {
  358. if json[i] == '"' {
  359. var sc int
  360. for j := i - 1; j > s; j-- {
  361. if json[j] == '\\' {
  362. sc++
  363. } else {
  364. break
  365. }
  366. }
  367. if sc%2 == 1 {
  368. continue
  369. }
  370. i++
  371. break
  372. }
  373. }
  374. return append(buf, json[s:i]...), i, nl, true
  375. }
  376. func appendPrettyNumber(buf, json []byte, i, nl int) ([]byte, int, int, bool) {
  377. s := i
  378. i++
  379. for ; i < len(json); i++ {
  380. if json[i] <= ' ' || json[i] == ',' || json[i] == ':' || json[i] == ']' || json[i] == '}' {
  381. break
  382. }
  383. }
  384. return append(buf, json[s:i]...), i, nl, true
  385. }
  386. func appendTabs(buf []byte, prefix, indent string, tabs int) []byte {
  387. if len(prefix) != 0 {
  388. buf = append(buf, prefix...)
  389. }
  390. if len(indent) == 2 && indent[0] == ' ' && indent[1] == ' ' {
  391. for i := 0; i < tabs; i++ {
  392. buf = append(buf, ' ', ' ')
  393. }
  394. } else {
  395. for i := 0; i < tabs; i++ {
  396. buf = append(buf, indent...)
  397. }
  398. }
  399. return buf
  400. }
  401. // Style is the color style
  402. type Style struct {
  403. Key, String, Number [2]string
  404. True, False, Null [2]string
  405. Escape [2]string
  406. Append func(dst []byte, c byte) []byte
  407. }
  408. func hexp(p byte) byte {
  409. switch {
  410. case p < 10:
  411. return p + '0'
  412. default:
  413. return (p - 10) + 'a'
  414. }
  415. }
  416. // TerminalStyle is for terminals
  417. var TerminalStyle *Style
  418. func init() {
  419. TerminalStyle = &Style{
  420. Key: [2]string{"\x1B[94m", "\x1B[0m"},
  421. String: [2]string{"\x1B[92m", "\x1B[0m"},
  422. Number: [2]string{"\x1B[93m", "\x1B[0m"},
  423. True: [2]string{"\x1B[96m", "\x1B[0m"},
  424. False: [2]string{"\x1B[96m", "\x1B[0m"},
  425. Null: [2]string{"\x1B[91m", "\x1B[0m"},
  426. Escape: [2]string{"\x1B[35m", "\x1B[0m"},
  427. Append: func(dst []byte, c byte) []byte {
  428. if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') {
  429. dst = append(dst, "\\u00"...)
  430. dst = append(dst, hexp((c>>4)&0xF))
  431. return append(dst, hexp((c)&0xF))
  432. }
  433. return append(dst, c)
  434. },
  435. }
  436. }
  437. // Color will colorize the json. The style parma is used for customizing
  438. // the colors. Passing nil to the style param will use the default
  439. // TerminalStyle.
  440. func (PrettyUtils) Color(src []byte, style *Style) []byte {
  441. if style == nil {
  442. style = TerminalStyle
  443. }
  444. apnd := style.Append
  445. if apnd == nil {
  446. apnd = func(dst []byte, c byte) []byte {
  447. return append(dst, c)
  448. }
  449. }
  450. type stackt struct {
  451. kind byte
  452. key bool
  453. }
  454. var dst []byte
  455. var stack []stackt
  456. for i := 0; i < len(src); i++ {
  457. if src[i] == '"' {
  458. key := len(stack) > 0 && stack[len(stack)-1].key
  459. if key {
  460. dst = append(dst, style.Key[0]...)
  461. } else {
  462. dst = append(dst, style.String[0]...)
  463. }
  464. dst = apnd(dst, '"')
  465. esc := false
  466. uesc := 0
  467. for i = i + 1; i < len(src); i++ {
  468. if src[i] == '\\' {
  469. if key {
  470. dst = append(dst, style.Key[1]...)
  471. } else {
  472. dst = append(dst, style.String[1]...)
  473. }
  474. dst = append(dst, style.Escape[0]...)
  475. dst = apnd(dst, src[i])
  476. esc = true
  477. if i+1 < len(src) && src[i+1] == 'u' {
  478. uesc = 5
  479. } else {
  480. uesc = 1
  481. }
  482. } else if esc {
  483. dst = apnd(dst, src[i])
  484. if uesc == 1 {
  485. esc = false
  486. dst = append(dst, style.Escape[1]...)
  487. if key {
  488. dst = append(dst, style.Key[0]...)
  489. } else {
  490. dst = append(dst, style.String[0]...)
  491. }
  492. } else {
  493. uesc--
  494. }
  495. } else {
  496. dst = apnd(dst, src[i])
  497. }
  498. if src[i] == '"' {
  499. j := i - 1
  500. for ; ; j-- {
  501. if src[j] != '\\' {
  502. break
  503. }
  504. }
  505. if (j-i)%2 != 0 {
  506. break
  507. }
  508. }
  509. }
  510. if esc {
  511. dst = append(dst, style.Escape[1]...)
  512. } else if key {
  513. dst = append(dst, style.Key[1]...)
  514. } else {
  515. dst = append(dst, style.String[1]...)
  516. }
  517. } else if src[i] == '{' || src[i] == '[' {
  518. stack = append(stack, stackt{src[i], src[i] == '{'})
  519. dst = apnd(dst, src[i])
  520. } else if (src[i] == '}' || src[i] == ']') && len(stack) > 0 {
  521. stack = stack[:len(stack)-1]
  522. dst = apnd(dst, src[i])
  523. } else if (src[i] == ':' || src[i] == ',') && len(stack) > 0 && stack[len(stack)-1].kind == '{' {
  524. stack[len(stack)-1].key = !stack[len(stack)-1].key
  525. dst = apnd(dst, src[i])
  526. } else {
  527. var kind byte
  528. if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' || isNaNOrInf(src[i:]) {
  529. kind = '0'
  530. dst = append(dst, style.Number[0]...)
  531. } else if src[i] == 't' {
  532. kind = 't'
  533. dst = append(dst, style.True[0]...)
  534. } else if src[i] == 'f' {
  535. kind = 'f'
  536. dst = append(dst, style.False[0]...)
  537. } else if src[i] == 'n' {
  538. kind = 'n'
  539. dst = append(dst, style.Null[0]...)
  540. } else {
  541. dst = apnd(dst, src[i])
  542. }
  543. if kind != 0 {
  544. for ; i < len(src); i++ {
  545. if src[i] <= ' ' || src[i] == ',' || src[i] == ':' || src[i] == ']' || src[i] == '}' {
  546. i--
  547. break
  548. }
  549. dst = apnd(dst, src[i])
  550. }
  551. if kind == '0' {
  552. dst = append(dst, style.Number[1]...)
  553. } else if kind == 't' {
  554. dst = append(dst, style.True[1]...)
  555. } else if kind == 'f' {
  556. dst = append(dst, style.False[1]...)
  557. } else if kind == 'n' {
  558. dst = append(dst, style.Null[1]...)
  559. }
  560. }
  561. }
  562. }
  563. return dst
  564. }
  565. // Spec strips out comments and trailing commas and convert the input to a
  566. // valid JSON per the official spec: https://tools.ietf.org/html/rfc8259
  567. //
  568. // The resulting JSON will always be the same length as the input and it will
  569. // include all of the same line breaks at matching offsets. This is to ensure
  570. // the result can be later processed by a external parser and that that
  571. // parser will report messages or errors with the correct offsets.
  572. func Spec(src []byte) []byte {
  573. return spec(src, nil)
  574. }
  575. // SpecInPlace is the same as Spec, but this method reuses the input json
  576. // buffer to avoid allocations. Do not use the original bytes slice upon return.
  577. func SpecInPlace(src []byte) []byte {
  578. return spec(src, src)
  579. }
  580. func spec(src, dst []byte) []byte {
  581. dst = dst[:0]
  582. for i := 0; i < len(src); i++ {
  583. if src[i] == '/' {
  584. if i < len(src)-1 {
  585. if src[i+1] == '/' {
  586. dst = append(dst, ' ', ' ')
  587. i += 2
  588. for ; i < len(src); i++ {
  589. if src[i] == '\n' {
  590. dst = append(dst, '\n')
  591. break
  592. } else if src[i] == '\t' || src[i] == '\r' {
  593. dst = append(dst, src[i])
  594. } else {
  595. dst = append(dst, ' ')
  596. }
  597. }
  598. continue
  599. }
  600. if src[i+1] == '*' {
  601. dst = append(dst, ' ', ' ')
  602. i += 2
  603. for ; i < len(src)-1; i++ {
  604. if src[i] == '*' && src[i+1] == '/' {
  605. dst = append(dst, ' ', ' ')
  606. i++
  607. break
  608. } else if src[i] == '\n' || src[i] == '\t' ||
  609. src[i] == '\r' {
  610. dst = append(dst, src[i])
  611. } else {
  612. dst = append(dst, ' ')
  613. }
  614. }
  615. continue
  616. }
  617. }
  618. }
  619. dst = append(dst, src[i])
  620. if src[i] == '"' {
  621. for i = i + 1; i < len(src); i++ {
  622. dst = append(dst, src[i])
  623. if src[i] == '"' {
  624. j := i - 1
  625. for ; ; j-- {
  626. if src[j] != '\\' {
  627. break
  628. }
  629. }
  630. if (j-i)%2 != 0 {
  631. break
  632. }
  633. }
  634. }
  635. } else if src[i] == '}' || src[i] == ']' {
  636. for j := len(dst) - 2; j >= 0; j-- {
  637. if dst[j] <= ' ' {
  638. continue
  639. }
  640. if dst[j] == ',' {
  641. dst[j] = ' '
  642. }
  643. break
  644. }
  645. }
  646. }
  647. return dst
  648. }