]> git.tdb.fi Git - libs/datafile.git/blob - source/binaryparser.cpp
Use custom encoding for floats in binary format
[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 "binaryparser.h"
6 #include "binfloat.h"
7 #include "input.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace DataFile {
13
14 class bad_definition: public runtime_error
15 {
16 public:
17         bad_definition(const std::string &w):
18                 runtime_error(w)
19         { }
20
21         virtual ~bad_definition() throw() { }
22 };
23
24
25 BinaryParser::BinaryParser(Input &i, const string &s):
26         ParserMode(i, s),
27         first(true),
28         float_precision(32)
29 {
30         dict[-1] = DictEntry("__kwd", "iss");
31         dict[-2] = DictEntry("__str", "is");
32         dict[-3] = DictEntry("__flt", "i");
33 }
34
35 Statement BinaryParser::parse()
36 {
37         while(1)
38         {
39                 Statement st = parse_statement();
40                 if(st.keyword=="__kwd")
41                 {
42                         if(st.args.size()!=3)
43                                 throw bad_definition("__kwd");
44
45                         const int id = st.args[0].get<unsigned>();
46                         const string &kw = st.args[1].get<const string &>();
47                         const string &args = st.args[2].get<const string &>();
48                         dict[id] = DictEntry(kw, args);
49                 }
50                 else if(st.keyword=="__str")
51                 {
52                         if(st.args.size()!=2)
53                                 throw bad_definition("__str");
54
55                         const unsigned id = st.args[0].get<unsigned>();
56                         strings[id] = st.args[1].get<const string &>();
57                 }
58                 else if(st.keyword=="__flt")
59                         float_precision = st.args[0].get<unsigned>();
60                 else
61                         return st;
62         }
63 }
64
65 Statement BinaryParser::parse_statement()
66 {
67         while(first && in.peek()=='\n')
68                 in.get();
69         first = false;
70
71         int id = parse_int();
72         if(!in)
73                 return Statement();
74
75         const DictEntry &de = get_item(dict, id);
76
77         Statement result;
78         result.keyword = de.keyword;
79         result.source = src;
80
81         for(unsigned j = 0; j<de.args.size(); ++j)
82         {
83                 switch(de.args[j])
84                 {
85                 case IntType::signature:
86                         result.args.push_back(parse_int());
87                         break;
88                 case FloatType::signature:
89                         result.args.push_back(parse_float());
90                         break;
91                 case StringType::signature:
92                         result.args.push_back(parse_string());
93                         break;
94                 case BoolType::signature:
95                         result.args.push_back(parse_bool());
96                         break;
97                 case SymbolType::signature:
98                         result.args.push_back(parse_symbol());
99                         break;
100                 }
101         }
102
103         unsigned nsub = parse_int();
104         for(unsigned j = 0; j<nsub; ++j)
105                 result.sub.push_back(parse());
106
107         result.valid = true;
108
109         return result;
110 }
111
112 IntType::Store BinaryParser::parse_int()
113 {
114         IntType::Store result = 0;
115         unsigned bits = 0;
116
117         while(in)
118         {
119                 int c = in.get();
120
121                 result = (result<<7) | (c&0x7F);
122                 bits += 7;
123
124                 if(!(c&0x80))
125                         break;
126         }
127
128         const IntType::Store mask = 1LL<<(bits-1);
129         result = (result^mask)-mask;
130
131         return result;
132 }
133
134 FloatType::Store BinaryParser::parse_float()
135 {
136         UInt64 encoded = 0;
137         for(unsigned i=0; i<float_precision; i+=8)
138         {
139                 int c = in.get();
140                 encoded = (encoded<<8) | (c&0xFF);
141         }
142
143         BinFloat bf = BinFloat::explode(encoded, float_precision);
144
145         if(numeric_limits<FloatType::Store>::is_iec559)
146                 return bf.compose_iec559<FloatType::Store>();
147         else
148         {
149                 /* Put the float together with arithmetic since we don't know its
150                 internal layout */
151                 FloatType::Store f = 0;
152                 if(bf.infinity)
153                 {
154                         if(numeric_limits<FloatType::Store>::has_infinity)
155                                 f = numeric_limits<FloatType::Store>::infinity();
156                         else
157                                 f = numeric_limits<FloatType::Store>::max();
158                 }
159                 else
160                 {
161                         for(unsigned i=0; i<64; ++i)
162                         {
163                                 f /= 2;
164                                 if(bf.mantissa&1)
165                                         f += 1;
166                                 bf.mantissa >>= 1;
167                         }
168                         for(int i=0; i<bf.exponent; ++i)
169                                 f *= 2;
170                         for(int i=0; i>bf.exponent; --i)
171                                 f /= 2;
172                 }
173                 if(bf.sign)
174                         f = -f;
175                 return f;
176         }
177 }
178
179 BoolType::Store BinaryParser::parse_bool()
180 {
181         return in.get();
182 }
183
184 StringType::Store BinaryParser::parse_string()
185 {
186         int len = parse_int();
187         if(len>=0)
188         {
189                 string result;
190                 result.reserve(len);
191                 for(int i = 0; i<len; ++i)
192                         result += in.get();
193                 return result;
194         }
195         else
196                 return get_item(strings, -len);
197 }
198
199 SymbolType::Store BinaryParser::parse_symbol()
200 {
201         return get_item(strings, parse_int());
202 }
203
204 } // namespace DataFile
205 } // namespace Msp