1 /* Copyright (C) 2014 SkySQL Ab, MariaDB Corporation Ab
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
15 
16 #include "mariadb.h"
17 #include "sql_priv.h"
18 #include "sql_string.h"
19 
20 #include "my_json_writer.h"
21 
append_indent()22 void Json_writer::append_indent()
23 {
24   if (!document_start)
25     output.append('\n');
26   for (int i=0; i< indent_level; i++)
27     output.append(' ');
28 }
29 
start_object()30 void Json_writer::start_object()
31 {
32   fmt_helper.on_start_object();
33 
34   if (!element_started)
35     start_element();
36 
37   output.append("{");
38   indent_level+=INDENT_SIZE;
39   first_child=true;
40   element_started= false;
41   document_start= false;
42 }
43 
start_array()44 void Json_writer::start_array()
45 {
46   if (fmt_helper.on_start_array())
47     return;
48 
49   if (!element_started)
50     start_element();
51 
52   output.append("[");
53   indent_level+=INDENT_SIZE;
54   first_child=true;
55   element_started= false;
56   document_start= false;
57 }
58 
59 
end_object()60 void Json_writer::end_object()
61 {
62   indent_level-=INDENT_SIZE;
63   if (!first_child)
64     append_indent();
65   output.append("}");
66 }
67 
68 
end_array()69 void Json_writer::end_array()
70 {
71   if (fmt_helper.on_end_array())
72     return;
73   indent_level-=INDENT_SIZE;
74   if (!first_child)
75     append_indent();
76   output.append("]");
77 }
78 
79 
add_member(const char * name)80 Json_writer& Json_writer::add_member(const char *name)
81 {
82   if (fmt_helper.on_add_member(name))
83     return *this; // handled
84 
85   // assert that we are in an object
86   DBUG_ASSERT(!element_started);
87   start_element();
88 
89   output.append('"');
90   output.append(name);
91   output.append("\": ");
92   return *this;
93 }
94 
95 
96 /*
97   Used by formatting helper to print something that is formatted by the helper.
98   We should only separate it from the previous element.
99 */
100 
start_sub_element()101 void Json_writer::start_sub_element()
102 {
103   //element_started= true;
104   if (first_child)
105     first_child= false;
106   else
107     output.append(',');
108 
109   append_indent();
110 }
111 
112 
start_element()113 void Json_writer::start_element()
114 {
115   element_started= true;
116 
117   if (first_child)
118     first_child= false;
119   else
120     output.append(',');
121 
122   append_indent();
123 }
124 
add_ll(longlong val)125 void Json_writer::add_ll(longlong val)
126 {
127   char buf[64];
128   my_snprintf(buf, sizeof(buf), "%lld", val);
129   add_unquoted_str(buf);
130 }
131 
add_ull(ulonglong val)132 void Json_writer::add_ull(ulonglong val)
133 {
134   char buf[64];
135   my_snprintf(buf, sizeof(buf), "%llu", val);
136   add_unquoted_str(buf);
137 }
138 
139 
140 /* Add a memory size, printing in Kb, Kb, Gb if necessary */
add_size(longlong val)141 void Json_writer::add_size(longlong val)
142 {
143   char buf[64];
144   if (val < 1024)
145     my_snprintf(buf, sizeof(buf), "%lld", val);
146   else if (val < 1024*1024*16)
147   {
148     /* Values less than 16MB are specified in KB for precision */
149     size_t len= my_snprintf(buf, sizeof(buf), "%lld", val/1024);
150     strcpy(buf + len, "Kb");
151   }
152   else
153   {
154     size_t len= my_snprintf(buf, sizeof(buf), "%lld", val/(1024*1024));
155     strcpy(buf + len, "Mb");
156   }
157   add_str(buf);
158 }
159 
160 
add_double(double val)161 void Json_writer::add_double(double val)
162 {
163   char buf[64];
164   my_snprintf(buf, sizeof(buf), "%lg", val);
165   add_unquoted_str(buf);
166 }
167 
168 
add_bool(bool val)169 void Json_writer::add_bool(bool val)
170 {
171   add_unquoted_str(val? "true" : "false");
172 }
173 
174 
add_null()175 void Json_writer::add_null()
176 {
177   add_unquoted_str("null");
178 }
179 
180 
add_unquoted_str(const char * str)181 void Json_writer::add_unquoted_str(const char* str)
182 {
183   if (fmt_helper.on_add_str(str))
184     return;
185 
186   if (!element_started)
187     start_element();
188 
189   output.append(str);
190   element_started= false;
191 }
192 
193 
add_str(const char * str)194 void Json_writer::add_str(const char *str)
195 {
196   if (fmt_helper.on_add_str(str))
197     return;
198 
199   if (!element_started)
200     start_element();
201 
202   output.append('"');
203   output.append(str);
204   output.append('"');
205   element_started= false;
206 }
207 
208 
add_str(const String & str)209 void Json_writer::add_str(const String &str)
210 {
211   add_str(str.ptr());
212 }
213 
214 
on_add_member(const char * name)215 bool Single_line_formatting_helper::on_add_member(const char *name)
216 {
217   DBUG_ASSERT(state== INACTIVE || state == DISABLED);
218   if (state != DISABLED)
219   {
220     // remove everything from the array
221     buf_ptr= buffer;
222 
223     //append member name to the array
224     size_t len= strlen(name);
225     if (len < MAX_LINE_LEN)
226     {
227       memcpy(buf_ptr, name, len);
228       buf_ptr+=len;
229       *(buf_ptr++)= 0;
230 
231       line_len= owner->indent_level + (uint)len + 1;
232       state= ADD_MEMBER;
233       return true; // handled
234     }
235   }
236   return false; // not handled
237 }
238 
239 
on_start_array()240 bool Single_line_formatting_helper::on_start_array()
241 {
242   if (state == ADD_MEMBER)
243   {
244     state= IN_ARRAY;
245     return true; // handled
246   }
247   else
248   {
249     if (state != DISABLED)
250       state= INACTIVE;
251     // TODO: what if we have accumulated some stuff already? shouldn't we
252     // flush it?
253     return false; // not handled
254   }
255 }
256 
257 
on_end_array()258 bool Single_line_formatting_helper::on_end_array()
259 {
260   if (state == IN_ARRAY)
261   {
262     flush_on_one_line();
263     state= INACTIVE;
264     return true; // handled
265   }
266   return false; // not handled
267 }
268 
269 
on_start_object()270 void Single_line_formatting_helper::on_start_object()
271 {
272   // Nested objects will not be printed on one line
273   disable_and_flush();
274 }
275 
276 
on_add_str(const char * str)277 bool Single_line_formatting_helper::on_add_str(const char *str)
278 {
279   if (state == IN_ARRAY)
280   {
281     size_t len= strlen(str);
282 
283     // New length will be:
284     //  "$string",
285     //  quote + quote + comma + space = 4
286     if (line_len + len + 4 > MAX_LINE_LEN)
287     {
288       disable_and_flush();
289       return false; // didn't handle the last element
290     }
291 
292     //append string to array
293     memcpy(buf_ptr, str, len);
294     buf_ptr+=len;
295     *(buf_ptr++)= 0;
296     line_len += (uint)len + 4;
297     return true; // handled
298   }
299 
300   disable_and_flush();
301   return false; // not handled
302 }
303 
304 
305 /*
306   Append everything accumulated to the output on one line
307 */
308 
flush_on_one_line()309 void Single_line_formatting_helper::flush_on_one_line()
310 {
311   owner->start_sub_element();
312   char *ptr= buffer;
313   int nr= 0;
314   while (ptr < buf_ptr)
315   {
316     char *str= ptr;
317 
318     if (nr == 0)
319     {
320       owner->output.append('"');
321       owner->output.append(str);
322       owner->output.append("\": ");
323       owner->output.append('[');
324     }
325     else
326     {
327       if (nr != 1)
328         owner->output.append(", ");
329       owner->output.append('"');
330       owner->output.append(str);
331       owner->output.append('"');
332     }
333     nr++;
334 
335     while (*ptr!=0)
336       ptr++;
337     ptr++;
338   }
339   owner->output.append(']');
340   /* We've printed out the contents of the buffer, mark it as empty */
341   buf_ptr= buffer;
342 }
343 
344 
disable_and_flush()345 void Single_line_formatting_helper::disable_and_flush()
346 {
347   if (state == DISABLED)
348     return;
349 
350   bool start_array= (state == IN_ARRAY);
351   state= DISABLED;
352   // deactivate ourselves and flush all accumulated calls.
353   char *ptr= buffer;
354   int nr= 0;
355   while (ptr < buf_ptr)
356   {
357     char *str= ptr;
358     if (nr == 0)
359     {
360       owner->add_member(str);
361       if (start_array)
362         owner->start_array();
363     }
364     else
365     {
366       //if (nr == 1)
367       //  owner->start_array();
368       owner->add_str(str);
369     }
370 
371     nr++;
372     while (*ptr!=0)
373       ptr++;
374     ptr++;
375   }
376   buf_ptr= buffer;
377   state= INACTIVE;
378 }
379 
380