Skip to content

Commit

Permalink
[*] feat: remove some string() conversion to cut off unmarshal time
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew-M-C committed Apr 21, 2021
1 parent 33803d2 commit 4b3d5b4
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 27 deletions.
45 changes: 26 additions & 19 deletions conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@ import (
"unicode"
"unicode/utf16"
"unicode/utf8"
"unsafe"
)

func parseUint(b []byte) (uint64, error) {
return strconv.ParseUint(string(b), 10, 64)
return strconv.ParseUint(unsafeBtoS(b), 10, 64)
}

func parseInt(b []byte) (int64, error) {
return strconv.ParseInt(string(b), 10, 64)
return strconv.ParseInt(unsafeBtoS(b), 10, 64)
}

func parseFloat(b []byte) (float64, error) {
return strconv.ParseFloat(string(b), 64)
return strconv.ParseFloat(unsafeBtoS(b), 64)
}

// reference: https://golang.org/src/encoding/json/decode.go, func unquote()
Expand All @@ -35,17 +34,19 @@ func parseString(b []byte) (string, []byte, error) {

t, ok := unquoteBytes(b)
if !ok {
return "", nil, fmt.Errorf("invalid string '%s'", string(b))
return "", nil, fmt.Errorf("invalid string '%s'", unsafeBtoS(b))
}
return string(t), b, nil
return unsafeBtoS(t), b, nil
}

func unquoteBytes(s []byte) (t []byte, ok bool) {
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
return
}
s = s[1 : len(s)-1]
return parseStrText(s[1 : len(s)-1])
}

func parseStrText(s []byte) (t []byte, ok bool) {
// Check for unusual characters. If there are none,
// then no unquoting is needed, so return a slice of the
// original bytes.
Expand Down Expand Up @@ -176,19 +177,25 @@ func getu4(s []byte) rune {
}

func parseStringNoQuote(b []byte) (string, error) {
if len(b) == 0 {
return "", nil
}
s := unsafe.Sizeof(b[0])
sh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
bh := reflect.SliceHeader{
Data: sh.Data - s,
Len: sh.Len + int(s+s),
Cap: sh.Len + int(s+s),
// if len(b) == 0 {
// return "", nil
// }
// s := unsafe.Sizeof(b[0])
// sh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
// bh := &reflect.SliceHeader{
// Data: sh.Data - s,
// Len: sh.Len + int(s+s),
// Cap: sh.Len + int(s+s),
// }
// b = *(*[]byte)(unsafe.Pointer(bh))
// str, _, err := parseString(b)
// return str, err

t, ok := parseStrText(b)
if !ok {
return "", fmt.Errorf("invalid string '%s'", string(b))
}
b = *(*[]byte)(unsafe.Pointer(&bh))
str, _, err := parseString(b)
return str, err
return unsafeBtoS(t), nil
}

func formatBool(b bool) string {
Expand Down
18 changes: 10 additions & 8 deletions jsonvalue.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ func (v *V) delCaselessKey(k string) {
}
}

// UnmarshalString is equavilent to Unmarshal(string(b)), but much more efficient.
// UnmarshalString is equavilent to Unmarshal(unsafeBtoS(b)), but much more efficient.
//
// UnmarshalString 等效于 Unmarshal(string(b)),但效率更高。
// UnmarshalString 等效于 Unmarshal(unsafeBtoS(b)),但效率更高。
func UnmarshalString(s string) (*V, error) {
// reference: https://stackoverflow.com/questions/41591097/slice-bounds-out-of-range-when-using-unsafe-pointer
// sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
Expand Down Expand Up @@ -199,14 +199,16 @@ func Unmarshal(b []byte) (ret *V, err error) {
return nil, ErrRawBytesUnrecignized
}

var dot = []byte{'.'}

func (v *V) parseNumber() (err error) {
b := v.valueBytes

// if v.num == nil {
// v.num = &num{}
// }

if bytes.Contains(b, []byte(".")) {
if bytes.Contains(b, dot) {
v.num.floated = true
v.num.f64, err = parseFloat(b)
if err != nil {
Expand Down Expand Up @@ -259,7 +261,7 @@ func newFromNumber(b []byte) (ret *V, err error) {
// }

func newFromTrue(b []byte) (ret *V, err error) {
if len(b) != 4 || string(b) != "true" {
if len(b) != 4 || unsafeBtoS(b) != "true" {
return nil, ErrNotValidBoolValue
}
v := new(jsonparser.Boolean)
Expand All @@ -270,7 +272,7 @@ func newFromTrue(b []byte) (ret *V, err error) {
}

func newFromFalse(b []byte) (ret *V, err error) {
if len(b) != 5 || string(b) != "false" {
if len(b) != 5 || unsafeBtoS(b) != "false" {
return nil, ErrNotValidBoolValue
}
v := new(jsonparser.Boolean)
Expand All @@ -283,7 +285,7 @@ func newFromFalse(b []byte) (ret *V, err error) {
func newFromBool(b []byte) (ret *V, err error) {
v := new(jsonparser.Boolean)

switch string(b) {
switch unsafeBtoS(b) {
case "true":
v.parsed = true
v.valueBytes = []byte{'t', 'r', 'u', 'e'}
Expand All @@ -300,7 +302,7 @@ func newFromBool(b []byte) (ret *V, err error) {
}

func newFromNull(b []byte) (ret *V, err error) {
if len(b) != 4 || string(b) != "null" {
if len(b) != 4 || unsafeBtoS(b) != "null" {
return nil, ErrNotValidBoolValue
}
v := new(jsonparser.Null)
Expand Down Expand Up @@ -682,7 +684,7 @@ func (v *V) String() string {
case jsonparser.Null:
return "null"
case jsonparser.Number:
return string(v.valueBytes)
return unsafeBtoS(v.valueBytes)
case jsonparser.String:
if !v.parsed {
var e error
Expand Down
7 changes: 7 additions & 0 deletions unsafe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package jsonvalue

import "unsafe"

func unsafeBtoS(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}

0 comments on commit 4b3d5b4

Please sign in to comment.