|
|
// 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"
|
|
|
)
|
|
|
|
|
|
// TODO(chef): 增加options: growRoundThreshold; 是否做检查
|
|
|
// TODO(chef): 扩容策略函数可由外部传入
|
|
|
|
|
|
const growMinThreshold = 128
|
|
|
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 {
|
|
|
b := &Buffer{}
|
|
|
if initCap > 0 {
|
|
|
b.core = make([]byte, initCap, initCap)
|
|
|
}
|
|
|
return b
|
|
|
}
|
|
|
|
|
|
// 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, this round need=%d, copy=%d", b, n, b.Len())
|
|
|
copy(b.core, b.core[b.rpos:b.wpos])
|
|
|
b.wpos -= b.rpos
|
|
|
b.rpos = 0
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 预分配一些
|
|
|
if n <= growMinThreshold {
|
|
|
n = growMinThreshold
|
|
|
} else if n < growRoundThreshold {
|
|
|
n = roundUpPowerOfTwo(n)
|
|
|
}
|
|
|
|
|
|
// 扩容后总共需要的大小
|
|
|
needed := b.Len() + n
|
|
|
|
|
|
if len(b.core) != 0 {
|
|
|
nazalog.Debugf("[%p] Buffer::Grow. realloc, this round need=%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.wpos -= b.rpos
|
|
|
b.rpos = 0
|
|
|
}
|
|
|
|
|
|
// WritableBytes 返回当前可写入的字节切片
|
|
|
//
|
|
|
func (b *Buffer) WritableBytes() []byte {
|
|
|
if len(b.core) == b.wpos {
|
|
|
return nil
|
|
|
}
|
|
|
return b.core[b.wpos:]
|
|
|
}
|
|
|
|
|
|
// ReserveBytes
|
|
|
//
|
|
|
// 返回可写入`n`大小的字节切片,如果空闲空间不够,内部会进行扩容。
|
|
|
//
|
|
|
// 注意,一般在完成写入后,需要调用 Flush。
|
|
|
//
|
|
|
// @return: 注意,返回值空间大小只会为`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 拷贝。内部空间不够时,会自动扩容
|
|
|
//
|
|
|
// @return n: 目前恒等于`len(p)`
|
|
|
//
|
|
|
// @return err: 目前恒等于nil
|
|
|
//
|
|
|
func (b *Buffer) Write(p []byte) (n int, err error) {
|
|
|
b.Grow(len(p))
|
|
|
copy(b.core[b.wpos:], p)
|
|
|
b.wpos += len(p)
|
|
|
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
|
|
|
}
|
|
|
|
|
|
// ResetAndFree 重置并不再持有底层内存块
|
|
|
//
|
|
|
func (b *Buffer) ResetAndFree() {
|
|
|
b.core = nil
|
|
|
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(String2BytesRef(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
|
|
|
}
|