Atlas 0.7.0
Networking protocol for the Worldforge system.
Bach.cpp
1// This file may be redistributed and modified under the terms of the
2// GNU Lesser General Public License (See COPYING for details).
3// Copyright (C) 2002 Michael Koch <konqueror@gmx.de>
4
5// $Id$
6
7#include <Atlas/Codecs/Bach.h>
8
9#include <Atlas/Debug.h>
10
11#include <iostream>
12
13#include <cstdlib>
14
15static const bool debug_flag = false;
16
17namespace Atlas {
18 namespace Codecs {
19
20 Bach::Bach(std::istream &in, std::ostream &out, Atlas::Bridge &b)
21 : m_istream(in), m_ostream(out), m_bridge(b), m_comma(false), m_linenum(0) {
22 m_state.push(PARSE_INIT);
23 }
24
25 void Bach::parseInit(char next) {
26 ATLAS_DEBUG(std::cout << "Bach::parseInit" << std::endl;)
27
28 if (next == '[') {
29 m_bridge.streamBegin();
30 m_state.push(PARSE_STREAM);
31 }
32 }
33
34 void Bach::parseStream(char next) {
35 ATLAS_DEBUG(std::cout << "Bach::parseStream" << std::endl;)
36
37 switch (next) {
38 case '{':
39 m_bridge.streamMessage();
40 m_state.push(PARSE_MAP);
41 break;
42
43 case ']':
44 m_bridge.streamEnd();
45 break;
46
47 default:
48 break;
49 }
50 }
51
52 void Bach::parseMap(char next) {
53 ATLAS_DEBUG(std::cout << "Bach::parseMap" << std::endl;)
54
55 switch (next) {
56 case '}':
57 m_bridge.mapEnd();
58 m_state.pop();
59 break;
60
61 case ',':
62 case ' ':
63 case '\t':
64 break;
65
66 case '\"':
67 m_state.push(PARSE_DATA);
68 m_state.push(PARSE_NAME);
69 break;
70
71 default:
72 if (((next >= 'a') && (next <= 'z')) ||
73 ((next >= 'A') && (next <= 'Z'))) {
74 m_istream.putback(next);
75 m_state.push(PARSE_DATA);
76 m_state.push(PARSE_NAME);
77 } else {
78 std::cerr << "Bach::parseMap: unexpected character: " << next << std::endl;
79 }
80 break;
81 }
82 }
83
84 void Bach::parseList(char next) {
85 ATLAS_DEBUG(std::cout << "Bach::parseList" << std::endl;)
86
87 switch (next) {
88 case ']':
89 m_bridge.listEnd();
90 m_state.pop();
91 break;
92
93 case '{':
94 m_bridge.listMapItem();
95 m_state.push(PARSE_MAP);
96 break;
97
98 case '[':
99 m_bridge.listListItem();
100 m_state.push(PARSE_LIST);
101 break;
102
103 case ',':
104 case ' ':
105 case '\t':
106 break;
107
108 case '1':
109 case '2':
110 case '3':
111 case '4':
112 case '5':
113 case '6':
114 case '7':
115 case '8':
116 case '9':
117 case '0':
118 case '-':
119 m_state.push(PARSE_INT);
120 m_istream.putback(next);
121 break;
122
123 case '\"':
124 m_state.push(PARSE_STRING);
125 break;
126
127 default:
128 std::cerr << "Bach::parseMap: unexpected character: " << next << std::endl;
129 break;
130 }
131 }
132
133 void Bach::parseInt(char next) {
134 ATLAS_DEBUG(std::cout << "Bach::parseInt" << std::endl;)
135
136 switch (next) {
137 case '[':
138 case ']':
139 case '{':
140 case '}':
141 case ',':
142 m_istream.putback(next);
143 m_state.pop();
144 if (m_state.top() == PARSE_MAP) {
145 ATLAS_DEBUG(std::cout << "Int: " << m_name << ": " << m_data << std::endl;)
146
147 m_bridge.mapIntItem(decodeString(std::move(m_name)), std::stol(m_data));
148 } else if (m_state.top() == PARSE_LIST) {
149 ATLAS_DEBUG(std::cout << "Int: " << m_data << std::endl;)
150
151 m_bridge.listIntItem(std::stol(m_data));
152 } else {
153 std::cerr << "Bach::parseIntt: Error" << std::endl;
154 }
155 m_name.erase();
156 m_data.erase();
157 break;
158
159 case '0':
160 case '1':
161 case '2':
162 case '3':
163 case '4':
164 case '5':
165 case '6':
166 case '7':
167 case '8':
168 case '9':
169 case '-':
170 case '+':
171 case 'e':
172 case 'E':
173 m_data += next;
174 break;
175
176 case '.':
177 m_state.pop();
178 m_state.push(PARSE_FLOAT);
179 m_data += next;
180 break;
181
182 default:
183 std::cerr << "Bach::parseInt: unexpected character: " << next << std::endl;
184 break;
185 }
186 }
187
188 void Bach::parseFloat(char next) {
189 ATLAS_DEBUG(std::cout << "Bach::parseFloat" << std::endl;)
190
191 switch (next) {
192 case '[':
193 case ']':
194 case '{':
195 case '}':
196 case ',':
197 m_istream.putback(next);
198 m_state.pop();
199 if (m_state.top() == PARSE_MAP) {
200 ATLAS_DEBUG(std::cout << "Float: " << m_name << ": " << m_data << std::endl;)
201
202 m_bridge.mapFloatItem(decodeString(std::move(m_name)), std::stod(m_data));
203 } else if (m_state.top() == PARSE_LIST) {
204 ATLAS_DEBUG(std::cout << "Float: " << m_data << std::endl;)
205
206 m_bridge.listFloatItem(std::stod(m_data));
207 } else {
208 std::cerr << "Bach::parseFloat: Error" << std::endl;
209 }
210 m_name.clear();
211 m_data.clear();
212 break;
213
214 case '0':
215 case '1':
216 case '2':
217 case '3':
218 case '4':
219 case '5':
220 case '6':
221 case '7':
222 case '8':
223 case '9':
224 case '.':
225 case '-':
226 case '+':
227 case 'e':
228 case 'E':
229 m_data += next;
230 break;
231
232 default:
233 std::cerr << "Bach::parseFloat: unexpected character: " << next << std::endl;
234 break;
235 }
236 }
237
238 void Bach::parseString(char next) {
239 ATLAS_DEBUG(std::cout << "Bach::parseString" << std::endl;)
240
241 switch (next) {
242 case '\"':
243 m_state.pop();
244 if (m_state.top() == PARSE_MAP) {
245 ATLAS_DEBUG(std::cout << "String: " << m_name << ": " << m_data << std::endl;)
246
247 m_bridge.mapStringItem(decodeString(std::move(m_name)), decodeString(std::move(m_data)));
248 } else if (m_state.top() == PARSE_LIST) {
249 ATLAS_DEBUG(std::cout << "String: " << m_data << std::endl;)
250
251 m_bridge.listStringItem(decodeString(std::move(m_data)));
252 } else {
253 std::cerr << "Bach::parseString: Error" << std::endl;
254 }
255 m_name.clear();
256 m_data.clear();
257 break;
258
259 case '\\':
260 m_state.push(PARSE_LITERAL);
261 break;
262
263 default:
264 m_data += next;
265 break;
266 }
267 }
268
269 void Bach::parseLiteral(char next) {
270 m_data += next;
271 m_state.pop();
272 }
273
274 void Bach::parseData(char next) {
275 ATLAS_DEBUG(std::cout << "Bach::parseData" << std::endl;)
276
277 switch (next) {
278 case '-':
279 case '1':
280 case '2':
281 case '3':
282 case '4':
283 case '5':
284 case '6':
285 case '7':
286 case '8':
287 case '9':
288 case '0':
289 m_istream.putback(next);
290 m_state.pop();
291 m_state.push(PARSE_INT);
292 break;
293
294 case '{':
295 m_state.pop();
296
297 switch (m_state.top()) {
298 case PARSE_MAP:
299 m_bridge.mapMapItem(decodeString(m_name));
300 m_name.erase();
301 break;
302
303 case PARSE_LIST:
304 m_bridge.listMapItem();
305 break;
306
307 default:
308 std::cerr << "Bach::parseData: Error: " << (int) m_state.top() << std::endl;
309 break;
310 }
311
312 m_state.push(PARSE_MAP);
313 break;
314
315 case '[':
316 m_state.pop();
317
318 switch (m_state.top()) {
319 case PARSE_MAP:
320 m_bridge.mapListItem(decodeString(m_name));
321 m_name.erase();
322 break;
323
324 case PARSE_LIST:
325 m_bridge.listListItem();
326 break;
327
328 default:
329 std::cerr << "Bach::parseData: Error: " << (int) m_state.top() << std::endl;
330 break;
331 }
332
333 m_state.push(PARSE_LIST);
334 break;
335
336 case '\"':
337 m_state.pop();
338 m_state.push(PARSE_STRING);
339 break;
340
341 case ',':
342 case ':':
343 break;
344
345 default:
346 std::cerr << "Bach::parseData: unexpected character: " << next << std::endl;
347 break;
348 }
349 }
350
351 void Bach::parseName(char next) {
352 ATLAS_DEBUG(std::cout << "Bach::parseName" << std::endl;)
353
354 switch (next) {
355 case ':':
356 case '\"':
357 ATLAS_DEBUG(std::cout << "Name: " << m_name << std::endl;)
358
359 m_state.pop();
360 break;
361
362 default:
363 if (((next >= 'a') && (next <= 'z')) ||
364 ((next >= 'A') && (next <= 'Z')) ||
365 ((next >= '0') && (next <= '9')) ||
366 (next == '_')) {
367 m_name += next;
368 } else {
369 std::cerr << "Bach::parseName: unexpected character: " << next << std::endl;
370 }
371 break;
372 }
373 }
374
375 void Bach::parseComment(char next) {
376 if (next == '\n')
377 m_state.pop();
378 }
379
380 bool Bach::stringmode() const {
381 switch (m_state.top()) {
382 case PARSE_COMMENT:
383 case PARSE_STRING:
384 case PARSE_LITERAL:
385 return true;
386 default:
387 return false;
388 }
389 }
390
391 void Bach::poll() {
392
393 m_istream.peek();
394
395 std::streamsize count;
396
397 while ((count = m_istream.rdbuf()->in_avail()) > 0) {
398
399 for (std::streamsize i = 0; i < count; ++i) {
400
401 char next = m_istream.rdbuf()->sbumpc();
402
403 // check for comment character here, so we don't have
404 // to do it in every section
405
406 switch (next) {
407 case '#':
408 if (!stringmode()) {
409 m_state.push(PARSE_COMMENT);
410 continue;
411 }
412 break;
413
414 case '\n':
415 m_linenum++;
416 if (!stringmode())
417 continue;
418 break;
419 case '\r': // dealing with DOS files, I guess
420 continue;
421 default:
422 break;
423 }
424
425 switch (m_state.top()) {
426 case PARSE_INIT:
427 parseInit(next);
428 break;
429 case PARSE_STREAM:
430 parseStream(next);
431 break;
432 case PARSE_MAP:
433 parseMap(next);
434 break;
435 case PARSE_LIST:
436 parseList(next);
437 break;
438 case PARSE_DATA:
439 parseData(next);
440 break;
441 case PARSE_INT:
442 parseInt(next);
443 break;
444 case PARSE_FLOAT:
445 parseFloat(next);
446 break;
447 case PARSE_STRING:
448 parseString(next);
449 break;
450 case PARSE_LITERAL:
451 parseLiteral(next);
452 break;
453 case PARSE_NAME:
454 parseName(next);
455 break;
456 case PARSE_COMMENT:
457 parseComment(next);
458 break;
459 }
460 }
461 }
462 }
463
464 std::string Bach::decodeString(std::string toDecode) {
465 std::string::size_type pos = 0;
466
467 while ((pos = toDecode.find("\\\"", pos)) != std::string::npos)
468 toDecode.replace(pos, 2, 1, '\"');
469
470 pos = 0;
471
472 while ((pos = toDecode.find("\\\\", pos)) != std::string::npos)
473 toDecode.replace(pos, 2, 1, '\\');
474
475 return toDecode;
476 }
477
478 std::string Bach::encodeString(std::string toEncode) {
479
480 for (size_t i = 0; i < toEncode.size(); ++i) {
481 if (toEncode[i] == '\\' || toEncode[i] == '\"') {
482 //First special character, use an encoded string instead
483 std::string encoded;
484 encoded.reserve(toEncode.size() + (toEncode.size() / 4));
485 encoded.assign(toEncode, 0, i);
486
487 for (; i < toEncode.size(); ++i) {
488 if (toEncode[i] == '\\') {
489 encoded += "\\\\";
490 } else if (toEncode[i] == '\"') {
491 encoded += "\\\"";
492 } else {
493 encoded += toEncode[i];
494 }
495 }
496 return encoded;
497 }
498 }
499 //If no special character, just return the original string, avoiding any allocations.
500 return toEncode;
501 }
502
503 void Bach::writeIntItem(const std::string &name, std::int64_t data) {
504 if (m_comma)
505 m_ostream << ",";
506
507 if (!name.empty())
508 m_ostream << name << ":";
509
510 m_ostream << data;
511 }
512
513 void Bach::writeFloatItem(const std::string &name, double data) {
514 if (m_comma)
515 m_ostream << ",";
516
517 if (!name.empty())
518 m_ostream << name << ":";
519
520 m_ostream << data;
521 }
522
523 void Bach::writeStringItem(const std::string &name, std::string data) {
524 if (m_comma)
525 m_ostream << ",";
526
527 if (!name.empty())
528 m_ostream << name << ":";
529
530 m_ostream << "\"" << encodeString(std::move(data)) << "\"";
531 }
532
533 void Bach::writeLine(const std::string &line, bool endline, bool endtag) {
534 if (m_comma && !endtag)
535 m_ostream << ",";
536
537 m_ostream << line;
538 }
539
540 void Bach::streamBegin() {
541 writeLine("[");
542 m_comma = false;
543 }
544
545 void Bach::streamEnd() {
546 writeLine("]", true, true);
547 }
548
549 void Bach::streamMessage() {
550 writeLine("{");
551 m_comma = false;
552 }
553
554 void Bach::mapMapItem(std::string name) {
555 writeLine(name + ":{");
556 m_comma = false;
557 }
558
559 void Bach::mapListItem(std::string name) {
560 writeLine(name + ":[");
561 m_comma = false;
562 }
563
564 void Bach::mapIntItem(std::string name, std::int64_t data) {
565 writeIntItem(name, data);
566 m_comma = true;
567 }
568
569 void Bach::mapFloatItem(std::string name, double data) {
570 writeFloatItem(name, data);
571 m_comma = true;
572 }
573
574 void Bach::mapStringItem(std::string name, std::string data) {
575 writeStringItem(name, std::move(data));
576 m_comma = true;
577 }
578
579 void Bach::mapNoneItem(std::string name) {
580 if (m_comma)
581 m_ostream << ",";
582
583 m_ostream << name << ":";
584 }
585
586 void Bach::mapEnd() {
587 writeLine("}", true, true);
588 m_comma = true;
589 }
590
591 void Bach::listMapItem() {
592 writeLine("{");
593 m_comma = false;
594 }
595
596 void Bach::listListItem() {
597 writeLine("[");
598 m_comma = false;
599 }
600
601 void Bach::listIntItem(std::int64_t data) {
602 writeIntItem("", data);
603 m_comma = true;
604 }
605
606 void Bach::listFloatItem(double data) {
607 writeFloatItem("", data);
608 m_comma = true;
609 }
610
611 void Bach::listStringItem(std::string data) {
612 writeStringItem("", std::move(data));
613 m_comma = true;
614 }
615
616 void Bach::listNoneItem() {
617 if (m_comma)
618 m_ostream << ",";
619 m_comma = true;
620 }
621
622 void Bach::listEnd() {
623 writeLine("]", true, true);
624 m_comma = true;
625 }
626
627 }
628} //namespace Atlas::Codecs
Definition: Bridge.h:20