1 // Filename: format.cpp
2 #include <cctype>  // std::isprint()
3 #include <sstream> // std::ostringstream
4 #include "dc/DistributedType.h"
5 #include "dc/ArrayType.h"
6 #include "dc/Struct.h"
7 #include "dc/Field.h"
8 #include "dc/Method.h"
9 #include "dc/Parameter.h"
10 #include "util/byteorder.h"
11 
12 #if defined(_WIN32) && defined(_MSC_VER) && _MSC_VER <= 1800
13 #define snprintf sprintf_s
14 #endif
15 
16 
17 #include "format.h"
18 using namespace std;
19 namespace dclass   // open namespace dclass
20 {
21 
22 // A Formatter steps through packed data and unpacks it as a .dc file parameter format.
23 //     This is created and called by format() to handle formatting.
24 struct Formatter {
25     const uint8_t* in;
26     ostream &out;
27     size_t offset;
28     size_t end;
29 
Formatterdclass::Formatter30     Formatter(const vector<uint8_t> &in, ostream &out) :
31         in(&in[0]), out(out), offset(0), end(in.size())
32     {
33     }
34 
Formatterdclass::Formatter35     Formatter(const string &in, ostream &out) :
36         in((const uint8_t*)&in[0]), out(out), offset(0), end(in.size())
37     {
38     }
39 
Formatterdclass::Formatter40     Formatter(const uint8_t* buffer, size_t length, ostream &out) :
41         in(buffer), out(out), offset(0), end(length)
42     {
43     }
44 
remainingdclass::Formatter45     inline bool remaining(sizetag_t length)
46     {
47         return (offset + length) <= end;
48     }
49 
read_lengthdclass::Formatter50     inline sizetag_t read_length()
51     {
52         sizetag_t v = swap_le(*(sizetag_t*)(in + offset));
53         offset += sizeof(sizetag_t);
54         return v;
55     }
56 
formatdclass::Formatter57     bool format(const DistributedType* dtype)
58     {
59         Type type = dtype->get_type();
60         switch(type) {
61         case T_INVALID: {
62             out << "<invalid>";
63             break;
64         }
65         case T_INT8: {
66             if(!remaining(sizeof(int8_t))) {
67                 return false;
68             }
69             int v = *(int8_t*)(in + offset);
70             offset += sizeof(int8_t);
71             out << v;
72             break;
73         }
74         case T_INT16: {
75             if(!remaining(sizeof(int16_t))) {
76                 return false;
77             }
78             int v = swap_le(*(int16_t*)(in + offset));
79             offset += sizeof(int16_t);
80             out << v;
81             break;
82         }
83         case T_INT32: {
84             if(!remaining(sizeof(int32_t))) {
85                 return false;
86             }
87             int v = swap_le(*(int32_t*)(in + offset));
88             offset += sizeof(int32_t);
89             out << v;
90             break;
91         }
92         case T_INT64: {
93             if(!remaining(sizeof(int64_t))) {
94                 return false;
95             }
96             int64_t v = swap_le(*(int64_t*)(in + offset));
97             offset += sizeof(int64_t);
98             out << v;
99             break;
100         }
101         case T_UINT8: {
102             if(!remaining(sizeof(uint8_t))) {
103                 return false;
104             }
105             unsigned int v = *(uint8_t*)(in + offset);
106             offset += sizeof(uint8_t);
107             out << v;
108             break;
109         }
110         case T_UINT16: {
111             if(!remaining(sizeof(uint16_t))) {
112                 return false;
113             }
114             unsigned int v = swap_le(*(uint16_t*)(in + offset));
115             offset += sizeof(uint16_t);
116             out << v;
117             break;
118         }
119         case T_UINT32: {
120             if(!remaining(sizeof(uint32_t))) {
121                 return false;
122             }
123             unsigned int v = swap_le(*(uint32_t*)(in + offset));
124             offset += sizeof(uint32_t);
125             out << v;
126             break;
127         }
128         case T_UINT64: {
129             if(!remaining(sizeof(uint64_t))) {
130                 return false;
131             }
132             uint64_t v = swap_le(*(uint64_t*)(in + offset));
133             offset += sizeof(uint64_t);
134             out << v;
135             break;
136         }
137         case T_FLOAT32: {
138             if(!remaining(sizeof(float))) {
139                 return false;
140             }
141             float v = (float)swap_le(*(float*)(in + offset));
142             offset += sizeof(float);
143             out << v;
144             break;
145         }
146         case T_FLOAT64: {
147             if(!remaining(sizeof(double))) {
148                 return false;
149             }
150             double v = (double)swap_le(*(double*)(in + offset));
151             offset += sizeof(double);
152             out << v;
153             break;
154         }
155         case T_CHAR: {
156             if(!remaining(sizeof(char))) {
157                 return false;
158             }
159             char v = *(char*)(in + offset);
160             format_quoted('\'', string(1, v), out);
161             offset += sizeof(char);
162             break;
163         }
164         case T_STRING: {
165             // Read string length
166             sizetag_t length = dtype->get_size();
167 
168 
169             // If we have a string alias format as a quoted string
170             if(dtype->has_alias() && dtype->get_alias() == "string") {
171                 // Read string
172                 if(!remaining(length)) {
173                     return false;
174                 }
175                 string str((const char*)in + offset, length);
176                 offset += length;
177 
178                 // Enquoute and escape string then output
179                 format_quoted('"', str, out);
180             } else {
181                 // Otherwise format as an array of char
182                 out << '[';
183                 const ArrayType* arr = dtype->as_array();
184                 bool ok = format(arr->get_element_type());
185                 if(!ok) {
186                     return false;
187                 }
188                 for(unsigned int i = 1; i < arr->get_array_size(); ++i) {
189                     out << ", ";
190                     ok = format(arr->get_element_type());
191                     if(!ok) {
192                         return false;
193                     }
194                 }
195 
196                 out << ']';
197             }
198             break;
199         }
200         case T_VARSTRING: {
201 
202             // If we have a string alias format as a quoted string
203             if(dtype->has_alias() && dtype->get_alias() == "string") {
204                 // Read string length
205                 if(!remaining(sizeof(sizetag_t))) {
206                     return false;
207                 }
208                 sizetag_t length = read_length();
209 
210                 // Read string
211                 if(!remaining(length)) {
212                     return false;
213                 }
214                 string str((const char*)in + offset, length);
215                 offset += length;
216 
217                 // Enquoute and escape string then output
218                 format_quoted('"', str, out);
219             } else {
220                 // Otherwise format as an array of char
221                 out << '[';
222                 // Read array byte length
223                 if(!remaining(sizeof(sizetag_t))) {
224                     out << ']';
225                     return false;
226                 }
227                 sizetag_t length = read_length();
228 
229                 if(length == 0) {
230                     out << ']';
231                     break;
232                 }
233 
234                 // Read array
235                 if(!remaining(length)) {
236                     out << ']';
237                     return false;
238                 }
239                 size_t array_end = offset + length;
240 
241                 const ArrayType* arr = dtype->as_array();
242                 bool ok = format(arr->get_element_type());
243                 if(!ok) {
244                     out << ']';
245                     return false;
246                 }
247 
248                 while(offset < array_end) {
249                     out << ", ";
250                     ok = format(arr->get_element_type());
251                     if(!ok) {
252                         out << ']';
253                         return false;
254                     }
255                 }
256 
257                 // Check to make sure we didn't overshoot the array while reading
258                 if(offset > array_end) {
259                     out << ']';
260                     return false;
261                 }
262 
263                 out << ']';
264             }
265 
266             break;
267         }
268         case T_BLOB: {
269             // Read blob length
270             sizetag_t length = dtype->get_size();
271 
272             // If we have a blob alias format as a hex constant
273             if(dtype->has_alias() && dtype->get_alias() == "blob") {
274                 // Read blob
275                 if(!remaining(length)) {
276                     return false;
277                 }
278                 string blob((const char*)in + offset, length);
279                 offset += length;
280 
281                 // Format blob as a hex constant then output
282                 format_hex(blob, out);
283             } else {
284                 // Otherwise format as an array of uint8
285                 out << '[';
286                 const ArrayType* arr = dtype->as_array();
287                 bool ok = format(arr->get_element_type());
288                 if(!ok) {
289                     out << ']';
290                     return false;
291                 }
292 
293                 for(unsigned int i = 1; i < arr->get_array_size(); ++i) {
294                     out << ", ";
295                     ok = format(arr->get_element_type());
296                     if(!ok) {
297                         out << ']';
298                         return false;
299                     }
300                 }
301 
302                 out << ']';
303             }
304 
305             break;
306         }
307         case T_VARBLOB: {
308             // If we have a blob alias format as a hex constant
309             if(dtype->has_alias() && dtype->get_alias() == "blob") {
310                 // Read blob length
311                 if(!remaining(sizeof(sizetag_t))) {
312                     return false;
313                 }
314                 sizetag_t length = read_length();
315 
316 
317                 // Read blob with length
318                 if(!remaining(length)) {
319                     return false;
320                 }
321                 string blob((const char*)in + offset - 2, length + 2);
322                 offset += length;
323 
324                 // Format blob and length as a hex constant then output
325                 format_hex(blob, out);
326             } else {
327                 // Otherwise format as an array of uint8
328                 out << '[';
329                 // Read array byte length
330                 if(!remaining(sizeof(sizetag_t))) {
331                     out << ']';
332                     return false;
333                 }
334                 sizetag_t length = read_length();
335 
336                 if(length == 0) {
337                     out << ']';
338                     break;
339                 }
340                 // Read array
341                 if(!remaining(length)) {
342                     out << ']';
343                     return false;
344                 }
345                 size_t array_end = offset + length;
346 
347                 const ArrayType* arr = dtype->as_array();
348                 bool ok = format(arr->get_element_type());
349                 if(!ok) {
350                     out << ']';
351                     return false;
352                 }
353                 while(offset < array_end) {
354                     out << ", ";
355                     ok = format(arr->get_element_type());
356                     if(!ok) {
357                         out << ']';
358                         return false;
359                     }
360                 }
361 
362                 // Check to make sure we didn't overshoot the array while reading
363                 if(offset > array_end) {
364                     out << ']';
365                     return false;
366                 }
367 
368                 out << ']';
369             }
370 
371             break;
372         }
373         case T_ARRAY: {
374             out << '[';
375             const ArrayType* arr = dtype->as_array();
376             bool ok = format(arr->get_element_type());
377             if(!ok) {
378                 out << ']';
379                 return false;
380             }
381             for(unsigned int i = 1; i < arr->get_array_size(); ++i) {
382                 out << ", ";
383                 ok = format(arr->get_element_type());
384                 if(!ok) {
385                     out << ']';
386                     return false;
387                 }
388             }
389 
390             out << ']';
391             break;
392         }
393         case T_VARARRAY: {
394             out << '[';
395             // Read array byte length
396             if(!remaining(sizeof(sizetag_t))) {
397                 out << ']';
398                 return false;
399             }
400             sizetag_t length = read_length();
401 
402             if(length == 0) {
403                 out << ']';
404                 break;
405             }
406 
407             // Read array
408             if(!remaining(length)) {
409                 out << ']';
410                 return false;
411             }
412             size_t array_end = offset + length;
413 
414             const ArrayType* arr = dtype->as_array();
415             bool ok = format(arr->get_element_type());
416             if(!ok) {
417                 out << ']';
418                 return false;
419             }
420             while(offset < array_end) {
421                 out << ", ";
422                 ok = format(arr->get_element_type());
423                 if(!ok) {
424                     out << ']';
425                     return false;
426                 }
427             }
428 
429             // Check to make sure we didn't overshoot the array while reading
430             if(offset > array_end) {
431                 out << ']';
432                 return false;
433             }
434 
435             out << ']';
436             break;
437         }
438         case T_STRUCT: {
439             out << '{';
440             const Struct* strct = dtype->as_struct();
441             size_t num_fields = strct->get_num_fields();
442             if(num_fields > 0) {
443                 bool ok = format(strct->get_field(0)->get_type());
444                 if(!ok) {
445                     out << '}';
446                     return false;
447                 }
448                 for(unsigned int i = 1; i < num_fields; ++i) {
449                     out << ", ";
450                     ok = format(strct->get_field(i)->get_type());
451                     if(!ok) {
452                         out << '}';
453                         return false;
454                     }
455                 }
456             }
457             out << '}';
458             break;
459         }
460         case T_METHOD: {
461             out << '(';
462             const Method* method = dtype->as_method();
463             size_t num_params = method->get_num_parameters();
464             if(num_params > 0) {
465                 bool ok = format(method->get_parameter(0)->get_type());
466                 if(!ok) {
467                     out << ')';
468                     return false;
469                 }
470                 for(unsigned int i = 1; i < num_params; ++i) {
471                     out << ", ";
472                     ok = format(method->get_parameter(i)->get_type());
473                     if(!ok) {
474                         out << ')';
475                         return false;
476                     }
477                 }
478             }
479             out << ')';
480             break;
481         }
482         default: {
483             out << "<error>";
484             return false;
485         }
486         }
487         return true;
488     }
489 };
490 
491 // format unpacks the packed data into a string formatted for a .dc file.
492 //     This is used to produce default values when outputting a distributed class to a file.
format_value(const DistributedType * dtype,const vector<uint8_t> & packed)493 string format_value(const DistributedType *dtype, const vector<uint8_t> &packed)
494 {
495     ostringstream ss;
496     format_value(dtype, packed, ss);
497     return ss.str();
498 }
format_value(const DistributedType * dtype,const string & packed)499 string format_value(const DistributedType *dtype, const string &packed)
500 {
501     ostringstream ss;
502     format_value(dtype, packed, ss);
503     return ss.str();
504 }
format_value(const DistributedType * dtype,const vector<uint8_t> & packed,ostream & out)505 void format_value(const DistributedType *dtype, const vector<uint8_t> &packed, ostream &out)
506 {
507     Formatter formatter(packed, out);
508     formatter.format(dtype);
509 }
format_value(const DistributedType * dtype,const string & packed,ostream & out)510 void format_value(const DistributedType *dtype, const string &packed, ostream &out)
511 {
512     Formatter formatter(packed, out);
513     formatter.format(dtype);
514 }
515 
516 // format_hex outputs <str> to <out> as a hexidecimal constant enclosed in angle-brackets (<>).
format_hex(const string & str,ostream & out)517 void format_hex(const string &str, ostream &out)
518 {
519     out << '<';
520     for(auto it = str.begin(); it != str.end(); ++it) {
521         char infer[10];
522         snprintf(infer, 10, "%02x", (unsigned char)(*it));
523         out << infer;
524     }
525     out << '>';
526 }
format_hex(const string & str)527 string format_hex(const string &str)
528 {
529     ostringstream ss;
530     format_hex(str, ss);
531     return ss.str();
532 }
533 
534 // format_quoted outputs <str> to <out> quoted with the character <quote_mark>.
535 //     Any instances of backslash (\) or the quoute character in the string are escaped.
536 //     Non-printable characters are replaced with an escaped hexidecimal constant.
format_quoted(char quote_mark,const string & str,ostream & out)537 void format_quoted(char quote_mark, const string &str, ostream &out)
538 {
539     out << quote_mark;
540     for(auto it = str.begin(); it != str.end(); ++it) {
541         char c = *it;
542         if(c == quote_mark || c == '\\') {
543             // escape the character
544             out << '\\' << c;
545 
546         } else if(!isprint(c)) { // character is not a printable ascii character
547             // print the character as an escaped hexidecimal character constant
548             char infer[10];
549             snprintf(infer, 10, "%02x", (unsigned char)c);
550             out << "\\x" << infer;
551         } else {
552             out << c;
553         }
554     }
555     out << quote_mark;
556 }
format_quoted(char quote_mark,const string & str)557 string format_quoted(char quote_mark, const string &str)
558 {
559     ostringstream ss;
560     format_quoted(quote_mark, str, ss);
561     return ss.str();
562 }
563 
564 
565 } // close namespace dclass
566