mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Implement ring-buffer for capturing early printf output
The ring-buffer implements both io.Reader and io.Writer and uses a fixed size of 2048 bytes (set by the ringBufferSize constant). This provides enough space to hold a standard 80x25 screen's output.
This commit is contained in:
parent
0f3af2e78d
commit
f691d75b29
65
src/gopheros/kernel/kfmt/ringbuf.go
Normal file
65
src/gopheros/kernel/kfmt/ringbuf.go
Normal file
@ -0,0 +1,65 @@
|
||||
package kfmt
|
||||
|
||||
import "io"
|
||||
|
||||
// ringBufferSize defines size of the ring buffer that buffers early Printf
|
||||
// output. Its default size is selected so it can buffer the contents of a
|
||||
// standard 80*25 text-mode console. The ring buffer size must always be a
|
||||
// power of 2.
|
||||
const ringBufferSize = 2048
|
||||
|
||||
// ringBuffer models a ring buffer of size ringBufferSize. This buffer is used
|
||||
// for capturing the output of Printf before the tty and console systems are
|
||||
// initialized.
|
||||
type ringBuffer struct {
|
||||
buffer [ringBufferSize]byte
|
||||
rIndex, wIndex int
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes from p to the ringBuffer.
|
||||
func (rb *ringBuffer) Write(p []byte) (int, error) {
|
||||
for _, b := range p {
|
||||
rb.buffer[rb.wIndex] = b
|
||||
rb.wIndex = (rb.wIndex + 1) & (ringBufferSize - 1)
|
||||
if rb.rIndex == rb.wIndex {
|
||||
rb.rIndex = (rb.rIndex + 1) & (ringBufferSize - 1)
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Read reads up to len(p) bytes into p. It returns the number of bytes read (0
|
||||
// <= n <= len(p)) and any error encountered.
|
||||
func (rb *ringBuffer) Read(p []byte) (n int, err error) {
|
||||
switch {
|
||||
case rb.rIndex < rb.wIndex:
|
||||
// read up to min(wIndex - rIndex, len(p)) bytes
|
||||
n = rb.wIndex - rb.rIndex
|
||||
if pLen := len(p); pLen < n {
|
||||
n = pLen
|
||||
}
|
||||
|
||||
copy(p, rb.buffer[rb.rIndex:rb.rIndex+n])
|
||||
rb.rIndex += n
|
||||
|
||||
return n, nil
|
||||
case rb.rIndex > rb.wIndex:
|
||||
// Read up to min(len(buf) - rIndex, len(p)) bytes
|
||||
n = len(rb.buffer) - rb.rIndex
|
||||
if pLen := len(p); pLen < n {
|
||||
n = pLen
|
||||
}
|
||||
|
||||
copy(p, rb.buffer[rb.rIndex:rb.rIndex+n])
|
||||
rb.rIndex += n
|
||||
|
||||
if rb.rIndex == len(rb.buffer) {
|
||||
rb.rIndex = 0
|
||||
}
|
||||
|
||||
return n, nil
|
||||
default: // rIndex == wIndex
|
||||
return 0, io.EOF
|
||||
}
|
||||
}
|
96
src/gopheros/kernel/kfmt/ringbuf_test.go
Normal file
96
src/gopheros/kernel/kfmt/ringbuf_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
package kfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRingBuffer(t *testing.T) {
|
||||
var (
|
||||
buf bytes.Buffer
|
||||
expStr = "the big brown fox jumped over the lazy dog"
|
||||
rb ringBuffer
|
||||
)
|
||||
|
||||
t.Run("read/write", func(t *testing.T) {
|
||||
rb.wIndex = 0
|
||||
rb.rIndex = 0
|
||||
n, err := rb.Write([]byte(expStr))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n != len(expStr) {
|
||||
t.Fatalf("expected to write %d bytes; wrote %d", len(expStr), n)
|
||||
}
|
||||
|
||||
if got := readByteByByte(&buf, &rb); got != expStr {
|
||||
t.Fatalf("expected to read %q; got %q", expStr, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("write moves read pointer", func(t *testing.T) {
|
||||
rb.wIndex = ringBufferSize - 1
|
||||
rb.rIndex = 0
|
||||
_, err := rb.Write([]byte{'!'})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if exp := 1; rb.rIndex != exp {
|
||||
t.Fatalf("expected write to push rIndex to %d; got %d", exp, rb.rIndex)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("wIndex < rIndex", func(t *testing.T) {
|
||||
rb.wIndex = ringBufferSize - 2
|
||||
rb.rIndex = ringBufferSize - 2
|
||||
n, err := rb.Write([]byte(expStr))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n != len(expStr) {
|
||||
t.Fatalf("expected to write %d bytes; wrote %d", len(expStr), n)
|
||||
}
|
||||
|
||||
if got := readByteByByte(&buf, &rb); got != expStr {
|
||||
t.Fatalf("expected to read %q; got %q", expStr, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with io.WriteTo", func(t *testing.T) {
|
||||
rb.wIndex = ringBufferSize - 2
|
||||
rb.rIndex = ringBufferSize - 2
|
||||
n, err := rb.Write([]byte(expStr))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n != len(expStr) {
|
||||
t.Fatalf("expected to write %d bytes; wrote %d", len(expStr), n)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, &rb)
|
||||
|
||||
if got := buf.String(); got != expStr {
|
||||
t.Fatalf("expected to read %q; got %q", expStr, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func readByteByByte(buf *bytes.Buffer, r io.Reader) string {
|
||||
buf.Reset()
|
||||
var b = make([]byte, 1)
|
||||
for {
|
||||
_, err := r.Read(b)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
buf.Write(b)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user