/* Copyright (C) 2019 AleaJactaEst This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Build : scons platform=linux bits=64 */ #include "bitstream.h" // TODO - check if on godot we have a function or variable to get if processor is little_endian or not bool little_endian = false; void BitStream::_bind_methods() { BitStream::init(); ClassDB::bind_method(D_METHOD("is_little_endian"), &BitStream::is_little_endian); ClassDB::bind_method(D_METHOD("size"), &BitStream::size); ClassDB::bind_method(D_METHOD("put_serial", "value", "nbits"), &BitStream::put_serial); ClassDB::bind_method(D_METHOD("put_bool", "value"), &BitStream::put_bool); ClassDB::bind_method(D_METHOD("put_char", "value"), &BitStream::put_char); ClassDB::bind_method(D_METHOD("put_uint8", "value"), &BitStream::put_uint8); ClassDB::bind_method(D_METHOD("put_sint8", "value"), &BitStream::put_sint8); ClassDB::bind_method(D_METHOD("put_sint16", "value"), &BitStream::put_sint16); ClassDB::bind_method(D_METHOD("put_uint16", "value"), &BitStream::put_uint16); ClassDB::bind_method(D_METHOD("put_sint32", "value"), &BitStream::put_sint32); ClassDB::bind_method(D_METHOD("put_uint32", "value"), &BitStream::put_uint32); ClassDB::bind_method(D_METHOD("put_sint64", "value"), &BitStream::put_sint64); ClassDB::bind_method(D_METHOD("put_uint64", "value"), &BitStream::put_uint64); ClassDB::bind_method(D_METHOD("put_string", "value"), &BitStream::put_string); ClassDB::bind_method(D_METHOD("put_string_hexa32", "hexa"), &BitStream::put_string_hexa32); ClassDB::bind_method(D_METHOD("put_array_uint8", "value"), &BitStream::put_array_uint8); ClassDB::bind_method(D_METHOD("show"), &BitStream::show); ClassDB::bind_method(D_METHOD("show_detail"), &BitStream::show_detail); ClassDB::bind_method(D_METHOD("show_counter"), &BitStream::show_counter); ClassDB::bind_method(D_METHOD("get_data"), &BitStream::get_data); ClassDB::bind_method(D_METHOD("put_data", "value"), &BitStream::put_data); ClassDB::bind_method(D_METHOD("get_serial", "nbits"), &BitStream::get_serial); ClassDB::bind_method(D_METHOD("get_bool"), &BitStream::get_bool); ClassDB::bind_method(D_METHOD("get_sint8"), &BitStream::get_sint8); ClassDB::bind_method(D_METHOD("get_uint8"), &BitStream::get_uint8); ClassDB::bind_method(D_METHOD("get_sint16"), &BitStream::get_sint16); ClassDB::bind_method(D_METHOD("get_uint16"), &BitStream::get_uint16); ClassDB::bind_method(D_METHOD("get_sint32"), &BitStream::get_sint32); ClassDB::bind_method(D_METHOD("get_uint32"), &BitStream::get_uint32); ClassDB::bind_method(D_METHOD("get_sint64"), &BitStream::get_sint64); ClassDB::bind_method(D_METHOD("get_uint64"), &BitStream::get_uint64); ClassDB::bind_method(D_METHOD("get_array_uint8", "length"), &BitStream::get_array_uint8); } void BitStream::clear() { this->_pos = 0; this->_read = 0; } BitStream::BitStream() { clear(); } BitStream::~BitStream() { // add your cleanup here } void BitStream::init() { uint32_t i=0x12345678; if ( (*((uint8_t*)(&i))) == 0x78 ) little_endian = true; else little_endian = false; } bool BitStream::is_little_endian() { return little_endian; } int BitStream::size() { return (this->_pos + 7) / 8; } int BitStream::size_data() { return this->_pos; } void BitStream::put_serial(uint32_t value, uint32_t nbits) { uint32_t v; uint32_t mask; uint32_t freeBits; uint32_t pos; if ( nbits == 0 ) return; else if ( nbits > 32 ) throw "Out of range"; if ( this->_pos % 8 == 0 ) // Increase node this->_data.append(0); if ( nbits != 32 ) { mask = (1 << nbits) - 1; v = value & mask; } else { v = value; } freeBits = 8 - (this->_pos % 8); pos = this->_data.size() - 1; if(nbits > freeBits) { this->_data.set(pos, this->_data[pos] | (v >> (nbits - freeBits))); this->_pos += freeBits; this->put_serial(v, nbits - freeBits); } else { this->_data.set(pos, this->_data[pos] | (v << (freeBits - nbits))); this->_pos += nbits; } } void BitStream::put_bool(bool value) { this->put_serial(value, 1); } void BitStream::put_sint8(int8_t value) { this->put_serial(value, 8); } void BitStream::put_uint8(uint8_t value) { this->put_serial(value, 8); } void BitStream::put_sint16(int16_t value) { this->put_serial(value, 16); } void BitStream::put_uint16(uint16_t value) { this->put_serial(value, 16); } void BitStream::put_sint32(int32_t value) { this->put_serial(value, 32); } void BitStream::put_uint32(uint32_t value) { this->put_serial(value, 32); } void BitStream::put_sint64(int64_t value) { if(little_endian) { this->put_serial(value>>32, 32); this->put_serial(value, 32); } else { this->put_serial(value, 32); this->put_serial(value>>32, 32); } } void BitStream::put_uint64(uint64_t value) { if(little_endian) { this->put_serial(value>>32, 32); this->put_serial(value, 32); } else { this->put_serial(value, 32); this->put_serial(value>>32, 32); } } void BitStream::put_string_hexa32(String hexa) { uint32_t res = 0; CharString tmp = hexa.ascii(); for(const char * c = tmp.get_data(); *c != 0; c++) { res <<= 4; if ( *c >= '0' && *c <= '9') res += *c - '0'; else if ( *c >= 'A' && *c <= 'F') res += *c - 'A' + 10; } this->put_serial(res,32); } void BitStream::put_char(String value) { if ( value.length() >= 1) this->put_uint8( value[0] ); else this->put_uint8( 0 ); } void BitStream::put_string(String value) { CharString data = value.ascii(); int lenvalue = data.size() - 1; this->put_uint32(lenvalue); for(int i = 0; i < lenvalue; ++i) { this->put_uint8(data[i]); } } void BitStream::put_array_uint8(PoolByteArray value) { for(int i = 0; i < value.size() ; ++i) this->put_serial(value[i], 8); } String BitStream::show() { String ret; uint32_t pos = 0; for(int i = 0; i < this->_data.size(); ++i) { uint8_t c = this->_data[i]; uint8_t mask = 0x80; while(mask) { if ( (c & mask) != 0 ) ret += String("1"); else ret += String("0"); mask >>= 1; pos += 1; if (pos >= this->_pos) break; } if (pos >= this->_pos) break; } return ret ; } String BitStream::show_detail() { String ret; uint32_t pos = 0; for(int i = 0; i < this->_data.size(); ++i) { uint8_t c = this->_data[i]; uint8_t mask = 0x80; while(mask) { if ( (c & mask) != 0 ) ret += String("1"); else ret += String("0"); mask >>= 1; pos += 1; if (pos >= this->_pos) break; } if (pos >= this->_pos) break; } String strsize; uint32_t t = this->_data.size(); if ( t == 0) strsize = "0"; while ( t > 0 ) { const char c = '0' + ( t % 10 ); strsize = String(&c) + strsize; t /= 10; } String strpos; t = this->_pos; if ( t == 0) strpos = "0"; while ( t > 0 ) { const char c = '0' + ( t % 10 ); strpos = String(&c) + strpos; t /= 10; } return "[size:" + strsize + ", pos:" + strpos + "]" + ret ; } String BitStream::show_counter() { String ret = "[" + String::num_int64(this->_read) + " / " + String::num_int64(this->_pos) + "]"; return ret; } PoolByteArray BitStream::get_data() { return this->_data; } void BitStream::put_data(PoolByteArray value) { this->_data = value; this->_pos = value.size() * 8; } uint32_t BitStream::get_serial(uint32_t nbits) { uint32_t value; uint32_t pos; uint32_t freeBits; uint32_t v; if ( nbits == 0 ) return 0; else if ( nbits > 32 ) { ERR_PRINT("Out of range (BitStream::get_serial)"); throw "Out of range"; } if (this->_read + nbits > this->_pos) { ERR_PRINT("Out of range (BitStream::get_serial)"); throw "Out of range"; } value = 0; pos = this->_read / 8; freeBits = 8 - (this->_read % 8); v = this->_data[pos] & ((1 << freeBits) - 1); if(nbits > freeBits) { value |= (v << (nbits-freeBits)); this->_read += freeBits; value |= this->get_serial(nbits - freeBits); } else { value |= (v >> (freeBits-nbits)); this->_read += nbits; } return value; } bool BitStream::get_bool() { if(this->get_serial(1) == 0) return false; return true; } int8_t BitStream::get_sint8() { return this->get_serial(8); } uint8_t BitStream::get_uint8() { return this->get_serial(8); } int16_t BitStream::get_sint16() { return this->get_serial(16); } uint16_t BitStream::get_uint16() { return this->get_serial(16); } int32_t BitStream::get_sint32() { return this->get_serial(32); } uint32_t BitStream::get_uint32() { return this->get_serial(32); } int64_t BitStream::get_sint64() { int64_t v1; int64_t v2; if(little_endian) { v1 = this->get_serial(32); v2 = this->get_serial(32); } else { v2 = this->get_serial(32); v1 = this->get_serial(32); } return (v1 << 32) | v2; } uint64_t BitStream::get_uint64() { int64_t v1; int64_t v2; if(little_endian) { v1 = this->get_serial(32); v2 = this->get_serial(32); } else { v2 = this->get_serial(32); v1 = this->get_serial(32); } return (v1 << 32) | v2; } PoolByteArray BitStream::get_array_uint8(uint32_t length) { PoolByteArray ret; while(length != 0) { ret.append(this->get_serial(8)); --length; } return ret; }