mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-27 14:17:37 +01:00
364 lines
14 KiB
C++
364 lines
14 KiB
C++
#ifndef INPUT_BUFFER_H
|
|
#define INPUT_BUFFER_H
|
|
|
|
/*************************************************************************/
|
|
/* data_buffer.h */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* PANDEMONIUM ENGINE */
|
|
/* https://github.com/Relintai/pandemonium_engine */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2022-present Péter Magyar. */
|
|
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
|
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* 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. */
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
@author AndreaCatania
|
|
*/
|
|
|
|
#include "core/object/class_db.h"
|
|
|
|
#include "bit_array.h"
|
|
|
|
class DataBuffer : public Object {
|
|
GDCLASS(DataBuffer, Object);
|
|
|
|
public:
|
|
enum DataType {
|
|
DATA_TYPE_BOOL,
|
|
DATA_TYPE_INT,
|
|
DATA_TYPE_UINT,
|
|
DATA_TYPE_REAL,
|
|
DATA_TYPE_POSITIVE_UNIT_REAL,
|
|
DATA_TYPE_UNIT_REAL,
|
|
DATA_TYPE_VECTOR2,
|
|
DATA_TYPE_NORMALIZED_VECTOR2,
|
|
DATA_TYPE_VECTOR3,
|
|
DATA_TYPE_NORMALIZED_VECTOR3,
|
|
// The only dynamic sized value.
|
|
DATA_TYPE_VARIANT
|
|
};
|
|
|
|
/// Compression level for the stored input data.
|
|
///
|
|
/// Depending on the data type and the compression level used the amount of
|
|
/// bits used and loss change.
|
|
///
|
|
///
|
|
/// ## Bool
|
|
/// Always use 1 bit
|
|
///
|
|
///
|
|
/// ## Int
|
|
/// COMPRESSION_LEVEL_0: 64 bits are used - Stores integers -9223372036854775808 / 9223372036854775807
|
|
/// COMPRESSION_LEVEL_1: 32 bits are used - Stores integers -2147483648 / 2147483647
|
|
/// COMPRESSION_LEVEL_2: 16 bits are used - Stores integers -32768 / 32767
|
|
/// COMPRESSION_LEVEL_3: 8 bits are used - Stores integers -128 / 127
|
|
///
|
|
///
|
|
/// ## Real
|
|
/// Precision depends on an integer range
|
|
/// COMPRESSION_LEVEL_0: 64 bits are used - Double precision. Up to 16 precision is 0.00000000000000177636 in worst case. Up to 512 precision is 0.00000000000005684342 in worst case. Up to 1024 precision is 0.00000000000011368684 in worst case.
|
|
/// COMPRESSION_LEVEL_1: 32 bits are used - Single precision (float). Up to 16 precision is 0.00000095367431640625 in worst case. Up to 512 precision is 0.000030517578125 in worst case. Up to 1024 precision is 0.00006103515625 in worst case.
|
|
/// COMPRESSION_LEVEL_2: 16 bits are used - Half precision. Up to 16 precision is 0.0078125 in worst case. Up to 512 precision is 0.25 in worst case. Up to 1024 precision is 0.5.
|
|
/// COMPRESSION_LEVEL_3: 8 bits are used - Minifloat: Up to 2 precision is 0.125. Up to 4 precision is 0.25. Up to 8 precision is 0.5.
|
|
///
|
|
/// To get the exact precision for the stored number, you need to find the lower power of two relative to the number and divide it by 2^mantissa_bits.
|
|
/// To get the mantissa or exponent bits for a specific compression level, you can use the get_mantissa_bits and get_exponent_bits functions.
|
|
///
|
|
///
|
|
/// ## Positive unit real
|
|
/// COMPRESSION_LEVEL_0: 10 bits are used - Max loss ~0.005%
|
|
/// COMPRESSION_LEVEL_1: 8 bits are used - Max loss ~0.020%
|
|
/// COMPRESSION_LEVEL_2: 6 bits are used - Max loss ~0.793%
|
|
/// COMPRESSION_LEVEL_3: 4 bits are used - Max loss ~3.333%
|
|
///
|
|
///
|
|
/// ## Unit real (uses one extra bit for the sign)
|
|
/// COMPRESSION_LEVEL_0: 11 bits are used - Max loss ~0.005%
|
|
/// COMPRESSION_LEVEL_1: 9 bits are used - Max loss ~0.020%
|
|
/// COMPRESSION_LEVEL_2: 7 bits are used - Max loss ~0.793%
|
|
/// COMPRESSION_LEVEL_3: 5 bits are used - Max loss ~3.333%
|
|
///
|
|
///
|
|
/// ## Vector2
|
|
/// COMPRESSION_LEVEL_0: 2 * 64 bits are used - Double precision (will fallback to level 1 if REAL_T_IS_DOUBLE is not defined)
|
|
/// COMPRESSION_LEVEL_1: 2 * 32 bits are used - Single precision
|
|
/// COMPRESSION_LEVEL_2: 2 * 16 bits are used - Half precision
|
|
/// COMPRESSION_LEVEL_3: 2 * 8 bits are used - Minifloat
|
|
///
|
|
/// For floating point precision, check the Real compression section.
|
|
///
|
|
///
|
|
/// ## Normalized Vector2
|
|
/// COMPRESSION_LEVEL_0: 12 bits are used - Max loss 0.17°
|
|
/// COMPRESSION_LEVEL_1: 11 bits are used - Max loss 0.35°
|
|
/// COMPRESSION_LEVEL_2: 10 bits are used - Max loss 0.7°
|
|
/// COMPRESSION_LEVEL_3: 9 bits are used - Max loss 1.1°
|
|
///
|
|
///
|
|
/// ## Vector3
|
|
/// COMPRESSION_LEVEL_0: 3 * 64 bits are used - Double precision (will fallback to level 1 if REAL_T_IS_DOUBLE is not defined)
|
|
/// COMPRESSION_LEVEL_1: 3 * 32 bits are used - Single precision
|
|
/// COMPRESSION_LEVEL_2: 3 * 16 bits are used - Half precision
|
|
/// COMPRESSION_LEVEL_3: 3 * 8 bits are used - Minifloat
|
|
///
|
|
/// For floating point precision, check the Real compression section.
|
|
///
|
|
///
|
|
/// ## Normalized Vector3
|
|
/// COMPRESSION_LEVEL_0: 11 * 3 bits are used - Max loss ~0.005% per axis
|
|
/// COMPRESSION_LEVEL_1: 9 * 3 bits are used - Max loss ~0.020% per axis
|
|
/// COMPRESSION_LEVEL_2: 7 * 3 bits are used - Max loss ~0.793% per axis
|
|
/// COMPRESSION_LEVEL_3: 5 * 3 bits are used - Max loss ~3.333% per axis
|
|
///
|
|
/// ## Variant
|
|
/// It's dynamic sized. It's not possible to compress it.
|
|
enum CompressionLevel {
|
|
COMPRESSION_LEVEL_0,
|
|
COMPRESSION_LEVEL_1,
|
|
COMPRESSION_LEVEL_2,
|
|
COMPRESSION_LEVEL_3
|
|
};
|
|
|
|
private:
|
|
int metadata_size = 0;
|
|
int bit_offset = 0;
|
|
int bit_size = 0;
|
|
bool is_reading = false;
|
|
BitArray buffer;
|
|
|
|
#if DEBUG_ENABLED
|
|
bool debug_enabled = true;
|
|
#endif
|
|
|
|
public:
|
|
static void _bind_methods();
|
|
|
|
DataBuffer() = default;
|
|
DataBuffer(const DataBuffer &p_other);
|
|
DataBuffer(const BitArray &p_buffer);
|
|
|
|
void copy(const DataBuffer &p_other);
|
|
void copy(const BitArray &p_buffer);
|
|
|
|
const BitArray &get_buffer() const {
|
|
return buffer;
|
|
}
|
|
|
|
BitArray &get_buffer_mut() {
|
|
return buffer;
|
|
}
|
|
|
|
/// Begin write.
|
|
void begin_write(int p_metadata_size);
|
|
|
|
/// Make sure the buffer takes least space possible.
|
|
void dry();
|
|
|
|
/// Seek the offset to a specific bit. Seek to a bit greater than the actual
|
|
/// size is not allowed.
|
|
void seek(int p_bits);
|
|
|
|
/// Set the bit size and the metadata size.
|
|
void shrink_to(int p_metadata_bit_size, int p_bit_size);
|
|
|
|
/// Returns the metadata size in bits.
|
|
int get_metadata_size() const;
|
|
/// Returns the buffer size in bits
|
|
int size() const;
|
|
/// Total size in bits.
|
|
int total_size() const;
|
|
|
|
/// Returns the bit offset.
|
|
int get_bit_offset() const;
|
|
|
|
/// Skip n bits.
|
|
void skip(int p_bits);
|
|
|
|
/// Begin read.
|
|
void begin_read();
|
|
|
|
/// Add a boolean to the buffer.
|
|
/// Returns the same data.
|
|
bool add_bool(bool p_input);
|
|
|
|
/// Parse the next data as boolean.
|
|
bool read_bool();
|
|
|
|
/// Add the next data as int.
|
|
int64_t add_int(int64_t p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse the next data as int.
|
|
int64_t read_int(CompressionLevel p_compression_level);
|
|
|
|
/// Add the next data as uint
|
|
uint64_t add_uint(uint64_t p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse the next data as uint.
|
|
uint64_t read_uint(CompressionLevel p_compression_level);
|
|
|
|
/// Add a real into the buffer. Depending on the compression level is possible
|
|
/// to store different range level.
|
|
/// The fractional part has a precision of ~0.3%
|
|
///
|
|
/// Returns the compressed value so both the client and the peers can use
|
|
/// the same data.
|
|
double add_real(double p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse the following data as a real.
|
|
double read_real(CompressionLevel p_compression_level);
|
|
|
|
/// Add a positive unit real into the buffer.
|
|
///
|
|
/// **Note:** Not unitary values lead to unexpected behaviour.
|
|
///
|
|
/// Returns the compressed value so both the client and the peers can use
|
|
/// the same data.
|
|
real_t add_positive_unit_real(real_t p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse the following data as a positive unit real.
|
|
real_t read_positive_unit_real(CompressionLevel p_compression_level);
|
|
|
|
/// Add a unit real into the buffer.
|
|
///
|
|
/// **Note:** Not unitary values lead to unexpected behaviour.
|
|
///
|
|
/// Returns the compressed value so both the client and the peers can use
|
|
/// the same data.
|
|
real_t add_unit_real(real_t p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse the following data as an unit real.
|
|
real_t read_unit_real(CompressionLevel p_compression_level);
|
|
|
|
/// Add a vector2 into the buffer.
|
|
/// Note: This kind of vector occupies more space than the normalized verison.
|
|
/// Consider use a normalized vector to save bandwidth if possible.
|
|
///
|
|
/// Returns the decompressed vector so both the client and the peers can use
|
|
/// the same data.
|
|
Vector2 add_vector2(Vector2 p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse next data as vector from the input buffer.
|
|
Vector2 read_vector2(CompressionLevel p_compression_level);
|
|
|
|
/// Add a normalized vector2 into the buffer.
|
|
/// Note: The compression algorithm rely on the fact that this is a
|
|
/// normalized vector. The behaviour is unexpected for not normalized vectors.
|
|
///
|
|
/// Returns the decompressed vector so both the client and the peers can use
|
|
/// the same data.
|
|
Vector2 add_normalized_vector2(Vector2 p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse next data as normalized vector from the input buffer.
|
|
Vector2 read_normalized_vector2(CompressionLevel p_compression_level);
|
|
|
|
/// Add a vector3 into the buffer.
|
|
/// Note: This kind of vector occupies more space than the normalized verison.
|
|
/// Consider use a normalized vector to save bandwidth if possible.
|
|
///
|
|
/// Returns the decompressed vector so both the client and the peers can use
|
|
/// the same data.
|
|
Vector3 add_vector3(Vector3 p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse next data as vector3 from the input buffer.
|
|
Vector3 read_vector3(CompressionLevel p_compression_level);
|
|
|
|
/// Add a normalized vector3 into the buffer.
|
|
/// Note: The compression algorithm rely on the fact that this is a
|
|
/// normalized vector. The behaviour is unexpected for not normalized vectors.
|
|
///
|
|
/// Returns the decompressed vector so both the client and the peers can use
|
|
/// the same data.
|
|
Vector3 add_normalized_vector3(Vector3 p_input, CompressionLevel p_compression_level);
|
|
|
|
/// Parse next data as normalized vector3 from the input buffer.
|
|
Vector3 read_normalized_vector3(CompressionLevel p_compression_level);
|
|
|
|
/// Add a variant. This is the only supported dynamic sized value.
|
|
Variant add_variant(const Variant &p_input);
|
|
|
|
/// Parse the next data as Variant and returns it.
|
|
Variant read_variant();
|
|
|
|
/// Puts all the bytes to 0.
|
|
void zero();
|
|
|
|
/** Skips the amount of bits a type takes. */
|
|
|
|
void skip_bool();
|
|
void skip_int(CompressionLevel p_compression);
|
|
void skip_uint(CompressionLevel p_compression);
|
|
void skip_real(CompressionLevel p_compression);
|
|
void skip_positive_unit_real(CompressionLevel p_compression);
|
|
void skip_unit_real(CompressionLevel p_compression);
|
|
void skip_vector2(CompressionLevel p_compression);
|
|
void skip_normalized_vector2(CompressionLevel p_compression);
|
|
void skip_vector3(CompressionLevel p_compression);
|
|
void skip_normalized_vector3(CompressionLevel p_compression);
|
|
|
|
/** Just returns the size of a specific type. */
|
|
|
|
int get_bool_size() const;
|
|
int get_int_size(CompressionLevel p_compression) const;
|
|
int get_uint_size(CompressionLevel p_compression) const;
|
|
int get_real_size(CompressionLevel p_compression) const;
|
|
int get_positive_unit_real_size(CompressionLevel p_compression) const;
|
|
int get_unit_real_size(CompressionLevel p_compression) const;
|
|
int get_vector2_size(CompressionLevel p_compression) const;
|
|
int get_normalized_vector2_size(CompressionLevel p_compression) const;
|
|
int get_vector3_size(CompressionLevel p_compression) const;
|
|
int get_normalized_vector3_size(CompressionLevel p_compression) const;
|
|
|
|
/** Read the size and pass to the next parameter. */
|
|
|
|
int read_bool_size();
|
|
int read_int_size(CompressionLevel p_compression);
|
|
int read_uint_size(CompressionLevel p_compression);
|
|
int read_real_size(CompressionLevel p_compression);
|
|
int read_positive_unit_real_size(CompressionLevel p_compression);
|
|
int read_unit_real_size(CompressionLevel p_compression);
|
|
int read_vector2_size(CompressionLevel p_compression);
|
|
int read_normalized_vector2_size(CompressionLevel p_compression);
|
|
int read_vector3_size(CompressionLevel p_compression);
|
|
int read_normalized_vector3_size(CompressionLevel p_compression);
|
|
int read_variant_size();
|
|
|
|
static int get_bit_taken(DataType p_data_type, CompressionLevel p_compression);
|
|
static int get_mantissa_bits(CompressionLevel p_compression);
|
|
static int get_exponent_bits(CompressionLevel p_compression);
|
|
|
|
private:
|
|
static uint64_t compress_unit_float(double p_value, double p_scale_factor);
|
|
static double decompress_unit_float(uint64_t p_value, double p_scale_factor);
|
|
|
|
void make_room_in_bits(int p_dim);
|
|
void make_room_pad_to_next_byte();
|
|
bool pad_to_next_byte();
|
|
};
|
|
|
|
VARIANT_ENUM_CAST(DataBuffer::DataType)
|
|
VARIANT_ENUM_CAST(DataBuffer::CompressionLevel)
|
|
|
|
#endif
|