]> git.tdb.fi Git - libs/datafile.git/blob - source/binaryparser.cpp
Move all exception classes to a common header
[libs/datafile.git] / source / binaryparser.cpp
1 #include <limits>
2 #include <sys/param.h>
3 #include <msp/core/maputils.h>
4 #include <msp/strings/format.h>
5 #include "argumentstore.h"
6 #include "binaryparser.h"
7 #include "binfloat.h"
8 #include "except.h"
9 #include "input.h"
10 #include "loaderaction.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace DataFile {
16
17 BinaryParser::BinaryParser(Input &i, const string &s):
18         ParserMode(i, s),
19         float_precision(32),
20         cur_info(0)
21 {
22         dict[-1] = StatementInfo("__kwd", "iss");
23         dict[-2] = StatementInfo("__str", "is");
24         dict[-3] = StatementInfo("__flt", "i");
25 }
26
27 Statement BinaryParser::parse()
28 {
29         const StatementKey *key;
30         if(cur_info)
31         {
32                 key = &cur_info->key;
33                 cur_info = 0;
34         }
35         else
36         {
37                 int id = parse_int();
38                 if(!in)
39                         return Statement();
40
41                 key = &get_item(dict, id).key;
42         }
43
44         Statement result;
45         result.keyword = key->keyword;
46         result.source = src;
47
48         for(unsigned j=0; j<key->signature.size(); ++j)
49         {
50                 switch(key->signature[j])
51                 {
52                 case IntType::signature:
53                         result.args.push_back(parse_int());
54                         break;
55                 case FloatType::signature:
56                         result.args.push_back(parse_float());
57                         break;
58                 case StringType::signature:
59                         result.args.push_back(parse_string());
60                         break;
61                 case BoolType::signature:
62                         result.args.push_back(parse_bool());
63                         break;
64                 case SymbolType::signature:
65                         result.args.push_back(parse_symbol());
66                         break;
67                 }
68         }
69
70         unsigned upper_nsub = (sub_remaining.empty() ? 0 : sub_remaining.back());
71
72         unsigned nsub = parse_int();
73         for(unsigned j = 0; j<nsub; ++j)
74                 result.sub.push_back(parse());
75
76         if(upper_nsub>0)
77                 sub_remaining.back() = upper_nsub-1;
78
79         result.valid = true;
80
81         return result;
82 }
83
84 void BinaryParser::process_control_statement(const Statement &st)
85 {
86         if(st.keyword=="__kwd")
87         {
88                 int id = st.args[0].get<int>();
89                 if(id<=0)
90                         throw bad_definition("__kwd");
91
92                 const string &kw = st.args[1].get<const string &>();
93                 const string &args = st.args[2].get<const string &>();
94                 for(string::const_iterator i=args.begin(); i!=args.end(); ++i)
95                         for(unsigned j=0; valid_signatures[j]!=*i; ++j)
96                                 if(!valid_signatures[j])
97                                         throw bad_definition("__kwd");
98
99                 dict[id] = StatementInfo(kw, args);
100         }
101         else if(st.keyword=="__str")
102         {
103                 int id = st.args[0].get<int>();
104                 if(id<=0)
105                         throw bad_definition("__str");
106
107                 strings[id] = st.args[1].get<const string &>();
108         }
109         else if(st.keyword=="__flt")
110                 float_precision = st.args[0].get<unsigned>();
111 }
112
113 const StatementKey *BinaryParser::peek(unsigned level)
114 {
115         if(level>sub_remaining.size())
116                 throw nesting_error("bad level");
117         while(level<sub_remaining.size())
118         {
119                 // Discard any substatements that haven't been parsed yet
120                 for(unsigned i=sub_remaining.back(); i-->0; )
121                         parse();
122                 sub_remaining.pop_back();
123                 cur_info = 0;
124         }
125
126         if(!sub_remaining.empty() && sub_remaining.back()==0)
127         {
128                 // No more substatements on this level
129                 cur_info = 0;
130                 return 0;
131         }
132
133         if(cur_info)
134                 return &cur_info->key;
135
136         int id = parse_int();
137         if(!in)
138                 return 0;
139
140         cur_info = &get_item(dict, id);
141         return &cur_info->key;
142 }
143
144 bool BinaryParser::parse_and_load(unsigned level, Loader &ldr, const LoaderAction &act)
145 {
146         if(!cur_info && !peek(level))
147                 return false;
148
149         ArgumentStore args(*cur_info);
150         for(unsigned i=0; i<cur_info->key.signature.size(); ++i)
151                 switch(cur_info->key.signature[i])
152                 {
153                 case IntType::signature:
154                         args.set(i, parse_int());
155                         break;
156                 case FloatType::signature:
157                         args.set(i, parse_float());
158                         break;
159                 case BoolType::signature:
160                         args.set(i, parse_bool());
161                         break;
162                 case StringType::signature:
163                         args.set(i, parse_string());
164                         break;
165                 case SymbolType::signature:
166                         args.set(i, parse_symbol());
167                         break;
168                 }
169
170         if(!sub_remaining.empty())
171                 --sub_remaining.back();
172         sub_remaining.push_back(parse_int());
173         cur_info = 0;
174
175         act.execute(ldr, args);
176
177         return true;
178 }
179
180 IntType::Store BinaryParser::parse_int()
181 {
182         IntType::Store result = 0;
183         unsigned bits = 0;
184
185         while(in)
186         {
187                 int c = in.get();
188
189                 result = (result<<7) | (c&0x7F);
190                 bits += 7;
191
192                 if(!(c&0x80))
193                         break;
194         }
195
196         const IntType::Store mask = 1LL<<(bits-1);
197         result = (result^mask)-mask;
198
199         return result;
200 }
201
202 FloatType::Store BinaryParser::parse_float()
203 {
204         UInt64 encoded = 0;
205         for(unsigned i=0; i<float_precision; i+=8)
206         {
207                 int c = in.get();
208                 encoded = (encoded<<8) | (c&0xFF);
209         }
210
211         BinFloat bf = BinFloat::explode(encoded, float_precision);
212
213         if(numeric_limits<FloatType::Store>::is_iec559)
214                 return bf.compose_iec559<FloatType::Store>();
215         else
216         {
217                 /* Put the float together with arithmetic since we don't know its
218                 internal layout */
219                 FloatType::Store f = 0;
220                 if(bf.infinity)
221                 {
222                         if(numeric_limits<FloatType::Store>::has_infinity)
223                                 f = numeric_limits<FloatType::Store>::infinity();
224                         else
225                                 f = numeric_limits<FloatType::Store>::max();
226                 }
227                 else
228                 {
229                         for(unsigned i=0; i<64; ++i)
230                         {
231                                 f /= 2;
232                                 if(bf.mantissa&1)
233                                         f += 1;
234                                 bf.mantissa >>= 1;
235                         }
236                         for(int i=0; i<bf.exponent; ++i)
237                                 f *= 2;
238                         for(int i=0; i>bf.exponent; --i)
239                                 f /= 2;
240                 }
241                 if(bf.sign)
242                         f = -f;
243                 return f;
244         }
245 }
246
247 BoolType::Store BinaryParser::parse_bool()
248 {
249         return in.get();
250 }
251
252 StringType::Store BinaryParser::parse_string()
253 {
254         int len = parse_int();
255         if(len>=0)
256         {
257                 string result;
258                 result.reserve(len);
259                 for(int i = 0; i<len; ++i)
260                         result += in.get();
261                 return result;
262         }
263         else
264                 return get_item(strings, -len);
265 }
266
267 SymbolType::Store BinaryParser::parse_symbol()
268 {
269         return get_item(strings, parse_int());
270 }
271
272 } // namespace DataFile
273 } // namespace Msp