]> git.tdb.fi Git - libs/datafile.git/blobdiff - source/binaryparser.cpp
More senseful validity checks for keyword and string definitions
[libs/datafile.git] / source / binaryparser.cpp
index ed1d71c8b197db64366e5e858b35ff032bc9b8df..fd918758294fd6c1d66ac497af5d5674d629c691 100644 (file)
@@ -1,13 +1,9 @@
-/* $Id$
-
-This file is part of libmspdatafile
-Copyright © 2007-2008  Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
+#include <limits>
 #include <sys/param.h>
-#include <msp/strings/formatter.h>
+#include <msp/core/maputils.h>
+#include <msp/strings/format.h>
 #include "binaryparser.h"
+#include "binfloat.h"
 #include "input.h"
 
 using namespace std;
@@ -15,12 +11,25 @@ using namespace std;
 namespace Msp {
 namespace DataFile {
 
+class bad_definition: public runtime_error
+{
+public:
+       bad_definition(const std::string &w):
+               runtime_error(w)
+       { }
+
+       virtual ~bad_definition() throw() { }
+};
+
+
 BinaryParser::BinaryParser(Input &i, const string &s):
        ParserMode(i, s),
-       first(true)
+       first(true),
+       float_precision(32)
 {
-       dict[1] = DictEntry("__kwd", "iss");
-       dict[2] = DictEntry("__str", "is");
+       dict[-1] = DictEntry("__kwd", "iss");
+       dict[-2] = DictEntry("__str", "is");
+       dict[-3] = DictEntry("__flt", "i");
 }
 
 Statement BinaryParser::parse()
@@ -30,22 +39,29 @@ Statement BinaryParser::parse()
                Statement st = parse_statement();
                if(st.keyword=="__kwd")
                {
-                       if(st.args.size()!=3)
-                               throw_at(TypeError("Keyword definition must have three arguments"), src);
+                       int id = st.args[0].get<int>();
+                       if(id<=0)
+                               throw bad_definition("__kwd");
 
-                       const unsigned id = st.args[0].get<unsigned>();
                        const string &kw = st.args[1].get<const string &>();
                        const string &args = st.args[2].get<const string &>();
+                       for(string::const_iterator i=args.begin(); i!=args.end(); ++i)
+                               for(unsigned j=0; valid_signatures[j]!=*i; ++j)
+                                       if(!valid_signatures[j])
+                                               throw bad_definition("__kwd");
+
                        dict[id] = DictEntry(kw, args);
                }
                else if(st.keyword=="__str")
                {
-                       if(st.args.size()!=2)
-                               throw_at(TypeError("String definition must have two arguments"), src);
+                       int id = st.args[0].get<int>();
+                       if(id<=0)
+                               throw bad_definition("__str");
 
-                       const unsigned id = st.args[0].get<unsigned>();
                        strings[id] = st.args[1].get<const string &>();
                }
+               else if(st.keyword=="__flt")
+                       float_precision = st.args[0].get<unsigned>();
                else
                        return st;
        }
@@ -57,14 +73,11 @@ Statement BinaryParser::parse_statement()
                in.get();
        first = false;
 
-       unsigned id = parse_int();
+       int id = parse_int();
        if(!in)
                return Statement();
 
-       Dictionary::const_iterator i = dict.find(id);
-       if(i==dict.end())
-               throw_at(KeyError("Unknown statement ID", lexical_cast(id)), src);
-       const DictEntry &de = i->second;
+       const DictEntry &de = get_item(dict, id);
 
        Statement result;
        result.keyword = de.keyword;
@@ -74,20 +87,20 @@ Statement BinaryParser::parse_statement()
        {
                switch(de.args[j])
                {
-               case 'i':
+               case IntType::signature:
                        result.args.push_back(parse_int());
                        break;
-               case 'f':
+               case FloatType::signature:
                        result.args.push_back(parse_float());
                        break;
-               case 's':
+               case StringType::signature:
                        result.args.push_back(parse_string());
                        break;
-               case 'b':
+               case BoolType::signature:
                        result.args.push_back(parse_bool());
                        break;
-               case 'e':
-                       result.args.push_back(Value(ENUM, parse_enum()));
+               case SymbolType::signature:
+                       result.args.push_back(parse_symbol());
                        break;
                }
        }
@@ -101,9 +114,9 @@ Statement BinaryParser::parse_statement()
        return result;
 }
 
-long long BinaryParser::parse_int()
+IntType::Store BinaryParser::parse_int()
 {
-       long long result = 0;
+       IntType::Store result = 0;
        unsigned bits = 0;
 
        while(in)
@@ -117,37 +130,63 @@ long long BinaryParser::parse_int()
                        break;
        }
 
-       const long long mask = 1LL<<(bits-1);
+       const IntType::Store mask = 1LL<<(bits-1);
        result = (result^mask)-mask;
 
        return result;
 }
 
-float BinaryParser::parse_float()
+FloatType::Store BinaryParser::parse_float()
 {
-       union
+       UInt64 encoded = 0;
+       for(unsigned i=0; i<float_precision; i+=8)
+       {
+               int c = in.get();
+               encoded = (encoded<<8) | (c&0xFF);
+       }
+
+       BinFloat bf = BinFloat::explode(encoded, float_precision);
+
+       if(numeric_limits<FloatType::Store>::is_iec559)
+               return bf.compose_iec559<FloatType::Store>();
+       else
        {
-               float f;
-               char d[sizeof(float)];
-       };
-
-#if BYTE_ORDER == LITTLE_ENDIAN
-       for(unsigned i = sizeof(float); i--;)
-               d[i] = in.get();
-#else
-       for(unsigned i = 0; i<sizeof(float); ++i)
-               d[i] = in.get();
-#endif
-
-       return f;
+               /* Put the float together with arithmetic since we don't know its
+               internal layout */
+               FloatType::Store f = 0;
+               if(bf.infinity)
+               {
+                       if(numeric_limits<FloatType::Store>::has_infinity)
+                               f = numeric_limits<FloatType::Store>::infinity();
+                       else
+                               f = numeric_limits<FloatType::Store>::max();
+               }
+               else
+               {
+                       for(unsigned i=0; i<64; ++i)
+                       {
+                               f /= 2;
+                               if(bf.mantissa&1)
+                                       f += 1;
+                               bf.mantissa >>= 1;
+                       }
+                       for(int i=0; i<bf.exponent; ++i)
+                               f *= 2;
+                       for(int i=0; i>bf.exponent; --i)
+                               f /= 2;
+               }
+               if(bf.sign)
+                       f = -f;
+               return f;
+       }
 }
 
-bool BinaryParser::parse_bool()
+BoolType::Store BinaryParser::parse_bool()
 {
        return in.get();
 }
 
-string BinaryParser::parse_string()
+StringType::Store BinaryParser::parse_string()
 {
        int len = parse_int();
        if(len>=0)
@@ -159,20 +198,12 @@ string BinaryParser::parse_string()
                return result;
        }
        else
-               return lookup_string(-len);
-}
-
-string BinaryParser::parse_enum()
-{
-       return lookup_string(parse_int());
+               return get_item(strings, -len);
 }
 
-const string &BinaryParser::lookup_string(unsigned id) const
+SymbolType::Store BinaryParser::parse_symbol()
 {
-       StringMap::const_iterator i = strings.find(id);
-       if(i==strings.end())
-               throw_at(KeyError("Unknown string", lexical_cast(id)), src);
-       return i->second;
+       return get_item(strings, parse_int());
 }
 
 } // namespace DataFile