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