From 9e45d490d92e835cba4ef5b9c1e1ea16c2027841 Mon Sep 17 00:00:00 2001
From: Wolfgang Welz <welzwo@gmail.com>
Date: Fri, 5 Jul 2019 17:43:23 +0200
Subject: [PATCH] Add unsafe []byte-string conversion

---
 packages/unsafeconvert/unsafeconvert.go      | 19 ++++
 packages/unsafeconvert/unsafeconvert_test.go | 92 ++++++++++++++++++++
 2 files changed, 111 insertions(+)
 create mode 100644 packages/unsafeconvert/unsafeconvert.go
 create mode 100644 packages/unsafeconvert/unsafeconvert_test.go

diff --git a/packages/unsafeconvert/unsafeconvert.go b/packages/unsafeconvert/unsafeconvert.go
new file mode 100644
index 00000000..eb43bb70
--- /dev/null
+++ b/packages/unsafeconvert/unsafeconvert.go
@@ -0,0 +1,19 @@
+package unsafeconvert
+
+import (
+	"unsafe"
+)
+
+// Converts a slice of bytes into a string without performing a copy.
+// NOTE: This is an unsafe operation and may lead to problems if the bytes
+// passed as argument are changed while the string is used.  No checking whether
+// bytes are valid UTF-8 data is performed.
+func BytesToString(bs []byte) string {
+	return *(*string)(unsafe.Pointer(&bs))
+}
+
+// Converts a string into a slice of bytes without performing a copy.
+// NOTE: This is an unsafe operation and may lead to problems if the bytes are changed.
+func StringToBytes(s string) []byte {
+	return *(*[]byte)(unsafe.Pointer(&s))
+}
diff --git a/packages/unsafeconvert/unsafeconvert_test.go b/packages/unsafeconvert/unsafeconvert_test.go
new file mode 100644
index 00000000..b6c343fd
--- /dev/null
+++ b/packages/unsafeconvert/unsafeconvert_test.go
@@ -0,0 +1,92 @@
+package unsafeconvert
+
+import (
+	"bytes"
+	"strings"
+	"testing"
+)
+
+var testStrings = []string{
+	"",
+	" ",
+	"test",
+	"こんにちは、 世界",
+	strings.Repeat(" ", 10),
+	strings.Repeat(" ", 100),
+	strings.Repeat(" ", 10000),
+	strings.Repeat(" ", 1000000),
+}
+
+func TestBytesToString(t *testing.T) {
+	for _, expected := range testStrings {
+		arg := []byte(expected)
+		actual := BytesToString(arg)
+		if actual != expected {
+			t.Errorf("BytesToString(%q) = %q but expected %q", arg, actual, expected)
+		}
+	}
+}
+
+func TestStringToBytes(t *testing.T) {
+	for _, arg := range testStrings {
+		expected := []byte(arg)
+		actual := StringToBytes(arg)
+		if !bytes.Equal(actual, expected) {
+			t.Errorf("Bytes(%q) = %q but expected %q", arg, actual, expected)
+		}
+	}
+}
+
+func TestNil(t *testing.T) {
+	actual := BytesToString(nil)
+	expected := ""
+	if actual != expected {
+		t.Errorf("String(nil) = %q but expected %q", actual, expected)
+	}
+}
+
+func createTestBytes() [][]byte {
+	result := make([][]byte, len(testStrings))
+	for i, str := range testStrings {
+		result[i] = []byte(str)
+	}
+	return result
+}
+
+func BenchmarkNativeBytesToString(b *testing.B) {
+	testBytes := createTestBytes()
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		for _, bs := range testBytes {
+			_ = string(bs)
+		}
+	}
+}
+
+func BenchmarkUnsafeBytesToString(b *testing.B) {
+	testBytes := createTestBytes()
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		for _, bs := range testBytes {
+			_ = BytesToString(bs)
+		}
+	}
+}
+
+func BenchmarkNativeStringToBytes(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for _, str := range testStrings {
+			_ = []byte(str)
+		}
+	}
+}
+
+func BenchmarkUnsafeStringToBytes(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for _, str := range testStrings {
+			_ = StringToBytes(str)
+		}
+	}
+}
-- 
GitLab