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