aboutsummaryrefslogtreecommitdiff
path: root/lib/bytes/json.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bytes/json.go')
-rw-r--r--lib/bytes/json.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/lib/bytes/json.go b/lib/bytes/json.go
new file mode 100644
index 00000000..1a296a3b
--- /dev/null
+++ b/lib/bytes/json.go
@@ -0,0 +1,172 @@
+// Copyright 2018, Shulhan <ms@kilabit.info>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bytes
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+)
+
+const (
+ errInvalidSyntax = "%s: invalid syntax at %d"
+)
+
+const (
+ bDoubleQuote = '"'
+ bRevSolidus = '\\'
+ bSolidus = '/'
+ bBackspace = '\b'
+ bFormFeed = '\f'
+ bLineFeed = '\n'
+ bCarReturn = '\r'
+ bTab = '\t'
+)
+
+//
+// JSONEscape escape the following character: `"` (quotation mark),
+// `\` (reverse solidus), `/` (solidus), `\b` (backspace), `\f` (formfeed),
+// `\n` (newline), `\r` (carriage return`), `\t` (horizontal tab), and control
+// character from 0 - 31.
+//
+// References:
+//
+// * https://tools.ietf.org/html/rfc7159#page-8
+//
+func JSONEscape(in []byte) []byte {
+ var buf bytes.Buffer
+
+ for x := 0; x < len(in); x++ {
+ if in[x] == bDoubleQuote || in[x] == bRevSolidus || in[x] == bSolidus {
+ buf.WriteByte(bRevSolidus)
+ buf.WriteByte(in[x])
+ continue
+ }
+ if in[x] == bBackspace {
+ buf.WriteByte(bRevSolidus)
+ buf.WriteByte('b')
+ continue
+ }
+ if in[x] == bFormFeed {
+ buf.WriteByte(bRevSolidus)
+ buf.WriteByte('f')
+ continue
+ }
+ if in[x] == bLineFeed {
+ buf.WriteByte(bRevSolidus)
+ buf.WriteByte('n')
+ continue
+ }
+ if in[x] == bCarReturn {
+ buf.WriteByte(bRevSolidus)
+ buf.WriteByte('r')
+ continue
+ }
+ if in[x] == bTab {
+ buf.WriteByte(bRevSolidus)
+ buf.WriteByte('t')
+ continue
+ }
+ if in[x] <= 31 {
+ buf.WriteString(fmt.Sprintf("\\u%04X", in[x]))
+ continue
+ }
+
+ buf.WriteByte(in[x])
+ }
+
+ return buf.Bytes()
+}
+
+//
+// JSONUnescape unescape JSON bytes, reversing what BytesJSONEscape do.
+//
+// If strict is true, any unknown control character will be returned as error.
+// For example, in string "\x", "x" is not valid control character, and the
+// function will return empty string and error.
+// If strict is false, it will return "x".
+//
+func JSONUnescape(in []byte, strict bool) ([]byte, error) {
+ var (
+ buf bytes.Buffer
+ uni bytes.Buffer
+ esc bool
+ )
+
+ for x := 0; x < len(in); x++ {
+ if esc {
+ if in[x] == 'u' {
+ uni.Reset()
+ x++
+
+ for y := 0; y < 4 && x < len(in); x++ {
+ uni.WriteByte(in[x])
+ y++
+ }
+
+ dec, err := strconv.ParseUint(uni.String(), 16, 32)
+ if err != nil {
+ return nil, err
+ }
+
+ if dec <= 31 {
+ buf.WriteByte(byte(dec))
+ } else {
+ buf.WriteRune(rune(dec))
+ }
+
+ esc = false
+ x--
+ continue
+ }
+ if in[x] == 't' {
+ buf.WriteByte(bTab)
+ esc = false
+ continue
+ }
+ if in[x] == 'r' {
+ buf.WriteByte(bCarReturn)
+ esc = false
+ continue
+ }
+ if in[x] == 'n' {
+ buf.WriteByte(bLineFeed)
+ esc = false
+ continue
+ }
+ if in[x] == 'f' {
+ buf.WriteByte(bFormFeed)
+ esc = false
+ continue
+ }
+ if in[x] == 'b' {
+ buf.WriteByte(bBackspace)
+ esc = false
+ continue
+ }
+ if in[x] == bDoubleQuote || in[x] == bRevSolidus || in[x] == bSolidus {
+ buf.WriteByte(in[x])
+ esc = false
+ continue
+ }
+
+ if strict {
+ err := fmt.Errorf(errInvalidSyntax, "BytesJSONUnescape", x)
+ return nil, err
+ }
+
+ buf.WriteByte(in[x])
+ esc = false
+ continue
+ }
+ if in[x] == bRevSolidus {
+ esc = true
+ continue
+ }
+ buf.WriteByte(in[x])
+ }
+
+ return buf.Bytes(), nil
+}