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