/ringbuffer

⏰ multi-function ringbuffer, Thread-safe, Automatic expansion, Pre-read the buffer data, Explore read etc. ⏰ 多功能环形缓存,可配置加锁(线程安全)与不加锁(性能更好),当空间满自动扩展,可预先查看缓存中的数据,探索API的方式读等。

Primary LanguageGoMIT LicenseMIT

Multi-function Ringbuffer

LICENSE Github Actions Go Report Card GoDoc

中文 | English

  • Control whether locking(thread safe) or unlocking(single thread; fast) is required via parameters
  • Automatic expansion of the circular buffer implementation
  • Pre-read the data in the cache by exploring(ExploreBegin()----ExploreRead()/ExploreSize()----ExploreCommit()/ExploreBreak())

Features

  • Freedom to decide whether to lock or not, balancing performance and thread safety
  • Automatically expands space when cache is full
  • Provides peek at cached content in advance
  • Provide explore class functions that simulate reading first, but don't move the actual

Performance Testing

📈 测试数据

os platform: Mac

test for write and read

have locked

goos: darwin
goarch: amd64
pkg: github.com/zput/ringbuffer
BenchmarkRingBuffer_Sync_Unlock-4   	29223921	        43.5 ns/op
PASS

unlocked

goos: darwin
goarch: amd64
pkg: github.com/zput/ringbuffer
BenchmarkRingBuffer_Sync_Lock-4   	12641550	        89.1 ns/op
PASS

Example

Create locked/unlocked ringbuffer objects
package main

import (
	"fmt"
	"github.com/zput/ringbuffer"
)

const bufferCapacity = 5

func main() {
	var (
		unLockBuffer *ringbuffer.RingBuffer
		lockBuffer *ringbuffer.RingBuffer
	)

	// default not thread safe
	unLockBuffer = ringbuffer.New(bufferCapacity)
	fmt.Println(unLockBuffer.WriteString("writing ..."))
	fmt.Println(unLockBuffer.Size(), unLockBuffer.Capacity())
	fmt.Println(string(unLockBuffer.ReadAll2NewByteSlice()))

	var(
		whetherThreadSafe = true
		data = make([]byte, bufferCapacity, bufferCapacity+2)
		err error
	)

	lockBuffer, err = ringbuffer.NewWithDataAndPointer(data, 0, 0, false, whetherThreadSafe)
	if err != nil{
		panic(err)
	}
	// should equal true
	fmt.Println(lockBuffer.IsFull())
	// size == 5  capacity == 5
	fmt.Println(lockBuffer.Size(), lockBuffer.Capacity())
	// [0 0 0 0 0]
	fmt.Println(lockBuffer.ReadAll2NewByteSlice())

	err = lockBuffer.WriteOneByte(byte(15))
	if err != nil{
		panic(err)
	}
	// [15 0 0 0 0 0] -compare- [15 0 0 0 0]
	// still use same memory between data and lockBuffer
	fmt.Println(lockBuffer.ReadAll2NewByteSlice(), "-compare-", data)
}

/*
11 <nil>
11 11
writing ...
true
5 5
[0 0 0 0 0]
[15 0 0 0 0 0] -compare- [15 0 0 0 0]

Process finished with exit code 0
*/
Peeking into the unread data in this Ringbuffer
package main

import (
	"fmt"
	"github.com/zput/ringbuffer"
)

const bufferCapacity = 1024

func main() {
	// default not thread safe
	buffer := ringbuffer.New(bufferCapacity)

	fmt.Println(buffer.WriteString("writing ..."))

	fmt.Printf("size[%d]; capacity[%d]\n", buffer.Size(), buffer.Capacity())

	print := func(first, second []byte) {
		if len(second) == 0 {
			fmt.Println(string(first))
		} else {
			first = append(first, second...)
			fmt.Println(string(first))
		}
	}

	print(buffer.Peek(7))

	print(buffer.PeekAll())

	fmt.Println(buffer.PeekUint8())

	fmt.Println(buffer.PeekUint16())

	fmt.Println(buffer.PeekUint32())

	fmt.Println(buffer.PeekUint64())
}

/*
11 <nil>
size[11]; capacity[1024]
writing
writing ...
119
30578
2003986804
8607057786564405024

Process finished with exit code 0
*/
Explore Class Functions
package main

import (
	"fmt"
	"github.com/zput/ringbuffer"
)

const bufferCapacity = 1024

func main() {
	// default not thread safe
	buffer := ringbuffer.New(bufferCapacity)

	fmt.Println(buffer.WriteString("writing ..."))

	fmt.Println(buffer.PrintRingBufferInfo())

	buffer.ExploreBegin()

	buf := make([]byte, 4)

	n, err := buffer.ExploreRead(buf)
	if err != nil {
		panic(err)
	}

	fmt.Printf("read %d byte through ExploreRead;\nremaining %d size to explore read;\nremaining %d size to read;\n", n, buffer.ExploreSize(), buffer.Size())

	buffer.ExploreCommit()

	fmt.Println("====after commit=====")

	fmt.Printf("remaining %d size to explore read;\nremaining %d size to read;", buffer.ExploreSize(), buffer.Size())

}

/*
11 <nil>

	Ring Buffer:
		Cap: 1024
		size(can read): 11
		FreeSpace: 1013
		Content: writing ...

read 4 byte through ExploreRead;
remaining 7 size to explore read;
remaining 11 size to read;
====after commit=====
remaining 7 size to explore read;
remaining 7 size to read;
Process finished with exit code 0
*/

Reference

Appendix

welcome pr