]> git.tdb.fi Git - libs/datafile.git/blobdiff - source/binarywriter.cpp
Add binary data format
[libs/datafile.git] / source / binarywriter.cpp
diff --git a/source/binarywriter.cpp b/source/binarywriter.cpp
new file mode 100644 (file)
index 0000000..e89def3
--- /dev/null
@@ -0,0 +1,141 @@
+/* $Id$
+
+This file is part of libmspdatafile
+Copyright © 2006  Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "binarywriter.h"
+#include "statement.h"
+
+using namespace std;
+
+namespace Msp {
+namespace DataFile {
+
+BinaryWriter::BinaryWriter(ostream &o):
+       WriterMode(o),
+       next_st_id(3),
+       next_enum_id(1)
+{
+       dict[DictEntry("__kw", "iss")]=1;
+       dict[DictEntry("__enum", "is")]=1;
+}
+
+void BinaryWriter::write(const Statement &st)
+{
+       collect_keywords(st);
+       write_(st);
+}
+
+void BinaryWriter::write_(const Statement &st)
+{
+       Dictionary::iterator i=dict.find(create_entry(st));
+       if(i==dict.end())
+               throw InvalidParameterValue("Unknown statement");
+
+       write_int(i->second);
+       for(ValueArray::const_iterator j=st.args.begin(); j!=st.args.end(); ++j)
+               switch(j->get_type())
+               {
+               case INTEGER: write_int   (j->get<long long>()); break;
+               case STRING:  write_string(j->get<const string &>()); break;
+               case BOOLEAN: write_int   (j->get<bool>()); break;
+               case FLOAT:   write_float (j->get<float>()); break;
+               case ENUM:    write_enum  (j->get_raw()); break;
+               }
+
+       write_int(st.sub.size());
+       for(list<Statement>::const_iterator j=st.sub.begin(); j!=st.sub.end(); ++j)
+               write(*j);
+}
+
+DictEntry BinaryWriter::create_entry(const Statement &st)
+{
+       static const char types[]="ifsbe";
+
+       string args;
+       for(ValueArray::const_iterator i=st.args.begin(); i!=st.args.end(); ++i)
+       {
+               if(i->get_type()>=5)
+                       throw InvalidParameterValue("Invalid argument type");
+               args+=types[i->get_type()];
+       }
+
+       return DictEntry(st.keyword, args);
+}
+
+void BinaryWriter::collect_keywords(const Statement &st)
+{
+       DictEntry de=create_entry(st);
+
+       if(!dict.count(de))
+       {
+               Statement kst;
+               kst.keyword="__kw";
+               kst.args.push_back(next_st_id);
+               kst.args.push_back(de.keyword);
+               kst.args.push_back(de.args);
+               write_(kst);
+
+               dict.insert(Dictionary::value_type(de, next_st_id++)).first;
+       }
+
+       for(ValueArray::const_iterator i=st.args.begin(); i!=st.args.end(); ++i)
+               if(i->get_type()==ENUM && !enums.count(i->get_raw()))
+               {
+                       Statement est;
+                       est.keyword="__enum";
+                       est.args.push_back(next_enum_id);
+                       est.args.push_back(i->get_raw());
+                       write_(est);
+
+                       enums[i->get_raw()]=next_enum_id++;
+               }
+
+       for(list<Statement>::const_iterator i=st.sub.begin(); i!=st.sub.end(); ++i)
+               collect_keywords(*i);
+}
+
+void BinaryWriter::write_int(long long n)
+{
+       unsigned i=1;
+       for(; n>>(i*7); ++i);
+       for(; i--;)
+               out.put(n>>(i*7) & 0x7F | (i?0x80:0));
+}
+
+void BinaryWriter::write_string(const string &s)
+{
+       write_int(s.size());
+       out.write(s.data(), s.size());
+}
+
+void BinaryWriter::write_float(float f)
+{
+       union
+       {
+               float v;
+               char d[sizeof(float)];
+       };
+
+       v=f;
+#if BYTE_ORDER == LITTLE_ENDIAN
+       for(unsigned i=sizeof(float); i--;)
+               out.put(d[i]);
+#else
+       for(unsigned i=0; i<sizeof(float); ++i)
+               out.put(d[i]);
+#endif
+}
+
+void BinaryWriter::write_enum(const string &e)
+{
+       EnumMap::const_iterator i=enums.find(e);
+       if(i==enums.end())
+               throw InvalidParameterValue("Unknown enum");
+       write_int(i->second);
+}
+
+} // namespace DataFile
+} // namespace Msp