Commit d7a28eda authored by Quentin Khan's avatar Quentin Khan

Clean up F(Mpi)Buffer{Writer,Reader} implementation

The classes FMpiBufferWriter and FBufferWriter implemented the same
functionality, FMpiBufferReader and FBufferReader did too. This replaces
the FBuffer implementations with the FMpiBuffer ones.

Steps taken:
  - remove FBuffer* implementation
  - rename FMpiBuffer* to FBuffer*
  - clean up code and documentation
  - fix missing includes

A few unused variables warnings are fixed in FAbstractBuffer.hpp.

There was a discrepancy in the getSize method of F(Mpi)BufferReader. The
behaviour of FMpiBufferReader is kept and the unit test is modified.
parent 06958c84
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
/** /**
* @brief The FAbstractBufferReader class defines what is an abstract buffer reader. * @brief The FAbstractBufferReader class defines what is an abstract buffer reader.
* The buffer used by the mpi algorithm for example should defines this methods. * The buffer used by the mpi algorithm for example should define this methods.
*/ */
class FAbstractBufferReader { class FAbstractBufferReader {
public: public:
...@@ -33,9 +33,9 @@ public: ...@@ -33,9 +33,9 @@ public:
virtual char* data() = 0; virtual char* data() = 0;
virtual const char* data() const = 0; virtual const char* data() const = 0;
virtual FSize getSize() const = 0; virtual FSize getSize() const = 0;
virtual void seek(const FSize inIndex) = 0; virtual void seek(const FSize inIndex) = 0;
virtual FSize tell() const = 0; virtual FSize tell() const = 0;
template <class ClassType> template <class ClassType>
ClassType getValue(){ ClassType getValue(){
...@@ -60,7 +60,7 @@ public: ...@@ -60,7 +60,7 @@ public:
/** /**
* @brief The FAbstractBufferWriter class defines what is an abstract buffer writer. * @brief The FAbstractBufferWriter class defines what is an abstract buffer writer.
* The buffer used by the mpi algorithm for example should defines this methods. * The buffer used by the mpi algorithm for example should define this methods.
*/ */
class FAbstractBufferWriter { class FAbstractBufferWriter {
public: public:
...@@ -69,19 +69,19 @@ public: ...@@ -69,19 +69,19 @@ public:
virtual char* data() = 0; virtual char* data() = 0;
virtual const char* data() const = 0; virtual const char* data() const = 0;
virtual FSize getSize() const = 0; virtual FSize getSize() const = 0;
virtual void reset() = 0; virtual void reset() = 0;
template <class ClassType> template <class ClassType>
void write(const ClassType& object){ void write(const ClassType& /*object*/){
static_assert(sizeof(ClassType) == 0, "Your Buffer should implement write."); static_assert(sizeof(ClassType) == 0, "Your Buffer should implement write.");
} }
template <class ClassType> template <class ClassType>
void writeAt(const FSize position, const ClassType& object){ void writeAt(const FSize /*position*/, const ClassType& /*object*/){
static_assert(sizeof(ClassType) == 0, "Your Buffer should implement writeAt."); static_assert(sizeof(ClassType) == 0, "Your Buffer should implement writeAt.");
} }
template <class ClassType> template <class ClassType>
void write(const ClassType* const objects, const FSize inSize){ void write(const ClassType* const /*objects*/, const FSize /*inSize*/){
static_assert(sizeof(ClassType) == 0, "Your Buffer should implement write."); static_assert(sizeof(ClassType) == 0, "Your Buffer should implement write.");
} }
template <class ClassType> template <class ClassType>
......
...@@ -20,98 +20,217 @@ ...@@ -20,98 +20,217 @@
#ifndef FBUFFERREADER_HPP #ifndef FBUFFERREADER_HPP
#define FBUFFERREADER_HPP #define FBUFFERREADER_HPP
#include "FVector.hpp" #include <memory>
#include "FAbstractBuffer.hpp" #include "FAbstractBuffer.hpp"
#include "../Utils/FAssert.hpp"
/** @author Berenger Bramas
* This class provide a fast way to manage a memory and convert /**
* the content to basic type. * \brief Provides memory management and conversion to basic type
* * \author Cyrille Piacibello, Berenger Bramas, Quentin Khan
* Specifie the needed space with reserve, then fill it with data *
* finaly read and convert. * This class is meant to ease data deserialisation through external libraries
*/ * such as MPI.
*
* - Reserve memory.
* - Pass the data() pointer the the library that will fill the buffer.
* - Read using the getValue, fillValue, fillArray methods.
*
* An internal index is kept to know what data has been read.
*/
class FBufferReader : public FAbstractBufferReader { class FBufferReader : public FAbstractBufferReader {
FVector<char> buffer; //< The memory buffer FSize arrayCapacity; ///< Allocated space
FSize index; //< The current index reading position std::unique_ptr<char[]> array; ///< Allocated array
FSize currentIndex; ///< First unread byte
public:
/** Construct with a memory size init to 0 */ public :
explicit FBufferReader(const FSize inCapacity = 0) : buffer(inCapacity), index(0) {
if(inCapacity){ /**
reserve(inCapacity); * \brief Construct the reader
*
* \param capacity Buffer capacity in bytes
*/
explicit FBufferReader(const FSize capacity = 512)
: arrayCapacity(capacity),
array(new char[capacity]),
currentIndex(0)
{
FAssertLF(array, "Cannot allocate array");
}
virtual ~FBufferReader() = default;
/**
* \brief Discard current buffer and allocate a new one if needed
*
* The read data index is reset.
*
* \param capacity New buffer capacity
*/
void cleanAndResize(const FSize capacity) {
if(capacity != arrayCapacity){
arrayCapacity = capacity;
array.reset(new char[capacity]);
} }
currentIndex = 0;
} }
/** Destructor */ /**
virtual ~FBufferReader() override { * \brief Reserve memory if needed, existing data is copied
*
* If the current capacity is enough, this operation is a no-op. Otherwise a
* new buffer is allocated and data is copied from the old buffer before
* discarding it.
*
* \param newCapacity Required minimal capacity
*/
void reserve(const FSize newCapacity) {
if(newCapacity > arrayCapacity) {
std::unique_ptr<char[]> new_array(new char[newCapacity]);
// The array is modified outside the class, we don't know what must
// be copied or not : we copy everything
std::copy(array.get(), array.get()+arrayCapacity, new_array.get());
array = std::move(new_array);
arrayCapacity = newCapacity;
}
} }
/** Get the memory area */
/**
* \brief Get pointer to allocated memory
*/
char* data() override { char* data() override {
return buffer.data(); return array.get();
} }
/** Get the memory area */ /**
const char* data() const override { * \brief Get pointer to allocated memory
return buffer.data(); */
const char* data() const override {
return array.get();
} }
/** Size of the meomry initialzed */ /**
* \brief Return already deserialised data size in bytes
*
* \return The size of the subpart of the buffer that has already been
* deserialised.
*/
FSize getSize() const override { FSize getSize() const override {
return buffer.getSize(); return currentIndex;
} }
/** Move the read index to a position */ /**
void seek(const FSize inIndex) override { * \brief Return the allocated buffer capacity in bytes
index = inIndex; */
FSize getCapacity() const{
return arrayCapacity;
} }
/** Get the read position */ /**
FSize tell() const override { * \brief Move the read index to a position
return index; *
* \param index Position from the beginning of the buffer to move the read
* index to.
*/
void seek(const FSize index) override {
FAssertLF(index <= arrayCapacity,
"FBufferReader :: can't move index because buffer isn't ",
"long enough ", index, " ", arrayCapacity);
currentIndex = index;
} }
/** Reset and allocate nbBytes memory filled with 0 */ /**
void reserve(const FSize nbBytes){ * \brief Get the read index value
reset(); */
buffer.set( 0, nbBytes); FSize tell() const override {
return currentIndex;
} }
/** Move the read index to 0 */ /**
void reset(){ * \brief Deserialise an object at read index
buffer.clear(); *
index = 0; * The object is byte copied into a new instance of T which is returned by
* value.
*
* The read index is incremented by sizeof(T).
*
* \tparam T Type of the object to deserialise
*/
template <class T>
T getValue(){
FAssertLF(currentIndex + FSize(sizeof(T)) <= arrayCapacity,
"The buffer does not have enough remaining memory to read a ",
" value of given type");
T value;
fillValue<T>(&value);
return value;
} }
/** Get a value with memory cast */ /**
template <class ClassType> * \brief Deserialise an object at a specified index
ClassType getValue(){ *
ClassType value = (*reinterpret_cast<ClassType*>(&buffer[index])); * The object is byte copied into a new instance of T which is returned by
index += FSize(sizeof(ClassType)); * value.
return value; *
* The read index is changed to `index + sizeof(T)`.
*
* \tparam T Type of the object to deserialise
*
* \param index Position of the object
*/
template <class T>
T getValue(const FSize index){
seek(index);
return getValue<T>();
} }
/** Fill a value with memory cast */ /**
template <class ClassType> * \brief Deserialise an object to given address
void fillValue(ClassType* const inValue){ *
(*inValue) = (*reinterpret_cast<ClassType*>(&buffer[index])); * Equivalent to `fillArray(ptr, 1)`.
index += FSize(sizeof(ClassType)); *
* \tparam T Type of the object to deserialise
*
* \param ptr Pointer to the object to copy to
*/
template <class T>
void fillValue(T* const ptr){
fillArray(ptr, 1);
} }
/** Fill one/many value(s) with memcpy */ /**
template <class ClassType> * \brief Deserialise contiguous values
void fillArray(ClassType* const inArray, const FSize inSize){ *
memcpy( inArray, &buffer[index], sizeof(ClassType) * inSize); * The objects are byte copied to the array pointed to by inArray.
index += FSize(sizeof(ClassType) * inSize); *
* The read index is incremented by `count * sizeof(T)`
*
* \tparam T Type of the object to deserialise
*
* \param inArray Array of objects to copy the values to
* \param count Object count in the array
*/
template <class T>
void fillArray(T* const inArray, const FSize count){
FAssertLF(currentIndex + FSize(sizeof(T))*count <= arrayCapacity );
memcpy(inArray, &array[currentIndex], sizeof(T)*count);
currentIndex += sizeof(T)*count;
} }
/** Same as fillValue */ /**
template <class ClassType> * \brief Stream-like deserialisation
FBufferReader& operator>>(ClassType& object){ *
* See fillArray.
*
* \tparam T Type of the object to deserialise
*
* \param object Object to deserialise to
*/
template <class T>
FBufferReader& operator>>(T& object){
fillValue(&object); fillValue(&object);
return *this; return *this;
} }
};
};
#endif // FBUFFERREADER_HPP #endif
...@@ -20,73 +20,168 @@ ...@@ -20,73 +20,168 @@
#ifndef FBUFFERWRITER_HPP #ifndef FBUFFERWRITER_HPP
#define FBUFFERWRITER_HPP #define FBUFFERWRITER_HPP
#include "FVector.hpp" #include <memory>
#include "FAbstractBuffer.hpp" #include "FAbstractBuffer.hpp"
#include "../Utils/FAssert.hpp"
/** @author Berenger Bramas /**
* This class provide a fast way to manage a memory and fill it * \brief Provides memory management and byte serialisation
* * \author Cyrille Piacibello, Berenger Bramas, Quentin Khan
* Put some data *
* then insert back if needed * This class is meant to ease data byte serialisation to pass through external
* finaly use data pointer as you like * libraries such as MPI.
*/ *
* - Reserve memory.
* - Insert objects.
* - Pass data() pointer to the library.
*
* An internal index is kept to know how much data has been written.
*/
class FBufferWriter : public FAbstractBufferWriter { class FBufferWriter : public FAbstractBufferWriter {
private: FSize arrayCapacity; ///< Allocated space
FVector<char> buffer; //< The buffer std::unique_ptr<char[]> array; ///< Allocated array
FSize currentIndex; ///< Currently filled space
/**
* \brief Ensure minimum remaining space in the buffer
*
* This methods checks whether there is at least min_rem_cap space left in
* the buffer. If not, a new buffer is allocated and the old data is copied
* into the new one.
*
* \param min_rem_cap Minimum remaining space in the buffer
*/
void expandIfNeeded(const FSize min_rem_cap) {
if( arrayCapacity < currentIndex + min_rem_cap){
arrayCapacity = FSize(double(currentIndex + min_rem_cap + 1) * 1.5);
char* arrayTmp = new char[arrayCapacity];
std::copy(array.get(), array.get()+currentIndex, arrayTmp);
array.reset(arrayTmp);
}
}
public: public:
/** Constructor with a default capacity of 512 bytes */
explicit FBufferWriter(const FSize inCapacity = 512) : buffer(inCapacity) { /**
* \brief Construct the writer
*
* \param capacity Buffer capacity in bytes
*/
explicit FBufferWriter(const FSize capacity = 1024)
: arrayCapacity(capacity),
array(new char[capacity]),
currentIndex(0)
{
FAssertLF(array, "Cannot allocate array");
}
virtual ~FBufferWriter() = default;
/**
* \brief Change buffer capacity and copy existing data to the new buffer
*
* If write index is bigger than new_capacity, the extra data is lost and
* the write index is changed accordingly.
*/
void resize(const FSize new_capacity){
if(new_capacity != arrayCapacity){
arrayCapacity = new_capacity;
char* arrayTmp = new char[arrayCapacity];
currentIndex = (currentIndex < arrayCapacity ? currentIndex : arrayCapacity-1);
std::copy(array.get(), array.get()+currentIndex, arrayTmp);
array.reset(arrayTmp);
}
}
/**
* \brief Reset the write index
*
* Subsequent writes will overwrite the buffer.
*/
void reset() override {
currentIndex = 0;
} }
/** Destructor */ /**
virtual ~FBufferWriter(){ * \brief Get pointer to allocated memory
*/
char* data() override {
return array.get();
} }
/** Get allocated memory pointer */ /**
char* data(){ * \brief Get pointer to allocated memory
return buffer.data(); */
const char* data() const override {
return array.get();
} }
/** Get allocated memory pointer */ /**
const char* data() const { * \brief Return the written memory size
return buffer.data(); */
FSize getSize() const override {
return currentIndex;
} }
/** Get the filled space */ /**
FSize getSize() const { * \brief Return the allocated buffer capacity in bytes
return buffer.getSize(); */
FSize getCapacity() const {
return arrayCapacity;
} }
/** Write data by mem cpy */ /**
* \brief Byte copy an object in the buffer
*
* The write index is increased by `sizeof(ClassType)`
*
* \tparam ClassType Type of the object to write
*
* \param object Object to write to the buffer
*/
template <class ClassType> template <class ClassType>
void write(const ClassType& object){ void write(const ClassType& object) {
buffer.memocopy(reinterpret_cast<const char*>(&object), FSize(sizeof(ClassType))); write(&object, 1);
} }
/** Write back, position + sizeof(object) has to be < size */ /**
* \brief Byte copy an object at given position in the buffer
*
* \tparam ClassType Type of the object to write
*
* \param object Object to write to the buffer
*/
template <class ClassType> template <class ClassType>
void writeAt(const FSize position, const ClassType& object){ void writeAt(const FSize position, const ClassType& object){
(*reinterpret_cast<ClassType*>(&buffer[position])) = object; FAssertLF(position+FSize(sizeof(ClassType)) <= currentIndex);
memcpy(&array[position], &object, sizeof(ClassType));
} }
/** Write an array */ /**
* \brief Byte copy contiguous objects to the buffer
*
* \tparam ClassType Type of the object to write
*
* \param objects Array of objects to write
* \param count Object count in the array
*/
template <class ClassType> template <class ClassType>
void write(const ClassType* const objects, const FSize inSize){ void write(const ClassType* const objects, const FSize inSize){
buffer.memocopy(reinterpret_cast<const char*>(objects), FSize(sizeof(ClassType)) * inSize); expandIfNeeded(sizeof(ClassType) * inSize);
memcpy(&array[currentIndex], objects, sizeof(ClassType)*inSize);
currentIndex += sizeof(ClassType)*inSize;
} }
/** Equivalent to write */ /**
* \brief Stream-like serialisation
*
* \tparam ClassType Type of the object to write
*/
template <class ClassType> template <class ClassType>
FBufferWriter& operator<<(const ClassType& object){ FBufferWriter& operator<<(const ClassType& object){
write(object); write(object);
return *this; return *this;
} }
/** Reset the writing index, but do not change the capacity */
void reset(){
buffer.clear();
}
}; };
......
// ===================================================================================
// Copyright ScalFmm 2016 INRIA, Olivier Coulaud, Bérenger Bramas,
// Matthias Messner olivier.coulaud@inria.fr, berenger.bramas@inria.fr
// This software is a computer program whose purpose is to compute the
// FMM.
//
// This software is governed by the CeCILL-C and LGPL licenses and
// abiding by the rules of distribution of free software.
// An extension to the license is given to allow static linking of scalfmm
// inside a proprietary application (no matter its license).
// See the main license file for more details.
//
// 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 and CeCILL-C Licenses for more details.
// "http://www.cecill.info".
// "http://www.gnu.org/licenses".
// ===================================================================================
#ifndef FMPIBUFFERREADER_HPP
#define FMPIBUFFERREADER_HPP
#include <memory>
#include "../Utils/FMpi.hpp"
#include "FAbstractBuffer.hpp"
#include "../Utils/FAssert.hpp"
/** @author Cyrille Piacibello, Berenger Bramas
* This class provide the same features as FBufferWriter
*
* Put some data
* then insert back if needed
* finally use data pointer as you like
*/
class FMpiBufferReader : public FAbstractBufferReader {
FSize arrayCapacity; //< Allocated space
std::unique_ptr<char[]> array; //< Allocated Array
FSize currentIndex;
public :
/*Constructor with a default arrayCapacity of 512 bytes */
explicit FMpiBufferReader(const FSize inDefaultCapacity = 512):
arrayCapacity(inDefaultCapacity),
array(new char[inDefaultCapacity]),
currentIndex(0){
FAssertLF(array, "Cannot allocate array");
}
/** To change the capacity (but reset the head to 0) */
void cleanAndResize(const FSize newCapacity){
if(newCapacity != arrayCapacity){
arrayCapacity = newCapacity;
array.reset(new char[newCapacity]);
}
currentIndex = 0;
}
/** Destructor
*/
virtual ~FMpiBufferReader(){
}
/** Get allocated memory pointer */
char* data() override {
return array.get();
}
/** Get allocated memory pointer */
const char* data() const override {
return array.get();
}
/** get the filled space */
FSize getSize() const override {
return currentIndex;
}
/** Size of the memory initialized */
FSize getCapacity() const{
return arrayCapacity;
}
/** Move the read index to a position */
void seek(const FSize inIndex) override {
FAssertLF(inIndex <= arrayCapacity, "FMpiBufferReader :: Aborting :: Can't move index because buffer isn't long enough ",inIndex," ",arrayCapacity);
currentIndex = inIndex;
}
/** Get the read position */
FSize tell() const override {
return currentIndex;
}
/** Get a value with memory cast */
template <class ClassType>
ClassType getValue(){
FAssertLF(currentIndex + FSize(sizeof(ClassType)) <= arrayCapacity );
ClassType value;
memcpy(&value, &array[currentIndex], sizeof(ClassType));
currentIndex += sizeof(ClassType);
return value;
}
/** Get a value with memory cast at a specified index */
template <class ClassType>
ClassType getValue(const FSize ind){
currentIndex = ind;
return getValue<ClassType>();
}
/** Fill a value with memory cast */
template <class ClassType>
void fillValue(ClassType* const inValue){
FAssertLF(currentIndex + FSize(sizeof(ClassType)) <= arrayCapacity );
memcpy(inValue, &array[currentIndex], sizeof(ClassType));
currentIndex += sizeof(ClassType);
}
/** Fill one/many value(s) with memcpy */
template <class ClassType>
void fillArray(ClassType* const inArray, const FSize inSize){
FAssertLF(currentIndex + FSize(sizeof(ClassType))*inSize <= arrayCapacity );
memcpy(inArray, &array[currentIndex], sizeof(ClassType)*inSize);
currentIndex += sizeof(ClassType)*inSize;
}
/** Same as fillValue */
template <class ClassType>
FMpiBufferReader& operator>>(ClassType& object){
fillValue(&object);
return *this;
}
};
#endif
// ===================================================================================
// Copyright ScalFmm 2016 INRIA, Olivier Coulaud, Bérenger Bramas,
// Matthias Messner olivier.coulaud@inria.fr, berenger.bramas@inria.fr
// This software is a computer program whose purpose is to compute the