waveterm/pkg/util/logview/multibuf.go
2024-11-15 14:02:33 -08:00

132 lines
2.7 KiB
Go

// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package logview
import (
"errors"
"io"
"os"
)
type MultiBufferByteGetter struct {
File *os.File
Offset int64
EOF bool
Buffers [][]byte
BufSize int64
}
var ErrBOF = errors.New("beginning of file")
func MakeMultiBufferByteGetter(file *os.File, bufSize int64) *MultiBufferByteGetter {
return &MultiBufferByteGetter{
File: file,
Offset: 0,
EOF: false,
Buffers: [][]byte{},
BufSize: bufSize,
}
}
func (mb *MultiBufferByteGetter) readFromBuffer(offset int64) (byte, bool) {
if offset < mb.Offset || offset >= mb.Offset+int64(mb.bufSize()) {
return 0, false
}
bufIdx := int((offset - mb.Offset) / mb.BufSize)
bufOffset := (offset - mb.Offset) % mb.BufSize
return mb.Buffers[bufIdx][bufOffset], true
}
func (mb *MultiBufferByteGetter) bufSize() int {
return len(mb.Buffers) * int(mb.BufSize)
}
func (mb *MultiBufferByteGetter) rebuffer(newOffset int64) error {
partNum := int(newOffset / mb.BufSize)
partOffset := int64(partNum) * mb.BufSize
newBuf := make([]byte, mb.BufSize)
n, err := mb.File.ReadAt(newBuf, partOffset)
var isEOF bool
if err == io.EOF {
newBuf = newBuf[:n]
isEOF = true
}
if err != nil {
return err
}
var newBuffers [][]byte
if len(mb.Buffers) > 0 {
firstBufPartNum := int(mb.Offset / mb.BufSize)
lastBufPartNum := int((mb.Offset + int64(mb.bufSize())) / mb.BufSize)
if firstBufPartNum == partNum+1 {
newBuffers = [][]byte{newBuf, mb.Buffers[0]}
} else if lastBufPartNum == partNum-1 {
newBuffers = [][]byte{mb.Buffers[0], newBuf}
} else {
newBuffers = [][]byte{newBuf}
}
} else {
newBuffers = [][]byte{newBuf}
}
mb.Buffers = newBuffers
mb.Offset = partOffset
mb.EOF = isEOF
return nil
}
func (mb *MultiBufferByteGetter) GetByte(offset int64) (byte, error) {
b, ok := mb.readFromBuffer(offset)
if ok {
return b, nil
}
if mb.EOF && offset >= mb.Offset+int64(mb.bufSize()) {
return 0, io.EOF
}
err := mb.rebuffer(offset)
if err != nil {
return 0, err
}
b, _ = mb.readFromBuffer(offset)
return b, nil
}
func (mb *MultiBufferByteGetter) NextLine(offset int64) (int64, error) {
for {
b, err := mb.GetByte(offset)
if err != nil {
return 0, err
}
if b == '\n' {
break
}
offset++
}
_, lastErr := mb.GetByte(offset + 1)
if lastErr == io.EOF {
return 0, io.EOF
}
return offset + 1, nil
}
func (mb *MultiBufferByteGetter) PrevLine(offset int64) (int64, error) {
if offset == 0 {
return 0, ErrBOF
}
offset = offset - 2
for {
if offset < 0 {
break
}
b, err := mb.GetByte(offset)
if err != nil {
return 0, err
}
if b == '\n' {
break
}
offset--
}
return offset + 1, nil
}