1*61e349acSEd Tanous #include "json_html_serializer.hpp" 2*61e349acSEd Tanous 3*61e349acSEd Tanous #include "http_response.hpp" 4*61e349acSEd Tanous 5*61e349acSEd Tanous #include <nlohmann/json.hpp> 6*61e349acSEd Tanous 7*61e349acSEd Tanous #include <algorithm> 8*61e349acSEd Tanous #include <array> 9*61e349acSEd Tanous #include <limits> 10*61e349acSEd Tanous 11*61e349acSEd Tanous namespace json_html_util 12*61e349acSEd Tanous { 13*61e349acSEd Tanous 14*61e349acSEd Tanous static constexpr uint8_t utf8Accept = 0; 15*61e349acSEd Tanous static constexpr uint8_t utf8Reject = 1; 16*61e349acSEd Tanous 17*61e349acSEd Tanous static uint8_t decode(uint8_t& state, uint32_t& codePoint, 18*61e349acSEd Tanous const uint8_t byte) noexcept 19*61e349acSEd Tanous { 20*61e349acSEd Tanous // clang-format off 21*61e349acSEd Tanous static const std::array<std::uint8_t, 400> utf8d = 22*61e349acSEd Tanous { 23*61e349acSEd Tanous { 24*61e349acSEd 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 25*61e349acSEd 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 26*61e349acSEd 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 27*61e349acSEd 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 28*61e349acSEd 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 29*61e349acSEd 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 30*61e349acSEd 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 31*61e349acSEd Tanous 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF 32*61e349acSEd Tanous 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF 33*61e349acSEd Tanous 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 34*61e349acSEd 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 35*61e349acSEd 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 36*61e349acSEd 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 37*61e349acSEd 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 38*61e349acSEd Tanous } 39*61e349acSEd Tanous }; 40*61e349acSEd Tanous // clang-format on 41*61e349acSEd Tanous 42*61e349acSEd Tanous if (state > 0x8) 43*61e349acSEd Tanous { 44*61e349acSEd Tanous return state; 45*61e349acSEd Tanous } 46*61e349acSEd Tanous 47*61e349acSEd Tanous const uint8_t type = utf8d[byte]; 48*61e349acSEd Tanous 49*61e349acSEd Tanous codePoint = (state != utf8Accept) 50*61e349acSEd Tanous ? (byte & 0x3fU) | (codePoint << 6) 51*61e349acSEd Tanous : static_cast<uint32_t>(0xff >> type) & (byte); 52*61e349acSEd Tanous 53*61e349acSEd Tanous state = utf8d[256U + state * 16U + type]; 54*61e349acSEd Tanous return state; 55*61e349acSEd Tanous } 56*61e349acSEd Tanous 57*61e349acSEd Tanous static void dumpEscaped(std::string& out, const std::string& str) 58*61e349acSEd Tanous { 59*61e349acSEd Tanous std::array<char, 512> stringBuffer{{}}; 60*61e349acSEd Tanous uint32_t codePoint = 0; 61*61e349acSEd Tanous uint8_t state = utf8Accept; 62*61e349acSEd Tanous std::size_t bytes = 0; // number of bytes written to string_buffer 63*61e349acSEd Tanous 64*61e349acSEd Tanous // number of bytes written at the point of the last valid byte 65*61e349acSEd Tanous std::size_t bytesAfterLastAccept = 0; 66*61e349acSEd Tanous std::size_t undumpedChars = 0; 67*61e349acSEd Tanous 68*61e349acSEd Tanous for (std::size_t i = 0; i < str.size(); ++i) 69*61e349acSEd Tanous { 70*61e349acSEd Tanous const uint8_t byte = static_cast<uint8_t>(str[i]); 71*61e349acSEd Tanous 72*61e349acSEd Tanous switch (decode(state, codePoint, byte)) 73*61e349acSEd Tanous { 74*61e349acSEd Tanous case utf8Accept: // decode found a new code point 75*61e349acSEd Tanous { 76*61e349acSEd Tanous switch (codePoint) 77*61e349acSEd Tanous { 78*61e349acSEd Tanous case 0x08: // backspace 79*61e349acSEd Tanous { 80*61e349acSEd Tanous stringBuffer[bytes++] = '\\'; 81*61e349acSEd Tanous stringBuffer[bytes++] = 'b'; 82*61e349acSEd Tanous break; 83*61e349acSEd Tanous } 84*61e349acSEd Tanous 85*61e349acSEd Tanous case 0x09: // horizontal tab 86*61e349acSEd Tanous { 87*61e349acSEd Tanous stringBuffer[bytes++] = '\\'; 88*61e349acSEd Tanous stringBuffer[bytes++] = 't'; 89*61e349acSEd Tanous break; 90*61e349acSEd Tanous } 91*61e349acSEd Tanous 92*61e349acSEd Tanous case 0x0A: // newline 93*61e349acSEd Tanous { 94*61e349acSEd Tanous stringBuffer[bytes++] = '\\'; 95*61e349acSEd Tanous stringBuffer[bytes++] = 'n'; 96*61e349acSEd Tanous break; 97*61e349acSEd Tanous } 98*61e349acSEd Tanous 99*61e349acSEd Tanous case 0x0C: // formfeed 100*61e349acSEd Tanous { 101*61e349acSEd Tanous stringBuffer[bytes++] = '\\'; 102*61e349acSEd Tanous stringBuffer[bytes++] = 'f'; 103*61e349acSEd Tanous break; 104*61e349acSEd Tanous } 105*61e349acSEd Tanous 106*61e349acSEd Tanous case 0x0D: // carriage return 107*61e349acSEd Tanous { 108*61e349acSEd Tanous stringBuffer[bytes++] = '\\'; 109*61e349acSEd Tanous stringBuffer[bytes++] = 'r'; 110*61e349acSEd Tanous break; 111*61e349acSEd Tanous } 112*61e349acSEd Tanous 113*61e349acSEd Tanous case 0x22: // quotation mark 114*61e349acSEd Tanous { 115*61e349acSEd Tanous stringBuffer[bytes++] = '&'; 116*61e349acSEd Tanous stringBuffer[bytes++] = 'q'; 117*61e349acSEd Tanous stringBuffer[bytes++] = 'u'; 118*61e349acSEd Tanous stringBuffer[bytes++] = 'o'; 119*61e349acSEd Tanous stringBuffer[bytes++] = 't'; 120*61e349acSEd Tanous stringBuffer[bytes++] = ';'; 121*61e349acSEd Tanous break; 122*61e349acSEd Tanous } 123*61e349acSEd Tanous 124*61e349acSEd Tanous case 0x27: // apostrophe 125*61e349acSEd Tanous { 126*61e349acSEd Tanous stringBuffer[bytes++] = '&'; 127*61e349acSEd Tanous stringBuffer[bytes++] = 'a'; 128*61e349acSEd Tanous stringBuffer[bytes++] = 'p'; 129*61e349acSEd Tanous stringBuffer[bytes++] = 'o'; 130*61e349acSEd Tanous stringBuffer[bytes++] = 's'; 131*61e349acSEd Tanous stringBuffer[bytes++] = ';'; 132*61e349acSEd Tanous break; 133*61e349acSEd Tanous } 134*61e349acSEd Tanous 135*61e349acSEd Tanous case 0x26: // ampersand 136*61e349acSEd Tanous { 137*61e349acSEd Tanous stringBuffer[bytes++] = '&'; 138*61e349acSEd Tanous stringBuffer[bytes++] = 'a'; 139*61e349acSEd Tanous stringBuffer[bytes++] = 'm'; 140*61e349acSEd Tanous stringBuffer[bytes++] = 'p'; 141*61e349acSEd Tanous stringBuffer[bytes++] = ';'; 142*61e349acSEd Tanous break; 143*61e349acSEd Tanous } 144*61e349acSEd Tanous 145*61e349acSEd Tanous case 0x3C: // less than 146*61e349acSEd Tanous { 147*61e349acSEd Tanous stringBuffer[bytes++] = '\\'; 148*61e349acSEd Tanous stringBuffer[bytes++] = 'l'; 149*61e349acSEd Tanous stringBuffer[bytes++] = 't'; 150*61e349acSEd Tanous stringBuffer[bytes++] = ';'; 151*61e349acSEd Tanous break; 152*61e349acSEd Tanous } 153*61e349acSEd Tanous 154*61e349acSEd Tanous case 0x3E: // greater than 155*61e349acSEd Tanous { 156*61e349acSEd Tanous stringBuffer[bytes++] = '\\'; 157*61e349acSEd Tanous stringBuffer[bytes++] = 'g'; 158*61e349acSEd Tanous stringBuffer[bytes++] = 't'; 159*61e349acSEd Tanous stringBuffer[bytes++] = ';'; 160*61e349acSEd Tanous break; 161*61e349acSEd Tanous } 162*61e349acSEd Tanous 163*61e349acSEd Tanous default: 164*61e349acSEd Tanous { 165*61e349acSEd Tanous // escape control characters (0x00..0x1F) 166*61e349acSEd Tanous if ((codePoint <= 0x1F) or (codePoint >= 0x7F)) 167*61e349acSEd Tanous { 168*61e349acSEd Tanous if (codePoint <= 0xFFFF) 169*61e349acSEd Tanous { 170*61e349acSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 171*61e349acSEd Tanous std::snprintf(&stringBuffer[bytes], 7, 172*61e349acSEd Tanous "\\u%04x", 173*61e349acSEd Tanous static_cast<uint16_t>(codePoint)); 174*61e349acSEd Tanous bytes += 6; 175*61e349acSEd Tanous } 176*61e349acSEd Tanous else 177*61e349acSEd Tanous { 178*61e349acSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 179*61e349acSEd Tanous std::snprintf( 180*61e349acSEd Tanous &stringBuffer[bytes], 13, "\\u%04x\\u%04x", 181*61e349acSEd Tanous static_cast<uint16_t>(0xD7C0 + 182*61e349acSEd Tanous (codePoint >> 10)), 183*61e349acSEd Tanous static_cast<uint16_t>(0xDC00 + 184*61e349acSEd Tanous (codePoint & 0x3FF))); 185*61e349acSEd Tanous bytes += 12; 186*61e349acSEd Tanous } 187*61e349acSEd Tanous } 188*61e349acSEd Tanous else 189*61e349acSEd Tanous { 190*61e349acSEd Tanous // copy byte to buffer (all previous bytes 191*61e349acSEd Tanous // been copied have in default case above) 192*61e349acSEd Tanous stringBuffer[bytes++] = str[i]; 193*61e349acSEd Tanous } 194*61e349acSEd Tanous break; 195*61e349acSEd Tanous } 196*61e349acSEd Tanous } 197*61e349acSEd Tanous 198*61e349acSEd Tanous // write buffer and reset index; there must be 13 bytes 199*61e349acSEd Tanous // left, as this is the maximal number of bytes to be 200*61e349acSEd Tanous // written ("\uxxxx\uxxxx\0") for one code point 201*61e349acSEd Tanous if (stringBuffer.size() - bytes < 13) 202*61e349acSEd Tanous { 203*61e349acSEd Tanous out.append(stringBuffer.data(), bytes); 204*61e349acSEd Tanous bytes = 0; 205*61e349acSEd Tanous } 206*61e349acSEd Tanous 207*61e349acSEd Tanous // remember the byte position of this accept 208*61e349acSEd Tanous bytesAfterLastAccept = bytes; 209*61e349acSEd Tanous undumpedChars = 0; 210*61e349acSEd Tanous break; 211*61e349acSEd Tanous } 212*61e349acSEd Tanous 213*61e349acSEd Tanous case utf8Reject: // decode found invalid UTF-8 byte 214*61e349acSEd Tanous { 215*61e349acSEd Tanous // in case we saw this character the first time, we 216*61e349acSEd Tanous // would like to read it again, because the byte 217*61e349acSEd Tanous // may be OK for itself, but just not OK for the 218*61e349acSEd Tanous // previous sequence 219*61e349acSEd Tanous if (undumpedChars > 0) 220*61e349acSEd Tanous { 221*61e349acSEd Tanous --i; 222*61e349acSEd Tanous } 223*61e349acSEd Tanous 224*61e349acSEd Tanous // reset length buffer to the last accepted index; 225*61e349acSEd Tanous // thus removing/ignoring the invalid characters 226*61e349acSEd Tanous bytes = bytesAfterLastAccept; 227*61e349acSEd Tanous 228*61e349acSEd Tanous stringBuffer[bytes++] = '\\'; 229*61e349acSEd Tanous stringBuffer[bytes++] = 'u'; 230*61e349acSEd Tanous stringBuffer[bytes++] = 'f'; 231*61e349acSEd Tanous stringBuffer[bytes++] = 'f'; 232*61e349acSEd Tanous stringBuffer[bytes++] = 'f'; 233*61e349acSEd Tanous stringBuffer[bytes++] = 'd'; 234*61e349acSEd Tanous 235*61e349acSEd Tanous bytesAfterLastAccept = bytes; 236*61e349acSEd Tanous 237*61e349acSEd Tanous undumpedChars = 0; 238*61e349acSEd Tanous 239*61e349acSEd Tanous // continue processing the string 240*61e349acSEd Tanous state = utf8Accept; 241*61e349acSEd Tanous break; 242*61e349acSEd Tanous } 243*61e349acSEd Tanous 244*61e349acSEd Tanous default: // decode found yet incomplete multi-byte code point 245*61e349acSEd Tanous { 246*61e349acSEd Tanous ++undumpedChars; 247*61e349acSEd Tanous break; 248*61e349acSEd Tanous } 249*61e349acSEd Tanous } 250*61e349acSEd Tanous } 251*61e349acSEd Tanous 252*61e349acSEd Tanous // we finished processing the string 253*61e349acSEd Tanous if (state == utf8Accept) 254*61e349acSEd Tanous { 255*61e349acSEd Tanous // write buffer 256*61e349acSEd Tanous if (bytes > 0) 257*61e349acSEd Tanous { 258*61e349acSEd Tanous out.append(stringBuffer.data(), bytes); 259*61e349acSEd Tanous } 260*61e349acSEd Tanous } 261*61e349acSEd Tanous else 262*61e349acSEd Tanous { 263*61e349acSEd Tanous // write all accepted bytes 264*61e349acSEd Tanous out.append(stringBuffer.data(), bytesAfterLastAccept); 265*61e349acSEd Tanous out += "\\ufffd"; 266*61e349acSEd Tanous } 267*61e349acSEd Tanous } 268*61e349acSEd Tanous 269*61e349acSEd Tanous static unsigned int countDigits(uint64_t number) noexcept 270*61e349acSEd Tanous { 271*61e349acSEd Tanous unsigned int nDigits = 1; 272*61e349acSEd Tanous for (;;) 273*61e349acSEd Tanous { 274*61e349acSEd Tanous if (number < 10) 275*61e349acSEd Tanous { 276*61e349acSEd Tanous return nDigits; 277*61e349acSEd Tanous } 278*61e349acSEd Tanous if (number < 100) 279*61e349acSEd Tanous { 280*61e349acSEd Tanous return nDigits + 1; 281*61e349acSEd Tanous } 282*61e349acSEd Tanous if (number < 1000) 283*61e349acSEd Tanous { 284*61e349acSEd Tanous return nDigits + 2; 285*61e349acSEd Tanous } 286*61e349acSEd Tanous if (number < 10000) 287*61e349acSEd Tanous { 288*61e349acSEd Tanous return nDigits + 3; 289*61e349acSEd Tanous } 290*61e349acSEd Tanous number = number / 10000U; 291*61e349acSEd Tanous nDigits += 4; 292*61e349acSEd Tanous } 293*61e349acSEd Tanous } 294*61e349acSEd Tanous 295*61e349acSEd Tanous template <typename NumberType, 296*61e349acSEd Tanous std::enable_if_t<std::is_same<NumberType, uint64_t>::value or 297*61e349acSEd Tanous std::is_same<NumberType, int64_t>::value, 298*61e349acSEd Tanous int> = 0> 299*61e349acSEd Tanous void dumpInteger(std::string& out, NumberType number) 300*61e349acSEd Tanous { 301*61e349acSEd Tanous std::array<char, 64> numberbuffer{{}}; 302*61e349acSEd Tanous 303*61e349acSEd Tanous static constexpr std::array<std::array<char, 2>, 100> digitsTo99{{ 304*61e349acSEd Tanous {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, 305*61e349acSEd Tanous {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, 306*61e349acSEd Tanous {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, 307*61e349acSEd Tanous {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, 308*61e349acSEd Tanous {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, 309*61e349acSEd Tanous {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, 310*61e349acSEd Tanous {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, 311*61e349acSEd Tanous {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, 312*61e349acSEd Tanous {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, 313*61e349acSEd Tanous {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, 314*61e349acSEd Tanous {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, 315*61e349acSEd Tanous {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, 316*61e349acSEd Tanous {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, 317*61e349acSEd Tanous {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, 318*61e349acSEd Tanous {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, 319*61e349acSEd Tanous {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, 320*61e349acSEd Tanous {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}, 321*61e349acSEd Tanous }}; 322*61e349acSEd Tanous 323*61e349acSEd Tanous // special case for "0" 324*61e349acSEd Tanous if (number == 0) 325*61e349acSEd Tanous { 326*61e349acSEd Tanous out += '0'; 327*61e349acSEd Tanous return; 328*61e349acSEd Tanous } 329*61e349acSEd Tanous 330*61e349acSEd Tanous // use a pointer to fill the buffer 331*61e349acSEd Tanous auto* bufferPtr = numberbuffer.begin(); 332*61e349acSEd Tanous 333*61e349acSEd Tanous const bool isNegative = std::is_same<NumberType, int64_t>::value && 334*61e349acSEd Tanous !(number >= 0); // see issue #755 335*61e349acSEd Tanous uint64_t absValue = 0; 336*61e349acSEd Tanous 337*61e349acSEd Tanous unsigned int nChars = 0; 338*61e349acSEd Tanous 339*61e349acSEd Tanous if (isNegative) 340*61e349acSEd Tanous { 341*61e349acSEd Tanous *bufferPtr = '-'; 342*61e349acSEd Tanous absValue = static_cast<uint64_t>(0 - number); 343*61e349acSEd Tanous 344*61e349acSEd Tanous // account one more byte for the minus sign 345*61e349acSEd Tanous nChars = 1 + countDigits(absValue); 346*61e349acSEd Tanous } 347*61e349acSEd Tanous else 348*61e349acSEd Tanous { 349*61e349acSEd Tanous absValue = static_cast<uint64_t>(number); 350*61e349acSEd Tanous nChars = countDigits(absValue); 351*61e349acSEd Tanous } 352*61e349acSEd Tanous 353*61e349acSEd Tanous // spare 1 byte for '\0' 354*61e349acSEd Tanous if (nChars >= numberbuffer.size() - 1) 355*61e349acSEd Tanous { 356*61e349acSEd Tanous return; 357*61e349acSEd Tanous } 358*61e349acSEd Tanous 359*61e349acSEd Tanous // jump to the end to generate the string from backward 360*61e349acSEd Tanous // so we later avoid reversing the result 361*61e349acSEd Tanous std::advance(bufferPtr, nChars - 1); 362*61e349acSEd Tanous 363*61e349acSEd Tanous // Fast int2ascii implementation inspired by "Fastware" talk by Andrei 364*61e349acSEd Tanous // Alexandrescu See: https://www.youtube.com/watch?v=o4-CwDo2zpg 365*61e349acSEd Tanous while (absValue >= 100) 366*61e349acSEd Tanous { 367*61e349acSEd Tanous const auto digitsIndex = static_cast<unsigned>((absValue % 100)); 368*61e349acSEd Tanous absValue /= 100; 369*61e349acSEd Tanous *bufferPtr = digitsTo99[digitsIndex][1]; 370*61e349acSEd Tanous bufferPtr = std::prev(bufferPtr); 371*61e349acSEd Tanous *bufferPtr = digitsTo99[digitsIndex][0]; 372*61e349acSEd Tanous bufferPtr = std::prev(bufferPtr); 373*61e349acSEd Tanous } 374*61e349acSEd Tanous 375*61e349acSEd Tanous if (absValue >= 10) 376*61e349acSEd Tanous { 377*61e349acSEd Tanous const auto digitsIndex = static_cast<unsigned>(absValue); 378*61e349acSEd Tanous *bufferPtr = digitsTo99[digitsIndex][1]; 379*61e349acSEd Tanous bufferPtr = std::prev(bufferPtr); 380*61e349acSEd Tanous *bufferPtr = digitsTo99[digitsIndex][0]; 381*61e349acSEd Tanous // assignment never used: bufferPtr = std::prev(bufferPtr); 382*61e349acSEd Tanous } 383*61e349acSEd Tanous else 384*61e349acSEd Tanous { 385*61e349acSEd Tanous *bufferPtr = static_cast<char>('0' + absValue); 386*61e349acSEd Tanous // assignment never used: bufferPtr = std::prev(bufferPtr); 387*61e349acSEd Tanous } 388*61e349acSEd Tanous 389*61e349acSEd Tanous out.append(numberbuffer.data(), nChars); 390*61e349acSEd Tanous } 391*61e349acSEd Tanous 392*61e349acSEd Tanous static void dumpfloat(std::string& out, double number) 393*61e349acSEd Tanous { 394*61e349acSEd Tanous // NaN / inf 395*61e349acSEd Tanous if (!std::isfinite(number)) 396*61e349acSEd Tanous { 397*61e349acSEd Tanous out += "null"; 398*61e349acSEd Tanous return; 399*61e349acSEd Tanous } 400*61e349acSEd Tanous std::array<char, 64> numberbuffer{{}}; 401*61e349acSEd Tanous 402*61e349acSEd Tanous ::nlohmann::detail::to_chars(numberbuffer.begin(), numberbuffer.end(), 403*61e349acSEd Tanous number); 404*61e349acSEd Tanous 405*61e349acSEd Tanous out += numberbuffer.data(); 406*61e349acSEd Tanous } 407*61e349acSEd Tanous 408*61e349acSEd Tanous static void dump(std::string& out, const nlohmann::json& val) 409*61e349acSEd Tanous { 410*61e349acSEd Tanous switch (val.type()) 411*61e349acSEd Tanous { 412*61e349acSEd Tanous case nlohmann::json::value_t::object: 413*61e349acSEd Tanous { 414*61e349acSEd Tanous if (val.empty()) 415*61e349acSEd Tanous { 416*61e349acSEd Tanous out += "{}"; 417*61e349acSEd Tanous return; 418*61e349acSEd Tanous } 419*61e349acSEd Tanous 420*61e349acSEd Tanous out += "{"; 421*61e349acSEd Tanous 422*61e349acSEd Tanous out += "<div class=tab>"; 423*61e349acSEd Tanous for (auto i = val.begin(); i != val.end();) 424*61e349acSEd Tanous { 425*61e349acSEd Tanous out += """; 426*61e349acSEd Tanous dumpEscaped(out, i.key()); 427*61e349acSEd Tanous out += "": "; 428*61e349acSEd Tanous 429*61e349acSEd Tanous bool inATag = false; 430*61e349acSEd Tanous if (i.key() == "@odata.id" || i.key() == "@odata.context" || 431*61e349acSEd Tanous i.key() == "Members@odata.nextLink" || i.key() == "Uri") 432*61e349acSEd Tanous { 433*61e349acSEd Tanous inATag = true; 434*61e349acSEd Tanous out += "<a href=\""; 435*61e349acSEd Tanous dumpEscaped(out, i.value()); 436*61e349acSEd Tanous out += "\">"; 437*61e349acSEd Tanous } 438*61e349acSEd Tanous dump(out, i.value()); 439*61e349acSEd Tanous if (inATag) 440*61e349acSEd Tanous { 441*61e349acSEd Tanous out += "</a>"; 442*61e349acSEd Tanous } 443*61e349acSEd Tanous i++; 444*61e349acSEd Tanous if (i != val.end()) 445*61e349acSEd Tanous { 446*61e349acSEd Tanous out += ","; 447*61e349acSEd Tanous } 448*61e349acSEd Tanous out += "<br>"; 449*61e349acSEd Tanous } 450*61e349acSEd Tanous out += "</div>"; 451*61e349acSEd Tanous out += '}'; 452*61e349acSEd Tanous 453*61e349acSEd Tanous return; 454*61e349acSEd Tanous } 455*61e349acSEd Tanous 456*61e349acSEd Tanous case nlohmann::json::value_t::array: 457*61e349acSEd Tanous { 458*61e349acSEd Tanous if (val.empty()) 459*61e349acSEd Tanous { 460*61e349acSEd Tanous out += "[]"; 461*61e349acSEd Tanous return; 462*61e349acSEd Tanous } 463*61e349acSEd Tanous 464*61e349acSEd Tanous out += "["; 465*61e349acSEd Tanous 466*61e349acSEd Tanous out += "<div class=tab>"; 467*61e349acSEd Tanous 468*61e349acSEd Tanous // first n-1 elements 469*61e349acSEd Tanous for (auto i = val.cbegin(); i != val.cend() - 1; ++i) 470*61e349acSEd Tanous { 471*61e349acSEd Tanous dump(out, *i); 472*61e349acSEd Tanous out += ",<br>"; 473*61e349acSEd Tanous } 474*61e349acSEd Tanous 475*61e349acSEd Tanous // last element 476*61e349acSEd Tanous dump(out, val.back()); 477*61e349acSEd Tanous 478*61e349acSEd Tanous out += "</div>"; 479*61e349acSEd Tanous out += ']'; 480*61e349acSEd Tanous 481*61e349acSEd Tanous return; 482*61e349acSEd Tanous } 483*61e349acSEd Tanous 484*61e349acSEd Tanous case nlohmann::json::value_t::string: 485*61e349acSEd Tanous { 486*61e349acSEd Tanous out += '\"'; 487*61e349acSEd Tanous const std::string* ptr = val.get_ptr<const std::string*>(); 488*61e349acSEd Tanous dumpEscaped(out, *ptr); 489*61e349acSEd Tanous out += '\"'; 490*61e349acSEd Tanous return; 491*61e349acSEd Tanous } 492*61e349acSEd Tanous 493*61e349acSEd Tanous case nlohmann::json::value_t::boolean: 494*61e349acSEd Tanous { 495*61e349acSEd Tanous if (*(val.get_ptr<const bool*>())) 496*61e349acSEd Tanous { 497*61e349acSEd Tanous out += "true"; 498*61e349acSEd Tanous } 499*61e349acSEd Tanous else 500*61e349acSEd Tanous { 501*61e349acSEd Tanous out += "false"; 502*61e349acSEd Tanous } 503*61e349acSEd Tanous return; 504*61e349acSEd Tanous } 505*61e349acSEd Tanous 506*61e349acSEd Tanous case nlohmann::json::value_t::number_integer: 507*61e349acSEd Tanous { 508*61e349acSEd Tanous dumpInteger(out, *(val.get_ptr<const int64_t*>())); 509*61e349acSEd Tanous return; 510*61e349acSEd Tanous } 511*61e349acSEd Tanous 512*61e349acSEd Tanous case nlohmann::json::value_t::number_unsigned: 513*61e349acSEd Tanous { 514*61e349acSEd Tanous dumpInteger(out, *(val.get_ptr<const uint64_t*>())); 515*61e349acSEd Tanous return; 516*61e349acSEd Tanous } 517*61e349acSEd Tanous 518*61e349acSEd Tanous case nlohmann::json::value_t::number_float: 519*61e349acSEd Tanous { 520*61e349acSEd Tanous dumpfloat(out, *(val.get_ptr<const double*>())); 521*61e349acSEd Tanous return; 522*61e349acSEd Tanous } 523*61e349acSEd Tanous 524*61e349acSEd Tanous case nlohmann::json::value_t::discarded: 525*61e349acSEd Tanous { 526*61e349acSEd Tanous out += "<discarded>"; 527*61e349acSEd Tanous return; 528*61e349acSEd Tanous } 529*61e349acSEd Tanous 530*61e349acSEd Tanous case nlohmann::json::value_t::null: 531*61e349acSEd Tanous { 532*61e349acSEd Tanous out += "null"; 533*61e349acSEd Tanous return; 534*61e349acSEd Tanous } 535*61e349acSEd Tanous case nlohmann::json::value_t::binary: 536*61e349acSEd Tanous { 537*61e349acSEd Tanous // Do nothing; Should never happen. 538*61e349acSEd Tanous return; 539*61e349acSEd Tanous } 540*61e349acSEd Tanous } 541*61e349acSEd Tanous } 542*61e349acSEd Tanous 543*61e349acSEd Tanous void dumpHtml(std::string& out, const nlohmann::json& json) 544*61e349acSEd Tanous { 545*61e349acSEd Tanous out += "<html>\n" 546*61e349acSEd Tanous "<head>\n" 547*61e349acSEd Tanous "<title>Redfish API</title>\n" 548*61e349acSEd Tanous "<link href=\"/redfish.css\" rel=\"stylesheet\">\n" 549*61e349acSEd Tanous "</head>\n" 550*61e349acSEd Tanous "<body>\n" 551*61e349acSEd Tanous "<div class=\"container\">\n" 552*61e349acSEd Tanous "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" " 553*61e349acSEd Tanous "height=\"406px\" " 554*61e349acSEd Tanous "width=\"576px\">\n" 555*61e349acSEd Tanous "<div class=\"content\">\n"; 556*61e349acSEd Tanous dump(out, json); 557*61e349acSEd Tanous out += "</div>\n" 558*61e349acSEd Tanous "</div>\n" 559*61e349acSEd Tanous "</body>\n" 560*61e349acSEd Tanous "</html>\n"; 561*61e349acSEd Tanous } 562*61e349acSEd Tanous 563*61e349acSEd Tanous void prettyPrintJson(crow::Response& res) 564*61e349acSEd Tanous { 565*61e349acSEd Tanous json_html_util::dumpHtml(res.body(), res.jsonValue); 566*61e349acSEd Tanous 567*61e349acSEd Tanous res.addHeader(boost::beast::http::field::content_type, 568*61e349acSEd Tanous "text/html;charset=UTF-8"); 569*61e349acSEd Tanous } 570*61e349acSEd Tanous 571*61e349acSEd Tanous } // namespace json_html_util 572