replace standard library time

This commit is contained in:
tursom 2023-04-26 17:15:33 +08:00
parent 5f6a994c48
commit d3b71a7eeb
26 changed files with 435 additions and 15 deletions

View File

@ -8,7 +8,8 @@ package concurrent
import (
"sync"
"time"
"github.com/tursom/GoCollections/util/time"
)
type (

View File

@ -9,7 +9,8 @@ package concurrent
import (
"fmt"
"testing"
"time"
"github.com/tursom/GoCollections/util/time"
)
func TestReentrantRWLock_RLock(t *testing.T) {

View File

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

View File

@ -2,7 +2,8 @@ package collections
import (
"sync"
"time"
"github.com/tursom/GoCollections/util/time"
)
type Park struct {

View File

@ -2,7 +2,8 @@ package collections
import (
"testing"
"time"
"github.com/tursom/GoCollections/util/time"
)
func TestPark_Park(t *testing.T) {

View File

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

View File

@ -9,7 +9,8 @@ package collections
import (
"fmt"
"testing"
"time"
"github.com/tursom/GoCollections/util/time"
"github.com/tursom/GoCollections/unsafe"
)

View File

@ -10,7 +10,8 @@ import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/tursom/GoCollections/util/time"
)
func TestSequence_Alloc(t *testing.T) {

View File

@ -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
View 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=

View File

@ -6,7 +6,7 @@
package lang
import "time"
import "github.com/tursom/GoCollections/util/time"
type (
SendChannel[T any] interface {

View File

@ -9,7 +9,8 @@ package lang
import (
"fmt"
"testing"
"time"
"github.com/tursom/GoCollections/util/time"
)
func TestChannel_Send(t *testing.T) {

View File

@ -7,7 +7,7 @@
package lang
import (
"time"
"github.com/tursom/GoCollections/util/time"
)
type (

View File

@ -10,7 +10,8 @@ import (
"fmt"
"sync"
"testing"
"time"
"github.com/tursom/GoCollections/util/time"
)
func TestInt32_SetBit(t *testing.T) {

View File

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

View File

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

View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}