├── Makefile ├── LICENSE.txt ├── tests └── test.cpp ├── src ├── bit_buffer.cpp └── bit_buffer.hpp └── README.md /Makefile: -------------------------------------------------------------------------------- 1 | CC := g++ -std=c++14 2 | CFLAGS := -Wall -I. 3 | 4 | SRCDIR = src 5 | BUILDDIR = build 6 | TESTDIR = tests 7 | 8 | SOURCES := $(wildcard $(SRCDIR)/*.cpp) 9 | SRC_OBJS := $(SOURCES:$(SRCDIR)/%.cpp=$(BUILDDIR)/%.o) 10 | TESTS := $(wildcard $(TESTDIR)/*.cpp) 11 | TEST_OBJS := $(TESTS:$(TESTDIR)/%.cpp=$(BUILDDIR)/%.o) 12 | 13 | test: $(SRC_OBJS) $(TEST_OBJS) 14 | $(CC) $^ -o $@ 15 | 16 | $(BUILDDIR)/%.o: $(SRCDIR)/%.cpp 17 | @mkdir -p $(BUILDDIR) 18 | $(CC) $(CFLAGS) -c -o $@ $^ 19 | 20 | $(BUILDDIR)/%.o: $(TESTDIR)/%.cpp 21 | @mkdir -p $(BUILDDIR) 22 | $(CC) $(CFLAGS) -c -o $@ $< 23 | 24 | clean: 25 | rm -r $(BUILDDIR) test 26 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Aadesh 3 | 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. 9 | -------------------------------------------------------------------------------- /tests/test.cpp: -------------------------------------------------------------------------------- 1 | #include "../src/bit_buffer.hpp" 2 | 3 | #include 4 | #include 5 | 6 | int main() { 7 | bit_buffer buffer(1000); 8 | buffer.write_bits(true, 1); 9 | 10 | buffer.write_bits(10, 4); 11 | buffer.write_bits(10, 4); 12 | 13 | assert(buffer.read_bit(1) == 1); 14 | assert(buffer.read_bits(1, 4) == 10); 15 | assert(buffer.read_bits(5, 4) == 10); 16 | 17 | bit_buffer buffer2(1000); 18 | buffer2.write_int(100); 19 | buffer2.write_bits(14, 4); 20 | buffer2.write_bits(11, 4); 21 | buffer2.write_short(20); 22 | 23 | assert(buffer2.read_bytes(0, 4) == 100); 24 | assert(buffer2.read_bits(32, 4) == 14); 25 | assert(buffer2.read_bits(36, 4) == 11); 26 | assert(buffer2.read_bytes(5, 2) == 20); 27 | 28 | bit_buffer bb; 29 | bb.write_bits(true, 1); 30 | bb.write_bits(false, 1); 31 | bb.write_bits(2, 2); 32 | bb.write_bits(10, 4); 33 | bb.write_byte(100); 34 | 35 | assert(bb.read_bit(0) == 1); 36 | assert(bb.read_bit(1) == 0); 37 | assert(bb.read_bits(2, 2) == 2); 38 | assert(bb.read_bits(4, 4) == 10); 39 | assert(bb.read_byte(1) == 100); 40 | assert(bb.read_bits(8, 8) == 100); 41 | assert(bb.read_byte(0) == 170); 42 | assert(bb.read_byte(1) == 100); 43 | assert(bb.read_bytes(0, 2) == 43620); 44 | 45 | puts("Test Success!"); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/bit_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "bit_buffer.hpp" 2 | 3 | bit_buffer::bit_buffer(const size_t size) : 4 | pos_(0), bit_index_(0) 5 | { 6 | this->buffer_.reserve(size); 7 | } 8 | 9 | bit_buffer::~bit_buffer() {} 10 | 11 | void bit_buffer::write_byte(const ubyte_t& data) { 12 | this->write_(data); 13 | } 14 | 15 | void bit_buffer::write_char(const char& data) { 16 | this->write_(data); 17 | } 18 | 19 | void bit_buffer::write_bool(const bool& data) { 20 | this->write_(data); 21 | } 22 | 23 | void bit_buffer::write_short(const short& data) { 24 | this->write_(data); 25 | } 26 | 27 | void bit_buffer::write_int(const uint32_t& data) { 28 | this->write_(data); 29 | } 30 | 31 | void bit_buffer::write_long(const uint64_t& data) { 32 | this->write_(data); 33 | } 34 | 35 | uint8_t bit_buffer::read_byte(const size_t byte_index) { 36 | return this->read_bytes_(byte_index, 1); 37 | } 38 | 39 | uint32_t bit_buffer::read_bytes(const size_t byte_index, const size_t num_bytes) { 40 | return this->read_bytes_(byte_index, num_bytes); 41 | } 42 | 43 | uint8_t bit_buffer::read_bit(const size_t bit_index) { 44 | return this->read_bits_(bit_index, 1, 0); 45 | } 46 | 47 | uint32_t bit_buffer::read_bits(const size_t bit_index, const size_t num_bits) { 48 | return this->read_bits_(bit_index, num_bits, 0); 49 | } 50 | 51 | bit_iterator bit_buffer::create_iter() const { 52 | return bit_iterator(*this); 53 | } 54 | 55 | bit_buffer::iterator bit_buffer::begin() { 56 | return bit_iterator(*this); 57 | } 58 | 59 | bit_buffer::const_iterator bit_buffer::begin() const { 60 | return bit_iterator(*this); 61 | } 62 | 63 | bit_buffer::iterator bit_buffer::end() { 64 | return bit_iterator(*this, this->buffer_.size() * 8); 65 | } 66 | 67 | bit_buffer::const_iterator bit_buffer::end() const { 68 | return bit_iterator(*this, this->buffer_.size() * 8); 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BitBufferCpp 2 | C++ buffer class that allows you to write a stream of individual bits and bytes 3 | 4 | ## Installation 5 | 6 | To compile and run the test file: 7 | ``` 8 | $ git clone https://github.com/Aadeshp/BitBufferCpp.git 9 | $ cd BitBufferCpp && make 10 | ``` 11 | 12 | ## How To Use 13 | 14 | ```c++ 15 | #include "/path/to/bit_buffer.hpp" 16 | #include 17 | 18 | bit_buffer bb; 19 | bb.write_bits(true, 1); 20 | bb.write_bits(false, 1); 21 | bb.write_bits(2, 2); 22 | bb.write_bits(10, 4); 23 | bb.write_byte(100); 24 | 25 | std::cout << bb.read_bit(0) << std::endl; // Prints out 1 (aka true) 26 | std::cout << bb.read_bit(1) << std::endl; // Prints out 0 (aka false) 27 | std::cout << bb.read_bits(2, 2) << std::endl; // Prints out 2 28 | std::cout << bb.read_bits(4, 4) << std::endl; // Prints out 10 29 | std::cout << bb.read_byte(1) << std::endl; // Prints out 100 30 | std::cout << bb.read_bits(8, 8) << std::endl; // Prints out 100 31 | std::cout << bb.read_byte(0) << std::endl; // 1010 1010, so it prints out 170 32 | std::cout << bb.read_bytes(0, 2) << std::endl; // 1010 1010 0110 0100, so it prints out 43620 33 | 34 | // Iterators 35 | 36 | // Bit Iterator 37 | for (bit_buffer::iterator iter = bb.begin(); iter != bb.end(); ++iter) { 38 | uint8_t bit = *iter; 39 | 40 | // Do stuff with bit 41 | } 42 | 43 | for (auto& bit : bb) { 44 | // Do stuff with bit 45 | } 46 | 47 | // Byte iterator 48 | for (auto& byte : bb.get_bytes()) { 49 | // Do stuff with byte 50 | } 51 | ``` 52 | 53 | ## License 54 | 55 | The MIT License (MIT) 56 | Copyright (c) 2016 Aadesh 57 | 58 | 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: 59 | 60 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 61 | 62 | 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. 63 | 64 | -------------------------------------------------------------------------------- /src/bit_buffer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BIT_BUFFER_HPP_ 2 | #define BIT_BUFFER_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #define DEFAULT_SIZE 1024 14 | 15 | class bit_iterator; 16 | 17 | /* 18 | * Byte buffer is a class that lets you easily serialize data 19 | * into the form of bits and bytes, and stores it all in a 20 | * byte array, but is accessed publicly through the read_bit(s) 21 | * and read_byte(s) methods. 22 | * 23 | * Example: 24 | * bit_buffer bb; 25 | * 26 | * bb.write_bit(true); 27 | * bb.write_bits(2, 2); 28 | * bb.write_bit(false); 29 | * bb.write_bits(10, 4); 30 | * bb.write_int(100); 31 | * bb.write_byte(8); 32 | * 33 | * std::cout << bb.read_bit(0) << std::endl; // Outputs 1 (true) 34 | * std::cout << bb.read_bits(1, 2) << std::endl; // Outputs 2 35 | * std::cout << bb.read_bit(3) << std::endl; // Outputs 0 (false) 36 | * std::cout << bb.read_bits(4, 4) << std::endl; // Outputs 10 37 | * std::cout << bb.read_bytes(1, 4) << std::endl; // Outputs 100 38 | * std::cout << bb.read_byte(5) << std::endl; // Outputs 8 39 | */ 40 | class bit_buffer { 41 | friend class bit_iterator; 42 | typedef bit_iterator iterator; 43 | typedef const bit_iterator const_iterator; 44 | 45 | private: 46 | /* 47 | * Private type definitions 48 | */ 49 | typedef uint8_t ubyte_t; 50 | typedef std::vector bytes_t; 51 | 52 | /* 53 | * Array of bytes that handles the storage of all bytes 54 | * for this class 55 | */ 56 | bytes_t buffer_; 57 | uint32_t pos_; 58 | uint8_t bit_index_; 59 | 60 | /* 61 | * Appends the data parameter's bits to the buffer (bytes array) 62 | */ 63 | void write_bits_(const uint32_t data, const size_t bits) { 64 | static constexpr uint8_t bits_per_byte = 8; 65 | 66 | if (this->bit_index_ == 0 && bits > 0) { 67 | this->buffer_.push_back(0); 68 | } 69 | 70 | // Not enough bits in this byte to write all the bits required 71 | if (this->bit_index_ + bits > bits_per_byte) { 72 | const uint8_t remainder_bits = this->bit_index_ + bits - bits_per_byte; 73 | const uint32_t remainder_value = (data & (0xFF >> (bits_per_byte - remainder_bits))); 74 | 75 | this->buffer_[this->pos_] |= (data >> remainder_bits); 76 | 77 | this->bit_index_ = 0; 78 | this->pos_++; 79 | 80 | // Process the remaining bits in the next byte 81 | this->write_bits_(remainder_value, remainder_bits); 82 | 83 | return; 84 | } 85 | 86 | const uint8_t offset = bits_per_byte - this->bit_index_ - bits; 87 | this->buffer_[this->pos_] |= (data << offset); 88 | this->bit_index_ += bits; 89 | 90 | if (this->bit_index_ == bits_per_byte) { 91 | this->bit_index_ = 0; 92 | this->pos_++; 93 | } 94 | } 95 | 96 | template 97 | inline void write_(const T& data) { 98 | size_t bytes = sizeof(T); 99 | 100 | this->write_bytes_(data, bytes); 101 | } 102 | 103 | template 104 | inline void write_bytes_(const T& data, size_t bytes) { 105 | const uint32_t temp = static_cast(data); 106 | 107 | // If beginning of a byte, then it can just push bytes 108 | if (this->bit_index_ == 0) { 109 | int i = bytes - 1; 110 | while (i >= 0) { 111 | this->buffer_.push_back(static_cast((temp >> (i * 8)))); 112 | --i; 113 | } 114 | 115 | this->pos_ += bytes; 116 | return; 117 | } 118 | 119 | // If the current byte has a few bits written to it already, but 120 | // not all eight bits, then it must process the data bit by bit 121 | this->write_bits_(data, bytes * 8); 122 | } 123 | 124 | uint32_t read_bits_(const size_t bit_index, const size_t num_bits, size_t ret) const { 125 | if (bit_index + num_bits > this->buffer_.size() * 8) { 126 | throw; 127 | } 128 | 129 | uint32_t pos = static_cast(bit_index / 8); 130 | uint8_t bit_index_start = static_cast(bit_index - (pos * 8)); 131 | uint32_t bit_index_end = bit_index_start + num_bits - 1; 132 | 133 | // If we exceeded the number of bits that can be read 134 | // from this byte, then move to the next byte and 135 | // continue reading the bits from that byte 136 | if (bit_index_end >= 8) { 137 | ubyte_t byte = this->buffer_[pos]; 138 | int offset = 8 - num_bits - bit_index_start; 139 | if (offset < 0) { 140 | ubyte_t mask = (0xFF >> bit_index_start); 141 | byte &= mask; 142 | } else { 143 | byte >>= offset; 144 | } 145 | 146 | //ret += byte; 147 | uint32_t bits_read = 8 - bit_index_start; 148 | uint32_t p = num_bits - bits_read; 149 | offset = 0; 150 | while (p < num_bits) { 151 | ret += static_cast(((byte >> offset) & 0x01) * pow(2, p)); 152 | ++p; 153 | ++offset; 154 | } 155 | 156 | return read_bits_(bit_index + bits_read, num_bits - bits_read, ret); 157 | } 158 | 159 | 160 | // Remove everything in front of the starting bit 161 | ubyte_t byte = this->buffer_[pos]; 162 | if (bit_index_start > 0) { 163 | ubyte_t mask = ~(0xFF << (8 - bit_index_start)); 164 | byte &= mask; 165 | } 166 | 167 | byte >>= (8 - num_bits - bit_index_start); 168 | ret += static_cast(byte); 169 | 170 | return ret; 171 | } 172 | 173 | inline uint32_t read_bytes_(const size_t byte_index, const size_t num_bytes) { 174 | if (byte_index + num_bytes > this->buffer_.size()) { 175 | throw; 176 | } 177 | 178 | return this->read_bits_(byte_index * 8, num_bytes * 8, 0); 179 | } 180 | 181 | public: 182 | bit_buffer(const size_t size = DEFAULT_SIZE); 183 | virtual ~bit_buffer(); 184 | 185 | /* 186 | * The methods below write the data to the byte 187 | * array 188 | */ 189 | 190 | template 191 | inline void write(const T& data) { 192 | this->write_(data); 193 | } 194 | 195 | template 196 | inline void write_bits(const T& data, const size_t num_bits) { 197 | this->write_bits_(static_cast(data), num_bits); 198 | } 199 | 200 | void write_byte(const ubyte_t& data); 201 | void write_char(const char& data); 202 | void write_bool(const bool& data); 203 | void write_short(const short& data); 204 | void write_int(const uint32_t& data); 205 | void write_long(const uint64_t& data); 206 | 207 | /* 208 | * Returns the value of the bytes starting at byte_index and 209 | * ending at (byte_index + num_bytes - 1) 210 | * 211 | * For example: 212 | * bit_buffer bf; 213 | * // If two bytes were written to the buffer 214 | * bf.write_byte(10); 215 | * bf.write_int(20); // 4 bytes 216 | * 217 | * std::cout << bf.read_byte(0) << std::endl; // Outputs 10 218 | * std::cout << bf.read_bytes(1, 4) << std::endl; // Outputs 20 219 | */ 220 | uint8_t read_byte(const size_t byte_index); 221 | uint32_t read_bytes(const size_t byte_index, const size_t num_bytes); 222 | 223 | /* 224 | * Returns the value of the bits starting at bit_index and 225 | * ending at (bit_index + num_bits - 1) 226 | * 227 | * For example: 228 | * bit_buffer bf; 229 | * // If two bytes were written to the buffer 230 | * bf.write_byte(10); 231 | * bf.write_byte(8); 232 | * bf.write_bits(5, 3); 233 | * 234 | * std::cout << bf.read_bits(0, 4) << std::endl; // Outputs 10 235 | * std::cout << bf.read_bits(4, 4) << std::endl; // Outputs 8 236 | * std::cout << bf.read_bits(8, 3) << std::endl; // Outputs 5 237 | */ 238 | uint8_t read_bit(const size_t bit_index); 239 | uint32_t read_bits(const size_t bit_index, const size_t num_bits); 240 | 241 | inline const bytes_t get_bytes() const { return this->buffer_; } 242 | bit_iterator create_iter() const; 243 | 244 | 245 | /* 246 | * For each loop iterator 247 | */ 248 | iterator begin(); 249 | const_iterator begin() const; 250 | iterator end(); 251 | const_iterator end() const; 252 | 253 | /* 254 | * Operator overrides 255 | */ 256 | bool operator==(const bit_buffer &other) { 257 | return this->buffer_ == other.buffer_; 258 | } 259 | 260 | bool operator!=(const bit_buffer &other) { 261 | return this->buffer_ != other.buffer_; 262 | } 263 | }; 264 | 265 | class bit_iterator { 266 | private: 267 | bit_buffer buffer_; 268 | size_t bit_index_; 269 | 270 | public: 271 | explicit bit_iterator(const bit_buffer& buffer) : 272 | buffer_(buffer), bit_index_(0) 273 | {} 274 | 275 | bit_iterator(const bit_buffer& buffer, const size_t bit_index) : 276 | buffer_(buffer), bit_index_(bit_index) 277 | {} 278 | 279 | inline uint8_t current_bit() const { 280 | return static_cast(this->buffer_.read_bits_(this->bit_index_, 1, 0)); 281 | } 282 | 283 | /* 284 | * Operator overrides 285 | */ 286 | bit_iterator &operator++() { 287 | ++this->bit_index_; 288 | 289 | return *this; 290 | } 291 | 292 | bit_iterator operator++(int) { 293 | bit_iterator temp = *this; 294 | ++*this; 295 | 296 | return temp; 297 | } 298 | 299 | bool operator==(const bit_iterator &other) { 300 | return this->bit_index_ == other.bit_index_; 301 | } 302 | 303 | bool operator!=(const bit_iterator &other) { 304 | return this->bit_index_ != other.bit_index_; 305 | } 306 | 307 | uint8_t operator*() { 308 | return this->current_bit(); 309 | } 310 | 311 | uint8_t operator*() const { 312 | return this->current_bit(); 313 | } 314 | }; 315 | 316 | #endif 317 | --------------------------------------------------------------------------------