mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
[syserror] Remove syserror from go_marshal
PiperOrigin-RevId: 368470656
This commit is contained in:
committed by
gVisor bot
parent
36dbd3b97d
commit
5c1052b6bb
+9
-1
@@ -1,4 +1,4 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
load("//tools:defs.bzl", "go_library", "go_test")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
@@ -10,3 +10,11 @@ go_library(
|
||||
stateify = False,
|
||||
visibility = ["//:sandbox"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "gohacks_test",
|
||||
size = "small",
|
||||
srcs = ["gohacks_test.go"],
|
||||
library = ":gohacks",
|
||||
deps = ["@org_golang_x_sys//unix:go_default_library"],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// 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 gohacks
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func randBuf(size int) []byte {
|
||||
b := make([]byte, size)
|
||||
for i := range b {
|
||||
b[i] = byte(rand.Intn(256))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Size of a page in bytes. Cloned from hostarch.PageSize to avoid a circular
|
||||
// dependency.
|
||||
const pageSize = 4096
|
||||
|
||||
func testCopy(dst, src []byte) (panicked bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panicked = true
|
||||
}
|
||||
}()
|
||||
debug.SetPanicOnFault(true)
|
||||
copy(dst, src)
|
||||
return panicked
|
||||
}
|
||||
|
||||
func TestSegVOnMemmove(t *testing.T) {
|
||||
// Test that SIGSEGVs received by runtime.memmove when *not* doing
|
||||
// CopyIn or CopyOut work gets propagated to the runtime.
|
||||
const bufLen = pageSize
|
||||
a, err := unix.Mmap(-1, 0, bufLen, unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE)
|
||||
if err != nil {
|
||||
t.Fatalf("Mmap failed: %v", err)
|
||||
|
||||
}
|
||||
defer unix.Munmap(a)
|
||||
b := randBuf(bufLen)
|
||||
|
||||
if !testCopy(b, a) {
|
||||
t.Fatalf("testCopy didn't panic when it should have")
|
||||
}
|
||||
|
||||
if !testCopy(a, b) {
|
||||
t.Fatalf("testCopy didn't panic when it should have")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigbusOnMemmove(t *testing.T) {
|
||||
// Test that SIGBUS received by runtime.memmove when *not* doing
|
||||
// CopyIn or CopyOut work gets propagated to the runtime.
|
||||
const bufLen = pageSize
|
||||
f, err := ioutil.TempFile("", "sigbus_test")
|
||||
if err != nil {
|
||||
t.Fatalf("TempFile failed: %v", err)
|
||||
}
|
||||
os.Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
a, err := unix.Mmap(int(f.Fd()), 0, bufLen, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
|
||||
if err != nil {
|
||||
t.Fatalf("Mmap failed: %v", err)
|
||||
|
||||
}
|
||||
defer unix.Munmap(a)
|
||||
b := randBuf(bufLen)
|
||||
|
||||
if !testCopy(b, a) {
|
||||
t.Fatalf("testCopy didn't panic when it should have")
|
||||
}
|
||||
|
||||
if !testCopy(a, b) {
|
||||
t.Fatalf("testCopy didn't panic when it should have")
|
||||
}
|
||||
}
|
||||
@@ -75,3 +75,17 @@ func StringFromImmutableBytes(bs []byte) string {
|
||||
// strings.Builder.String().
|
||||
return *(*string)(unsafe.Pointer(&bs))
|
||||
}
|
||||
|
||||
// Note that go:linkname silently doesn't work if the local name is exported,
|
||||
// necessitating an indirection for exported functions.
|
||||
|
||||
// Memmove is runtime.memmove, exported for SeqAtomicLoad/SeqAtomicTryLoad<T>.
|
||||
//
|
||||
//go:nosplit
|
||||
func Memmove(to, from unsafe.Pointer, n uintptr) {
|
||||
memmove(to, from, n)
|
||||
}
|
||||
|
||||
//go:linkname memmove runtime.memmove
|
||||
//go:noescape
|
||||
func memmove(to, from unsafe.Pointer, n uintptr)
|
||||
|
||||
@@ -166,6 +166,9 @@ type Marshallable interface {
|
||||
// %s is the first argument to the slice clause. This directive is not supported
|
||||
// for newtypes on arrays.
|
||||
//
|
||||
// Note: Partial copies are not supported for Slice API UnmarshalUnsafe and
|
||||
// MarshalUnsafe.
|
||||
//
|
||||
// The slice clause also takes an optional second argument, which must be the
|
||||
// value "inner":
|
||||
//
|
||||
|
||||
@@ -19,8 +19,6 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
@@ -568,63 +566,3 @@ func TestCompareAndSwapUint32BusError(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func testCopy(dst, src []byte) (panicked bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panicked = true
|
||||
}
|
||||
}()
|
||||
debug.SetPanicOnFault(true)
|
||||
copy(dst, src)
|
||||
return
|
||||
}
|
||||
|
||||
func TestSegVOnMemmove(t *testing.T) {
|
||||
// Test that SIGSEGVs received by runtime.memmove when *not* doing
|
||||
// CopyIn or CopyOut work gets propagated to the runtime.
|
||||
const bufLen = pageSize
|
||||
a, err := unix.Mmap(-1, 0, bufLen, unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE)
|
||||
if err != nil {
|
||||
t.Fatalf("Mmap failed: %v", err)
|
||||
|
||||
}
|
||||
defer unix.Munmap(a)
|
||||
b := randBuf(bufLen)
|
||||
|
||||
if !testCopy(b, a) {
|
||||
t.Fatalf("testCopy didn't panic when it should have")
|
||||
}
|
||||
|
||||
if !testCopy(a, b) {
|
||||
t.Fatalf("testCopy didn't panic when it should have")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigbusOnMemmove(t *testing.T) {
|
||||
// Test that SIGBUS received by runtime.memmove when *not* doing
|
||||
// CopyIn or CopyOut work gets propagated to the runtime.
|
||||
const bufLen = pageSize
|
||||
f, err := ioutil.TempFile("", "sigbus_test")
|
||||
if err != nil {
|
||||
t.Fatalf("TempFile failed: %v", err)
|
||||
}
|
||||
os.Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
a, err := unix.Mmap(int(f.Fd()), 0, bufLen, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
|
||||
if err != nil {
|
||||
t.Fatalf("Mmap failed: %v", err)
|
||||
|
||||
}
|
||||
defer unix.Munmap(a)
|
||||
b := randBuf(bufLen)
|
||||
|
||||
if !testCopy(b, a) {
|
||||
t.Fatalf("testCopy didn't panic when it should have")
|
||||
}
|
||||
|
||||
if !testCopy(a, b) {
|
||||
t.Fatalf("testCopy didn't panic when it should have")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ go_library(
|
||||
],
|
||||
visibility = ["//:sandbox"],
|
||||
deps = [
|
||||
"//pkg/gohacks",
|
||||
"//pkg/log",
|
||||
"//pkg/metric",
|
||||
"//pkg/sync",
|
||||
|
||||
@@ -43,6 +43,7 @@ go_template(
|
||||
],
|
||||
deps = [
|
||||
":sync",
|
||||
"//pkg/gohacks",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ package seqatomic
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/gohacks"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
@@ -39,7 +40,7 @@ func SeqAtomicTryLoad(seq *sync.SeqCount, epoch sync.SeqCountEpoch, ptr *Value)
|
||||
// runtime.RaceDisable() doesn't actually stop the race detector, so it
|
||||
// can't help us here. Instead, call runtime.memmove directly, which is
|
||||
// not instrumented by the race detector.
|
||||
sync.Memmove(unsafe.Pointer(&val), unsafe.Pointer(ptr), unsafe.Sizeof(val))
|
||||
gohacks.Memmove(unsafe.Pointer(&val), unsafe.Pointer(ptr), unsafe.Sizeof(val))
|
||||
} else {
|
||||
// This is ~40% faster for short reads than going through memmove.
|
||||
val = *ptr
|
||||
|
||||
@@ -17,20 +17,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Note that go:linkname silently doesn't work if the local name is exported,
|
||||
// necessitating an indirection for exported functions.
|
||||
|
||||
// Memmove is runtime.memmove, exported for SeqAtomicLoad/SeqAtomicTryLoad<T>.
|
||||
//
|
||||
//go:nosplit
|
||||
func Memmove(to, from unsafe.Pointer, n uintptr) {
|
||||
memmove(to, from, n)
|
||||
}
|
||||
|
||||
//go:linkname memmove runtime.memmove
|
||||
//go:noescape
|
||||
func memmove(to, from unsafe.Pointer, n uintptr)
|
||||
|
||||
// Gopark is runtime.gopark. Gopark calls unlockf(pointer to runtime.g, lock);
|
||||
// if unlockf returns true, Gopark blocks until Goready(pointer to runtime.g)
|
||||
// is called. unlockf and its callees must be nosplit and norace, since stack
|
||||
|
||||
@@ -18,6 +18,7 @@ go_library(
|
||||
name = "seqatomic",
|
||||
srcs = ["seqatomic_int_unsafe.go"],
|
||||
deps = [
|
||||
"//pkg/gohacks",
|
||||
"//pkg/sync",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -57,7 +57,6 @@ go_marshal = rule(
|
||||
# marshal_deps are the dependencies requied by generated code.
|
||||
marshal_deps = [
|
||||
"//pkg/gohacks",
|
||||
"//pkg/safecopy",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/marshal",
|
||||
]
|
||||
|
||||
@@ -112,10 +112,8 @@ func NewGenerator(srcs []string, out, outTest, outTestUnconditional, pkg string,
|
||||
g.imports.add("runtime")
|
||||
g.imports.add("unsafe")
|
||||
g.imports.add("gvisor.dev/gvisor/pkg/gohacks")
|
||||
g.imports.add("gvisor.dev/gvisor/pkg/safecopy")
|
||||
g.imports.add("gvisor.dev/gvisor/pkg/hostarch")
|
||||
g.imports.add("gvisor.dev/gvisor/pkg/marshal")
|
||||
|
||||
return &g, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@ func (g *interfaceGenerator) validateArrayNewtype(n *ast.Ident, a *ast.ArrayType
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitMarshallableForArrayNewtype(n *ast.Ident, a *ast.ArrayType, elt *ast.Ident) {
|
||||
g.recordUsedImport("gohacks")
|
||||
g.recordUsedImport("hostarch")
|
||||
g.recordUsedImport("io")
|
||||
g.recordUsedImport("marshal")
|
||||
g.recordUsedImport("reflect")
|
||||
g.recordUsedImport("runtime")
|
||||
g.recordUsedImport("safecopy")
|
||||
g.recordUsedImport("unsafe")
|
||||
g.recordUsedImport("hostarch")
|
||||
|
||||
lenExpr := g.arrayLenExpr(a)
|
||||
|
||||
@@ -89,14 +89,14 @@ func (g *interfaceGenerator) emitMarshallableForArrayNewtype(n *ast.Ident, a *as
|
||||
g.emit("// MarshalUnsafe implements marshal.Marshallable.MarshalUnsafe.\n")
|
||||
g.emit("func (%s *%s) MarshalUnsafe(dst []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("safecopy.CopyIn(dst, unsafe.Pointer(%s))\n", g.r)
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&%s[0]), uintptr(len(dst)))\n", g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// UnmarshalUnsafe implements marshal.Marshallable.UnmarshalUnsafe.\n")
|
||||
g.emit("func (%s *%s) UnmarshalUnsafe(src []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("safecopy.CopyOut(unsafe.Pointer(%s), src)\n", g.r)
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(len(src)))\n", g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
|
||||
@@ -95,13 +95,13 @@ func (g *interfaceGenerator) validatePrimitiveNewtype(t *ast.Ident) {
|
||||
// newtypes are always packed, so we can omit the various fallbacks required for
|
||||
// non-packed structs.
|
||||
func (g *interfaceGenerator) emitMarshallableForPrimitiveNewtype(nt *ast.Ident) {
|
||||
g.recordUsedImport("gohacks")
|
||||
g.recordUsedImport("hostarch")
|
||||
g.recordUsedImport("io")
|
||||
g.recordUsedImport("marshal")
|
||||
g.recordUsedImport("reflect")
|
||||
g.recordUsedImport("runtime")
|
||||
g.recordUsedImport("safecopy")
|
||||
g.recordUsedImport("unsafe")
|
||||
g.recordUsedImport("hostarch")
|
||||
|
||||
g.emit("// SizeBytes implements marshal.Marshallable.SizeBytes.\n")
|
||||
g.emit("//go:nosplit\n")
|
||||
@@ -141,14 +141,14 @@ func (g *interfaceGenerator) emitMarshallableForPrimitiveNewtype(nt *ast.Ident)
|
||||
g.emit("// MarshalUnsafe implements marshal.Marshallable.MarshalUnsafe.\n")
|
||||
g.emit("func (%s *%s) MarshalUnsafe(dst []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("safecopy.CopyIn(dst, unsafe.Pointer(%s))\n", g.r)
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(%s), uintptr(len(dst)))\n", g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// UnmarshalUnsafe implements marshal.Marshallable.UnmarshalUnsafe.\n")
|
||||
g.emit("func (%s *%s) UnmarshalUnsafe(src []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("safecopy.CopyOut(unsafe.Pointer(%s), src)\n", g.r)
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(len(src)))\n", g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
@@ -260,11 +260,9 @@ func (g *interfaceGenerator) emitMarshallableSliceForPrimitiveNewtype(nt *ast.Id
|
||||
g.emit("}\n")
|
||||
g.emit("size := (*%s)(nil).SizeBytes()\n\n", g.typeName())
|
||||
|
||||
g.emitNoEscapeSliceDataPointer("&src", "val")
|
||||
|
||||
g.emit("length, err := safecopy.CopyIn(dst[:(size*count)], val)\n")
|
||||
g.emitKeepAlive("src")
|
||||
g.emit("return length, err\n")
|
||||
g.emit("dst = dst[:size*count]\n")
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&src[0]), uintptr(len(dst)))\n")
|
||||
g.emit("return size*count, nil\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
@@ -279,11 +277,9 @@ func (g *interfaceGenerator) emitMarshallableSliceForPrimitiveNewtype(nt *ast.Id
|
||||
g.emit("}\n")
|
||||
g.emit("size := (*%s)(nil).SizeBytes()\n\n", g.typeName())
|
||||
|
||||
g.emitNoEscapeSliceDataPointer("&dst", "val")
|
||||
|
||||
g.emit("length, err := safecopy.CopyOut(val, src[:(size*count)])\n")
|
||||
g.emitKeepAlive("dst")
|
||||
g.emit("return length, err\n")
|
||||
g.emit("src = src[:(size*count)]\n")
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&src[0]), uintptr(len(src)))\n")
|
||||
g.emit("return size*count, nil\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
@@ -270,18 +270,18 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) {
|
||||
g.emit("%s.MarshalBytes(dst)\n", g.r)
|
||||
}
|
||||
if thisPacked {
|
||||
g.recordUsedImport("safecopy")
|
||||
g.recordUsedImport("gohacks")
|
||||
g.recordUsedImport("unsafe")
|
||||
if cond, ok := g.areFieldsPackedExpression(); ok {
|
||||
g.emit("if %s {\n", cond)
|
||||
g.inIndent(func() {
|
||||
g.emit("safecopy.CopyIn(dst, unsafe.Pointer(%s))\n", g.r)
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(%s), uintptr(len(dst)))\n", g.r)
|
||||
})
|
||||
g.emit("} else {\n")
|
||||
g.inIndent(fallback)
|
||||
g.emit("}\n")
|
||||
} else {
|
||||
g.emit("safecopy.CopyIn(dst, unsafe.Pointer(%s))\n", g.r)
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(%s), uintptr(len(dst)))\n", g.r)
|
||||
}
|
||||
} else {
|
||||
fallback()
|
||||
@@ -297,25 +297,23 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) {
|
||||
g.emit("%s.UnmarshalBytes(src)\n", g.r)
|
||||
}
|
||||
if thisPacked {
|
||||
g.recordUsedImport("safecopy")
|
||||
g.recordUsedImport("unsafe")
|
||||
g.recordUsedImport("gohacks")
|
||||
if cond, ok := g.areFieldsPackedExpression(); ok {
|
||||
g.emit("if %s {\n", cond)
|
||||
g.inIndent(func() {
|
||||
g.emit("safecopy.CopyOut(unsafe.Pointer(%s), src)\n", g.r)
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(len(src)))\n", g.r)
|
||||
})
|
||||
g.emit("} else {\n")
|
||||
g.inIndent(fallback)
|
||||
g.emit("}\n")
|
||||
} else {
|
||||
g.emit("safecopy.CopyOut(unsafe.Pointer(%s), src)\n", g.r)
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(len(src)))\n", g.r)
|
||||
}
|
||||
} else {
|
||||
fallback()
|
||||
}
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// CopyOutN implements marshal.Marshallable.CopyOutN.\n")
|
||||
g.emit("//go:nosplit\n")
|
||||
g.recordUsedImport("marshal")
|
||||
@@ -561,16 +559,15 @@ func (g *interfaceGenerator) emitMarshallableSliceForStruct(st *ast.StructType,
|
||||
g.recordUsedImport("reflect")
|
||||
g.recordUsedImport("runtime")
|
||||
g.recordUsedImport("unsafe")
|
||||
g.recordUsedImport("gohacks")
|
||||
if _, ok := g.areFieldsPackedExpression(); ok {
|
||||
g.emit("if !src[0].Packed() {\n")
|
||||
g.inIndent(fallback)
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
g.emitNoEscapeSliceDataPointer("&src", "val")
|
||||
|
||||
g.emit("length, err := safecopy.CopyIn(dst[:(size*count)], val)\n")
|
||||
g.emitKeepAlive("src")
|
||||
g.emit("return length, err\n")
|
||||
g.emit("dst = dst[:size*count]\n")
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&src[0]), uintptr(len(dst)))\n")
|
||||
g.emit("return size * count, nil\n")
|
||||
} else {
|
||||
fallback()
|
||||
}
|
||||
@@ -598,19 +595,19 @@ func (g *interfaceGenerator) emitMarshallableSliceForStruct(st *ast.StructType,
|
||||
g.emit("return size * count, nil\n")
|
||||
}
|
||||
if thisPacked {
|
||||
g.recordUsedImport("gohacks")
|
||||
g.recordUsedImport("reflect")
|
||||
g.recordUsedImport("runtime")
|
||||
g.recordUsedImport("unsafe")
|
||||
if _, ok := g.areFieldsPackedExpression(); ok {
|
||||
g.emit("if !dst[0].Packed() {\n")
|
||||
g.inIndent(fallback)
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
g.emitNoEscapeSliceDataPointer("&dst", "val")
|
||||
|
||||
g.emit("length, err := safecopy.CopyOut(val, src[:(size*count)])\n")
|
||||
g.emitKeepAlive("dst")
|
||||
g.emit("return length, err\n")
|
||||
g.emit("src = src[:(size*count)]\n")
|
||||
g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&src[0]), uintptr(len(src)))\n")
|
||||
|
||||
g.emit("return count*size, nil\n")
|
||||
} else {
|
||||
fallback()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user