087e140ddd5d1d1f79173afac9d3cb9f7e57fb8b
[libs/datafile.git] / source / binarywriter.cpp
1 #include <limits>
2 #include <msp/core/maputils.h>
3 #include "binarywriter.h"
4 #include "binfloat.h"
5 #include "output.h"
6 #include "statement.h"
7
8 using namespace std;
9
10 namespace Msp {
11 namespace DataFile {
12
13 BinaryWriter::BinaryWriter(Output &o):
14         WriterMode(o),
15         next_kwd_id(1),
16         next_str_id(1),
17         float_precision(32)
18 {
19         dict[StatementKey("__kwd", "iss")] = -1;
20         dict[StatementKey("__str", "is")] = -2;
21         dict[StatementKey("__flt", "i")] = -3;
22 }
23
24 void BinaryWriter::set_float_precision(unsigned fp)
25 {
26         if(fp<16 || fp>64 || fp%8)
27                 throw invalid_argument("BinaryWriter::set_float_precision");
28         float_precision = fp;
29         Statement fst;
30         fst.keyword = "__flt";
31         fst.args.push_back(float_precision);
32         write_(fst);
33 }
34
35 void BinaryWriter::write(const Statement &st)
36 {
37         collect_keywords(st);
38         write_(st);
39 }
40
41 void BinaryWriter::write_(const Statement &st)
42 {
43         int id = get_item(dict, StatementKey(st.keyword, st.get_signature()));
44
45         write_int(id);
46         for(Statement::Arguments::const_iterator j = st.args.begin(); j!=st.args.end(); ++j)
47                 switch(j->get_signature())
48                 {
49                 case IntType::signature:    write_int   (j->get<IntType::Store>()); break;
50                 case StringType::signature: write_string(j->get<StringType::Store>()); break;
51                 case BoolType::signature:   write_int   (j->get<BoolType::Store>()); break;
52                 case FloatType::signature:  write_float (j->get<FloatType::Store>()); break;
53                 case SymbolType::signature: write_symbol(j->get<SymbolType::Store>()); break;
54                 }
55
56         write_int(st.sub.size());
57         for(list<Statement>::const_iterator j = st.sub.begin(); j!=st.sub.end(); ++j)
58                 write(*j);
59 }
60
61 void BinaryWriter::collect_keywords(const Statement &st)
62 {
63         StatementKey key(st.keyword, st.get_signature());
64
65         if(!dict.count(key))
66         {
67                 Statement kst;
68                 kst.keyword = "__kwd";
69                 kst.args.push_back(next_kwd_id);
70                 kst.args.push_back(key.keyword);
71                 kst.args.push_back(key.signature);
72                 write_(kst);
73
74                 dict[key] = next_kwd_id++;
75         }
76
77         for(ValueArray::const_iterator i = st.args.begin(); i!=st.args.end(); ++i)
78         {
79                 char sig = i->get_signature();
80                 string str;
81                 if(sig==SymbolType::signature)
82                         str = i->get<Symbol>().name;
83                 else if(sig==StringType::signature)
84                 {
85                         str = i->get<string>();
86                         if(str.size()>32)
87                                 continue;
88                 }
89                 else
90                         continue;
91
92                 if(strings.count(str))
93                         continue;
94
95                 Statement sst;
96                 sst.keyword = "__str";
97                 sst.args.push_back(next_str_id);
98                 sst.args.push_back(str);
99                 write_(sst);
100
101                 strings[str] = next_str_id++;
102         }
103
104         for(list<Statement>::const_iterator i = st.sub.begin(); i!=st.sub.end(); ++i)
105                 collect_keywords(*i);
106 }
107
108 void BinaryWriter::write_int(IntType::Store n)
109 {
110         unsigned i = sizeof(IntType::Store)-1;
111
112         if(n>=0)
113                 for(; (i>0 && (n>>(i*7-1))==0); --i) ;
114         else
115                 for(; (i>0 && (n>>(i*7-1))==-1); --i) ;
116
117         for(++i; i--;)
118                 out.put((n>>(i*7) & 0x7F) | (i?0x80:0));
119 }
120
121 void BinaryWriter::write_string(const StringType::Store &s)
122 {
123         StringMap::const_iterator i = strings.find(s);
124         if(i!=strings.end())
125                 write_int(-static_cast<int>(i->second));
126         else
127         {
128                 write_int(s.size());
129                 out.write(s);
130         }
131 }
132
133 void BinaryWriter::write_float(FloatType::Store f)
134 {
135         BinFloat bf;
136
137         if(numeric_limits<FloatType::Store>::is_iec559)
138                 bf = BinFloat::explode_iec559(f);
139         else
140         {
141                 /* The structure of the float is unknown, so we must use arithmetic to
142                 reduce it to components. */
143                 bf.sign = f<0;
144                 bf.exponent = 0;
145                 bf.mantissa = 0;
146
147                 if(f<0)
148                         f = -f;
149                 if(!(f+f>f))
150                         bf.infinity = true;
151                 else if(f!=0)
152                 {
153                         for(; f<1; f*=2)
154                                 --bf.exponent;
155                         for(; f>=2; f/=2)
156                                 ++bf.exponent;
157                         for(unsigned i=0; i<64; ++i)
158                         {
159                                 bf.mantissa <<= 1;
160                                 if(f>=1)
161                                 {
162                                         bf.mantissa |= 1;
163                                         f -= 1;
164                                 }
165                                 f *= 2;
166                         }
167                 }
168         }
169
170         UInt64 encoded = bf.compose(float_precision);
171         for(unsigned i=float_precision/8; i--; )
172                 out.put((encoded>>(i*8))&0xFF);
173 }
174
175 void BinaryWriter::write_symbol(const SymbolType::Store &s)
176 {
177         write_int(get_item(strings, s.name));
178 }
179
180 } // namespace DataFile
181 } // namespace Msp