Skip to content

Commit

Permalink
Merge branch 'buffer-copy-cut-paste'
Browse files Browse the repository at this point in the history
  • Loading branch information
itchyny committed Apr 14, 2018
2 parents 565894e + d02503f commit 1817e54
Show file tree
Hide file tree
Showing 2 changed files with 279 additions and 0 deletions.
128 changes: 128 additions & 0 deletions buffer/buffer.go
Expand Up @@ -150,6 +150,134 @@ func (b *Buffer) Clone() *Buffer {
return newBuf
}

// Copy a part of the buffer.
func (b *Buffer) Copy(start, end int64) *Buffer {
b.mu.Lock()
defer b.mu.Unlock()
newBuf := new(Buffer)
newBuf.rrs = make([]readerRange, 0, len(b.rrs)+1)
index := start
for _, rr := range b.rrs {
if index < rr.min || index >= end {
break
}
if index >= rr.max {
continue
}
max := mathutil.MinInt64(end-index, rr.max-index)
switch br := rr.r.(type) {
case *bytesReader:
bs := make([]byte, max)
copy(bs, br.bs[index+rr.diff:])
newBuf.rrs = append(newBuf.rrs, readerRange{newBytesReader(bs), index - start, index - start + max, -index + start})
default:
newBuf.rrs = append(newBuf.rrs, readerRange{br, index - start, index - start + max, rr.diff + start})
}
index += max
}
newBuf.rrs = append(newBuf.rrs, readerRange{newBytesReader(nil), index - start, math.MaxInt64, -index + start})
newBuf.cleanup()
newBuf.index = 0
newBuf.mu = new(sync.Mutex)
return newBuf
}

// Cut a part of the buffer.
func (b *Buffer) Cut(start, end int64) {
b.mu.Lock()
defer b.mu.Unlock()
rrs := make([]readerRange, 0, len(b.rrs)+1)
var index, max int64
for _, rr := range b.rrs {
if start >= rr.max {
rrs = append(rrs, rr)
index = rr.max
continue
}
if end <= rr.min {
max = rr.max - rr.min + index
if rr.max == math.MaxInt64 {
max = math.MaxInt64
}
rrs = append(rrs, readerRange{rr.r, index, max, rr.diff - index + rr.min})
index = max
continue
}
if start >= rr.min {
max = start - index
switch br := rr.r.(type) {
case *bytesReader:
bs := make([]byte, max)
copy(bs, br.bs[index+rr.diff:])
rrs = append(rrs, readerRange{newBytesReader(bs), index, index + max, -index})
default:
rrs = append(rrs, readerRange{br, index, index + max, rr.diff})
}
index += max
}
if end < rr.max {
max = rr.max - end
switch br := rr.r.(type) {
case *bytesReader:
bs := make([]byte, max)
copy(bs, br.bs[end+rr.diff:])
rrs = append(rrs, readerRange{newBytesReader(bs), index, index + max, -index})
default:
if rr.max == math.MaxInt64 {
max = math.MaxInt64 - index
}
rrs = append(rrs, readerRange{br, index, index + max, rr.diff + end - index})
}
index += max
}
}
if index != math.MaxInt64 {
rrs = append(rrs, readerRange{newBytesReader(nil), index, math.MaxInt64, -index})
}
b.rrs = rrs
b.index = 0
b.cleanup()
}

// Paste a buffer into a buffer.
func (b *Buffer) Paste(offset int64, c *Buffer) {
b.mu.Lock()
c.mu.Lock()
defer b.mu.Unlock()
defer c.mu.Unlock()
rrs := make([]readerRange, 0, len(b.rrs)+len(c.rrs)+1)
var index, max int64
for _, rr := range b.rrs {
if offset >= rr.max {
rrs = append(rrs, rr)
continue
}
if offset < rr.min {
max = mathutil.MinInt64(rr.max, math.MaxInt64-index+rr.min) + index - rr.min
rrs = append(rrs, readerRange{b.clone(rr.r), index, max, rr.diff - index + rr.min})
index = max
continue
}
rrs = append(rrs, readerRange{b.clone(rr.r), rr.min, offset, rr.diff})
index = offset
for _, rr := range c.rrs {
if rr.max == math.MaxInt64 {
l, _ := rr.r.Seek(0, io.SeekEnd)
max = l + index
} else {
max = rr.max - rr.min + index
}
rrs = append(rrs, readerRange{b.clone(rr.r), index, max, rr.diff - index + rr.min})
index = max
}
max = mathutil.MinInt64(rr.max, math.MaxInt64-index+offset) + index - offset
rrs = append(rrs, readerRange{b.clone(rr.r), index, max, rr.diff - index + offset})
index = max
}
b.rrs = rrs
b.cleanup()
}

// Insert inserts a byte at the specific position.
func (b *Buffer) Insert(offset int64, c byte) {
b.mu.Lock()
Expand Down
151 changes: 151 additions & 0 deletions buffer/buffer_test.go
Expand Up @@ -2,6 +2,7 @@ package buffer

import (
"io"
"math"
"reflect"
"strings"
"testing"
Expand Down Expand Up @@ -188,6 +189,156 @@ func TestBufferClone(t *testing.T) {
}
}

func TestBufferCopy(t *testing.T) {
b := NewBuffer(strings.NewReader("0123456789abcdef"))
b.Replace(3, 0x41)
b.Replace(4, 0x42)
b.Replace(5, 0x43)
b.Replace(9, 0x43)
b.Replace(10, 0x44)
b.Replace(11, 0x45)
b.Replace(12, 0x46)
b.Replace(14, 0x47)
testCases := []struct {
start, end int64
expected string
}{
{0, 16, "012ABC678CDEFdGf"},
{0, 15, "012ABC678CDEFdG"},
{1, 12, "12ABC678CDE"},
{4, 14, "BC678CDEFd"},
{2, 10, "2ABC678C"},
{4, 10, "BC678C"},
{2, 7, "2ABC6"},
{5, 10, "C678C"},
{7, 11, "78CD"},
{8, 10, "8C"},
{14, 20, "Gf"},
{9, 9, ""},
{10, 8, ""},
}
for _, testCase := range testCases {
got := b.Copy(testCase.start, testCase.end)
p := make([]byte, 17)
_, _ = got.Read(p)
if !strings.HasPrefix(string(p), testCase.expected+"\x00") {
t.Errorf("Copy(%d, %d) should clone %q but got %q", testCase.start, testCase.end, testCase.expected, string(p))
}
for _, rr := range got.rrs {
switch br := rr.r.(type) {
case *bytesReader:
if rr.max != math.MaxInt64 && int64(len(br.bs)) != rr.max-rr.min || rr.min+rr.diff != 0 {
t.Errorf("invalid bytesReader after Copy(%d, %d): %#v %#v", testCase.start, testCase.end, br, rr)
}
}
}
got.Insert(0, 0x48)
got.Insert(int64(len(testCase.expected)+1), 0x49)
p = make([]byte, 19)
_, err := got.Seek(0, io.SeekStart)
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
_, _ = got.Read(p)
if !strings.HasPrefix(string(p), "H"+testCase.expected+"I\x00") {
t.Errorf("Copy(%d, %d) should clone %q but got %q", testCase.start, testCase.end, testCase.expected, string(p))
}
}
}

func TestBufferCut(t *testing.T) {
b := NewBuffer(strings.NewReader("0123456789abcdef"))
b.Replace(3, 0x41)
b.Replace(4, 0x42)
b.Replace(5, 0x43)
b.Replace(9, 0x43)
b.Replace(10, 0x44)
b.Replace(11, 0x45)
b.Replace(12, 0x46)
b.Replace(14, 0x47)
testCases := []struct {
start, end int64
expected string
}{
{0, 0, "012ABC678CDEFdGf"},
{0, 4, "BC678CDEFdGf"},
{0, 7, "78CDEFdGf"},
{0, 10, "DEFdGf"},
{0, 16, ""},
{0, 20, ""},
{3, 4, "012BC678CDEFdGf"},
{3, 6, "012678CDEFdGf"},
{3, 11, "012EFdGf"},
{6, 10, "012ABCDEFdGf"},
{6, 14, "012ABCGf"},
{6, 15, "012ABCf"},
{6, 17, "012ABC"},
{8, 10, "012ABC67DEFdGf"},
{8, 10, "012ABC67DEFdGf"},
{10, 8, "012ABC678CDEFdGf"},
}
for _, testCase := range testCases {
got := b.Clone()
got.Cut(testCase.start, testCase.end)
p := make([]byte, 17)
_, _ = got.Read(p)
if !strings.HasPrefix(string(p), testCase.expected+"\x00") {
t.Errorf("Cut(%d, %d) should result into %q but got %q", testCase.start, testCase.end, testCase.expected, string(p))
}
for _, rr := range got.rrs {
switch br := rr.r.(type) {
case *bytesReader:
if rr.max != math.MaxInt64 && int64(len(br.bs)) != rr.max-rr.min || rr.min+rr.diff != 0 {
t.Errorf("invalid bytesReader after Cut(%d, %d): %#v %#v", testCase.start, testCase.end, br, rr)
}
}
}
got.Insert(0, 0x48)
got.Insert(int64(len(testCase.expected)+1), 0x49)
p = make([]byte, 19)
_, err := got.Seek(0, io.SeekStart)
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
_, _ = got.Read(p)
if !strings.HasPrefix(string(p), "H"+testCase.expected+"I\x00") {
t.Errorf("Cut(%d, %d) should result into %q but got %q", testCase.start, testCase.end, testCase.expected, string(p))
}
}
}

func TestBufferPaste(t *testing.T) {
b := NewBuffer(strings.NewReader("0123456789abcdef"))
c := b.Copy(3, 13)
b.Paste(5, c)
p := make([]byte, 100)
_, _ = b.Seek(0, io.SeekStart)
_, _ = b.Read(p)
expected := "012343456789abc56789abcdef"
if !strings.HasPrefix(string(p), expected+"\x00") {
t.Errorf("p should be %q but got: %q", expected, string(p))
}
c.Replace(5, 0x41)
c.Insert(6, 0x42)
b.Paste(10, c)
p = make([]byte, 100)
_, _ = b.Seek(0, io.SeekStart)
_, _ = b.Read(p)
expected = "012343456734567AB9abc89abc56789abcdef"
if !strings.HasPrefix(string(p), expected+"\x00") {
t.Errorf("p should be %q but got: %q", expected, string(p))
}
b.Cut(11, 14)
b.Cut(13, 20)
p = make([]byte, 100)
_, _ = b.Seek(0, io.SeekStart)
_, _ = b.Read(p)
expected = "012343456737Aabc56789abcdef"
if !strings.HasPrefix(string(p), expected+"\x00") {
t.Errorf("p should be %q but got: %q", expected, string(p))
}
}

func TestBufferInsert(t *testing.T) {
b := NewBuffer(strings.NewReader("0123456789abcdef"))

Expand Down

0 comments on commit 1817e54

Please sign in to comment.