From a344d1fa82e1f76c71a71bfaee7c81cbbe1e1d02 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Fri, 15 Sep 2023 21:09:47 +0300 Subject: revamped bitreader; with new functionality and bug fixes --- README.md | 76 +-- bitreader.go | 419 +++++++++----- bitreader_test.go | 1575 ++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 1689 insertions(+), 381 deletions(-) diff --git a/README.md b/README.md index f88c216..6264cd4 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,72 @@ -# BitReader [![Go Report Card](https://goreportcard.com/badge/github.com/pektezol/bitreader)](https://goreportcard.com/report/github.com/pektezol/bitreader) [![License: LGPL 2.1](https://img.shields.io/badge/License-LGPL_v2.1-blue.svg)](https://github.com/pektezol/bitreader/blob/main/LICENSE) [![Go Reference](https://pkg.go.dev/badge/github.com/pektezol/bitreader.svg)](https://pkg.go.dev/github.com/pektezol/bitreader) -A simple bit reader with big/little-endian support for golang.\ -Reads stream data from an io.Reader; can read from os.File and a byte array with bytes.NewReader(array).\ -Uses bitwise operations.\ -Support reading up to 64 bits at one time.\ -Includes wrapper functions for most used data types.\ -Error checking on all but wrapper functions. +# BitReader [![Go Reference](https://pkg.go.dev/badge/github.com/pektezol/bitreader.svg)](https://pkg.go.dev/github.com/pektezol/bitreader) [![Go Report Card](https://goreportcard.com/badge/github.com/pektezol/bitreader)](https://goreportcard.com/report/github.com/pektezol/bitreader) [![License: LGPL 2.1](https://img.shields.io/badge/License-LGPL_v2.1-blue.svg)](https://github.com/pektezol/bitreader/blob/main/LICENSE) +A simple bit reader with big/little-endian support for golang. ## Installation ```bash $ go get github.com/pektezol/bitreader ``` -## Usage +## Usage Examples ```go import "github.com/pektezol/bitreader" -// data: io.Reader Data to read from an io stream -// le: bool Little-endian(true) or big-endian(false) state -reader := bitreader.Reader(data, le) +// ioStream: io.Reader Data to read from an io stream +// byteStream: []byte Data to read from a byte slice +// littleEndian: bool Little-endian(true) or big-endian(false) state +reader := bitreader.NewReader(ioStream, le) +reader := bitreader.NewReaderFromBytes(byteStream, le) + +// Fork Reader, Copies Current Reader +newReader, err := reader.Fork() + +// Read Total Number of Bits Left +bits, err := reader.ReadRemainingBits() // Read First Bit state, err := reader.ReadBool() -// Skip Bits/Bytes -err := reader.SkipBits(8) -err := reader.SkipBytes(4) - // Read Bits/Bytes -value, err := reader.ReadBytes(4) // up to 8 bytes value, err := reader.ReadBits(64) // up to 64 bits +value, err := reader.ReadBytes(8) // up to 8 bytes // Read String -text, err := reader.ReadString() // null-terminated -text, err := reader.ReadStringLen(256) // length-specified +text, err := reader.ReadString() // null-terminated +text, err := reader.ReadStringLength(256) // length-specified // Read Bits/Bytes into Slice arr, err := reader.ReadBitsToSlice(128) arr, err := reader.ReadBytesToSlice(64) +// Skip Bits/Bytes +err := reader.SkipBits(8) +err := reader.SkipBytes(4) + // Wrapper functions -text := reader.TryReadString() // string -text := reader.TryReadStringLen(64) // string -arr := reader.ReadBitsToSlice(128) // []byte -arr := reader.ReadBytesToSlice(64) // []byte -state := reader.TryReadBool() // bool -value := reader.TryReadInt1() // uint8 -value := reader.TryReadInt8() // uint8 -value := reader.TryReadInt16() // uint16 -value := reader.TryReadInt32() // uint32 -value := reader.TryReadInt64() // uint64 -value := reader.TryReadFloat32() // float32 -value := reader.TryReadFloat64() // float64 -value := reader.TryReadBits(64) // uint64 -value := reader.TryReadBytes(8) // uint64 +state := reader.TryReadBool() // bool +value := reader.TryReadInt1() // uint8 +value := reader.TryReadUInt8() // uint8 +value := reader.TryReadSInt8() // int8 +value := reader.TryReadUInt16() // uint16 +value := reader.TryReadSInt16() // int16 +value := reader.TryReadUInt32() // uint32 +value := reader.TryReadSInt32() // int32 +value := reader.TryReadUInt64() // uint64 +value := reader.TryReadSInt64() // int64 +value := reader.TryReadFloat32() // float32 +value := reader.TryReadFloat64() // float64 +value := reader.TryReadBits(64) // uint64 +value := reader.TryReadBytes(8) // uint64 +text := reader.TryReadString() // string +text := reader.TryReadStringLength(64) // string +arr := reader.TryReadBitsToSlice(1024) // []byte +arr := reader.TryReadBytesToSlice(128) // []byte +bits := reader.TryReadRemainingBits() // uint64 ``` ## Error Handling -ReadBits(x), ReadBytes(x), ReadBool(), ReadString(), ReadStringLen(x), ReadBitsToSlice(x), ReadBytesToSlice(x), SkipBits(x) and SkipBytes(x) functions returns an error message when they don't work as expected. It is advised to always handle errors. \ -Wrapper functions, however, only returns the value and panics if an error is encountered. +All ReadXXX(), SkipXXX() and Fork() functions returns an error message when they don't work as expected. It is advised to always handle errors. \ +Wrapper functions, however, only returns the value and panics if an error is encountered, for the sake of ease of use. ## Bug Report / Feature Request Using [Github Issues](https://github.com/pektezol/BitReader/issues/new/choose), you can report a bug that you encountered and/or request a feature that you would like to be added. diff --git a/bitreader.go b/bitreader.go index 89c3526..d8f9f89 100644 --- a/bitreader.go +++ b/bitreader.go @@ -1,46 +1,72 @@ // BitReader is a simple bit reader with big/little-endian support for golang. -// It can read stream data from an io.Reader; can read from os.File and a byte array with bytes.NewReader(array). -// Uses bitwise operations for v2. -// Supports reading up to 64 bits at one time. -// Includes wrapper functions for most used data types. -// Error checking on all but wrapper functions. -// Thanks to github.com/mlugg for the big help! package bitreader import ( + "bytes" + "errors" "fmt" "io" "math" ) -// ReaderType is the main structure of our Reader. -// Whenever index == 0, we need to read a new byte from stream into curByte +// Reader is the main structure of our Reader. +// Whenever index == 0, we need to read a new byte from stream into currentByte // -// stream io.Reader The underlying stream we're reading bytes from -// index uint18 The current index into the byte [0-7] -// curByte byte The byte we're currently reading from -// le bool Whether to read in little-endian order -type ReaderType struct { - stream io.Reader - index uint8 - curByte byte - le bool -} - -// Reader is the main constructor that creates the ReaderType object -// with stream data and little-endian state. -func Reader(stream io.Reader, le bool) *ReaderType { - return &ReaderType{ - stream: stream, - index: 0, - curByte: 0, // Initial value doesn't matter, it'll be read as soon as we try to read any bits - le: le, - } -} - -// TryReadBool is a wrapper function that gets the state of 1-bit, -// returns true if 1, false if 0. Panics on error. -func (reader *ReaderType) TryReadBool() bool { +// stream io.Reader The underlying stream we're reading bytes from +// index uint8 The current index into the byte [0-7] +// currentByte byte The byte we're currently reading from +// le bool Whether to read in little-endian order or not +type Reader struct { + stream io.Reader + index uint8 + currentByte byte + littleEndian bool +} + +// NewReader is the main constructor that creates the Reader object +// with stream reader data and little-endian state. +func NewReader(stream io.Reader, littleEndian bool) *Reader { + return &Reader{ + stream: stream, + index: 0, + currentByte: 0, + littleEndian: littleEndian, + } +} + +// NewReaderFromBytes is the main constructor that creates the Reader object +// with stream byte data and little-endian state. +func NewReaderFromBytes(stream []byte, littleEndian bool) *Reader { + return &Reader{ + stream: bytes.NewReader(stream), + index: 0, + currentByte: 0, + littleEndian: littleEndian, + } +} + +// Fork is a function that copies the original reader into a new reader +// with all of its current values. +func (reader *Reader) Fork() (*Reader, error) { + originalIndex := reader.index + originalCurrentByte := reader.currentByte + byteStream, err := io.ReadAll(reader.stream) + if err != nil { + return nil, err // Will only happen when there's no memory, lol + } + reader.stream = bytes.NewReader(byteStream) + return &Reader{ + stream: bytes.NewReader(byteStream), + index: uint8(originalIndex), + currentByte: originalCurrentByte, + littleEndian: reader.littleEndian, + }, nil +} + +// TryReadBool is a wrapper function that gets the state of 1-bit. +// +// Returns true if 1, false if 0. Panics on overflow. +func (reader *Reader) TryReadBool() bool { flag, err := reader.ReadBool() if err != nil { panic(err) @@ -49,8 +75,9 @@ func (reader *ReaderType) TryReadBool() bool { } // TryReadInt1 is a wrapper function that returns the value of 1-bit. -// Returns type uint8. Panics on error. -func (reader *ReaderType) TryReadInt1() uint8 { +// +// Returns type uint8. Panics on overflow. +func (reader *Reader) TryReadInt1() uint8 { value, err := reader.ReadBits(1) if err != nil { panic(err) @@ -58,9 +85,10 @@ func (reader *ReaderType) TryReadInt1() uint8 { return uint8(value) } -// TryReadInt8 is a wrapper function that returns the value of 8-bits. -// Returns uint8. Panics on error. -func (reader *ReaderType) TryReadInt8() uint8 { +// TryReadUInt8 is a wrapper function that returns the value of 8-bits. +// +// Returns uint8. Panics on overflow. +func (reader *Reader) TryReadUInt8() uint8 { value, err := reader.ReadBits(8) if err != nil { panic(err) @@ -68,9 +96,21 @@ func (reader *ReaderType) TryReadInt8() uint8 { return uint8(value) } -// TryReadInt16 is a wrapper function that returns the value of 16-bits. -// Returns uint16. Panics on error. -func (reader *ReaderType) TryReadInt16() uint16 { +// TryReadSInt8 is a wrapper function that returns the value of 8-bits. +// +// Returns int8. Panics on overflow. +func (reader *Reader) TryReadSInt8() int8 { + value, err := reader.ReadBits(8) + if err != nil { + panic(err) + } + return int8(value) +} + +// TryReadUInt16 is a wrapper function that returns the value of 16-bits. +// +// Returns uint16. Panics on overflow. +func (reader *Reader) TryReadUInt16() uint16 { value, err := reader.ReadBits(16) if err != nil { panic(err) @@ -78,9 +118,21 @@ func (reader *ReaderType) TryReadInt16() uint16 { return uint16(value) } -// TryReadInt32 is a wrapper function that returns the value of 32-bits. -// Returns uint32. Panics on error. -func (reader *ReaderType) TryReadInt32() uint32 { +// TryReadSInt16 is a wrapper function that returns the value of 16-bits. +// +// Returns uint16. Panics on overflow. +func (reader *Reader) TryReadSInt16() int16 { + value, err := reader.ReadBits(16) + if err != nil { + panic(err) + } + return int16(value) +} + +// TryReadUInt32 is a wrapper function that returns the value of 32-bits. +// +// Returns uint32. Panics on overflow. +func (reader *Reader) TryReadUInt32() uint32 { value, err := reader.ReadBits(32) if err != nil { panic(err) @@ -88,9 +140,21 @@ func (reader *ReaderType) TryReadInt32() uint32 { return uint32(value) } -// TryReadInt64 is a wrapper function that returns the value of 64-bits. -// Returns uint64. Panics on error. -func (reader *ReaderType) TryReadInt64() uint64 { +// TryReadSInt32 is a wrapper function that returns the value of 32-bits. +// +// Returns int32. Panics on overflow. +func (reader *Reader) TryReadSInt32() int32 { + value, err := reader.ReadBits(32) + if err != nil { + panic(err) + } + return int32(value) +} + +// TryReadUInt64 is a wrapper function that returns the value of 64-bits. +// +// Returns uint64. Panics on overflow. +func (reader *Reader) TryReadUInt64() uint64 { value, err := reader.ReadBits(64) if err != nil { panic(err) @@ -98,9 +162,21 @@ func (reader *ReaderType) TryReadInt64() uint64 { return value } +// TryReadSInt64 is a wrapper function that returns the value of 64-bits. +// +// Returns int64. Panics on overflow. +func (reader *Reader) TryReadSInt64() int64 { + value, err := reader.ReadBits(64) + if err != nil { + panic(err) + } + return int64(value) +} + // TryReadFloat32 is a wrapper function that returns the value of 32-bits. -// Returns float32. Panics on error. -func (reader *ReaderType) TryReadFloat32() float32 { +// +// Returns float32. Panics on overflow. +func (reader *Reader) TryReadFloat32() float32 { value, err := reader.ReadBits(32) if err != nil { panic(err) @@ -109,8 +185,9 @@ func (reader *ReaderType) TryReadFloat32() float32 { } // TryReadFloat64 is a wrapper function that returns the value of 64-bits. -// Returns float64. Panics on error. -func (reader *ReaderType) TryReadFloat64() float64 { +// +// Returns float64. Panics on overflow. +func (reader *Reader) TryReadFloat64() float64 { value, err := reader.ReadBits(64) if err != nil { panic(err) @@ -119,8 +196,9 @@ func (reader *ReaderType) TryReadFloat64() float64 { } // TryReadBits is a wrapper function that returns the value of bits specified in the parameter. -// Returns uint64. Panics on error. -func (reader *ReaderType) TryReadBits(bits int) uint64 { +// +// Returns uint64. Panics on overflow. +func (reader *Reader) TryReadBits(bits int) uint64 { value, err := reader.ReadBits(bits) if err != nil { panic(err) @@ -129,8 +207,9 @@ func (reader *ReaderType) TryReadBits(bits int) uint64 { } // TryReadBytes is a wrapper function that returns the value of bits specified in the parameter. -// Returns uint64. Panics on error. -func (reader *ReaderType) TryReadBytes(bytes int) uint64 { +// +// Returns uint64. Panics on overflow. +func (reader *Reader) TryReadBytes(bytes int) uint64 { value, err := reader.ReadBytes(bytes) if err != nil { panic(err) @@ -140,21 +219,33 @@ func (reader *ReaderType) TryReadBytes(bytes int) uint64 { // TryReadString is a wrapper function that returns the string // that is read until it is null-terminated. -func (reader *ReaderType) TryReadString() string { - text, _ := reader.ReadString() +// +// Returns string. Panics on overflow. +func (reader *Reader) TryReadString() string { + text, err := reader.ReadString() + if err != nil { + panic(err) + } return text } -// TryReadStringLen is a wrapper function that returns the string +// TryReadStringLength is a wrapper function that returns the string // that is read until the given length is reached or it is null-terminated. -func (reader *ReaderType) TryReadStringLen(length int) string { - text, _ := reader.ReadStringLen(length) +// +// Returns string. Panics on overflow. +func (reader *Reader) TryReadStringLength(length int) string { + text, err := reader.ReadStringLength(length) + if err != nil { + panic(err) + } return text } // TryReadBytesToSlice is a wrapper function that reads the specified amount of bits // from the parameter and puts each bit into a slice and returns this slice. -func (reader *ReaderType) TryReadBitsToSlice(bits int) []byte { +// +// Returns []byte. Panics on overflow. +func (reader *Reader) TryReadBitsToSlice(bits int) []byte { bytes := (bits / 8) if bits%8 != 0 { bytes++ @@ -189,7 +280,9 @@ func (reader *ReaderType) TryReadBitsToSlice(bits int) []byte { // TryReadBytesToSlice is a wrapper function that reads the specified amount of bytes // from the parameter and puts each byte into a slice and returns this slice. -func (reader *ReaderType) TryReadBytesToSlice(bytes int) []byte { +// +// Returns []byte. Panics on overflow. +func (reader *Reader) TryReadBytesToSlice(bytes int) []byte { var out []byte for i := 0; i < bytes; i++ { val, err := reader.ReadBytes(1) @@ -201,42 +294,70 @@ func (reader *ReaderType) TryReadBytesToSlice(bytes int) []byte { return out } -// SkipBits is a function that increases Reader index -// based on given input bits number. +// TryReadBytesToSlice is a wrapper function that reads the remaining bits +// left in the stream and returns the count of bits. +// +// Returns uint64. Panics on overflow. +func (reader *Reader) TryReadRemainingBits() uint64 { + bits, err := reader.ReadRemainingBits() + if err != nil { + panic(err) + } + return bits +} + +// ReadBool is a function that reads one bit and returns the state, error +// based on the output. Returns the read value in a bool format. // // Returns an error if there are no remaining bits. -func (reader *ReaderType) SkipBits(bits int) error { - // Read as many raw bytes as we can - bytes := bits / 8 - buf := make([]byte, bytes) - _, err := reader.stream.Read(buf) +func (reader *Reader) ReadBool() (bool, error) { + val, err := reader.readBit() if err != nil { - return err + return false, err } - // The final read byte should be the new current byte - if bytes > 0 { - reader.curByte = buf[bytes-1] + return val == 1, nil +} + +// ReadBits is a function that reads the specified amount of bits +// from the parameter and returns the value, error +// based on the output. It can read up to 64 bits. Returns the read +// value in type uint64. +// +// Returns an error if there are no remaining bits. +func (reader *Reader) ReadBits(bits int) (uint64, error) { + if bits < 1 || bits > 64 { + return 0, errors.New("ReadBits(bits) ERROR: Bits number should be between 1 and 64") } - // Read the extra bits - for i := bytes * 8; i < bits; i++ { - _, err := reader.readBit() + var val uint64 + for i := 0; i < bits; i++ { + bit, err := reader.readBit() if err != nil { - return err + return 0, err + } + if reader.littleEndian { + val |= uint64(bit) << i + } else { + val |= uint64(bit) << (bits - 1 - i) } } - return nil + return val, nil } -// SkipBytes is a function that increases Reader index -// based on given input bytes number. +// ReadBytes is a function that reads the specified amount of bytes +// from the parameter and returns the value, error +// based on the output. It can read up to 8 bytes. Returns the read +// value in type uint64. // // Returns an error if there are no remaining bits. -func (reader *ReaderType) SkipBytes(bytes int) error { - err := reader.SkipBits(bytes * 8) +func (reader *Reader) ReadBytes(bytes int) (uint64, error) { + if bytes < 1 || bytes > 8 { + return 0, errors.New("ReadBytes(bytes) ERROR: Bytes number should be between 1 and 8") + } + value, err := reader.ReadBits(bytes * 8) if err != nil { - return err + return 0, err } - return nil + return value, nil } // ReadString is a function that reads every byte @@ -244,7 +365,7 @@ func (reader *ReaderType) SkipBytes(bytes int) error { // string that is read until the null-termination. // // Returns an error if there are no remaining bits. -func (reader *ReaderType) ReadString() (string, error) { +func (reader *Reader) ReadString() (string, error) { var out string for { value, err := reader.ReadBytes(1) @@ -259,13 +380,13 @@ func (reader *ReaderType) ReadString() (string, error) { return out, nil } -// ReadStringLen is a function that reads every byte +// ReadStringLength is a function that reads every byte // until the given length, or it is null-terminated (the byte is 0). // Returns the string that is read until the lenth or null-termination. // It will skip the remaining bytes if it is null-terminated. // // Returns an error if there are no remaining bits. -func (reader *ReaderType) ReadStringLen(length int) (string, error) { +func (reader *Reader) ReadStringLength(length int) (string, error) { var out string for i := 0; i < length; i++ { value, err := reader.ReadBytes(1) @@ -281,54 +402,11 @@ func (reader *ReaderType) ReadStringLen(length int) (string, error) { return out, nil } -// ReadBits is a function that reads the specified amount of bits -// from the parameter and returns the value, error -// based on the output. It can read up to 64 bits. Returns the read -// value in type uint64. -// -// Returns an error if there are no remaining bits. -func (reader *ReaderType) ReadBits(bits int) (uint64, error) { - if bits < 1 || bits > 64 { - return 0, fmt.Errorf("ReadBits(bits) ERROR: Bits number should be between 1 and 64.") - } - var val uint64 - for i := 0; i < bits; i++ { - bit, err := reader.readBit() - if err != nil { - return 0, err - } - - if reader.le { - val |= uint64(bit) << i - } else { - val |= uint64(bit) << (bits - 1 - i) - } - } - return val, nil -} - -// ReadBytes is a function that reads the specified amount of bytes -// from the parameter and returns the value, error -// based on the output. It can read up to 8 bytes. Returns the read -// value in type uint64. -// -// Returns an error if there are no remaining bits. -func (reader *ReaderType) ReadBytes(bytes int) (uint64, error) { - if bytes < 1 || bytes > 8 { - return 0, fmt.Errorf("ReadBytes(bytes) ERROR: Bytes number should be between 1 and 8.") - } - value, err := reader.ReadBits(bytes * 8) - if err != nil { - return 0, err - } - return value, nil -} - // ReadBitsToSlice is a function that reads the specified amount of bits // from the parameter and puts each bit into a slice and returns this slice. // // Returns an error if there are no remaining bits. -func (reader *ReaderType) ReadBitsToSlice(bits int) ([]byte, error) { +func (reader *Reader) ReadBitsToSlice(bits int) ([]byte, error) { bytes := (bits / 8) if bits%8 != 0 { bytes++ @@ -365,7 +443,7 @@ func (reader *ReaderType) ReadBitsToSlice(bits int) ([]byte, error) { // from the parameter and puts each byte into a slice and returns this slice. // // Returns an error if there are no remaining bytes. -func (reader *ReaderType) ReadBytesToSlice(bytes int) ([]byte, error) { +func (reader *Reader) ReadBytesToSlice(bytes int) ([]byte, error) { var out []byte for i := 0; i < bytes; i++ { val, err := reader.ReadBytes(1) @@ -377,35 +455,84 @@ func (reader *ReaderType) ReadBytesToSlice(bytes int) ([]byte, error) { return out, nil } -// ReadBool is a function that reads one bit and returns the state, error -// based on the output. Returns the read value in a bool format. +// SkipBits is a function that increases Reader index +// based on given input bits number. // // Returns an error if there are no remaining bits. -func (reader *ReaderType) ReadBool() (bool, error) { - val, err := reader.readBit() +func (reader *Reader) SkipBits(bits int) error { + // Read as many raw bytes as we can + bytes := bits / 8 + if bytes > 0 { + buf := make([]byte, bytes) + _, err := reader.stream.Read(buf) + if err != nil { + return err + } + // The final read byte should be the new current byte + reader.currentByte = buf[bytes-1] + } + // Read the extra bits + for i := bytes * 8; i < bits; i++ { + _, err := reader.readBit() + if err != nil { + return err + } + } + return nil +} + +// SkipBytes is a function that increases Reader index +// based on given input bytes number. +// +// Returns an error if there are no remaining bits. +func (reader *Reader) SkipBytes(bytes int) error { + err := reader.SkipBits(bytes * 8) if err != nil { - return false, err + return err } - return val == 1, nil + return nil +} + +// ReadRemainingBits is a function that reads the total amount of remaining bits in the stream. +// It first forks the original reader to check this count, so that it does not interfere with the original stream. +// +// Returns an error if there are no remaining bits. +func (reader *Reader) ReadRemainingBits() (uint64, error) { + newReader, err := reader.Fork() + if err != nil { + return 0, err + } + var bits uint64 = 0 + for { + err := newReader.SkipBits(1) + if err != nil { + break // EOF + } + fmt.Printf("%+v\n", newReader) + bits++ + } + return bits, nil } // readBit is a private function that reads a single bit from the stream. // This is the main function that makes us read stream data. -func (reader *ReaderType) readBit() (uint8, error) { +func (reader *Reader) readBit() (uint8, error) { if reader.index == 0 { - // Read a byte from stream into curByte - buf := make([]byte, 1) - _, err := reader.stream.Read(buf) + // Read a byte from stream into currentByte + buffer := make([]byte, 1) + // We are not checking for the n return value from stream.Read, because we are only reading 1 byte at a time. + // Meaning if an EOF happens with a 1 byte read, we dont have any extra byte reading anyways. + _, err := reader.stream.Read(buffer) if err != nil { return 0, err } - reader.curByte = buf[0] + reader.currentByte = buffer[0] } var val bool - if reader.le { - val = (reader.curByte & (1 << reader.index)) != 0 + if reader.littleEndian { + val = (reader.currentByte & (1 << reader.index)) != 0 } else { - val = (reader.curByte & (1 << (7 - reader.index))) != 0 + val = (reader.currentByte & (1 << (7 - reader.index))) != 0 } reader.index = (reader.index + 1) % 8 if val { diff --git a/bitreader_test.go b/bitreader_test.go index 2926758..acd980e 100644 --- a/bitreader_test.go +++ b/bitreader_test.go @@ -1,212 +1,1385 @@ +// BitReader is a simple bit reader with big/little-endian support for golang. +// %83.2 coerage package bitreader import ( "bytes" + "io" + "math" + "reflect" "testing" ) -// TODO: Write better unit tests +func TestNewReader(t *testing.T) { + stream := bytes.NewReader([]byte{0x01, 0x02, 0x03}) + type args struct { + stream io.Reader + littleEndian bool + } + tests := []struct { + name string + args args + want *Reader + }{ + { + name: "ReaderLE", + args: args{ + stream: stream, + littleEndian: true, + }, + want: &Reader{ + stream: stream, + index: 0, + currentByte: 0, + littleEndian: true, + }, + }, + { + name: "ReaderBE", + args: args{ + stream: stream, + littleEndian: false, + }, + want: &Reader{ + stream: stream, + index: 0, + currentByte: 0, + littleEndian: false, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewReader(tt.args.stream, tt.args.littleEndian); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewReader() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestNewReaderFromBytes(t *testing.T) { + type args struct { + stream []byte + littleEndian bool + } + tests := []struct { + name string + args args + want *Reader + }{ + { + name: "ReaderLE", + args: args{ + stream: []byte{0x01, 0x02, 0x03}, + littleEndian: true, + }, + want: &Reader{ + stream: bytes.NewReader([]byte{0x01, 0x02, 0x03}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + }, + { + name: "ReaderBE", + args: args{ + stream: []byte{0x01, 0x02, 0x03}, + littleEndian: false, + }, + want: &Reader{ + stream: bytes.NewReader([]byte{0x01, 0x02, 0x03}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewReaderFromBytes(tt.args.stream, tt.args.littleEndian); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewReaderFromBytes() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_Fork(t *testing.T) { + stream := bytes.NewReader([]byte{53}) + tests := []struct { + name string + reader *Reader + want *Reader + wantErr bool + }{ + { + name: "Fork", + reader: &Reader{ + stream: stream, + index: 4, + currentByte: 53, + littleEndian: false, + }, + want: &Reader{ + index: 4, + currentByte: 53, + littleEndian: false, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.Fork() + tt.want.stream = got.stream + if (err != nil) != tt.wantErr { + t.Errorf("Reader.Fork() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Reader.Fork() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadBool(t *testing.T) { + tests := []struct { + name string + reader *Reader + want bool + }{ + { + name: "ReadBoolTrueLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b00000001}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: true, + }, + { + name: "ReadBoolTrueBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10000000}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: true, + }, + { + name: "ReadBoolFalseLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b00000010}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: false, + }, + { + name: "ReadBoolFalseBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b01000000}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadBool(); got != tt.want { + t.Errorf("Reader.TryReadBool() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadInt1(t *testing.T) { + tests := []struct { + name string + reader *Reader + want uint8 + }{ + { + name: "ReadInt1TrueLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b00000001}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 0b1, + }, + { + name: "ReadInt1TrueBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10000000}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: 0b1, + }, + { + name: "ReadInt1FalseLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b00000010}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 0b0, + }, + { + name: "ReadInt1FalseBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b01000000}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: 0b0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadInt1(); got != tt.want { + t.Errorf("Reader.TryReadInt1() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadUInt8(t *testing.T) { + tests := []struct { + name string + reader *Reader + want uint8 + }{ + { + name: "ReadUInt8LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{202}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 202, + }, + { + name: "ReadUInt8BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{202}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: 202, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadUInt8(); got != tt.want { + t.Errorf("Reader.TryReadUInt8() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadSInt8(t *testing.T) { + tests := []struct { + name string + reader *Reader + want int8 + }{ + { + name: "ReadSInt8LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{202}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: -54, + }, + { + name: "ReadSInt8BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{202}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: -54, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadSInt8(); got != tt.want { + t.Errorf("Reader.TryReadSInt8() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadUInt16(t *testing.T) { + tests := []struct { + name string + reader *Reader + want uint16 + }{ + { + name: "ReadUInt16LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 0b0101010110101010, + }, + { + name: "ReadUInt16BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: 0b1010101001010101, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadUInt16(); got != tt.want { + t.Errorf("Reader.TryReadUInt16() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadSInt16(t *testing.T) { + tests := []struct { + name string + reader *Reader + want int16 + }{ + { + name: "ReadSInt16LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 21930, + }, + { + name: "ReadSInt16BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: -21931, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadSInt16(); got != tt.want { + t.Errorf("Reader.TryReadSInt16() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadUInt32(t *testing.T) { + tests := []struct { + name string + reader *Reader + want uint32 + }{ + { + name: "ReadUInt32LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 0b00001111111100000101010110101010, + }, + { + name: "ReadUInt32BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: 0b10101010010101011111000000001111, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadUInt32(); got != tt.want { + t.Errorf("Reader.TryReadUInt32() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadSInt32(t *testing.T) { + tests := []struct { + name string + reader *Reader + want int32 + }{ + { + name: "ReadSInt32LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 267408810, + }, + { + name: "ReadSInt32BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: -1437208561, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadSInt32(); got != tt.want { + t.Errorf("Reader.TryReadSInt32() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadUInt64(t *testing.T) { + tests := []struct { + name string + reader *Reader + want uint64 + }{ + { + name: "ReadUInt64LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111, 0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 0b0000111111110000010101011010101000001111111100000101010110101010, + }, + { + name: "ReadUInt64BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111, 0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: 0b1010101001010101111100000000111110101010010101011111000000001111, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadUInt64(); got != tt.want { + t.Errorf("Reader.TryReadUInt64() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadSInt64(t *testing.T) { + tests := []struct { + name string + reader *Reader + want int64 + }{ + { + name: "ReadSInt64LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111, 0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: 1148512093879686570, + }, + { + name: "ReadSInt64BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111, 0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: -6172763764168462321, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadSInt64(); got != tt.want { + t.Errorf("Reader.TryReadSInt64() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadFloat32(t *testing.T) { + tests := []struct { + name string + reader *Reader + want float32 + }{ + { + name: "ReadFloat32LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: math.Float32frombits(0b00001111111100000101010110101010), + }, + { + name: "ReadFloat32BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: math.Float32frombits(0b10101010010101011111000000001111), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadFloat32(); got != tt.want { + t.Errorf("Reader.TryReadFloat32() = %+v, want %+v", got, tt.want) + } + }) + } +} -// 01110001, 00001101, 00000000, 00000000, 10100010, 00011011, 00000000, 00000000, 11001100 -var TestArray = [...]byte{113, 13, 0, 0, 162, 27, 0, 0, 204} +func TestReader_TryReadFloat64(t *testing.T) { + tests := []struct { + name string + reader *Reader + want float64 + }{ + { + name: "ReadFloat64LE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111, 0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: math.Float64frombits(0b0000111111110000010101011010101000001111111100000101010110101010), + }, + { + name: "ReadFloat64BE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10101010, 0b01010101, 0b11110000, 0b00001111, 0b10101010, 0b01010101, 0b11110000, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: math.Float64frombits(0b1010101001010101111100000000111110101010010101011111000000001111), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadFloat64(); got != tt.want { + t.Errorf("Reader.TryReadFloat64() = %+v, want %+v", got, tt.want) + } + }) + } +} -func TestTryReadFloat32(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []float32{6.98198182157e+29, -2.10064170919e-18} - for i := range expected { - value := bitreader.TryReadFloat32() - if value != expected[i] { - t.Fatalf("TryReadFloat32 FAIL for index %d: Expected %f, Got %f", i, expected[i], value) - } - } -} - -func TestTryReadFloat64(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []float64{3.68828741038253948851462939603e+236} - for i := range expected { - value := bitreader.TryReadFloat64() - if value != expected[i] { - t.Fatalf("TryReadFloat64 FAIL for index %d: Expected %f, Got %f", i, expected[i], value) - } - } -} - -func TestTryReadInt8(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []int{113, 13, 0} - for i := range expected { - value := bitreader.TryReadInt8() - if int(value) != expected[i] { - t.Fatalf("TryReadInt8 FAIL for index %d: Expected %d, Got %d", i, expected[i], value) - } - } -} - -func TestTryReadInt16(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []int{28941, 0, 41499, 0} - for i := range expected { - value := bitreader.TryReadInt16() - if int(value) != expected[i] { - t.Fatalf("TryReadInt16 FAIL for index %d: Expected %d, Got %d", i, expected[i], value) - } - } -} - -func TestTryReadInt32(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []int{1896677376, 2719678464} - for i := range expected { - value := bitreader.TryReadInt32() - if int(value) != expected[i] { - t.Fatalf("TryReadInt32 FAIL for index %d: Expected %d, Got %d", i, expected[i], value) - } - } -} - -func TestTryReadInt64(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []int{8146167303702773760} - for i := range expected { - value := bitreader.TryReadInt64() - if int(value) != expected[i] { - t.Fatalf("TryReadInt64 FAIL for index %d: Expected %d, Got %d", i, expected[i], value) - } - } -} - -func TestReadBit(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []bool{false, true, true, true} - for i := range expected { - value, err := bitreader.ReadBool() - if err != nil { - t.Fatal(err) - } - if value != expected[i] { - t.Fatalf("ReadBit FAIL for index %d: Expected %t, Got %t", i, expected[i], value) - } - } -} - -func TestReadBitLE(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), true) - expected := []bool{true, false, false, false} - for i := range expected { - value, err := bitreader.ReadBool() - if err != nil { - t.Fatal(err) - } - if value != expected[i] { - t.Fatalf("ReadBitLE FAIL for index %d: Expected %t, Got %t", i, expected[i], value) - } - } -} - -func TestReadBits(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []int{3793354753, 2288779267} // 11100010000110100000000000000001, 10001000011011000000000000000011 - expectedBool := []bool{false, false} - for i := range expected { - bool, err := bitreader.ReadBool() - if bool != expectedBool[i] { - t.Fatalf("ReadBits ReadBit FAIL for index %d: Expected %t, Got %t", i, expectedBool[i], bool) - } - if err != nil { - t.Fatal(err) - } - value, err := bitreader.ReadBits(32) - if err != nil { - t.Fatal(err) - } - if int(value) != expected[i] { - t.Fatalf("ReadBits FAIL for index %d: Expected %d, Got %d", i, expected[i], value) - } - } -} - -func TestReadBitsLE(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), true) - expected := []int{1720, 1768} // 11010111000, 11011101000 - for i := range expected { - bitreader.ReadBool() - value, err := bitreader.ReadBits(32) - if err != nil { - t.Fatal(err) - } - if int(value) != expected[i] { - t.Fatalf("ReadBitsLE FAIL for index %d: Expected %d, Got %d", i, expected[i], value) - } - } -} - -func TestReadBytes(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []int{3793354753, 2288779267} // 11100010000110100000000000000001, 10001000011011000000000000000011 - expectedBool := []bool{false, false} - for i := range expected { - bool, err := bitreader.ReadBool() - if bool != expectedBool[i] { - t.Fatalf("ReadBytes ReadBit FAIL for index %d: Expected %t, Got %t", i, expectedBool[i], bool) - } - if err != nil { - t.Fatal(err) - } - value, err := bitreader.ReadBytes(4) - if err != nil { - t.Fatal(err) - } - if int(value) != expected[i] { - t.Fatalf("ReadBytes FAIL for index %d: Expected %d, Got %d", i, expected[i], value) - } - } -} - -func TestReadBytesLE(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), true) - expected := []int{1720, 1768} // 11010111000, 11011101000 - for i := range expected { - bitreader.ReadBool() - value, err := bitreader.ReadBytes(4) - if err != nil { - t.Fatal(err) - } - if int(value) != expected[i] { - t.Fatalf("ReadBytesLE FAIL for index %d: Expected %d, Got %d", i, expected[i], value) - } - } -} - -func TestSkipBits(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), false) - expected := []bool{true, true, false, true} //00001101 - err := bitreader.SkipBits(12) - if err != nil { - t.Fatal(err) - } - for i := range expected { - value, err := bitreader.ReadBool() - if err != nil { - t.Fatal(err) - } - if value != expected[i] { - t.Fatalf("SkipBits ReadBit FAIL for index %d: Expected %t, Got %t", i, expected[i], value) - } - } -} - -func TestSkipBitsLE(t *testing.T) { - bitreader := Reader(bytes.NewReader(TestArray[:]), true) - expected := []bool{false, false, false, false} //10110000 - bitreader.SkipBits(12) - for i := range expected { - value, err := bitreader.ReadBool() - if err != nil { - t.Fatal(err) - } - if value != expected[i] { - t.Fatalf("SkipBitsLE ReadBit FAIL for index %d: Expected %t, Got %t", i, expected[i], value) - } +func TestReader_TryReadBits(t *testing.T) { + type args struct { + bits int + } + tests := []struct { + name string + reader *Reader + args args + want uint64 + }{ + { + name: "ReadBitsLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110000, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bits: 12, + }, + want: 0b010111110000, + }, + { + name: "ReadBitsBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110000, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bits: 12, + }, + want: 0b111100000101, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadBits(tt.args.bits); got != tt.want { + t.Errorf("Reader.TryReadBits() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadBytes(t *testing.T) { + type args struct { + bytes int + } + tests := []struct { + name string + reader *Reader + args args + want uint64 + }{ + { + name: "ReadBytesLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110000, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bytes: 2, + }, + want: 0b0101010111110000, + }, + { + name: "ReadBytesBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110000, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bytes: 2, + }, + want: 0b1111000001010101, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadBytes(tt.args.bytes); got != tt.want { + t.Errorf("Reader.TryReadBytes() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadString(t *testing.T) { + tests := []struct { + name string + reader *Reader + want string + }{ + { + name: "ReadStringLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'H', 'e', 'l', 'l', 'o', 0, '!'}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: "Hello", + }, + { + name: "ReadStringBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'W', 'o', 'r', 'l', 'd', 0, '!'}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: "World", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadString(); got != tt.want { + t.Errorf("Reader.TryReadString() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadStringLength(t *testing.T) { + type args struct { + length int + } + tests := []struct { + name string + reader *Reader + args args + want string + }{ + { + name: "ReadStringLengthLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'H', 'e', 'l', 'l', 'o', 0, '!'}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + length: 4, + }, + want: "Hell", + }, + { + name: "ReadStringLengthBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'W', 'o', 'r', 'l', 'd', '!', '?'}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + length: 6, + }, + want: "World!", + }, + { + name: "ReadStringLengthNullHitBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'W', 'o', 'r', 'l', 'd', 0, '!', '?'}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + length: 7, + }, + want: "World", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadStringLength(tt.args.length); got != tt.want { + t.Errorf("Reader.TryReadStringLength() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadBitsToSlice(t *testing.T) { + type args struct { + bits int + } + tests := []struct { + name string + reader *Reader + args args + want []byte + }{ + { + name: "ReadBitsToSliceBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bits: 12, + }, + want: []byte{0b11110010, 0b0}, + }, + { + name: "ReadBitsToSliceLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bits: 12, + }, + want: []byte{0b11110010, 0b00001111}, + }, + { + name: "ReadBitsToSliceBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bits: 16, + }, + want: []byte{0b11110010, 0b00001111}, + }, + { + name: "ReadBitsToSliceLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bits: 16, + }, + want: []byte{0b11110010, 0b00001111}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadBitsToSlice(tt.args.bits); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Reader.TryReadBitsToSlice() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadBytesToSlice(t *testing.T) { + type args struct { + bytes int + } + tests := []struct { + name string + reader *Reader + args args + want []byte + }{ + { + name: "ReadBytesToSliceBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bytes: 2, + }, + want: []byte{0b11110010, 0b00001111}, + }, + { + name: "ReadBytesToSliceLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bytes: 2, + }, + want: []byte{0b11110010, 0b00001111}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.reader.TryReadBytesToSlice(tt.args.bytes); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Reader.TryReadBytesToSlice() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_TryReadRemainingBits(t *testing.T) { + tests := []struct { + name string + reader *Reader + want uint64 + wantErr bool + }{ + { + name: "ReadRemainingBits", + reader: &Reader{ + stream: bytes.NewReader([]byte{0x11, 0x22}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: 16, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.reader.TryReadRemainingBits() + if got != tt.want { + t.Errorf("Reader.TryReadRemainingBits() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestReader_ReadBool(t *testing.T) { + tests := []struct { + name string + reader *Reader + want bool + wantErr bool + }{ + { + name: "ReadBoolTrueLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b00000001}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: true, + }, + { + name: "ReadBoolTrueBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b10000000}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: true, + }, + { + name: "ReadBoolFalseLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b00000010}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: false, + }, + { + name: "ReadBoolFalseBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b01000000}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.ReadBool() + if (err != nil) != tt.wantErr { + t.Errorf("Reader.ReadBool() error = %+v, wantErr %+v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Reader.ReadBool() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_ReadBits(t *testing.T) { + type args struct { + bits int + } + tests := []struct { + name string + reader *Reader + args args + want uint64 + wantErr bool + }{ + { + name: "ReadBitsLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110000, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bits: 12, + }, + want: 0b010111110000, + }, + { + name: "ReadBitsBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110000, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bits: 12, + }, + want: 0b111100000101, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.ReadBits(tt.args.bits) + if (err != nil) != tt.wantErr { + t.Errorf("Reader.ReadBits() error = %+v, wantErr %+v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Reader.ReadBits() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_ReadBytes(t *testing.T) { + type args struct { + bytes int + } + tests := []struct { + name string + reader *Reader + args args + want uint64 + wantErr bool + }{ + { + name: "ReadBytesLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110000, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bytes: 2, + }, + want: 0b0101010111110000, + }, + { + name: "ReadBytesBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110000, 0b01010101}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bytes: 2, + }, + want: 0b1111000001010101, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.ReadBytes(tt.args.bytes) + if (err != nil) != tt.wantErr { + t.Errorf("Reader.ReadBytes() error = %+v, wantErr %+v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Reader.ReadBytes() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_ReadString(t *testing.T) { + tests := []struct { + name string + reader *Reader + want string + wantErr bool + }{ + { + name: "ReadStringLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'H', 'e', 'l', 'l', 'o', 0, '!'}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + want: "Hello", + }, + { + name: "ReadStringBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'W', 'o', 'r', 'l', 'd', 0, '!'}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: "World", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.ReadString() + if (err != nil) != tt.wantErr { + t.Errorf("Reader.ReadString() error = %+v, wantErr %+v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Reader.ReadString() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_ReadStringLength(t *testing.T) { + type args struct { + length int + } + tests := []struct { + name string + reader *Reader + args args + want string + wantErr bool + }{ + { + name: "ReadStringLengthLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'H', 'e', 'l', 'l', 'o', 0, '!'}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + length: 4, + }, + want: "Hell", + }, + { + name: "ReadStringLengthBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'W', 'o', 'r', 'l', 'd', '!', '?'}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + length: 6, + }, + want: "World!", + }, + { + name: "ReadStringLengthNullHitBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{'W', 'o', 'r', 'l', 'd', 0, '!', '?'}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + length: 7, + }, + want: "World", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.ReadStringLength(tt.args.length) + if (err != nil) != tt.wantErr { + t.Errorf("Reader.ReadStringLength() error = %+v, wantErr %+v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Reader.ReadStringLength() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_ReadBitsToSlice(t *testing.T) { + type args struct { + bits int + } + tests := []struct { + name string + reader *Reader + args args + want []byte + wantErr bool + }{ + { + name: "ReadBitsToSliceBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bits: 12, + }, + want: []byte{0b11110010, 0b0}, + }, + { + name: "ReadBitsToSliceLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bits: 12, + }, + want: []byte{0b11110010, 0b00001111}, + }, + { + name: "ReadBitsToSliceBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bits: 16, + }, + want: []byte{0b11110010, 0b00001111}, + }, + { + name: "ReadBitsToSliceLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bits: 16, + }, + want: []byte{0b11110010, 0b00001111}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.ReadBitsToSlice(tt.args.bits) + if (err != nil) != tt.wantErr { + t.Errorf("Reader.ReadBitsToSlice() error = %+v, wantErr %+v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Reader.ReadBitsToSlice() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_ReadBytesToSlice(t *testing.T) { + type args struct { + bytes int + } + tests := []struct { + name string + reader *Reader + args args + want []byte + wantErr bool + }{ + { + name: "ReadBytesToSliceBE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + args: args{ + bytes: 2, + }, + want: []byte{0b11110010, 0b00001111}, + }, + { + name: "ReadBytesToSliceLE", + reader: &Reader{ + stream: bytes.NewReader([]byte{0b11110010, 0b00001111}), + index: 0, + currentByte: 0, + littleEndian: true, + }, + args: args{ + bytes: 2, + }, + want: []byte{0b11110010, 0b00001111}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.ReadBytesToSlice(tt.args.bytes) + if (err != nil) != tt.wantErr { + t.Errorf("Reader.ReadBytesToSlice() error = %+v, wantErr %+v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Reader.ReadBytesToSlice() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func TestReader_ReadRemainingBits(t *testing.T) { + tests := []struct { + name string + reader *Reader + want uint64 + wantErr bool + }{ + { + name: "ReadRemainingBits", + reader: &Reader{ + stream: bytes.NewReader([]byte{0x11, 0x22}), + index: 0, + currentByte: 0, + littleEndian: false, + }, + want: 16, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.reader.ReadRemainingBits() + if (err != nil) != tt.wantErr { + t.Errorf("Reader.ReadRemainingBits() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Reader.ReadRemainingBits() = %v, want %v", got, tt.want) + } + }) } } -- cgit v1.2.3