source: trunk/FACT++/src/Converter.cc@ 10211

Last change on this file since 10211 was 10183, checked in by tbretz, 14 years ago
New import.
File size: 11.7 KB
Line 
1// **************************************************************************
2/** @class Converter
3
4@brief An interpreter to convert a command line into a command plus data memory
5
6The Converter class interprets arguments in a string accoring to the
7given format definition and produces a corresponding memory block from it
8which can be attached to an event later.
9
10The format is given according to the Dim format description:
11
12 The format parameter specifies the contents of the structure in the
13 form T:N[;T:N]*[;T] where T is the item type: (I)nteger, (C)haracter,
14 (L)ong, (S)hort, (F)loat, (D)ouble, X(tra long==long long) and N is the
15 number of such items. The type alone at the end means all following items
16 are of the same type. Example: "I:3;F:2;C" means 3 Integers, 2 Floats and
17 Characters until the end. The format parameter is used for
18 communicating between different platforms.
19
20For example:
21
22 Converter c(cout, "I:1;F:2;I:2", "COMMAND 1 2.5 4.2 3 4");
23
24would produce a 20 byte data block with the integers 1, the floats
252.5 and 4.2, and the intergers 3 and 4, in this order.
26
27The opposite direction is also possible
28
29 Converter c(cout, "I:1;F:2;I:2", pointer, size);
30
31@remark
32 Most probably we support more formats than dim does...
33
34*/
35// **************************************************************************
36#include "Converter.h"
37
38#include <iomanip>
39#include <sstream>
40
41#include <boost/regex.hpp>
42
43#include "Readline.h"
44#include "WindowLog.h"
45
46using namespace std;
47
48// --------------------------------------------------------------------------
49//
50//! Gets a value of the template type T from the stringstream and adds the
51//! value as binary data to the end of the vector. If a value couldn't be
52//! obtained it is set to 0.
53//!
54//! @param i
55//! The number of the processed argument (currently used for some debug
56//! output when an argument is not available and artificially set to 0)
57//!
58//! @param line
59//! The stringstream from which the data should be read
60//!
61//! @param vec
62//! The vector of bytes at which end the data is added in binary format
63//!
64template <class T>
65 void Converter::Eval(int i, std::stringstream &line, std::vector<char> &vec) const
66{
67 T val = 0;
68 line >> val;
69 if (!line)
70 wout << " arg[" << i << "]";
71 else
72 wout << " (" << val << ")";
73
74 vec.insert(vec.end(),
75 reinterpret_cast<char*>(&val),
76 reinterpret_cast<char*>(&val+1));
77}
78
79// --------------------------------------------------------------------------
80//
81//! Constructs a data block from the given string according to the given
82//! format. (See also the class reference for more details).
83//!
84//! The data block is stored in a vector<char>. It's content can be
85//! retrieved using the member functions Ptr() and Size(). Whether parsing
86//! was successfull or not can be checked with GetRc().
87//! If parsing was not successfull, either the format contained something
88//! odd, the conversion failed (e.g. 5.5 for an int) or the string contained
89//! a wrong number of arguments.
90//!
91//! @param out
92//! The ostream to which errors and debug messages should be printed.
93//!
94//! @param fmt
95//! The format descriptor according to the dim definition
96//!
97//! @param str
98//! The string which should be interpreted, e.g. "1 2 5.5 abcdef"
99//!
100Converter::Converter(std::ostream &out, const std::string &fmt, const std::string &str)
101: rc(false), wout(out)
102{
103 // If the format is empty we are already done
104 if (fmt.empty())
105 {
106 if (!str.empty())
107 {
108 wout << endl;
109 wout << kRed << "Data string not empty as it ought to be!" << endl;
110 return;
111 }
112
113 wout << endl;
114 rc = true;
115 return;
116 }
117
118 // Access both, the data and the format through a stringstream
119 stringstream line(str);
120 stringstream stream(fmt);
121
122 // For better performance we could use sregex
123 static const boost::regex expr("^[ ]*([CSILFDX])[ ]*(:[ ]*([1-9]+[0-9]*))?[ ]*$");
124
125 // Tokenize the format
126 int arg = 0;
127 string buffer;
128 while (getline(stream, buffer, ';'))
129 {
130 boost::smatch what;
131 if (!boost::regex_match(buffer, what, expr))
132 {
133 wout << endl;
134 wout << kRed << "Wrong format string '" << buffer << "'!" << endl;
135 return;
136 }
137
138 const string t = what[1]; // type id
139 const string n = what[3]; // counter
140
141 int cnt = atoi(n.c_str());
142
143 // Check if the format is just C (without a number)
144 // That would mean that it is a \0 terminated string
145 if (t[0]=='C' && cnt==0)
146 {
147 // Remove leading whitespaces
148 while (line.peek()==' ')
149 line.get();
150
151 // Copy the string into the buffer
152 getline(line, buffer, '\0');
153
154 wout << " (" << buffer << ")";
155 data.insert(data.end(), buffer.begin(), buffer.end());
156 data.push_back(0);
157 continue;
158 }
159
160 // if the :N part was not given assume 1
161 if (cnt==0)
162 cnt=1;
163
164 // Get as many items from the input line as requested
165 for (int j=0; j<cnt; j++)
166 switch (t[0])
167 {
168 case 'C': // Skip whitespaces when checking for characters
169 if (j>0)
170 line >> noskipws;
171 Eval<char>(arg++, line, data);
172 line >> skipws;
173 break;
174 case 'S': Eval<short> (arg++, line, data); break;
175 case 'I': Eval<int> (arg++, line, data); break;
176 case 'L': Eval<long> (arg++, line, data); break;
177 case 'F': Eval<float> (arg++, line, data); break;
178 case 'D': Eval<double> (arg++, line, data); break;
179 case 'X': Eval<long long>(arg++, line, data); break;
180 default:
181 // This should never happen!
182 wout << endl << kRed << "Format '" << t[0] << " not known!" << endl;
183 break;
184 }
185 }
186
187 wout << " [" << fmt << "]=" << data.size() << endl;
188
189 // Something wrong with the conversion (e.g. 5.5 for an int)
190 if (line.fail() && !line.eof())
191 {
192 line.clear(); // This is necesasary to get a proper response from tellg()
193 wout << kRed << "Error converting argument at " << arg << " [fmt=" << fmt << "]!" << endl;
194 wout << kRed << str << endl;
195 wout << kRed << setw(int(line.tellg())) << " " << "^" << endl;
196 return;
197 }
198
199 // Not enough arguments, we have not reached the end
200 if (line.fail() && line.eof())
201 {
202 line.clear();
203 wout << kRed << "Not enough arguments [fmt=" << fmt << "]!" << endl;
204 wout << kRed << str << endl;
205 wout << kRed << setw(int(line.tellg())+1) << " " << "^" << endl;
206 return;
207 }
208
209 // Too many arguments, we have not reached the end
210 // Unfortunately, this can also mean that there is something
211 // wrong with the last argument
212 if (line.good() && !line.eof())
213 {
214 wout << kRed << "More arguments available than expected [" << fmt << "]!" << endl;
215 wout << kRed << str << endl;
216 wout << kRed << setw(int(line.tellg())+1) << " " << "^" << endl;
217 return;
218 }
219
220 // Set return code to true (successfull)
221 rc = true;
222}
223
224// --------------------------------------------------------------------------
225//
226//! Gets the value as a binary from the ptr and return it as a string.
227//! The pointer is increased accordingly.
228//!
229//! @param ptr
230//! A reference to a pointer to the data which should be converted.
231//! The pointer is increased according to the size of the data.
232//!
233//! @returns
234//! The data converted into a string
235//!
236template<class T>
237string Converter::Get(const char* &ptr) const
238{
239 ostringstream stream;
240 stream << *reinterpret_cast<const T*>(ptr);
241 ptr += sizeof(T);
242
243 return stream.str();
244}
245
246// --------------------------------------------------------------------------
247//
248//! Constructs a string from the given data block according to the specified
249//! format. (See also the class reference for more details).
250//!
251//! The resulting string is stored in vector<char> and 0-terminated.
252//! It can be accessed through Ptr().
253//!
254//! If the conversion faild GetRc will return false. In this case the data
255//! contents might not be well defined.
256//!
257//! If no format is given (size == 0) but the data size is finite (>0)
258//! then the data is converted into a hex representation.
259//!
260//! @remark
261//! In cases of failures the stored data might be inexisting and
262//! Ptr() might return NULL. If you output NULL to our streams
263//! they might not show any further output anymore.
264//!
265//! @param fmt
266//! The format descriptor according to the dim definition
267//!
268//! @param out
269//! The ostream to which errors and debug messages should be printed.
270//!
271//! @param dat
272//! Pointer to the start of the binary data
273//!
274//! @param size
275//! Size of the binary data region
276//!
277Converter::Converter(ostream &out, const string &fmt, const void *dat, int size)
278: rc(false), wout(out)
279{
280 const char *ptr = reinterpret_cast<const char *>(dat);
281
282 ostringstream text;
283
284 // Structure: print hex representation
285 if (fmt.size()==0)
286 {
287 if (size==0)
288 {
289 data.push_back(0);
290 rc = true;
291 return;
292 }
293
294 text << hex;
295
296 for (int i=0; i<size; i++)
297 text << setw(2) << ptr[i] << " ";
298
299 const string &ref = text.str();
300 data.insert(data.begin(), ref.begin(), ref.end());
301 data.push_back(0);
302 return;
303 }
304
305 // Access both, the data and the format through a stringstream
306 stringstream stream(fmt);
307
308 // For better performance we could use sregex
309 static const boost::regex expr("^[ ]*([CSILFDX])[ ]*(:[ ]*([1-9]+[0-9]*))?[ ]*$");
310
311 // Tokenize the format
312 string buffer;
313 while (getline(stream, buffer, ';'))
314 {
315 if (ptr-size>=dat)
316 {
317 wout << kRed << "Format description '" << fmt << "' exceeds available data size (" << size << ")" << endl;
318 return;
319 }
320
321 boost::smatch what;
322 if (!boost::regex_match(buffer, what, expr))
323 {
324 wout << kRed << "Wrong format string '" << buffer << "'!" << endl;
325 return;
326 }
327
328 const string t = what[1]; // type id
329 const string n = what[3]; // counter
330
331 int cnt = atoi(n.c_str());
332
333 // Check if the format is just C (without a number)
334 // That would mean that it is a \0 terminated string
335 if (t[0]=='C' && cnt==0)
336 {
337 const string str(ptr);
338 text << ' ' << str;
339 ptr += str.length()+1;
340 break;
341 }
342
343 // if the :N part was not given assume 1
344 if (cnt==0)
345 cnt=1;
346
347 // Get as many items from the input line as requested
348 for (int j=0; j<cnt; j++)
349 {
350 text << ' ';
351
352 switch (t[0])
353 {
354 case 'C': text << Get<char> (ptr); break;
355 case 'S': text << Get<short> (ptr); break;
356 case 'I': text << Get<int> (ptr); break;
357 case 'L': text << Get<long> (ptr); break;
358 case 'F': text << Get<float> (ptr); break;
359 case 'D': text << Get<double> (ptr); break;
360 case 'X': text << Get<long long>(ptr); break;
361 default:
362 // This should never happen!
363 wout << kRed << "Format '" << t[0] << " not known!" << endl;
364 return;
365 }
366 }
367 }
368
369 if (ptr-size!=dat)
370 {
371 wout << kRed << "Data block size (" << size << ") doesn't fit format description '" << fmt << "'" << endl;
372 return;
373 }
374
375 rc = true;
376
377 const string &ref = text.str();
378 data.insert(data.begin(), ref.begin()+1, ref.end());
379 data.push_back(0);
380}
Note: See TracBrowser for help on using the repository browser.