diff --git a/.github/workflows/compatibility_test-windows.yml b/.github/workflows/compatibility_test-windows.yml index d4a5ac0bd..e352bb30f 100644 --- a/.github/workflows/compatibility_test-windows.yml +++ b/.github/workflows/compatibility_test-windows.yml @@ -26,12 +26,12 @@ jobs: - name: main run: | set GOMAXPROCS=4 - go test -v -race github.com/bytedance/sonic + go test -v -race ./ - name: ast run: | set GOMAXPROCS=4 - go test -v -race github.com/bytedance/sonic/ast + go test -v -race ./ast - name: external run: | diff --git a/.github/workflows/compatibility_test.yml b/.github/workflows/compatibility_test.yml index 78c48bc87..ef0aa0ffd 100644 --- a/.github/workflows/compatibility_test.yml +++ b/.github/workflows/compatibility_test.yml @@ -28,13 +28,13 @@ jobs: ${{ runner.os }}-go- - name: main - run: go test -race -v -gcflags="all=-l" github.com/bytedance/sonic + run: go test -race -v -gcflags="all=-l" ./ - name: decoder - run: go test -race -v -gcflags="all=-l" github.com/bytedance/sonic/decoder + run: go test -race -v -gcflags="all=-l" ./decoder - name: encoder - run: go test -race -v -gcflags="all=-l" github.com/bytedance/sonic/encoder + run: go test -race -v -gcflags="all=-l" ./encoder - name: ast - run: go test -race -v -gcflags="all=-l" github.com/bytedance/sonic/ast + run: go test -race -v -gcflags="all=-l" ./ast diff --git a/.github/workflows/test-arm64.yml b/.github/workflows/test-arm64.yml index deb3131aa..147badb00 100644 --- a/.github/workflows/test-arm64.yml +++ b/.github/workflows/test-arm64.yml @@ -6,9 +6,8 @@ on: pull_request jobs: build: strategy: - max-parallel: 4 matrix: - go-version: [1.20.x, 1.21.x, 1.22.x, 1.23.x] + go-version: [1.20.x, 1.21.x, 1.22.x, 1.23.x, 1.24.x] runner_arch: [ubuntu-24.04-arm] runs-on: ${{ matrix.runner_arch }} diff --git a/.github/workflows/test-x86.yml b/.github/workflows/test-x86.yml index 2ac7f4918..bc6065140 100644 --- a/.github/workflows/test-x86.yml +++ b/.github/workflows/test-x86.yml @@ -7,7 +7,7 @@ jobs: build: strategy: matrix: - go-version: [1.17.x, 1.18.x, 1.19.x, 1.20.x, 1.21.x, 1.22.x, 1.23.x] + go-version: [1.17.x, 1.18.x, 1.19.x, 1.20.x, 1.21.x, 1.22.x, 1.23.x, 1.24.x] runner_arch: [ubuntu-latest] runs-on: ${{ matrix.runner_arch }} diff --git a/README.md b/README.md index 576c15bce..bf8880816 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ A blazingly fast JSON serializing & deserializing library, accelerated by JI ## Requirement -- Go: 1.17~1.23 +- Go: 1.17~1.24 - OS: Linux / MacOS / Windows -- CPU: AMD64 / ARM64(need go1.20 above) +- CPU: AMD64 / (ARM64, need go1.20 above) ## Features diff --git a/README_ZH_CN.md b/README_ZH_CN.md index cf6e80764..b0715eef9 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -6,9 +6,9 @@ ## 依赖 -- Go: 1.17~1.23 +- Go: 1.17~1.24 - OS: Linux / MacOS / Windows -- CPU: AMD64 / ARM64(需要 Go1.20 以上) +- CPU: AMD64 / (ARM64, 需要 Go1.20 以上) ## 接口 diff --git a/ast/api.go b/ast/api.go index 7c8253aa1..36151f270 100644 --- a/ast/api.go +++ b/ast/api.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2022 ByteDance Inc. diff --git a/ast/api_compat.go b/ast/api_compat.go index a2efe5402..74119fed6 100644 --- a/ast/api_compat.go +++ b/ast/api_compat.go @@ -1,4 +1,4 @@ -// +build !amd64,!arm64 go1.24 !go1.17 arm64,!go1.20 +// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20 /* * Copyright 2022 ByteDance Inc. @@ -20,16 +20,15 @@ package ast import ( `encoding/json` - `fmt` - `os` `unicode/utf8` `github.com/bytedance/sonic/internal/native/types` `github.com/bytedance/sonic/internal/rt` + `github.com/bytedance/sonic/internal/compat` ) func init() { - fmt.Fprintln(os.Stderr, "WARNING:(ast) sonic only supports go1.17~1.23, but your environment is not suitable") + compat.Warn("sonic/ast") } func quote(buf *[]byte, val string) { diff --git a/ast/api_native_test.go b/ast/api_native_test.go index 61cf85d94..404bcd4af 100644 --- a/ast/api_native_test.go +++ b/ast/api_native_test.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2022 ByteDance Inc. diff --git a/ast/node.go b/ast/node.go index 3b1aee47c..17964c32f 100644 --- a/ast/node.go +++ b/ast/node.go @@ -1678,7 +1678,7 @@ func NewBytes(src []byte) Node { if len(src) == 0 { panic("empty src bytes") } - out := rt.EncodeBase64(src) + out := rt.EncodeBase64ToString(src) return NewString(out) } diff --git a/ast/stubs.go b/ast/stubs.go index 53bf3b8aa..9991cc89e 100644 --- a/ast/stubs.go +++ b/ast/stubs.go @@ -17,126 +17,15 @@ package ast import ( - "unicode/utf8" "unsafe" "github.com/bytedance/sonic/internal/rt" ) -//go:noescape -//go:linkname memmove runtime.memmove -//goland:noinspection GoUnusedParameter -func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr) - -//go:linkname unsafe_NewArray reflect.unsafe_NewArray -//goland:noinspection GoUnusedParameter -func unsafe_NewArray(typ *rt.GoType, n int) unsafe.Pointer - //go:nosplit func mem2ptr(s []byte) unsafe.Pointer { return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr } -var safeSet = [utf8.RuneSelf]bool{ - ' ': true, - '!': true, - '"': false, - '#': true, - '$': true, - '%': true, - '&': true, - '\'': true, - '(': true, - ')': true, - '*': true, - '+': true, - ',': true, - '-': true, - '.': true, - '/': true, - '0': true, - '1': true, - '2': true, - '3': true, - '4': true, - '5': true, - '6': true, - '7': true, - '8': true, - '9': true, - ':': true, - ';': true, - '<': true, - '=': true, - '>': true, - '?': true, - '@': true, - 'A': true, - 'B': true, - 'C': true, - 'D': true, - 'E': true, - 'F': true, - 'G': true, - 'H': true, - 'I': true, - 'J': true, - 'K': true, - 'L': true, - 'M': true, - 'N': true, - 'O': true, - 'P': true, - 'Q': true, - 'R': true, - 'S': true, - 'T': true, - 'U': true, - 'V': true, - 'W': true, - 'X': true, - 'Y': true, - 'Z': true, - '[': true, - '\\': false, - ']': true, - '^': true, - '_': true, - '`': true, - 'a': true, - 'b': true, - 'c': true, - 'd': true, - 'e': true, - 'f': true, - 'g': true, - 'h': true, - 'i': true, - 'j': true, - 'k': true, - 'l': true, - 'm': true, - 'n': true, - 'o': true, - 'p': true, - 'q': true, - 'r': true, - 's': true, - 't': true, - 'u': true, - 'v': true, - 'w': true, - 'x': true, - 'y': true, - 'z': true, - '{': true, - '|': true, - '}': true, - '~': true, - '\u007f': true, -} - -var hex = "0123456789abcdef" - //go:linkname unquoteBytes encoding/json.unquoteBytes func unquoteBytes(s []byte) (t []byte, ok bool) diff --git a/compat.go b/compat.go index b32342a84..b694d7ce9 100644 --- a/compat.go +++ b/compat.go @@ -1,4 +1,4 @@ -// +build !amd64,!arm64 go1.24 !go1.17 arm64,!go1.20 +// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20 /* * Copyright 2021 ByteDance Inc. diff --git a/decode_test.go b/decode_test.go index 0b4d2b344..6624e45ca 100644 --- a/decode_test.go +++ b/decode_test.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/decoder/decoder_compat.go b/decoder/decoder_compat.go index 0ee9d96bc..754e5fbc2 100644 --- a/decoder/decoder_compat.go +++ b/decoder/decoder_compat.go @@ -1,5 +1,5 @@ -//go:build (!amd64 && !arm64) || go1.24 || !go1.17 || (arm64 && !go1.20) -// +build !amd64,!arm64 go1.24 !go1.17 arm64,!go1.20 +//go:build (!amd64 && !arm64) || go1.25 || !go1.17 || (arm64 && !go1.20) +// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20 /* * Copyright 2023 ByteDance Inc. @@ -22,19 +22,18 @@ package decoder import ( "bytes" "encoding/json" - "fmt" "io" - "os" "reflect" "unsafe" "github.com/bytedance/sonic/internal/decoder/consts" "github.com/bytedance/sonic/internal/native/types" "github.com/bytedance/sonic/option" + "github.com/bytedance/sonic/internal/compat" ) func init() { - fmt.Fprintln(os.Stderr, "WARNING: sonic/decoder only supports (Go1.17~1.23 && CPU amd64) or (go1.20~1.23 && CPU arm64), but your environment is not suitable") + compat.Warn("sonic/decoder") } const ( diff --git a/decoder/decoder_native.go b/decoder/decoder_native.go index bf71e1bd4..563ca944b 100644 --- a/decoder/decoder_native.go +++ b/decoder/decoder_native.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* diff --git a/decoder/decoder_native_test.go b/decoder/decoder_native_test.go index 62e85b52a..78b801513 100644 --- a/decoder/decoder_native_test.go +++ b/decoder/decoder_native_test.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/encode_test.go b/encode_test.go index f97244a2c..2185434f8 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/encoder/encoder_compat.go b/encoder/encoder_compat.go index 786e46ff3..d48e99b1f 100644 --- a/encoder/encoder_compat.go +++ b/encoder/encoder_compat.go @@ -1,4 +1,4 @@ -// +build !amd64,!arm64 go1.24 !go1.17 arm64,!go1.20 +// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20 /* * Copyright 2023 ByteDance Inc. @@ -20,17 +20,16 @@ package encoder import ( `io` - `fmt` - `os` `bytes` `encoding/json` `reflect` `github.com/bytedance/sonic/option` + `github.com/bytedance/sonic/internal/compat` ) func init() { - fmt.Fprintln(os.Stderr, "WARNING:(encoder) sonic only supports (Go1.17~1.23 && CPU amd64) or (G01.20~1.23 && CPU arm64) , but your environment is not suitable") + compat.Warn("sonic/encoder") } // EnableFallback indicates if encoder use fallback diff --git a/encoder/encoder_native.go b/encoder/encoder_native.go index b300ebf08..2881da8d7 100644 --- a/encoder/encoder_native.go +++ b/encoder/encoder_native.go @@ -1,4 +1,4 @@ -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2023 ByteDance Inc. diff --git a/encoder/encoder_native_test.go b/encoder/encoder_native_test.go index 84c0d187b..2f8b75705 100644 --- a/encoder/encoder_native_test.go +++ b/encoder/encoder_native_test.go @@ -1,4 +1,4 @@ -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/examples/example_stream_test.go b/examples/example_stream_test.go index 96a644c3c..5ebaced4d 100644 --- a/examples/example_stream_test.go +++ b/examples/example_stream_test.go @@ -1,3 +1,5 @@ +// +build !go1.24 + package example import ( diff --git a/internal/base64/b64_amd64.go b/internal/base64/b64_amd64.go deleted file mode 100644 index 43db80d8a..000000000 --- a/internal/base64/b64_amd64.go +++ /dev/null @@ -1,46 +0,0 @@ -// +build amd64,go1.17,!go1.24 - -/** - * Copyright 2023 ByteDance Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package base64 - -import ( - "github.com/cloudwego/base64x" -) - -func DecodeBase64(src string) ([]byte, error) { - return base64x.StdEncoding.DecodeString(src) -} - -func EncodeBase64(buf []byte, src []byte) []byte { - if len(src) == 0 { - return append(buf, '"', '"') - } - buf = append(buf, '"') - need := base64x.StdEncoding.EncodedLen(len(src)) - if cap(buf) - len(buf) < need { - tmp := make([]byte, len(buf), len(buf) + need*2) - copy(tmp, buf) - buf = tmp - } - base64x.StdEncoding.Encode(buf[len(buf):cap(buf)], src) - buf = buf[:len(buf) + need] - buf = append(buf, '"') - return buf -} - - \ No newline at end of file diff --git a/internal/base64/b64_compat.go b/internal/base64/b64_compat.go deleted file mode 100644 index 6688faa20..000000000 --- a/internal/base64/b64_compat.go +++ /dev/null @@ -1,44 +0,0 @@ -// +build !amd64 !go1.17 go1.24 - -/* - * Copyright 2022 ByteDance Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package base64 - -import ( - "encoding/base64" -) - -func EncodeBase64(buf []byte, src []byte) []byte { - if len(src) == 0 { - return append(buf, '"', '"') - } - buf = append(buf, '"') - need := base64.StdEncoding.EncodedLen(len(src)) - if cap(buf) - len(buf) < need { - tmp := make([]byte, len(buf), len(buf) + need*2) - copy(tmp, buf) - buf = tmp - } - base64.StdEncoding.Encode(buf[len(buf):cap(buf)], src) - buf = buf[:len(buf) + need] - buf = append(buf, '"') - return buf -} - -func DecodeBase64(src string) ([]byte, error) { - return base64.StdEncoding.DecodeString(src) -} diff --git a/internal/caching/hashing.go b/internal/caching/hashing.go index b8876a410..f185543f5 100644 --- a/internal/caching/hashing.go +++ b/internal/caching/hashing.go @@ -23,16 +23,12 @@ import ( ) var ( - V_strhash = rt.UnpackEface(strhash) + V_strhash = rt.UnpackEface(rt.Strhash) S_strhash = *(*uintptr)(V_strhash.Value) ) -//go:noescape -//go:linkname strhash runtime.strhash -func strhash(_ unsafe.Pointer, _ uintptr) uintptr - func StrHash(s string) uint64 { - if v := strhash(unsafe.Pointer(&s), 0); v == 0 { + if v := rt.Strhash(unsafe.Pointer(&s), 0); v == 0 { return 1 } else { return uint64(v) diff --git a/internal/compat/warn.go b/internal/compat/warn.go new file mode 100644 index 000000000..803a4a51b --- /dev/null +++ b/internal/compat/warn.go @@ -0,0 +1,12 @@ +// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20 + +package compat + +import ( + "fmt" + "os" +) + +func Warn(prefix string) { + fmt.Fprintf(os.Stderr, "WARNING: %s only supports (go1.17~1.24 && amd64 CPU) or (go1.20~1.24 && CPU arm64 CPU), but your environment is not suitable and will fallback to encoding/json\n", prefix) +} \ No newline at end of file diff --git a/internal/decoder/api/decoder_amd64.go b/internal/decoder/api/decoder_amd64.go index 4e1c3f42c..551b35b1b 100644 --- a/internal/decoder/api/decoder_amd64.go +++ b/internal/decoder/api/decoder_amd64.go @@ -1,5 +1,5 @@ -//go:build go1.17 && !go1.24 -// +build go1.17,!go1.24 +//go:build go1.17 && !go1.25 +// +build go1.17,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/internal/decoder/api/decoder_arm64.go b/internal/decoder/api/decoder_arm64.go index 16e55965a..a56e1579d 100644 --- a/internal/decoder/api/decoder_arm64.go +++ b/internal/decoder/api/decoder_arm64.go @@ -1,4 +1,4 @@ -// +build go1.17,!go1.24 +// +build go1.17,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/internal/decoder/jitdec/asm_stubs_amd64_go117.go b/internal/decoder/jitdec/asm_stubs_amd64_go117.go index 48f73e5bf..5a455ebd5 100644 --- a/internal/decoder/jitdec/asm_stubs_amd64_go117.go +++ b/internal/decoder/jitdec/asm_stubs_amd64_go117.go @@ -20,21 +20,16 @@ import ( `strconv` `unsafe` + `github.com/bytedance/sonic/internal/rt` `github.com/bytedance/sonic/internal/jit` `github.com/twitchyliquid64/golang-asm/obj` `github.com/twitchyliquid64/golang-asm/obj/x86` ) -//go:linkname _runtime_writeBarrier runtime.writeBarrier -var _runtime_writeBarrier uintptr - -//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier -func gcWriteBarrierAX() - var ( - _V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&_runtime_writeBarrier)))) + _V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier)))) - _F_gcWriteBarrierAX = jit.Func(gcWriteBarrierAX) + _F_gcWriteBarrierAX = jit.Func(rt.GcWriteBarrierAX) ) func (self *_Assembler) WritePtrAX(i int, rec obj.Addr, saveDI bool) { diff --git a/internal/decoder/jitdec/asm_stubs_amd64_go121.go b/internal/decoder/jitdec/asm_stubs_amd64_go121.go index cbec3d248..d6101b2e6 100644 --- a/internal/decoder/jitdec/asm_stubs_amd64_go121.go +++ b/internal/decoder/jitdec/asm_stubs_amd64_go121.go @@ -1,4 +1,4 @@ -// +build go1.21,!go1.24 +// +build go1.21,!go1.25 // Copyright 2023 CloudWeGo Authors // @@ -20,25 +20,19 @@ import ( `strconv` `unsafe` + `github.com/bytedance/sonic/internal/rt` `github.com/bytedance/sonic/internal/jit` `github.com/twitchyliquid64/golang-asm/obj` `github.com/twitchyliquid64/golang-asm/obj/x86` ) -//go:linkname _runtime_writeBarrier runtime.writeBarrier -var _runtime_writeBarrier uintptr - -//go:nosplit -//go:linkname gcWriteBarrier2 runtime.gcWriteBarrier2 -func gcWriteBarrier2() - // Notice: gcWriteBarrier must use R11 register!! var _R11 = _IC var ( - _V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&_runtime_writeBarrier)))) + _V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier)))) - _F_gcWriteBarrier2 = jit.Func(gcWriteBarrier2) + _F_gcWriteBarrier2 = jit.Func(rt.GcWriteBarrier2) ) func (self *_Assembler) WritePtrAX(i int, rec obj.Addr, saveDI bool) { diff --git a/internal/decoder/jitdec/assembler_regabi_amd64.go b/internal/decoder/jitdec/assembler_regabi_amd64.go index ed709ceb6..294ea33e1 100644 --- a/internal/decoder/jitdec/assembler_regabi_amd64.go +++ b/internal/decoder/jitdec/assembler_regabi_amd64.go @@ -1,5 +1,5 @@ -//go:build go1.17 && !go1.24 -// +build go1.17,!go1.24 +//go:build go1.17 && !go1.25 +// +build go1.17,!go1.25 /* * Copyright 2021 ByteDance Inc. @@ -457,7 +457,7 @@ func (self *_Assembler) call_vf(fn obj.Addr) { /** Assembler Error Handlers **/ var ( - _F_convT64 = jit.Func(convT64) + _F_convT64 = jit.Func(rt.ConvT64) _F_error_wrap = jit.Func(error_wrap) _F_error_type = jit.Func(error_type) _F_error_field = jit.Func(error_field) @@ -663,7 +663,7 @@ func (self *_Assembler) skip_key_value() { var ( _T_byte = jit.Type(byteType) - _F_mallocgc = jit.Func(mallocgc) + _F_mallocgc = jit.Func(rt.Mallocgc) ) func (self *_Assembler) malloc_AX(nb obj.Addr, ret obj.Addr) { @@ -965,8 +965,8 @@ func (self *_Assembler) unquote_twice(p obj.Addr, n obj.Addr, stack bool) { /** Memory Clearing Routines **/ var ( - _F_memclrHasPointers = jit.Func(memclrHasPointers) - _F_memclrNoHeapPointers = jit.Func(memclrNoHeapPointers) + _F_memclrHasPointers = jit.Func(rt.MemclrHasPointers) + _F_memclrNoHeapPointers = jit.Func(rt.MemclrNoHeapPointers) ) func (self *_Assembler) mem_clear_fn(ptrfree bool) { @@ -990,10 +990,10 @@ func (self *_Assembler) mem_clear_rem(size int64, ptrfree bool) { /** Map Assigning Routines **/ var ( - _F_mapassign = jit.Func(mapassign) - _F_mapassign_fast32 = jit.Func(mapassign_fast32) - _F_mapassign_faststr = jit.Func(mapassign_faststr) - _F_mapassign_fast64ptr = jit.Func(mapassign_fast64ptr) + _F_mapassign = jit.Func(rt.Mapassign) + _F_mapassign_fast32 = jit.Func(rt.Mapassign_fast32) + _F_mapassign_faststr = jit.Func(rt.Mapassign_faststr) + _F_mapassign_fast64ptr = jit.Func(rt.Mapassign_fast64ptr) ) var ( @@ -1184,12 +1184,12 @@ func (self *_Assembler) decode_dynamic(vt obj.Addr, vp obj.Addr) { /** OpCode Assembler Functions **/ var ( - _F_memequal = jit.Func(memequal) - _F_memmove = jit.Func(memmove) + _F_memequal = jit.Func(rt.MemEqual) + _F_memmove = jit.Func(rt.Memmove) _F_growslice = jit.Func(rt.GrowSlice) - _F_makeslice = jit.Func(makeslice) - _F_makemap_small = jit.Func(makemap_small) - _F_mapassign_fast64 = jit.Func(mapassign_fast64) + _F_makeslice = jit.Func(rt.MakeSliceStd) + _F_makemap_small = jit.Func(rt.MakemapSmall) + _F_mapassign_fast64 = jit.Func(rt.Mapassign_fast64) ) var ( @@ -1198,7 +1198,7 @@ var ( ) var ( - _F_b64decode = jit.Imm(int64(_subr__b64decode)) + _F_b64decode = jit.Imm(int64(rt.SubrB64Decode)) _F_decodeValue = jit.Imm(int64(_subr_decode_value)) ) @@ -1553,7 +1553,7 @@ func (self *_Assembler) _asm_OP_map_key_i32(p *_Instr) { self.parse_signed(int32Type, "", p.vi()) // PARSE int32 self.range_signed_CX(_I_int32, _T_int32, math.MinInt32, math.MaxInt32) // RANGE int32 self.match_char('"') - if vt := p.vt(); !mapfast(vt) { + if vt := p.vt(); !rt.IsMapfast(vt) { self.mapassign_std(vt, _VAR_st_Iv) // MAPASSIGN int32, mapassign, st.Iv } else { self.Emit("MOVQ", _CX, _AX) // MOVQ CX, AX @@ -1564,7 +1564,7 @@ func (self *_Assembler) _asm_OP_map_key_i32(p *_Instr) { func (self *_Assembler) _asm_OP_map_key_i64(p *_Instr) { self.parse_signed(int64Type, "", p.vi()) // PARSE int64 self.match_char('"') - if vt := p.vt(); !mapfast(vt) { + if vt := p.vt(); !rt.IsMapfast(vt) { self.mapassign_std(vt, _VAR_st_Iv) // MAPASSIGN int64, mapassign, st.Iv } else { self.Emit("MOVQ", _VAR_st_Iv, _AX) // MOVQ st.Iv, AX @@ -1590,7 +1590,7 @@ func (self *_Assembler) _asm_OP_map_key_u32(p *_Instr) { self.parse_unsigned(uint32Type, "", p.vi()) // PARSE uint32 self.range_unsigned_CX(_I_uint32, _T_uint32, math.MaxUint32) // RANGE uint32 self.match_char('"') - if vt := p.vt(); !mapfast(vt) { + if vt := p.vt(); !rt.IsMapfast(vt) { self.mapassign_std(vt, _VAR_st_Iv) // MAPASSIGN uint32, vt.Iv } else { self.Emit("MOVQ", _CX, _AX) // MOVQ CX, AX @@ -1601,7 +1601,7 @@ func (self *_Assembler) _asm_OP_map_key_u32(p *_Instr) { func (self *_Assembler) _asm_OP_map_key_u64(p *_Instr) { self.parse_unsigned(uint64Type, "", p.vi()) // PARSE uint64 self.match_char('"') - if vt := p.vt(); !mapfast(vt) { + if vt := p.vt(); !rt.IsMapfast(vt) { self.mapassign_std(vt, _VAR_st_Iv) // MAPASSIGN uint64, vt.Iv } else { self.Emit("MOVQ", _VAR_st_Iv, _AX) // MOVQ st.Iv, AX @@ -1626,7 +1626,7 @@ func (self *_Assembler) _asm_OP_map_key_f64(p *_Instr) { func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) { self.parse_string() // PARSE STRING self.unquote_once(_ARG_sv_p, _ARG_sv_n, true, true) // UNQUOTE once, sv.p, sv.n - if vt := p.vt(); !mapfast(vt) { + if vt := p.vt(); !rt.IsMapfast(vt) { self.valloc(vt.Key(), _DI) self.Emit("MOVOU", _ARG_sv, _X0) self.Emit("MOVOU", _X0, jit.Ptr(_DI, 0)) diff --git a/internal/decoder/jitdec/generic_regabi_amd64.go b/internal/decoder/jitdec/generic_regabi_amd64.go index e6d5e3e84..51a850a8a 100644 --- a/internal/decoder/jitdec/generic_regabi_amd64.go +++ b/internal/decoder/jitdec/generic_regabi_amd64.go @@ -1,4 +1,4 @@ -// +build go1.17,!go1.24 +// +build go1.17,!go1.25 /* * Copyright 2021 ByteDance Inc. @@ -27,6 +27,7 @@ import ( `github.com/bytedance/sonic/internal/native` `github.com/bytedance/sonic/internal/native/types` `github.com/twitchyliquid64/golang-asm/obj` + `github.com/bytedance/sonic/internal/rt` ) /** Crucial Registers: @@ -173,8 +174,8 @@ var ( ) var ( - _F_convTslice = jit.Func(convTslice) - _F_convTstring = jit.Func(convTstring) + _F_convTslice = jit.Func(rt.ConvTslice) + _F_convTstring = jit.Func(rt.ConvTstring) _F_invalid_vtype = jit.Func(invalid_vtype) ) @@ -725,5 +726,5 @@ var ( //go:nosplit func invalid_vtype(vt types.ValueType) { - throw(fmt.Sprintf("invalid value type: %d", vt)) + rt.Throw(fmt.Sprintf("invalid value type: %d", vt)) } diff --git a/internal/decoder/jitdec/generic_regabi_amd64_test.s b/internal/decoder/jitdec/generic_regabi_amd64_test.s index 19ed3752f..f287eaeb4 100644 --- a/internal/decoder/jitdec/generic_regabi_amd64_test.s +++ b/internal/decoder/jitdec/generic_regabi_amd64_test.s @@ -1,4 +1,4 @@ -// +build go1.17,!go1.24 +// +build go1.17,!go1.25 // // Copyright 2021 ByteDance Inc. diff --git a/internal/decoder/jitdec/pools.go b/internal/decoder/jitdec/pools.go index 200af6a60..33ccd77e4 100644 --- a/internal/decoder/jitdec/pools.go +++ b/internal/decoder/jitdec/pools.go @@ -102,7 +102,7 @@ func newStack() *_Stack { } func resetStack(p *_Stack) { - memclrNoHeapPointers(unsafe.Pointer(p), _StackSize) + rt.MemclrNoHeapPointers(unsafe.Pointer(p), _StackSize) } func freeStack(p *_Stack) { diff --git a/internal/decoder/jitdec/stubs_go116.go b/internal/decoder/jitdec/stubs_go116.go deleted file mode 100644 index 8fa7c32fc..000000000 --- a/internal/decoder/jitdec/stubs_go116.go +++ /dev/null @@ -1,106 +0,0 @@ -// +build go1.17,!go1.20 - -/* - * Copyright 2021 ByteDance Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jitdec - -import ( - `unsafe` - `reflect` - - _ `github.com/cloudwego/base64x` - - `github.com/bytedance/sonic/internal/rt` -) - -//go:linkname _subr__b64decode github.com/cloudwego/base64x._subr__b64decode -var _subr__b64decode uintptr - -// runtime.maxElementSize -const _max_map_element_size uintptr = 128 - -func mapfast(vt reflect.Type) bool { - return vt.Elem().Size() <= _max_map_element_size -} - -//go:nosplit -//go:linkname throw runtime.throw -//goland:noinspection GoUnusedParameter -func throw(s string) - -//go:linkname convT64 runtime.convT64 -//goland:noinspection GoUnusedParameter -func convT64(v uint64) unsafe.Pointer - -//go:linkname convTslice runtime.convTslice -//goland:noinspection GoUnusedParameter -func convTslice(v []byte) unsafe.Pointer - -//go:linkname convTstring runtime.convTstring -//goland:noinspection GoUnusedParameter -func convTstring(v string) unsafe.Pointer - -//go:noescape -//go:linkname memequal runtime.memequal -//goland:noinspection GoUnusedParameter -func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool - -//go:noescape -//go:linkname memmove runtime.memmove -//goland:noinspection GoUnusedParameter -func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr) - -//go:linkname mallocgc runtime.mallocgc -//goland:noinspection GoUnusedParameter -func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer - -//go:linkname makeslice runtime.makeslice -//goland:noinspection GoUnusedParameter -func makeslice(et *rt.GoType, len int, cap int) unsafe.Pointer - -//go:linkname makemap_small runtime.makemap_small -func makemap_small() unsafe.Pointer - -//go:linkname mapassign runtime.mapassign -//goland:noinspection GoUnusedParameter -func mapassign(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer - -//go:linkname mapassign_fast32 runtime.mapassign_fast32 -//goland:noinspection GoUnusedParameter -func mapassign_fast32(t *rt.GoType, h unsafe.Pointer, k uint32) unsafe.Pointer - -//go:linkname mapassign_fast64 runtime.mapassign_fast64 -//goland:noinspection GoUnusedParameter -func mapassign_fast64(t *rt.GoType, h unsafe.Pointer, k uint64) unsafe.Pointer - -//go:linkname mapassign_fast64ptr runtime.mapassign_fast64ptr -//goland:noinspection GoUnusedParameter -func mapassign_fast64ptr(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer - -//go:linkname mapassign_faststr runtime.mapassign_faststr -//goland:noinspection GoUnusedParameter -func mapassign_faststr(t *rt.GoType, h unsafe.Pointer, s string) unsafe.Pointer - -//go:nosplit -//go:linkname memclrHasPointers runtime.memclrHasPointers -//goland:noinspection GoUnusedParameter -func memclrHasPointers(ptr unsafe.Pointer, n uintptr) - -//go:noescape -//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers -//goland:noinspection GoUnusedParameter -func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) \ No newline at end of file diff --git a/internal/decoder/jitdec/stubs_go120.go b/internal/decoder/jitdec/stubs_go120.go deleted file mode 100644 index a6dad26d7..000000000 --- a/internal/decoder/jitdec/stubs_go120.go +++ /dev/null @@ -1,106 +0,0 @@ -// +build go1.20 - -/* - * Copyright 2021 ByteDance Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jitdec - -import ( - `unsafe` - `reflect` - - _ `github.com/cloudwego/base64x` - - `github.com/bytedance/sonic/internal/rt` -) - -//go:linkname _subr__b64decode github.com/cloudwego/base64x._subr__b64decode -var _subr__b64decode uintptr - -// runtime.maxElementSize -const _max_map_element_size uintptr = 128 - -func mapfast(vt reflect.Type) bool { - return vt.Elem().Size() <= _max_map_element_size -} - -//go:nosplit -//go:linkname throw runtime.throw -//goland:noinspection GoUnusedParameter -func throw(s string) - -//go:linkname convT64 runtime.convT64 -//goland:noinspection GoUnusedParameter -func convT64(v uint64) unsafe.Pointer - -//go:linkname convTslice runtime.convTslice -//goland:noinspection GoUnusedParameter -func convTslice(v []byte) unsafe.Pointer - -//go:linkname convTstring runtime.convTstring -//goland:noinspection GoUnusedParameter -func convTstring(v string) unsafe.Pointer - -//go:noescape -//go:linkname memequal runtime.memequal -//goland:noinspection GoUnusedParameter -func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool - -//go:noescape -//go:linkname memmove runtime.memmove -//goland:noinspection GoUnusedParameter -func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr) - -//go:linkname mallocgc runtime.mallocgc -//goland:noinspection GoUnusedParameter -func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer - -//go:linkname makeslice runtime.makeslice -//goland:noinspection GoUnusedParameter -func makeslice(et *rt.GoType, len int, cap int) unsafe.Pointer - -//go:linkname makemap_small runtime.makemap_small -func makemap_small() unsafe.Pointer - -//go:linkname mapassign runtime.mapassign -//goland:noinspection GoUnusedParameter -func mapassign(t *rt.GoMapType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer - -//go:linkname mapassign_fast32 runtime.mapassign_fast32 -//goland:noinspection GoUnusedParameter -func mapassign_fast32(t *rt.GoMapType, h unsafe.Pointer, k uint32) unsafe.Pointer - -//go:linkname mapassign_fast64 runtime.mapassign_fast64 -//goland:noinspection GoUnusedParameter -func mapassign_fast64(t *rt.GoMapType, h unsafe.Pointer, k uint64) unsafe.Pointer - -//go:linkname mapassign_fast64ptr runtime.mapassign_fast64ptr -//goland:noinspection GoUnusedParameter -func mapassign_fast64ptr(t *rt.GoMapType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer - -//go:linkname mapassign_faststr runtime.mapassign_faststr -//goland:noinspection GoUnusedParameter -func mapassign_faststr(t *rt.GoMapType, h unsafe.Pointer, s string) unsafe.Pointer - -//go:nosplit -//go:linkname memclrHasPointers runtime.memclrHasPointers -//goland:noinspection GoUnusedParameter -func memclrHasPointers(ptr unsafe.Pointer, n uintptr) - -//go:noescape -//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers -//goland:noinspection GoUnusedParameter -func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) diff --git a/internal/encoder/alg/mapiter.go b/internal/encoder/alg/mapiter.go index 5d9956a90..032ae3b8a 100644 --- a/internal/encoder/alg/mapiter.go +++ b/internal/encoder/alg/mapiter.go @@ -165,19 +165,20 @@ func IteratorNext(p *MapIterator) { p.ki++ } -func IteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*MapIterator, error) { +func IteratorStart(t *rt.GoMapType, m unsafe.Pointer, fv uint64) (*MapIterator, error) { it := newIterator() rt.Mapiterinit(t, m, &it.It) + count := rt.Maplen(m) /* check for key-sorting, empty map don't need sorting */ - if m.Count == 0 || (fv & (1< it.kv.Cap { - it.kv = rt.GrowSlice(iteratorPair, it.kv, m.Count) + if count > it.kv.Cap { + it.kv = rt.GrowSlice(iteratorPair, it.kv, count) } /* dump all the key-value pairs */ @@ -189,7 +190,7 @@ func IteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*MapIterator, error } /* sort the keys, map with only 1 item don't need sorting */ - if it.ki = 1; m.Count > 1 { + if it.ki = 1; count > 1 { radixQsort(it.data(), 0, maxDepth(it.kv.Len)) } diff --git a/internal/encoder/alg/primitives.go b/internal/encoder/alg/primitives.go index 63fa01890..e2610fbc8 100644 --- a/internal/encoder/alg/primitives.go +++ b/internal/encoder/alg/primitives.go @@ -1,12 +1,12 @@ /** * Copyright 2024 ByteDance Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,8 +19,11 @@ package alg import ( "encoding" "encoding/json" + "reflect" + "unsafe" "github.com/bytedance/sonic/internal/encoder/vars" + "github.com/bytedance/sonic/internal/resolver" "github.com/bytedance/sonic/internal/rt" ) @@ -92,4 +95,10 @@ func EncodeTextMarshaler(buf *[]byte, val encoding.TextMarshaler, opt uint64) er return nil } } - \ No newline at end of file + +func IsZero(val unsafe.Pointer, fv *resolver.FieldMeta) bool { + rv := reflect.NewAt(fv.Type, val).Elem() + b1 := fv.IsZero == nil && rv.IsZero() + b2 := fv.IsZero != nil && fv.IsZero(rv) + return b1 || b2 +} diff --git a/internal/encoder/alg/spec.go b/internal/encoder/alg/spec.go index bff943626..6f76ac739 100644 --- a/internal/encoder/alg/spec.go +++ b/internal/encoder/alg/spec.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.16 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.16,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.16 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.16,!go1.25 arm64,go1.20,!go1.25 /** * Copyright 2024 ByteDance Inc. diff --git a/internal/encoder/alg/spec_compat.go b/internal/encoder/alg/spec_compat.go index c15cbf7d8..cd8369834 100644 --- a/internal/encoder/alg/spec_compat.go +++ b/internal/encoder/alg/spec_compat.go @@ -1,4 +1,4 @@ -// +build !amd64,!arm64 go1.24 !go1.16 arm64,!go1.20 +// +build !amd64,!arm64 go1.25 !go1.16 arm64,!go1.20 /** * Copyright 2024 ByteDance Inc. diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go index 575d362f8..737dd3e07 100644 --- a/internal/encoder/compiler.go +++ b/internal/encoder/compiler.go @@ -440,7 +440,8 @@ func (self *Compiler) compileStructBody(p *ir.Program, sp int, vt reflect.Type) p.Add(ir.OP_cond_set) /* compile each field */ - for _, fv := range resolver.ResolveStruct(vt) { + fvs := resolver.ResolveStruct(vt) + for i, fv := range fvs { var s []int var o resolver.Offset @@ -463,7 +464,12 @@ func (self *Compiler) compileStructBody(p *ir.Program, sp int, vt reflect.Type) /* check for "omitempty" option */ if fv.Type.Kind() != reflect.Struct && fv.Type.Kind() != reflect.Array && (fv.Opts&resolver.F_omitempty) != 0 { s = append(s, p.PC()) - self.compileStructFieldZero(p, fv.Type) + self.compileStructFieldEmpty(p, fv.Type) + } + /* check for "omitzero" option */ + if fv.Opts&resolver.F_omitzero != 0 { + s = append(s, p.PC()) + p.VField(ir.OP_is_zero, &fvs[i]) } /* add the comma if not the first element */ @@ -574,7 +580,7 @@ func (self *Compiler) compileStructFieldStr(p *ir.Program, sp int, vt reflect.Ty } } -func (self *Compiler) compileStructFieldZero(p *ir.Program, vt reflect.Type) { +func (self *Compiler) compileStructFieldEmpty(p *ir.Program, vt reflect.Type) { switch vt.Kind() { case reflect.Bool: p.Add(ir.OP_is_zero_1) diff --git a/internal/encoder/encoder_test.go b/internal/encoder/encoder_test.go index ecb0d6fa7..299c0c782 100644 --- a/internal/encoder/encoder_test.go +++ b/internal/encoder/encoder_test.go @@ -639,4 +639,4 @@ func BenchmarkEncode_Float32(b *testing.B) { b.Run(name, lib.test) } } -} \ No newline at end of file +} diff --git a/internal/encoder/ir/op.go b/internal/encoder/ir/op.go index 32cc1ad01..fe5a4ebe7 100644 --- a/internal/encoder/ir/op.go +++ b/internal/encoder/ir/op.go @@ -24,6 +24,7 @@ import ( "unsafe" "github.com/bytedance/sonic/internal/encoder/vars" + "github.com/bytedance/sonic/internal/resolver" "github.com/bytedance/sonic/internal/rt" ) @@ -81,6 +82,7 @@ const ( OP_cond_set OP_cond_testc OP_unsupported + OP_is_zero ) const ( @@ -231,6 +233,11 @@ type typAndTab struct { itab *rt.GoItab } +type typAndField struct { + vt reflect.Type + fv *resolver.FieldMeta +} + func NewInsVtab(op Op, vt reflect.Type, itab *rt.GoItab) Instr { return Instr{ o: op, @@ -241,6 +248,13 @@ func NewInsVtab(op Op, vt reflect.Type, itab *rt.GoItab) Instr { } } +func NewInsField(op Op, fv *resolver.FieldMeta) Instr { + return Instr{ + o: op, + p: unsafe.Pointer(fv), + } +} + func NewInsVp(op Op, vt reflect.Type, pv bool) Instr { i := 0 if pv { @@ -265,6 +279,10 @@ func (self Instr) Vf() uint8 { return (*rt.GoType)(self.p).KindFlags } +func (self Instr) VField() (*resolver.FieldMeta) { + return (*resolver.FieldMeta)(self.p) +} + func (self Instr) Vs() (v string) { (*rt.GoString)(unsafe.Pointer(&v)).Ptr = self.p (*rt.GoString)(unsafe.Pointer(&v)).Len = self.Vi() @@ -448,6 +466,10 @@ func (self *Program) Vtab(op Op, vt reflect.Type, itab *rt.GoItab) { *self = append(*self, NewInsVtab(op, vt, itab)) } +func (self *Program) VField(op Op, fv *resolver.FieldMeta) { + *self = append(*self, NewInsField(op, fv)) +} + func (self Program) Disassemble() string { nb := len(self) tab := make([]bool, nb+1) diff --git a/internal/encoder/omitzero_test.go b/internal/encoder/omitzero_test.go new file mode 100644 index 000000000..10fd88eb7 --- /dev/null +++ b/internal/encoder/omitzero_test.go @@ -0,0 +1,216 @@ +//go:build go1.24 + +/** + * Copyright 2025 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package encoder + +import ( + "testing" + "time" + "fmt" + "strings" +) + +type NonZeroStruct struct{} + +func (nzs NonZeroStruct) IsZero() bool { + return false +} + +type NoPanicStruct struct { + Int int `json:"int,omitzero"` +} + +func (nps *NoPanicStruct) IsZero() bool { + return nps.Int != 0 +} + +type isZeroer interface { + IsZero() bool +} + +type OptionalsZero struct { + Sr string `json:"sr"` + So string `json:"so,omitzero"` + Sw string `json:"-"` + + Ir int `json:"omitzero"` // actually named omitzero, not an option + Io int `json:"io,omitzero"` + + Slr []string `json:"slr,random"` + Slo []string `json:"slo,omitzero"` + SloNonNil []string `json:"slononnil,omitzero"` + + Mr map[string]interface{} `json:"mr"` + Mo map[string]interface{} `json:",omitzero"` + Moo map[string]interface{} `json:"moo,omitzero"` + + Fr float64 `json:"fr"` + Fo float64 `json:"fo,omitzero"` + Foo float64 `json:"foo,omitzero"` + Foo2 [2]float64 `json:"foo2,omitzero"` + + Br bool `json:"br"` + Bo bool `json:"bo,omitzero"` + + Ur uint `json:"ur"` + Uo uint `json:"uo,omitzero"` + + Str struct{} `json:"str"` + Sto struct{} `json:"sto,omitzero"` + + Time time.Time `json:"time,omitzero"` + TimeLocal time.Time `json:"timelocal,omitzero"` + Nzs NonZeroStruct `json:"nzs,omitzero"` + + NilIsZeroer isZeroer `json:"niliszeroer,omitzero"` // nil interface + NonNilIsZeroer isZeroer `json:"nonniliszeroer,omitzero"` // non-nil interface + NoPanicStruct0 isZeroer `json:"nps0,omitzero"` // non-nil interface with nil pointer + NoPanicStruct1 isZeroer `json:"nps1,omitzero"` // non-nil interface with non-nil pointer + NoPanicStruct2 *NoPanicStruct `json:"nps2,omitzero"` // nil pointer + NoPanicStruct3 *NoPanicStruct `json:"nps3,omitzero"` // non-nil pointer + NoPanicStruct4 NoPanicStruct `json:"nps4,omitzero"` // concrete type +} + +func TestOmitZero(t *testing.T) { + // ForceUseVM() + const want = `{ + "sr": "", + "omitzero": 0, + "slr": null, + "slononnil": [], + "mr": {}, + "Mo": {}, + "fr": 0, + "br": false, + "ur": 0, + "str": {}, + "nzs": {}, + "nps1": {}, + "nps3": {}, + "nps4": {} +}` + var o OptionalsZero + o.Sw = "something" + o.SloNonNil = make([]string, 0) + o.Mr = map[string]interface{}{} + o.Mo = map[string]interface{}{} + + o.Foo = -0 + o.Foo2 = [2]float64{+0, -0} + + o.TimeLocal = time.Time{}.Local() + + o.NonNilIsZeroer = time.Time{} + o.NoPanicStruct0 = (*NoPanicStruct)(nil) + o.NoPanicStruct1 = &NoPanicStruct{} + o.NoPanicStruct3 = &NoPanicStruct{} + + got, err := EncodeIndented(&o, "", " ", 0) + if err != nil { + t.Fatalf("MarshalIndent error: %v", err) + } + if got := string(got); got != want { + t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want)) + } +} + +func TestOmitZeroMap(t *testing.T) { + const want = `{ + "foo": { + "sr": "", + "omitzero": 0, + "slr": null, + "mr": null, + "fr": 0, + "br": false, + "ur": 0, + "str": {}, + "nzs": {}, + "nps4": {} + } +}` + m := map[string]OptionalsZero{"foo": {}} + got, err := EncodeIndented(m, "", " ", 0) + if err != nil { + t.Fatalf("MarshalIndent error: %v", err) + } + if got := string(got); got != want { + fmt.Println(got) + t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want)) + } +} + +type OptionalsEmptyZero struct { + Sr string `json:"sr"` + So string `json:"so,omitempty,omitzero"` + Sw string `json:"-"` + + Io int `json:"io,omitempty,omitzero"` + + Slr []string `json:"slr,random"` + Slo []string `json:"slo,omitempty,omitzero"` + SloNonNil []string `json:"slononnil,omitempty,omitzero"` + + Mr map[string]interface{} `json:"mr"` + Mo map[string]interface{} `json:",omitempty,omitzero"` + + Fr float64 `json:"fr"` + Fo float64 `json:"fo,omitempty,omitzero"` + + Br bool `json:"br"` + Bo bool `json:"bo,omitempty,omitzero"` + + Ur uint `json:"ur"` + Uo uint `json:"uo,omitempty,omitzero"` + + Str struct{} `json:"str"` + Sto struct{} `json:"sto,omitempty,omitzero"` + + Time time.Time `json:"time,omitempty,omitzero"` + Nzs NonZeroStruct `json:"nzs,omitempty,omitzero"` +} + +func TestOmitEmptyZero(t *testing.T) { + const want = `{ + "sr": "", + "slr": null, + "mr": {}, + "fr": 0, + "br": false, + "ur": 0, + "str": {}, + "nzs": {} +}` + var o OptionalsEmptyZero + o.Sw = "something" + o.SloNonNil = make([]string, 0) + o.Mr = map[string]interface{}{} + o.Mo = map[string]interface{}{} + + got, err := EncodeIndented(&o, "", " ", 0) + if err != nil { + t.Fatalf("MarshalIndent error: %v", err) + } + if got := string(got); got != want { + t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want)) + } +} + +func indentNewlines(s string) string { + return strings.Join(strings.Split(s, "\n"), "\n\t") +} diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index 84a52cf97..aa3f515ce 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -26,7 +26,6 @@ import ( "github.com/bytedance/sonic/internal/encoder/ir" "github.com/bytedance/sonic/internal/encoder/vars" "github.com/bytedance/sonic/internal/rt" - "github.com/bytedance/sonic/internal/base64" ) const ( @@ -176,7 +175,7 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. buf = alg.F64toa(buf, v) case ir.OP_bin: v := *(*[]byte)(p) - buf = base64.EncodeBase64(buf, v) + buf = rt.EncodeBase64(buf, v) case ir.OP_quote: v := *(*string)(p) buf = alg.Quote(buf, v, true) @@ -202,13 +201,13 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. } buf = *b case ir.OP_is_zero_map: - v := *(**rt.GoMap)(p) - if v == nil || v.Count == 0 { + v := *(*unsafe.Pointer)(p) + if v == nil || rt.Maplen(v) == 0 { pc = ins.Vi() continue } case ir.OP_map_iter: - v := *(**rt.GoMap)(p) + v := *(*unsafe.Pointer)(p) vt := ins.Vr() it, err := alg.IteratorStart(rt.MapType(vt), v, flags) if err != nil { @@ -284,6 +283,12 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. pc = ins.Vi() continue } + case ir.OP_is_zero: + fv := ins.VField() + if alg.IsZero(p, fv) { + pc = ins.Vi() + continue + } case ir.OP_is_zero_1: if *(*uint8)(p) == 0 { pc = ins.Vi() diff --git a/internal/encoder/x86/asm_stubs_amd64_go121.go b/internal/encoder/x86/asm_stubs_amd64_go121.go index 3d70021e4..6956bd9ba 100644 --- a/internal/encoder/x86/asm_stubs_amd64_go121.go +++ b/internal/encoder/x86/asm_stubs_amd64_go121.go @@ -1,5 +1,5 @@ -//go:build go1.21 && !go1.24 -// +build go1.21,!go1.24 +//go:build go1.21 && !go1.25 +// +build go1.21,!go1.25 // Copyright 2023 CloudWeGo Authors // diff --git a/internal/encoder/x86/assembler_regabi_amd64.go b/internal/encoder/x86/assembler_regabi_amd64.go index b118ea05c..d6d451329 100644 --- a/internal/encoder/x86/assembler_regabi_amd64.go +++ b/internal/encoder/x86/assembler_regabi_amd64.go @@ -1,5 +1,5 @@ -//go:build go1.17 && !go1.24 -// +build go1.17,!go1.24 +//go:build go1.17 && !go1.25 +// +build go1.17,!go1.25 /* * Copyright 2021 ByteDance Inc. @@ -266,6 +266,7 @@ var _OpFuncTab = [256]func(*Assembler, *ir.Instr){ ir.OP_cond_set: (*Assembler)._asm_OP_cond_set, ir.OP_cond_testc: (*Assembler)._asm_OP_cond_testc, ir.OP_unsupported: (*Assembler)._asm_OP_unsupported, + ir.OP_is_zero: (*Assembler)._asm_OP_is_zero, } func (self *Assembler) instr(v *ir.Instr) { @@ -757,7 +758,7 @@ var ( _F_f32toa = jit.Imm(int64(native.S_f32toa)) _F_i64toa = jit.Imm(int64(native.S_i64toa)) _F_u64toa = jit.Imm(int64(native.S_u64toa)) - _F_b64encode = jit.Imm(int64(_subr__b64encode)) + _F_b64encode = jit.Imm(int64(rt.SubrB64Encode)) ) var ( @@ -1098,6 +1099,20 @@ func (self *Assembler) _asm_OP_is_zero_map(p *ir.Instr) { self.Xjmp("JE", p.Vi()) // JE p.Vi() } +var ( + _F_is_zero = jit.Func(alg.IsZero) + _T_reflect_Type = rt.UnpackIface(reflect.Type(nil)) +) + +func (self *Assembler) _asm_OP_is_zero(p *ir.Instr) { + fv := p.VField() + self.Emit("MOVQ", _SP_p, _AX) // ptr + self.Emit("MOVQ", jit.ImmPtr(unsafe.Pointer(fv)), _BX) // fv + self.call_go(_F_is_zero) // CALL $fn + self.Emit("CMPB", _AX, jit.Imm(0)) // CMPB (SP.p), $0 + self.Xjmp("JNE", p.Vi()) // JE p.Vi() +} + func (self *Assembler) _asm_OP_goto(p *ir.Instr) { self.Xjmp("JMP", p.Vi()) } diff --git a/internal/encoder/x86/debug_go117.go b/internal/encoder/x86/debug_go117.go index 0aca3f4c5..1d1338756 100644 --- a/internal/encoder/x86/debug_go117.go +++ b/internal/encoder/x86/debug_go117.go @@ -1,5 +1,5 @@ -//go:build go1.17 && !go1.24 -// +build go1.17,!go1.24 +//go:build go1.17 && !go1.25 +// +build go1.17,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/internal/encoder/x86/stbus.go b/internal/encoder/x86/stbus.go index b9fa473f5..7b6b1f96b 100644 --- a/internal/encoder/x86/stbus.go +++ b/internal/encoder/x86/stbus.go @@ -27,9 +27,6 @@ import ( _ "github.com/cloudwego/base64x" ) -//go:linkname _subr__b64encode github.com/cloudwego/base64x._subr__b64encode -var _subr__b64encode uintptr - var compiler func(*rt.GoType, ... interface{}) (interface{}, error) func SetCompiler(c func(*rt.GoType, ... interface{}) (interface{}, error)) { diff --git a/internal/jit/arch_amd64.go b/internal/jit/arch_amd64.go index 7405052d6..1098a096c 100644 --- a/internal/jit/arch_amd64.go +++ b/internal/jit/arch_amd64.go @@ -17,8 +17,10 @@ package jit import ( - `github.com/twitchyliquid64/golang-asm/asm/arch` - `github.com/twitchyliquid64/golang-asm/obj` + "unsafe" + + "github.com/twitchyliquid64/golang-asm/asm/arch" + "github.com/twitchyliquid64/golang-asm/obj" ) var ( @@ -33,6 +35,13 @@ func As(op string) obj.As { } } +func ImmPtr(imm unsafe.Pointer) obj.Addr { + return obj.Addr { + Type : obj.TYPE_CONST, + Offset : int64(uintptr(imm)), + } +} + func Imm(imm int64) obj.Addr { return obj.Addr { Type : obj.TYPE_CONST, diff --git a/internal/jit/backend.go b/internal/jit/backend.go index 75e180415..25569c470 100644 --- a/internal/jit/backend.go +++ b/internal/jit/backend.go @@ -21,6 +21,7 @@ import ( `sync` _ `unsafe` + `github.com/bytedance/sonic/internal/rt` `github.com/twitchyliquid64/golang-asm/asm/arch` `github.com/twitchyliquid64/golang-asm/obj` `github.com/twitchyliquid64/golang-asm/objabi` @@ -38,10 +39,6 @@ var ( _progPool sync.Pool ) -//go:nosplit -//go:linkname throw runtime.throw -func throw(_ string) - func newProg() *obj.Prog { if val := _progPool.Get(); val == nil { return new(obj.Prog) @@ -71,7 +68,7 @@ func newLinkContext(arch *obj.LinkArch) (ret *obj.Link) { } func diagLinkContext(str string, args ...interface{}) { - throw(fmt.Sprintf(str, args...)) + rt.Throw(fmt.Sprintf(str, args...)) } func (self *Backend) New() (ret *obj.Prog) { diff --git a/internal/native/traceback_test.mock_tmpl b/internal/native/traceback_test.mock_tmpl index 706d2dbea..c60887882 100644 --- a/internal/native/traceback_test.mock_tmpl +++ b/internal/native/traceback_test.mock_tmpl @@ -1,5 +1,5 @@ -// +build !race,amd64,go1.16,!go1.24 +// +build !race,amd64,go1.16,!go1.25 // Code generated by Makefile, DO NOT EDIT. diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 795434f4e..1c30ad113 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -17,10 +17,10 @@ package resolver import ( - `fmt` - `reflect` - `strings` - `sync` + "fmt" + "reflect" + "strings" + "sync" ) type FieldOpts int @@ -29,6 +29,7 @@ type OffsetType int const ( F_omitempty FieldOpts = 1 << iota F_stringize + F_omitzero ) const ( @@ -47,6 +48,7 @@ type FieldMeta struct { Path []Offset Opts FieldOpts Type reflect.Type + IsZero func(reflect.Value) bool } func (self *FieldMeta) String() string { @@ -117,20 +119,26 @@ func resolveFields(vt reflect.Type) []FieldMeta { /* convert each field */ for _, fv := range tfv.list { + /* add to result */ + ret = append(ret, FieldMeta{}) + fm := &ret[len(ret)-1] + item := vt path := []Offset(nil) - opts := FieldOpts(0) /* check for "string" */ if fv.quoted { - opts |= F_stringize + fm.Opts |= F_stringize } /* check for "omitempty" */ if fv.omitEmpty { - opts |= F_omitempty + fm.Opts |= F_omitempty } + /* handle the "omitzero" */ + handleOmitZero(fv, fm) + /* dump the field path */ for _, i := range fv.index { kind := F_offset @@ -161,13 +169,9 @@ func resolveFields(vt reflect.Type) []FieldMeta { path[idx].Kind = F_offset } - /* add to result */ - ret = append(ret, FieldMeta { - Type: fvt, - Opts: opts, - Path: path, - Name: fv.name, - }) + fm.Type = fvt + fm.Path = path + fm.Name = fv.name } /* optimize the offsets */ diff --git a/internal/resolver/stubs_compat.go b/internal/resolver/stubs_go120.go similarity index 94% rename from internal/resolver/stubs_compat.go rename to internal/resolver/stubs_go120.go index a3479543d..c0276f544 100644 --- a/internal/resolver/stubs_compat.go +++ b/internal/resolver/stubs_go120.go @@ -1,3 +1,4 @@ +//go:build !go1.21 // +build !go1.21 /* @@ -46,3 +47,5 @@ type StdStructFields struct { //go:noescape //go:linkname typeFields encoding/json.typeFields func typeFields(_ reflect.Type) StdStructFields + +func handleOmitZero(f StdField, fv *FieldMeta) {} diff --git a/internal/resolver/stubs_go123.go b/internal/resolver/stubs_go123.go new file mode 100644 index 000000000..a73f024f5 --- /dev/null +++ b/internal/resolver/stubs_go123.go @@ -0,0 +1,51 @@ +//go:build go1.21 && !go1.24 +// +build go1.21,!go1.24 + +/* + * Copyright 2021 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package resolver + +import ( + _ `encoding/json` + `reflect` + _ `unsafe` +) + +type StdField struct { + name string + nameBytes []byte + nameNonEsc string + nameEscHTML string + tag bool + index []int + typ reflect.Type + omitEmpty bool + quoted bool + encoder func() +} + +type StdStructFields struct { + list []StdField + nameIndex map[string]*StdField + byFoldedName map[string]*StdField +} + +//go:noescape +//go:linkname typeFields encoding/json.typeFields +func typeFields(_ reflect.Type) StdStructFields + +func handleOmitZero(f StdField, fv *FieldMeta) {} diff --git a/internal/resolver/stubs_latest.go b/internal/resolver/stubs_latest.go index f5a9bff2b..b9486a699 100644 --- a/internal/resolver/stubs_latest.go +++ b/internal/resolver/stubs_latest.go @@ -1,4 +1,5 @@ -// +build go1.21 +//go:build go1.24 +// +build go1.24 /* * Copyright 2021 ByteDance Inc. @@ -33,6 +34,8 @@ type StdField struct { index []int typ reflect.Type omitEmpty bool + omitZero bool + isZero func(reflect.Value) bool quoted bool encoder func() } @@ -46,3 +49,11 @@ type StdStructFields struct { //go:noescape //go:linkname typeFields encoding/json.typeFields func typeFields(_ reflect.Type) StdStructFields + +func handleOmitZero(fv StdField, fm *FieldMeta) { + if fv.omitZero { + fm.Opts |= F_omitzero + fm.IsZero = fv.isZero + } +} + diff --git a/internal/rt/base64_amd64.go b/internal/rt/base64_amd64.go index 263bc592f..ec5ea88a0 100644 --- a/internal/rt/base64_amd64.go +++ b/internal/rt/base64_amd64.go @@ -1,8 +1,9 @@ -// +build amd64,go1.17,!go1.24 +// +build amd64,go1.17,!go1.25 package rt import ( + _ "unsafe" "github.com/cloudwego/base64x" ) @@ -15,6 +16,29 @@ func DecodeBase64(raw []byte) ([]byte, error) { return ret[:n], nil } -func EncodeBase64(src []byte) string { +func EncodeBase64ToString(src []byte) string { return base64x.StdEncoding.EncodeToString(src) } + +func EncodeBase64(buf []byte, src []byte) []byte { + if len(src) == 0 { + return append(buf, '"', '"') + } + buf = append(buf, '"') + need := base64x.StdEncoding.EncodedLen(len(src)) + if cap(buf) - len(buf) < need { + tmp := make([]byte, len(buf), len(buf) + need*2) + copy(tmp, buf) + buf = tmp + } + base64x.StdEncoding.Encode(buf[len(buf):cap(buf)], src) + buf = buf[:len(buf) + need] + buf = append(buf, '"') + return buf +} + +//go:linkname SubrB64Decode github.com/cloudwego/base64x._subr__b64decode +var SubrB64Decode uintptr + +//go:linkname SubrB64Encode github.com/cloudwego/base64x._subr__b64encode +var SubrB64Encode uintptr diff --git a/internal/rt/base64_compat.go b/internal/rt/base64_compat.go index 791f79355..bd3150fe0 100644 --- a/internal/rt/base64_compat.go +++ b/internal/rt/base64_compat.go @@ -1,4 +1,4 @@ -// +build !amd64 !go1.17 go1.24 +// +build !amd64 !go1.17 go1.25 package rt @@ -15,6 +15,23 @@ func DecodeBase64(raw []byte) ([]byte, error) { return ret[:n], nil } -func EncodeBase64(src []byte) string { +func EncodeBase64ToString(src []byte) string { return base64.StdEncoding.EncodeToString(src) } + +func EncodeBase64(buf []byte, src []byte) []byte { + if len(src) == 0 { + return append(buf, '"', '"') + } + buf = append(buf, '"') + need := base64.StdEncoding.EncodedLen(len(src)) + if cap(buf) - len(buf) < need { + tmp := make([]byte, len(buf), len(buf) + need*2) + copy(tmp, buf) + buf = tmp + } + base64.StdEncoding.Encode(buf[len(buf):cap(buf)], src) + buf = buf[:len(buf) + need] + buf = append(buf, '"') + return buf +} diff --git a/internal/rt/fastvalue.go b/internal/rt/fastvalue.go index 85c90fc07..bd41afa6c 100644 --- a/internal/rt/fastvalue.go +++ b/internal/rt/fastvalue.go @@ -75,36 +75,6 @@ func (self *GoType) Indirect() bool { return self.KindFlags&F_direct == 0 } -type GoMap struct { - Count int - Flags uint8 - B uint8 - Overflow uint16 - Hash0 uint32 - Buckets unsafe.Pointer - OldBuckets unsafe.Pointer - Evacuate uintptr - Extra unsafe.Pointer -} - -type GoMapIterator struct { - K unsafe.Pointer - V unsafe.Pointer - T *GoMapType - H *GoMap - Buckets unsafe.Pointer - Bptr *unsafe.Pointer - Overflow *[]unsafe.Pointer - OldOverflow *[]unsafe.Pointer - StartBucket uintptr - Offset uint8 - Wrapped bool - B uint8 - I uint8 - Bucket uintptr - CheckBucket uintptr -} - type GoItab struct { it unsafe.Pointer Vt *GoType diff --git a/internal/rt/gcwb.go b/internal/rt/gcwb.go index 9c670e721..bd3e4ef26 100644 --- a/internal/rt/gcwb.go +++ b/internal/rt/gcwb.go @@ -1,4 +1,4 @@ -// +build go1.21 +// +build go1.21,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/internal/rt/gcwb_legacy.go b/internal/rt/gcwb_legacy.go index 86e14f2b2..f1669df0a 100644 --- a/internal/rt/gcwb_legacy.go +++ b/internal/rt/gcwb_legacy.go @@ -1,4 +1,4 @@ -// +build go1.16,!go1.21 +// +build go1.17,!go1.21 /* * Copyright 2021 ByteDance Inc. diff --git a/internal/rt/map_legacy.go b/internal/rt/map_legacy.go new file mode 100644 index 000000000..fc8fe5171 --- /dev/null +++ b/internal/rt/map_legacy.go @@ -0,0 +1,25 @@ +// +build !go1.24 + +package rt + +import ( + "unsafe" +) + +type GoMapIterator struct { + K unsafe.Pointer + V unsafe.Pointer + T *GoMapType + H unsafe.Pointer + Buckets unsafe.Pointer + Bptr *unsafe.Pointer + Overflow *[]unsafe.Pointer + OldOverflow *[]unsafe.Pointer + StartBucket uintptr + Offset uint8 + Wrapped bool + B uint8 + I uint8 + Bucket uintptr + CheckBucket uintptr +} diff --git a/internal/rt/map_nosiwss_go124.go b/internal/rt/map_nosiwss_go124.go new file mode 100644 index 000000000..8ecb9878e --- /dev/null +++ b/internal/rt/map_nosiwss_go124.go @@ -0,0 +1,28 @@ +//go:build go1.24 && !go1.25 && !goexperiment.swissmap +// +build go1.24,!go1.25,!goexperiment.swissmap + +package rt + +import ( + "unsafe" +) + +type GoMapIterator struct { + K unsafe.Pointer + V unsafe.Pointer + T *GoMapType + H unsafe.Pointer + Buckets unsafe.Pointer + Bptr *unsafe.Pointer + Overflow *[]unsafe.Pointer + OldOverflow *[]unsafe.Pointer + StartBucket uintptr + Offset uint8 + Wrapped bool + B uint8 + I uint8 + Bucket uintptr + CheckBucket uintptr + // different from go1.23 + ClearSeq uint64 +} diff --git a/internal/rt/map_siwss_go124.go b/internal/rt/map_siwss_go124.go new file mode 100644 index 000000000..b5bf7803c --- /dev/null +++ b/internal/rt/map_siwss_go124.go @@ -0,0 +1,15 @@ +//go:build go1.24 && !go1.25 && goexperiment.swissmap +// +build go1.24,!go1.25,goexperiment.swissmap + +package rt + +import ( + "unsafe" +) + +type GoMapIterator struct { + K unsafe.Pointer + V unsafe.Pointer + T *GoMapType + It unsafe.Pointer +} diff --git a/internal/rt/stubs.go b/internal/rt/stubs.go index 1074f491b..f692f1563 100644 --- a/internal/rt/stubs.go +++ b/internal/rt/stubs.go @@ -24,12 +24,19 @@ import ( //go:noescape //go:linkname Memmove runtime.memmove func Memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr) +//go:noescape +//go:linkname MemEqual runtime.memequal +//goland:noinspection GoUnusedParameter +func MemEqual(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool //go:linkname Mapiternext runtime.mapiternext func Mapiternext(it *GoMapIterator) //go:linkname Mapiterinit runtime.mapiterinit -func Mapiterinit(t *GoMapType, m *GoMap, it *GoMapIterator) +func Mapiterinit(t *GoMapType, m unsafe.Pointer, it *GoMapIterator) + +//go:linkname Maplen reflect.maplen +func Maplen(h unsafe.Pointer) int //go:linkname IsValidNumber encoding/json.isValidNumber func IsValidNumber(s string) bool @@ -72,6 +79,9 @@ func Mallocgc(size uintptr, typ *GoType, needzero bool) unsafe.Pointer //go:linkname Makemap reflect.makemap func Makemap(*GoType, int) unsafe.Pointer +//go:linkname MakemapSmall runtime.makemap_small +func MakemapSmall() unsafe.Pointer + //go:linkname Mapassign runtime.mapassign //goland:noinspection GoUnusedParameter func Mapassign(t *GoMapType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer @@ -128,9 +138,9 @@ func GetMap64Assign(vt reflect.Type) Map64Assign { var emptyBytes = make([]byte, 0, 0) var EmptySlice = *(*GoSlice)(unsafe.Pointer(&emptyBytes)) -//go:linkname makeslice runtime.makeslice +//go:linkname MakeSliceStd runtime.makeslice //goland:noinspection GoUnusedParameter -func makeslice(et *GoType, len int, cap int) unsafe.Pointer +func MakeSliceStd(et *GoType, len int, cap int) unsafe.Pointer func MakeSlice(oldPtr unsafe.Pointer, et *GoType, newLen int) *GoSlice { if newLen == 0 { @@ -139,7 +149,7 @@ func MakeSlice(oldPtr unsafe.Pointer, et *GoType, newLen int) *GoSlice { if *(*unsafe.Pointer)(oldPtr) == nil { return &GoSlice{ - Ptr: makeslice(et, newLen, newLen), + Ptr: MakeSliceStd(et, newLen, newLen), Len: newLen, Cap: newLen, } @@ -163,3 +173,28 @@ func MakeSlice(oldPtr unsafe.Pointer, et *GoType, newLen int) *GoSlice { new.Len = newLen return &new } + +//go:nosplit +//go:linkname Throw runtime.throw +//goland:noinspection GoUnusedParameter +func Throw(s string) + +//go:linkname ConvT64 runtime.convT64 +//goland:noinspection GoUnusedParameter +func ConvT64(v uint64) unsafe.Pointer + +//go:linkname ConvTslice runtime.convTslice +//goland:noinspection GoUnusedParameter +func ConvTslice(v []byte) unsafe.Pointer + +//go:linkname ConvTstring runtime.convTstring +//goland:noinspection GoUnusedParameter +func ConvTstring(v string) unsafe.Pointer + +//go:linkname Mapassign_fast64ptr runtime.mapassign_fast64ptr +//goland:noinspection GoUnusedParameter +func Mapassign_fast64ptr(t *GoMapType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer + +//go:noescape +//go:linkname Strhash runtime.strhash +func Strhash(_ unsafe.Pointer, _ uintptr) uintptr diff --git a/loader/funcdata_compat.go b/loader/funcdata_compat.go index b4a24bcd6..68bea25d0 100644 --- a/loader/funcdata_compat.go +++ b/loader/funcdata_compat.go @@ -1,5 +1,5 @@ -//go:build !go1.17 || go1.24 -// +build !go1.17 go1.24 +//go:build !go1.17 || go1.25 +// +build !go1.17 go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/loader/funcdata_go123.go b/loader/funcdata_go123.go index a50cd364f..e1fa473a3 100644 --- a/loader/funcdata_go123.go +++ b/loader/funcdata_go123.go @@ -1,5 +1,5 @@ -//go:build go1.23 && !go1.24 -// +build go1.23,!go1.24 +//go:build go1.23 && !go1.25 +// +build go1.23,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/loader/funcdata_latest.go b/loader/funcdata_latest.go index 8b6018bdc..eb64ffa6d 100644 --- a/loader/funcdata_latest.go +++ b/loader/funcdata_latest.go @@ -1,5 +1,5 @@ -// go:build go1.18 && !go1.24 -// +build go1.18,!go1.24 +// go:build go1.18 && !go1.25 +// +build go1.18,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/rfc_test.go b/rfc_test.go index 90fbc83b5..ee431357a 100644 --- a/rfc_test.go +++ b/rfc_test.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 package sonic_test diff --git a/search_test.go b/search_test.go index a17a4c6e6..1fdadec5f 100644 --- a/search_test.go +++ b/search_test.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2021 ByteDance Inc. diff --git a/sonic.go b/sonic.go index a9adc8a8b..395730362 100644 --- a/sonic.go +++ b/sonic.go @@ -1,5 +1,5 @@ -//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24) -// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24 +//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25) +// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25 /* * Copyright 2021 ByteDance Inc.