Skip to content

Commit

Permalink
fix: reusable check
Browse files Browse the repository at this point in the history
  • Loading branch information
joway committed Apr 22, 2024
1 parent 8cd1577 commit 7c443cf
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 6 deletions.
2 changes: 1 addition & 1 deletion nocopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ const (
defaultLinkBufferMode = reusableMask // default buffer mode is reusable but not readonly
// reusable mode indicate to whether reuse buffer node data, default value is true
reusableMask uint8 = 1 << 0 // 0000 0001
// read-only mode enable by Refer/WriteString/WriteBinary/etc. API, default value is false
// readonly mode enable by Refer/WriteString/WriteBinary/etc. API, default value is false
readonlyMask uint8 = 1 << 1 // 0000 0010
)

Expand Down
15 changes: 10 additions & 5 deletions nocopy_linkbuffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,8 @@ func (b *UnsafeLinkBuffer) WriteBinary(p []byte) (n int, err error) {
}

// WriteDirect cannot be mixed with WriteString or WriteBinary functions.
func (b *UnsafeLinkBuffer) WriteDirect(p []byte, remainLen int) error {
n := len(p)
func (b *UnsafeLinkBuffer) WriteDirect(extra []byte, remainLen int) error {
n := len(extra)
if n == 0 || remainLen < 0 {
return nil
}
Expand All @@ -484,11 +484,16 @@ func (b *UnsafeLinkBuffer) WriteDirect(p []byte, remainLen int) error {
origin = origin.next
}
// Add the buf length of the original node
// `malloc` is the origin buffer offset that already malloced, the extra buffer should be inserted after that offset.
malloc += len(origin.buf)

// Create dataNode and newNode and insert them into the chain
dataNode := newLinkBufferNode(0)
dataNode.buf, dataNode.malloc = p[:0], n
// dataNode wrap the user buffer extra, and newNode wrap the origin left netpoll buffer
// - originNode{buf=origin, off=0, malloc=malloc, readonly=true} : non-reusable
// - dataNode{buf=extra, off=0, malloc=len(extra), readonly=true} : non-reusable
// - newNode{buf=origin, off=malloc, malloc=origin.malloc, readonly=false} : reusable
dataNode := newLinkBufferNode(0) // zero node will be set by readonly mode
dataNode.buf, dataNode.malloc = extra[:0], n

if remainLen > 0 {
newNode := newLinkBufferNode(0)
Expand Down Expand Up @@ -831,5 +836,5 @@ func (node *linkBufferNode) setMode(mask uint8, enable bool) {
}

func (node *linkBufferNode) reusable() bool {
return !(node.mode&reusableMask > 0 || node.mode&readonlyMask > 0)
return node.mode&reusableMask == 1 && node.mode&readonlyMask == 0
}
31 changes: 31 additions & 0 deletions nocopy_linkbuffer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,41 @@ func TestWriteDirect(t *testing.T) {
}
}

func TestNoCopyWriteAndRead(t *testing.T) {
// [512bytes] + [512bytes] + [1bytes]
buf := NewLinkBuffer()
userBuf := make([]byte, 512)
for i := 0; i < len(userBuf); i++ {
userBuf[i] = 'b'
}
bt, _ := buf.Malloc(1024)
for i := 0; i < 512; i++ {
bt[i] = 'a'
}
buf.WriteDirect(userBuf, 512) // nocopy write
bt[512] = 'c'
buf.MallocAck(1025)
buf.Flush()
Equal(t, buf.Len(), 1025)

bt, _ = buf.ReadBinary(512) // nocopy read
for i := 0; i < len(bt); i++ {
MustTrue(t, bt[i] == 'a')
}
MustTrue(t, !buf.read.reusable() && buf.read.getMode(readonlyMask)) // next read node must be read-only
bt, _ = buf.ReadBinary(512) // nocopy read userBuf
for i := 0; i < len(bt); i++ {
MustTrue(t, bt[i] == 'b')
}
MustTrue(t, &bt[0] == &userBuf[0])
_ = buf.Release()
}

func TestBufferMode(t *testing.T) {
bufnode := newLinkBufferNode(0)
MustTrue(t, bufnode.getMode(reusableMask))
MustTrue(t, bufnode.getMode(readonlyMask))
MustTrue(t, !bufnode.reusable())

bufnode = newLinkBufferNode(1)
MustTrue(t, bufnode.getMode(reusableMask))
Expand Down

0 comments on commit 7c443cf

Please sign in to comment.