1. [feat] package nazabytes: 新增包,提供Buffer struct 2. [refactor] 删除pkg/fake/time.go

pull/3/head
q191201771 3 years ago
parent eb9f5482d2
commit 02dcf10646

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

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

@ -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 package mock
import ( import (
"github.com/q191201771/naza/pkg/nazalog"
"sort" "sort"
"sync" "sync"
"time" "time"
@ -53,11 +60,11 @@ func (c *stdClock) NewTimer(d time.Duration) *Timer {
} }
func (c *stdClock) Add(d time.Duration) { 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) { func (c *stdClock) Set(t time.Time) {
nazalog.Warnf("calling stdClock::Set will do nothing actually, are you sure about this?") // noop
} }
// --------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------

@ -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 package mock
import ( import (
"github.com/q191201771/naza/pkg/assert" "fmt"
"github.com/q191201771/naza/pkg/nazalog"
"testing" "testing"
"time" "time"
"github.com/q191201771/naza/pkg/assert"
) )
func TestClock(t *testing.T) { func TestClock(t *testing.T) {
@ -18,40 +27,40 @@ func TestClock(t *testing.T) {
// 测试Now // 测试Now
{ {
c = NewStdClock() c = NewStdClock()
nazalog.Debugf("%+v", c.Now()) fmt.Printf("%+v\n", c.Now())
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
nazalog.Debugf("%+v", c.Now()) fmt.Printf("%+v\n", c.Now())
c = NewFakeClock() c = NewFakeClock()
nazalog.Debugf("%+v", c.Now()) fmt.Printf("%+v\n", c.Now())
c.Add(10 * time.Millisecond) c.Add(10 * time.Millisecond)
nazalog.Debugf("%+v", c.Now()) fmt.Printf("%+v\n", c.Now())
} }
// 简单测试Timer // 简单测试Timer
{ {
c = NewStdClock() c = NewStdClock()
nazalog.Debugf("%+v", c.Now()) fmt.Printf("%+v\n", c.Now())
timer = c.NewTimer(100 * time.Millisecond) timer = c.NewTimer(100 * time.Millisecond)
ch = <-timer.C ch = <-timer.C
nazalog.Debugf("%+v", ch) fmt.Printf("%+v\n", ch)
c = NewFakeClock() c = NewFakeClock()
nazalog.Debugf("%+v", c.Now()) fmt.Printf("%+v\n", c.Now())
timer = c.NewTimer(100 * time.Millisecond) timer = c.NewTimer(100 * time.Millisecond)
c.Add(100 * time.Millisecond) c.Add(100 * time.Millisecond)
ch = <-timer.C ch = <-timer.C
nazalog.Debugf("%+v", ch) fmt.Printf("%+v\n", ch)
} }
// 测试Set // 测试Set
{ {
c = NewFakeClock() 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)) 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()) c.Set(time.Now())
nazalog.Debugf("%+v", c.Now()) fmt.Printf("%+v\n", c.Now())
} }
// 测试Timer::Stop // 测试Timer::Stop

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

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

@ -17,6 +17,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/q191201771/naza/pkg/mock"
"github.com/q191201771/naza/pkg/nazacolor" "github.com/q191201771/naza/pkg/nazacolor"
"github.com/q191201771/naza/pkg/nazareflect" "github.com/q191201771/naza/pkg/nazareflect"
@ -26,6 +28,8 @@ import (
var _ Logger = new(logger) var _ Logger = new(logger)
var Clock = mock.NewStdClock()
const ( const (
levelTraceString = "TRACE " levelTraceString = "TRACE "
levelDebugString = "DEBUG " levelDebugString = "DEBUG "
@ -193,7 +197,7 @@ func (l *logger) Out(level Level, calldepth int, s string) {
return return
} }
now := fake.Time_Now() now := Clock.Now()
var file string var file string
var line int var line int

@ -17,6 +17,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/q191201771/naza/pkg/mock"
"github.com/q191201771/naza/pkg/nazalog" "github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/naza/pkg/fake" "github.com/q191201771/naza/pkg/fake"
@ -130,11 +132,12 @@ func TestRotate(t *testing.T) {
}) })
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
nazalog.Info("aaa") nazalog.Info("aaa")
fake.WithFakeTimeNow(func() time.Time {
return time.Now().Add(48 * time.Hour) now := time.Now()
}, func() { nazalog.Clock = mock.NewFakeClock()
nazalog.Info("bbb") nazalog.Clock.Set(now.Add(48 * time.Hour))
}) nazalog.Info("bbb")
nazalog.Clock = mock.NewStdClock()
} }
func TestPanic(t *testing.T) { func TestPanic(t *testing.T) {

Loading…
Cancel
Save