commit 5d9bd6de45b58a30c16f3a12699f4400f49d43c7 Author: Anselme Date: Thu Jan 19 12:43:51 2017 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..996ee89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.user +build* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..58aa5d7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +project(SparrowSerializer) +cmake_minimum_required(VERSION 2.8) + +# choose source file +file(GLOB LIB_SRC_LIST src/property.cpp src/serializable.cpp src/serializationmanager.cpp) +file(GLOB LIB_HEAD_LIST src/*.h) +set(EXEC_SRC_LIST src/main.cpp) + +#set compilation option +set(IS_LIBRARY True) + +set(CMAKE_TEMPLATE_PATH "../CMakeTemplate") + +include(${CMAKE_TEMPLATE_PATH}/template.cmake) diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4823388 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,74 @@ +#include + +#include "serializationmanager.h" +#include "serializable.h" + +#include +#include + +class Foo : public Serializable +{ +public: + INT (myInt) + STRING (myString) + VECTOR_FLOAT(myFloatArray) + VEC3 (myVec3) + REFERENCE (Foo, myFoo) + + SERIALIZABLE(Foo, + CAST(myInt), + CAST(myString), + CAST(myFloatArray), + CAST(myVec3), + CAST(myFoo)) + + Foo() + { + myInt = 3; + myString = "plop"; + myFloatArray.push_back(1.5); + myFloatArray.push_back(1.3); + myFloatArray.push_back(2.5); + myFloatArray.push_back(6.2); + myVec3 = glm::vec3(0.1, 0, 5.4); + myFoo = this; + } +}; + +INIT_SERIALIZABLE(Foo) + +int main() +{ + // init + Foo* f1 = new Foo(); + f1->myString = "Fo1"; + Foo* f2 = new Foo(); + f2->myString = "Fo2"; + f1->myFloatArray.clear(); + f2->myVec3 = glm::vec3(1, 2, 3); + f1->myFoo = f2; + f2->myFoo = nullptr; + + // serialization + ObjectSaver saver; + saver.addObject(f1); + std::fstream outFile; + outFile.open("test.bin", std::ios_base::out); + saver.saveBinary(outFile); + outFile.close(); + + ObjectLoader loader; + std::fstream inFile; + inFile.open("test.bin", std::ios_base::in); + loader.loadBinary(inFile); + inFile.close(); + + // print result + const std::vector fooVec = loader.getObjects(); + ObjectSaver printer; + for(Foo* f : fooVec) + printer.addObject(f); + printer.saveAscii(std::cout); + + return 0; +} diff --git a/src/property.cpp b/src/property.cpp new file mode 100644 index 0000000..61e3e0b --- /dev/null +++ b/src/property.cpp @@ -0,0 +1,145 @@ +#include "property.h" +#include "serializable.h" + +// INTEGER PROPERTY +template <> std::ostream& Property::saveBinary(std::ostream& os) { return os.write((char*)&value, sizeof(int)); } +template <> std::ostream& Property::saveAscii(std::ostream& os) { return os << value; } +template <> std::istream& Property::loadBinary(std::istream& is) { return is.read((char*)&value, sizeof(int)); } +template <> std::istream& Property::loadAscii(std::istream& is) { return is >> value; } +template <> AbstractProperty::PropertyType Property::getStaticPropertyType() { return AbstractProperty::INTEGER; } + +// FLOAT PROPERTY +template <> std::ostream& Property::saveBinary(std::ostream& os) { return os.write((char*)&value, sizeof(float)); } +template <> std::ostream& Property::saveAscii(std::ostream& os) { return os << value; } +template <> std::istream& Property::loadBinary(std::istream& is) { return is.read((char*)&value, sizeof(float)); } +template <> std::istream& Property::loadAscii(std::istream& is) { return is >> value; } +template <> AbstractProperty::PropertyType Property::getStaticPropertyType() { return AbstractProperty::FLOAT; } + +// BOOLEAN PROPERTY +template <> std::ostream& Property::saveBinary(std::ostream& os) { return os.write((char*)&value, sizeof(bool)); } +template <> std::ostream& Property::saveAscii(std::ostream& os) { return os << value; } +template <> std::istream& Property::loadBinary(std::istream& is) { return is.read((char*)&value, sizeof(bool)); } +template <> std::istream& Property::loadAscii(std::istream& is) { return is >> value; } +template <> AbstractProperty::PropertyType Property::getStaticPropertyType() { return AbstractProperty::BOOLEAN; } + +// VEC2 PROPERTY +template <> std::ostream& Property::saveBinary(std::ostream& os) { return os.write((char*)glm::value_ptr(value), sizeof(float)*value.length()); } +template <> std::ostream& Property::saveAscii(std::ostream& os) { os << "["; for(int i=0; i std::istream& Property::loadBinary(std::istream& is) { return is.read((char*)glm::value_ptr(value), sizeof(float)*value.length()); } +template <> std::istream& Property::loadAscii(std::istream& is) { is.ignore(1); for(int i=0; i> (glm::value_ptr(value)[i]), is.ignore(1); return is; } +template <> AbstractProperty::PropertyType Property::getStaticPropertyType() { return AbstractProperty::VEC2; } + +// VEC3 PROPERTY +template <> std::ostream& Property::saveBinary(std::ostream& os) { return os.write((char*)glm::value_ptr(value), sizeof(float)*value.length()); } +template <> std::ostream& Property::saveAscii(std::ostream& os) { os << "["; for(int i=0; i std::istream& Property::loadBinary(std::istream& is) { return is.read((char*)glm::value_ptr(value), sizeof(float)*value.length()); } +template <> std::istream& Property::loadAscii(std::istream& is) { is.ignore(1); for(int i=0; i> (glm::value_ptr(value)[i]), is.ignore(1); return is; } +template <> AbstractProperty::PropertyType Property::getStaticPropertyType() { return AbstractProperty::VEC3; } + +// MAT4 PROPERTY +template <> std::ostream& Property::saveBinary(std::ostream& os) { return os.write((char*)glm::value_ptr(value), sizeof(float)*value.length()); } +template <> std::ostream& Property::saveAscii(std::ostream& os) { os << "["; for(int i=0; i std::istream& Property::loadBinary(std::istream& is) { return is.read((char*)glm::value_ptr(value), sizeof(float)*value.length()); } +template <> std::istream& Property::loadAscii(std::istream& is) { is.ignore(1); for(int i=0; i> (glm::value_ptr(value)[i]), is.ignore(1); return is; } +template <> AbstractProperty::PropertyType Property::getStaticPropertyType() { return AbstractProperty::MAT4; } + +// STRING PROPERTY +template <> std::ostream& Property::saveBinary(std::ostream& os) +{ + static const char endChar = '\0'; + os.write(value.data(), sizeof(char)*value.size()); + return os.write(&endChar, sizeof(char));; +} +template <> std::ostream& Property::saveAscii(std::ostream& os) +{ + os << "\""; + for(char c : value) + { + switch(c) + { + case '\t' : + os << "\\t"; + break; + case '\\' : + os << "\\\\"; + break; + case '\n' : + os << "\\n"; + break; + case '\r' : + os << "\\r"; + break; + case '"' : + os << "\\\""; + break; + default: + os << c; + } + } + return os << "\""; +} +template <> std::istream& Property::loadBinary(std::istream& is) +{ + value = ""; + for(char c = is.get(); c != '\0'; c = is.get()) + value.push_back(c); + return is; +} +template <> std::istream& Property::loadAscii(std::istream& is) +{ + value = ""; + is.ignore(1); // " + for(char c = is.get(); c != '"'; c = is.get()) + { + if(c == '\\') + { + c = is.get(); + switch(c) + { + case 't' : + value.push_back('\t'); + break; + case '\\' : + value.push_back('\\'); + break; + case 'n' : + value.push_back('\n'); + break; + case 'r' : + value.push_back('\r'); + break; + case '"' : + value.push_back('"'); + c = 'c'; + break; + default: + value.push_back(c); + } + } + else + value.push_back(c); + } + return is; +} +template <> AbstractProperty::PropertyType Property::getStaticPropertyType() { return AbstractProperty::STRING; } + +// ARRAY PROPERTY SPECIAL BOOL HANDLING +template <> std::ostream& ArrayProperty::saveBinary(std::ostream& os) +{ + unsigned int size = value.size(); + os.write((char*)&size, sizeof(unsigned int)); + for(bool it : value) + Property(it).saveBinary(os); + return os; +} + +template <> std::ostream& ArrayProperty::saveAscii(std::ostream& os) +{ + os << "{ " << value.size() << ", [" << std::endl; + for(bool it : value) + { + Property(it).saveAscii(os); + os << "," << std::endl; + } + return os << "] }"; +} diff --git a/src/property.h b/src/property.h new file mode 100644 index 0000000..d145347 --- /dev/null +++ b/src/property.h @@ -0,0 +1,213 @@ +#ifndef PROPERTY_H +#define PROPERTY_H + +#include +#include +#include + +// PROPERTIES MACROS (used by Serializable class) + +#define PROPERTY(type, var)\ + Property var##Property;\ + type &var = var##Property.getValueRef();\ + +#define INT(var) PROPERTY(int, var) +#define FLOAT(var) PROPERTY(float, var) +#define BOOL(var) PROPERTY(bool, var) +#define VEC2(var) PROPERTY(glm::vec2, var) +#define VEC3(var) PROPERTY(glm::vec3, var) +#define MAT4(var) PROPERTY(glm::mat4, var) +#define STRING(var) PROPERTY(std::string, var) + +// the provided type must be a class inheriting Serializable +#define REFERENCE(type, var)\ + Property var##Property;\ + type*& var = (type*&)(var##Property.getValueRef());\ + +#define VECTOR_PROPERTY(type, var)\ + ArrayProperty var##Property;\ + std::vector &var = var##Property.getValueRef();\ + + +#define VECTOR_INT(var) VECTOR_PROPERTY(int, var) +#define VECTOR_FLOAT(var) VECTOR_PROPERTY(float, var) +#define VECTOR_BOOL(var) VECTOR_PROPERTY(bool, var) +#define VECTOR_VEC2(var) VECTOR_PROPERTY(glm::vec2, var) +#define VECTOR_VEC3(var) VECTOR_PROPERTY(glm::vec3, var) +#define VECTOR_MAT4(var) VECTOR_PROPERTY(glm::mat4, var) +#define VECTOR_STRING(var) VECTOR_PROPERTY(std::string, var) +#define VECTOR_REFERENCE(type, var) VECTOR_PROPERTY(type*, var) + +#define CAST(var) (AbstractProperty*)&var##Property + +// FORWARD DECLARATIONS AND TYPES + +class AbstractProperty; +class Serializable; + +typedef std::vector PropertySet; + +// PROPERTY INTERFACE + +class AbstractProperty +{ +public: + enum PropertyType + { + UNDEFINED, + + INTEGER, + FLOAT, + BOOLEAN, + VEC2, + VEC3, + MAT4, + STRING, + REFERENCE, + ARRAY, // this is a bit special, an array must also have one of the other types + + // keep last + NB_PROPERTY_TYPES + }; + + virtual ~AbstractProperty() {} + + virtual std::ostream& saveBinary(std::ostream& os) = 0; + virtual std::ostream& saveAscii(std::ostream& os) = 0; + + virtual std::istream& loadBinary(std::istream& is) = 0; + virtual std::istream& loadAscii(std::istream& is) = 0; + + virtual PropertyType getPropertyType() = 0; + + friend PropertySet& operator << (PropertySet &properties, AbstractProperty& p) + { properties.push_back(&p); return properties; } +}; + +// GENERIC PROPERTY + +template +class Property : public AbstractProperty +{ + T value; +public: + Property(T val) : value(val) {} + Property() {} + ~Property() {} + + std::ostream& saveBinary(std::ostream& os); + std::ostream& saveAscii(std::ostream& os); + + std::istream& loadBinary(std::istream& is); + std::istream& loadAscii(std::istream& is); + + PropertyType getPropertyType() { return getStaticPropertyType(); } + static PropertyType getStaticPropertyType(); + + T &getValueRef() { return value; } +}; + +/** + * Reference properties are implemented through partial specialization of Property, + * it can compile with any pointer, + * but will only work will pointers to a class that inherits Serializable + */ +template +class Property : public AbstractProperty +{ + T* value; +public: + Property(T* val) : value(val) {} + Property() {} + ~Property() {} + + // will not be used, serialization of references is entirely done by the serialization manager + std::ostream& saveBinary(std::ostream& os) { return os; } + std::ostream& saveAscii(std::ostream& os) { return os; } + std::istream& loadBinary(std::istream& is) { return is; } + std::istream& loadAscii(std::istream& is) { return is; } + + PropertyType getPropertyType() { return getStaticPropertyType(); } + static PropertyType getStaticPropertyType() { return REFERENCE; } + T*& getValueRef() { return value; } +}; + +// GENERIC VECTOR PROPERTY + +class AbstractArrayProperty : public AbstractProperty +{ +public: + PropertyType getPropertyType() { return ARRAY; } + + virtual PropertyType getDataType() { return UNDEFINED; } +}; + +template +class ArrayProperty : public AbstractArrayProperty +{ + std::vector value; +public: + ArrayProperty(const std::vector &val) : value(val) {} + ArrayProperty() {} + ~ArrayProperty() {} + + std::ostream& saveBinary(std::ostream& os) + { + Property size(value.size()); + size.saveBinary(os); + for(T &it : value) + Property(it).saveBinary(os); + return os; + } + + std::ostream& saveAscii(std::ostream& os) + { + os << "["; + int i = value.size(); + for(T &it : value) + { + Property(it).saveAscii(os); + if(--i) os << ", "; + } + return os << "]"; + } + + std::istream& loadBinary(std::istream& is) + { + Property size; + size.loadBinary(is); + value.resize(size.getValueRef()); + Property p; + for(int i=0; i p; + while(is.peek() != ']') + { + p.loadAscii(is); + value.push_back(p.getValueRef()); + if(is.peek() == ',') + is.ignore(1); // , + } + return is.ignore(1); + } + + // array specific methods + + PropertyType getDataType() { return Property::getStaticPropertyType(); } + + std::vector &getValueRef() { return value; } + + unsigned int getArraySize() { return value.size(); } +}; + +#endif // PROPERTY_H diff --git a/src/serializable.cpp b/src/serializable.cpp new file mode 100644 index 0000000..30e1973 --- /dev/null +++ b/src/serializable.cpp @@ -0,0 +1,16 @@ +#include "serializable.h" + +std::unordered_map* Serializable::s_typeMap = nullptr; + +bool Serializable::addType(const std::string &name, Instancer fun) +{ + if(s_typeMap == nullptr) + s_typeMap = new std::unordered_map(); + (*s_typeMap)[name] = fun; + return true; +} + +PropertySet* Serializable::initProperties(PropertySet* properties, AbstractProperty* p) const +{ + properties->push_back(p); return properties; +} diff --git a/src/serializable.h b/src/serializable.h new file mode 100644 index 0000000..2968165 --- /dev/null +++ b/src/serializable.h @@ -0,0 +1,83 @@ +#ifndef SERIALIZABLE_H +#define SERIALIZABLE_H + +#include +#include "property.h" + +/** +// To use these macros, your class should inherit the abstract class "Serializable" : + +class MyClass : public Serializable + +// you should first declare properties like this : + +private: + INT(myIntProperty) + REFERENCE(myPointer) + VECTOR_FLOAT(myFloatArray) + +// and then, you'll be able to automatically save these properties after using the following macro : + +public: + SERIALIZABLE(MyClass, CAST(myIntProperty), CAST(myPointer), CAST(myFloatArray)) + +// to be able to load your saved object, you will need to add the following declaration in the CPP file of your class : + +INIT_SERIALIZABLE(MyClass) + +// that's it ! now you can use the ObjectSaver and ObjectLoader classes to serialize your objects automatically. +*/ + +// ALTERNATIVE INITIALIZATION : + +// use this if you really can't have a cpp file for your class +#define MANUAL_SERIALIZABLE(className, args...)\ + virtual const std::string &getType() const { return className::getStaticType(); }\ + static const std::string &getStaticType() { static const std::string name = #className; return name; }\ + virtual PropertySet* getProperties() const { return initProperties(new PropertySet(), args); }\ + static Serializable* instantiate() { return (Serializable*)(new className()); }\ + +// can be called to init the class manually whenever you want in your program (before any deserialization of this class) +#define MANUAL_INIT_SERIALIZABLE(className) Serializable::addType(#className, className::instantiate); + +// RECOMMENDED INITIALIZATION + +#define SERIALIZABLE(className, args...)\ + virtual const std::string &getType() const { return className::getStaticType(); }\ + static const std::string &getStaticType() { static const std::string name = #className; return name; }\ + virtual PropertySet* getProperties() const { return initProperties(new PropertySet(), args); }\ + static bool init;\ + static Serializable* instantiate() { return (Serializable*)(new className()); }\ + +// used to auto-init the class at startup (with global variables initialization) +#define INIT_SERIALIZABLE(className) bool className::init = MANUAL_INIT_SERIALIZABLE(className)\ + +// END OF MACROS + +/** + * @brief Most of the methods and members of this class are only used by the macros and the automatic serializer, you shouldn't have to worry about them. + */ +class Serializable +{ +public: + virtual const std::string &getType() const = 0; + virtual PropertySet* getProperties() const = 0; + + typedef Serializable* (*Instancer)(void); + static Instancer getInstancer(const std::string &type) { return (*s_typeMap)[type]; } +private: + static std::unordered_map *s_typeMap; +protected: + static bool addType(const std::string &name, Instancer fun); + + PropertySet* initProperties(PropertySet* properties, AbstractProperty* p) const; + + template // recursive variadic + PropertySet* initProperties(PropertySet* properties, AbstractProperty* p, Args... args) const + { + properties->push_back(p); + return initProperties(properties, args...); + } +}; + +#endif // SERIALIZABLE_H diff --git a/src/serializationmanager.cpp b/src/serializationmanager.cpp new file mode 100644 index 0000000..6052b36 --- /dev/null +++ b/src/serializationmanager.cpp @@ -0,0 +1,348 @@ +#include "serializationmanager.h" +#include "serializable.h" +#include + +void ObjectSaver::addObject(Serializable *object) +{ + if(object == nullptr) + return; + const std::string &type = object->getType(); + ObjectType &objectsOfSameType = m_objects[type]; + if(objectsOfSameType.count(object) == 0) + { + int n = objectsOfSameType.size(); + objectsOfSameType[object] = n; + PropertySet *properties = object->getProperties(); + for(AbstractProperty* p : *properties) + { + if(p->getPropertyType() == AbstractProperty::REFERENCE) + addObject(((Property*)p)->getValueRef()); + else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE) + { + std::vector &vecRef = ((ArrayProperty*)p)->getValueRef(); + for(Serializable *ref : vecRef) + addObject(ref); + } + } + delete properties; + } +} + +std::ostream& ObjectSaver::saveBinary(std::ostream& os) +{ + // loop on serializable types + Property(m_objects.size()).saveBinary(os); // amount of types + for(const std::pair &type : m_objects) + { + // serialize type + Property(type.first).saveBinary(os); // type name + Property(type.second.size()).saveBinary(os); // amount of objects of this type + + // order the objects + std::vector orderedObjects(type.second.size()); + for(const std::pair &entry : type.second) + orderedObjects[entry.second] = entry.first; + + // serialize objects of this type + for(Serializable* object : orderedObjects) + { + PropertySet *properties = object->getProperties(); + for(AbstractProperty *p : *properties) + { + if(p->getPropertyType() == AbstractProperty::REFERENCE) + { + Serializable *ref = ((Property*)p)->getValueRef(); + if(ref == nullptr) + Property("null").saveBinary(os); + else + { + Property(ref->getType()).saveBinary(os); // type name + Property(m_objects[ref->getType()][ref]).saveBinary(os); // id of the object + } + } + else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE) + { + std::vector &vecRef = ((ArrayProperty*)p)->getValueRef(); + Property(vecRef.size()).saveBinary(os); // amount of references + for(Serializable *ref : vecRef) + { + if(ref == nullptr) + Property("null").saveBinary(os); + else + { + Property(ref->getType()).saveBinary(os); // type name + Property(m_objects[ref->getType()][ref]).saveBinary(os); // id of the object + } + } + } + else + p->saveBinary(os); + } + delete properties; + } + } + return os; +} + +std::ostream& ObjectSaver::saveAscii(std::ostream& os) +{ + os << "{" << std::endl; + // loop on serializable types + for(const std::pair &type : m_objects) + { + // serialize type + os << "\t\"" << type.first << "\": [" << std::endl; + + // order the objects + std::vector orderedObjects(type.second.size()); + for(const std::pair &entry : type.second) + orderedObjects[entry.second] = entry.first; + + // serialize objects of this type + for(Serializable* object : orderedObjects) + { + os << "\t\t{" << std::endl; + PropertySet *properties = object->getProperties(); + for(AbstractProperty *p : *properties) + { + os << "\t\t\t"; + if(p->getPropertyType() == AbstractProperty::REFERENCE) + { + Serializable *ref = ((Property*)p)->getValueRef(); + if(ref == nullptr) + os << "\"null\""; + else + os << "{\"" << ref->getType() << "\", " << (m_objects[ref->getType()][ref]) << "}"; + } + else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE) + { + std::vector &vecRef = ((ArrayProperty*)p)->getValueRef(); + os << "["; + int i = vecRef.size(); + for(Serializable *ref : vecRef) + { + if(ref == nullptr) + os << "\"null\""; + else + os << "{\"" << ref->getType() << "\", " << (m_objects[ref->getType()][ref]) << "}"; + if(--i) os << ", "; + } + os << "]"; + } + else + p->saveAscii(os); + + if(properties->back() == p) + os << std::endl; + else + os << "," << std::endl; + } + delete properties; + os << "\t\t}"; + if(orderedObjects.back() == object) + os << std::endl; + else + os << "," << std::endl; + } + os << "\t]" << std::endl; + } + return os << "}" << std::endl; +} + +std::istream& ObjectLoader::loadBinary(std::istream& is) +{ + Property nbTypes; + nbTypes.loadBinary(is); // get amount of types to read + for(int i=0; i typeName; + typeName.loadBinary(is); // get the type name + Serializable::Instancer instancer = Serializable::getInstancer(typeName.getValueRef()); + Property nbObjects; + nbObjects.loadBinary(is); // get the amount of objects of that type + std::vector &objectsOfThatType = m_objects[typeName.getValueRef()]; + for(int j=0; jgetProperties(); + for(AbstractProperty* p : *properties) + { + if(p->getPropertyType() == AbstractProperty::REFERENCE) + { + Property type; // type name + type.loadBinary(is); + if(type.getValueRef().compare("null") != 0) + { + Property id; // id of the object + id.loadBinary(is); + PendingReference ref; + ref.ptr = &(((Property*)p)->getValueRef()); + ref.type = type.getValueRef(); + ref.id = id.getValueRef(); + m_references.push_back(ref); + } + else + ((Property*)p)->getValueRef() = nullptr; + } + else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE) + { + Property nbRef; // amount of references in the array + nbRef.loadBinary(is); + std::vector &vecRef = ((ArrayProperty*)p)->getValueRef(); + vecRef = std::vector(nbRef.getValueRef(), nullptr); + for(Serializable* &it : vecRef) + { + Property type; // type name + type.loadBinary(is); + if(type.getValueRef().compare("null") != 0) + { + Property id; // id of the object + id.loadBinary(is); + PendingReference ref; + ref.ptr = ⁢ + ref.type = type.getValueRef(); + ref.id = id.getValueRef(); + m_references.push_back(ref); + } + } + } + else + p->loadBinary(is); + } + delete properties; + } + } + buildReferences(); + return is; +} + +std::istream& ObjectLoader::loadAscii(std::istream& is) +{ + // get trimmed version of the stream into a stringstream + while(is.get() != '{'); // got to first opening brace + int nbBraces = 1; + bool inString = false; + std::stringstream ss; + while(nbBraces > 0) // read and trim until we find the corresponding closing brace + { + int c = is.get(); + switch(c) + { + case '{': + ++nbBraces; + ss.put(c); + break; + case '}': + --nbBraces; + ss.put(c); + break; + case '\\': + if(is.peek() == '"') + inString = !inString; + ss.put(c); + break; + case '"': + inString = !inString; + ss.put(c); + break; + case ' ': + if(inString) + ss.put(c); + case '\n': + case '\t': + case '\r': + break; + default: + ss.put(c); + } + } + + // begin parsing + while(ss.peek() != '}') // loop on serializable types + { + Property typeName; + typeName.loadAscii(ss); + Serializable::Instancer instancer = Serializable::getInstancer(typeName.getValueRef()); + std::vector &objectsOfThatType = m_objects[typeName.getValueRef()]; + ss.ignore(2); // :[ + while(ss.peek() != ']') // loop on the objects of that type + { + ss.ignore(1); // { + Serializable* object = instancer(); + objectsOfThatType.push_back(object); + PropertySet* properties = object->getProperties(); + for(AbstractProperty* p : *properties) + { + if(p->getPropertyType() == AbstractProperty::REFERENCE) + { + if(ss.peek() == '{') + { + ss.ignore(1); // { + Property type; // type name + type.loadAscii(ss); + ss.ignore(1); // , + Property id; // id of the object + id.loadAscii(ss); + ss.ignore(1); + PendingReference ref; + ref.ptr = &(((Property*)p)->getValueRef()); + ref.type = type.getValueRef(); + ref.id = id.getValueRef(); + m_references.push_back(ref); + ss.ignore(1); // } + } + else + { + ss.ignore(6); // "null" + ((Property*)p)->getValueRef() = nullptr; + } + } + else if(p->getPropertyType() == AbstractProperty::ARRAY && ((AbstractArrayProperty*)p)->getDataType() == AbstractProperty::REFERENCE) + { + std::vector &vecRef = ((ArrayProperty*)p)->getValueRef(); + ss.ignore(1); // [ + while(ss.peek() != ']') + { + vecRef.push_back(nullptr); + if(ss.peek() == '{') + { + ss.ignore(1); // { + Property type; // type name + type.loadAscii(ss); + ss.ignore(1); // , + Property id; // id of the object + id.loadAscii(ss); + ss.ignore(1); // } + PendingReference ref; + ref.ptr = &(vecRef.back()); + ref.type = type.getValueRef(); + ref.id = id.getValueRef(); + m_references.push_back(ref); + } + else + ss.ignore(6); // "null" + if(ss.peek() != ',') + ss.ignore(1); // , + } + ss.ignore(1); // ] + } + else + p->loadAscii(ss); + if(p != properties->back()) + ss.ignore(1); // , + } + ss.ignore(1); // } + } + ss.ignore(1); // ] + } + buildReferences(); + return is; +} + +void ObjectLoader::buildReferences() +{ + for(PendingReference &ref : m_references) + *(ref.ptr) = m_objects[ref.type][ref.id]; + m_references.clear(); +} diff --git a/src/serializationmanager.h b/src/serializationmanager.h new file mode 100644 index 0000000..267ab50 --- /dev/null +++ b/src/serializationmanager.h @@ -0,0 +1,44 @@ +#ifndef SERIALIZATIONMANAGER_H +#define SERIALIZATIONMANAGER_H + +#include +#include + +class Serializable; + +class ObjectSaver +{ + typedef std::unordered_map ObjectType; + std::unordered_map m_objects; +public: + void addObject(Serializable *object); + template + void addObject(T *object) { addObject((Serializable*)object); } + + std::ostream& saveBinary(std::ostream& os); + std::ostream& saveAscii(std::ostream& os); +}; + +class ObjectLoader +{ + struct PendingReference + { + Serializable **ptr; + std::string type; + int id; + }; + + std::vector m_references; + std::unordered_map> m_objects; + + void buildReferences(); +public: + std::istream& loadBinary(std::istream& is); + std::istream& loadAscii(std::istream& is); + + const std::vector& getObjects(const std::string &type) { return m_objects[type]; } + template + const std::vector& getObjects() { return (const std::vector&)(m_objects[T::getStaticType()]); } +}; + +#endif // SERIALIZATIONMANAGER_H