xref: /openbmc/bmcweb/src/json_html_serializer.cpp (revision 61e349acc34787ad80aebf7809ab73c4f03c7520)
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 += "&quot";
426*61e349acSEd Tanous                 dumpEscaped(out, i.key());
427*61e349acSEd Tanous                 out += "&quot: ";
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