2
0
Fork 0
mirror of https://github.com/ii64/sonic.git synced 2026-06-20 16:45:22 +08:00

opt: skip number in json.Number parsing (#209)

* opt: skip number in json.Number  parsing

* fix: generic use skip_number

* test: add json.Number decoding benchmarks

Co-authored-by: liuqiang <liuqiang.06@bytedance.com>
Co-authored-by: duanyi.aster <duanyi.aster@bytedance.com>
This commit is contained in:
liu 2022-04-01 17:45:06 +08:00 committed by GitHub
parent fe2497d01e
commit 57989f38ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1570 additions and 1153 deletions

View file

@ -581,15 +581,6 @@ func (self *_Assembler) check_err() {
self.Sjmp("JS" , _LB_parsing_error_v) // JNE _parsing_error_v
}
func (self *_Assembler) check_err_num() {
self.Emit("MOVQ" , _VAR_st_Vt, _AX) // MOVQ st.Vt, AX
self.Emit("TESTQ", _AX, _AX) // CMPQ AX, ${native.V_STRING}
self.Sjmp("JNS" , "check_err_end_{n}")
self.Emit("CMPQ" , _AX, jit.Imm(-int64(types.ERR_FLOAT_INFINITY)))
self.Sjmp("JNE" , _LB_parsing_error_v)
self.Link("check_err_end_{n}")
}
func (self *_Assembler) check_eof(d int64) {
if d == 1 {
self.Emit("CMPQ", _IC, _IL) // CMPQ IC, IL
@ -942,6 +933,7 @@ func (self *_Assembler) mapassign_utext(t reflect.Type, addressable bool) {
var (
_F_skip_one = jit.Imm(int64(native.S_skip_one))
_F_skip_number = jit.Imm(int64(native.S_skip_number))
)
func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool) {
@ -1176,13 +1168,21 @@ func (self *_Assembler) _asm_OP_bool(_ *_Instr) {
func (self *_Assembler) _asm_OP_num(_ *_Instr) {
self.Emit("MOVQ", jit.Imm(0), _VAR_fl)
self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm('"'))
self.Sjmp("JNE", "_parse_number_{n}")
self.Sjmp("JNE", "_skip_number_{n}")
self.Emit("MOVQ", jit.Imm(1), _VAR_fl)
self.Emit("ADDQ", jit.Imm(1), _IC)
self.Link("_parse_number_{n}")
self.call_vf(_F_vnumber) // call vnumber
self.check_err_num()
self.slice_from(_VAR_st_Ep, 0) // SLICE st.Ep, $0
self.Link("_skip_number_{n}")
/* call skip_number */
self.Emit("LEAQ", _ARG_s, _DI) // LEAQ s<>+0(FP), DI
self.Emit("MOVQ", _IC, _ARG_ic) // MOVQ IC, ic<>+16(FP)
self.Emit("LEAQ", _ARG_ic, _SI) // LEAQ ic<>+16(FP), SI
self.call(_F_skip_number) // CALL _F_skip_number
self.Emit("MOVQ", _ARG_ic, _IC) // MOVQ ic<>+16(FP), IC
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , _LB_parsing_error_v) // JS _parse_error_v
self.slice_from_r(_AX, 0)
self.Emit("BTQ", jit.Imm(_F_copy_string), _ARG_fv)
self.Sjmp("JNC", "_num_write_{n}")
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9

View file

@ -591,15 +591,6 @@ func (self *_Assembler) check_err() {
self.Sjmp("JS" , _LB_parsing_error_v) // JNE _parsing_error_v
}
func (self *_Assembler) check_err_num() {
self.Emit("MOVQ" , _VAR_st_Vt, _AX) // MOVQ st.Vt, AX
self.Emit("TESTQ", _AX, _AX) // CMPQ AX, ${native.V_STRING}
self.Sjmp("JNS" , "check_err_end_{n}")
self.Emit("CMPQ" , _AX, jit.Imm(-int64(types.ERR_FLOAT_INFINITY)))
self.Sjmp("JNE" , _LB_parsing_error_v)
self.Link("check_err_end_{n}")
}
func (self *_Assembler) check_eof(d int64) {
if d == 1 {
self.Emit("CMPQ", _IC, _IL) // CMPQ IC, IL
@ -943,6 +934,7 @@ func (self *_Assembler) mapassign_utext(t reflect.Type, addressable bool) {
var (
_F_skip_one = jit.Imm(int64(native.S_skip_one))
_F_skip_number = jit.Imm(int64(native.S_skip_number))
)
func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool) {
@ -1173,13 +1165,21 @@ func (self *_Assembler) _asm_OP_bool(_ *_Instr) {
func (self *_Assembler) _asm_OP_num(_ *_Instr) {
self.Emit("MOVQ", jit.Imm(0), _VAR_fl)
self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm('"'))
self.Sjmp("JNE", "_parse_number_{n}")
self.Sjmp("JNE", "_skip_number_{n}")
self.Emit("MOVQ", jit.Imm(1), _VAR_fl)
self.Emit("ADDQ", jit.Imm(1), _IC)
self.Link("_parse_number_{n}")
self.call_vf(_F_vnumber)
self.check_err_num()
self.slice_from(_VAR_st_Ep, 0) // SLICE st.Ep, $0
self.Link("_skip_number_{n}")
/* call skip_number */
self.Emit("LEAQ", _ARG_s, _DI) // LEAQ s<>+0(FP), DI
self.Emit("MOVQ", _IC, _ARG_ic) // MOVQ IC, ic<>+16(FP)
self.Emit("LEAQ", _ARG_ic, _SI) // LEAQ ic<>+16(FP), SI
self.callc(_F_skip_number) // CALL _F_skip_number
self.Emit("MOVQ", _ARG_ic, _IC) // MOVQ ic<>+16(FP), IC
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , _LB_parsing_error_v) // JS _parse_error_v
self.slice_from_r(_AX, 0)
self.Emit("BTQ", jit.Imm(_F_copy_string), _ARG_fv)
self.Sjmp("JNC", "_num_write_{n}")
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9

View file

@ -33,6 +33,8 @@ const (
_F_disable_urc
_F_disable_unknown
_F_copy_string
_F_allow_control = 31
)
// Decoder is the decoder context object

View file

@ -266,21 +266,16 @@ func (self *_ValueDecoder) compile() {
self.Emit("MOVQ", _IL, _SI) // MOVQ IL, SI
self.Emit("MOVQ", _IC, _DX) // MOVQ IC, DX
self.Emit("LEAQ", _VAR_ss, _CX) // LEAQ ss, CX
self.Emit("MOVL", jit.Imm(1), _R8) // MOVL $1, R8
self.Emit("MOVQ", _VAR_df, _R8) // MOVQ $df, R8
self.Emit("BTSQ", jit.Imm(_F_allow_control), _R8) // ANDQ $1<<_F_allow_control, R8
self.call(_F_value) // CALL value
self.Emit("MOVQ", _AX, _IC) // MOVQ AX, IC
/* check for errors */
self.Emit("MOVQ" , _VAR_ss_Vt, _AX) // MOVQ ss.Vt, AX
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , "_parsing_error")
self.Sjmp("JZ" , "_invalid_vtype") // JZ _invalid_vtype
self.Sjmp("JNS" , "_not_sign") // JNS
self.Emit("CMPQ" , _AX, jit.Imm(-int64(types.ERR_FLOAT_INFINITY)))
self.Sjmp("JNE" , "_parsing_error")
self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df
self.Sjmp("JNC" , "_parsing_error")
self.Emit("MOVQ" , jit.Imm(int64(types.V_DOUBLE)), _AX)
self.Link("_not_sign")
self.Emit("CMPQ" , _AX, _V_max) // CMPQ AX, _V_max
self.Sjmp("JA" , "_invalid_vtype") // JA _invalid_vtype

View file

@ -281,21 +281,16 @@ func (self *_ValueDecoder) compile() {
self.Emit("MOVQ", _IL, _SI) // MOVQ IL, SI
self.Emit("MOVQ", _IC, _DX) // MOVQ IC, DX
self.Emit("LEAQ", _VAR_ss, _CX) // LEAQ ss, CX
self.Emit("MOVL", jit.Imm(1), _R8) // MOVL $1, R8
self.Emit("MOVQ", _VAR_df, _R8) // MOVQ $df, R8
self.Emit("BTSQ", jit.Imm(_F_allow_control), _R8) // ANDQ $1<<_F_allow_control, R8
self.callc(_F_value) // CALL value
self.Emit("MOVQ", _AX, _IC) // MOVQ AX, IC
/* check for errors */
self.Emit("MOVQ" , _VAR_ss_Vt, _AX) // MOVQ ss.Vt, AX
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , "_parsing_error")
self.Sjmp("JZ" , "_invalid_vtype") // JZ _invalid_vtype
self.Sjmp("JNS" , "_not_sign") // JNS
self.Emit("CMPQ" , _AX, jit.Imm(-int64(types.ERR_FLOAT_INFINITY)))
self.Sjmp("JNE" , "_parsing_error")
self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df
self.Sjmp("JNC" , "_parsing_error")
self.Emit("MOVQ" , jit.Imm(int64(types.V_DOUBLE)), _AX)
self.Link("_not_sign")
self.Emit("CMPQ" , _AX, _V_max) // CMPQ AX, _V_max
self.Sjmp("JA" , "_invalid_vtype") // JA _invalid_vtype

View file

@ -104,6 +104,11 @@ func __skip_array(s *string, p *int, m *types.StateMachine) (ret int)
//goland:noinspection GoUnusedParameter
func __skip_object(s *string, p *int, m *types.StateMachine) (ret int)
//go:nosplit
//go:noescape
//goland:noinspection GoUnusedParameter
func __skip_number(s *string, p *int) (ret int)
//go:nosplit
//go:noescape
//goland:noinspection GoUnusedParameter

File diff suppressed because it is too large Load diff

View file

@ -565,3 +565,11 @@ func TestNative_SkipObject(t *testing.T) {
__skip_object(&s, &p, &types.StateMachine{})
assert.Equal(t, p, 15)
}
func TestNative_SkipNumber(t *testing.T) {
p := 0
s := `-1.23e+12`
q := __skip_number(&s, &p)
assert.Equal(t, 9, p)
assert.Equal(t, 0, q)
}

View file

@ -42,4 +42,5 @@ var (
S_skip_one = _subr__skip_one
S_skip_array = _subr__skip_array
S_skip_object = _subr__skip_object
S_skip_number = _subr__skip_number
)

View file

@ -15,17 +15,18 @@ var (
_subr__lspace = __native_entry__() + 301
_subr__lzero = __native_entry__() + 13
_subr__quote = __native_entry__() + 4955
_subr__skip_array = __native_entry__() + 17370
_subr__skip_object = __native_entry__() + 17407
_subr__skip_one = __native_entry__() + 15518
_subr__skip_array = __native_entry__() + 17551
_subr__skip_number = __native_entry__() + 20669
_subr__skip_object = __native_entry__() + 17588
_subr__skip_one = __native_entry__() + 15699
_subr__u64toa = __native_entry__() + 3735
_subr__unquote = __native_entry__() + 6005
_subr__validate_one = __native_entry__() + 20488
_subr__validate_one = __native_entry__() + 20786
_subr__value = __native_entry__() + 10880
_subr__vnumber = __native_entry__() + 13676
_subr__vsigned = __native_entry__() + 14990
_subr__vstring = __native_entry__() + 12641
_subr__vunsigned = __native_entry__() + 15249
_subr__vnumber = __native_entry__() + 13857
_subr__vsigned = __native_entry__() + 15171
_subr__vstring = __native_entry__() + 12822
_subr__vunsigned = __native_entry__() + 15430
)
const (
@ -36,12 +37,13 @@ const (
_stack__lzero = 8
_stack__quote = 80
_stack__skip_array = 160
_stack__skip_number = 96
_stack__skip_object = 160
_stack__skip_one = 160
_stack__u64toa = 8
_stack__unquote = 88
_stack__validate_one = 160
_stack__value = 400
_stack__value = 416
_stack__vnumber = 312
_stack__vsigned = 16
_stack__vstring = 128
@ -56,6 +58,7 @@ var (
_ = _subr__lzero
_ = _subr__quote
_ = _subr__skip_array
_ = _subr__skip_number
_ = _subr__skip_object
_ = _subr__skip_one
_ = _subr__u64toa
@ -76,6 +79,7 @@ const (
_ = _stack__lzero
_ = _stack__quote
_ = _stack__skip_array
_ = _stack__skip_number
_ = _stack__skip_object
_ = _stack__skip_one
_ = _stack__u64toa

View file

@ -104,6 +104,11 @@ func __skip_array(s *string, p *int, m *types.StateMachine) (ret int)
//goland:noinspection GoUnusedParameter
func __skip_object(s *string, p *int, m *types.StateMachine) (ret int)
//go:nosplit
//go:noescape
//goland:noinspection GoUnusedParameter
func __skip_number(s *string, p *int) (ret int)
//go:nosplit
//go:noescape
//goland:noinspection GoUnusedParameter

File diff suppressed because it is too large Load diff

View file

@ -565,3 +565,11 @@ func TestNative_SkipObject(t *testing.T) {
__skip_object(&s, &p, &types.StateMachine{})
assert.Equal(t, p, 15)
}
func TestNative_SkipNumber(t *testing.T) {
p := 0
s := `-1.23e+12`
q := __skip_number(&s, &p)
assert.Equal(t, 9, p)
assert.Equal(t, 0, q)
}

View file

@ -42,4 +42,5 @@ var (
S_skip_one = _subr__skip_one
S_skip_array = _subr__skip_array
S_skip_object = _subr__skip_object
S_skip_number = _subr__skip_number
)

View file

@ -15,17 +15,18 @@ var (
_subr__lspace = __native_entry__() + 429
_subr__lzero = __native_entry__() + 13
_subr__quote = __native_entry__() + 5328
_subr__skip_array = __native_entry__() + 21375
_subr__skip_object = __native_entry__() + 21412
_subr__skip_one = __native_entry__() + 18275
_subr__skip_array = __native_entry__() + 21558
_subr__skip_number = __native_entry__() + 25206
_subr__skip_object = __native_entry__() + 21595
_subr__skip_one = __native_entry__() + 18458
_subr__u64toa = __native_entry__() + 4008
_subr__unquote = __native_entry__() + 7080
_subr__validate_one = __native_entry__() + 25023
_subr__validate_one = __native_entry__() + 25323
_subr__value = __native_entry__() + 13781
_subr__vnumber = __native_entry__() + 16433
_subr__vsigned = __native_entry__() + 17747
_subr__vstring = __native_entry__() + 15556
_subr__vunsigned = __native_entry__() + 18006
_subr__vnumber = __native_entry__() + 16616
_subr__vsigned = __native_entry__() + 17930
_subr__vstring = __native_entry__() + 15739
_subr__vunsigned = __native_entry__() + 18189
)
const (
@ -36,12 +37,13 @@ const (
_stack__lzero = 8
_stack__quote = 64
_stack__skip_array = 136
_stack__skip_number = 96
_stack__skip_object = 136
_stack__skip_one = 136
_stack__u64toa = 8
_stack__unquote = 72
_stack__validate_one = 136
_stack__value = 392
_stack__value = 408
_stack__vnumber = 312
_stack__vsigned = 16
_stack__vstring = 112
@ -56,6 +58,7 @@ var (
_ = _subr__lzero
_ = _subr__quote
_ = _subr__skip_array
_ = _subr__skip_number
_ = _subr__skip_object
_ = _subr__skip_one
_ = _subr__u64toa
@ -76,6 +79,7 @@ const (
_ = _stack__lzero
_ = _stack__quote
_ = _stack__skip_array
_ = _stack__skip_number
_ = _stack__skip_object
_ = _stack__skip_one
_ = _stack__u64toa

View file

@ -51,6 +51,7 @@ var (
S_skip_one uintptr
S_skip_array uintptr
S_skip_object uintptr
S_skip_number uintptr
)
//go:nosplit
@ -113,6 +114,7 @@ func useAVX() {
S_skip_one = avx.S_skip_one
S_skip_array = avx.S_skip_array
S_skip_object = avx.S_skip_object
S_skip_number = avx.S_skip_number
}
func useAVX2() {
@ -130,6 +132,7 @@ func useAVX2() {
S_skip_one = avx2.S_skip_one
S_skip_array = avx2.S_skip_array
S_skip_object = avx2.S_skip_object
S_skip_number = avx2.S_skip_number
}
func init() {

View file

@ -102,6 +102,11 @@ func __skip_array(s *string, p *int, m *types.StateMachine) (ret int)
//goland:noinspection GoUnusedParameter
func __skip_object(s *string, p *int, m *types.StateMachine) (ret int)
//go:nosplit
//go:noescape
//goland:noinspection GoUnusedParameter
func __skip_number(s *string, p *int) (ret int)
//go:nosplit
//go:noescape
//goland:noinspection GoUnusedParameter

View file

@ -563,3 +563,11 @@ func TestNative_SkipObject(t *testing.T) {
__skip_object(&s, &p, &types.StateMachine{})
assert.Equal(t, p, 15)
}
func TestNative_SkipNumber(t *testing.T) {
p := 0
s := `-1.23e+12`
q := __skip_number(&s, &p)
assert.Equal(t, 9, p)
assert.Equal(t, 0, q)
}

View file

@ -40,4 +40,5 @@ var (
S_skip_one = _subr__skip_one
S_skip_array = _subr__skip_array
S_skip_object = _subr__skip_object
S_skip_number = _subr__skip_number
)

View file

@ -31,6 +31,7 @@ import (
var issue_19x_idata = "\"" + strings.Repeat("9", 1000) + "\""
var issue_19x_fdata = "\"" + strings.Repeat("9", 100) + "." + strings.Repeat("9", 1000) + "\""
var issue_19x_ndata = strings.Repeat("9", 1000)
var issue_19x_invalid = strings.Repeat("9", 100) + "abc99"
func TestDecodeLongStringToJsonNumber(t *testing.T) {
var objs, obje json.Number
@ -45,6 +46,13 @@ func TestDecodeLongStringToJsonNumber(t *testing.T) {
require.Equal(t, erre, errs)
require.Equal(t, fobje, fobjs)
var objs2, obje2 json.Number
errs = sonic.UnmarshalString(issue_19x_invalid, &objs2)
erre = json.Unmarshal([]byte(issue_19x_invalid), &obje2)
require.NotNil(t, erre)
require.NotNil(t, errs)
var iobjs, iobje interface{}
dc := decoder.NewDecoder(issue_19x_ndata)
dc.UseNumber()
@ -54,4 +62,37 @@ func TestDecodeLongStringToJsonNumber(t *testing.T) {
erre = r.Decode(&iobje)
require.Equal(t, erre, errs)
require.Equal(t, iobje, iobjs)
var iobjs2, iobje2 interface{}
dc = decoder.NewDecoder(issue_19x_invalid)
dc.UseNumber()
errs = dc.Decode(&iobjs2)
r = json.NewDecoder(bytes.NewBufferString(issue_19x_invalid))
r.UseNumber()
erre = r.Decode(&iobje2)
require.Equal(t, erre, errs)
require.Equal(t, iobje2, iobjs2)
// spew.Dump(iobje2)
}
var jsonNumberBig = "\"" + strings.Repeat("9", 10) + "." + strings.Repeat("9", 100) + "\""
func BenchmarkDecodeJsonNumber_Sonic(b *testing.B) {
b.SetBytes(int64(len(jsonNumberBig)))
b.ResetTimer()
for i:=0; i<b.N; i++ {
var obj json.Number
_ = sonic.UnmarshalString(jsonNumberBig, &obj)
}
}
func BenchmarkDecodeUseNumber_Sonic(b *testing.B) {
b.SetBytes(int64(len(jsonNumberBig)))
b.ResetTimer()
for i:=0; i<b.N; i++ {
var obj interface{}
dc := decoder.NewDecoder(jsonNumberBig)
dc.UseNumber()
_ = dc.Decode(&obj)
}
}

View file

@ -108,7 +108,7 @@ ssize_t quote(const char *sp, ssize_t nb, char *dp, ssize_t *dn, uint64_t flags)
ssize_t unquote(const char *sp, ssize_t nb, char *dp, ssize_t *ep, uint64_t flags);
ssize_t html_escape(const char *sp, ssize_t nb, char *dp, ssize_t *dn);
long value(const char *s, size_t n, long p, JsonState *ret, int allow_control);
long value(const char *s, size_t n, long p, JsonState *ret, uint64_t flags);
void vstring(const GoString *src, long *p, JsonState *ret);
void vnumber(const GoString *src, long *p, JsonState *ret);
void vsigned(const GoString *src, long *p, JsonState *ret);
@ -121,6 +121,7 @@ long skip_object(const GoString *src, long *p, StateMachine *m);
long skip_string(const GoString *src, long *p);
long skip_negative(const GoString *src, long *p);
long skip_positive(const GoString *src, long *p);
long skip_number(const GoString *src, long *p);
bool atof_eisel_lemire64(uint64_t mant, int exp10, int sgn, double *val);
double atof_native(const char *sp, ssize_t nb, char* dbuf, ssize_t cap);

View file

@ -51,8 +51,20 @@ static inline char isspace(char ch) {
return ch == ' ' || ch == '\r' || ch == '\n' | ch == '\t';
}
static inline void vdigits(const GoString *src, long *p, JsonState *ret) {
const int MASK_USE_NUMBER = 1<<1;
static inline void vdigits(const GoString *src, long *p, JsonState *ret, uint64_t flag) {
--*p;
if (flag & MASK_USE_NUMBER) {
long i = skip_number(src, p);
if (i < 0) {
ret->vt = i;
return;
}
ret->vt = V_DOUBLE;
ret->ep = i;
return;
}
vnumber(src, p, ret);
}
@ -572,9 +584,12 @@ static inline ssize_t advance_validate_string(const GoString *src, long p, int64
/** Value Scanning Routines **/
long value(const char *s, size_t n, long p, JsonState *ret, int allow_control) {
const uint64_t MASK_ALLOW_CONTROL = 1ul<<31;
long value(const char *s, size_t n, long p, JsonState *ret, uint64_t flags) {
long q = p;
GoString m = {.buf = s, .len = n};
bool allow_control = (flags&MASK_ALLOW_CONTROL) != 0;
/* parse the next identifier, q is UNSAFE, may cause out-of-bounds accessing */
switch (advance_ns(&m, &q)) {
@ -588,7 +603,7 @@ long value(const char *s, size_t n, long p, JsonState *ret, int allow_control) {
case '6' : /* fallthrough */
case '7' : /* fallthrough */
case '8' : /* fallthrough */
case '9' : vdigits(&m, &q, ret) ; return q;
case '9' : vdigits(&m, &q, ret, flags) ; return q;
case '"' : vstring(&m, &q, ret) ; return q;
case 'n' : ret->vt = advance_dword(&m, &q, 1, V_NULL, VS_NULL) ; return q;
case 't' : ret->vt = advance_dword(&m, &q, 1, V_TRUE, VS_TRUE) ; return q;
@ -1135,7 +1150,7 @@ static inline long fsm_exec(StateMachine *self, const GoString *src, long *p, in
} \
}
static inline long skip_number(const char *sp, size_t nb) {
static inline long do_skip_number(const char *sp, size_t nb) {
long di = -1;
long ei = -1;
long si = -1;
@ -1380,7 +1395,7 @@ long validate_string(const GoString *src, long *p) {
long skip_negative(const GoString *src, long *p) {
long i = *p;
long r = skip_number(src->buf + i, src->len - i);
long r = do_skip_number(src->buf + i, src->len - i);
/* check for errors */
if (r < 0) {
@ -1395,7 +1410,7 @@ long skip_negative(const GoString *src, long *p) {
long skip_positive(const GoString *src, long *p) {
long i = *p - 1;
long r = skip_number(src->buf + i, src->len - i);
long r = do_skip_number(src->buf + i, src->len - i);
/* check for errors */
if (r < 0) {
@ -1408,6 +1423,35 @@ long skip_positive(const GoString *src, long *p) {
return i;
}
long skip_number(const GoString *src, long *p) {
const char* ss = src->buf;
const char* sp = src->buf + *p;
size_t nb = src->len;
long i = *p;
long r;
bool neg = *sp == '-';
sp += neg;
nb -= neg;
if (unlikely(nb <= 0)) {
*p = sp - ss;
return -ERR_EOF;
}
if (unlikely(i < nb && (*sp > '9' || *sp < '0'))) {
*p = sp - ss;
return -ERR_INVAL;
}
r = do_skip_number(sp, nb);
if (unlikely(r < 0)) {
*p = sp - (r + 1) - ss;
return -ERR_INVAL;
}
*p = sp + r - ss;
return i;
}
long validate_one(const GoString *src, long *p, StateMachine *m) {
fsm_init(m, FSM_VAL);
return fsm_exec(m, src, p, VALID_FULL);