test-client-godot/modules/bitstream/bitstream.cpp
2020-01-24 19:56:51 +01:00

456 lines
10 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
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;
}