/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuHex.cpp Date: 2021-8-28 Author: Reece ***/ #include #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 AuROString &hex, AuUInt64 &val) { auto pHex = hex.data(); auto uLength = hex.length(); 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 AuROString &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(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; } }