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",
188*bd79bce8SPatrick Williams static_cast<uint16_t>(
189*bd79bce8SPatrick Williams 0xD7C0 + (codePoint >> 10)),
190*bd79bce8SPatrick Williams static_cast<uint16_t>(
191*bd79bce8SPatrick Williams 0xDC00 + (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 += """;
43361e349acSEd Tanous dumpEscaped(out, i.key());
43461e349acSEd Tanous out += "": ";
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*>();
495dc8cfa66SEd Tanous if (ptr == nullptr)
496dc8cfa66SEd Tanous {
497dc8cfa66SEd Tanous return;
498dc8cfa66SEd 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"
559a529a6aaSEd Tanous "<link href=\"/styles/redfish.css\" rel=\"stylesheet\">\n"
56061e349acSEd Tanous "</head>\n"
56161e349acSEd Tanous "<body>\n"
56261e349acSEd Tanous "<div class=\"container\">\n"
563a529a6aaSEd Tanous "<img src=\"/images/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