MIL-STD-188-110C/include/bitstream/traits/array_traits.h

165 lines
5.4 KiB
C++

#pragma once
#include "../utility/assert.h"
#include "../utility/meta.h"
#include "../utility/parameter.h"
#include "../stream/serialize_traits.h"
#include "../traits/bool_trait.h"
#include "../traits/integral_traits.h"
#include <cstdint>
namespace bitstream
{
/**
* @brief Wrapper type for subsets of arrays
* @tparam T The type of the array
*/
template<typename T, typename = T>
struct array_subset;
/**
* @brief A trait used for serializing a subset of an array of objects
* @tparam T The type of the object in the array
* @tparam Trait
*/
template<typename T, typename Trait>
struct serialize_traits<array_subset<T, Trait>>
{
private:
template<uint32_t Min, uint32_t Max, typename Stream>
static bool serialize_difference(Stream& stream, int& previous, int& current, uint32_t& difference)
{
bool use_bits;
if constexpr (Stream::writing)
use_bits = difference <= Max;
BS_ASSERT(stream.template serialize<bool>(use_bits));
if (use_bits)
{
using bounded_trait = bounded_int<uint32_t, Min, Max>;
BS_ASSERT(stream.template serialize<bounded_trait>(difference));
if constexpr (Stream::reading)
current = previous + difference;
previous = current;
return true;
}
return false;
}
template<typename Stream>
static bool serialize_index(Stream& stream, int& previous, int& current, int max_size)
{
uint32_t difference;
if constexpr (Stream::writing)
{
BS_ASSERT(previous < current);
difference = current - previous;
BS_ASSERT(difference > 0);
}
// +1 (1 bit)
bool plus_one;
if constexpr (Stream::writing)
plus_one = difference == 1;
BS_ASSERT(stream.template serialize<bool>(plus_one));
if (plus_one)
{
if constexpr (Stream::reading)
current = previous + 1;
previous = current;
return true;
}
// [+2,5] -> [0,3] (2 bits)
if (serialize_difference<2, 5>(stream, previous, current, difference))
return true;
// [6,13] -> [0,7] (3 bits)
if (serialize_difference<6, 13>(stream, previous, current, difference))
return true;
// [14,29] -> [0,15] (4 bits)
if (serialize_difference<14, 29>(stream, previous, current, difference))
return true;
// [30,61] -> [0,31] (5 bits)
if (serialize_difference<30, 61>(stream, previous, current, difference))
return true;
// [62,125] -> [0,63] (6 bits)
if (serialize_difference<62, 125>(stream, previous, current, difference))
return true;
// [126,MaxObjects+1]
BS_ASSERT(stream.template serialize<uint32_t>(difference, 126, max_size));
if constexpr (Stream::reading)
current = previous + difference;
previous = current;
return true;
}
public:
/**
* @brief Writes a subset of the array @p values into the writer
* @tparam Compare A function type which returns a bool
* @tparam ...Args The types of any additional arguments
* @param writer The stream to write to
* @param values The array of objects to serialize
* @param max_size The size of the array
* @param compare A function which returns true if the object should be written, false otherwise
* @param ...args Any additional arguments to use when serializing each individual object
* @return Success
*/
template<typename Stream, typename Compare, typename... Args>
typename utility::is_writing_t<Stream>
static serialize(Stream& writer, T* values, int max_size, Compare compare, Args&&... args) noexcept
{
int prev_index = -1;
for (int index = 0; index < max_size; index++)
{
if (!compare(values[index]))
continue;
BS_ASSERT(serialize_index(writer, prev_index, index, max_size));
BS_ASSERT(writer.template serialize<Trait>(values[index], std::forward<Args>(args)...));
}
BS_ASSERT(serialize_index(writer, prev_index, max_size, max_size));
return true;
}
/**
* @brief Writes a subset of a serialized array into @p values
* @tparam ...Args The types of any additional arguments
* @param reader The stream to read from
* @param values The array of objects to read into
* @param max_size The size of the array
* @param compare Not used, but kept for compatibility with the serialize write function
* @param ...args Any additional arguments to use when serializing each individual object
* @return Success
*/
template<typename Stream, typename... Args>
typename utility::is_reading_t<Stream>
static serialize(Stream& reader, T* values, int max_size, Args&&... args) noexcept
{
int prev_index = -1;
int index = 0;
while (true)
{
BS_ASSERT(serialize_index(reader, prev_index, index, max_size));
if (index == max_size)
break;
BS_ASSERT(reader.template serialize<Trait>(values[index], std::forward<Args>(args)...));
}
return true;
}
};
}