AuroraRuntime/Source/Parse/AuHex.cpp
Reece Wilson 124038df62 [*] Refactor public headers
[+] AuConsole::Commands::RemoveCommand
[+] EExtendedUsage::eServerAuth
[+] SysPanic2 for SysSafeLine hints (backtrace may contain optimized SysPanics, making tracing the true origin difficult)
[*] Reworked SysAssertions
[*] AuParse::EncodeHex now takes AuMemoryViewRead
[*] AuRng::ReadSecureRNG now takes AuMemoryViewWrite
[*] AuRng::ReadFastRNG now takes AuMemoryViewWrite
2023-01-15 06:05:22 +00:00

337 lines
9.0 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuHex.cpp
Date: 2021-8-28
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuHex.hpp"
namespace Aurora::Parse
{
static bool HexToOctet(const char *hex, AuUInt8 &val)
{
val = 0;
for (auto i = 0u; i < 2; i++)
{
AuUInt8 byte = *hex++;
if (byte >= '0' && byte <= '9')
{
byte = byte - '0';
}
else if (byte >= 'A' && byte <= 'F')
{
byte = byte - 'A' + 10;
}
else if (byte >= 'a' && byte <= 'f')
{
byte = byte - 'a' + 10;
}
else
{
return false;
}
val = (val << 4) | (byte & 0xF);
}
return true;
}
AUKN_SYM bool HexToInt(const char *pHex, AuUInt32 uLength, AuUInt64 &val)
{
val = 0;
uLength = AuMin(AuUInt32(sizeof(AuUInt64) * 2), uLength);
if (!pHex && uLength)
{
return false;
}
for (auto i = 0u; i < uLength; i++)
{
AuUInt8 byte = *pHex++;
if (byte >= '0' && byte <= '9')
{
byte = byte - '0';
}
else if (byte >= 'A' && byte <= 'F')
{
byte = byte - 'A' + 10;
}
else if (byte >= 'a' && byte <= 'f')
{
byte = byte - 'a' + 10;
}
else
{
return false;
}
val = (val << 4) | (byte & 0xF);
}
return true;
}
AUKN_SYM bool HexToByte(const char (hex)[2], AuUInt8 &val)
{
return HexToOctet(hex, val);
}
static inline char NibbleToChar(AuUInt8 nib)
{
if (nib < 10)
return '0' + nib;
else
return 'A' + (nib - 10);
}
AUKN_SYM void ByteToHex(AuUInt8 val, char (& hex)[2])
{
AuUInt8 lowNibble = val & 0xF;
AuUInt8 hiNibble = (val >> 4) & 0xF;
hex[0] = NibbleToChar(hiNibble);
hex[1] = NibbleToChar(lowNibble);
}
AUKN_SYM bool DecodeHex(const AuString &in, AuByteBuffer &out)
{
#define HEX_GRAMMAR_CONSUME_ALL(x) \
for (; i < in.size(); i++) \
{ \
if (in[i] != x) \
{ \
break; \
} \
}
#define HEX_GRAMMAR_CONSUME_ONE(x) \
if (i < in.size()) \
{ \
if (in[i] == x) \
{ \
i ++; \
} \
}
#define HEX_GRAMMAR_CONSUME_EITHER(a, b) \
if (i < in.size()) \
{ \
if ((in[i] == a) || \
(in[i] == b)) \
{ \
i++; \
} \
}
#define HEX_GRAMMAR_CONSUME_SPACE \
HEX_GRAMMAR_CONSUME_ALL(' ')
#define HEX_GRAMMAR_CONSUME_NEW_LINE \
HEX_GRAMMAR_CONSUME_ONE('\r') \
HEX_GRAMMAR_CONSUME_ALL('\n')
#define HEX_GRAMMAR_CONSUME_END_OF_LINE \
HEX_GRAMMAR_CONSUME_SPACE \
HEX_GRAMMAR_CONSUME_NEW_LINE \
HEX_GRAMMAR_CONSUME_SPACE
AuUInt i = 0u;
auto writeView = out.GetOrAllocateLinearWriteable(in.size() / 2);
if (!writeView)
{
SysPushErrorMem();
return false;
}
// Consume whitespace and/or literal start
{
HEX_GRAMMAR_CONSUME_SPACE;
HEX_GRAMMAR_CONSUME_EITHER('{', '[');
HEX_GRAMMAR_CONSUME_END_OF_LINE;
}
// Consume array
for (; i < in.size(); )
{
// Consume 0x prefix
if ((i + 1) < in.size())
{
if ((in[i] == '0') &&
(in[i + 1] == 'x'))
{
i += 2;
}
}
// Consume octal
if ((i + 1) < in.size())
{
AuUInt8 byte;
if (!HexToOctet(&in[i], byte))
{
out.clear();
return false;
}
*(out.writePtr++) = byte;
i += 2;
}
HEX_GRAMMAR_CONSUME_ONE('h'); // some hex dump code and people prefer FFh over 0xFF
HEX_GRAMMAR_CONSUME_EITHER(':' /*wireshark and other networking tools*/, ',' /*latin array splitter*/);
HEX_GRAMMAR_CONSUME_END_OF_LINE; // all whitespace [+newline[+all whitespace]]
// Consume end literal hint
if (i < in.size())
{
if ((in[i] == '}' /*c-like*/) ||
(in[i] == ']' /*more latin array tag*/))
{
break;
}
}
}
return true;
}
AUKN_SYM bool EncodeHex(Memory::MemoryViewRead view, EHexDump formatting, AuString &in)
{
auto pBuf = view.ptr;
auto uLength = view.length;
bool hexedit = formatting == EHexDump::eHexEditor;
bool squareBracket = formatting == EHexDump::eJSLiteral;
bool curlyBracket = formatting == EHexDump::eCLiteral;
bool zeroX = formatting == EHexDump::eCLiteral ||
formatting == EHexDump::eJSLiteral ||
formatting == EHexDump::eZeroXSpace;
bool space = formatting != EHexDump::eString;
if (!pBuf && uLength)
{
SysPushErrorArg();
return false;
}
try
{
AuTryReserve(in, uLength * 4);
auto &newLine = AuLocale::NewLine();
if (hexedit)
{
in.insert(in.size(), "00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15");
in.insert(in.size(), newLine);
in.insert(in.size(), "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
in.insert(in.size(), newLine);
in.insert(in.size(), " ");
in.insert(in.size(), newLine);
}
else if (squareBracket)
{
in.insert(in.end(), '[');
in.insert(in.size(), newLine);
}
else if (curlyBracket)
{
in.insert(in.end(), '{');
in.insert(in.size(), newLine);
}
for (auto i = 0u; i < uLength; )
{
AuUInt32 x, rowMax;
if (i != 0)
{
if (hexedit || squareBracket || curlyBracket)
{
in.insert(in.size(), newLine);
}
}
if (squareBracket || curlyBracket)
{
in.insert(in.size(), " ");
}
rowMax = AuMin(AuUInt32(i + 16), AuUInt32(uLength));
for (x = i;
x < rowMax;
x++)
{
if (space)
{
if (x != i)
{
in.insert(in.end(), ' ');
}
}
if (zeroX)
{
in.insert(in.size(), "0x");
}
{
char hex[2];
ByteToHex(reinterpret_cast<const AuUInt8 *>(pBuf)[x], hex);
in.insert(in.size(), hex, 2);
}
if (x != (uLength - 1))
{
if (squareBracket || curlyBracket)
{
in.insert(in.end(), ',');
}
}
}
i = x;
if (hexedit)
{
// TODO: print space + i * 16 padded + space hex(i * 16 padded)
}
}
if (squareBracket)
{
in.insert(in.size(), newLine);
in.insert(in.end(), ']');
}
else if (curlyBracket)
{
in.insert(in.size(), newLine);
in.insert(in.end(), '}');
}
in.shrink_to_fit();
}
catch (...)
{
SysPushErrorCatch("EncodeHex failed");
in.clear();
return false;
}
return true;
}
}