diff --git a/conv.go b/conv.go index 70d3dc1..a363715 100644 --- a/conv.go +++ b/conv.go @@ -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() @@ -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. @@ -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 { diff --git a/jsonvalue.go b/jsonvalue.go index 65e34ae..c0bf464 100644 --- a/jsonvalue.go +++ b/jsonvalue.go @@ -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)) @@ -199,6 +199,8 @@ func Unmarshal(b []byte) (ret *V, err error) { return nil, ErrRawBytesUnrecignized } +var dot = []byte{'.'} + func (v *V) parseNumber() (err error) { b := v.valueBytes @@ -206,7 +208,7 @@ func (v *V) parseNumber() (err error) { // 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 { @@ -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) @@ -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) @@ -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'} @@ -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) @@ -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 diff --git a/unsafe.go b/unsafe.go new file mode 100644 index 0000000..34f544a --- /dev/null +++ b/unsafe.go @@ -0,0 +1,7 @@ +package jsonvalue + +import "unsafe" + +func unsafeBtoS(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +}