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