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 
15 static const bool debug_flag = false;
16 
17 namespace 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
Bach.h
Atlas::Bridge
Definition: Bridge.h:36
Atlas
Definition: Bridge.h:20