Skip to content

Commit

Permalink
#3 added support for byte buffer input streams to read data from a bu…
Browse files Browse the repository at this point in the history
…ffer
  • Loading branch information
Krishnakant C authored and Krishnakant C committed Sep 11, 2024
1 parent c5e3fca commit 0ed8f00
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 0 deletions.
125 changes: 125 additions & 0 deletions pkg/utils/input_stream.go
Original file line number Diff line number Diff line change
@@ -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
}
122 changes: 122 additions & 0 deletions pkg/utils/input_stream_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit 0ed8f00

Please sign in to comment.