From 04abb7a3ea0b0085a05b229be5abeb825a8a32f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=87=95=E5=BD=92=E6=9D=A5?= Date: Mon, 17 Apr 2023 16:07:14 +0800 Subject: [PATCH] feat: add some function for strutil package #88 (#89) --- strutil/string.go | 66 ++++++++++++++++++++++++++++++++++++++++++ strutil/string_test.go | 53 +++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/strutil/string.go b/strutil/string.go index 73b48e2..bb7afed 100644 --- a/strutil/string.go +++ b/strutil/string.go @@ -4,9 +4,11 @@ package strutil import ( + "reflect" "strings" "unicode" "unicode/utf8" + "unsafe" ) // CamelCase coverts string to camelCase string. Non letters and numbers will be ignored. @@ -373,3 +375,67 @@ func RemoveNonPrintable(str string) string { return result } + +// StringToBytes converts a string to byte slice without a memory allocation +func StringToBytes(str string) (b []byte) { + sh := *(*reflect.StringHeader)(unsafe.Pointer(&str)) + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len + return b +} + +// BytesToString converts a byte slice to string without a memory allocation +func BytesToString(bytes []byte) string { + return *(*string)(unsafe.Pointer(&bytes)) +} + +// IsBlank checks if a string is whitespace, empty +func IsBlank(str string) bool { + if len(str) == 0 { + return true + } + // memory copies will occur here, but UTF8 will be compatible + runes := []rune(str) + for _, r := range runes { + if !unicode.IsSpace(r) { + return false + } + } + return true +} + +// HasPrefixAny check if a string starts with any of an array of specified strings +func HasPrefixAny(str string, prefixes []string) bool { + if len(str) == 0 || len(prefixes) == 0 { + return false + } + for _, prefix := range prefixes { + if strings.HasPrefix(str, prefix) { + return true + } + } + return false +} + +// HasSuffixAny check if a string ends with any of an array of specified strings +func HasSuffixAny(str string, suffixes []string) bool { + if len(str) == 0 || len(suffixes) == 0 { + return false + } + for _, suffix := range suffixes { + if strings.HasSuffix(str, suffix) { + return true + } + } + return false +} + +// IndexOffset returns the index of the first instance of substr in s after offsetting the string by `idxFrom`, +// or -1 if substr is not present in s. +func IndexOffset(str string, substr string, idxFrom int) int { + if idxFrom > len(str)-1 || idxFrom < 0 { + return -1 + } + + return strings.Index(str[idxFrom:], substr) + idxFrom +} diff --git a/strutil/string_test.go b/strutil/string_test.go index 2f90a5f..e151f81 100644 --- a/strutil/string_test.go +++ b/strutil/string_test.go @@ -1,9 +1,9 @@ package strutil import ( - "testing" - "github.com/duke-git/lancet/v2/internal" + "reflect" + "testing" ) func TestCamelCase(t *testing.T) { @@ -349,3 +349,52 @@ func TestRemoveNonPrintable(t *testing.T) { assert.Equal("hello world", RemoveNonPrintable("hello\u00a0 \u200bworld\n")) assert.Equal("你好😄", RemoveNonPrintable("你好😄")) } + +func TestString2Bytes(t *testing.T) { + assert := internal.NewAssert(t, "TestString2Bytes") + str := "abc" + bytes := StringToBytes(str) + assert.Equal(reflect.DeepEqual(bytes, []byte{'a', 'b', 'c'}), true) +} + +func TestBytes2String(t *testing.T) { + assert := internal.NewAssert(t, "TestBytes2String") + bytes := []byte{'a', 'b', 'c'} + str := BytesToString(bytes) + assert.Equal(str == "abc", true) +} + +func TestIsBlank(t *testing.T) { + assert := internal.NewAssert(t, "TestIsBlank") + assert.Equal(IsBlank(""), true) + assert.Equal(IsBlank("\t\v\f\n"), true) + assert.Equal(IsBlank(" 中文"), false) +} + +func TestHasPrefixAny(t *testing.T) { + assert := internal.NewAssert(t, "TestHasPrefixAny") + str := "foo bar" + prefixes := []string{"fo", "xyz", "hello"} + notMatches := []string{"oom", "world"} + assert.Equal(HasPrefixAny(str, prefixes), true) + assert.Equal(HasPrefixAny(str, notMatches), false) +} + +func TestHasSuffixAny(t *testing.T) { + assert := internal.NewAssert(t, "TestHasSuffixAny") + str := "foo bar" + suffixes := []string{"bar", "xyz", "hello"} + notMatches := []string{"oom", "world"} + assert.Equal(HasSuffixAny(str, suffixes), true) + assert.Equal(HasSuffixAny(str, notMatches), false) +} + +func TestIndexOffset(t *testing.T) { + assert := internal.NewAssert(t, "TestIndexOffset") + str := "foo bar hello world" + assert.Equal(IndexOffset(str, "o", 5), 12) + assert.Equal(IndexOffset(str, "o", 0), 1) + assert.Equal(IndexOffset(str, "d", len(str)-1), len(str)-1) + assert.Equal(IndexOffset(str, "d", len(str)), -1) + assert.Equal(IndexOffset(str, "f", -1), -1) +}