1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-23 13:52:26 +08:00

Compare commits

..

5 Commits

Author SHA1 Message Date
dudaodong
6d4fc981b6 release v2.2.9 2024-02-01 10:03:26 +08:00
dudaodong
4c21fe700c refactor: update StringToBytes logic 2024-01-30 10:46:55 +08:00
pigwantacat
b7370e8ef8 refactor:refactor random function (#164)
Co-authored-by: zhuhebin <zhuhebin@fengtaisec.com>
2024-01-30 10:00:14 +08:00
dudaodong
38920e3be6 fix: fix issue #162 2024-01-29 11:00:50 +08:00
dudaodong
a630a7cda9 refactor: remove struct_internal.go 2024-01-29 10:02:36 +08:00
9 changed files with 135 additions and 63 deletions

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.2.8-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.2.9-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -1945,22 +1945,6 @@ import "github.com/duke-git/lancet/v2/xerror"
#### [Contributing Guide](./CONTRIBUTING.en-US.md)
## Sponsor
Hello, I am a software developer and have been engaged in development work for 13 years. Love open source software. And be willing to put in the energy for it. I am the creator of project lancet. Since Lancet was released as open source two years ago, it has been used by more than 1,000 internal and external projects. lancet will always be free for all users. Your support is a powerful encouragement for me to continue my struggle. Thanks! You can use WeChat to scans the following QR code or clicks the following sponsor button to initiate sponsorship.
<div style="position: relative;display: inline-block;">
<img src="./docs/public/wechat_pay.png" width="260" height="260"/>
<a href="https://en.liberapay.com/Duke_Du/donate" target="\_blank"><img src="./docs/public/sponsor_btn.png" width="220" height="60"/></a>
</div>
<br/>
<br/>
*Donated funds will be used to maintain [lancet](https://www.golancet.cn/en/) website and pay for cloud server costs. Or just buy me a cup of ☕️ when I'm sleepy writing code.*
## Contributors
Thank you to all the people who contributed to lancet!

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.2.8-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.2.9-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -1946,23 +1946,9 @@ import "github.com/duke-git/lancet/v2/xerror"
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
## 赞助
您好,我是一名软件开发者,从事开发工作 13 年。热爱软件开源。并愿意为此付出精力。是开源项目 lancet 的作者。Lancet 自两年前开源发布以来,已有超过 1000 个内外部项目使用。lancet 一直会对所有用户免费。您的支持是对我继续奋斗的有力鼓励。谢谢! 微信扫描以下二维码或点击以下赞助按钮发起赞助。
<div style="position: relative;display: inline-block;">
<img src="./docs/public/wechat_pay.png" width="260" height="260"/>
<a href="https://liberapay.com/Duke_Du/donate" target="\_blank"><img src="./docs/public/sponsor_btn.png" width="220" height="60"/></a>
</div>
<br/>
<br/>
_捐赠的资金将用于后续[lancet](https://www.golancet.cn/)官网的维护和云服务器的费用支付。或者当我写代码困倦时,给我买杯 ☕️。_
## 贡献者
感谢所有为 lancet 贡献过代码的人!
感谢所有为lancet贡献过代码的人
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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
}

View File

@@ -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())
}

View File

@@ -1,7 +0,0 @@
package structs
import "fmt"
func errInvalidStruct(v any) error {
return fmt.Errorf("invalid struct %v", v)
}

View File

@@ -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()

View File

@@ -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.