Skip to content

Instantly share code, notes, and snippets.

@Unbinilium
Created July 16, 2025 06:03
Show Gist options
  • Save Unbinilium/8343d51396f0954ba90a3064fcc5d3d4 to your computer and use it in GitHub Desktop.
Save Unbinilium/8343d51396f0954ba90a3064fcc5d3d4 to your computer and use it in GitHub Desktop.
#pragma once
#ifndef ENCODER_HPP
#define ENCODER_HPP
#include <algorithm>
#include <array>
#include <cerrno>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <limits>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
template<typename T, typename P>
class Encoder
{
public:
using ValueType = typename T::ValueType;
using WriteCallback = typename T::WriteCallback;
using StateType = typename T::State;
~Encoder() noexcept = default;
StateType state() const noexcept
{
return static_cast<const P *>(this)->state();
}
template<typename U, std::enable_if_t<std::is_same_v<U, ValueType>, bool> = true>
int encode(const U *data, size_t size, WriteCallback write_callback) noexcept
{
return static_cast<P *>(this)->encode(data, size, write_callback);
}
protected:
Encoder() noexcept = default;
};
namespace encoder {
class ADPCM
{
public:
using ValueType = int16_t;
using WriteCallback = std::function<int(const void *data, size_t size)>;
struct State final
{
State() noexcept : predictor(0), step_index(0) { }
~State() noexcept = default;
ValueType predictor;
uint8_t step_index;
};
};
class ASCII
{
public:
using ValueType = unsigned char;
using WriteCallback = std::function<int(const void *data, size_t size)>;
struct State final
{
State() noexcept = default;
~State() noexcept = default;
};
};
} // namespace encoder
namespace detail {
using namespace encoder;
class ADPCMIMA final: public Encoder<ADPCM, ADPCMIMA>
{
public:
ADPCMIMA(void *buffer = nullptr, size_t buffer_size = 1024) noexcept
: _state(), _buffer(buffer), _buffer_size(buffer_size), _internal_buffer(false)
{
if (!_buffer && _buffer_size > 0)
{
_buffer = new (std::nothrow) std::byte[_buffer_size];
_internal_buffer = true;
}
}
~ADPCMIMA() noexcept
{
if (_internal_buffer && _buffer)
{
delete[] static_cast<std::byte *>(_buffer);
_buffer = nullptr;
}
}
StateType state() const noexcept
{
return _state;
}
int encode(const ValueType *data, size_t size, const WriteCallback &write_callback) noexcept
{
if (!data || size == 0 || size > std::numeric_limits<int>::max() || !write_callback) [[unlikely]]
{
return -EINVAL;
}
if (!_buffer) [[unlikely]]
{
return -ENOMEM;
}
size = (size >> 1) << 1;
size_t p = 0;
for (size_t i = 0; i < size; ++i)
{
uint8_t nibble = 0x0;
int diff = data[i] - _state.predictor;
if (diff < 0)
{
nibble = 0x8;
diff = ~diff + 1;
}
int step_size = _step_table[_state.step_index];
int predictor_diff = step_size >> 3;
if (diff >= step_size)
{
nibble |= 0x4;
predictor_diff += step_size;
diff -= step_size;
}
step_size >>= 1;
if (diff >= step_size)
{
nibble |= 0x2;
predictor_diff += step_size;
diff -= step_size;
}
step_size >>= 1;
if (diff >= step_size)
{
nibble |= 0x1;
predictor_diff += step_size;
}
if ((nibble & 0x8) == 0x8)
{
predictor_diff = ~predictor_diff + 1;
}
_state.predictor = std::clamp(_state.predictor + predictor_diff, -32768, 32767);
_state.step_index = std::clamp(static_cast<int>(_state.step_index + _index_table[nibble]), 0,
static_cast<int>(_step_table.size() - 1));
if (i & 1)
{
static_cast<std::byte *>(_buffer)[p++] |= static_cast<std::byte>(nibble);
}
else
{
static_cast<std::byte *>(_buffer)[p] = static_cast<std::byte>(nibble << 4);
}
if (p >= _buffer_size) [[unlikely]]
{
int res = write_callback(_buffer, _buffer_size);
if (res < 0) [[unlikely]]
{
return res;
}
p = 0;
}
}
if (p > 0)
{
int res = write_callback(_buffer, p);
if (res < 0) [[unlikely]]
{
return res;
}
}
return size;
}
private:
StateType _state;
void *_buffer;
size_t _buffer_size;
bool _internal_buffer;
static inline constexpr const std::array<int16_t, 89> _step_table = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21,
23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230,
253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707,
1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,
10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 };
static inline constexpr const std::array<int16_t, 16> _index_table
= { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 };
};
class ASCIIBase64 final: public Encoder<ASCII, ASCIIBase64>
{
public:
ASCIIBase64(void *buffer = nullptr, size_t buffer_size = 1024) noexcept
: _state(), _buffer(buffer), _buffer_size(buffer_size), _internal_buffer(false)
{
if (!_buffer && _buffer_size >= 4)
{
_buffer = new (std::nothrow) std::byte[_buffer_size];
_internal_buffer = true;
}
else if (_buffer_size < 4) [[unlikely]]
{
_buffer = nullptr;
_buffer_size = 0;
}
}
~ASCIIBase64() noexcept
{
if (_internal_buffer && _buffer)
{
delete[] static_cast<std::byte *>(_buffer);
_buffer = nullptr;
}
}
StateType state() const noexcept
{
return _state;
}
int encode(const ValueType *data, size_t size, const WriteCallback &write_callback) noexcept
{
if (!data || size == 0 || size > std::numeric_limits<int>::max() || !write_callback) [[unlikely]]
{
return -EINVAL;
}
if (!_buffer) [[unlikely]]
{
return -ENOMEM;
}
uint8_t bytes[4];
size_t p = 0;
const size_t len = static_cast<size_t>(size / 3) * 3;
for (size_t i = 0; i < len; i += 3)
{
auto b0 = static_cast<uint8_t>(data[i]);
auto b1 = static_cast<uint8_t>(data[i + 1]);
auto b2 = static_cast<uint8_t>(data[i + 2]);
bytes[0] = _base64_chars[b0 >> 2];
bytes[1] = _base64_chars[((b0 & 0x03) << 4) | (b1 >> 4)];
bytes[2] = _base64_chars[((b1 & 0x0F) << 2) | (b2 >> 6)];
bytes[3] = _base64_chars[b2 & 0x3f];
size_t next_p = p + 4;
if (next_p > _buffer_size) [[unlikely]]
{
int res = write_callback(_buffer, _buffer_size);
if (res < 0) [[unlikely]]
{
return res;
}
p = 0;
next_p = 4;
}
std::memcpy(static_cast<std::byte *>(_buffer) + p, bytes, 4);
p = next_p;
}
switch (size - len)
{
case 2: {
auto b0 = static_cast<uint8_t>(data[len]);
auto b1 = static_cast<uint8_t>(data[len + 1]);
bytes[0] = _base64_chars[b0 >> 2];
bytes[1] = _base64_chars[((b0 & 0x03) << 4) | (b1 >> 4)];
bytes[2] = _base64_chars[(b1 & 0x0F) << 2];
bytes[3] = '=';
size_t next_p = p + 4;
if (next_p > _buffer_size) [[unlikely]]
{
int res = write_callback(_buffer, _buffer_size);
if (res < 0) [[unlikely]]
{
return res;
}
p = 0;
next_p = 4;
}
std::memcpy(static_cast<std::byte *>(_buffer) + p, bytes, 4);
p = next_p;
break;
}
case 1: {
auto b0 = static_cast<uint8_t>(data[len]);
bytes[0] = _base64_chars[b0 >> 2];
bytes[1] = _base64_chars[(b0 & 0x03) << 4];
bytes[2] = '=';
bytes[3] = '=';
size_t next_p = p + 4;
if (next_p > _buffer_size) [[unlikely]]
{
int res = write_callback(_buffer, _buffer_size);
if (res < 0) [[unlikely]]
{
return res;
}
p = 0;
next_p = 4;
}
std::memcpy(static_cast<std::byte *>(_buffer) + p, bytes, 4);
p = next_p;
break;
}
default:
break;
}
if (p > 0)
{
int res = write_callback(_buffer, p);
if (res < 0) [[unlikely]]
{
return res;
}
}
return static_cast<int>(size);
}
private:
ASCII::State _state;
void *_buffer;
size_t _buffer_size;
bool _internal_buffer;
constexpr static inline const std::array<char, 64> _base64_chars = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e',
'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
};
} // namespace detail
using EncoderADPCMIMA = detail::ADPCMIMA;
using EncoderASCIIBase64 = detail::ASCIIBase64;
#endif // ENCODER_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment