xref: /openbmc/bmcweb/src/json_html_serializer.cpp (revision 27b0cf90f6cba207837f5c263a45c6ea5651975b)
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 += "&quot";
42661e349acSEd Tanous                 dumpEscaped(out, i.key());
42761e349acSEd Tanous                 out += "&quot: ";
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