From 4a7d49f8b6ec7cca3699ad531718a7004cb1c519 Mon Sep 17 00:00:00 2001 From: ickby Date: Wed, 17 Oct 2018 07:01:38 +0200 Subject: [PATCH] Persistence: Enable binary dumps of properties --- src/App/PropertyContainerPy.xml | 17 +++++ src/App/PropertyContainerPyImp.cpp | 36 ++++++++++ src/Base/Persistence.cpp | 102 +++++++++++++++++++++++++++++ src/Base/Persistence.h | 6 ++ src/Base/PersistencePyImp.cpp | 90 +------------------------ 5 files changed, 163 insertions(+), 88 deletions(-) diff --git a/src/App/PropertyContainerPy.xml b/src/App/PropertyContainerPy.xml index cbf300ae46..112ad67a08 100644 --- a/src/App/PropertyContainerPy.xml +++ b/src/App/PropertyContainerPy.xml @@ -57,6 +57,23 @@ If the list contains 'Hidden' then the item even doesn't appear in the property Return the documentation string of the property of this class. + + + Dumps the content of the property, both the XML representation as well as the additional datafiles + required, into a byte representation. It will be returned as byte array. + dumpPropertyContent(propertyname) -- returns a byte array with full content + dumpPropertyContent(propertyname, [Compression=1-9]) -- Sets the data compression from 0 (no) to 9 (max) + + + + + + Restore the content of given property from a byte representation as stored by \"dumpContent\". + It could be restored from any python object implementing the buffer protocol. + restorePropertyContent(propertyname, buffer) -- restores from the given byte array + + + A list of all property names diff --git a/src/App/PropertyContainerPyImp.cpp b/src/App/PropertyContainerPyImp.cpp index f50347672f..a07e157651 100644 --- a/src/App/PropertyContainerPyImp.cpp +++ b/src/App/PropertyContainerPyImp.cpp @@ -226,6 +226,42 @@ Py::List PropertyContainerPy::getPropertiesList(void) const return ret; } + +PyObject* PropertyContainerPy::dumpPropertyContent(PyObject *args, PyObject *kwds) { + + int compression = 3; + char* property; + static char* kwds_def[] = {"Compression",NULL}; + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwds_def, &property, &compression)) { + return NULL; + } + + Property* prop = getPropertyContainerPtr()->getPropertyByName(property); + if (!prop) { + PyErr_Format(PyExc_AttributeError, "Property container has no property '%s'", property); + return 0; + } + + return prop->dumpToPython(compression); +} + +PyObject* PropertyContainerPy::restorePropertyContent(PyObject *args) { + + PyObject* buffer; + char* property; + if( !PyArg_ParseTuple(args, "sO", &property, &buffer) ) + return NULL; + + Property* prop = getPropertyContainerPtr()->getPropertyByName(property); + if (!prop) { + PyErr_Format(PyExc_AttributeError, "Property container has no property '%s'", property); + return 0; + } + + return prop->restoreFromPython(buffer); +} + PyObject *PropertyContainerPy::getCustomAttributes(const char* attr) const { // search in PropertyList diff --git a/src/Base/Persistence.cpp b/src/Base/Persistence.cpp index cd215c4043..ddf52e931f 100644 --- a/src/Base/Persistence.cpp +++ b/src/Base/Persistence.cpp @@ -22,6 +22,9 @@ #include "PreCompiled.h" +#include "Writer.h" +#include "Reader.h" +#include "PyObjectBase.h" #ifndef _PreComp_ #endif @@ -95,3 +98,102 @@ std::string Persistence::encodeAttribute(const std::string& str) return tmp; } + +PyObject* Persistence::dumpToPython(int compression) { + + //setup the stream. the in flag is needed to make "read" work^ + std::stringstream stream(std::stringstream::out | std::stringstream::in | std::stringstream::binary); + + //we need to close the zipstream to get a good result, the only way to do this is to delete the ZipWriter. + //Hence the scope... + { + //create the writer + Base::ZipWriter writer(stream); + writer.setLevel(compression); + writer.putNextEntry("Document.xml"); + writer.setMode("BinaryBrep"); + + //save the content (we need to encapsulte it with xml tags to be able to read single element xmls like happen for properties) + writer.Stream() << "" << std::endl; + Save(writer); + writer.Stream() << ""; + writer.writeFiles(); + } + + //build the byte array with correct size + if(!stream.seekp(0, stream.end)) { + PyErr_SetString(PyExc_IOError, "Unable to find end of stream"); + return NULL; + } + std::stringstream::pos_type offset = stream.tellp(); + if(!stream.seekg(0, stream.beg)) { + PyErr_SetString(PyExc_IOError, "Unable to find begin of stream"); + return NULL; + } + + PyObject* ba = PyByteArray_FromStringAndSize(NULL, offset); + + //use the buffer protocol to access the underlying array and write into it + Py_buffer buf = Py_buffer(); + PyObject_GetBuffer(ba, &buf, PyBUF_WRITABLE); + try { + if(!stream.read((char*)buf.buf, offset)) { + PyErr_SetString(PyExc_IOError, "Error copying data into byte array"); + return NULL; + } + PyBuffer_Release(&buf); + } + catch(...) { + PyBuffer_Release(&buf); + PyErr_SetString(PyExc_IOError, "Error copying data into byte array"); + return NULL; + } + + return ba; +} + +PyObject* Persistence::restoreFromPython(PyObject *buffer) { + + //check if it really is a buffer + if( !PyObject_CheckBuffer(buffer) ) { + PyErr_SetString(PyExc_TypeError, "Must be a buffer object"); + return NULL; + } + + Py_buffer buf; + if(PyObject_GetBuffer(buffer, &buf, PyBUF_SIMPLE) < 0) + return NULL; + + if(!PyBuffer_IsContiguous(&buf, 'C')) { + PyErr_SetString(PyExc_TypeError, "Buffer must be contiguous"); + return NULL; + } + + try { + + //TODO: this mkes a stupid copy, we should make a stream directly from the buffer + std::stringstream stream(std::string((char*)buf.buf, buf.len), std::stringstream::in | std::stringstream::binary); + + zipios::ZipInputStream zipstream(stream); + Base::XMLReader reader("", zipstream); + + if (!reader.isValid()) { + PyErr_SetString(PyExc_IOError, "Unable to read file"); + return NULL; + } + + reader.readElement("Content"); + Restore(reader); + reader.readFiles(zipstream); + } + catch (const Base::Exception& e) { + PyErr_SetString(PyExc_IOError, e.what()); + return NULL; + } + catch (const std::exception& e) { + PyErr_SetString(PyExc_IOError, e.what()); + return NULL; + } + + return Py_None; +} diff --git a/src/Base/Persistence.h b/src/Base/Persistence.h index 7bbda4d31a..1ec0c0d81c 100644 --- a/src/Base/Persistence.h +++ b/src/Base/Persistence.h @@ -148,6 +148,12 @@ public: virtual void RestoreDocFile(Reader &/*reader*/); /// Encodes an attribute upon saving. static std::string encodeAttribute(const std::string&); + + //dump the persistence data into a python byte array data structure + PyObject* dumpToPython(int compression); + + //restore the persistence data from a python data structure that supports the buffer protocol + PyObject* restoreFromPython(PyObject*); }; } //namespace Base diff --git a/src/Base/PersistencePyImp.cpp b/src/Base/PersistencePyImp.cpp index 890773996b..823631b9ad 100644 --- a/src/Base/PersistencePyImp.cpp +++ b/src/Base/PersistencePyImp.cpp @@ -23,7 +23,6 @@ #include "PreCompiled.h" #include "Writer.h" -#include "Reader.h" #include "Persistence.h" // inclution of the generated files (generated By PersitancePy.xml) @@ -63,53 +62,7 @@ PyObject* PersistencePy::dumpContent(PyObject *args, PyObject *kwds) { return NULL; } - //setup the streams - std::stringstream stream(std::stringstream::out | std::stringstream::in | std::stringstream::binary); - - //we need to close the zipstream to get a good result, the only way to do this is to delete the ZipWriter. - //Hence the scope... - { - //create the writer - Base::ZipWriter writer(stream); - writer.setLevel(compression); - writer.putNextEntry("Document.xml"); - writer.setMode("BinaryBrep"); - - //save the content - getPersistencePtr()->Save(writer); - writer.writeFiles(); - } - - //build the byte array with correct size - if(!stream.seekp(0, stream.end)) { - PyErr_SetString(PyExc_IOError, "Unable to find end of stream"); - return NULL; - } - std::stringstream::pos_type offset = stream.tellp(); - if(!stream.seekg(0, stream.beg)) { - PyErr_SetString(PyExc_IOError, "Unable to find begin of stream"); - return NULL; - } - - PyObject* ba = PyByteArray_FromStringAndSize(NULL, offset); - - //use the buffer protocol to access the underlying array and write into it - Py_buffer buf = Py_buffer(); - PyObject_GetBuffer(ba, &buf, PyBUF_WRITABLE); - try { - if(!stream.read((char*)buf.buf, offset)) { - PyErr_SetString(PyExc_IOError, "Error copying data into byte array"); - return NULL; - } - PyBuffer_Release(&buf); - } - catch(...) { - PyBuffer_Release(&buf); - PyErr_SetString(PyExc_IOError, "Error copying data into byte array"); - return NULL; - } - - return ba; + return getPersistencePtr()->dumpToPython(compression); } PyObject* PersistencePy::restoreContent(PyObject *args) { @@ -119,46 +72,7 @@ PyObject* PersistencePy::restoreContent(PyObject *args) { return NULL; //check if it really is a buffer - if( !PyObject_CheckBuffer(buffer) ) { - PyErr_SetString(PyExc_TypeError, "Must be a buffer object"); - return NULL; - } - - Py_buffer buf; - if(PyObject_GetBuffer(buffer, &buf, PyBUF_SIMPLE) < 0) - return NULL; - - if(!PyBuffer_IsContiguous(&buf, 'C')) { - PyErr_SetString(PyExc_TypeError, "Buffer must be contiguous"); - return NULL; - } - - try { - - //TODO: this mkes a stupid copy, we should make a stream directly from the buffer - std::stringstream stream(std::string((char*)buf.buf, buf.len), std::stringstream::in | std::stringstream::binary); - - zipios::ZipInputStream zipstream(stream); - Base::XMLReader reader("", zipstream); - - if (!reader.isValid()) { - PyErr_SetString(PyExc_IOError, "Unable to read file"); - return NULL; - } - - getPersistencePtr()->Restore(reader); - reader.readFiles(zipstream); - } - catch (const Base::Exception& e) { - PyErr_SetString(PyExc_IOError, e.what()); - return NULL; - } - catch (const std::exception& e) { - PyErr_SetString(PyExc_IOError, e.what()); - return NULL; - } - - return Py_None; + return getPersistencePtr()->restoreFromPython(buffer); } PyObject *PersistencePy::getCustomAttributes(const char*) const