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