mirror of
https://github.com/soypat/lneto.git
synced 2026-08-20 22:49:11 +00:00
done for today :)
This commit is contained in:
+23
-9
@@ -28,10 +28,14 @@ type Ring struct {
|
||||
// SizeLimited returns the amount of bytes that can be written up to the
|
||||
// argument offset limitOffset. See [Ring.WriteLimited]
|
||||
func (r *Ring) FreeLimited(limitOffset int) (free int) {
|
||||
if limitOffset > r.End {
|
||||
free = limitOffset - r.End
|
||||
end := r.End
|
||||
if r.End == 0 {
|
||||
end = r.Off
|
||||
}
|
||||
if limitOffset > end {
|
||||
free = limitOffset - end
|
||||
} else {
|
||||
free = len(r.Buf) - r.End + limitOffset
|
||||
free = len(r.Buf) - end + limitOffset
|
||||
}
|
||||
return free
|
||||
}
|
||||
@@ -57,7 +61,8 @@ func (r *Ring) WriteString(s string) (int, error) {
|
||||
return r.Write(unsafe.Slice(unsafe.StringData(s), len(s)))
|
||||
}
|
||||
|
||||
// Write appends data to the ring buffer that can then be read back in order with [Ring.Read] methods. An error is returned if length of data too large for buffer.
|
||||
// Write appends data to the ring buffer that can then be read back in order with [Ring.Read] methods.
|
||||
// An error is returned if length of data too large for buffer. Write is guaranteed to start at buffer index [Ring.Off].
|
||||
func (r *Ring) Write(b []byte) (int, error) {
|
||||
free := r.Free()
|
||||
if len(b) > free {
|
||||
@@ -70,6 +75,10 @@ func (r *Ring) Write(b []byte) (int, error) {
|
||||
n := copy(r.Buf[r.End:r.Off], b)
|
||||
r.End += n
|
||||
return n, nil
|
||||
} else if r.End == 0 {
|
||||
// To ensure Write begins on r.Off.
|
||||
// Specialised for when user controls Off manually instead of by internal calls to [Ring.onReadEnd] or calls to [Ring.Reset].
|
||||
r.End = r.Off
|
||||
}
|
||||
// start off end len(buf)
|
||||
// | sfree | used | efree |
|
||||
@@ -86,14 +95,14 @@ func (r *Ring) Write(b []byte) (int, error) {
|
||||
// ReadDiscard is a performance auxiliary method that performs a dummy read or no-op read
|
||||
// for advancing the read pointer n bytes without actually copying data.
|
||||
// This method panics if amount of bytes is more than buffered (see [Ring.Buffered]).
|
||||
func (r *Ring) ReadDiscard(n int) {
|
||||
if n < 0 {
|
||||
panic("negative discard amount")
|
||||
func (r *Ring) ReadDiscard(n int) error {
|
||||
if n <= 0 {
|
||||
return errors.New("invalid discard amount")
|
||||
}
|
||||
buffered := r.Buffered()
|
||||
switch {
|
||||
case n > buffered:
|
||||
panic("discard exceeds length")
|
||||
return errors.New("discard exceeds length")
|
||||
case n == buffered:
|
||||
r.Reset()
|
||||
case n+r.Off > len(r.Buf):
|
||||
@@ -101,6 +110,7 @@ func (r *Ring) ReadDiscard(n int) {
|
||||
default:
|
||||
r.Off += n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadAt reads data at an offset from start of readable data but does not advance read pointer. [io.EOF] returned when no data available.
|
||||
@@ -171,7 +181,7 @@ func (r *Ring) Buffered() int {
|
||||
|
||||
// Free returns amount of bytes that can be read into ring buffer before reaching maximum capacity given by [ring.Size]. Always less than [ring.Size].
|
||||
func (r *Ring) Free() int {
|
||||
if r.Off == 0 || r.End == 0 {
|
||||
if r.End == 0 || r.Off == 0 {
|
||||
return len(r.Buf) - r.End
|
||||
}
|
||||
if r.Off < r.End {
|
||||
@@ -201,11 +211,15 @@ func (r *Ring) onReadEnd(totalRead int) {
|
||||
newOff := r.addOff(r.Off, totalRead)
|
||||
if newOff == r.End {
|
||||
r.Reset()
|
||||
} else if newOff == len(r.Buf) {
|
||||
r.Off = 0 // Optimization case.
|
||||
} else {
|
||||
r.Off = newOff
|
||||
}
|
||||
}
|
||||
|
||||
// addOff sums a and b to return an index within 1..[Ring.Size] supposing a and b are each less-equal than [Ring.Size].
|
||||
// Result will never be 0 unless both a and b are 0.
|
||||
func (r *Ring) addOff(a, b int) int {
|
||||
result := a + b
|
||||
if result > len(r.Buf) {
|
||||
|
||||
+40
-21
@@ -175,6 +175,7 @@ func TestRingEmpty(t *testing.T) {
|
||||
if buf2 != 0 {
|
||||
t.Fatalf("want 0 bytes buffered(second call), got buf=%d->%d for off=%d->%d, end=0->%d size=%d", buf, buf2, off, r.Off, r.End, r.Size())
|
||||
}
|
||||
testRingSanity(t, r)
|
||||
for _, read := range readCalls {
|
||||
n, err := read(data)
|
||||
if err != io.EOF {
|
||||
@@ -182,8 +183,8 @@ func TestRingEmpty(t *testing.T) {
|
||||
} else if n != 0 {
|
||||
t.Fatalf("expected no bytes read, got %d", n)
|
||||
}
|
||||
testRingSanity(t, r)
|
||||
}
|
||||
testRingSanity(t, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -243,6 +244,42 @@ func TestRingNonEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRing_OffWrite(t *testing.T) {
|
||||
const bufSize = 8
|
||||
var rawbuf, auxbuf, readback [bufSize]byte
|
||||
r := &Ring{Buf: rawbuf[:]}
|
||||
for n := 1; n < bufSize+1; n++ {
|
||||
for off := 0; off < bufSize+1; off++ {
|
||||
r.Off = off // Start write at off.
|
||||
r.End = 0 // Reset to use no data.
|
||||
for i := 0; i < n; i++ {
|
||||
rawbuf[(off+i)%len(rawbuf)] = 0
|
||||
auxbuf[i] = byte(i) + 1
|
||||
}
|
||||
ngot, err := r.Write(auxbuf[:n])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if ngot != n {
|
||||
t.Fatal(n, ngot)
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
offz := (off + i) % len(rawbuf)
|
||||
if rawbuf[offz] != auxbuf[i] {
|
||||
t.Fatalf("mismatch pos=%d off=%d %q!=%q", i, offz, rawbuf[offz], auxbuf[i])
|
||||
}
|
||||
}
|
||||
ngot, err = r.Read(readback[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if ngot != n {
|
||||
t.Fatal(n, ngot)
|
||||
} else if !bytes.Equal(readback[:n], auxbuf[:n]) {
|
||||
t.Fatalf("want readback %q, got %q", auxbuf[:n], readback[:n])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRing_TwoWrite(t *testing.T) {
|
||||
const bufSize = 8
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
@@ -441,24 +478,6 @@ func testRing1_loopback(t *testing.T, rng *rand.Rand, ringbuf, data, auxbuf []by
|
||||
return !t.Failed()
|
||||
}
|
||||
|
||||
func fragmentReadInto(r io.Reader, buf []byte) (n int, _ error) {
|
||||
maxSize := len(buf) / 4
|
||||
for {
|
||||
ntop := min(n+rand.Intn(maxSize)+1, len(buf))
|
||||
ngot, err := r.Read(buf[n:ntop])
|
||||
n += ngot
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return n, nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if n == len(buf) {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setRingData(t *testing.T, r *Ring, offset int, data []byte) {
|
||||
t.Helper()
|
||||
sz := r.Size()
|
||||
@@ -520,7 +539,7 @@ func testRingSanity(t *testing.T, r *Ring) {
|
||||
|
||||
func canonRing(r *Ring) {
|
||||
if r.Buffered() == 0 {
|
||||
// r.End = r.addOff(r.Off, 1)
|
||||
// r.onReadEnd(1)
|
||||
r.End = r.addOff(r.Off, 1)
|
||||
r.onReadEnd(1)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user