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