]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Deserializer.cc
extreme refactoring
[chaz/yoink] / src / Moof / Deserializer.cc
diff --git a/src/Moof/Deserializer.cc b/src/Moof/Deserializer.cc
new file mode 100644 (file)
index 0000000..8bf230f
--- /dev/null
@@ -0,0 +1,253 @@
+
+/*******************************************************************************
+
+ Copyright (c) 2009, Charles McGarvey
+ All rights reserved.
+ Redistribution   and   use  in  source  and  binary  forms,  with  or  without
+ modification, are permitted provided that the following conditions are met:
+   * Redistributions  of  source  code  must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+   * Redistributions  in binary form must reproduce the above copyright notice,
+     this  list of conditions and the following disclaimer in the documentation
+     and/or other materials provided with the distribution.
+ THIS  SOFTWARE  IS  PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND  ANY  EXPRESS  OR  IMPLIED  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED.  IN  NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES  (INCLUDING,  BUT  NOT  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES;  LOSS  OF  USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#include <fstream>
+#include <queue>
+
+#include <yajl/yajl_parse.h>
+
+#include "Deserializer.hh"
+#include "Serializable.hh"
+
+
+namespace Mf {
+
+
+class Deserializer::DeserializerImpl
+{
+public:
+       DeserializerImpl(const std::string& filePath, bool comments = false,
+                       bool check = false)
+       {
+               std::ifstream* input = new std::ifstream(filePath.c_str());
+               init(*input, true, comments, check);
+       }
+
+       DeserializerImpl(std::istream& input, bool comments = false,
+                       bool check = false)
+       {
+               init(input, false, comments, check);
+       }
+
+       ~DeserializerImpl()
+       {
+               while (!parsed.empty())
+               {
+                       delete parsed.front();
+                       parsed.pop();
+               }
+
+               if (deleteWhenDone)
+               {
+                       delete in;
+               }
+               yajl_free(hand);
+       }
+
+       void throwError()
+       {
+               unsigned char* errorStr = yajl_get_error(hand, 0, 0, 0);
+               Deserializer::Exception exception((char*)errorStr);
+               yajl_free_error(hand, errorStr);
+               throw exception;
+       }
+
+
+       static int parsedNull(void* ctx)
+       {
+               ((DeserializerImpl*)ctx)->parsed.push(new SerializableNull);
+               return 1;
+       }
+
+       static int parsedBoolean(void* ctx, int value)
+       {
+               ((DeserializerImpl*)ctx)->parsed.push(new SerializableBoolean(value));
+               return 1;
+       }
+
+       static int parsedInteger(void* ctx, long value)
+       {
+               ((DeserializerImpl*)ctx)->parsed.push(new SerializableInteger(value));
+               return 1;
+       }
+
+       static int parsedFloat(void* ctx, double value)
+       {
+               ((DeserializerImpl*)ctx)->parsed.push(new SerializableReal(value));
+               return 1;
+       }
+
+       static int parsedString(void* ctx, const unsigned char* value,
+                       unsigned length)
+       {
+               SerializableString* parsed =
+                       new SerializableString(std::string((char*)value, length));
+               ((DeserializerImpl*)ctx)->parsed.push(parsed);
+               return 1;
+       }
+
+       static int parsedBeginMap(void* ctx)
+       {
+               ((DeserializerImpl*)ctx)->parsed.push(new SerializableMap);
+               return 1;
+       }
+
+       static int parsedMapKey(void* ctx, const unsigned char* value,
+                       unsigned length)
+       {
+               // same thing as a string
+               return parsedString(ctx, value, length);
+       }
+
+       static int parsedEndMap(void* ctx)
+       {
+               ((DeserializerImpl*)ctx)->parsed.push(0);
+               return 1;
+       }
+
+       static int parsedBeginArray(void* ctx)
+       {
+               ((DeserializerImpl*)ctx)->parsed.push(new SerializableArray);
+               return 1;
+       }
+
+       static int parsedEndArray(void* ctx)
+       {
+               ((DeserializerImpl*)ctx)->parsed.push(0);
+               return 1;
+       }
+
+
+       void parse()
+       {
+               unsigned char buffer[4096];
+
+               yajl_status stat;
+
+               while (parsed.empty() && in->good())
+               {
+                       in->read((char*)buffer, sizeof(buffer));
+                       unsigned readIn = in->gcount();
+
+                       if (readIn > 0)
+                       {
+                               stat = yajl_parse(hand, buffer, readIn);
+                       }
+                       else
+                       {
+                               stat = yajl_parse_complete(hand);
+                       }
+
+                       if (stat != yajl_status_ok &&
+                                       stat != yajl_status_insufficient_data)
+                       {
+                               throwError();
+                       }
+               }
+       }
+
+
+       yajl_handle hand;
+
+       std::istream* in;
+       bool deleteWhenDone;
+
+       std::queue<Serializable*> parsed;
+
+private:
+       void init(std::istream& input, bool deleteIn, bool comments, bool check)
+       {
+               // this has to be static because yajl actually does not copy it into its
+               // internal data structures but rather keeps a pointer to this
+               static const yajl_callbacks callbacks =
+               {
+                       DeserializerImpl::parsedNull,
+                       DeserializerImpl::parsedBoolean,
+                       DeserializerImpl::parsedInteger,
+                       DeserializerImpl::parsedFloat,
+                       0,
+                       DeserializerImpl::parsedString,
+                       DeserializerImpl::parsedBeginMap,
+                       DeserializerImpl::parsedMapKey,
+                       DeserializerImpl::parsedEndMap,
+                       DeserializerImpl::parsedBeginArray,
+                       DeserializerImpl::parsedEndArray
+               };
+
+               in = &input;
+               deleteWhenDone = deleteIn;
+
+               yajl_parser_config config = {comments, check};
+               hand = yajl_alloc(&callbacks, &config, NULL, this);
+       }
+};
+
+
+Deserializer::Deserializer(const std::string& filePath, bool comments,
+               bool check) :
+       // pass through
+       impl_(new Deserializer::DeserializerImpl(filePath, comments, check)) {}
+
+Deserializer::Deserializer(std::istream& input, bool comments, bool check) :
+       // pass through
+       impl_(new Deserializer::DeserializerImpl(input, comments, check)) {}
+
+
+SerializablePtr Deserializer::deserialize()
+{
+       Serializable* ptr = pullNext();
+       if (ptr)
+       {
+               ptr->deserialize(*this);
+       }
+       return SerializablePtr(ptr);
+}
+
+
+Serializable* Deserializer::pullNext()
+{
+       impl_->parse();
+       if (!impl_->parsed.empty())
+       {
+               Serializable* ptr = impl_->parsed.front();
+               return ptr;
+       }
+       return 0;
+}
+
+void Deserializer::pop()
+{
+       // pass through
+       impl_->parsed.pop();
+}
+
+
+} // namespace Mf
+
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+
This page took 0.025255 seconds and 4 git commands to generate.