Skip to content

Commit

Permalink
refactor and improve base16 encoding (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
cyberjunk authored Dec 15, 2024
1 parent f2c0b1e commit 4d669e4
Show file tree
Hide file tree
Showing 9 changed files with 613 additions and 599 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [macos-12,macos-13,macos-14]
os: [macos-13,macos-14]
arch: [arm64, x64]
cpurev: [default]
steps:
Expand Down
17 changes: 17 additions & 0 deletions include/CppCore.Interface.C/cppcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
CPPCORE_EXPORT int cppcore_basex_encode##size(void* in, char* out, int len, unsigned int base, char* alphabet, unsigned int writeterm); \
CPPCORE_EXPORT unsigned int cppcore_basex_decode##size(char* in, void* out, char* alphabet);

// macro for base16 function declarations
#define CPPCORE_BASE16_DECLARATION(size) \
CPPCORE_EXPORT void cppcore_base16_encode##size(void* in, char* out, unsigned int bigendian, unsigned int writeterm, unsigned int uppercase); \
CPPCORE_EXPORT unsigned int cppcore_base16_decode##size(char* in, unsigned int len, void* out, unsigned int bigendian);

// macro for hash function declarations
#define CPPCORE_HASH_DECLARATION(name) \
typedef struct _ ## name name; \
Expand Down Expand Up @@ -128,6 +133,18 @@ extern "C" {
CPPCORE_BASEX_DECLARATION(4096)
CPPCORE_BASEX_DECLARATION(8192)

// base16

CPPCORE_BASE16_DECLARATION(32)
CPPCORE_BASE16_DECLARATION(64)
CPPCORE_BASE16_DECLARATION(128)
CPPCORE_BASE16_DECLARATION(256)
CPPCORE_BASE16_DECLARATION(512)
CPPCORE_BASE16_DECLARATION(1024)
CPPCORE_BASE16_DECLARATION(2048)
CPPCORE_BASE16_DECLARATION(4096)
CPPCORE_BASE16_DECLARATION(8192)

// hash

CPPCORE_HASH_DECLARATION(cppcore_md5)
Expand Down
296 changes: 183 additions & 113 deletions include/CppCore.Test/Encoding.h

Large diffs are not rendered by default.

687 changes: 289 additions & 398 deletions include/CppCore/Encoding.h

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions include/CppCore/Math/BigInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,14 @@ namespace CppCore
/// </summary>
constexpr INLINE uintx_t(const int32_t v) : uintx_t((uint32_t)v) { }

/// <summary>
/// Constructor from Hex string
/// </summary>
INLINE uintx_t(const ::std::string& input, bool bigendian)
{
CppCore::Hex::parse(input, *thiss(), bigendian);
}

/// <summary>
/// Constructor from Decimal string
/// </summary>
Expand Down Expand Up @@ -2631,17 +2639,13 @@ namespace CppCore
}

/// <summary>
/// Returns a fixed-length, leading zero padded string representation in Hex (Base16). Optimized.
/// Returns a fixed-length (zero padded) string representation in Hex (Base16). Optimized.
/// </summary>
INLINE string toHexString() const
INLINE string toHexString(bool bigendian = true) const
{
string s;
s.resize(N64 * 16U);
#if defined(CPPCORE_CPU_64BIT)
CppCore::Hex::tostring(d.i64, s.data(), N64, false);
#else
CppCore::Hex::tostring(d.i32, s.data(), N32, false);
#endif
CppCore::Hex::tostring(*thiss(), s.data(), bigendian, true);
return s;
}

Expand Down Expand Up @@ -2694,14 +2698,10 @@ namespace CppCore
/// Unlike tryParse(), it does not support any alphabet and it does not detect
/// errors like invalid symbols or overflows. But it's faster!
/// </summary>
constexpr INLINE static TC parseHex(const char* input)
constexpr INLINE static TC parseHex(const char* input, bool bigendian = true)
{
TC r(0ULL);
while(const char c = *input++)
{
r <<= 4;
r.d.i32[0] |= Hex::Util::valueofhexchar(c);
}
TC r;
CppCore::Hex::parse(input, r, bigendian);
return r;
}

Expand Down
73 changes: 12 additions & 61 deletions include/CppCore/String.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,22 +188,6 @@ namespace CppCore
return *thiss();
}

/// <summary>
/// Helper for appending hex number.
/// </summary>
template<typename T, size_t N>
INLINE S& _appendHex(const T v, const bool bigendian)
{
const auto LENOLD = mLength;
const auto LENNEW = LENOLD + N;
if (thiss()->available() < N)
thiss()->resize(LENNEW);
Hex::tostring(v, mData + LENOLD, bigendian, true);
mLength = LENNEW; // always writes N symbols
//mLength = Hex::tostring(v, mData + LENOLD, true) - mData;;
return *thiss();
}

/// <summary>
/// Helper for appending number in any encoding.
/// </summary>
Expand Down Expand Up @@ -1075,64 +1059,31 @@ namespace CppCore
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// <summary>
/// Append v in hex encoding.
/// </summary>
INLINE S& appendHex(const uint16_t v, const bool bigendian = true)
{
return thiss()->template _appendHex<uint16_t, CPPCORE_MAXLENGTH_B16_16>(v, bigendian);
}

/// <summary>
/// Append v in hex encoding.
/// </summary>
INLINE S& appendHex(const uint32_t v, const bool bigendian = true)
{
return thiss()->template _appendHex<uint32_t, CPPCORE_MAXLENGTH_B16_32>(v, bigendian);
}

/// <summary>
/// Append v in hex encoding.
/// Append integer in hex encoding.
/// </summary>
INLINE S& appendHex(const uint64_t v, const bool bigendian = true)
template<typename T, size_t N = sizeof(T)*2>
INLINE S& appendHex(const T v, const bool bigendian = true)
{
return thiss()->template _appendHex<uint64_t, CPPCORE_MAXLENGTH_B16_64>(v, bigendian);
}

/// <summary>
/// Append v in hex encoding.
/// </summary>
INLINE S& appendHex(const int16_t v, const bool bigendian = true)
{
return thiss()->appendHex((uint16_t)v, bigendian);
}

/// <summary>
/// Append v in hex encoding.
/// </summary>
INLINE S& appendHex(const int32_t v, const bool bigendian = true)
{
return thiss()->appendHex((uint32_t)v, bigendian);
}

/// <summary>
/// Append v in hex encoding.
/// </summary>
INLINE S& appendHex(const int64_t v, const bool bigendian = true)
{
return thiss()->appendHex((uint64_t)v, bigendian);
const auto LENOLD = mLength;
const auto LENNEW = LENOLD + N;
if (thiss()->available() < N)
thiss()->resize(LENNEW);
Hex::tostring(v, mData + LENOLD, bigendian, true);
mLength = LENNEW; // always writes N symbols
return *thiss();
}

/// <summary>
/// Append memory with len in hex encoding.
/// </summary>
INLINE S& appendHex(const void* mem, const size_t len, const bool bigendian = true)
INLINE S& appendHex(const void* mem, const size_t len, const bool reverse = false)
{
const auto LENMEM = len << 1U;
const auto LENOLD = mLength;
const auto LENNEW = LENOLD + LENMEM;
if (thiss()->available() < LENMEM)
thiss()->resize(LENNEW);
Hex::tostring(mem, len, mData + LENOLD, bigendian, true);
Hex::encode(mem, mData + LENOLD, len, reverse, true);
mLength = LENNEW;
return *thiss();
}
Expand Down
32 changes: 25 additions & 7 deletions src/CppCore.Interface.C/cppcore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ CPPCORE_UINT_IMPLEMENTATION(cppcore_uint8192, CppCore::Block8192)
#include <CppCore/Encoding.h>
#include <CppCore/Block.h>

unsigned int cppcore_basex_estimate_bits(unsigned int symbols, unsigned int base) {
return CppCore::BaseX::estimateBits(symbols, base);
}
unsigned int cppcore_basex_estimate_symbols(unsigned int bits, unsigned int base) {
return CppCore::BaseX::estimateSymbols(bits, base);
}

#define CPPCORE_BASEX_IMPLEMENTATION(size,block) \
int cppcore_basex_encode##size( \
void* in, char* out, int len, \
Expand All @@ -95,13 +102,6 @@ CPPCORE_UINT_IMPLEMENTATION(cppcore_uint8192, CppCore::Block8192)
return CppCore::BaseX::tryparse(in, *(block*)out, alphabet); \
}

unsigned int cppcore_basex_estimate_bits(unsigned int symbols, unsigned int base) {
return CppCore::BaseX::estimateBits(symbols, base);
}
unsigned int cppcore_basex_estimate_symbols(unsigned int bits, unsigned int base) {
return CppCore::BaseX::estimateSymbols(bits, base);
}

CPPCORE_BASEX_IMPLEMENTATION(32, std::uint32_t)
CPPCORE_BASEX_IMPLEMENTATION(64, std::uint64_t)
CPPCORE_BASEX_IMPLEMENTATION(128, CppCore::Block128)
Expand All @@ -112,6 +112,24 @@ CPPCORE_BASEX_IMPLEMENTATION(2048, CppCore::Block2048)
CPPCORE_BASEX_IMPLEMENTATION(4096, CppCore::Block4096)
CPPCORE_BASEX_IMPLEMENTATION(8192, CppCore::Block8192)

#define CPPCORE_BASE16_IMPLEMENTATION(size,block) \
void cppcore_base16_encode##size(void* in, char* out, unsigned int bigendian, unsigned int writeterm, unsigned int uppercase) { \
CppCore::Hex::tostring(*(block*)in, out, (bool)bigendian, (bool)writeterm, (bool)uppercase); \
} \
unsigned int cppcore_base16_decode##size(char* in, unsigned int len, void* out, unsigned int bigendian) { \
return CppCore::Hex::tryparse((const char*)in, len, *(block*)out, (bool)bigendian); \
}

CPPCORE_BASE16_IMPLEMENTATION(32, std::uint32_t)
CPPCORE_BASE16_IMPLEMENTATION(64, std::uint64_t)
CPPCORE_BASE16_IMPLEMENTATION(128, CppCore::Block128)
CPPCORE_BASE16_IMPLEMENTATION(256, CppCore::Block256)
CPPCORE_BASE16_IMPLEMENTATION(512, CppCore::Block512)
CPPCORE_BASE16_IMPLEMENTATION(1024, CppCore::Block1024)
CPPCORE_BASE16_IMPLEMENTATION(2048, CppCore::Block2048)
CPPCORE_BASE16_IMPLEMENTATION(4096, CppCore::Block4096)
CPPCORE_BASE16_IMPLEMENTATION(8192, CppCore::Block8192)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HASH
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
74 changes: 70 additions & 4 deletions src/CppCore.Interface.JS/libcppcore.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,15 +282,81 @@ export class Base10 extends BaseX {
}
}

export class Base16 extends BaseX {
export class Base16 {
#strbuf
constructor() {
super("0123456789abcdef");
this.#strbuf = new CString(2048);
}
get strbuf() { return this.#strbuf; }
static estimateBits(symbols) {
return super.estimateBits(symbols, 16);
return EXPORTS.cppcore_basex_estimate_bits(symbols, 16);
}
static estimateSymbols(bits) {
return super.estimateSymbols(bits, 16);
return EXPORTS.cppcore_basex_estimate_symbols(bits, 16);
}
encode(v, uppercase, bigendian) {
let f = null;
if (uppercase == null) uppercase = false;
if (bigendian == null) bigendian = true;
if (v.byteLength == 4) { f = EXPORTS.cppcore_base16_encode32; }
else if (v.byteLength == 8) { f = EXPORTS.cppcore_base16_encode64; }
else if (v.byteLength == 16) { f = EXPORTS.cppcore_base16_encode128; }
else if (v.byteLength == 32) { f = EXPORTS.cppcore_base16_encode256; }
else if (v.byteLength == 64) { f = EXPORTS.cppcore_base16_encode512; }
else if (v.byteLength == 128) { f = EXPORTS.cppcore_base16_encode1024; }
else if (v.byteLength == 256) { f = EXPORTS.cppcore_base16_encode2048; }
else if (v.byteLength == 512) { f = EXPORTS.cppcore_base16_encode4096; }
else if (v.byteLength == 1024) { f = EXPORTS.cppcore_base16_encode8192; }
else throw new RangeError('Invalid byteLength in Base16.encode()');
f(
v.ptr, // inbuf ptr
this.#strbuf.ptr, // outbuf ptr
bigendian, // big endian output
1, // write term 0x00
uppercase // uppercase
);
this.#strbuf.byteLength = v.byteLength << 1;
return this.#strbuf.toString();
}
decode(str, bits, bigendian) {
if (bigendian == null) bigendian = true;
this.#strbuf.set(str);
const len = this.#strbuf.byteLength;
let f = null;
let t = null;
if (!bits) bits = len&1 ? (len+1)<<2 : len<<2;
if (bits <= 32) { t = UInt32; f = EXPORTS.cppcore_base16_decode32; }
else if (bits <= 64) { t = UInt64; f = EXPORTS.cppcore_base16_decode64; }
else if (bits <= 128) { t = UInt128; f = EXPORTS.cppcore_base16_decode128; }
else if (bits <= 256) { t = UInt256; f = EXPORTS.cppcore_base16_decode256; }
else if (bits <= 512) { t = UInt512; f = EXPORTS.cppcore_base16_decode512; }
else if (bits <= 1024) { t = UInt1024; f = EXPORTS.cppcore_base16_decode1024; }
else if (bits <= 2048) { t = UInt2048; f = EXPORTS.cppcore_base16_decode2048; }
else if (bits <= 4096) { t = UInt4096; f = EXPORTS.cppcore_base16_decode4096; }
else if (bits <= 8192) { t = UInt8192; f = EXPORTS.cppcore_base16_decode8192; }
else throw new RangeError('Invalid bitLength in Base16.decode()');
const uint = new t();
const r = f(this.#strbuf.ptr, this.#strbuf.byteLength, uint.ptr, bigendian);
if (r == 0) throw new Error('Invalid symbol or overflow');
return uint;
}
decodeInto(str, buf, bigendian) {
if (bigendian == null) bigendian = true;
this.#strbuf.set(str);
const byteLength = buf.byteLength;
let f = null;
if (byteLength == 4) { f = EXPORTS.cppcore_base16_decode32; }
else if (byteLength == 8) { f = EXPORTS.cppcore_base16_decode64; }
else if (byteLength == 16) { f = EXPORTS.cppcore_base16_decode128; }
else if (byteLength == 32) { f = EXPORTS.cppcore_base16_decode256; }
else if (byteLength == 64) { f = EXPORTS.cppcore_base16_decode512; }
else if (byteLength == 128) { f = EXPORTS.cppcore_base16_decode1024; }
else if (byteLength == 256) { f = EXPORTS.cppcore_base16_decode2048; }
else if (byteLength == 512) { f = EXPORTS.cppcore_base16_decode4096; }
else if (byteLength == 1024) { f = EXPORTS.cppcore_base16_decode8192; }
else throw new RangeError('Invalid byteLength in Base16.decodeInto()');
const r = f(this.#strbuf.ptr, this.#strbuf.byteLength, buf.ptr, bigendian);
if (r == 0) throw new Error('Invalid symbol or overflow');
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/CppCore.Test/Test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,8 @@ int main()
std::cout << "-------------------------------" << std::endl;
std::cout << " CppCore::Encoding::Hex " << std::endl;
std::cout << "-------------------------------" << std::endl;
TEST(CppCore::Test::Encoding::Hex::tostring, "tostring: ", std::endl);
TEST(CppCore::Test::Encoding::Hex::encode, "encode: ", std::endl);
TEST(CppCore::Test::Encoding::Hex::decode, "decode: ", std::endl);
TEST(CppCore::Test::Encoding::Hex::tostring16, "tostring16: ", std::endl);
TEST(CppCore::Test::Encoding::Hex::tostring32, "tostring32: ", std::endl);
TEST(CppCore::Test::Encoding::Hex::tostring64, "tostring64: ", std::endl);
Expand Down

0 comments on commit 4d669e4

Please sign in to comment.