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