mirror of
https://github.com/tursom/GoCollections.git
synced 2024-12-25 23:40:12 +08:00
replace standard library time
This commit is contained in:
parent
5f6a994c48
commit
d3b71a7eeb
@ -8,7 +8,8 @@ package concurrent
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -9,7 +9,8 @@ package concurrent
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
func TestReentrantRWLock_RLock(t *testing.T) {
|
||||
|
@ -11,9 +11,10 @@ import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
|
||||
"github.com/tursom/GoCollections/collections"
|
||||
"github.com/tursom/GoCollections/exceptions"
|
||||
"github.com/tursom/GoCollections/lang"
|
||||
|
@ -2,7 +2,8 @@ package collections
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
type Park struct {
|
||||
|
@ -2,7 +2,8 @@ package collections
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
func TestPark_Park(t *testing.T) {
|
||||
|
@ -9,7 +9,8 @@ package collections
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
|
||||
"github.com/tursom/GoCollections/concurrent"
|
||||
"github.com/tursom/GoCollections/exceptions"
|
||||
|
@ -9,7 +9,8 @@ package collections
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
|
||||
"github.com/tursom/GoCollections/unsafe"
|
||||
)
|
||||
|
@ -10,7 +10,8 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
func TestSequence_Alloc(t *testing.T) {
|
||||
|
@ -9,7 +9,8 @@ package util
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
func TestPipeline(t *testing.T) {
|
||||
|
10
go.sum
Normal file
10
go.sum
Normal file
@ -0,0 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/petermattis/goid v0.0.0-20220302125637-5f11c28912df h1:/B1Q9E4W1cmiwPQfC2vymWL7FXHCEsUzg8Rywl5avtQ=
|
||||
github.com/petermattis/goid v0.0.0-20220302125637-5f11c28912df/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/timandy/routine v1.1.1 h1:6/Z7qLFZj3GrzuRksBFzIG8YGUh8CLhjnnMePBQTrEI=
|
||||
github.com/timandy/routine v1.1.1/go.mod h1:OZHPOKSvqL/ZvqXFkNZyit0xIVelERptYXdAHH00adQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
@ -6,7 +6,7 @@
|
||||
|
||||
package lang
|
||||
|
||||
import "time"
|
||||
import "github.com/tursom/GoCollections/util/time"
|
||||
|
||||
type (
|
||||
SendChannel[T any] interface {
|
||||
|
@ -9,7 +9,8 @@ package lang
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
func TestChannel_Send(t *testing.T) {
|
||||
|
@ -7,7 +7,7 @@
|
||||
package lang
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -10,7 +10,8 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
func TestInt32_SetBit(t *testing.T) {
|
||||
|
@ -24,3 +24,13 @@ func (s Stringer) String() string {
|
||||
}
|
||||
return s.stringer()
|
||||
}
|
||||
|
||||
func Reverse[T any](s []T) []T {
|
||||
for i := 0; i < len(s)/2; i++ {
|
||||
temp := s[i]
|
||||
s[i] = s[len(s)-i-1]
|
||||
s[len(s)-i-1] = temp
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
39
util/b62/base62.go
Normal file
39
util/b62/base62.go
Normal file
@ -0,0 +1,39 @@
|
||||
package b62
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/tursom/GoCollections/util"
|
||||
)
|
||||
|
||||
var (
|
||||
digits = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
|
||||
)
|
||||
|
||||
func Encode(n uint64) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
|
||||
for n != 0 {
|
||||
remainder := n % 62
|
||||
buffer.WriteByte(digits[remainder])
|
||||
n /= 62
|
||||
}
|
||||
|
||||
return string(util.Reverse(buffer.Bytes()))
|
||||
}
|
||||
|
||||
func Decode(str string) uint64 {
|
||||
var sum uint64
|
||||
var base uint64 = 1
|
||||
|
||||
for i := range str {
|
||||
indexByte := bytes.IndexByte(digits, str[len(str)-i-1])
|
||||
if indexByte < 0 || indexByte >= 62 {
|
||||
panic(indexByte)
|
||||
}
|
||||
sum += uint64(indexByte) * base
|
||||
base *= 62
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
13
util/b62/base62_test.go
Normal file
13
util/b62/base62_test.go
Normal file
@ -0,0 +1,13 @@
|
||||
package b62
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBase62(t *testing.T) {
|
||||
encode := Encode(math.MaxUint64)
|
||||
if Decode(encode) != math.MaxUint64 {
|
||||
t.Fatal(Decode(encode))
|
||||
}
|
||||
}
|
@ -12,7 +12,8 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
func TestBloom_Contains(t *testing.T) {
|
||||
@ -46,7 +47,7 @@ func TestBloom_miss(t *testing.T) {
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
for i := 0; i < int(base/1000); i++ {
|
||||
for i := 0; i < int(base); i++ {
|
||||
bloom.Add([]byte(fmt.Sprintf("%d", i)))
|
||||
}
|
||||
|
||||
|
107
util/snowflake/snowflake.go
Normal file
107
util/snowflake/snowflake.go
Normal file
@ -0,0 +1,107 @@
|
||||
package snowflake
|
||||
|
||||
import (
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
|
||||
"github.com/tursom/GoCollections/lang"
|
||||
"github.com/tursom/GoCollections/lang/atomic"
|
||||
"github.com/tursom/GoCollections/util/b62"
|
||||
)
|
||||
|
||||
/**
|
||||
* 被改造过的雪花ID
|
||||
* |---------|------------|-----------|------------|
|
||||
* | 空位(1) | 时间戳(43) | 自增位(7) | 机器ID(13) |
|
||||
* |---------|------------|-----------|------------|
|
||||
* 时间戳仅在初始化时使用, 与序列号接壤, 这样做可以避免某一时间内大量请求导致的ID爆炸
|
||||
* 当前方案在ID溢出时, 溢出的数据会让时间戳 +1.
|
||||
* 这样做, 只要节点不重启或者在重启前平均QPS没有超标, 重启后分配的ID仍能唯一
|
||||
*
|
||||
* 当前被调试为平均每毫秒可以相应 128 个消息
|
||||
* 如果平均每个人 10 秒发一条消息, 1 秒 128 条消息大约要 1280 人, 1 毫秒 128 条消息就大约需要 128W 用户了
|
||||
* 单点无法应付如此巨量的并发, ID生成器保证性能过剩
|
||||
*
|
||||
* 当前最多支持 8192 个节点同时上线, 未来如果节点数超过了 8192 个, 也可以以ID生成的最晚时间为代价提升节点最高数量
|
||||
*/
|
||||
|
||||
const (
|
||||
incrementBase uint64 = 0x2000
|
||||
)
|
||||
|
||||
type (
|
||||
Snowflake interface {
|
||||
New() uint64
|
||||
NewStr() string
|
||||
Close()
|
||||
}
|
||||
|
||||
snowflake struct {
|
||||
nodeId uint16
|
||||
incrementLength int
|
||||
seed atomic.UInt64
|
||||
closer lang.SendChannel[struct{}]
|
||||
}
|
||||
)
|
||||
|
||||
func New(nodeId uint16) Snowflake {
|
||||
if uint64(nodeId) >= incrementBase {
|
||||
panic(nodeId)
|
||||
}
|
||||
|
||||
closer := make(chan struct{})
|
||||
|
||||
s := &snowflake{
|
||||
nodeId: nodeId,
|
||||
incrementLength: 7,
|
||||
closer: lang.RawChannel[struct{}](closer),
|
||||
}
|
||||
|
||||
s.updateTime()
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-closer:
|
||||
return
|
||||
case _, ok := <-ticker.C:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
s.updateTime()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *snowflake) Close() {
|
||||
s.closer.TrySend(struct{}{})
|
||||
}
|
||||
|
||||
func (s *snowflake) updateTime() {
|
||||
seed := s.newSeed()
|
||||
old := s.seed.Load()
|
||||
for old < seed && !s.seed.CompareAndSwap(old, seed) {
|
||||
seed = s.newSeed()
|
||||
old = s.seed.Load()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *snowflake) newSeed() uint64 {
|
||||
return uint64(s.nodeId&0x1fff) |
|
||||
(uint64(time.Now().UnixMilli()<<(s.incrementLength+13)) & uint64(0x7f_ff_ff_ff_ff_ff_ff_ff))
|
||||
}
|
||||
|
||||
func (s *snowflake) NewStr() string {
|
||||
return b62.Encode(s.New())
|
||||
}
|
||||
|
||||
func (s *snowflake) New() uint64 {
|
||||
return s.seed.Add(incrementBase)
|
||||
}
|
41
util/snowflake/snowflake_test.go
Normal file
41
util/snowflake/snowflake_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
package snowflake
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/tursom/GoCollections/util/time"
|
||||
)
|
||||
|
||||
func Test_snowflake_Close(t *testing.T) {
|
||||
s := New(0)
|
||||
s.Close()
|
||||
s.Close()
|
||||
}
|
||||
|
||||
func Test_snowflake_New(t *testing.T) {
|
||||
s := New(1)
|
||||
|
||||
ticker := time.NewTicker(time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 16; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
old := s.New()
|
||||
for i := 0; i < 10_000_000; i++ {
|
||||
n := s.New()
|
||||
if n <= old {
|
||||
panic(n)
|
||||
}
|
||||
old = n
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
42
util/time/format.go
Normal file
42
util/time/format.go
Normal file
@ -0,0 +1,42 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
Layout = time.Layout
|
||||
ANSIC = time.ANSIC
|
||||
UnixDate = time.UnixDate
|
||||
RubyDate = time.RubyDate
|
||||
RFC822 = time.RFC822
|
||||
RFC822Z = time.RFC822Z
|
||||
RFC850 = time.RFC850
|
||||
RFC1123 = time.RFC1123
|
||||
RFC1123Z = time.RFC1123Z
|
||||
RFC3339 = time.RFC3339
|
||||
RFC3339Nano = time.RFC3339Nano
|
||||
Kitchen = time.Kitchen
|
||||
// Handy time stamps.
|
||||
Stamp = time.Stamp
|
||||
StampMilli = time.StampMilli
|
||||
StampMicro = time.StampMicro
|
||||
StampNano = time.StampNano
|
||||
DateTime = time.DateTime
|
||||
DateOnly = time.DateOnly
|
||||
TimeOnly = time.TimeOnly
|
||||
)
|
||||
|
||||
type ParseError = time.ParseError
|
||||
|
||||
func Parse(layout, value string) (Time, error) {
|
||||
return time.Parse(layout, value)
|
||||
}
|
||||
|
||||
func ParseInLocation(layout, value string, loc *Location) (Time, error) {
|
||||
return time.ParseInLocation(layout, value, loc)
|
||||
}
|
||||
|
||||
func ParseDuration(s string) (Duration, error) {
|
||||
return time.ParseDuration(s)
|
||||
}
|
11
util/time/genzabbrs.go
Normal file
11
util/time/genzabbrs.go
Normal file
@ -0,0 +1,11 @@
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type MapZone = time.MapZone
|
||||
|
||||
type SupplementalData = time.SupplementalData
|
21
util/time/sleep.go
Normal file
21
util/time/sleep.go
Normal file
@ -0,0 +1,21 @@
|
||||
package time
|
||||
|
||||
import "time"
|
||||
|
||||
func Sleep(d Duration) {
|
||||
time.Sleep(d)
|
||||
}
|
||||
|
||||
type Timer = time.Timer
|
||||
|
||||
func NewTimer(d Duration) *Timer {
|
||||
return time.NewTimer(d)
|
||||
}
|
||||
|
||||
func After(d Duration) <-chan Time {
|
||||
return time.After(d)
|
||||
}
|
||||
|
||||
func AfterFunc(d Duration, f func()) *Timer {
|
||||
return time.AfterFunc(d, f)
|
||||
}
|
25
util/time/tick.go
Normal file
25
util/time/tick.go
Normal file
@ -0,0 +1,25 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"time"
|
||||
unsafe2 "unsafe"
|
||||
|
||||
"github.com/tursom/GoCollections/unsafe"
|
||||
)
|
||||
|
||||
type Ticker struct {
|
||||
time.Ticker
|
||||
}
|
||||
|
||||
func NewTicker(d Duration) *Ticker {
|
||||
return &Ticker{*time.NewTicker(d)}
|
||||
}
|
||||
|
||||
func (t *Ticker) Stop() {
|
||||
t.Ticker.Stop()
|
||||
close(*unsafe.ForceCast[chan time.Time](unsafe2.Pointer(&t.C)))
|
||||
}
|
||||
|
||||
func Tick(d Duration) <-chan Time {
|
||||
return time.Tick(d)
|
||||
}
|
70
util/time/time.go
Normal file
70
util/time/time.go
Normal file
@ -0,0 +1,70 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Time = time.Time
|
||||
|
||||
// A Month specifies a month of the year (January = 1, ...).
|
||||
type Month = time.Month
|
||||
|
||||
const (
|
||||
January = time.January
|
||||
February = time.February
|
||||
March = time.March
|
||||
April = time.April
|
||||
May = time.May
|
||||
June = time.June
|
||||
July = time.July
|
||||
August = time.August
|
||||
September = time.September
|
||||
October = time.October
|
||||
November = time.November
|
||||
December = time.December
|
||||
)
|
||||
|
||||
// A Weekday specifies a day of the week (Sunday = 0, ...).
|
||||
type Weekday = time.Weekday
|
||||
|
||||
const (
|
||||
Sunday = time.Sunday
|
||||
Monday = time.Monday
|
||||
Tuesday = time.Tuesday
|
||||
Wednesday = time.Wednesday
|
||||
Thursday = time.Thursday
|
||||
Friday = time.Friday
|
||||
Saturday = time.Saturday
|
||||
)
|
||||
|
||||
type Duration = time.Duration
|
||||
|
||||
const (
|
||||
Nanosecond = time.Nanosecond
|
||||
Microsecond = time.Microsecond
|
||||
Millisecond = time.Millisecond
|
||||
Second = time.Second
|
||||
Minute = time.Minute
|
||||
Hour = time.Hour
|
||||
)
|
||||
|
||||
// Now returns the current local time.
|
||||
func Now() Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
func Unix(sec int64, nsec int64) Time {
|
||||
return time.Unix(sec, nsec)
|
||||
}
|
||||
|
||||
func UnixMilli(msec int64) Time {
|
||||
return time.UnixMilli(msec)
|
||||
}
|
||||
|
||||
func UnixMicro(usec int64) Time {
|
||||
return time.UnixMicro(usec)
|
||||
}
|
||||
|
||||
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time {
|
||||
return time.Date(year, month, day, hour, min, sec, nsec, loc)
|
||||
}
|
19
util/time/zoneinfo.go
Normal file
19
util/time/zoneinfo.go
Normal file
@ -0,0 +1,19 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Location = time.Location
|
||||
|
||||
var UTC = time.UTC
|
||||
|
||||
var Local = time.Local
|
||||
|
||||
func FixedZone(name string, offset int) *Location {
|
||||
return time.FixedZone(name, offset)
|
||||
}
|
||||
|
||||
func LoadLocation(name string) (*Location, error) {
|
||||
return time.LoadLocation(name)
|
||||
}
|
Loading…
Reference in New Issue
Block a user