-/* $Id$
-
-This file is part of libmspdatafile
-Copyright © 2006 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
+#include <limits>
+#include <msp/core/maputils.h>
#include "binarywriter.h"
+#include "binfloat.h"
+#include "output.h"
#include "statement.h"
using namespace std;
namespace Msp {
namespace DataFile {
-BinaryWriter::BinaryWriter(ostream &o):
+BinaryWriter::BinaryWriter(Output &o):
WriterMode(o),
- next_st_id(3),
- next_enum_id(1)
+ next_kwd_id(1),
+ next_str_id(1),
+ float_precision(32)
{
- dict[DictEntry("__st", "iss")]=1;
- dict[DictEntry("__enum", "is")]=1;
+ dict[StatementKey("__kwd", "iss")] = -1;
+ dict[StatementKey("__str", "is")] = -2;
+ dict[StatementKey("__flt", "i")] = -3;
+}
+
+void BinaryWriter::set_float_precision(unsigned fp)
+{
+ if(fp<16 || fp>64 || fp%8)
+ throw invalid_argument("BinaryWriter::set_float_precision");
+ float_precision = fp;
+ Statement fst;
+ fst.keyword = "__flt";
+ fst.args.push_back(float_precision);
+ write_(fst);
}
void BinaryWriter::write(const Statement &st)
void BinaryWriter::write_(const Statement &st)
{
- Dictionary::iterator i=dict.find(create_entry(st));
- if(i==dict.end())
- throw InvalidParameterValue("Unknown statement");
+ int id = get_item(dict, StatementKey(st.keyword, st.get_signature()));
- write_int(i->second);
- for(ValueArray::const_iterator j=st.args.begin(); j!=st.args.end(); ++j)
- switch(j->get_type())
+ write_int(id);
+ for(const Value &a: st.args)
+ switch(a.get_signature())
{
- 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;
+ case IntType::signature: write_int (a.get<IntType::Store>()); break;
+ case StringType::signature: write_string(a.get<StringType::Store>()); break;
+ case BoolType::signature: write_int (a.get<BoolType::Store>()); break;
+ case FloatType::signature: write_float (a.get<FloatType::Store>()); break;
+ case SymbolType::signature: write_symbol(a.get<SymbolType::Store>()); 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);
+ for(const Statement &s: st.sub)
+ write(s);
}
void BinaryWriter::collect_keywords(const Statement &st)
{
- DictEntry de=create_entry(st);
+ StatementKey key(st.keyword, st.get_signature());
- if(!dict.count(de))
+ if(!dict.count(key))
{
Statement kst;
- kst.keyword="__st";
- kst.args.push_back(next_st_id);
- kst.args.push_back(de.keyword);
- kst.args.push_back(de.args);
+ kst.keyword = "__kwd";
+ kst.args.push_back(next_kwd_id);
+ kst.args.push_back(key.keyword);
+ kst.args.push_back(key.signature);
write_(kst);
- dict.insert(Dictionary::value_type(de, next_st_id++)).first;
+ dict[key] = next_kwd_id++;
}
- for(ValueArray::const_iterator i=st.args.begin(); i!=st.args.end(); ++i)
- if(i->get_type()==ENUM && !enums.count(i->get_raw()))
+ for(const Value &a: st.args)
+ {
+ char sig = a.get_signature();
+ string str;
+ if(sig==SymbolType::signature)
+ str = a.get<Symbol>().name;
+ else if(sig==StringType::signature)
{
- 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++;
+ str = a.get<string>();
+ if(str.size()>32)
+ continue;
}
+ else
+ continue;
- for(list<Statement>::const_iterator i=st.sub.begin(); i!=st.sub.end(); ++i)
- collect_keywords(*i);
+ if(strings.count(str))
+ continue;
+
+ Statement sst;
+ sst.keyword = "__str";
+ sst.args.push_back(next_str_id);
+ sst.args.push_back(str);
+ write_(sst);
+
+ strings[str] = next_str_id++;
+ }
+
+ for(const Statement &s: st.sub)
+ collect_keywords(s);
}
-void BinaryWriter::write_int(long long n)
+void BinaryWriter::write_int(IntType::Store n)
{
- unsigned i=1;
- for(; n>>(i*7); ++i);
- for(; i--;)
- out.put(n>>(i*7) & 0x7F | (i?0x80:0));
+ unsigned i = sizeof(IntType::Store)-1;
+
+ if(n>=0)
+ for(; (i>0 && (n>>(i*7-1))==0); --i) ;
+ else
+ for(; (i>0 && (n>>(i*7-1))==-1); --i) ;
+
+ for(++i; i--;)
+ out.put((n>>(i*7) & 0x7F) | (i?0x80:0));
}
-void BinaryWriter::write_string(const string &s)
+void BinaryWriter::write_string(const StringType::Store &s)
{
- write_int(s.size());
- out.write(s.data(), s.size());
+ StringMap::const_iterator i = strings.find(s);
+ if(i!=strings.end())
+ write_int(-static_cast<int>(i->second));
+ else
+ {
+ write_int(s.size());
+ out.write(s);
+ }
}
-void BinaryWriter::write_float(float f)
+void BinaryWriter::write_float(FloatType::Store f)
{
- union
+ BinFloat bf;
+
+ if(numeric_limits<FloatType::Store>::is_iec559)
+ bf = BinFloat::explode_iec559(f);
+ else
{
- 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
+ /* The structure of the float is unknown, so we must use arithmetic to
+ reduce it to components. */
+ bf.sign = f<0;
+ bf.exponent = 0;
+ bf.mantissa = 0;
+
+ if(f<0)
+ f = -f;
+ if(!(f+f>f))
+ bf.infinity = true;
+ else if(f!=0)
+ {
+ for(; f<1; f*=2)
+ --bf.exponent;
+ for(; f>=2; f/=2)
+ ++bf.exponent;
+ for(unsigned i=0; i<64; ++i)
+ {
+ bf.mantissa <<= 1;
+ if(f>=1)
+ {
+ bf.mantissa |= 1;
+ f -= 1;
+ }
+ f *= 2;
+ }
+ }
+ }
+
+ UInt64 encoded = bf.compose(float_precision);
+ for(unsigned i=float_precision/8; i--; )
+ out.put((encoded>>(i*8))&0xFF);
}
-void BinaryWriter::write_enum(const string &e)
+void BinaryWriter::write_symbol(const SymbolType::Store &s)
{
- EnumMap::const_iterator i=enums.find(e);
- if(i==enums.end())
- throw InvalidParameterValue("Unknown enum");
- write_int(i->second);
+ write_int(get_item(strings, s.name));
}
} // namespace DataFile