mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-23 13:52:26 +08:00
Compare commits
4 Commits
66dfd9c4fd
...
4c21fe700c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c21fe700c | ||
|
|
b7370e8ef8 | ||
|
|
38920e3be6 | ||
|
|
a630a7cda9 |
@@ -4,7 +4,9 @@
|
||||
// Package pointer contains some util functions to operate go pointer.
|
||||
package pointer
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Of returns a pointer to the value `v`.
|
||||
// Play: https://go.dev/play/p/HFd70x4DrMj
|
||||
@@ -47,5 +49,10 @@ func ExtractPointer(value any) any {
|
||||
if t.Kind() != reflect.Pointer {
|
||||
return value
|
||||
}
|
||||
return ExtractPointer(v.Elem().Interface())
|
||||
|
||||
if v.Elem().IsValid() {
|
||||
return ExtractPointer(v.Elem().Interface())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,20 +8,25 @@ import (
|
||||
crand "crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
const (
|
||||
Numeral = "0123456789"
|
||||
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||
MaximumCapacity = 1 << 31
|
||||
Numeral = "0123456789"
|
||||
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||
)
|
||||
|
||||
var rn = rand.NewSource(time.Now().UnixNano())
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
@@ -108,18 +113,61 @@ func RandSymbolChar(length int) string {
|
||||
return random(SymbolChars, length)
|
||||
}
|
||||
|
||||
// nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂,参考java8的hashmap的tableSizeFor函数
|
||||
func nearestPowerOfTwo(cap int) int {
|
||||
n := cap - 1
|
||||
n |= n >> 1
|
||||
n |= n >> 2
|
||||
n |= n >> 4
|
||||
n |= n >> 8
|
||||
n |= n >> 16
|
||||
if n < 0 {
|
||||
return 1
|
||||
} else if n >= MaximumCapacity {
|
||||
return MaximumCapacity
|
||||
}
|
||||
return n + 1
|
||||
}
|
||||
|
||||
// random generate a random string based on given string range.
|
||||
func random(s string, length int) string {
|
||||
b := make([]byte, length)
|
||||
|
||||
// fix: https://github.com/duke-git/lancet/issues/75
|
||||
// r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
for i := range b {
|
||||
b[i] = s[rand.Int63()%int64(len(s))]
|
||||
// 仿照strings.Builder
|
||||
// 创建一个长度为 length 的字节切片
|
||||
bytes := make([]byte, length)
|
||||
strLength := len(s)
|
||||
if strLength <= 0 {
|
||||
return ""
|
||||
} else if strLength == 1 {
|
||||
for i := 0; i < length; i++ {
|
||||
bytes[i] = s[0]
|
||||
}
|
||||
return *(*string)(unsafe.Pointer(&bytes))
|
||||
}
|
||||
|
||||
return string(b)
|
||||
// s的字符需要使用多少个比特位数才能表示完
|
||||
// letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快
|
||||
letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(strLength))))
|
||||
// 最大的字母id掩码
|
||||
var letterIdMask int64 = 1<<letterIdBits - 1
|
||||
// 可用次数的最大值
|
||||
letterIdMax := 63 / letterIdBits
|
||||
// 循环生成随机字符串
|
||||
for i, cache, remain := length-1, rn.Int63(), letterIdMax; i >= 0; {
|
||||
// 检查随机数生成器是否用尽所有随机数
|
||||
if remain == 0 {
|
||||
cache, remain = rn.Int63(), letterIdMax
|
||||
}
|
||||
// 从可用字符的字符串中随机选择一个字符
|
||||
if idx := int(cache & letterIdMask); idx < len(s) {
|
||||
bytes[i] = s[idx]
|
||||
i--
|
||||
}
|
||||
// 右移比特位数,为下次选择字符做准备
|
||||
cache >>= letterIdBits
|
||||
remain--
|
||||
}
|
||||
// 仿照strings.Builder用unsafe包返回一个字符串,避免拷贝
|
||||
// 将字节切片转换为字符串并返回
|
||||
return *(*string)(unsafe.Pointer(&bytes))
|
||||
}
|
||||
|
||||
// UUIdV4 generate a random UUID of version 4 according to RFC 4122.
|
||||
|
||||
@@ -52,6 +52,16 @@ func (f *Field) IsZero() bool {
|
||||
return reflect.DeepEqual(z, v)
|
||||
}
|
||||
|
||||
// IsNil returns true if the given field is nil value.
|
||||
func (f *Field) IsNil() bool {
|
||||
v := f.Value()
|
||||
if v == nil || (reflect.ValueOf(v)).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil() {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Name returns the name of the given field
|
||||
func (f *Field) Name() string {
|
||||
return f.field.Name
|
||||
@@ -111,7 +121,9 @@ func (f *Field) mapValue(value any) any {
|
||||
ret = v.Interface()
|
||||
}
|
||||
default:
|
||||
ret = v.Interface()
|
||||
if v.Kind().String() != "invalid" {
|
||||
ret = v.Interface()
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/pointer"
|
||||
@@ -61,7 +62,7 @@ func New(value any, tagName ...string) *Struct {
|
||||
// ToMap convert the exported fields of a struct to map.
|
||||
func (s *Struct) ToMap() (map[string]any, error) {
|
||||
if !s.IsStruct() {
|
||||
return nil, errInvalidStruct(s)
|
||||
return nil, fmt.Errorf("invalid struct %v", s)
|
||||
}
|
||||
|
||||
result := make(map[string]any)
|
||||
@@ -70,9 +71,15 @@ func (s *Struct) ToMap() (map[string]any, error) {
|
||||
if !f.IsExported() || f.tag.IsEmpty() || f.tag.Name == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.IsZero() && f.tag.HasOption("omitempty") {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
result[f.tag.Name] = f.mapValue(f.Value())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package structs
|
||||
|
||||
import "fmt"
|
||||
|
||||
func errInvalidStruct(v any) error {
|
||||
return fmt.Errorf("invalid struct %v", v)
|
||||
}
|
||||
@@ -65,6 +65,45 @@ func TestStruct_ToMap(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ToMap2(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestStruct_ToMap")
|
||||
|
||||
type M struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type S struct {
|
||||
ID int `json:"id"`
|
||||
M *M `json:"m"`
|
||||
}
|
||||
|
||||
s1 := &S{
|
||||
ID: 1,
|
||||
}
|
||||
var expect1 = map[string]any{"id": 1}
|
||||
map1, err := ToMap(s1)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(expect1, map1)
|
||||
|
||||
s2 := &S{
|
||||
ID: 1,
|
||||
M: &M{
|
||||
Name: "test",
|
||||
},
|
||||
}
|
||||
var expect2 = map[string]any{
|
||||
"id": 1,
|
||||
"m": map[string]any{
|
||||
"name": "test",
|
||||
}}
|
||||
map2, err := ToMap(s2)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(expect2, map2)
|
||||
}
|
||||
|
||||
func TestStruct_Fields(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
@@ -380,10 +379,7 @@ func RemoveNonPrintable(str string) string {
|
||||
// StringToBytes converts a string to byte slice without a memory allocation.
|
||||
// Play: https://go.dev/play/p/7OyFBrf9AxA
|
||||
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
|
||||
return *(*[]byte)(unsafe.Pointer(&str))
|
||||
}
|
||||
|
||||
// BytesToString converts a byte slice to string without a memory allocation.
|
||||
|
||||
Reference in New Issue
Block a user