Skip to content
Snippets Groups Projects
Commit abe5b064 authored by Federico's avatar Federico
Browse files

Separate stream and buffer API in different headers

parent 9694df87
Branches
Tags
Loading
/***************************************************************************
* Copyright (C) 2018 by Terraneo Federico *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include "buffer.h"
using namespace std;
namespace tscpp {
int TypePoolBuffer::unserializeUnknownImpl(const char *name, const void *buffer, int bufSize) const
{
auto it=types.find(name);
if(it==types.end()) return UnknownType;
if(it->second.size>bufSize) return BufferTooSmall;
it->second.usc(buffer);
return it->second.size;
}
int serializeImpl(void *buffer, int bufSize, const char *name, const void *data, int size)
{
int nameSize=strlen(name);
int serializedSize=nameSize+1+size;
if(serializedSize>bufSize) return BufferTooSmall;
char *buf=reinterpret_cast<char*>(buffer);
memcpy(buf,name,nameSize+1); //Copy also the \0
memcpy(buf+nameSize+1,data,size);
return serializedSize;
}
int unserializeImpl(const char *name, void *data, int size, const void *buffer, int bufSize)
{
int nameSize=strlen(name);
int serializedSize=nameSize+1+size;
if(serializedSize>bufSize) return BufferTooSmall;
const char *buf=reinterpret_cast<const char*>(buffer);
if(memcmp(buf,name,nameSize+1)) return WrongType;
//NOTE: we are writing on top of a constructed type without calling its
//destructor. However, since it is trivially copyable, we at least aren't
//overwriting pointers to allocated memory.
memcpy(data,buf+nameSize+1,size);
return serializedSize;
}
int unserializeUnknown(const TypePoolBuffer& tp, const void *buffer, int bufSize)
{
const char *buf=reinterpret_cast<const char*>(buffer);
int nameSize=strnlen(buf,bufSize);
if(nameSize>=bufSize) return BufferTooSmall;
const char *name=buf;
buf+=nameSize+1;
bufSize-=nameSize+1;
auto result=tp.unserializeUnknownImpl(name,buf,bufSize);
if(result<0) return result;
return result+nameSize+1;
}
string peekTypeName(const void *buffer, int bufSize)
{
const char *buf=reinterpret_cast<const char*>(buffer);
int nameSize=strnlen(buf,bufSize);
if(nameSize>=bufSize) return "";
return buf;
}
} //namespace tscpp
buffer.h 0 → 100644
/***************************************************************************
* Copyright (C) 2018 by Terraneo Federico *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#pragma once
#include <type_traits>
#include <functional>
#include <cstring>
#include <string>
#include <map>
/**
* \file buffer.h
* TSCPP buffer API. This file contains functions to serialize types to raw
* memory buffers. These classes provide a low level API to serialization.
* Error reporting is performed through return codes.
*
* NOTE: the serialization format between the buffer and stream API is
* interchangeable, so you can for example serialize using the buffer API and
* unserialize using the stream API.
*/
namespace tscpp {
/**
* Error codes returned by the buffer API of tscpp
*/
enum TscppError
{
BufferTooSmall = -1, ///< Buffer is too small for the given type
WrongType = -2, ///< While unserializing a different type was found
UnknownType = -3 ///< While unserializing the type was not found in the pool
};
/**
* Type pool for the TSCPP buffer API.
* A type pool is a class where you can register types and associate callbacks
* to them. It is used to unserialize types when you don't know the exact type
* or order in which types have been serialized.
*/
class TypePoolBuffer
{
public:
/**
* Register a type and the associated callback
* \tparam T type to be registered
* \param callback callabck to be called whan the given type is unserialized
*/
template<typename T>
void registerType(std::function<void (T& t)> callback)
{
#ifndef _MIOSIX
static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable");
#endif
types[typeid(T).name()]=UnserializerImpl(sizeof(T),[=](const void *buffer) {
//NOTE: We copy the buffer to respect alignment requirements.
//The buffer may not be suitably aligned for the unserialized type
//TODO: support classes without default constructor
//NOTE: we are writing on top of a constructed type without callingits
//destructor. However, since it is trivially copyable, we at least aren't
//overwriting pointers to allocated memory.
T t;
memcpy(&t,buffer,sizeof(T));
callback(t);
});
}
/**
* \internal
*/
int unserializeUnknownImpl(const char *name, const void *buffer, int bufSize) const;
private:
/**
* \internal
*/
class UnserializerImpl
{
public:
UnserializerImpl() : size(0) {}
UnserializerImpl(int size, std::function<void (const void*)> usc) : size(size), usc(usc) {}
int size;
std::function<void (const void*)> usc;
};
std::map<std::string,UnserializerImpl> types; ///< Registered types
};
/**
* \internal
*/
int serializeImpl(void *buffer, int bufSize, const char *name, const void *data, int size);
/**
* Serialize a type to a memory buffer
* \param buffer ponter to the memory buffer where to serialize the type
* \param bufSize buffer size
* \param t type to serialize
* \return the size of the serialized type (which is larger than sizeof(T) due
* to serialization overhead), or TscppError::BufferTooSmall if the given
* buffer is too small
*/
template<typename T>
int serialize(void *buffer, int bufSize, const T& t)
{
#ifndef _MIOSIX
static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable");
#endif
return serializeImpl(buffer,bufSize,typeid(t).name(),&t,sizeof(t));
}
/**
* \internal
*/
int unserializeImpl(const char *name, void *data, int size, const void *buffer, int bufSize);
/**
* Unserialize a known type from a memory buffer
* \param t type to unserialize
* \param buffer pointer to buffer where the serialized type is
* \param bufSize buffer size
* \return the size of the unserialized type (which is larger than sizeof(T) due
* to serialization overhead), or TscppError::WrongType if the buffer does
* not contain the given type or TscppError::BufferTooSmall if the type is
* truncated, i.e the buffer is smaller tah the serialized type size
*/
template<typename T>
int unserialize(T& t, const void *buffer, int bufSize)
{
#ifndef _MIOSIX
static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable");
#endif
return unserializeImpl(typeid(t).name(),&t,sizeof(t),buffer,bufSize);
}
/**
* Unserialize an unknown type from a memory buffer
* \param tp type pool where possible serialized types are registered
* \param buffer pointer to buffer where the serialized type is
* \param bufSize buffer size
* \return the size of the unserialized type (which is larger than sizeof(T) due
* to serialization overhead), or TscppError::UnknownType if the pool does
* not contain the type found in the buffer or TscppError::BufferTooSmall if the
* type is truncated, i.e the buffer is smaller tah the serialized type size
*/
int unserializeUnknown(const TypePoolBuffer& tp, const void *buffer, int bufSize);
/**
* Given a buffer where a type has been serialized, return the C++ mangled
* name of the serialized type.
* It is useful when unserialize returns TscppError::WrongType to print an
* error message with the name of the type in the buffer
* \code
* Foo f;
* auto result=unserialize(f,buffer,size);
* if(result==WrongType)
* {
* cerr<<"While unserializing Foo, "<<demangle(peekTypeName(buffer,size))<<" was found\n";
* }
* \endcode
* \param buffer pointer to buffer where the serialized type is
* \param bufSize buffer size
* \return the serialized type name, or "" if the buffer does not contain a type
* name
*/
std::string peekTypeName(const void *buffer, int bufSize);
} // namespace tscpp
......@@ -2,7 +2,7 @@
#include <iostream>
#include <sstream>
#include <cassert>
#include <tscpp.h>
#include <tscpp/stream.h>
#include "types.h"
using namespace std;
......
......@@ -3,7 +3,7 @@
#include <sstream>
#include <cassert>
#include <cstdlib>
#include <tscpp.h>
#include <tscpp/stream.h>
#include "types.h"
using namespace std;
......@@ -17,7 +17,7 @@ int main()
MiscData md(p2d,p3d,12,-1);
//Prepare a type pool for unserialization
TypePool tp;
TypePoolStream tp;
tp.registerType<Point2d>([&](Point2d& t)
{
cout<<"Found a Point2d"<<endl;
......
#include <iostream>
#include <cassert>
#include <tscpp.h>
#include <tscpp/buffer.h>
#include "types.h"
using namespace std;
......
......@@ -2,7 +2,7 @@
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <tscpp.h>
#include <tscpp/buffer.h>
#include "types.h"
using namespace std;
......@@ -16,7 +16,7 @@ int main()
MiscData md(p2d,p3d,12,-1);
//Prepare a type pool for unserialization
TypePool tp;
TypePoolBuffer tp;
tp.registerType<Point2d>([&](Point2d& t)
{
cout<<"Found a Point2d"<<endl;
......
......@@ -2,7 +2,7 @@
#include <iostream>
#include <sstream>
#include <cassert>
#include <tscpp.h>
#include <tscpp/stream.h>
#include "types.h"
using namespace std;
......@@ -67,7 +67,7 @@ int main()
}
}
TypePool tp;
TypePoolStream tp;
tp.registerType<Point2d>([&](Point2d& t) { assert(false); });
tp.registerType<Point3d>([&](Point3d& t) { assert(false); });
......
......@@ -2,7 +2,7 @@
#include <iostream>
#include <sstream>
#include <cassert>
#include <tscpp.h>
#include <tscpp/buffer.h>
#include "types.h"
using namespace std;
......@@ -41,7 +41,7 @@ int main()
assert(q.x==0 && q.y==0);
}
TypePool tp;
TypePoolBuffer tp;
tp.registerType<Point2d>([&](Point2d& t) { assert(false); });
tp.registerType<Point3d>([&](Point3d& t) { assert(false); });
......
CXX = g++
CXXFLAGS = -std=c++11 -g -O0 -fsanitize=address -Wall -I..
CXXFLAGS = -std=c++11 -g -O0 -fsanitize=address -Wall -I../..
all:
$(CXX) $(CXXFLAGS) 1_stream_known.cpp ../tscpp.cpp -o 1_stream_known
$(CXX) $(CXXFLAGS) 2_stream_unknown.cpp ../tscpp.cpp -o 2_stream_unknown
$(CXX) $(CXXFLAGS) 3_buffer_known.cpp ../tscpp.cpp -o 3_buffer_known
$(CXX) $(CXXFLAGS) 4_buffer_unknown.cpp ../tscpp.cpp -o 4_buffer_unknown
$(CXX) $(CXXFLAGS) 5_stream_failtest.cpp ../tscpp.cpp -o 5_stream_failtest
$(CXX) $(CXXFLAGS) 6_buffer_failtest.cpp ../tscpp.cpp -o 6_buffer_failtest
$(CXX) $(CXXFLAGS) 1_stream_known.cpp ../stream.cpp -o 1_stream_known
$(CXX) $(CXXFLAGS) 2_stream_unknown.cpp ../stream.cpp -o 2_stream_unknown
$(CXX) $(CXXFLAGS) 3_buffer_known.cpp ../buffer.cpp -o 3_buffer_known
$(CXX) $(CXXFLAGS) 4_buffer_unknown.cpp ../buffer.cpp -o 4_buffer_unknown
$(CXX) $(CXXFLAGS) 5_stream_failtest.cpp ../stream.cpp -o 5_stream_failtest
$(CXX) $(CXXFLAGS) 6_buffer_failtest.cpp ../buffer.cpp -o 6_buffer_failtest
./1_stream_known
./2_stream_unknown
./3_buffer_known
......
......@@ -25,7 +25,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include "tscpp.h"
#include "stream.h"
#include <memory>
#if defined(__GNUC__) && !defined(_MIOSIX)
#include <cxxabi.h>
......@@ -35,18 +35,7 @@ using namespace std;
namespace tscpp {
int TypePool::unserializeUnknownImpl(const char *name, const void *buffer, int bufSize) const
{
auto it=types.find(name);
if(it==types.end()) return UnknownType;
if(it->second.size>bufSize) return BufferTooSmall;
it->second.usc(buffer);
return it->second.size;
}
void TypePool::unserializeUnknownImpl(const string& name, istream& is, streampos pos) const
void TypePoolStream::unserializeUnknownImpl(const string& name, istream& is, streampos pos) const
{
auto it=types.find(name);
if(it==types.end()) {
......@@ -60,70 +49,6 @@ void TypePool::unserializeUnknownImpl(const string& name, istream& is, streampos
it->second.usc(unserialized.get());
}
int serializeImpl(void *buffer, int bufSize, const char *name, const void *data, int size)
{
int nameSize=strlen(name);
int serializedSize=nameSize+1+size;
if(serializedSize>bufSize) return BufferTooSmall;
char *buf=reinterpret_cast<char*>(buffer);
memcpy(buf,name,nameSize+1); //Copy also the \0
memcpy(buf+nameSize+1,data,size);
return serializedSize;
}
int unserializeImpl(const char *name, void *data, int size, const void *buffer, int bufSize)
{
int nameSize=strlen(name);
int serializedSize=nameSize+1+size;
if(serializedSize>bufSize) return BufferTooSmall;
const char *buf=reinterpret_cast<const char*>(buffer);
if(memcmp(buf,name,nameSize+1)) return WrongType;
//NOTE: we are writing on top of a constructed type without calling its
//destructor. However, since it is trivially copyable, we at least aren't
//overwriting pointers to allocated memory.
memcpy(data,buf+nameSize+1,size);
return serializedSize;
}
int unserializeUnknown(const TypePool& tp, const void *buffer, int bufSize)
{
const char *buf=reinterpret_cast<const char*>(buffer);
int nameSize=strnlen(buf,bufSize);
if(nameSize>=bufSize) return BufferTooSmall;
const char *name=buf;
buf+=nameSize+1;
bufSize-=nameSize+1;
auto result=tp.unserializeUnknownImpl(name,buf,bufSize);
if(result<0) return result;
return result+nameSize+1;
}
string peekTypeName(const void *buffer, int bufSize)
{
const char *buf=reinterpret_cast<const char*>(buffer);
int nameSize=strnlen(buf,bufSize);
if(nameSize>=bufSize) return "";
return buf;
}
string demangle(const string& name)
{
#if defined(__GNUC__) && !defined(_MIOSIX)
string result=name;
int status;
char* demangled=abi::__cxa_demangle(name.c_str(),NULL,0,&status);
if(status==0 && demangled) result=demangled;
if(demangled) free(demangled);
return result;
#else
return name; //Demangle not supported
#endif
}
void OutputArchive::serializeImpl(const char *name, const void *data, int size)
{
int nameSize=strlen(name);
......@@ -167,4 +92,18 @@ void UnknownInputArchive::unserialize()
tp.unserializeUnknownImpl(name,is,pos);
}
string demangle(const string& name)
{
#if defined(__GNUC__) && !defined(_MIOSIX)
string result=name;
int status;
char* demangled=abi::__cxa_demangle(name.c_str(),NULL,0,&status);
if(status==0 && demangled) result=demangled;
if(demangled) free(demangled);
return result;
#else
return name; //Demangle not supported
#endif
}
} //namespace tscpp
......@@ -25,6 +25,17 @@
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
/**
* \file stream.h
* TSCPP stream API. This file contains classes to serialize types to std
* streams. These classes provide a high level API compatible with the C++ stl.
* Error reporting is performed through exceptions.
*
* NOTE: the serialization format between the buffer and stream API is
* interchangeable, so you can for example serialize using the buffer API and
* unserialize using the stream API.
*/
#pragma once
#include <type_traits>
......@@ -39,21 +50,12 @@
namespace tscpp {
/**
* Error codes returned by the buffer API of tscpp
*/
enum TscppError
{
BufferTooSmall = -1, ///< Buffer is too small for the given type
WrongType = -2, ///< While unserializing a different type was found
UnknownType = -3 ///< While unserializing the type was not found in the pool
};
/**
* Type pool for the TSCPP stream API.
* A type pool is a class where you can register types and associate callbacks
* to them. It is used to unserialize types when you don't know the exact type
* or order in which types have been serialized.
*/
class TypePool
class TypePoolStream
{
public:
/**
......@@ -80,11 +82,6 @@ public:
});
}
/**
* \internal
*/
int unserializeUnknownImpl(const char *name, const void *buffer, int bufSize) const;
/**
* \internal
*/
......@@ -106,94 +103,6 @@ private:
std::map<std::string,UnserializerImpl> types; ///< Registered types
};
/**
* \internal
*/
int serializeImpl(void *buffer, int bufSize, const char *name, const void *data, int size);
/**
* Serialize a type to a memory buffer
* \param buffer ponter to the memory buffer where to serialize the type
* \param bufSize buffer size
* \param t type to serialize
* \return the size of the serialized type (which is larger than sizeof(T) due
* to serialization overhead), or TscppError::BufferTooSmall if the given
* buffer is too small
*/
template<typename T>
int serialize(void *buffer, int bufSize, const T& t)
{
#ifndef _MIOSIX
static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable");
#endif
return serializeImpl(buffer,bufSize,typeid(t).name(),&t,sizeof(t));
}
/**
* \internal
*/
int unserializeImpl(const char *name, void *data, int size, const void *buffer, int bufSize);
/**
* Unserialize a known type from a memory buffer
* \param t type to unserialize
* \param buffer pointer to buffer where the serialized type is
* \param bufSize buffer size
* \return the size of the unserialized type (which is larger than sizeof(T) due
* to serialization overhead), or TscppError::WrongType if the buffer does
* not contain the given type or TscppError::BufferTooSmall if the type is
* truncated, i.e the buffer is smaller tah the serialized type size
*/
template<typename T>
int unserialize(T& t, const void *buffer, int bufSize)
{
#ifndef _MIOSIX
static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable");
#endif
return unserializeImpl(typeid(t).name(),&t,sizeof(t),buffer,bufSize);
}
/**
* Unserialize an unknown type from a memory buffer
* \param tp type pool where possible serialized types are registered
* \param buffer pointer to buffer where the serialized type is
* \param bufSize buffer size
* \return the size of the unserialized type (which is larger than sizeof(T) due
* to serialization overhead), or TscppError::UnknownType if the pool does
* not contain the type found in the buffer or TscppError::BufferTooSmall if the
* type is truncated, i.e the buffer is smaller tah the serialized type size
*/
int unserializeUnknown(const TypePool& tp, const void *buffer, int bufSize);
/**
* Given a buffer where a type has been serialized, return the C++ mangled
* name of the serialized type.
* It is useful when unserialize returns TscppError::WrongType to print an
* error message with the name of the type in the buffer
* \code
* Foo f;
* auto result=unserialize(f,buffer,size);
* if(result==WrongType)
* {
* cerr<<"While unserializing Foo, "<<demangle(peekTypeName(buffer,size))<<" was found\n";
* }
* \endcode
* \param buffer pointer to buffer where the serialized type is
* \param bufSize buffer size
* \return the serialized type name, or "" if the buffer does not contain a type
* name
*/
std::string peekTypeName(const void *buffer, int bufSize);
/**
* Demangle a C++ name. Useful for printing type names in error logs.
* This function may not be supported in all platforms, in this case it returns
* the the same string passed as a parameter.
* \param name name to demangle
* \return the demangled name
*/
std::string demangle(const std::string& name);
/**
* The output archive.
* This class allows to serialize objects to any ostream using the familiar
......@@ -297,7 +206,7 @@ public:
* Constructor
* \param os ostream where srialized types will be written
*/
UnknownInputArchive(std::istream& is, const TypePool& tp) : is(is), tp(tp) {}
UnknownInputArchive(std::istream& is, const TypePoolStream& tp) : is(is), tp(tp) {}
/**
* Unserialize one type from the input stream, calling the corresponding
......@@ -312,7 +221,7 @@ private:
UnknownInputArchive& operator=(const UnknownInputArchive&)=delete;
std::istream& is;
const TypePool& tp;
const TypePoolStream& tp;
};
/**
......@@ -357,4 +266,13 @@ private:
std::string n;
};
/**
* Demangle a C++ name. Useful for printing type names in error logs.
* This function may not be supported in all platforms, in this case it returns
* the the same string passed as a parameter.
* \param name name to demangle
* \return the demangled name
*/
std::string demangle(const std::string& name);
} // namespace tscpp
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment