From 0ed8f00cc46b38005f7b0f7457d50e00aa29880d Mon Sep 17 00:00:00 2001 From: Krishnakant C Date: Wed, 11 Sep 2024 10:51:15 +0530 Subject: [PATCH] #3 added support for byte buffer input streams to read data from a buffer --- pkg/utils/input_stream.go | 125 +++++++++++++++++++++++++++++++++ pkg/utils/input_stream_test.go | 122 ++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 pkg/utils/input_stream.go create mode 100644 pkg/utils/input_stream_test.go diff --git a/pkg/utils/input_stream.go b/pkg/utils/input_stream.go new file mode 100644 index 0000000..bfb0bff --- /dev/null +++ b/pkg/utils/input_stream.go @@ -0,0 +1,125 @@ +// Copyright 2024 Atomstate Technologies Private Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "io" +) + +// ByteBufferInputStream represents a reader backed by a byte slice. +// It allows reading data from a buffer in a manner similar to an InputStream in Java. +// +// Example usage: +// +// buffer := []byte("Hello, World!") +// reader := NewByteBufferInputStream(buffer) +// +// // Reading data +// p := make([]byte, 5) +// n, err := reader.Read(p) +// if err != nil && err != io.EOF { +// log.Fatal(err) +// } +// fmt.Println(string(p[:n])) // Output: Hello +// +// // Checking available bytes +// available := reader.Available() +// fmt.Println(available) // Output: 8 +// +// // Reading remaining data +// p = make([]byte, 8) +// n, err = reader.Read(p) +// if err != nil && err != io.EOF { +// log.Fatal(err) +// } +// fmt.Println(string(p[:n])) // Output: , World! +// fmt.Println(reader.Available()) // Output: 0 +type ByteBufferInputStream struct { + buffer []byte + pos int +} + +// NewByteBufferInputStream creates a new ByteBufferInputStream with the given buffer. +// +// Parameters: +// - buffer: A byte slice to back the input stream. +// +// Returns: +// - A pointer to a new ByteBufferInputStream instance. +// +// Example: +// +// buffer := []byte("Example") +// reader := NewByteBufferInputStream(buffer) +func NewByteBufferInputStream(buffer []byte) *ByteBufferInputStream { + return &ByteBufferInputStream{ + buffer: buffer, + pos: 0, + } +} + +// Read reads up to len(p) bytes into p from the buffer. It returns the number of bytes read and an error, if any. +// The function will read as many bytes as possible but will not exceed the length of the provided slice. +// +// Parameters: +// - p: A byte slice into which data will be read. +// +// Returns: +// - The number of bytes read. +// - An error, if any (e.g., io.EOF if the end of the buffer is reached). +// +// Example: +// +// p := make([]byte, 10) +// n, err := reader.Read(p) +// if err != nil && err != io.EOF { +// log.Fatal(err) +// } +// fmt.Println(string(p[:n])) // Output depends on buffer contents +func (b *ByteBufferInputStream) Read(p []byte) (int, error) { + if b.pos >= len(b.buffer) { + return 0, io.EOF + } + + bytesToRead := len(p) + remainingBytes := len(b.buffer) - b.pos + if bytesToRead > remainingBytes { + bytesToRead = remainingBytes + } + + // Copy data from buffer to p + copy(p, b.buffer[b.pos:b.pos+bytesToRead]) + b.pos += bytesToRead + + // Return the number of bytes read + return bytesToRead, nil +} + +// Available returns the number of bytes available to read from the buffer. +// It gives the remaining number of bytes that can be read before reaching the end of the buffer. +// +// Returns: +// - The number of bytes available to read. +// +// Example: +// +// available := reader.Available() +// fmt.Println(available) // Output: number of bytes remaining in the buffer +func (b *ByteBufferInputStream) Available() int { + if b.pos >= len(b.buffer) { + return 0 + } + return len(b.buffer) - b.pos +} diff --git a/pkg/utils/input_stream_test.go b/pkg/utils/input_stream_test.go new file mode 100644 index 0000000..de30995 --- /dev/null +++ b/pkg/utils/input_stream_test.go @@ -0,0 +1,122 @@ +// Copyright 2024 Atomstate Technologies Private Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "io" + "testing" +) + +func TestRead(t *testing.T) { + buffer := []byte("Hello, World!") + reader := NewByteBufferInputStream(buffer) + + // Test reading fewer bytes than available + p := make([]byte, 5) + n, err := reader.Read(p) + if err != nil { + t.Fatalf("Read() error = %v", err) + } + if got := string(p[:n]); got != "Hello" { + t.Errorf("Read() got = %v, want %v", got, "Hello") + } + + // Test reading exactly the remaining bytes + p = make([]byte, 8) + n, err = reader.Read(p) + if err != nil { + t.Fatalf("Read() error = %v", err) + } + if got := string(p[:n]); got != ", World!" { + t.Errorf("Read() got = %v, want %v", got, ", World!") + } + + // Test reading beyond the buffer + p = make([]byte, 10) + n, err = reader.Read(p) + if err != io.EOF { + t.Errorf("Read() error = %v, want %v", err, io.EOF) + } + if got := string(p[:n]); got != "" { + t.Errorf("Read() got = %v, want %v", got, "") + } +} + +func TestAvailable(t *testing.T) { + buffer := []byte("Hello, World!") + reader := NewByteBufferInputStream(buffer) + + if got := reader.Available(); got != 13 { + t.Errorf("Available() = %v, want %v", got, 13) + } + + // Read 5 bytes + p := make([]byte, 5) + _, err := reader.Read(p) + if err != nil { + t.Fatalf("Read() error = %v", err) + } + + if got := reader.Available(); got != 8 { + t.Errorf("Available() = %v, want %v", got, 8) + } + + // Read remaining bytes + p = make([]byte, 8) + _, err = reader.Read(p) + if err != nil && err != io.EOF { + t.Errorf("Read() error = %v, want %v", err, io.EOF) + } + + if got := reader.Available(); got != 0 { + t.Errorf("Available() = %v, want %v", got, 0) + } +} + +func TestReadEmptyBuffer(t *testing.T) { + buffer := []byte("") + reader := NewByteBufferInputStream(buffer) + + p := make([]byte, 5) + n, err := reader.Read(p) + if err != io.EOF { + t.Errorf("Read() error = %v, want %v", err, io.EOF) + } + if n != 0 { + t.Errorf("Read() n = %v, want %v", n, 0) + } + + if got := reader.Available(); got != 0 { + t.Errorf("Available() = %v, want %v", got, 0) + } +} + +func TestReadWithBufferLargerThanAvailable(t *testing.T) { + buffer := []byte("Short") + reader := NewByteBufferInputStream(buffer) + + p := make([]byte, 10) + n, err := reader.Read(p) + if err != nil { + t.Fatalf("Read() error = %v", err) + } + if got := string(p[:n]); got != "Short" { + t.Errorf("Read() got = %v, want %v", got, "Short") + } + + if got := reader.Available(); got != 0 { + t.Errorf("Available() = %v, want %v", got, 0) + } +}