diff --git a/jsn/filter.go b/jsn/filter.go index 9d01d9c..417c580 100644 --- a/jsn/filter.go +++ b/jsn/filter.go @@ -28,12 +28,19 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error { var k []byte state := expectKey instr := false + slash := 0 for i := 0; i < len(b); i++ { + if instr && b[i] == '\\' { + slash++ + continue + } + + if b[i] == '"' && (slash%2 == 0) { + instr = !instr + } + if state == expectObjClose || state == expectListClose { - if b[i-1] != '\\' && b[i] == '"' { - instr = !instr - } if !instr { switch b[i] { case '{', '[': @@ -70,7 +77,7 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error { state = expectKeyClose s = i - case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'): + case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)): state = expectColon k = b[(s + 1):i] @@ -80,7 +87,7 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error { case state == expectValue && b[i] == '"': state = expectString - case state == expectString && (b[i-1] != '\\' && b[i] == '"'): + case state == expectString && (b[i] == '"' && (slash%2 == 0)): e = i case state == expectValue && b[i] == '[': diff --git a/jsn/get.go b/jsn/get.go index 75d08ef..1975460 100644 --- a/jsn/get.go +++ b/jsn/get.go @@ -52,12 +52,19 @@ func Get(b []byte, keys [][]byte) []Field { n := 0 instr := false + slash := 0 for i := 0; i < len(b); i++ { + if instr && b[i] == '\\' { + slash++ + continue + } + + if b[i] == '"' && (slash%2 == 0) { + instr = !instr + } + if state == expectObjClose || state == expectListClose { - if b[i-1] != '\\' && b[i] == '"' { - instr = !instr - } if !instr { switch b[i] { case '{', '[': @@ -73,7 +80,7 @@ func Get(b []byte, keys [][]byte) []Field { state = expectKeyClose s = i - case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'): + case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)): state = expectColon k = b[(s + 1):i] @@ -84,7 +91,7 @@ func Get(b []byte, keys [][]byte) []Field { state = expectString s = i - case state == expectString && (b[i-1] != '\\' && b[i] == '"'): + case state == expectString && (b[i] == '"' && (slash%2 == 0)): e = i case state == expectValue && b[i] == '[': @@ -155,6 +162,8 @@ func Get(b []byte, keys [][]byte) []Field { state = expectKey e = 0 } + + slash = 0 } return res[:n] diff --git a/jsn/json_test.go b/jsn/json_test.go index 0931ad8..0f093e9 100644 --- a/jsn/json_test.go +++ b/jsn/json_test.go @@ -2,7 +2,9 @@ package jsn import ( "bytes" + "fmt" "io/ioutil" + "strings" "testing" ) @@ -163,7 +165,9 @@ var ( input6 = ` {"users" : [{"id" : 1, "email" : "vicram@gmail.com", "slug" : "vikram-rangnekar", "threads" : [], "threads_cursor" : null}, {"id" : 3, "email" : "marareilly@lang.name", "slug" : "raymundo-corwin", "threads" : [{"id" : 9, "title" : "Et alias et aut porro praesentium nam in voluptatem reiciendis quisquam perspiciatis inventore eos quia et et enim qui amet."}, {"id" : 25, "title" : "Ipsam quam nemo culpa tempore amet optio sit sed eligendi autem consequatur quaerat rem velit quibusdam quibusdam optio a voluptatem."}], "threads_cursor" : 25}], "users_cursor" : 3}` - input7, _ = ioutil.ReadFile("test.json") + input7, _ = ioutil.ReadFile("test7.json") + + input8, _ = ioutil.ReadFile("test8.json") ) func TestGet(t *testing.T) { @@ -268,6 +272,23 @@ func TestGet3(t *testing.T) { } } +func TestGet4(t *testing.T) { + exp := `"# \n\n@@@java\npackage main\n\nimport (\n \"net/http\"\n \"strings\"\n\n \"github.com/gin-gonic/gin\"\n)\n\nfunc main() {\n r := gin.Default()\n r.LoadHTMLGlob(\"templates/*\")\n\n r.GET(\"/\", handleIndex)\n r.GET(\"/to/:name\", handleIndex)\n r.Run()\n}\n\n// Hello is page data for the template\ntype Hello struct {\n Name string\n}\n\nfunc handleIndex(c *gin.Context) {\n name := c.Param(\"name\")\n if name != \"\" {\n name = strings.TrimPrefix(c.Param(\"name\"), \"/\")\n }\n c.HTML(http.StatusOK, \"hellofly.tmpl\", gin.H{\"Name\": name})\n}\n@@@\n\n\\"` + + exp = strings.ReplaceAll(exp, "@", "`") + + values := Get(input8, [][]byte{[]byte("body")}) + + if string(values[0].Key) != "body" { + t.Fatal("unexpected key") + } + + if string(values[0].Value) != exp { + fmt.Println(string(values[0].Value)) + t.Fatal("unexpected value") + } +} + func TestValue(t *testing.T) { v1 := []byte("12345") if !bytes.Equal(Value(v1), v1) { diff --git a/jsn/keys.go b/jsn/keys.go index d0960c9..ce2af1c 100644 --- a/jsn/keys.go +++ b/jsn/keys.go @@ -11,12 +11,19 @@ func Keys(b []byte) [][]byte { st := NewStack() ae := 0 instr := false + slash := 0 for i := 0; i < len(b); i++ { + if instr && b[i] == '\\' { + slash++ + continue + } + + if b[i] == '"' && (slash%2 == 0) { + instr = !instr + } + if state == expectObjClose || state == expectListClose { - if b[i-1] != '\\' && b[i] == '"' { - instr = !instr - } if !instr { switch b[i] { case '{', '[': @@ -52,7 +59,7 @@ func Keys(b []byte) [][]byte { state = expectKeyClose s = i - case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'): + case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)): state = expectColon k = b[(s + 1):i] @@ -63,7 +70,7 @@ func Keys(b []byte) [][]byte { state = expectString s = i - case state == expectString && (b[i-1] != '\\' && b[i] == '"'): + case state == expectString && (b[i] == '"' && (slash%2 == 0)): e = i case state == expectValue && b[i] == '{': @@ -135,6 +142,7 @@ func Keys(b []byte) [][]byte { e = 0 } + slash = 0 } return res diff --git a/jsn/replace.go b/jsn/replace.go index bb169e9..b910932 100644 --- a/jsn/replace.go +++ b/jsn/replace.go @@ -12,6 +12,11 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error { return errors.New("'from' and 'to' must be of the same length") } + if len(from) == 0 || len(to) == 0 { + _, err := w.Write(b) + return err + } + h := xxhash.New() tmap := make(map[uint64]int, len(from)) @@ -33,17 +38,24 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error { ws, we := -1, len(b) instr := false + slash := 0 for i := 0; i < len(b); i++ { + if instr && b[i] == '\\' { + slash++ + continue + } + // skip any left padding whitespace if ws == -1 && (b[i] == '{' || b[i] == '[') { ws = i } + if b[i] == '"' && (slash%2 == 0) { + instr = !instr + } + if state == expectObjClose || state == expectListClose { - if b[i-1] != '\\' && b[i] == '"' { - instr = !instr - } if !instr { switch b[i] { case '{', '[': @@ -59,7 +71,7 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error { state = expectKeyClose s = i - case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'): + case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)): state = expectColon if _, err := h.Write(b[(s + 1):i]); err != nil { return err @@ -73,7 +85,7 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error { state = expectString s = i - case state == expectString && (b[i-1] != '\\' && b[i] == '"'): + case state == expectString && (b[i] == '"' && (slash%2 == 0)): e = i case state == expectValue && b[i] == '[': @@ -167,6 +179,8 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error { e = 0 d = 0 } + + slash = 0 } if ws == -1 || (ws == 0 && we == len(b)) { diff --git a/jsn/strip.go b/jsn/strip.go index 3dee29b..3684a6c 100644 --- a/jsn/strip.go +++ b/jsn/strip.go @@ -12,12 +12,19 @@ func Strip(b []byte, path [][]byte) []byte { pm := false state := expectKey instr := false + slash := 0 for i := 0; i < len(b); i++ { + if instr && b[i] == '\\' { + slash++ + continue + } + + if b[i] == '"' && (slash%2 == 0) { + instr = !instr + } + if state == expectObjClose || state == expectListClose { - if b[i-1] != '\\' && b[i] == '"' { - instr = !instr - } if !instr { switch b[i] { case '{', '[': @@ -33,7 +40,7 @@ func Strip(b []byte, path [][]byte) []byte { state = expectKeyClose s = i - case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'): + case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)): state = expectColon if pi == len(path) { pi = 0 @@ -50,7 +57,7 @@ func Strip(b []byte, path [][]byte) []byte { state = expectString s = i - case state == expectString && (b[i-1] != '\\' && b[i] == '"'): + case state == expectString && (b[i] == '"' && (slash%2 == 0)): e = i case state == expectValue && b[i] == '[': @@ -107,6 +114,8 @@ func Strip(b []byte, path [][]byte) []byte { state = expectKey e = 0 } + + slash = 0 } return ob diff --git a/jsn/test.json b/jsn/test7.json similarity index 100% rename from jsn/test.json rename to jsn/test7.json diff --git a/jsn/test8.json b/jsn/test8.json new file mode 100644 index 0000000..879fed0 --- /dev/null +++ b/jsn/test8.json @@ -0,0 +1,7 @@ +{ + "data": { + "slug": "javapackage-mainimport-nethttp-strings-githubcomgi-2786", + "published": true, + "body": "# \n\n```java\npackage main\n\nimport (\n \"net/http\"\n \"strings\"\n\n \"github.com/gin-gonic/gin\"\n)\n\nfunc main() {\n r := gin.Default()\n r.LoadHTMLGlob(\"templates/*\")\n\n r.GET(\"/\", handleIndex)\n r.GET(\"/to/:name\", handleIndex)\n r.Run()\n}\n\n// Hello is page data for the template\ntype Hello struct {\n Name string\n}\n\nfunc handleIndex(c *gin.Context) {\n name := c.Param(\"name\")\n if name != \"\" {\n name = strings.TrimPrefix(c.Param(\"name\"), \"/\")\n }\n c.HTML(http.StatusOK, \"hellofly.tmpl\", gin.H{\"Name\": name})\n}\n```\n\n\\" + } +} \ No newline at end of file