]> git.tdb.fi Git - libs/datafile.git/blob - source/binarywriter.cpp
Add binary data format
[libs/datafile.git] / source / binarywriter.cpp
1 /* $Id$
2
3 This file is part of libmspdatafile
4 Copyright © 2006  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include "binarywriter.h"
9 #include "statement.h"
10
11 using namespace std;
12
13 namespace Msp {
14 namespace DataFile {
15
16 BinaryWriter::BinaryWriter(ostream &o):
17         WriterMode(o),
18         next_st_id(3),
19         next_enum_id(1)
20 {
21         dict[DictEntry("__kw", "iss")]=1;
22         dict[DictEntry("__enum", "is")]=1;
23 }
24
25 void BinaryWriter::write(const Statement &st)
26 {
27         collect_keywords(st);
28         write_(st);
29 }
30
31 void BinaryWriter::write_(const Statement &st)
32 {
33         Dictionary::iterator i=dict.find(create_entry(st));
34         if(i==dict.end())
35                 throw InvalidParameterValue("Unknown statement");
36
37         write_int(i->second);
38         for(ValueArray::const_iterator j=st.args.begin(); j!=st.args.end(); ++j)
39                 switch(j->get_type())
40                 {
41                 case INTEGER: write_int   (j->get<long long>()); break;
42                 case STRING:  write_string(j->get<const string &>()); break;
43                 case BOOLEAN: write_int   (j->get<bool>()); break;
44                 case FLOAT:   write_float (j->get<float>()); break;
45                 case ENUM:    write_enum  (j->get_raw()); break;
46                 }
47
48         write_int(st.sub.size());
49         for(list<Statement>::const_iterator j=st.sub.begin(); j!=st.sub.end(); ++j)
50                 write(*j);
51 }
52
53 DictEntry BinaryWriter::create_entry(const Statement &st)
54 {
55         static const char types[]="ifsbe";
56
57         string args;
58         for(ValueArray::const_iterator i=st.args.begin(); i!=st.args.end(); ++i)
59         {
60                 if(i->get_type()>=5)
61                         throw InvalidParameterValue("Invalid argument type");
62                 args+=types[i->get_type()];
63         }
64
65         return DictEntry(st.keyword, args);
66 }
67
68 void BinaryWriter::collect_keywords(const Statement &st)
69 {
70         DictEntry de=create_entry(st);
71
72         if(!dict.count(de))
73         {
74                 Statement kst;
75                 kst.keyword="__kw";
76                 kst.args.push_back(next_st_id);
77                 kst.args.push_back(de.keyword);
78                 kst.args.push_back(de.args);
79                 write_(kst);
80
81                 dict.insert(Dictionary::value_type(de, next_st_id++)).first;
82         }
83
84         for(ValueArray::const_iterator i=st.args.begin(); i!=st.args.end(); ++i)
85                 if(i->get_type()==ENUM && !enums.count(i->get_raw()))
86                 {
87                         Statement est;
88                         est.keyword="__enum";
89                         est.args.push_back(next_enum_id);
90                         est.args.push_back(i->get_raw());
91                         write_(est);
92
93                         enums[i->get_raw()]=next_enum_id++;
94                 }
95
96         for(list<Statement>::const_iterator i=st.sub.begin(); i!=st.sub.end(); ++i)
97                 collect_keywords(*i);
98 }
99
100 void BinaryWriter::write_int(long long n)
101 {
102         unsigned i=1;
103         for(; n>>(i*7); ++i);
104         for(; i--;)
105                 out.put(n>>(i*7) & 0x7F | (i?0x80:0));
106 }
107
108 void BinaryWriter::write_string(const string &s)
109 {
110         write_int(s.size());
111         out.write(s.data(), s.size());
112 }
113
114 void BinaryWriter::write_float(float f)
115 {
116         union
117         {
118                 float v;
119                 char d[sizeof(float)];
120         };
121
122         v=f;
123 #if BYTE_ORDER == LITTLE_ENDIAN
124         for(unsigned i=sizeof(float); i--;)
125                 out.put(d[i]);
126 #else
127         for(unsigned i=0; i<sizeof(float); ++i)
128                 out.put(d[i]);
129 #endif
130 }
131
132 void BinaryWriter::write_enum(const string &e)
133 {
134         EnumMap::const_iterator i=enums.find(e);
135         if(i==enums.end())
136                 throw InvalidParameterValue("Unknown enum");
137         write_int(i->second);
138 }
139
140 } // namespace DataFile
141 } // namespace Msp