503 lines
11 KiB
Go
503 lines
11 KiB
Go
package buffer
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"math"
|
|
"slices"
|
|
"sync"
|
|
)
|
|
|
|
// Buffer represents a buffer.
|
|
type Buffer struct {
|
|
rrs []readerRange
|
|
index int64
|
|
mu *sync.Mutex
|
|
bytes []byte
|
|
offset int64
|
|
}
|
|
|
|
type readAtSeeker interface {
|
|
io.ReaderAt
|
|
io.Seeker
|
|
}
|
|
|
|
type readerRange struct {
|
|
r readAtSeeker
|
|
min int64
|
|
max int64
|
|
diff int64
|
|
}
|
|
|
|
// NewBuffer creates a new buffer.
|
|
func NewBuffer(r readAtSeeker) *Buffer {
|
|
return &Buffer{
|
|
rrs: []readerRange{{r: r, min: 0, max: math.MaxInt64, diff: 0}},
|
|
index: 0,
|
|
mu: new(sync.Mutex),
|
|
}
|
|
}
|
|
|
|
// Read reads bytes.
|
|
func (b *Buffer) Read(p []byte) (int, error) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.read(p)
|
|
}
|
|
|
|
func (b *Buffer) read(p []byte) (i int, err error) {
|
|
index := b.index
|
|
for _, rr := range b.rrs {
|
|
if b.index < rr.min {
|
|
break
|
|
}
|
|
if b.index >= rr.max {
|
|
continue
|
|
}
|
|
m := int(min(int64(len(p)-i), rr.max-b.index))
|
|
var k int
|
|
if k, err = rr.r.ReadAt(p[i:i+m], b.index+rr.diff); err != nil && k == 0 {
|
|
break
|
|
}
|
|
err = nil
|
|
b.index += int64(m)
|
|
i += k
|
|
}
|
|
if len(b.bytes) > 0 {
|
|
j, k := max(b.offset-index, 0), max(index-b.offset, 0)
|
|
if j < int64(len(p)) && k < int64(len(b.bytes)) {
|
|
if cnt := copy(p[j:], b.bytes[k:]); i < int(j)+cnt {
|
|
i = int(j) + cnt
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Seek sets the offset.
|
|
func (b *Buffer) Seek(offset int64, whence int) (int64, error) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.seek(offset, whence)
|
|
}
|
|
|
|
func (b *Buffer) seek(offset int64, whence int) (int64, error) {
|
|
var index int64
|
|
switch whence {
|
|
case io.SeekStart:
|
|
index = offset
|
|
case io.SeekCurrent:
|
|
index = b.index + offset
|
|
case io.SeekEnd:
|
|
var l int64
|
|
var err error
|
|
if l, err = b.len(); err != nil {
|
|
return 0, err
|
|
}
|
|
index = l + offset
|
|
default:
|
|
return 0, errors.New("buffer.Buffer.Seek: invalid whence")
|
|
}
|
|
if index < 0 {
|
|
return 0, errors.New("buffer.Buffer.Seek: negative position")
|
|
}
|
|
b.index = index
|
|
return index, nil
|
|
}
|
|
|
|
// Len returns the total size of the buffer.
|
|
func (b *Buffer) Len() (int64, error) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.len()
|
|
}
|
|
|
|
func (b *Buffer) len() (int64, error) {
|
|
rr := b.rrs[len(b.rrs)-1]
|
|
l, err := rr.r.Seek(0, io.SeekEnd)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return max(l-rr.diff, b.offset+int64(len(b.bytes))), nil
|
|
}
|
|
|
|
// ReadAt reads bytes at the specific offset.
|
|
func (b *Buffer) ReadAt(p []byte, offset int64) (int, error) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if _, err := b.seek(offset, io.SeekStart); err != nil {
|
|
return 0, err
|
|
}
|
|
return b.read(p)
|
|
}
|
|
|
|
// EditedIndices returns the indices of edited regions.
|
|
func (b *Buffer) EditedIndices() []int64 {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
eis := make([]int64, 0, len(b.rrs))
|
|
for _, rr := range b.rrs {
|
|
switch rr.r.(type) {
|
|
case *bytesReader, constReader:
|
|
// constReader can be adjacent to another bytesReader or constReader.
|
|
if l := len(eis); l > 0 && eis[l-1] == rr.min {
|
|
eis[l-1] = rr.max
|
|
continue
|
|
}
|
|
eis = append(eis, rr.min, rr.max)
|
|
}
|
|
}
|
|
if len(b.bytes) > 0 {
|
|
eis = insertInterval(eis, b.offset, b.offset+int64(len(b.bytes)))
|
|
}
|
|
return eis
|
|
}
|
|
|
|
func insertInterval(xs []int64, start, end int64) []int64 {
|
|
i, fi := slices.BinarySearch(xs, start)
|
|
j, fj := slices.BinarySearch(xs, end)
|
|
if i%2 == 0 {
|
|
if i == j && !fi && !fj {
|
|
return slices.Insert(xs, i, start, end)
|
|
}
|
|
xs[i] = start
|
|
i++
|
|
}
|
|
if j%2 == 0 {
|
|
if fj {
|
|
j++
|
|
} else {
|
|
j--
|
|
xs[j] = end
|
|
}
|
|
}
|
|
return slices.Delete(xs, i, j)
|
|
}
|
|
|
|
// Clone the buffer.
|
|
func (b *Buffer) Clone() *Buffer {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
newBuf := new(Buffer)
|
|
newBuf.rrs = make([]readerRange, len(b.rrs))
|
|
for i, rr := range b.rrs {
|
|
newBuf.rrs[i] = readerRange{rr.r, rr.min, rr.max, rr.diff}
|
|
}
|
|
newBuf.index = b.index
|
|
newBuf.mu = new(sync.Mutex)
|
|
newBuf.bytes = slices.Clone(b.bytes)
|
|
newBuf.offset = b.offset
|
|
return newBuf
|
|
}
|
|
|
|
// Copy a part of the buffer.
|
|
func (b *Buffer) Copy(start, end int64) *Buffer {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.flush()
|
|
newBuf := new(Buffer)
|
|
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
|
|
}
|
|
size := min(end-index, rr.max-index)
|
|
rrs = append(rrs, readerRange{rr.r, index - start, index - start + size, rr.diff + start})
|
|
index += size
|
|
}
|
|
newBuf.rrs = append(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()
|
|
b.flush()
|
|
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 {
|
|
if rr.max == math.MaxInt64 {
|
|
max = math.MaxInt64
|
|
} else {
|
|
max = rr.max - rr.min + index
|
|
}
|
|
rrs = append(rrs, readerRange{rr.r, index, max, rr.diff - index + rr.min})
|
|
index = max
|
|
continue
|
|
}
|
|
if start >= rr.min {
|
|
max = start
|
|
rrs = append(rrs, readerRange{rr.r, index, max, rr.diff})
|
|
index = max
|
|
}
|
|
if end < rr.max {
|
|
if rr.max == math.MaxInt64 {
|
|
max = math.MaxInt64
|
|
} else {
|
|
max = rr.max - end + index
|
|
}
|
|
rrs = append(rrs, readerRange{rr.r, 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()
|
|
b.flush()
|
|
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 {
|
|
if rr.max == math.MaxInt64 {
|
|
max = math.MaxInt64
|
|
} else {
|
|
max = rr.max - rr.min + index
|
|
}
|
|
rrs = append(rrs, readerRange{rr.r, index, max, rr.diff - index + rr.min})
|
|
index = max
|
|
continue
|
|
}
|
|
rrs = append(rrs, readerRange{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{rr.r, index, max, rr.diff - index + rr.min})
|
|
index = max
|
|
}
|
|
if rr.max == math.MaxInt64 {
|
|
max = math.MaxInt64
|
|
} else {
|
|
max = rr.max - offset + index
|
|
}
|
|
rrs = append(rrs, readerRange{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()
|
|
defer b.mu.Unlock()
|
|
b.flush()
|
|
for i, rr := range b.rrs {
|
|
if offset > rr.max {
|
|
continue
|
|
}
|
|
var r *bytesReader
|
|
var ok bool
|
|
if rr.max != math.MaxInt64 {
|
|
if r, ok = rr.r.(*bytesReader); ok {
|
|
r = r.clone()
|
|
r.insert(offset+rr.diff, c)
|
|
b.rrs[i], i = readerRange{r, rr.min, rr.max + 1, rr.diff}, i+1
|
|
}
|
|
}
|
|
if !ok {
|
|
b.rrs = append(b.rrs, readerRange{}, readerRange{})
|
|
copy(b.rrs[i+2:], b.rrs[i:])
|
|
b.rrs[i], i = readerRange{rr.r, rr.min, offset, rr.diff}, i+1
|
|
b.rrs[i], i = readerRange{newBytesReader([]byte{c}), offset, offset + 1, -offset}, i+1
|
|
b.rrs[i].min = offset
|
|
}
|
|
for ; i < len(b.rrs); i++ {
|
|
b.rrs[i].min++
|
|
if b.rrs[i].max != math.MaxInt64 {
|
|
b.rrs[i].max++
|
|
}
|
|
b.rrs[i].diff--
|
|
}
|
|
b.cleanup()
|
|
return
|
|
}
|
|
panic("buffer.Buffer.Insert: unreachable")
|
|
}
|
|
|
|
// Replace replaces a byte at the specific position.
|
|
// This method does not overwrite the reader ranges,
|
|
// but just append the byte to the temporary byte slice
|
|
// in order to cancel the replacement with backspace key.
|
|
func (b *Buffer) Replace(offset int64, c byte) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if b.offset+int64(len(b.bytes)) != offset {
|
|
b.flush()
|
|
}
|
|
if len(b.bytes) == 0 {
|
|
b.offset = offset
|
|
}
|
|
b.bytes = append(b.bytes, c)
|
|
}
|
|
|
|
// UndoReplace removes the last byte of the replacing byte slice.
|
|
func (b *Buffer) UndoReplace(offset int64) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if len(b.bytes) > 0 && b.offset+int64(len(b.bytes))-1 == offset {
|
|
b.bytes = b.bytes[:len(b.bytes)-1]
|
|
}
|
|
}
|
|
|
|
// ReplaceIn replaces bytes within a specific range.
|
|
func (b *Buffer) ReplaceIn(start, end int64, c byte) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
rrs := make([]readerRange, 0, len(b.rrs)+1)
|
|
for _, rr := range b.rrs {
|
|
if rr.max <= start || end <= rr.min {
|
|
rrs = append(rrs, rr)
|
|
continue
|
|
}
|
|
if start > rr.min {
|
|
rrs = append(rrs, readerRange{rr.r, rr.min, start, rr.diff})
|
|
}
|
|
if start >= rr.min {
|
|
rrs = append(rrs, readerRange{constReader(c), start, end, -start})
|
|
}
|
|
if end < rr.max {
|
|
rrs = append(rrs, readerRange{rr.r, end, rr.max, rr.diff})
|
|
}
|
|
}
|
|
b.rrs = rrs
|
|
b.cleanup()
|
|
}
|
|
|
|
// Flush temporary bytes.
|
|
func (b *Buffer) Flush() {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.flush()
|
|
}
|
|
|
|
func (b *Buffer) flush() {
|
|
if len(b.bytes) == 0 {
|
|
return
|
|
}
|
|
rrs := make([]readerRange, 0, len(b.rrs)+1)
|
|
end := b.offset + int64(len(b.bytes))
|
|
for _, rr := range b.rrs {
|
|
if b.offset >= rr.max || end <= rr.min {
|
|
rrs = append(rrs, rr)
|
|
continue
|
|
}
|
|
if b.offset >= rr.min {
|
|
if rr.min < b.offset {
|
|
rrs = append(rrs, readerRange{rr.r, rr.min, b.offset, rr.diff})
|
|
}
|
|
rrs = append(rrs, readerRange{newBytesReader(b.bytes), b.offset, end, -b.offset})
|
|
}
|
|
if rr.max == math.MaxInt64 {
|
|
l, _ := rr.r.Seek(0, io.SeekEnd)
|
|
if l-rr.diff <= end {
|
|
rrs = append(rrs, readerRange{newBytesReader(nil), end, math.MaxInt64, -end})
|
|
continue
|
|
}
|
|
}
|
|
if end < rr.max {
|
|
rrs = append(rrs, readerRange{rr.r, end, rr.max, rr.diff})
|
|
}
|
|
}
|
|
b.rrs = rrs
|
|
b.offset = 0
|
|
b.bytes = nil
|
|
b.cleanup()
|
|
}
|
|
|
|
// Delete deletes a byte at the specific position.
|
|
func (b *Buffer) Delete(offset int64) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.flush()
|
|
for i, rr := range b.rrs {
|
|
if offset >= rr.max {
|
|
continue
|
|
}
|
|
if r, ok := rr.r.(*bytesReader); ok {
|
|
r = r.clone()
|
|
r.delete(offset + rr.diff)
|
|
b.rrs[i] = readerRange{r, rr.min, rr.max - 1, rr.diff}
|
|
} else {
|
|
b.rrs = append(b.rrs, readerRange{})
|
|
copy(b.rrs[i+1:], b.rrs[i:])
|
|
b.rrs[i] = readerRange{rr.r, rr.min, offset, rr.diff}
|
|
b.rrs[i+1] = readerRange{rr.r, offset + 1, rr.max, rr.diff}
|
|
}
|
|
for i++; i < len(b.rrs); i++ {
|
|
b.rrs[i].min--
|
|
if b.rrs[i].max != math.MaxInt64 {
|
|
b.rrs[i].max--
|
|
}
|
|
b.rrs[i].diff++
|
|
}
|
|
b.cleanup()
|
|
return
|
|
}
|
|
panic("buffer.Buffer.Delete: unreachable")
|
|
}
|
|
|
|
func (b *Buffer) cleanup() {
|
|
for i := 0; i < len(b.rrs); i++ {
|
|
if rr := b.rrs[i]; rr.min == rr.max {
|
|
b.rrs = slices.Delete(b.rrs, i, i+1)
|
|
}
|
|
}
|
|
for i := len(b.rrs) - 1; i > 0; i-- {
|
|
rr1, rr2 := b.rrs[i-1], b.rrs[i]
|
|
switch r1 := rr1.r.(type) {
|
|
case constReader:
|
|
if r1 == rr2.r {
|
|
b.rrs[i-1].max = rr2.max
|
|
b.rrs = slices.Delete(b.rrs, i, i+1)
|
|
}
|
|
case *bytesReader:
|
|
if r2, ok := rr2.r.(*bytesReader); ok {
|
|
bs := make([]byte, int(rr1.max-rr1.min)+len(r2.bs)-int(rr2.min+rr2.diff))
|
|
copy(bs, r1.bs[rr1.min+rr1.diff:rr1.max+rr1.diff])
|
|
copy(bs[rr1.max-rr1.min:], r2.bs[rr2.min+rr2.diff:])
|
|
b.rrs[i-1] = readerRange{newBytesReader(bs), rr1.min, rr2.max, -rr1.min}
|
|
b.rrs = slices.Delete(b.rrs, i, i+1)
|
|
}
|
|
default:
|
|
if r1 == rr2.r && rr1.diff == rr2.diff && rr1.max == rr2.min {
|
|
b.rrs[i-1].max = rr2.max
|
|
b.rrs = slices.Delete(b.rrs, i, i+1)
|
|
}
|
|
}
|
|
}
|
|
}
|