diff --git a/pkg/fake/time.go b/pkg/fake/time.go deleted file mode 100644 index 284cbe7..0000000 --- a/pkg/fake/time.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020, Chef. All rights reserved. -// https://github.com/q191201771/naza -// -// Use of this source code is governed by a MIT-style license -// that can be found in the License file. -// -// Author: Chef (191201771@qq.com) - -package fake - -import "time" - -var now = time.Now - -func Time_Now() time.Time { - return now() -} - -func WithFakeTimeNow(n func() time.Time, fn func()) { - now = n - fn() - now = time.Now -} diff --git a/pkg/fake/time_test.go b/pkg/fake/time_test.go deleted file mode 100644 index 559e8eb..0000000 --- a/pkg/fake/time_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2020, Chef. All rights reserved. -// https://github.com/q191201771/naza -// -// Use of this source code is governed by a MIT-style license -// that can be found in the License file. -// -// Author: Chef (191201771@qq.com) - -package fake_test - -import ( - "testing" - "time" - - "github.com/q191201771/naza/pkg/assert" - - "github.com/q191201771/naza/pkg/fake" -) - -func TestWithFakeTimeNow(t *testing.T) { - fake.WithFakeTimeNow(func() time.Time { - return time.Now().Add(time.Duration(2 * time.Hour)) - }, func() { - n := fake.Time_Now() - assert.Equal(t, true, n.Sub(time.Now()).Hours() > 1) - }) -} diff --git a/pkg/mock/time.go b/pkg/mock/time.go index e8a7d29..ef3214b 100644 --- a/pkg/mock/time.go +++ b/pkg/mock/time.go @@ -1,7 +1,14 @@ +// Copyright 2021, Chef. All rights reserved. +// https://github.com/q191201771/naza +// +// Use of this source code is governed by a MIT-style license +// that can be found in the License file. +// +// Author: Chef (191201771@qq.com) + package mock import ( - "github.com/q191201771/naza/pkg/nazalog" "sort" "sync" "time" @@ -53,11 +60,11 @@ func (c *stdClock) NewTimer(d time.Duration) *Timer { } func (c *stdClock) Add(d time.Duration) { - nazalog.Warnf("calling stdClock::Add will do nothing actually, are you sure about this?") + // noop } func (c *stdClock) Set(t time.Time) { - nazalog.Warnf("calling stdClock::Set will do nothing actually, are you sure about this?") + // noop } // --------------------------------------------------------------------------------------------------------------------- diff --git a/pkg/mock/time_test.go b/pkg/mock/time_test.go index 149d40c..e84ba9e 100644 --- a/pkg/mock/time_test.go +++ b/pkg/mock/time_test.go @@ -1,10 +1,19 @@ +// Copyright 2021, Chef. All rights reserved. +// https://github.com/q191201771/naza +// +// Use of this source code is governed by a MIT-style license +// that can be found in the License file. +// +// Author: Chef (191201771@qq.com) + package mock import ( - "github.com/q191201771/naza/pkg/assert" - "github.com/q191201771/naza/pkg/nazalog" + "fmt" "testing" "time" + + "github.com/q191201771/naza/pkg/assert" ) func TestClock(t *testing.T) { @@ -18,40 +27,40 @@ func TestClock(t *testing.T) { // 测试Now { c = NewStdClock() - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) time.Sleep(10 * time.Millisecond) - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) c = NewFakeClock() - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) c.Add(10 * time.Millisecond) - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) } // 简单测试Timer { c = NewStdClock() - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) timer = c.NewTimer(100 * time.Millisecond) ch = <-timer.C - nazalog.Debugf("%+v", ch) + fmt.Printf("%+v\n", ch) c = NewFakeClock() - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) timer = c.NewTimer(100 * time.Millisecond) c.Add(100 * time.Millisecond) ch = <-timer.C - nazalog.Debugf("%+v", ch) + fmt.Printf("%+v\n", ch) } // 测试Set { c = NewFakeClock() - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) c.Set(time.Date(2000, 1, 2, 3, 4, 5, 6, time.Local)) - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) c.Set(time.Now()) - nazalog.Debugf("%+v", c.Now()) + fmt.Printf("%+v\n", c.Now()) } // 测试Timer::Stop diff --git a/pkg/nazabytes/buffer.go b/pkg/nazabytes/buffer.go new file mode 100644 index 0000000..9ab09ac --- /dev/null +++ b/pkg/nazabytes/buffer.go @@ -0,0 +1,286 @@ +// Copyright 2021, Chef. All rights reserved. +// https://github.com/q191201771/lal +// +// Use of this source code is governed by a MIT-style license +// that can be found in the License file. +// +// Author: Chef (191201771@qq.com) + +package nazabytes + +import ( + "fmt" + "io" + + "github.com/q191201771/naza/pkg/nazalog" + "github.com/q191201771/naza/pkg/nazastring" +) + +// TODO(chef): refactor 移入naza中 +// TODO(chef): 增加options: growRoundThreshold; 是否做检查 +// TODO(chef): 扩容策略函数可由外部传入 + +const growRoundThreshold = 1048576 // 1MB + +// Buffer 先进先出可扩容流式buffer,可直接读写内部切片避免拷贝 +// +// 部分Api示例: +// 读取方式1 +// buf := Bytes() +// ... // 读取buf的内容 +// Skip(n) +// +// 读取方式2 +// buf := Peek(n) +// ... +// +// 读取方式3 +// buf := make([]byte, n) +// nn, err := Read(buf) +// +// 读取方式4 +// ... String() ... +// +// 写入方式1 +// Grow(n) +// buf := WritableBytes()[:n] +// ... // 向buf中写入内容 +// Flush(n) +// // 或者直接 +// buf := ReserveBytes(n) +// ... // 向buf中写入内容 +// Flush(n) +// +// 写入方式2 +// n, err := Write(buf) +// +// 写入方式3 +// ... WriteString() ... +// +type Buffer struct { + core []byte + rpos int + wpos int +} + +func NewBuffer(initCap int) *Buffer { + return &Buffer{ + core: make([]byte, initCap, initCap), + } +} + +// NewBufferRefBytes +// +// 注意,不拷贝参数`b`的内存块,仅持有 +// +func NewBufferRefBytes(b []byte) *Buffer { + return &Buffer{ + core: b, + } +} + +// --------------------------------------------------------------------------------------------------------------------- + +// Bytes Buffer中所有未读数据,类似于PeekAll,不拷贝 +// +func (b *Buffer) Bytes() []byte { + if b.rpos == b.wpos { + return nil + } + return b.core[b.rpos:b.wpos] +} + +// Peek 查看指定长度的未读数据,不拷贝,类似于Next,但是不会修改读取偏移位置 +// +func (b *Buffer) Peek(n int) []byte { + if b.rpos == b.wpos { + return nil + } + if b.Len() < n { + return b.Bytes() + } + return b.core[b.rpos : b.rpos+n] +} + +// Skip 将前`n`未读数据标记为已读(也即消费完成) +// +func (b *Buffer) Skip(n int) { + if n > b.wpos-b.rpos { + nazalog.Warnf("[%p] Buffer::Skip too large. n=%d, %s", b, n, b.DebugString()) + b.Reset() + return + } + b.rpos += n + b.resetIfEmpty() +} + +// --------------------------------------------------------------------------------------------------------------------- + +// Grow 确保Buffer中至少有`n`大小的空间可写,类似于Reserve +// +func (b *Buffer) Grow(n int) { + //nazalog.Debugf("[%p] > Buffer::Grow. n=%d, %s", b, n, b.DebugString()) + tail := len(b.core) - b.wpos + if tail >= n { + // 尾部空闲空间足够 + return + } + + if b.rpos+tail >= n { + // 头部加上尾部空闲空间足够,将可读数据移动到头部,回收头部空闲空间 + nazalog.Debugf("[%p] Buffer::Grow. move, n=%d, copy=%d", b, n, b.Len()) + copy(b.core, b.core[b.rpos:b.wpos]) + b.wpos -= b.rpos + b.rpos = 0 + return + } + + // 扩容后总共需要的大小 + needed := b.Len() + n + + // 扩容大小在阈值范围内时,向上取值到2的倍数 + if needed < growRoundThreshold { + needed = roundUpPowerOfTwo(needed) + } + + nazalog.Debugf("[%p] Buffer::Grow. realloc, n=%d, copy=%d, cap=(%d, %d)", b, n, b.Len(), b.Cap(), needed) + core := make([]byte, needed, needed) + copy(core, b.core[b.rpos:b.wpos]) + b.core = core + b.rpos = 0 + b.wpos -= b.rpos +} + +// WritableBytes 返回当前可写入的字节切片 +// +func (b *Buffer) WritableBytes() []byte { + if len(b.core) == b.wpos { + return nil + } + return b.core[b.wpos:] +} + +// ReserveBytes 返回可写入`n`大小的字节切片,如果空闲空间不够,内部会进行扩容 +// +// 注意,返回值空间大小只会为`n`, +// +func (b *Buffer) ReserveBytes(n int) []byte { + b.Grow(n) + return b.WritableBytes()[:n] +} + +// Flush 写入完成,更新写入位置 +// +func (b *Buffer) Flush(n int) { + if len(b.core)-b.wpos < n { + nazalog.Warnf("[%p] Buffer::Flush too large. n=%d, %s", b, n, b.DebugString()) + b.wpos = len(b.core) + return + } + b.wpos += n +} + +// ----- implement io.Reader interface --------------------------------------------------------------------------------- + +// Read 拷贝,`p`空间由外部申请 +// +func (b *Buffer) Read(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + if b.Len() == 0 { + return 0, io.EOF + } + n = copy(p, b.core[b.rpos:b.wpos]) + b.Skip(n) + return n, nil +} + +// ----- implement io.Writer interface --------------------------------------------------------------------------------- + +// Write 拷贝 +// +func (b *Buffer) Write(p []byte) (n int, err error) { + b.Grow(len(p)) + copy(b.core[b.wpos:], p) + b.wpos += n + return len(p), nil +} + +// --------------------------------------------------------------------------------------------------------------------- + +// Truncate 丢弃可读数据的末尾`n`大小的数据,或者理解为取消写 +// +func (b *Buffer) Truncate(n int) { + if b.Len() < n { + nazalog.Warnf("[%p] Buffer::Truncate too large. n=%d, %s", b, n, b.DebugString()) + b.Reset() + return + } + b.wpos -= n + b.resetIfEmpty() +} + +// Reset 重置 +// +// 注意,并不会释放内存块 +// +func (b *Buffer) Reset() { + b.rpos = 0 + b.wpos = 0 +} + +// --------------------------------------------------------------------------------------------------------------------- + +// Len Buffer中还没有读的数据的长度 +// +func (b *Buffer) Len() int { + return b.wpos - b.rpos +} + +// Cap 整个Buffer占用的空间 +// +func (b *Buffer) Cap() int { + return cap(b.core) +} + +// --------------------------------------------------------------------------------------------------------------------- + +func (b *Buffer) WriteString(s string) (n int, err error) { + return b.Write(nazastring.StringToSliceByteTmp(s)) +} + +func (b *Buffer) String() string { + return string(b.Bytes()) +} + +// --------------------------------------------------------------------------------------------------------------------- + +func (b *Buffer) DebugString() string { + return fmt.Sprintf("len(core)=%d, rpos=%d, wpos=%d", len(b.core), b.rpos, b.wpos) +} + +// --------------------------------------------------------------------------------------------------------------------- + +func (b *Buffer) resetIfEmpty() { + if b.rpos == b.wpos { + b.Reset() + } +} + +// TODO(chef): refactor 移入naza中 +func roundUpPowerOfTwo(n int) int { + if n <= 2 { + return 2 + } + + n-- + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + n |= n >> 32 + n++ + return n +} diff --git a/pkg/nazabytes/buffer_test.go b/pkg/nazabytes/buffer_test.go new file mode 100644 index 0000000..68310e4 --- /dev/null +++ b/pkg/nazabytes/buffer_test.go @@ -0,0 +1,93 @@ +// Copyright 2021, Chef. All rights reserved. +// https://github.com/q191201771/lal +// +// Use of this source code is governed by a MIT-style license +// that can be found in the License file. +// +// Author: Chef (191201771@qq.com) + +package nazabytes + +import ( + "testing" + + "github.com/q191201771/naza/pkg/assert" + "github.com/q191201771/naza/pkg/nazalog" +) + +func TestBuffer(t *testing.T) { + golden := []byte("1234567890") + + b := NewBuffer(8) + assert.Equal(t, nil, b.Bytes()) + assert.Equal(t, 8, len(b.WritableBytes())) + assert.Equal(t, 0, b.Len()) + assert.Equal(t, 8, b.Cap()) + + // 简单写读 + b.Grow(5) + buf := b.WritableBytes()[:5] + assert.Equal(t, nil, b.Bytes()) + copy(buf, golden[:5]) + b.Flush(5) + buf = b.Bytes() + assert.Equal(t, golden[:5], buf) + assert.Equal(t, 5, b.Len()) + b.Skip(5) + assert.Equal(t, nil, b.Bytes()) + assert.Equal(t, 8, len(b.WritableBytes())) + assert.Equal(t, 0, b.Len()) + assert.Equal(t, 8, b.Cap()) + + // 发生扩容 + buf = b.ReserveBytes(10) + copy(buf, golden) + b.Flush(10) + buf = b.Bytes() + assert.Equal(t, golden, buf) + b.Skip(10) + assert.Equal(t, nil, b.Bytes()) + assert.Equal(t, 16, len(b.WritableBytes())) + assert.Equal(t, 0, b.Len()) + assert.Equal(t, 16, b.Cap()) + + // 利用头部空闲空间扩容 + buf = b.ReserveBytes(10) + copy(buf, golden) + b.Flush(10) + b.Skip(2) + buf = b.ReserveBytes(7) + copy(buf, golden[:7]) + b.Flush(7) + nazalog.Debugf("%s", string(b.Bytes())) + assert.Equal(t, golden[2:], b.Bytes()[:8]) + assert.Equal(t, golden[:7], b.Bytes()[8:]) + assert.Equal(t, 15, b.Len()) + assert.Equal(t, 16, b.Cap()) + + // Truncate + b.Reset() + buf = b.ReserveBytes(10) + copy(buf, golden) + b.Flush(10) + b.Truncate(4) + assert.Equal(t, golden[:6], b.Bytes()) + + // 特殊值 + b.Reset() + b.Flush(b.Cap()) + assert.Equal(t, nil, b.WritableBytes()) + + // 一些错误 + b.Reset() + b.Skip(1) + assert.Equal(t, nil, b.Bytes()) + b.Truncate(1) + assert.Equal(t, nil, b.Bytes()) + b.Flush(b.Cap() + 1) + assert.Equal(t, b.Cap(), b.Len()) + + // 特殊值,极小的扩容 + b = NewBuffer(1) + buf = b.ReserveBytes(2) +} diff --git a/pkg/nazalog/log.go b/pkg/nazalog/log.go index 3d93ce6..31eb8b6 100644 --- a/pkg/nazalog/log.go +++ b/pkg/nazalog/log.go @@ -17,6 +17,8 @@ import ( "sync" "time" + "github.com/q191201771/naza/pkg/mock" + "github.com/q191201771/naza/pkg/nazacolor" "github.com/q191201771/naza/pkg/nazareflect" @@ -26,6 +28,8 @@ import ( var _ Logger = new(logger) +var Clock = mock.NewStdClock() + const ( levelTraceString = "TRACE " levelDebugString = "DEBUG " @@ -193,7 +197,7 @@ func (l *logger) Out(level Level, calldepth int, s string) { return } - now := fake.Time_Now() + now := Clock.Now() var file string var line int diff --git a/pkg/nazalog/log_test.go b/pkg/nazalog/log_test.go index 63c57ef..3304041 100644 --- a/pkg/nazalog/log_test.go +++ b/pkg/nazalog/log_test.go @@ -17,6 +17,8 @@ import ( "testing" "time" + "github.com/q191201771/naza/pkg/mock" + "github.com/q191201771/naza/pkg/nazalog" "github.com/q191201771/naza/pkg/fake" @@ -130,11 +132,12 @@ func TestRotate(t *testing.T) { }) assert.Equal(t, nil, err) nazalog.Info("aaa") - fake.WithFakeTimeNow(func() time.Time { - return time.Now().Add(48 * time.Hour) - }, func() { - nazalog.Info("bbb") - }) + + now := time.Now() + nazalog.Clock = mock.NewFakeClock() + nazalog.Clock.Set(now.Add(48 * time.Hour)) + nazalog.Info("bbb") + nazalog.Clock = mock.NewStdClock() } func TestPanic(t *testing.T) {