ParseBytes no longer copies data + added result.Bytes()
Summary
ParseBytes(json []byte) Resultnow uses thebytesStringhelper function (unsafe cast) overstring(...)conversion to avoid copying the byte slice.- A new
result.Bytes()helper was added to accessresult.Rawas[]bytewithout copying data.
Motivation
This change aims at allowing the use of []byte over string as input when using gjson without having to copy data or perform extra memory allocations, since one of the selling points of this library is its ability to parse and process JSON with no copy / zero allocations.
@tidwall , what do you think about this change?
This PR breaks the rules of immutability for strings in Go.
Using the change in this PR, run this code:
json := []byte(`{ "first": "Janet", "last": "Prichard" }`)
first := gjson.ParseBytes(json).Get("first").String()
println(first)
copy(json[11:18], `"Carol"`)
println(first)
// Output:
// Janet
// Carol
It should not be possible to change the backed memory of a string, but in the example above the string first was changed from "Janet" to "Carol".
This is because the json byte slice was unsafely cast as a string in ParseBytes and returned as a string field in the Result type. Then further calls to Result expects that string to be immutable.
A safer workaround would be to cast before parse, then immediately copy the result (which contains a mutable string), back into an immutable string.
json := []byte(`{ "first": "Janet", "last": "Prichard" }`)
jsonStr := *(*string)(unsafe.Pointer(&json)) // cast []bytes to string, zero-copy
first := Parse(jsonStr).Get("first").String() // use Parse instead of ParseBytes
first = string([]byte(first)) // copy result back into string
println(first)
copy(json[11:18], `"Carol"`)
println(first)
// Output:
// Janet
// Janet