From 4a09f5844cb1649ecbdfec260628285cfcd640ec Mon Sep 17 00:00:00 2001 From: BiSaXa <1669855+BiSaXa@users.noreply.github.com> Date: Sun, 4 Sep 2022 01:07:28 +0300 Subject: init - first full version --- LICENSE | 21 +++++++++ README.md | 1 + bitreader.go | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ bitreader_test.go | 103 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 ++ 5 files changed, 257 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bitreader.go create mode 100644 bitreader_test.go create mode 100644 go.mod diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7ea74e1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Arda Serdar Pektezol + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a493a1f --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# BitReader diff --git a/bitreader.go b/bitreader.go new file mode 100644 index 0000000..9d9a273 --- /dev/null +++ b/bitreader.go @@ -0,0 +1,129 @@ +package main + +import ( + "fmt" + "math/bits" + "strconv" + "strings" +) + +type ReaderType struct { + data []byte + base int + index int + lsb bool +} + +func Reader(data []byte) *ReaderType { + return &ReaderType{ + data: data, + base: 0, + index: 0, + lsb: false, + } +} + +func ReaderLSB(data []byte) *ReaderType { + dataReversed := data + for index, byteValue := range data { + dataReversed[index] = bits.Reverse8(byteValue) + } + return &ReaderType{ + data: dataReversed, + base: 0, + index: 0, + lsb: true, + } +} + +func (reader *ReaderType) SkipBits(bits int) error { + if bits <= 0 { + return fmt.Errorf("SkipBits Error: Bits value %d lower or equals than 0.", bits) + } + for reader.index+bits > 7 { + reader.base++ + reader.index = 0 + bits -= 8 + } + reader.index += bits + return nil +} + +func (reader *ReaderType) ReadBits32(bits int) (int, error) { + if bits <= 0 { + return -1, fmt.Errorf("ReadBits Error: Bits value %d lower or equals than 0.", bits) + } + if bits > 32 { + return -1, fmt.Errorf("ReadBits Error: Bits value %d higher than 32.", bits) + } + err := reader.checkAvailableBits(bits) + if err != nil { + return -1, err + } + if reader.lsb { + var output string + // Go to last bit and read backwards from there + reader.base += bits / 8 + reader.index += bits % 8 + if reader.index > 7 { + reader.index -= 8 + reader.base++ + } + for i := 0; i < bits; i++ { + reader.index-- + if reader.index < 0 { + reader.base-- + reader.index = 7 + } + binary := fmt.Sprintf("%08b", reader.data[reader.base]) + binaryArr := strings.Split(binary, "") + output += binaryArr[reader.index] + } + // Return to last bit after reading + reader.base += bits / 8 + reader.index += bits % 8 + if reader.index > 7 { + reader.index -= 8 + } + // Conversion of string binary to int + value, err := strconv.ParseUint(output, 2, 32) + if err != nil { + return -1, fmt.Errorf("%s", err) + } + return int(value), nil + } else { + var output string + for i := 0; i < bits; i++ { + binary := fmt.Sprintf("%08b", reader.data[reader.base]) + binaryArr := strings.Split(binary, "") + output += binaryArr[reader.index] + reader.index++ + if reader.index > 7 { + reader.base++ + reader.index = 0 + } + } + // Conversion of string binary to int + value, err := strconv.ParseUint(output, 2, 32) + if err != nil { + return -1, fmt.Errorf("%s", err) + } + return int(value), nil + } +} + +func (reader *ReaderType) ReadBit() (bool, error) { + value, err := reader.ReadBits32(1) + if err != nil { + return false, fmt.Errorf("ReadBit Error: %s", err) + } + return value != 0, nil +} + +func (reader *ReaderType) checkAvailableBits(bits int) error { + availableBits := (len(reader.data)-reader.base)*8 - reader.index + if availableBits < bits { + return fmt.Errorf("BitReaderOutOfBounds: Wanted to read %d bit(s) but only %d bit(s) is/are available.", bits, availableBits) + } + return nil +} diff --git a/bitreader_test.go b/bitreader_test.go new file mode 100644 index 0000000..c29a3bd --- /dev/null +++ b/bitreader_test.go @@ -0,0 +1,103 @@ +package main + +import ( + "testing" +) + +// 01110001, 00001101, 00000000, 00000000, 10100010, 00011011, 00000000, 00000000, 11001100 +var TestArray = [...]byte{113, 13, 0, 0, 162, 27, 0, 0, 204} + +func TestReadBit(t *testing.T) { + bitreader := Reader(TestArray[:]) + expected := []bool{false, true, true, true} + for i := range expected { + value, err := bitreader.ReadBit() + 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 TestReadBitLSB(t *testing.T) { + bitreader := ReaderLSB(TestArray[:]) + expected := []bool{true, false, false, false} + for i := range expected { + value, err := bitreader.ReadBit() + if err != nil { + t.Fatal(err) + } + if value != expected[i] { + t.Fatalf("ReadBitLSB FAIL for index %d: Expected %t, Got %t", i, expected[i], value) + } + } +} + +func TestReadBits32(t *testing.T) { + bitreader := Reader(TestArray[:]) + expected := []int{3793354753, 2288779267} // 11100010000110100000000000000001, 10001000011011000000000000000011 + expectedBool := []bool{false, false} + for i := range expected { + bool, err := bitreader.ReadBit() + if bool != expectedBool[i] { + t.Fatalf("ReadBits32 ReadBit FAIL for index %d: Expected %t, Got %t", i, expectedBool[i], bool) + } + if err != nil { + t.Fatal(err) + } + value, err := bitreader.ReadBits32(32) + if err != nil { + t.Fatal(err) + } + if value != expected[i] { + t.Fatalf("ReadBits32 FAIL for index %d: Expected %d, Got %d", i, expected[i], value) + } + } +} + +func TestReadBits32LSB(t *testing.T) { + bitreader := ReaderLSB(TestArray[:]) + expected := []int{1720, 1768} // 11010111000, 11011101000 + for i := range expected { + bitreader.ReadBit() + value, err := bitreader.ReadBits32(32) + if err != nil { + t.Fatal(err) + } + if value != expected[i] { + t.Fatalf("ReadBits32LSB FAIL for index %d: Expected %d, Got %d", i, expected[i], value) + } + } +} + +func TestSkipBits(t *testing.T) { + bitreader := Reader(TestArray[:]) + expected := []bool{true, true, false, true} //00001101 + bitreader.SkipBits(12) + for i := range expected { + value, err := bitreader.ReadBit() + 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 TestSkipBitsLSB(t *testing.T) { + bitreader := ReaderLSB(TestArray[:]) + expected := []bool{false, false, false, false} //10110000 + bitreader.SkipBits(12) + for i := range expected { + value, err := bitreader.ReadBit() + 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) + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2caf20e --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/bisaxa/bitreader + +go 1.19 -- cgit v1.2.3