From 518f6673a7114c49c1dfe73fc7f748875bb4f0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 10 Feb 2024 20:12:34 +0100 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Unsafe:?= =?UTF-8?q?=20improve=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- convert.go | 20 +++++++++++++++++--- convert_test.go | 20 ++++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/convert.go b/convert.go index aff392d..33ec41a 100644 --- a/convert.go +++ b/convert.go @@ -15,18 +15,32 @@ import ( const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112) -// #nosec G103 // UnsafeString returns a string pointer without allocation func UnsafeString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) + return unsafe.String(unsafe.SliceData(b), len(b)) } -// #nosec G103 // UnsafeBytes returns a byte pointer without allocation. func UnsafeBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } +// UnsafeString returns a string pointer without allocation +func UnsafeString_old(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// UnsafeBytes returns a byte pointer without allocation. +func UnsafeBytes_old(s string) []byte { + if s == "" { + return nil + } + + return (*[MaxStringLen]byte)(unsafe.Pointer( + (*reflect.StringHeader)(unsafe.Pointer(&s)).Data), + )[:len(s):len(s)] +} + // CopyString copies a string to make it immutable func CopyString(s string) string { return string(UnsafeBytes(s)) diff --git a/convert_test.go b/convert_test.go index b965cb0..6dc0703 100644 --- a/convert_test.go +++ b/convert_test.go @@ -57,12 +57,18 @@ func Benchmark_UnsafeString(b *testing.B) { } require.Equal(b, "Hello, World!", res) }) - b.Run("default", func(b *testing.B) { + b.Run("unsafe_old", func(b *testing.B) { for n := 0; n < b.N; n++ { - res = string(hello) + res = UnsafeString_old(hello) } require.Equal(b, "Hello, World!", res) }) + //b.Run("default", func(b *testing.B) { + // for n := 0; n < b.N; n++ { + // res = string(hello) + // } + // require.Equal(b, "Hello, World!", res) + //}) } func Test_UnsafeBytes(t *testing.T) { @@ -82,12 +88,18 @@ func Benchmark_UnsafeBytes(b *testing.B) { } require.Equal(b, []byte("Hello, World!"), res) }) - b.Run("default", func(b *testing.B) { + b.Run("unsafe_old", func(b *testing.B) { for n := 0; n < b.N; n++ { - res = []byte(hello) + res = UnsafeBytes_old(hello) } require.Equal(b, []byte("Hello, World!"), res) }) + //b.Run("default", func(b *testing.B) { + // for n := 0; n < b.N; n++ { + // res = []byte(hello) + // } + // require.Equal(b, []byte("Hello, World!"), res) + //}) } func Test_CopyString(t *testing.T) { From ea2107e9e1338415188c286290d8dddca41c71da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 10 Feb 2024 20:25:34 +0100 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Unsafe:?= =?UTF-8?q?=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- convert.go | 22 +++------------------- convert_test.go | 20 ++++---------------- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/convert.go b/convert.go index 33ec41a..717d11f 100644 --- a/convert.go +++ b/convert.go @@ -13,11 +13,11 @@ import ( "unsafe" ) -const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112) - // UnsafeString returns a string pointer without allocation func UnsafeString(b []byte) string { - return unsafe.String(unsafe.SliceData(b), len(b)) + // the new way is slower `return unsafe.String(unsafe.SliceData(b), len(b))` + // unsafe.Pointer variant: 0.5410 ns/op vs unsafe.String variant: 0.3538 ns/op + return *(*string)(unsafe.Pointer(&b)) } // UnsafeBytes returns a byte pointer without allocation. @@ -25,22 +25,6 @@ func UnsafeBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } -// UnsafeString returns a string pointer without allocation -func UnsafeString_old(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} - -// UnsafeBytes returns a byte pointer without allocation. -func UnsafeBytes_old(s string) []byte { - if s == "" { - return nil - } - - return (*[MaxStringLen]byte)(unsafe.Pointer( - (*reflect.StringHeader)(unsafe.Pointer(&s)).Data), - )[:len(s):len(s)] -} - // CopyString copies a string to make it immutable func CopyString(s string) string { return string(UnsafeBytes(s)) diff --git a/convert_test.go b/convert_test.go index 6dc0703..b965cb0 100644 --- a/convert_test.go +++ b/convert_test.go @@ -57,18 +57,12 @@ func Benchmark_UnsafeString(b *testing.B) { } require.Equal(b, "Hello, World!", res) }) - b.Run("unsafe_old", func(b *testing.B) { + b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { - res = UnsafeString_old(hello) + res = string(hello) } require.Equal(b, "Hello, World!", res) }) - //b.Run("default", func(b *testing.B) { - // for n := 0; n < b.N; n++ { - // res = string(hello) - // } - // require.Equal(b, "Hello, World!", res) - //}) } func Test_UnsafeBytes(t *testing.T) { @@ -88,18 +82,12 @@ func Benchmark_UnsafeBytes(b *testing.B) { } require.Equal(b, []byte("Hello, World!"), res) }) - b.Run("unsafe_old", func(b *testing.B) { + b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { - res = UnsafeBytes_old(hello) + res = []byte(hello) } require.Equal(b, []byte("Hello, World!"), res) }) - //b.Run("default", func(b *testing.B) { - // for n := 0; n < b.N; n++ { - // res = []byte(hello) - // } - // require.Equal(b, []byte("Hello, World!"), res) - //}) } func Test_CopyString(t *testing.T) { From 119dde24f35c4dc2dee212954286fff54ac59461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 10 Feb 2024 20:27:34 +0100 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Unsafe:?= =?UTF-8?q?=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- convert.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/convert.go b/convert.go index 717d11f..27828ec 100644 --- a/convert.go +++ b/convert.go @@ -13,6 +13,7 @@ import ( "unsafe" ) +// #nosec G103 // UnsafeString returns a string pointer without allocation func UnsafeString(b []byte) string { // the new way is slower `return unsafe.String(unsafe.SliceData(b), len(b))` @@ -20,6 +21,7 @@ func UnsafeString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } +// #nosec G103 // UnsafeBytes returns a byte pointer without allocation. func UnsafeBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s))