161e349acSEd Tanous #include "json_html_serializer.hpp" 261e349acSEd Tanous 361e349acSEd Tanous #include "http_response.hpp" 461e349acSEd Tanous 561e349acSEd Tanous #include <nlohmann/json.hpp> 661e349acSEd Tanous 761e349acSEd Tanous #include <algorithm> 861e349acSEd Tanous #include <array> 961e349acSEd Tanous #include <limits> 1061e349acSEd Tanous 1161e349acSEd Tanous namespace json_html_util 1261e349acSEd Tanous { 1361e349acSEd Tanous 1461e349acSEd Tanous static constexpr uint8_t utf8Accept = 0; 1561e349acSEd Tanous static constexpr uint8_t utf8Reject = 1; 1661e349acSEd Tanous 1761e349acSEd Tanous static uint8_t decode(uint8_t& state, uint32_t& codePoint, 1861e349acSEd Tanous const uint8_t byte) noexcept 1961e349acSEd Tanous { 2061e349acSEd Tanous // clang-format off 2161e349acSEd Tanous static const std::array<std::uint8_t, 400> utf8d = 2261e349acSEd Tanous { 2361e349acSEd Tanous { 2461e349acSEd Tanous 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F 2561e349acSEd Tanous 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F 2661e349acSEd Tanous 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F 2761e349acSEd Tanous 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F 2861e349acSEd Tanous 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F 2961e349acSEd Tanous 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF 3061e349acSEd Tanous 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF 3161e349acSEd Tanous 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF 3261e349acSEd Tanous 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF 3361e349acSEd Tanous 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 3461e349acSEd Tanous 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 3561e349acSEd Tanous 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 3661e349acSEd Tanous 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 3761e349acSEd Tanous 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 3861e349acSEd Tanous } 3961e349acSEd Tanous }; 4061e349acSEd Tanous // clang-format on 4161e349acSEd Tanous 4261e349acSEd Tanous if (state > 0x8) 4361e349acSEd Tanous { 4461e349acSEd Tanous return state; 4561e349acSEd Tanous } 4661e349acSEd Tanous 4761e349acSEd Tanous const uint8_t type = utf8d[byte]; 4861e349acSEd Tanous 4961e349acSEd Tanous codePoint = (state != utf8Accept) 5061e349acSEd Tanous ? (byte & 0x3fU) | (codePoint << 6) 5161e349acSEd Tanous : static_cast<uint32_t>(0xff >> type) & (byte); 5261e349acSEd Tanous 5361e349acSEd Tanous state = utf8d[256U + state * 16U + type]; 5461e349acSEd Tanous return state; 5561e349acSEd Tanous } 5661e349acSEd Tanous 5761e349acSEd Tanous static void dumpEscaped(std::string& out, const std::string& str) 5861e349acSEd Tanous { 5961e349acSEd Tanous std::array<char, 512> stringBuffer{{}}; 6061e349acSEd Tanous uint32_t codePoint = 0; 6161e349acSEd Tanous uint8_t state = utf8Accept; 6261e349acSEd Tanous std::size_t bytes = 0; // number of bytes written to string_buffer 6361e349acSEd Tanous 6461e349acSEd Tanous // number of bytes written at the point of the last valid byte 6561e349acSEd Tanous std::size_t bytesAfterLastAccept = 0; 6661e349acSEd Tanous std::size_t undumpedChars = 0; 6761e349acSEd Tanous 6861e349acSEd Tanous for (std::size_t i = 0; i < str.size(); ++i) 6961e349acSEd Tanous { 7061e349acSEd Tanous const uint8_t byte = static_cast<uint8_t>(str[i]); 7161e349acSEd Tanous 7261e349acSEd Tanous switch (decode(state, codePoint, byte)) 7361e349acSEd Tanous { 7461e349acSEd Tanous case utf8Accept: // decode found a new code point 7561e349acSEd Tanous { 7661e349acSEd Tanous switch (codePoint) 7761e349acSEd Tanous { 7861e349acSEd Tanous case 0x08: // backspace 7961e349acSEd Tanous { 8061e349acSEd Tanous stringBuffer[bytes++] = '\\'; 8161e349acSEd Tanous stringBuffer[bytes++] = 'b'; 8261e349acSEd Tanous break; 8361e349acSEd Tanous } 8461e349acSEd Tanous 8561e349acSEd Tanous case 0x09: // horizontal tab 8661e349acSEd Tanous { 8761e349acSEd Tanous stringBuffer[bytes++] = '\\'; 8861e349acSEd Tanous stringBuffer[bytes++] = 't'; 8961e349acSEd Tanous break; 9061e349acSEd Tanous } 9161e349acSEd Tanous 9261e349acSEd Tanous case 0x0A: // newline 9361e349acSEd Tanous { 9461e349acSEd Tanous stringBuffer[bytes++] = '\\'; 9561e349acSEd Tanous stringBuffer[bytes++] = 'n'; 9661e349acSEd Tanous break; 9761e349acSEd Tanous } 9861e349acSEd Tanous 9961e349acSEd Tanous case 0x0C: // formfeed 10061e349acSEd Tanous { 10161e349acSEd Tanous stringBuffer[bytes++] = '\\'; 10261e349acSEd Tanous stringBuffer[bytes++] = 'f'; 10361e349acSEd Tanous break; 10461e349acSEd Tanous } 10561e349acSEd Tanous 10661e349acSEd Tanous case 0x0D: // carriage return 10761e349acSEd Tanous { 10861e349acSEd Tanous stringBuffer[bytes++] = '\\'; 10961e349acSEd Tanous stringBuffer[bytes++] = 'r'; 11061e349acSEd Tanous break; 11161e349acSEd Tanous } 11261e349acSEd Tanous 11361e349acSEd Tanous case 0x22: // quotation mark 11461e349acSEd Tanous { 11561e349acSEd Tanous stringBuffer[bytes++] = '&'; 11661e349acSEd Tanous stringBuffer[bytes++] = 'q'; 11761e349acSEd Tanous stringBuffer[bytes++] = 'u'; 11861e349acSEd Tanous stringBuffer[bytes++] = 'o'; 11961e349acSEd Tanous stringBuffer[bytes++] = 't'; 12061e349acSEd Tanous stringBuffer[bytes++] = ';'; 12161e349acSEd Tanous break; 12261e349acSEd Tanous } 12361e349acSEd Tanous 12461e349acSEd Tanous case 0x27: // apostrophe 12561e349acSEd Tanous { 12661e349acSEd Tanous stringBuffer[bytes++] = '&'; 12761e349acSEd Tanous stringBuffer[bytes++] = 'a'; 12861e349acSEd Tanous stringBuffer[bytes++] = 'p'; 12961e349acSEd Tanous stringBuffer[bytes++] = 'o'; 13061e349acSEd Tanous stringBuffer[bytes++] = 's'; 13161e349acSEd Tanous stringBuffer[bytes++] = ';'; 13261e349acSEd Tanous break; 13361e349acSEd Tanous } 13461e349acSEd Tanous 13561e349acSEd Tanous case 0x26: // ampersand 13661e349acSEd Tanous { 13761e349acSEd Tanous stringBuffer[bytes++] = '&'; 13861e349acSEd Tanous stringBuffer[bytes++] = 'a'; 13961e349acSEd Tanous stringBuffer[bytes++] = 'm'; 14061e349acSEd Tanous stringBuffer[bytes++] = 'p'; 14161e349acSEd Tanous stringBuffer[bytes++] = ';'; 14261e349acSEd Tanous break; 14361e349acSEd Tanous } 14461e349acSEd Tanous 14561e349acSEd Tanous case 0x3C: // less than 14661e349acSEd Tanous { 14761e349acSEd Tanous stringBuffer[bytes++] = '\\'; 14861e349acSEd Tanous stringBuffer[bytes++] = 'l'; 14961e349acSEd Tanous stringBuffer[bytes++] = 't'; 15061e349acSEd Tanous stringBuffer[bytes++] = ';'; 15161e349acSEd Tanous break; 15261e349acSEd Tanous } 15361e349acSEd Tanous 15461e349acSEd Tanous case 0x3E: // greater than 15561e349acSEd Tanous { 15661e349acSEd Tanous stringBuffer[bytes++] = '\\'; 15761e349acSEd Tanous stringBuffer[bytes++] = 'g'; 15861e349acSEd Tanous stringBuffer[bytes++] = 't'; 15961e349acSEd Tanous stringBuffer[bytes++] = ';'; 16061e349acSEd Tanous break; 16161e349acSEd Tanous } 16261e349acSEd Tanous 16361e349acSEd Tanous default: 16461e349acSEd Tanous { 16561e349acSEd Tanous // escape control characters (0x00..0x1F) 16661e349acSEd Tanous if ((codePoint <= 0x1F) or (codePoint >= 0x7F)) 16761e349acSEd Tanous { 16861e349acSEd Tanous if (codePoint <= 0xFFFF) 16961e349acSEd Tanous { 17061e349acSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 17161e349acSEd Tanous std::snprintf(&stringBuffer[bytes], 7, 17261e349acSEd Tanous "\\u%04x", 17361e349acSEd Tanous static_cast<uint16_t>(codePoint)); 17461e349acSEd Tanous bytes += 6; 17561e349acSEd Tanous } 17661e349acSEd Tanous else 17761e349acSEd Tanous { 17861e349acSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 17961e349acSEd Tanous std::snprintf( 18061e349acSEd Tanous &stringBuffer[bytes], 13, "\\u%04x\\u%04x", 18161e349acSEd Tanous static_cast<uint16_t>(0xD7C0 + 18261e349acSEd Tanous (codePoint >> 10)), 18361e349acSEd Tanous static_cast<uint16_t>(0xDC00 + 18461e349acSEd Tanous (codePoint & 0x3FF))); 18561e349acSEd Tanous bytes += 12; 18661e349acSEd Tanous } 18761e349acSEd Tanous } 18861e349acSEd Tanous else 18961e349acSEd Tanous { 19061e349acSEd Tanous // copy byte to buffer (all previous bytes 19161e349acSEd Tanous // been copied have in default case above) 19261e349acSEd Tanous stringBuffer[bytes++] = str[i]; 19361e349acSEd Tanous } 19461e349acSEd Tanous break; 19561e349acSEd Tanous } 19661e349acSEd Tanous } 19761e349acSEd Tanous 19861e349acSEd Tanous // write buffer and reset index; there must be 13 bytes 19961e349acSEd Tanous // left, as this is the maximal number of bytes to be 20061e349acSEd Tanous // written ("\uxxxx\uxxxx\0") for one code point 20161e349acSEd Tanous if (stringBuffer.size() - bytes < 13) 20261e349acSEd Tanous { 20361e349acSEd Tanous out.append(stringBuffer.data(), bytes); 20461e349acSEd Tanous bytes = 0; 20561e349acSEd Tanous } 20661e349acSEd Tanous 20761e349acSEd Tanous // remember the byte position of this accept 20861e349acSEd Tanous bytesAfterLastAccept = bytes; 20961e349acSEd Tanous undumpedChars = 0; 21061e349acSEd Tanous break; 21161e349acSEd Tanous } 21261e349acSEd Tanous 21361e349acSEd Tanous case utf8Reject: // decode found invalid UTF-8 byte 21461e349acSEd Tanous { 21561e349acSEd Tanous // in case we saw this character the first time, we 21661e349acSEd Tanous // would like to read it again, because the byte 21761e349acSEd Tanous // may be OK for itself, but just not OK for the 21861e349acSEd Tanous // previous sequence 21961e349acSEd Tanous if (undumpedChars > 0) 22061e349acSEd Tanous { 22161e349acSEd Tanous --i; 22261e349acSEd Tanous } 22361e349acSEd Tanous 22461e349acSEd Tanous // reset length buffer to the last accepted index; 22561e349acSEd Tanous // thus removing/ignoring the invalid characters 22661e349acSEd Tanous bytes = bytesAfterLastAccept; 22761e349acSEd Tanous 22861e349acSEd Tanous stringBuffer[bytes++] = '\\'; 22961e349acSEd Tanous stringBuffer[bytes++] = 'u'; 23061e349acSEd Tanous stringBuffer[bytes++] = 'f'; 23161e349acSEd Tanous stringBuffer[bytes++] = 'f'; 23261e349acSEd Tanous stringBuffer[bytes++] = 'f'; 23361e349acSEd Tanous stringBuffer[bytes++] = 'd'; 23461e349acSEd Tanous 23561e349acSEd Tanous bytesAfterLastAccept = bytes; 23661e349acSEd Tanous 23761e349acSEd Tanous undumpedChars = 0; 23861e349acSEd Tanous 23961e349acSEd Tanous // continue processing the string 24061e349acSEd Tanous state = utf8Accept; 24161e349acSEd Tanous break; 24261e349acSEd Tanous } 24361e349acSEd Tanous 24461e349acSEd Tanous default: // decode found yet incomplete multi-byte code point 24561e349acSEd Tanous { 24661e349acSEd Tanous ++undumpedChars; 24761e349acSEd Tanous break; 24861e349acSEd Tanous } 24961e349acSEd Tanous } 25061e349acSEd Tanous } 25161e349acSEd Tanous 25261e349acSEd Tanous // we finished processing the string 25361e349acSEd Tanous if (state == utf8Accept) 25461e349acSEd Tanous { 25561e349acSEd Tanous // write buffer 25661e349acSEd Tanous if (bytes > 0) 25761e349acSEd Tanous { 25861e349acSEd Tanous out.append(stringBuffer.data(), bytes); 25961e349acSEd Tanous } 26061e349acSEd Tanous } 26161e349acSEd Tanous else 26261e349acSEd Tanous { 26361e349acSEd Tanous // write all accepted bytes 26461e349acSEd Tanous out.append(stringBuffer.data(), bytesAfterLastAccept); 26561e349acSEd Tanous out += "\\ufffd"; 26661e349acSEd Tanous } 26761e349acSEd Tanous } 26861e349acSEd Tanous 26961e349acSEd Tanous static unsigned int countDigits(uint64_t number) noexcept 27061e349acSEd Tanous { 27161e349acSEd Tanous unsigned int nDigits = 1; 27261e349acSEd Tanous for (;;) 27361e349acSEd Tanous { 27461e349acSEd Tanous if (number < 10) 27561e349acSEd Tanous { 27661e349acSEd Tanous return nDigits; 27761e349acSEd Tanous } 27861e349acSEd Tanous if (number < 100) 27961e349acSEd Tanous { 28061e349acSEd Tanous return nDigits + 1; 28161e349acSEd Tanous } 28261e349acSEd Tanous if (number < 1000) 28361e349acSEd Tanous { 28461e349acSEd Tanous return nDigits + 2; 28561e349acSEd Tanous } 28661e349acSEd Tanous if (number < 10000) 28761e349acSEd Tanous { 28861e349acSEd Tanous return nDigits + 3; 28961e349acSEd Tanous } 29061e349acSEd Tanous number = number / 10000U; 29161e349acSEd Tanous nDigits += 4; 29261e349acSEd Tanous } 29361e349acSEd Tanous } 29461e349acSEd Tanous 29561e349acSEd Tanous template <typename NumberType, 29661e349acSEd Tanous std::enable_if_t<std::is_same<NumberType, uint64_t>::value or 29761e349acSEd Tanous std::is_same<NumberType, int64_t>::value, 29861e349acSEd Tanous int> = 0> 29961e349acSEd Tanous void dumpInteger(std::string& out, NumberType number) 30061e349acSEd Tanous { 30161e349acSEd Tanous std::array<char, 64> numberbuffer{{}}; 30261e349acSEd Tanous 30361e349acSEd Tanous static constexpr std::array<std::array<char, 2>, 100> digitsTo99{{ 30461e349acSEd Tanous {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, 30561e349acSEd Tanous {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, 30661e349acSEd Tanous {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, 30761e349acSEd Tanous {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, 30861e349acSEd Tanous {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, 30961e349acSEd Tanous {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, 31061e349acSEd Tanous {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, 31161e349acSEd Tanous {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, 31261e349acSEd Tanous {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, 31361e349acSEd Tanous {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, 31461e349acSEd Tanous {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, 31561e349acSEd Tanous {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, 31661e349acSEd Tanous {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, 31761e349acSEd Tanous {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, 31861e349acSEd Tanous {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, 31961e349acSEd Tanous {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, 32061e349acSEd Tanous {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}, 32161e349acSEd Tanous }}; 32261e349acSEd Tanous 32361e349acSEd Tanous // special case for "0" 32461e349acSEd Tanous if (number == 0) 32561e349acSEd Tanous { 32661e349acSEd Tanous out += '0'; 32761e349acSEd Tanous return; 32861e349acSEd Tanous } 32961e349acSEd Tanous 33061e349acSEd Tanous // use a pointer to fill the buffer 33161e349acSEd Tanous auto* bufferPtr = numberbuffer.begin(); 33261e349acSEd Tanous 33361e349acSEd Tanous const bool isNegative = std::is_same<NumberType, int64_t>::value && 33461e349acSEd Tanous !(number >= 0); // see issue #755 33561e349acSEd Tanous uint64_t absValue = 0; 33661e349acSEd Tanous 33761e349acSEd Tanous unsigned int nChars = 0; 33861e349acSEd Tanous 33961e349acSEd Tanous if (isNegative) 34061e349acSEd Tanous { 34161e349acSEd Tanous *bufferPtr = '-'; 34261e349acSEd Tanous absValue = static_cast<uint64_t>(0 - number); 34361e349acSEd Tanous 34461e349acSEd Tanous // account one more byte for the minus sign 34561e349acSEd Tanous nChars = 1 + countDigits(absValue); 34661e349acSEd Tanous } 34761e349acSEd Tanous else 34861e349acSEd Tanous { 34961e349acSEd Tanous absValue = static_cast<uint64_t>(number); 35061e349acSEd Tanous nChars = countDigits(absValue); 35161e349acSEd Tanous } 35261e349acSEd Tanous 35361e349acSEd Tanous // spare 1 byte for '\0' 35461e349acSEd Tanous if (nChars >= numberbuffer.size() - 1) 35561e349acSEd Tanous { 35661e349acSEd Tanous return; 35761e349acSEd Tanous } 35861e349acSEd Tanous 35961e349acSEd Tanous // jump to the end to generate the string from backward 36061e349acSEd Tanous // so we later avoid reversing the result 36161e349acSEd Tanous std::advance(bufferPtr, nChars - 1); 36261e349acSEd Tanous 36361e349acSEd Tanous // Fast int2ascii implementation inspired by "Fastware" talk by Andrei 36461e349acSEd Tanous // Alexandrescu See: https://www.youtube.com/watch?v=o4-CwDo2zpg 36561e349acSEd Tanous while (absValue >= 100) 36661e349acSEd Tanous { 36761e349acSEd Tanous const auto digitsIndex = static_cast<unsigned>((absValue % 100)); 36861e349acSEd Tanous absValue /= 100; 36961e349acSEd Tanous *bufferPtr = digitsTo99[digitsIndex][1]; 37061e349acSEd Tanous bufferPtr = std::prev(bufferPtr); 37161e349acSEd Tanous *bufferPtr = digitsTo99[digitsIndex][0]; 37261e349acSEd Tanous bufferPtr = std::prev(bufferPtr); 37361e349acSEd Tanous } 37461e349acSEd Tanous 37561e349acSEd Tanous if (absValue >= 10) 37661e349acSEd Tanous { 37761e349acSEd Tanous const auto digitsIndex = static_cast<unsigned>(absValue); 37861e349acSEd Tanous *bufferPtr = digitsTo99[digitsIndex][1]; 37961e349acSEd Tanous bufferPtr = std::prev(bufferPtr); 38061e349acSEd Tanous *bufferPtr = digitsTo99[digitsIndex][0]; 38161e349acSEd Tanous // assignment never used: bufferPtr = std::prev(bufferPtr); 38261e349acSEd Tanous } 38361e349acSEd Tanous else 38461e349acSEd Tanous { 38561e349acSEd Tanous *bufferPtr = static_cast<char>('0' + absValue); 38661e349acSEd Tanous // assignment never used: bufferPtr = std::prev(bufferPtr); 38761e349acSEd Tanous } 38861e349acSEd Tanous 38961e349acSEd Tanous out.append(numberbuffer.data(), nChars); 39061e349acSEd Tanous } 39161e349acSEd Tanous 39261e349acSEd Tanous static void dumpfloat(std::string& out, double number) 39361e349acSEd Tanous { 39461e349acSEd Tanous // NaN / inf 39561e349acSEd Tanous if (!std::isfinite(number)) 39661e349acSEd Tanous { 39761e349acSEd Tanous out += "null"; 39861e349acSEd Tanous return; 39961e349acSEd Tanous } 40061e349acSEd Tanous std::array<char, 64> numberbuffer{{}}; 40161e349acSEd Tanous 40261e349acSEd Tanous ::nlohmann::detail::to_chars(numberbuffer.begin(), numberbuffer.end(), 40361e349acSEd Tanous number); 40461e349acSEd Tanous 40561e349acSEd Tanous out += numberbuffer.data(); 40661e349acSEd Tanous } 40761e349acSEd Tanous 40861e349acSEd Tanous static void dump(std::string& out, const nlohmann::json& val) 40961e349acSEd Tanous { 41061e349acSEd Tanous switch (val.type()) 41161e349acSEd Tanous { 41261e349acSEd Tanous case nlohmann::json::value_t::object: 41361e349acSEd Tanous { 41461e349acSEd Tanous if (val.empty()) 41561e349acSEd Tanous { 41661e349acSEd Tanous out += "{}"; 41761e349acSEd Tanous return; 41861e349acSEd Tanous } 41961e349acSEd Tanous 42061e349acSEd Tanous out += "{"; 42161e349acSEd Tanous 42261e349acSEd Tanous out += "<div class=tab>"; 42361e349acSEd Tanous for (auto i = val.begin(); i != val.end();) 42461e349acSEd Tanous { 42561e349acSEd Tanous out += """; 42661e349acSEd Tanous dumpEscaped(out, i.key()); 42761e349acSEd Tanous out += "": "; 42861e349acSEd Tanous 42961e349acSEd Tanous bool inATag = false; 43061e349acSEd Tanous if (i.key() == "@odata.id" || i.key() == "@odata.context" || 43161e349acSEd Tanous i.key() == "Members@odata.nextLink" || i.key() == "Uri") 43261e349acSEd Tanous { 43361e349acSEd Tanous inATag = true; 43461e349acSEd Tanous out += "<a href=\""; 43561e349acSEd Tanous dumpEscaped(out, i.value()); 43661e349acSEd Tanous out += "\">"; 43761e349acSEd Tanous } 43861e349acSEd Tanous dump(out, i.value()); 43961e349acSEd Tanous if (inATag) 44061e349acSEd Tanous { 44161e349acSEd Tanous out += "</a>"; 44261e349acSEd Tanous } 44361e349acSEd Tanous i++; 44461e349acSEd Tanous if (i != val.end()) 44561e349acSEd Tanous { 44661e349acSEd Tanous out += ","; 44761e349acSEd Tanous } 44861e349acSEd Tanous out += "<br>"; 44961e349acSEd Tanous } 45061e349acSEd Tanous out += "</div>"; 45161e349acSEd Tanous out += '}'; 45261e349acSEd Tanous 45361e349acSEd Tanous return; 45461e349acSEd Tanous } 45561e349acSEd Tanous 45661e349acSEd Tanous case nlohmann::json::value_t::array: 45761e349acSEd Tanous { 45861e349acSEd Tanous if (val.empty()) 45961e349acSEd Tanous { 46061e349acSEd Tanous out += "[]"; 46161e349acSEd Tanous return; 46261e349acSEd Tanous } 46361e349acSEd Tanous 46461e349acSEd Tanous out += "["; 46561e349acSEd Tanous 46661e349acSEd Tanous out += "<div class=tab>"; 46761e349acSEd Tanous 46861e349acSEd Tanous // first n-1 elements 46961e349acSEd Tanous for (auto i = val.cbegin(); i != val.cend() - 1; ++i) 47061e349acSEd Tanous { 47161e349acSEd Tanous dump(out, *i); 47261e349acSEd Tanous out += ",<br>"; 47361e349acSEd Tanous } 47461e349acSEd Tanous 47561e349acSEd Tanous // last element 47661e349acSEd Tanous dump(out, val.back()); 47761e349acSEd Tanous 47861e349acSEd Tanous out += "</div>"; 47961e349acSEd Tanous out += ']'; 48061e349acSEd Tanous 48161e349acSEd Tanous return; 48261e349acSEd Tanous } 48361e349acSEd Tanous 48461e349acSEd Tanous case nlohmann::json::value_t::string: 48561e349acSEd Tanous { 48661e349acSEd Tanous out += '\"'; 48761e349acSEd Tanous const std::string* ptr = val.get_ptr<const std::string*>(); 48861e349acSEd Tanous dumpEscaped(out, *ptr); 48961e349acSEd Tanous out += '\"'; 49061e349acSEd Tanous return; 49161e349acSEd Tanous } 49261e349acSEd Tanous 49361e349acSEd Tanous case nlohmann::json::value_t::boolean: 49461e349acSEd Tanous { 49561e349acSEd Tanous if (*(val.get_ptr<const bool*>())) 49661e349acSEd Tanous { 49761e349acSEd Tanous out += "true"; 49861e349acSEd Tanous } 49961e349acSEd Tanous else 50061e349acSEd Tanous { 50161e349acSEd Tanous out += "false"; 50261e349acSEd Tanous } 50361e349acSEd Tanous return; 50461e349acSEd Tanous } 50561e349acSEd Tanous 50661e349acSEd Tanous case nlohmann::json::value_t::number_integer: 50761e349acSEd Tanous { 50861e349acSEd Tanous dumpInteger(out, *(val.get_ptr<const int64_t*>())); 50961e349acSEd Tanous return; 51061e349acSEd Tanous } 51161e349acSEd Tanous 51261e349acSEd Tanous case nlohmann::json::value_t::number_unsigned: 51361e349acSEd Tanous { 51461e349acSEd Tanous dumpInteger(out, *(val.get_ptr<const uint64_t*>())); 51561e349acSEd Tanous return; 51661e349acSEd Tanous } 51761e349acSEd Tanous 51861e349acSEd Tanous case nlohmann::json::value_t::number_float: 51961e349acSEd Tanous { 52061e349acSEd Tanous dumpfloat(out, *(val.get_ptr<const double*>())); 52161e349acSEd Tanous return; 52261e349acSEd Tanous } 52361e349acSEd Tanous 52461e349acSEd Tanous case nlohmann::json::value_t::discarded: 52561e349acSEd Tanous { 52661e349acSEd Tanous out += "<discarded>"; 52761e349acSEd Tanous return; 52861e349acSEd Tanous } 52961e349acSEd Tanous 53061e349acSEd Tanous case nlohmann::json::value_t::null: 53161e349acSEd Tanous { 53261e349acSEd Tanous out += "null"; 53361e349acSEd Tanous return; 53461e349acSEd Tanous } 53561e349acSEd Tanous case nlohmann::json::value_t::binary: 53661e349acSEd Tanous { 53761e349acSEd Tanous // Do nothing; Should never happen. 53861e349acSEd Tanous return; 53961e349acSEd Tanous } 54061e349acSEd Tanous } 54161e349acSEd Tanous } 54261e349acSEd Tanous 54361e349acSEd Tanous void dumpHtml(std::string& out, const nlohmann::json& json) 54461e349acSEd Tanous { 54561e349acSEd Tanous out += "<html>\n" 54661e349acSEd Tanous "<head>\n" 54761e349acSEd Tanous "<title>Redfish API</title>\n" 54861e349acSEd Tanous "<link href=\"/redfish.css\" rel=\"stylesheet\">\n" 54961e349acSEd Tanous "</head>\n" 55061e349acSEd Tanous "<body>\n" 55161e349acSEd Tanous "<div class=\"container\">\n" 55261e349acSEd Tanous "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" " 55361e349acSEd Tanous "height=\"406px\" " 55461e349acSEd Tanous "width=\"576px\">\n" 55561e349acSEd Tanous "<div class=\"content\">\n"; 55661e349acSEd Tanous dump(out, json); 55761e349acSEd Tanous out += "</div>\n" 55861e349acSEd Tanous "</div>\n" 55961e349acSEd Tanous "</body>\n" 56061e349acSEd Tanous "</html>\n"; 56161e349acSEd Tanous } 56261e349acSEd Tanous 56361e349acSEd Tanous void prettyPrintJson(crow::Response& res) 56461e349acSEd Tanous { 565*27b0cf90SEd Tanous std::string html; 566*27b0cf90SEd Tanous json_html_util::dumpHtml(html, res.jsonValue); 56761e349acSEd Tanous 568*27b0cf90SEd Tanous res.write(std::move(html)); 56961e349acSEd Tanous res.addHeader(boost::beast::http::field::content_type, 57061e349acSEd Tanous "text/html;charset=UTF-8"); 57161e349acSEd Tanous } 57261e349acSEd Tanous 57361e349acSEd Tanous } // namespace json_html_util 574