This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,7 @@
OV_SET_CUSTOM_DOCUMENTATION("ebml" "EBML parser and formater library" "src" "include")
# Add all the subdirs as projects of the named branch
OV_ADD_PROJECTS("MODULES")
# Sort target into directories for better visualization in IDE
SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON)
SET(MODULES_FOLDER Modules)
@@ -0,0 +1,52 @@
PROJECT(openvibe-module-communication)
INCLUDE_DIRECTORIES("include/communication/")
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp include/*.h include/*.hpp)
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DCommunication_Shared -DCommunication_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DCommunication_Static -DCommunication_Exports")
IF(UNIX)
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF(UNIX)
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEModuleSocket")
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR})
# Link the socket library to the static version of lib-communication
SET(PROJECT_NAME ${PROJECT_NAME}-static)
SET(DYNAMIC_LINK_OPENVIBE_SDK false)
INCLUDE("FindOpenViBEModuleSocket")
#add_subdirectory(test)
@@ -0,0 +1,25 @@
#pragma once
#include <ov_common_defines.h>
#if defined Communication_Shared
# if defined TARGET_OS_Windows
# define Communication_API_Export __declspec(dllexport)
# define Communication_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
# define Communication_API_Export __attribute__((visibility("default")))
# define Communication_API_Import __attribute__((visibility("default")))
# else
# define Communication_API_Export
# define Communication_API_Import
# endif
#else
# define Communication_API_Export
# define Communication_API_Import
#endif
#if defined Communication_Exports
# define Communication_API Communication_API_Export
#else
# define Communication_API Communication_API_Import
#endif
@@ -0,0 +1,323 @@
#pragma once
#include <socket/IConnection.h>
#include "defines.h"
#include "ovCMessagingProtocol.h"
namespace Communication {
/**
* \brief The purpose of this class is to provide a communication protocol to exchange EBML between a client and a server.
*/
class Communication_API CMessaging
{
public:
/**
* \brief Library error codes
*/
enum ELibraryError
{
NoError = 0,
Socket_NotConnected = 1,
Socket_FailedToConnect = 2,
Socket_ReceiveBufferFail = 3,
Socket_SendBufferFail = 4,
Socket_NoIncomingClientConnection = 6,
Socket_NotReadyToSend = 7,
Socket_NoDataReceived = 8,
Socket_FailedCloseClientConnection = 10,
Socket_FailedToCloseConnection = 11,
Socket_FailedConnectClient = 12,
Socket_ClientAlreadyConnected = 13,
Deserialize_BufferTooSmall = 30,
Deserialize_Header = 31,
Deserialize_ProtocolVersionMessage = 32,
Deserialize_BoxDescriptionMessage = 33,
Deserialize_EBMLMessage = 34,
Deserialize_EndMessage = 35,
Deserialize_ErrorMessage = 36,
Deserialize_LogMessage = 37,
Deserialize_AuthenticationMessage = 38,
Deserialize_MessageTypeNotSupported = 39,
BoxDescriptionAlreadyReceived = 60,
BoxDescriptionNotReceived = 61,
BadAuthenticationReceived = 70,
NoAuthenticationReceived = 71,
ThreadJoinFailed = 80
};
CMessaging();
virtual ~CMessaging();
/**
* \brief Get the code of the last error produced by the API
*
* \retval Error code
*/
ELibraryError getLastError() const;
/**
* \brief Give a short description of an error.
*
* \param error the error code
*
* \return Description of the error.
*/
static std::string getErrorString(ELibraryError error);
/**
* \brief Check that the socket is connected.
*
* \retval True if the socket is connected.
* \retval False if the socket is not connected.
*/
bool isConnected() const;
/**
* \brief Check that the synchronization is in error state.
*
* \retval True if the synchronization is in error state.
* \retval False if the synchronization is ok.
*/
bool isInErrorState() const;
/**
* \brief Set the connection ID to a new value
* \param connectionID The connection Id to set
*/
void setConnectionID(const std::string& connectionID) const;
/**
* \brief Check that a End message was received.
*
* \retval True if the end message from the client is received.
* \retval False if the end message from the client is not received.
*/
virtual bool isEndReceived();
/**
* \brief Get the time.
*/
virtual uint64_t getTime();
protected:
/**
* \brief Push a message to the send buffer.
* The message will be really sent in the socket in the next synchronization.
*
* \param message The message to send.
*
* \retval True if it succeeds.
* \retval False if library is in error state.
*/
bool pushMessage(const Message& message) const;
/**
* \brief Set the last error code.
*
* \param libraryError The error
*
* \sa getLastError
*/
void setLastError(ELibraryError libraryError) const;
/**
* \brief Provide the connection to the base class to communicate.
*
* \param connection The connection
*/
void setConnection(Socket::IConnection* connection) const;
/**
* \brief Start a thread that will push the outgoing data, pull and process the incoming data.
* This sync will be stopped in cases:
* - An error raised
* - stopSyncing() function was called.
*
* \retval True if it succeeds.
* \retval False if an error occured.
*
* \sa stopSyncing
*/
bool startSyncing();
/**
* \brief Request to stop the sync and stop the thread.
*
* \retval True if it succeeds.
* \retval False if an error occured.
*
* \sa startSyncing
*/
bool stopSyncing() const;
/**
* @brief Get the oldest authentication message
* @param id[out] Identifier of the message
* @param connectionID[out] Connection Id
* @return true if a message was popped, false if the queue is empty
*/
virtual bool popAuthentication(uint64_t& id, std::string& connectionID);
/**
* @brief Get the oldest box description message
* @param id[out] Identifier of the message
* @param boxDescription[out] Descriptor of the box
* @return true if a message was popped, false if the queue is empty
*/
virtual bool popBoxDescriptions(uint64_t& id, BoxDescriptionMessage& boxDescription);
/**
* @brief popCommunicationProtocolVersion
* @param id[out] Identifier of the message
* @param majorVersion[out] major version of the protocol
* @param minorVersion[out] minor version of the protocol
* @return true if a message was popped, false if the queue is empty
*/
virtual bool popCommunicationProtocolVersion(uint64_t& id, uint8_t& majorVersion, uint8_t& minorVersion);
/**
* @brief Pop the oldest EBML message from the queue
* @param id[out] Identifier of the message
* @param index[out] Input index to which the EBML should be directed
* @param startTime[out] Start time of the buffer
* @param endTime[out] End time of the buffer
* @param ebml[out] The encoded EBML buffer
* @return true if a message was popped, false if the queue is empty
*/
virtual bool popEBML(uint64_t& id, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml);
/**
* @brief Pop the oldest log message from the queue
* @param id[out] Identifier of the message
* @param type[out] Log level of the message
* @param message[out] Message text
* @return true if a message was popped, false if the queue is empty
*/
virtual bool popLog(uint64_t& id, ELogLevel& type, std::string& message);
/**
* @brief Pop the oldest error message from the queue
* @param id[out] Identifier of the message
* @param type[out] Error code
* @param guiltyId[out] If of the sent message that caused this error
* @return true if a message was popped, false if the queue is empty
*/
virtual bool popError(uint64_t& id, EError& type, uint64_t& guiltyId);
/**
* @brief Get the oldes End message
* @param id[out] Identifier of the message
* @return true if a message was popped, false if the queue is empty
*/
virtual bool popEnd(uint64_t& id);
/**
* @brief Reset the library state. Stop sending and receiving buffers, disconnect
* and empty all buffers.
*/
void reset() const;
/**
* \brief Checks if a sync message was received and reset it if it was.
*
* This method should be used in a busy loop to check if the other
* party has finished processing all of the data.
*
* From the external program standpoint this method will return true
* when the box has finished sending all of the data that has to be
* processed in one bulk.
*
* From the box standpoint, this means that the external program has
* finished processing and sending all of the data that the box has
* sent.
*
* \retval True if a sync message is received.
* \retval False if no sync message was received.
*/
virtual bool waitForSyncMessage();
private:
/**
* \brief Receive all the available data from the socket and insert it in the rcv buffer.
*
* \retval True if it succeeds.
* \retval False if an error occured.
* Library errors: TODO
*
* \sa push
*/
bool pull() const;
/**
* \brief Send all the data to the socket.
*
* \retval True if it succeeds.
* \retval False if an error occured.
* Library errors:
* - Socket_NotConnected
*
*
* \sa push
*/
bool push() const;
/**
* \brief Process incoming data, unpack it and put messages in queues.
* It use processBuffer.
*
* \retval True if it succeeds.
* \retval False if an error occured.
* Library errors: Error from processBuffer
*
* \sa processBuffer
*/
bool processIncomingMessages() const;
/**
* \brief Process buffer, pack it and put messages in queues.
*
* \param buffer Buffer with the incoming serialized data.
* \param[out] byteRead Number of bytes read and processed.
*
* \retval True if it succeeds.
* \retval False if an error occured.
* Library errors:
* - Deserialize_Header
* - Deserialize_AuthenticationMessage
* - Deserialize_ProtocolVersionMessage
* - Deserialize_BoxDescriptionMessage
* - Deserialize_EBMLMessage
* - Deserialize_LogMessage
* - Deserialize_ErrorMessage
* - Deserialize_MessageTypeNotSupported
*
* \sa processIncomingMessages
*/
bool processBuffer(const std::vector<uint8_t>& buffer, size_t& byteRead) const;
/**
* \brief Sync fucntion that is used in a thread to pull, push and process the incoming data.
*
* \retval True if it succeeds.
* \retval False if an error occured.
* Library errors: Errors from processIncomingMessages(), push() or pull()
*
* \sa pull
* \sa push
* \sa processIncomingMessages
*/
void sync() const;
public:
static const uint8_t s_CommunicationProtocol_MajorVersion = 1;
static const uint8_t s_CommunicationProtocol_MinorVersion = 1;
protected:
struct SMessagingImpl;
SMessagingImpl* impl = nullptr;
};
} // namespace Communication
@@ -0,0 +1,218 @@
#pragma once
#include "defines.h"
#include "ovCMessaging.h"
#include "socket/IConnectionClient.h"
namespace Communication {
class Communication_API MessagingClient : public CMessaging
{
public:
/**
* \brief Default constructor.
*/
MessagingClient();
/**
* \brief Destructor.
*/
~MessagingClient() override;
/**
* \brief Connect to a server.
*
* \param uri URI of the server.
* \param port The port.
*
* \retval True if it succeeds.
* \retval False there is no available error.
* Library errors:
* - FailedToConnect
* - NotConnected
* - NotReadyToSend
*
* \sa close
*/
bool connect(const std::string& uri, const size_t port);
/**
* \brief Closes the connection to the server.
*
* \retval True if it succeeds.
* \retval False there is no available error.
* Library errors:
* - NotConnected
* - FailedToCloseConnection
*
* \sa getLastError
*/
bool close() const;
/**
* \brief Return the number of box information's parameters available.
*
* \return The number of parameters available.
*
* \sa getParameter
*/
size_t getParameterCount() const;
/**
* \brief Return the number of box information's input available.
*
* \return The number of input available.
*
* \sa getInput
*/
size_t getInputCount() const;
/**
* \brief Return the number of output of the box information, available.
*
* \return The number of output available.
*
* \sa getOutput
*/
size_t getOutputCount() const;
/**
* \brief Get parameter information.
*
* \param i The index.
* \param[out] id The id
* \param[out] type The type (corresponding to the OpenViBE CIdentifier)
* \param[out] name Name
* \param[out] value Value (to convert accordinf to the type)
*
* \retval True if it succeeds.
* \retval False if the index is out of range.
*/
bool getParameter(size_t i, uint64_t& id, uint64_t& type, std::string& name, std::string& value) const;
/**
* \brief Get input information.
*
* \param i The index.
* \param[out] id The id
* \param[out] type The type (corresponding to the OpenViBE CIdentifier)
* \param[out] name Name
*
* \retval True if it succeeds.
* \retval False if the index is out of range.
*
* \sa getInputCount
*/
bool getInput(size_t i, uint64_t& id, uint64_t& type, std::string& name) const;
/**
* \brief Get input information.
*
* \param i The index.
* \param[out] id The id
* \param[out] type The type (corresponding to the OpenViBE CIdentifier)
* \param[out] name Name
*
* \retval True if it succeeds.
* \retval False if the index is out of range.
*
* \sa getOutputCount
*/
bool getOutput(size_t i, uint64_t& id, uint64_t& type, std::string& name) const;
/**
* \brief Get the oldest error message, if available.
*
* \param packetId
* \param[out] type The error's type.
* \param[out] guiltyId Identifier of the guilty message
*
* \retval True if it succeeds.
* \retval False there is no available error.
*/
bool popError(uint64_t& packetId, EError& type, uint64_t& guiltyId) override;
/**
* \brief Get the oldest EBML message, if available.
*
* \param packetId
* \param[out] index Box input index.
* \param[out] startTime The start time.
* \param[out] endTime The end time.
* \param[out] ebml The EBML vector.
*
* \retval True if it succeeds.
* \retval False there is no available error.
*/
bool popEBML(uint64_t& packetId, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml) override;
/**
* \brief Push a log message to the server.
*
* \param logLevel The log level.
* \param log The log string message.
*
* \retval True if it succeeds.
* \retval False there is no available error.
* Library errors:
* - NotConnected
* - NotReadyToSend
*
* \sa getLastError
*/
bool pushLog(ELogLevel logLevel, const std::string& log) const;
/**
* \brief Pushes an ebml.
*
* \param index Index of the box output.
* \param startTime The start time.
* \param endTime The endtime.
* \param ebml The ebml vector.
*
* \retval True if it succeeds.
* \retval False there is no available error.
* Library errors:
* - NotConnected
* - NotReadyToSend
*
* \sa getLastError
*/
bool pushEBML(size_t index, uint64_t startTime, uint64_t endTime, const std::shared_ptr<const std::vector<uint8_t>>& ebml) const;
/**
* \brief Push Sync message to the server.
*
* \retval True if it succeeds.
* \retval False if the library is in error state.
*/
bool pushSync() const;
/**
* \brief Check if a sync message is received.
*
* \retval True if a sync message is received.
* \retval False if no sync message was received.
*/
bool waitForSyncMessage() override;
private:
/**
* \brief Pushes an authentication message).
*
* \param connectionID The connection identifier.
*
* \retval True if it succeeds.
* \retval False there is no available error.
* Library errors:
* - NotConnected
* - NotReadyToSend
*/
bool pushAuthentication(const std::string& connectionID) const;
Socket::IConnectionClient* m_Client = nullptr;
bool m_BoxDescriptionReceived = false;
};
} // namespace Communication
@@ -0,0 +1,424 @@
#pragma once
#include "defines.h"
#include <cstdint>
#include <vector>
#include <memory>
#include <string>
#include <limits>
namespace Communication {
/**
* \brief Log level used by the client to log information in the server side
*/
enum ELogLevel : uint8_t
{
LogLevel_Info = 1,
LogLevel_Warning = 2,
LogLevel_Error = 3,
LogLevel_Fatal = 4,
LogLevel_Max = 5,
LogLevel_Unknown = 0xFF
};
/**
* \brief Values that represent errors.
*/
enum EError : uint16_t
{
Error_AuthenticationFail = 1,
Error_AuthenticationRequested = 2,
Error_InvalidOutputIndex = 3,
Error_BadCommunicationProtocol = 4,
Error_InvalidEBML = 5,
Error_InvalidLogLevel = 6,
Error_InvalidMessageType = 7,
Error_BadMessage = 8,
Error_Max = 9,
Error_Unknown = 0xFF
};
/**
* \brief Message type
*/
enum EMessageType : uint8_t
{
MessageType_Authentication = 0,
MessageType_ProtocolVersion = 1,
MessageType_BoxInformation = 2,
MessageType_EBML = 3,
MessageType_Log = 4,
MessageType_End = 5,
MessageType_Error = 6,
MessageType_Time = 7,
MessageType_Sync = 8,
MessageType_Max = 9,
MessageType_Unknown = 0xFF,
};
/**
* \brief A packet part is a part of a packet compound by an Header and a Message.
*/
class Communication_API CPacketPart
{
public:
virtual ~CPacketPart() {}
/**
* \brief Provide array of bytes that represent the object.
*
* \return A vector with the serialized information of the message that respect the communication protocol.
*
* \sa fromBytes
*/
virtual std::vector<uint8_t> toBytes() const = 0;
/**
* \brief Transform bytes vector into an object.
*
* \param buffer The buffer.
* \param [in,out] bufferIndex Zero-based index of the buffer.
*
* \retval True if it succeeds
* \retval False if it fails.
*
* \sa toBytes
*/
virtual bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) = 0;
/**
* \brief Query if this object is valid.
*
* \retval True if it is valid.
* \retval False if it is invalid.
*/
bool isValid() const { return m_isValid; }
protected:
bool m_isValid = false;
};
/**
* \brief A message is the second part of a packet after the header.
*/
class Message : public CPacketPart
{
public:
virtual EMessageType getMessageType() const = 0;
};
/**
* \brief A header is associated to a message. It give information about the message, like the type and the size.
*/
class Header final : CPacketPart
{
public:
Header();
Header(EMessageType type, uint64_t id, size_t size);
std::vector<uint8_t> toBytes() const override;
void setId(const uint64_t id) { m_id = id; }
uint64_t getId() const { return m_id; }
EMessageType getType() const { return m_type; }
size_t getSize() const { return m_size; }
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
private:
EMessageType m_type;
uint64_t m_id = 0;
size_t m_size = 0;
static const size_t TYPE_SIZE = sizeof(EMessageType);
static const size_t ID_SIZE = sizeof(uint64_t);
static const size_t SIZE_SIZE = sizeof(size_t);
static const size_t TYPE_INDEX = 0;
static const size_t ID_INDEX = TYPE_INDEX + TYPE_SIZE;
static const size_t SIZE_INDEX = ID_INDEX + ID_SIZE;
static const size_t MINIMUM_SIZE = TYPE_SIZE + ID_SIZE + SIZE_SIZE;
};
/**
* \brief Represent an Authentication message.
*/
class AuthenticationMessage final : public Message
{
public:
AuthenticationMessage() { m_isValid = false; }
AuthenticationMessage(const std::string& connectionID) : m_connectionID(connectionID) { m_isValid = true; }
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
EMessageType getMessageType() const override { return MessageType_Authentication; }
std::string getConnectionID() const { return m_connectionID; }
private:
static const size_t SIZE_SIZE = sizeof(size_t);
static const size_t SIZE_INDEX = 0;
static const size_t CONNECTION_ID_INDEX = SIZE_INDEX + SIZE_SIZE;
static const size_t MINIMUM_SIZE = SIZE_SIZE;
std::string m_connectionID;
};
/**
* \brief This message is used to inform the server or the client about the current communication protocol version used.
*/
class CommunicationProtocolVersionMessage final : public Message
{
public:
CommunicationProtocolVersionMessage() { m_isValid = false; }
CommunicationProtocolVersionMessage(const uint8_t majorVersion, const uint8_t minorVersion) : m_minorVersion(minorVersion), m_majorVersion(majorVersion)
{
m_isValid = true;
}
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
EMessageType getMessageType() const override { return MessageType_ProtocolVersion; }
uint8_t getMajorVersion() const { return m_majorVersion; }
uint8_t getMinorVersion() const { return m_minorVersion; }
private:
uint8_t m_minorVersion = 0;
uint8_t m_majorVersion = 0;
static const size_t MAJOR_SIZE = sizeof(uint8_t);
static const size_t MINOR_SIZE = sizeof(uint8_t);
static const size_t MAJOR_INDEX = 0;
static const size_t MINOR_INDEX = MAJOR_INDEX + MAJOR_SIZE;
static const size_t MINIMUM_SIZE = MAJOR_SIZE + MINOR_SIZE;
};
/**
* \brief InputOutput class describes the input or output of a box.
*/
class InputOutput final : public CPacketPart
{
public:
InputOutput();
InputOutput(uint64_t id, size_t type, const std::string& name);
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
uint64_t getId() const { return m_id; }
size_t getType() const { return m_type; }
std::string getName() const { return m_name; }
private:
uint64_t m_id = 0;
size_t m_type = 0;
std::string m_name;
static const size_t ID_SIZE = sizeof(uint64_t);
static const size_t TYPE_SIZE = sizeof(size_t);
static const size_t NAME_SIZE_SIZE = sizeof(size_t);
static const size_t ID_INDEX = 0;
static const size_t TYPE_INDEX = ID_INDEX + ID_SIZE;
static const size_t NAME_SIZE_INDEX = TYPE_INDEX + TYPE_SIZE;
static const size_t NAME_INDEX = NAME_SIZE_INDEX + NAME_SIZE_SIZE;
static const size_t MINIMUM_SIZE = ID_SIZE + TYPE_SIZE + NAME_SIZE_SIZE;
};
class Parameter final : public CPacketPart
{
public:
Parameter();
Parameter(uint64_t id, size_t type, const std::string& name, const std::string& value);
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
uint64_t getId() const { return m_id; }
size_t getType() const { return m_type; }
std::string getName() const { return m_name; }
std::string getValue() const { return m_value; }
private:
uint64_t m_id = 0;
size_t m_type = 0;
std::string m_name;
std::string m_value;
static const size_t ID_SIZE = sizeof(uint64_t);
static const size_t TYPE_SIZE = sizeof(size_t);
static const size_t NAME_SIZE_SIZE = sizeof(size_t);
static const size_t VALUE_SIZE_SIZE = sizeof(size_t);
static const size_t ID_INDEX = 0;
static const size_t TYPE_INDEX = ID_INDEX + ID_SIZE;
static const size_t NAME_SIZE_INDEX = TYPE_INDEX + TYPE_SIZE;
static const size_t VALUE_SIZE_INDEX = NAME_SIZE_INDEX + NAME_SIZE_SIZE;
static const size_t NAME_INDEX = VALUE_SIZE_INDEX + VALUE_SIZE_SIZE;
static const size_t MINIMUM_SIZE = ID_SIZE + TYPE_SIZE + NAME_SIZE_SIZE + VALUE_SIZE_SIZE;
};
/**
* \brief This message contains information about the box used in the NeuroRT pipeline.
* This message is sent by the server and received by the client.
*/
class BoxDescriptionMessage final : public Message
{
public:
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
EMessageType getMessageType() const override;
bool addInput(uint64_t id, size_t type, const std::string& name);
bool addOutput(uint64_t id, size_t type, const std::string& name);
bool addParameter(uint64_t id, size_t type, const std::string& name, const std::string& value);
const std::vector<InputOutput>* getInputs() const { return &m_inputs; }
const std::vector<InputOutput>* getOutputs() const { return &m_outputs; }
const std::vector<Parameter>* getParameters() const { return &m_parameters; }
private:
std::vector<InputOutput> m_inputs;
std::vector<InputOutput> m_outputs;
std::vector<Parameter> m_parameters;
static const size_t N_INPUT_SIZE = sizeof(size_t);
static const size_t N_OUTPUT_SIZE = sizeof(size_t);
static const size_t N_PARAMETER_SIZE = sizeof(size_t);
static const size_t N_INPUT_INDEX = 0;
static const size_t N_OUTPUT_INDEX = N_INPUT_INDEX + N_INPUT_SIZE;
static const size_t N_PARAMETER_INDEX = N_OUTPUT_INDEX + N_OUTPUT_SIZE;
static const size_t MINIMUM_SIZE = N_INPUT_SIZE + N_OUTPUT_SIZE + N_PARAMETER_SIZE;
};
/**
* \brief Log message is a way to communicate information from the client to the server.
*/
class LogMessage final : public Message
{
public:
LogMessage();
LogMessage(ELogLevel type, const std::string& message);
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
EMessageType getMessageType() const override { return MessageType_Log; }
ELogLevel getType() const { return m_type; }
std::string getMessage() const { return m_message; }
private:
ELogLevel m_type;
std::string m_message;
static const size_t TYPE_SIZE = sizeof(ELogLevel);
static const size_t SIZE_SIZE = sizeof(size_t);
static const size_t TYPE_INDEX = 0;
static const size_t SIZE_INDEX = TYPE_INDEX + TYPE_SIZE;
static const size_t NAME_INDEX = SIZE_INDEX + SIZE_SIZE;
static const size_t MINIMUM_SIZE = TYPE_SIZE + SIZE_SIZE;
};
/**
* \brief EBML message is used to send EBML data.
*/
class EBMLMessage final : public Message
{
public:
EBMLMessage();
EBMLMessage(size_t index, uint64_t startTime, uint64_t endTime, const std::shared_ptr<const std::vector<uint8_t>>& ebml);
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
EMessageType getMessageType() const override { return MessageType_EBML; }
size_t getIndex() const { return m_ioIdx; }
uint64_t getStartTime() const { return m_startTime; }
uint64_t getEndTime() const { return m_endTime; }
std::shared_ptr<const std::vector<uint8_t>> getEBML() const { return m_EBML; }
private:
size_t m_ioIdx = 0;
uint64_t m_startTime = 0;
uint64_t m_endTime = 0;
std::shared_ptr<const std::vector<uint8_t>> m_EBML;
static const size_t IO_INDEX_SIZE = sizeof(size_t);
static const size_t START_TIME_SIZE = sizeof(uint64_t);
static const size_t END_TIME_SIZE = sizeof(uint64_t);
static const size_t SIZE_SIZE = sizeof(size_t);
static const size_t IO_INDEX_INDEX = 0;
static const size_t START_TIME_INDEX = IO_INDEX_INDEX + IO_INDEX_SIZE;
static const size_t END_TIME_INDEX = START_TIME_INDEX + START_TIME_SIZE;
static const size_t SIZE_INDEX = END_TIME_INDEX + END_TIME_SIZE;
static const size_t EBML_INDEX = SIZE_INDEX + SIZE_SIZE;
static const size_t MINIMUM_SIZE = IO_INDEX_SIZE + START_TIME_SIZE + END_TIME_SIZE + SIZE_SIZE;
};
/**
* \brief Error message is used to alert the client that the server raise an error due to a message by the client.
*/
class ErrorMessage final : public Message
{
public:
ErrorMessage() : m_type(Error_Unknown), m_guiltyId(std::numeric_limits<decltype(m_guiltyId)>::max()) {}
ErrorMessage(const EError error, const uint64_t guiltyId) : m_type(error), m_guiltyId(guiltyId) {}
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
EMessageType getMessageType() const override { return MessageType_Error; }
EError getType() const { return m_type; }
uint64_t getGuiltyId() const { return m_guiltyId; }
private:
EError m_type;
uint64_t m_guiltyId = 0;
static const size_t TYPE_SIZE = sizeof(EError);
static const size_t GUILTY_ID_SIZE = sizeof(uint64_t);
static const size_t TYPE_INDEX = 0;
static const size_t GUILTY_ID_INDEX = TYPE_INDEX + TYPE_SIZE;
static const size_t MINIMUM_SIZE = GUILTY_ID_SIZE + TYPE_SIZE;
};
/**
* \brief End message can be sent by the server or/and by the client to inform that the processing is stopped and the connection will be closed.
*/
class EndMessage final : public Message
{
public:
EndMessage() {}
std::vector<uint8_t> toBytes() const override { return std::vector<uint8_t>(); }
bool fromBytes(const std::vector<uint8_t>& /*buffer*/, size_t& /*index*/) override { return false; }
EMessageType getMessageType() const override { return MessageType_End; }
};
/**
* \brief Time messages are only sent by the server to inform the client about the current time of the pipeline.
*/
class TimeMessage final : public Message
{
public:
TimeMessage(const uint64_t time = 0) : m_time(time) {}
std::vector<uint8_t> toBytes() const override;
bool fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex) override;
EMessageType getMessageType() const override { return MessageType_Time; }
uint64_t getTime() const { return m_time; }
private:
uint64_t m_time = 0;
static const size_t TIME_SIZE = sizeof(uint64_t);
static const size_t TIME_INDEX = 0;
static const size_t MINIMUM_SIZE = TIME_SIZE;
};
/**
* \brief Sync message can be sent by the server or/and by the client to inform that it's waiting the other part.
*/
class SyncMessage final : public Message
{
public:
SyncMessage() {}
std::vector<uint8_t> toBytes() const override { return std::vector<uint8_t>(); }
bool fromBytes(const std::vector<uint8_t>& /*buffer*/, size_t& /*index*/) override { return false; }
EMessageType getMessageType() const override { return MessageType_Sync; }
};
} // namespace Communication
@@ -0,0 +1,185 @@
#pragma once
#include "defines.h"
#include "ovCMessaging.h"
#include "socket/IConnectionServer.h"
namespace Communication {
class Communication_API MessagingServer : public CMessaging
{
public:
/**
* \brief Default constructor.
*/
MessagingServer() : CMessaging(), m_Server(Socket::createConnectionServer()) {}
/**
* \brief Destructor.
*/
~MessagingServer() override;
/**
* \brief Start listening on the given port.
*
* \param port The port.
*
* \retval True if listenning
* \retval False if not.
*
* \sa close
*/
bool listen(const size_t port) const { return m_Server->listen(port); }
/**
* \brief Close the connection
* *
* \retval True if it succeeds
* \retval False if it fails.
*/
bool close();
/**
* \brief Accepts one incoming connection.
*
* \retval True if it succeeds.
* \retval False if it fails.
*/
bool accept();
/**
* \brief Returns the port on the one the server is listening.
* This is useful if you set the port to '0'.
* \param port [out]: port on the one the server is listening
*/
bool getSocketPort(size_t& port) const { return m_Server->getSocketPort(port); }
/**
* \brief Adds a parameter in the Box Information.
*
* \param id The parameter's identifier.
* \param type The parameter's type. Use OpenViBE::CIdentifier.
* \param name The parameter's name.
* \param value The parameter's value. A string.
*
* \retval True if it succeeds.
* \retval False if an output with the given id already exists.
*
* \sa addInput
* \sa addOutput
*/
bool addParameter(uint64_t id, size_t type, const std::string& name, const std::string& value) const;
/**
* \brief Adds an input in the box Information.
*
* \param id The input's identifier.
* \param type The input's type. Use OpenViBE::CIdentifier.
* \param name The input's name.
*
* \retval True if it succeeds.
* \retval False if an output with the given id already exists.
*
* \sa addParameter
* \sa addOutput
*/
bool addInput(uint64_t id, size_t type, const std::string& name) const;
/**
* \brief Adds an output in the box information.
*
* \param id The output's identifier.
* \param type The output's type. Use OpenViBE::CIdentifier.
* \param name The output's name.
*
* \retval True if it succeeds.
* \retval False if an output with the given id already exists.
*
* \sa addParameter
* \sa addInput
*/
bool addOutput(uint64_t id, size_t type, const std::string& name) const;
/**
* \brief Get the log message received from the client.
*
* \param[out] packetId Packet id
* \param[out] type Level of the log
* \param[out] message Log message
*
* \retval True if it succeeds.
* \retval False if the library is in error state.
*/
bool popLog(uint64_t& packetId, ELogLevel& type, std::string& message) override;
/**
* \brief Get the EBML data received from the client.
*
* \param[out] packetId Packet id
* \param[out] index Output index
* \param[out] startTime Chunk time in OpenViBE 32:32 format
* \param[out] endTime Chunk time in OpenViBE 32:32 format
* \param[out] ebml EBML data
*
* \retval True if it succeeds.
* \retval False if the library is in error state.
*/
bool popEBML(uint64_t& packetId, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml) override;
/**
* \brief Push Error message to the client
*
* \param error Error code
* \param guiltyId Id of the received message that raised the error.
*
* \retval True if it succeeds.
* \retval False if the library is in error state.
*/
bool pushError(EError error, uint64_t guiltyId) const;
/**
* \brief Push EBML message to the client
*
* \param index Index of the input.
* \param startTime Chunk time in OpenViBE 32:32 format
* \param endTime Chunk time in OpenViBE 32:32 format
* \param ebml EBML data
*
* \retval True if it succeeds.
* \retval False if the library is in error state.
*/
bool pushEBML(size_t index, uint64_t startTime, uint64_t endTime, const std::shared_ptr<const std::vector<uint8_t>>& ebml) const;
/**
* \brief Push Time message to the client
*
* \param time Time in OpenViBE 32:32 format
*
* \retval True if it succeeds.
* \retval False if the library is in error state.
*/
bool pushTime(uint64_t time) { return this->pushMessage(TimeMessage(time)); }
/**
* \brief Push Sync message to the client
*
* \retval True if it succeeds.
* \retval False if the library is in error state.
*/
bool pushSync() { return this->pushMessage(SyncMessage()); }
/**
* \brief Check if a sync message is received.
*
* \retval True if a sync message is received.
* \retval False if no sync message was received.
*/
bool waitForSyncMessage() override { return CMessaging::waitForSyncMessage(); }
private:
Socket::IConnectionServer* m_Server = nullptr; //< Server connection
Socket::IConnection* m_Client = nullptr;
};
} // namespace Communication
@@ -0,0 +1,494 @@
#include <vector>
#include <array>
#include <string>
#include <cstdint>
#include <thread>
#include <mutex>
#include <memory>
#include <map>
#include "ovCMessaging.h"
#include "ovCMessagingImpl.hpp"
namespace Communication {
static const std::map<CMessaging::ELibraryError, std::string> ERRORS_STRING =
{
{ CMessaging::NoError, "No error" },
{ CMessaging::Socket_NotConnected, "Not connected" },
{ CMessaging::Socket_FailedToConnect, "Failed to connect" },
{ CMessaging::Socket_ReceiveBufferFail, "Failed to receive the buffer" },
{ CMessaging::Socket_SendBufferFail, "Failed to send the buffer" },
{ CMessaging::Socket_NoIncomingClientConnection, "No incoming client connection before the timeout" },
{ CMessaging::Socket_NotReadyToSend, "Socket not ready to send the buffer" },
{ CMessaging::Socket_NoDataReceived, "No data received by the socket" },
{ CMessaging::Socket_FailedCloseClientConnection, "Failed to close the client connection" },
{ CMessaging::Socket_FailedToCloseConnection, "Failed to close the server connection" },
{ CMessaging::Socket_FailedConnectClient, "Failed to connect the client" },
{ CMessaging::Socket_ClientAlreadyConnected, "A client is already connected" },
{ CMessaging::Deserialize_BufferTooSmall, "Buffer received is too small to be unpacked" },
{ CMessaging::Deserialize_Header, "Fail to unpack the buffer to a Header" },
{ CMessaging::Deserialize_ProtocolVersionMessage, "Fail to unpack Protocol Version message" },
{ CMessaging::Deserialize_BoxDescriptionMessage, "Fail to unpack Box description message" },
{ CMessaging::Deserialize_EBMLMessage, "Fail to unpack EBML message" },
{ CMessaging::Deserialize_EndMessage, "Fail to unpack End message" },
{ CMessaging::Deserialize_ErrorMessage, "Fail to unpack error message" },
{ CMessaging::Deserialize_LogMessage, "Fail to unpack log message" },
{ CMessaging::Deserialize_AuthenticationMessage, "Fail to unpack Authentication message" },
{ CMessaging::Deserialize_MessageTypeNotSupported, "Message type not supported" },
{ CMessaging::BoxDescriptionAlreadyReceived, "Box Description already received" },
{ CMessaging::BoxDescriptionNotReceived, "Box description not received" },
{ CMessaging::BadAuthenticationReceived, "Authentication received is invalid" },
{ CMessaging::NoAuthenticationReceived, "No authentication received before the timeout" },
{ CMessaging::ThreadJoinFailed, "Failed to terminate the thread" }
};
CMessaging::CMessaging()
{
impl = new SMessagingImpl();
impl->m_nMessage = 0;
impl->m_Connection = nullptr;
impl->m_LastLibraryError = NoError;
impl->m_IsStopRequested = false;
impl->m_IsInErrorState = false;
impl->m_IsEndMessageReceived = false;
}
CMessaging::~CMessaging()
{
this->reset();
delete impl;
}
void CMessaging::reset() const
{
this->stopSyncing();
impl->m_nMessage = 0;
impl->m_IsInErrorState = false;
impl->m_IsStopRequested = false;
impl->m_LastLibraryError = NoError;
std::queue<std::pair<uint64_t, AuthenticationMessage>>().swap(impl->m_IncomingAuthentications);
std::queue<std::pair<uint64_t, CommunicationProtocolVersionMessage>>().swap(impl->m_IncomingCommunicationProtocolVersions);
std::queue<std::pair<uint64_t, BoxDescriptionMessage>>().swap(impl->m_IncomingBoxDescriptions);
std::queue<std::pair<uint64_t, EBMLMessage>>().swap(impl->m_IncomingEBMLs);
std::queue<std::pair<uint64_t, LogMessage>>().swap(impl->m_IncomingLogs);
std::queue<std::pair<uint64_t, ErrorMessage>>().swap(impl->m_IncomingErrors);
impl->m_RcvBuffer.clear();
impl->m_SendBuffer.clear();
impl->m_SendBuffer.reserve(impl->s_BufferSize);
impl->m_Connection = nullptr;
}
CMessaging::ELibraryError CMessaging::getLastError() const { return impl->m_LastLibraryError; }
void CMessaging::setLastError(const ELibraryError libraryError) const { impl->m_LastLibraryError = libraryError; }
bool CMessaging::push() const
{
if (!impl->m_SendBuffer.empty() && impl->m_Connection->isReadyToSend(1))
{
std::lock_guard<std::mutex> lock(impl->m_SendBufferMutex);
const uint_fast32_t result = impl->m_Connection->sendBufferBlocking(impl->m_SendBuffer.data(), impl->m_SendBuffer.size());
if (result == 0)
{
this->setLastError(Socket_SendBufferFail);
return false;
}
impl->m_SendBuffer.clear();
}
return true;
}
bool CMessaging::pull() const
{
if (impl->m_Connection == nullptr)
{
this->setLastError(Socket_NotConnected);
return false;
}
while (impl->m_Connection->isReadyToReceive(1))
{
const uint_fast32_t bytesReceived = uint_fast32_t(impl->m_Connection->receiveBuffer(impl->m_TempRcvBuffer.data(), impl->m_TempRcvBuffer.size()));
if (bytesReceived == 0)
{
this->setLastError(Socket_ReceiveBufferFail);
return false;
}
impl->m_RcvBuffer.insert(impl->m_RcvBuffer.end(), impl->m_TempRcvBuffer.cbegin(), impl->m_TempRcvBuffer.cbegin() + int(bytesReceived));
}
return true;
}
bool CMessaging::processIncomingMessages() const
{
size_t byteRead = 0;
while (!impl->m_RcvBuffer.empty())
{
if (!this->processBuffer(impl->m_RcvBuffer, byteRead))
{
impl->m_RcvBuffer.clear();
// Error set in the function
return false;
}
// If the processing succeed, we erase the buffer part processed.
if (byteRead != 0) { impl->m_RcvBuffer.erase(impl->m_RcvBuffer.begin(), impl->m_RcvBuffer.begin() + static_cast<const long>(byteRead)); }
else
{
// The processing succeed but the byte count read is 0 so more data is waited.
break;
}
}
return true;
}
bool CMessaging::processBuffer(const std::vector<uint8_t>& buffer, size_t& byteRead) const
{
byteRead = 0;
if (buffer.empty()) { return true; }
// First, we try to fromBytes the buffer to found header information
Header header;
if (!header.fromBytes(buffer, byteRead))
{
this->setLastError(Deserialize_Header);
byteRead = size_t(header.getSize());
return false;
}
if (buffer.size() < header.getSize())
{
byteRead = 0;
return true; // Just wait for more data
}
// Try to unpack the object according to the type given by the header.
switch (header.getType())
{
case MessageType_Authentication:
{
AuthenticationMessage authentication;
if (!authentication.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_AuthenticationMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncAuthMutex);
impl->m_IncomingAuthentications.emplace(header.getId(), authentication);
}
break;
case MessageType_ProtocolVersion:
{
CommunicationProtocolVersionMessage communicationProtocolVersion;
if (!communicationProtocolVersion.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_ProtocolVersionMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncCommProVerMutex);
impl->m_IncomingCommunicationProtocolVersions.emplace(header.getId(), communicationProtocolVersion);
}
break;
case MessageType_BoxInformation:
{
BoxDescriptionMessage boxDescription;
if (!boxDescription.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_BoxDescriptionMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncBoxDescriptionMutex);
impl->m_IncomingBoxDescriptions.emplace(header.getId(), boxDescription);
}
break;
case MessageType_EBML:
{
EBMLMessage ebml;
if (!ebml.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_EBMLMessage);
return false;
}
if (ebml.getIndex() >= impl->m_BoxDesc.getOutputs()->size()) { this->pushMessage(ErrorMessage(Error_InvalidOutputIndex, header.getId())); }
std::lock_guard<std::mutex> lock(impl->m_IncEBMLMutex);
impl->m_IncomingEBMLs.emplace(header.getId(), ebml);
}
break;
case MessageType_Log:
{
LogMessage log;
if (!log.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_LogMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncLogMutex);
impl->m_IncomingLogs.emplace(header.getId(), log);
}
break;
case MessageType_End: { impl->m_IsEndMessageReceived = true; }
break;
case MessageType_Error:
{
ErrorMessage error;
if (!error.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_ErrorMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncErrorsMutex);
impl->m_IncomingErrors.emplace(header.getId(), error);
}
break;
case MessageType_Time:
{
TimeMessage timeMessage;
if (!timeMessage.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_ErrorMessage);
return false;
}
impl->m_Time = timeMessage.getTime();
}
break;
case MessageType_Sync: { impl->m_WasSyncMessageReceived = true; }
break;
case MessageType_Unknown:
case MessageType_Max:
this->pushMessage(ErrorMessage(Error_InvalidMessageType, header.getId()));
this->setLastError(Deserialize_MessageTypeNotSupported);
return false;
}
return true;
}
bool CMessaging::isInErrorState() const { return impl->m_IsInErrorState.load(); }
bool CMessaging::isEndReceived() { return impl->m_IsEndMessageReceived; }
uint64_t CMessaging::getTime() { return impl->m_Time; }
bool CMessaging::pushMessage(const Message& message) const
{
if (this->isInErrorState()) { return false; }
std::vector<uint8_t> messageBuffer = message.toBytes();
const Header header(message.getMessageType(), impl->m_nMessage++, messageBuffer.size());
std::vector<uint8_t> headerBuffer = header.toBytes();
std::lock_guard<std::mutex> lock(impl->m_SendBufferMutex);
impl->m_SendBuffer.insert(impl->m_SendBuffer.end(), headerBuffer.begin(), headerBuffer.end());
impl->m_SendBuffer.insert(impl->m_SendBuffer.end(), messageBuffer.begin(), messageBuffer.end());
return true;
}
std::string CMessaging::getErrorString(const ELibraryError error) { return ERRORS_STRING.at(error); }
bool CMessaging::isConnected() const
{
if (impl->m_Connection == nullptr) { return false; }
return impl->m_Connection->isConnected();
}
void CMessaging::sync() const
{
while (true)
{
if (!this->push())
{
impl->m_IsInErrorState = true;
break;
}
if (!this->pull())
{
impl->m_IsInErrorState = true;
break;
}
if (!this->processIncomingMessages())
{
impl->m_IsInErrorState = true;
break;
}
if (impl->m_IsStopRequested)
{
// Used to be sure to send the end message
this->pull();
break;
}
}
}
void CMessaging::setConnection(Socket::IConnection* connection) const { impl->m_Connection = connection; }
bool CMessaging::startSyncing()
{
impl->m_IsEndMessageReceived = false;
impl->m_SyncThread = std::thread(&CMessaging::sync, this);
impl->m_IsStopRequested = false;
return true;
}
bool CMessaging::stopSyncing() const
{
impl->m_IsStopRequested = true;
if (impl->m_SyncThread.joinable()) { impl->m_SyncThread.join(); }
return true;
}
bool CMessaging::popAuthentication(uint64_t& id, std::string& connectionID)
{
std::lock_guard<std::mutex> lock(impl->m_IncAuthMutex);
if (impl->m_IncomingAuthentications.empty()) { return false; }
id = impl->m_IncomingAuthentications.front().first;
connectionID = impl->m_IncomingAuthentications.front().second.getConnectionID();
impl->m_IncomingAuthentications.pop();
return true;
}
bool CMessaging::popBoxDescriptions(uint64_t& id, BoxDescriptionMessage& boxDescription)
{
std::lock_guard<std::mutex> lock(impl->m_IncBoxDescriptionMutex);
if (impl->m_IncomingBoxDescriptions.empty()) { return false; }
id = impl->m_IncomingBoxDescriptions.front().first;
boxDescription = impl->m_IncomingBoxDescriptions.front().second;
impl->m_IncomingBoxDescriptions.pop();
return true;
}
bool CMessaging::popCommunicationProtocolVersion(uint64_t& id, uint8_t& majorVersion, uint8_t& minorVersion)
{
std::lock_guard<std::mutex> lock(impl->m_IncCommProVerMutex);
if (impl->m_IncomingCommunicationProtocolVersions.empty()) { return false; }
id = impl->m_IncomingCommunicationProtocolVersions.front().first;
majorVersion = impl->m_IncomingCommunicationProtocolVersions.front().second.getMajorVersion();
minorVersion = impl->m_IncomingCommunicationProtocolVersions.front().second.getMinorVersion();
impl->m_IncomingEBMLs.pop();
return true;
}
bool CMessaging::popLog(uint64_t& id, ELogLevel& type, std::string& message)
{
std::lock_guard<std::mutex> lock(impl->m_IncLogMutex);
if (impl->m_IncomingLogs.empty()) { return false; }
id = impl->m_IncomingLogs.front().first;
type = impl->m_IncomingLogs.front().second.getType();
message = impl->m_IncomingLogs.front().second.getMessage();
impl->m_IncomingLogs.pop();
return true;
}
bool CMessaging::popError(uint64_t& id, EError& type, uint64_t& guiltyId)
{
std::lock_guard<std::mutex> lock(impl->m_IncErrorsMutex);
if (impl->m_IncomingErrors.empty()) { return false; }
id = impl->m_IncomingErrors.front().first;
type = impl->m_IncomingErrors.front().second.getType();
guiltyId = impl->m_IncomingErrors.front().second.getGuiltyId();
impl->m_IncomingErrors.pop();
return true;
}
bool CMessaging::popEBML(uint64_t& id, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml)
{
std::lock_guard<std::mutex> lock(impl->m_IncEBMLMutex);
if (impl->m_IncomingEBMLs.empty()) { return false; }
id = impl->m_IncomingEBMLs.front().first;
index = impl->m_IncomingEBMLs.front().second.getIndex();
startTime = impl->m_IncomingEBMLs.front().second.getStartTime();
endTime = impl->m_IncomingEBMLs.front().second.getEndTime();
ebml = impl->m_IncomingEBMLs.front().second.getEBML();
impl->m_IncomingEBMLs.pop();
return true;
}
bool CMessaging::popEnd(uint64_t& id)
{
std::lock_guard<std::mutex> lock(impl->m_IncEndMutex);
if (impl->m_IncomingEnds.empty()) { return false; }
id = impl->m_IncomingEnds.front().first;
impl->m_IncomingEnds.pop();
return true;
}
void CMessaging::setConnectionID(const std::string& connectionID) const { impl->m_ConnectionID = connectionID; }
bool CMessaging::waitForSyncMessage()
{
if (impl->m_WasSyncMessageReceived)
{
impl->m_WasSyncMessageReceived = false;
return true;
}
return false;
}
} // namespace Communication
@@ -0,0 +1,172 @@
#include "ovCMessagingClient.h"
#include "ovCMessagingImpl.hpp"
namespace Communication {
MessagingClient::MessagingClient() : CMessaging(), m_Client(Socket::createConnectionClient()) {}
MessagingClient::~MessagingClient()
{
this->close();
m_Client->release();
}
bool MessagingClient::connect(const std::string& uri, const size_t port)
{
this->reset();
if (!m_Client->connect(uri.c_str(), port))
{
this->setLastError(Socket_FailedToConnect);
return false;
}
this->setConnection(m_Client);
// Once the connection is done, the client push an authentication message to the server
if (!this->pushAuthentication(impl->m_ConnectionID))
{
// Error set in the function
const ELibraryError error = this->getLastError();
this->close();
this->setLastError(error);
return false;
}
if (!this->startSyncing())
{
this->close();
return false;
}
// To work, the client need to receive box information message. So we wait to receive the box information message.
// A timeout of 10 seconds is set.
const std::chrono::time_point<std::chrono::system_clock> startClock = std::chrono::system_clock::now();
uint64_t packetId;
while (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - startClock).count() < 10)
{
if (this->popBoxDescriptions(packetId, impl->m_BoxDesc))
{
m_BoxDescriptionReceived = true;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if (!m_BoxDescriptionReceived)
{
this->close();
this->setLastError(BoxDescriptionNotReceived);
return false;
}
return true;
}
bool MessagingClient::close() const
{
this->pushMessage(EndMessage());
this->stopSyncing();
if (m_Client != nullptr)
{
if (!m_Client->close())
{
this->setLastError(Socket_FailedToCloseConnection);
return false;
}
}
return true;
}
size_t MessagingClient::getParameterCount() const
{
if (!m_BoxDescriptionReceived) { return 0; }
return impl->m_BoxDesc.getParameters()->size();
}
size_t MessagingClient::getInputCount() const
{
if (!m_BoxDescriptionReceived) { return 0; }
return impl->m_BoxDesc.getInputs()->size();
}
size_t MessagingClient::getOutputCount() const
{
if (!m_BoxDescriptionReceived) { return 0; }
return impl->m_BoxDesc.getOutputs()->size();
}
bool MessagingClient::getParameter(const size_t i, uint64_t& id, uint64_t& type, std::string& name, std::string& value) const
{
if (!m_BoxDescriptionReceived) { return false; }
const std::vector<Parameter>* parameters = impl->m_BoxDesc.getParameters();
if (parameters->size() <= i) { return false; }
id = parameters->at(i).getId();
type = parameters->at(i).getType();
name = parameters->at(i).getName();
value = parameters->at(i).getValue();
return true;
}
bool MessagingClient::getInput(const size_t i, uint64_t& id, uint64_t& type, std::string& name) const
{
if (!m_BoxDescriptionReceived) { return false; }
const std::vector<InputOutput>* inputs = impl->m_BoxDesc.getInputs();
if (inputs->size() <= i) { return false; }
id = inputs->at(i).getId();
type = inputs->at(i).getType();
name = inputs->at(i).getName();
return true;
}
bool MessagingClient::getOutput(const size_t i, uint64_t& id, uint64_t& type, std::string& name) const
{
if (!m_BoxDescriptionReceived)
{
const_cast<MessagingClient*>(this)->setLastError(BoxDescriptionNotReceived);
return false;
}
const std::vector<InputOutput>* outputs = impl->m_BoxDesc.getOutputs();
if (outputs->size() <= i) { return false; }
id = outputs->at(i).getId();
type = outputs->at(i).getType();
name = outputs->at(i).getName();
return true;
}
bool MessagingClient::popError(uint64_t& packetId, EError& type, uint64_t& guiltyId) { return CMessaging::popError(packetId, type, guiltyId); }
bool MessagingClient::popEBML(uint64_t& packetId, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml)
{
return CMessaging::popEBML(packetId, index, startTime, endTime, ebml);
}
bool MessagingClient::pushAuthentication(const std::string& connectionID) const { return this->pushMessage(AuthenticationMessage(connectionID)); }
bool MessagingClient::pushLog(const ELogLevel logLevel, const std::string& log) const { return this->pushMessage(LogMessage(logLevel, log)); }
bool MessagingClient::pushEBML(const size_t index, const uint64_t startTime, const uint64_t endTime,
const std::shared_ptr<const std::vector<uint8_t>>& ebml) const
{
return this->pushMessage(EBMLMessage(index, startTime, endTime, ebml));
}
bool MessagingClient::pushSync() const { return this->pushMessage(SyncMessage()); }
bool MessagingClient::waitForSyncMessage() { return CMessaging::waitForSyncMessage(); }
} // namespace Communication
@@ -0,0 +1,65 @@
#pragma once
#include "ovCMessaging.h"
#include "ovCMessagingProtocol.h"
#include <vector>
#include <array>
#include <string>
#include <cstdint>
#include <thread>
#include <mutex>
#include <queue>
#include <atomic>
namespace Communication {
struct CMessaging::SMessagingImpl
{
std::string m_ConnectionID;
BoxDescriptionMessage m_BoxDesc;
std::atomic<uint64_t> m_Time;
uint64_t m_nMessage = 0;
std::mutex m_IncAuthMutex;
std::queue<std::pair<uint64_t, AuthenticationMessage>> m_IncomingAuthentications;
std::mutex m_IncCommProVerMutex;
std::queue<std::pair<uint64_t, CommunicationProtocolVersionMessage>> m_IncomingCommunicationProtocolVersions;
std::mutex m_IncBoxDescriptionMutex;
std::queue<std::pair<uint64_t, BoxDescriptionMessage>> m_IncomingBoxDescriptions;
std::mutex m_IncEBMLMutex;
std::queue<std::pair<uint64_t, EBMLMessage>> m_IncomingEBMLs;
std::mutex m_IncLogMutex;
std::queue<std::pair<uint64_t, LogMessage>> m_IncomingLogs;
std::mutex m_IncErrorsMutex;
std::queue<std::pair<uint64_t, ErrorMessage>> m_IncomingErrors;
std::mutex m_IncEndMutex;
std::queue<std::pair<uint64_t, EndMessage>> m_IncomingEnds;
static const size_t s_BufferSize = 1024 * 64; //< Empirical value
std::vector<uint8_t> m_RcvBuffer;
std::array<uint8_t, s_BufferSize> m_TempRcvBuffer;
std::mutex m_SendBufferMutex;
std::vector<uint8_t> m_SendBuffer;
Socket::IConnection* m_Connection = nullptr;
mutable std::atomic<ELibraryError> m_LastLibraryError;
std::thread m_SyncThread;
std::atomic<bool> m_IsStopRequested;
std::atomic<bool> m_IsInErrorState;
std::atomic<bool> m_IsEndMessageReceived;
std::atomic<bool> m_WasSyncMessageReceived;
};
} // namespace Communication
@@ -0,0 +1,565 @@
#include <array>
#include <string>
#include <algorithm>
#include <cstring>
#include "ovCMessaging.h"
namespace Communication {
/**
* \brief Copy a string to buffer
*
* \param [out] dest The buffer destination
* \param [out] bufferIndex The index of the buffer where the beginning of the string must be copied.
* \param value The string to copy.
*
* \retval True if it succeeds
* \retval False if it fails.
*/
static bool copyTobuffer(std::vector<uint8_t>& dest, size_t& bufferIndex, const std::string& value)
{
if (dest.size() < bufferIndex + value.size()) { return false; }
memcpy(dest.data() + bufferIndex, value.data(), value.size());
bufferIndex += value.size();
return true;
}
/**
* \brief Copy a value to a buffer.
*
* \param [out] dest Destination for the.
* \param [out] bufferIndex Zero-based index of the buffer.
* \param value The value.
*
* \return True if it succeeds, false if it fails.
*/
template <class T>
static bool copyTobuffer(std::vector<uint8_t>& dest, size_t& bufferIndex, const T& value)
{
if (dest.size() < bufferIndex + sizeof(value)) { return false; }
memcpy(dest.data() + bufferIndex, &value, sizeof(value));
bufferIndex += sizeof(value);
return true;
}
template <class T>
static bool copyToVariable(const std::vector<uint8_t>& src, const size_t bufferIndex, T& destVariable)
{
if (src.size() < bufferIndex + sizeof(destVariable)) { return false; }
memcpy(&destVariable, src.data() + bufferIndex, sizeof(destVariable));
return true;
}
/**
* \brief Convert a buffer to a string
*
* \param src The buffer
* \param bufferIndex The index where to start the convertion.
* \param size The size of the string.
* \param [out] string The string.
*
* \retval True if it succeeds
* \retval False if it fails.
*
* \sa copyToVariable
*/
static bool copyToString(const std::vector<uint8_t>& src, const size_t bufferIndex, const size_t size, std::string& string)
{
if (src.size() < bufferIndex + size) { return false; }
string = std::string(src.begin() + static_cast<const long>(bufferIndex),
src.begin() + static_cast<const long>(bufferIndex) + static_cast<const long>(size));
return true;
}
/******************************************************************************
*
* Header
*
******************************************************************************/
Header::Header() : m_type(MessageType_Unknown), m_id(std::numeric_limits<decltype(m_id)>::max()) { m_isValid = false; }
Header::Header(const EMessageType type, const uint64_t id, const size_t size) : m_type(type), m_id(id), m_size(size) { m_isValid = true; }
std::vector<uint8_t> Header::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_id);
copyTobuffer(buffer, bufferIndex, m_size);
return buffer;
}
bool Header::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
uint8_t typeInt;
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, typeInt)) { return false; }
if (typeInt >= MessageType_Max) { return false; }
m_type = EMessageType(typeInt);
if (!copyToVariable(buffer, bufferIndex + ID_INDEX, m_id)) { return false; }
if (!copyToVariable(buffer, bufferIndex + SIZE_INDEX, m_size)) { return false; }
bufferIndex += MINIMUM_SIZE;
m_isValid = true;
return true;
}
/******************************************************************************
*
* Authentication
*
******************************************************************************/
std::vector<uint8_t> AuthenticationMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE + m_connectionID.size());
size_t bufferIndex = 0;
const size_t size = m_connectionID.size();
copyTobuffer(buffer, bufferIndex, size);
copyTobuffer(buffer, bufferIndex, m_connectionID);
return buffer;
}
bool AuthenticationMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
size_t passPhraseSize;
if (!copyToVariable(buffer, bufferIndex + SIZE_INDEX, passPhraseSize)) { return false; }
if (!copyToString(buffer, bufferIndex + CONNECTION_ID_INDEX, passPhraseSize, m_connectionID)) { return false; }
m_isValid = true;
bufferIndex += MINIMUM_SIZE + passPhraseSize;
return true;
}
/******************************************************************************
*
* Communication protocol version
*
******************************************************************************/
std::vector<uint8_t> CommunicationProtocolVersionMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_majorVersion);
copyTobuffer(buffer, bufferIndex, m_minorVersion);
return buffer;
}
bool CommunicationProtocolVersionMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + MAJOR_INDEX, m_majorVersion)) { return false; }
if (!copyToVariable(buffer, bufferIndex + MINOR_INDEX, m_minorVersion)) { return false; }
m_isValid = true;
bufferIndex += MINIMUM_SIZE;
return true;
}
/******************************************************************************
*
* Inout and output
*
******************************************************************************/
InputOutput::InputOutput()
: m_id(std::numeric_limits<decltype(m_id)>::max()), m_type(std::numeric_limits<decltype(m_type)>::max()), m_name(std::string()) { m_isValid = false; }
InputOutput::InputOutput(const uint64_t id, const size_t type, const std::string& name) : m_id(id), m_type(type), m_name(name) { m_isValid = true; }
std::vector<uint8_t> InputOutput::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE + m_name.size());
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_id);
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_name.size());
copyTobuffer(buffer, bufferIndex, m_name);
return buffer;
}
bool InputOutput::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + ID_INDEX, m_id)) { return false; }
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, m_type)) { return false; }
size_t nameSize;
if (!copyToVariable(buffer, bufferIndex + NAME_SIZE_INDEX, nameSize)) { return false; }
if (!copyToString(buffer, bufferIndex + NAME_INDEX, nameSize, m_name)) { return false; }
m_isValid = true;
bufferIndex += MINIMUM_SIZE + nameSize;
return true;
}
/******************************************************************************
*
* Parameter
*
******************************************************************************/
Parameter::Parameter()
: m_id(std::numeric_limits<decltype(m_id)>::max()), m_type(std::numeric_limits<decltype(m_type)>::max()), m_name(std::string()), m_value(std::string())
{
m_isValid = false;
}
Parameter::Parameter(const uint64_t id, const size_t type, const std::string& name, const std::string& value)
: m_id(id), m_type(type), m_name(name), m_value(value) { m_isValid = true; }
std::vector<uint8_t> Parameter::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE + m_name.size() + m_value.size());
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_id);
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_name.size());
copyTobuffer(buffer, bufferIndex, m_value.size());
copyTobuffer(buffer, bufferIndex, m_name);
copyTobuffer(buffer, bufferIndex, m_value);
return buffer;
}
bool Parameter::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + ID_INDEX, m_id)) { return false; }
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, m_type)) { return false; }
size_t nameSize;
if (!copyToVariable(buffer, bufferIndex + NAME_SIZE_INDEX, nameSize)) { return false; }
size_t valueSize;
if (!copyToVariable(buffer, bufferIndex + VALUE_SIZE_INDEX, valueSize)) { return false; }
if (!copyToString(buffer, bufferIndex + NAME_INDEX, nameSize, m_name)) { return false; }
if (!copyToString(buffer, bufferIndex + NAME_INDEX + nameSize, valueSize, m_value)) { return false; }
bufferIndex += MINIMUM_SIZE + nameSize + valueSize;
return true;
}
/******************************************************************************
*
* Box description
*
******************************************************************************/
std::vector<uint8_t> BoxDescriptionMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_inputs.size());
copyTobuffer(buffer, bufferIndex, m_outputs.size());
copyTobuffer(buffer, bufferIndex, m_parameters.size());
for (const InputOutput& input : m_inputs)
{
std::vector<uint8_t> inputBuffer = input.toBytes();
buffer.insert(buffer.end(), inputBuffer.begin(), inputBuffer.end());
}
for (const InputOutput& output : m_outputs)
{
std::vector<uint8_t> outputBuffer = output.toBytes();
buffer.insert(buffer.end(), outputBuffer.begin(), outputBuffer.end());
}
for (const Parameter& parameter : m_parameters)
{
std::vector<uint8_t> parameterBuffer = parameter.toBytes();
buffer.insert(buffer.end(), parameterBuffer.begin(), parameterBuffer.end());
}
return buffer;
}
bool BoxDescriptionMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
size_t inputCount;
size_t outputCount;
size_t parameterCount;
if (!copyToVariable(buffer, bufferIndex + N_INPUT_INDEX, inputCount)) { return false; }
if (!copyToVariable(buffer, bufferIndex + N_OUTPUT_INDEX, outputCount)) { return false; }
if (!copyToVariable(buffer, bufferIndex + N_PARAMETER_INDEX, parameterCount)) { return false; }
bufferIndex += MINIMUM_SIZE;
m_inputs.clear();
m_outputs.clear();
m_parameters.clear();
for (size_t i = 0; i < inputCount; ++i)
{
InputOutput input;
if (!input.fromBytes(buffer, bufferIndex)) { return false; }
m_inputs.push_back(input);
}
for (size_t i = 0; i < outputCount; ++i)
{
InputOutput output;
if (!output.fromBytes(buffer, bufferIndex)) { return false; }
m_outputs.push_back(output);
}
for (size_t i = 0; i < parameterCount; ++i)
{
Parameter parameter;
if (!parameter.fromBytes(buffer, bufferIndex)) { return false; }
m_parameters.push_back(parameter);
}
m_isValid = true;
return true;
}
EMessageType BoxDescriptionMessage::getMessageType() const { return MessageType_BoxInformation; }
bool BoxDescriptionMessage::addInput(const uint64_t id, const size_t type, const std::string& name)
{
const auto it = std::find_if(m_inputs.begin(), m_inputs.end(), [&id](const InputOutput& obj) { return obj.getId() == id; });
if (it != m_inputs.end()) { return false; }
m_inputs.emplace_back(id, type, name);
return true;
}
bool BoxDescriptionMessage::addOutput(const uint64_t id, const size_t type, const std::string& name)
{
const auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [&id](const InputOutput& obj) { return obj.getId() == id; });
if (it != m_outputs.end()) { return false; }
m_outputs.emplace_back(id, type, name);
return true;
}
bool BoxDescriptionMessage::addParameter(const uint64_t id, const size_t type, const std::string& name, const std::string& value)
{
const auto it = std::find_if(m_parameters.begin(), m_parameters.end(), [&id](const Parameter& obj) { return obj.getId() == id; });
if (it != m_parameters.end()) { return false; }
m_parameters.emplace_back(id, type, name, value);
return true;
}
/******************************************************************************
*
* Packet part
*
******************************************************************************/
/******************************************************************************
*
* Log
*
******************************************************************************/
LogMessage::LogMessage() : m_type(LogLevel_Unknown) { m_isValid = false; }
LogMessage::LogMessage(const ELogLevel type, const std::string& message) : m_type(type), m_message(message) { m_isValid = true; }
std::vector<uint8_t> LogMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE + m_message.size());
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_message.size());
copyTobuffer(buffer, bufferIndex, m_message);
return buffer;
}
bool LogMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
// Type
uint8_t typeInt;
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, typeInt)) { return false; }
if (typeInt >= LogLevel_Max) { return false; }
m_type = ELogLevel(typeInt);
// Message size
size_t messageSize;
if (!copyToVariable(buffer, bufferIndex + SIZE_INDEX, messageSize)) { return false; }
if (!copyToString(buffer, bufferIndex + NAME_INDEX, messageSize, m_message)) { return false; }
bufferIndex += MINIMUM_SIZE + messageSize;
m_isValid = true;
return true;
}
/******************************************************************************
*
* EBML
*
******************************************************************************/
EBMLMessage::EBMLMessage()
: m_ioIdx(std::numeric_limits<decltype(m_ioIdx)>::max()), m_startTime(std::numeric_limits<decltype(m_startTime)>::max()),
m_endTime(std::numeric_limits<decltype(m_endTime)>::max()) { m_isValid = false; }
EBMLMessage::EBMLMessage(const size_t index, const uint64_t startTime, const uint64_t endTime, const std::shared_ptr<const std::vector<uint8_t>>& ebml)
: m_ioIdx(index), m_startTime(startTime), m_endTime(endTime), m_EBML(ebml) { m_isValid = true; }
std::vector<uint8_t> EBMLMessage::toBytes() const
{
if (!m_isValid) { return std::vector<uint8_t>(); }
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_ioIdx);
copyTobuffer(buffer, bufferIndex, m_startTime);
copyTobuffer(buffer, bufferIndex, m_endTime);
copyTobuffer(buffer, bufferIndex, size_t(m_EBML->size()));
if (!m_EBML->empty()) { buffer.insert(buffer.end(), m_EBML->begin(), m_EBML->end()); }
return buffer;
}
bool EBMLMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + IO_INDEX_INDEX, m_ioIdx)) { return false; }
if (!copyToVariable(buffer, bufferIndex + START_TIME_INDEX, m_startTime)) { return false; }
if (!copyToVariable(buffer, bufferIndex + END_TIME_INDEX, m_endTime)) { return false; }
size_t EBMLsize;
if (!copyToVariable(buffer, bufferIndex + SIZE_INDEX, EBMLsize)) { return false; }
if (buffer.size() < bufferIndex + EBML_INDEX + EBMLsize) { return false; }
m_EBML.reset(new std::vector<uint8_t>(buffer.begin() + static_cast<const long>(bufferIndex) + EBML_INDEX,
buffer.begin() + static_cast<const long>(bufferIndex) + EBML_INDEX + EBMLsize));
bufferIndex += MINIMUM_SIZE + EBMLsize;
m_isValid = true;
return true;
}
/******************************************************************************
*
* Error
*
******************************************************************************/
std::vector<uint8_t> ErrorMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_guiltyId);
return buffer;
}
bool ErrorMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, m_type)) { return false; }
if (!copyToVariable(buffer, bufferIndex + GUILTY_ID_INDEX, m_guiltyId)) { return false; }
bufferIndex += MINIMUM_SIZE;
m_isValid = true;
return true;
}
/******************************************************************************
*
* Time
*
******************************************************************************/
std::vector<uint8_t> TimeMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_time);
return buffer;
}
bool TimeMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + TIME_INDEX, m_time)) { return false; }
bufferIndex += MINIMUM_SIZE;
m_isValid = true;
return true;
}
} // namespace Communication
@@ -0,0 +1,150 @@
#include "ovCMessagingServer.h"
#include "ovCMessagingImpl.hpp"
namespace Communication {
MessagingServer::~MessagingServer()
{
this->close();
m_Server->release();
}
bool MessagingServer::accept()
{
this->reset();
if (this->isConnected())
{
// A connection is already done to a client
this->setLastError(Socket_ClientAlreadyConnected);
return false;
}
if (!m_Server->isReadyToReceive())
{
this->setLastError(Socket_NoIncomingClientConnection);
return false;
}
m_Client = m_Server->accept();
if (m_Client == nullptr)
{
this->setLastError(Socket_NoIncomingClientConnection);
return false;
}
this->setConnection(m_Client);
this->startSyncing();
if (!impl->m_ConnectionID.empty())
{
// The server has to verify that the client is the one that is waited. So the server wait the authentication packet.
const std::chrono::time_point<std::chrono::system_clock> startClock = std::chrono::system_clock::now();
std::string connectionID;
uint64_t id = 0;
bool isAuthReceived = false;
while (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - startClock).count() < 10)
{
if (this->popAuthentication(id, connectionID))
{
isAuthReceived = true;
if (connectionID != impl->m_ConnectionID)
{
this->pushError(Error_AuthenticationFail, id);
this->close();
this->setLastError(BadAuthenticationReceived);
return false;
}
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if (!isAuthReceived)
{
this->pushError(Error_AuthenticationRequested, id);
this->close();
this->setLastError(NoAuthenticationReceived);
return false;
}
}
if (!this->pushMessage(CommunicationProtocolVersionMessage(s_CommunicationProtocol_MajorVersion, s_CommunicationProtocol_MinorVersion)))
{
// Error set in the function
const ELibraryError error = this->getLastError();
this->close();
this->setLastError(error);
return false;
}
if (!this->pushMessage(impl->m_BoxDesc))
{
// Error set in the function
const ELibraryError error = this->getLastError();
this->close();
this->setLastError(error);
return false;
}
return true;
}
bool MessagingServer::close()
{
this->pushMessage(EndMessage());
this->stopSyncing();
bool errorRaised = false;
if (m_Client != nullptr)
{
if (!m_Client->close())
{
errorRaised = true;
this->setLastError(Socket_FailedToCloseConnection);
}
m_Client->release();
m_Client = nullptr;
}
if (!m_Server->close())
{
this->setLastError(Socket_FailedToCloseConnection);
errorRaised = true;
}
return !errorRaised;
}
bool MessagingServer::addParameter(const uint64_t id, const size_t type, const std::string& name, const std::string& value) const
{
return impl->m_BoxDesc.addParameter(id, type, name, value);
}
bool MessagingServer::addInput(const uint64_t id, const size_t type, const std::string& name) const { return impl->m_BoxDesc.addInput(id, type, name); }
bool MessagingServer::addOutput(const uint64_t id, const size_t type, const std::string& name) const { return impl->m_BoxDesc.addOutput(id, type, name); }
bool MessagingServer::popLog(uint64_t& packetId, ELogLevel& type, std::string& message) { return CMessaging::popLog(packetId, type, message); }
bool MessagingServer::popEBML(uint64_t& packetId, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml)
{
return CMessaging::popEBML(packetId, index, startTime, endTime, ebml);
}
bool MessagingServer::pushError(const EError error, const uint64_t guiltyId) const { return this->pushMessage(ErrorMessage(error, guiltyId)); }
bool MessagingServer::pushEBML(const size_t index, const uint64_t startTime, const uint64_t endTime,
const std::shared_ptr<const std::vector<uint8_t>>& ebml) const
{
return this->pushMessage(EBMLMessage(index, startTime, endTime, ebml));
}
} // namespace Communication
@@ -0,0 +1,55 @@
PROJECT(openvibe-module-csv)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp include/*.h src/*.hpp)
INCLUDE_DIRECTORIES(include/csv)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DCSV_Shared -DCSV_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DCSV_Static -DCSV_Exports")
IF(UNIX)
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF(UNIX)
SET(DYNAMIC_LINK_OPENVIBE_MODULE_FS OFF)
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEModuleFS")
INCLUDE("FindThirdPartyBoost")
INCLUDE("FindThirdPartyBoost_FileSystem")
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} COMPONENT headers FILES_MATCHING PATTERN "*.h")
@@ -0,0 +1,23 @@
#pragma once
#if defined CSV_Shared
# if defined TARGET_OS_Windows
# define CSV_API_Export __declspec(dllexport)
# define CSV_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
# define CSV_API_Export __attribute__((visibility("default")))
# define CSV_API_Import __attribute__((visibility("default")))
# else
# define CSV_API_Export
# define CSV_API_Import
# endif
#else
# define CSV_API_Export
# define CSV_API_Import
#endif
#if defined CSV_Exports
# define CSV_API CSV_API_Export
#else
# define CSV_API CSV_API_Import
#endif
@@ -0,0 +1,380 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "defines.h"
#include <queue>
#include <string>
#include <vector>
#include <cstdint>
namespace OpenViBE {
namespace CSV {
struct SMatrixChunk
{
double startTime; // in second
double endTime; // in second
std::vector<double> matrix;
uint64_t epoch;
SMatrixChunk(const double startTime, const double endTime, const std::vector<double>& matrix, const uint64_t epoch)
: startTime(startTime), endTime(endTime), matrix(matrix), epoch(epoch) { }
};
struct SStimulationChunk
{
uint64_t id;
double date; // in second
double duration; // in second
SStimulationChunk(const uint64_t id, const double date, const double duration) : id(id), date(date), duration(duration) { }
};
enum class EStreamType
{
StreamedMatrix = 0,
FeatureVector = 1,
Signal = 2,
Spectrum = 3,
CovarianceMatrix = 4
};
enum ELogErrorCodes
{
LogErrorCodes_HeaderNotRead = -32,
LogErrorCodes_MissingData = -31,
LogErrorCodes_WrongParameters = -30,
LogErrorCodes_ErrorWhileWriting = -29,
LogErrorCodes_CantWriteHeader = -28,
LogErrorCodes_InvalidArgumentException = -27,
LogErrorCodes_OutOfRangeException = -26,
LogErrorCodes_DimensionCountZero = -25,
LogErrorCodes_WrongDimensionSize = -24,
LogErrorCodes_EmptyColumn = -23,
LogErrorCodes_StimulationSize = -22,
LogErrorCodes_InvalidStimulationArgument = -21,
LogErrorCodes_WrongSampleDate = -20,
LogErrorCodes_NegativeStimulation = -19,
LogErrorCodes_NotEnoughLines = -18,
LogErrorCodes_SampleNotEmpty = -17,
LogErrorCodes_WrongLineSize = -16,
LogErrorCodes_WrongHeader = -15,
LogErrorCodes_WrongInputType = -14,
LogErrorCodes_SetInfoOnce = -13,
LogErrorCodes_NoChannelsName = -12,
LogErrorCodes_DimensionSizeEmpty = -11,
LogErrorCodes_DimensionSizeZero = -10,
LogErrorCodes_MatrixEmpty = -9,
LogErrorCodes_WrongMatrixSize = -8,
LogErrorCodes_NoMatrixLabels = -7,
LogErrorCodes_NoSample = -6,
LogErrorCodes_DateError = -5,
LogErrorCodes_DurationError = -4,
LogErrorCodes_CantOpenFile = -3,
LogErrorCodes_NoFileDefined = -2,
LogErrorCodes_ErrorWhileClosing = -1,
LogErrorCodes_NoError = 0
};
enum class EFileAccessMode
{
Write,
Append,
Read
};
class CSV_API ICSVHandler
{
public:
/**
* \brief Return the number of digit of float numbers in output file.
*
* \return size_t number of digits
*/
virtual size_t getOutputFloatPrecision() = 0;
/**
* \brief Set the number of digits of float numbers in output file.
*
* \param precision number of digits
*/
virtual void setOutputFloatPrecision(const size_t precision) = 0;
/**
* \brief Set the format type that will be written or read.
*
* \param typeID Could be Streamed Matrix, Signal, Covariance Matrix, etc ...
*/
virtual void setFormatType(const EStreamType typeID) = 0;
/**
* \brief Return the format type
*
* \return EStreamType format type
*/
virtual EStreamType getFormatType() = 0;
/**
* \brief Set the state of the LastMatrixOnly mode.
*
* \param isActivated bool setting it to true will activate the LastMatrixOnly mode, false will disable it.
*/
virtual void setLastMatrixOnlyMode(const bool isActivated) = 0;
/**
* \brief Return the state of the LastMatrixOnly mode
*
* \retval true if LastMatrixOnly mode is activated
* \retval false if this is not (normal mode)
*/
virtual bool getLastMatrixOnlyMode() = 0;
/**
* \brief Set informations to read or write signal data
*
* \param channelNames all channels names for the matrix
* \param sampling sampling frequency
* \param nSamplePerBuffer number of sample per buffer
*
* \retval true in case of success
* \retval false in case of incorrect or incomplete parameters
*/
virtual bool setSignalInformation(const std::vector<std::string>& channelNames, size_t sampling, size_t nSamplePerBuffer) = 0;
/**
* \brief Get signal information in file
*
* \param channelNames reference to fill with file channel names
* \param sampling sampling frequency
* \param nSamplePerBuffer number of sample per buffer
*
* \retval true in case of success
* \retval false in case of failure
*/
virtual bool getSignalInformation(std::vector<std::string>& channelNames, size_t& sampling, size_t& nSamplePerBuffer) = 0;
/**
* \brief Set informations to read or write spectrum data
*
* \param channelNames Channels names of the matrix
* \param frequencyAbscissa Frequencies abscissa of the measurements
* \param sampling Sampling rate of the original measurements
*
* \retval true in case of success
* \retval false in case of incorrect or incomplete parameters
*/
virtual bool setSpectrumInformation(const std::vector<std::string>& channelNames, const std::vector<double>& frequencyAbscissa, size_t sampling) = 0;
/**
* \brief get spectrum information in file
*
* \param channelNames Channels names of the matrix
* \param frequencyAbscissa Frequencies abscissa of the measurements
* \param sampling Sampling rate of the original measurements
*
* \retval true in case of success
* \retval false in case of failure
*/
virtual bool getSpectrumInformation(std::vector<std::string>& channelNames, std::vector<double>& frequencyAbscissa, size_t& sampling) = 0;
/**
* \brief Set informations to read or write vector data
*
* \param channelNames all channels names for the matrix
*
* \retval true in case of success
* \retval false in case of incorrect or incomplete parameters
*/
virtual bool setFeatureVectorInformation(const std::vector<std::string>& channelNames) = 0;
/**
* \brief Get feature vector information in file
*
* \param channelNames reference to fill with file channel names
*
* \retval true in case of success
* \retval false in case of failure
*/
virtual bool getFeatureVectorInformation(std::vector<std::string>& channelNames) = 0;
/**
* \brief Set informations to read or write streamed or covariance matrix data
*
* \param dimensionSizes size of each dimension
* \param labels all channels names for the matrix
*
* \retval true in case of success
* \retval false in case of incorrect or incomplete parameters
*/
virtual bool setStreamedMatrixInformation(const std::vector<size_t>& dimensionSizes, const std::vector<std::string>& labels) = 0;
/**
* \brief Get streamed or covariance matrix information in file
*
* \param dimensionSizes
* \param labels reference to fill with file channel names
*
* \retval true in case of success
* \retval false in case of error
*/
virtual bool getStreamedMatrixInformation(std::vector<size_t>& dimensionSizes, std::vector<std::string>& labels) = 0;
/**
* \brief Write the header if available in the file
*
* \retval true in case of writing header
* \retval false in case of already written header
*/
virtual bool writeHeaderToFile() = 0;
/**
* \brief Write the matrix data in the opened file
*
* \retval true in case of success
* \return false in case of error while writing
*/
virtual bool writeDataToFile() = 0;
/**
* \brief Write all the remaining data
*
* \retval true in case of sucess
* \retval false in case of error while writing
*/
virtual bool writeAllDataToFile() = 0;
/**
* \brief Read samples and events on the specified number of chunks
*
* \param chunksToRead number of chunks to read
* \param samples structures reference to put the data in
* \param events reference to a vector of event structure to put the data in
*
* \retval true in case of sucess
* \retval false in case of error while writing
*/
virtual bool readSamplesAndEventsFromFile(size_t chunksToRead, std::vector<SMatrixChunk>& samples, std::vector<SStimulationChunk>& events) = 0;
/**
* \brief Open file specified on parameter
*
* \param filename is the filename of the file to open
* \param mode
*
* \retval true in case of success
* \retval false in case of error while opening the file
*/
virtual bool openFile(const std::string& filename, EFileAccessMode mode) = 0;
/**
* \brief close the opened file
*
* \retval true in case of sucess
* \retval false in case of error while closing
*/
virtual bool closeFile() = 0;
/**
* \brief Add a sample of the data type
*
* \param sample is the sample to add
*
* \retval true in case of correct sample
* \retval false in case of empty or incorrect sample
*/
virtual bool addSample(const SMatrixChunk& sample) = 0;
/**
* \brief Add a buffer (multiple samples) of the data type.
*
* \param samples to add
*
* \retval true in case of success
* \retval false in case of error in the added sample
*/
virtual bool addBuffer(const std::vector<SMatrixChunk>& samples) = 0;
/**
* \brief Add an event
*
* \param code is the event identifier
* \param date in seconds (must be positive)
* \param duration in seconds (must be positive)
*
* \retval true in case of correct event
* \retval false in case of incorrect param
*/
virtual bool addEvent(uint64_t code, double date, double duration) = 0;
/**
* \brief Add an event
*
* \param event is the event to add (with identifier, data and duration)
*
* \retval true in case of success
* \retval false in case of incorrect event
*/
virtual bool addEvent(const SStimulationChunk& event) = 0;
/**
* \brief Set the time of the next stimulation
*
* \param date the date of the next stimulation
*
* \retval true in case of success
* \retval false in case of negative date
*/
virtual bool noEventsUntilDate(double date) = 0;
/**
* \brief Return the error last log error.
*
* \return last log error code
*/
virtual ELogErrorCodes getLastLogError() = 0;
/**
* \brief Return the error associated to the code in parameter.
*
* \param code error code
*
* \return error string
*/
static std::string getLogError(ELogErrorCodes code);
/**
* \brief Return additionnal information on the error in certain cases
*
* \return string additionnal information
*/
virtual std::string getLastErrorString() = 0;
virtual bool hasDataToRead() const = 0;
protected:
virtual ~ICSVHandler() {}
};
extern CSV_API ICSVHandler* createCSVHandler();
extern CSV_API void releaseCSVHandler(ICSVHandler* object);
} // namespace CSV
} // namespace OpenViBE
File diff suppressed because it is too large Load Diff
+355
View File
@@ -0,0 +1,355 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <ovICSV.h>
#include <fstream>
#include <string>
#include <vector>
namespace OpenViBE {
namespace CSV {
class CCSVHandler final : public ICSVHandler
{
public:
/**
* \brief Set lib value to default
*/
CCSVHandler() : m_inputTypeID(EStreamType::StreamedMatrix), m_dimSizes({}) {}
/**
* \brief Close the file if it is open.
*/
~CCSVHandler() override { this->closeFile(); }
/**
* \brief Get the floating point precision used to write float values.
*
* \return the Floating point precision.
*/
size_t getOutputFloatPrecision() override { return m_oPrecision; }
/**
* \brief Set the floating point precision used to write float values.
*
* \param precision the floating point precision.
*/
void setOutputFloatPrecision(const size_t precision) override { m_oPrecision = precision; }
void setFormatType(const EStreamType typeID) override;
EStreamType getFormatType() override { return m_inputTypeID; }
void setLastMatrixOnlyMode(const bool isActivated) override { m_lastMatrixOnly = isActivated; }
bool getLastMatrixOnlyMode() override { return m_lastMatrixOnly; }
bool setSignalInformation(const std::vector<std::string>& channelNames, size_t sampling, size_t nSamplePerBuffer) override;
bool getSignalInformation(std::vector<std::string>& channelNames, size_t& sampling, size_t& nSamplePerBuffer) override;
bool setSpectrumInformation(const std::vector<std::string>& channelNames, const std::vector<double>& frequencyAbscissa, size_t sampling) override;
bool getSpectrumInformation(std::vector<std::string>& channelNames, std::vector<double>& frequencyAbscissa, size_t& sampling) override;
bool setFeatureVectorInformation(const std::vector<std::string>& channelNames) override;
bool getFeatureVectorInformation(std::vector<std::string>& channelNames) override;
bool setStreamedMatrixInformation(const std::vector<size_t>& dimSizes, const std::vector<std::string>& labels) override;
bool getStreamedMatrixInformation(std::vector<size_t>& dimSizes, std::vector<std::string>& labels) override;
/**
* \brief Write the header to the file
*
* \retval True in case of success.
* \retval False in case of error.
*/
bool writeHeaderToFile() override;
/**
* \brief Write current available data to the file until the last stimulation or if you set that it will not have new event before a date.
*
* \retval True in case of success.
* \retval False in case of error.
*
* \sa noEventsUntilDate
*/
bool writeDataToFile() override;
/**
* \brief Write current available data to the file.
*
* \retval True in case of success.
* \retval False in case of error.
*/
bool writeAllDataToFile() override;
/**
* \brief Read samples and stimulations.
*
* \param lineNb Maximum number of lines to read. If there is no more data in the file, the number of lines read can be lower.
* \param chunks[out] Valid chunks read.
* \param stimulations[out] Valid stimulations read.
*
* \retval True in case of success, even if the number of lines is different than the lineNb parameter.
* \retval False in case of error.
*/
bool readSamplesAndEventsFromFile(size_t lineNb, std::vector<SMatrixChunk>& chunks, std::vector<SStimulationChunk>& stimulations) override;
/**
* \brief Open a OV CSV file.
*
* \retval True in case of success.
* \retval False in case of error.
*/
bool openFile(const std::string& fileName, EFileAccessMode mode) override;
/**
* \brief Close the opened file.
*
* \retval True in case of success.
* \retval False in case of error.
*/
bool closeFile() override;
/**
* \brief Add a single sample.
*
* \retval True in case of success.
* \retval False in case of error.
*/
bool addSample(const SMatrixChunk& sample) override;
/**
* \brief Add several samples.
*
* \retval True in case of success.
* \retval False in case of error.
*/
bool addBuffer(const std::vector<SMatrixChunk>& samples) override;
/**
* \brief Add a single stimulation.
*
* \retval True in case of success.
* \retval False in case of error.
*/
bool addEvent(uint64_t code, double date, double duration) override;
/**
* \brief Add several stimulations.
*
* \retval True in case of success.
* \retval False in case of error.
*/
bool addEvent(const SStimulationChunk& event) override;
/**
* \brief Guarantee that will not have new event before a date.
* This information is used to allow the library to write all the chunks available, before this date, to a file.
*
* \retval True in case of success.
* \retval False in case of error.
*
* \sa writeDataToFile
*/
bool noEventsUntilDate(double date) override;
ELogErrorCodes getLastLogError() override { return m_logError; }
std::string getLastErrorString() override { return m_lastStringError; }
/**
* \brief Check if there is still data to read in the file.
*
* \retval True if there is still data to read in the file.
* \retval False if there is no more data to read in the file.
*/
bool hasDataToRead() const override { return m_hasDataToRead; }
private:
/**
* \brief Split a string into a vector of strings.
*
* \param in String to split.
* \param delimiter Delimitor.
* \param out[out] Vector of string.
*/
void split(const std::string& in, char delimiter, std::vector<std::string>& out) const;
/**
* \brief Create a string with stimulations to add in the buffer.
*
* \param stimulationsToPrint stimulations to put into the buffer
*
* \return string with stimulations to write
*/
std::string stimulationsToString(const std::vector<SStimulationChunk>& stimulationsToPrint) const;
/**
* \brief Create a string representation of the header data.
*
* \retval true Header data as it should be written in the file
* \retval "" in case of error
*/
std::string createHeaderString();
/**
* \brief Set the buffer in function of data saved.
*
* \param canWriteAll true if it must write all lines, false if it write only the next buffer
* \param csv The CSV string.
*
* \retval true in case of success
* \retval false in case of wrong data sent
*/
bool createCSVStringFromData(bool canWriteAll, std::string& csv);
/**
* \brief Parsing header to read data.
*
* \retval true in case of correct header
* \retval false in case of incorrect header
*/
bool parseHeader();
/**
* \brief Parsing header to read signal data.
*
* \param header[out]
*
* \retval true in case of correct header
* \retval false in case of incorrect header
*/
bool parseSignalHeader(const std::vector<std::string>& header);
/**
* \brief Parsing header to read Spectrum data.
*
* \param header[out]
*
* \retval true in case of correct header
* \retval false in case of incorrect header
*/
bool parseSpectrumHeader(const std::vector<std::string>& header);
/**
* \brief Parsing header to read matrix data (Streamed Matrix, Covariance matrix and Feature Vector).
*
* \param header[out]
*
* \retval true in case of correct header
* \retval false in case of incorrect header
*/
bool parseMatrixHeader(const std::vector<std::string>& header);
/**
* \brief Read line data concerning time, epoch and matrix.
*
* \param line line to read
* \param sample[out] reference to stock data in
* \param lineNb index of the read line
*
* \retval true in case of success
* \retval false in case of error (as letters instead of numbers)
*/
bool readSampleChunk(const std::string& line, SMatrixChunk& sample, size_t lineNb);
/**
* \brief Read line data conerning stimulations.
*
* \param line the line to read
* \param stimulations[out] vector to stock stimulations in (identifier, date and duration)
* \param lineNb the line actually reading
*
* \retval true in case of success
* \retval false in case of error (as letters instead of numbers)
*/
bool readStimulationChunk(const std::string& line, std::vector<SStimulationChunk>& stimulations, size_t lineNb);
/**
* \brief Update position into the matrix while reading or writing.
*
* \param position[out] is the position into the matrix
*
* \retval true in case of success
* \retval false in case of browse matrix
*/
bool increasePositionIndexes(std::vector<size_t>& position);
/**
* \brief Read lines of the first epoch to found sample count per buffer.
*
* \retval true in case of success
* \retval false in case of error
*/
bool calculateSampleCountPerBuffer();
/**
* \brief Read a stream until a delimiter and provide the string before the delimiter.
*
* \param in The stream to read.
* \param out The string before the next delimitor.
* \param delimiter The delimiter .
* \param bufferHistory
*/
bool streamReader(std::istream& in, std::string& out, char delimiter, std::string& bufferHistory) const;
std::fstream m_fs;
std::string m_filename;
std::deque<SMatrixChunk> m_chunks;
std::deque<SStimulationChunk> m_stimulations;
ELogErrorCodes m_logError = LogErrorCodes_NoError;
std::string m_lastStringError = "";
EStreamType m_inputTypeID;
typedef std::istream& GetLine(std::istream& in, std::string& out, char delimiter);
size_t m_nDim = 0;
std::vector<size_t> m_dimSizes;
std::vector<std::string> m_dimLabels;
size_t m_nSamplePerBuffer = 0;
double m_noEventSince = 0;
std::vector<double> m_frequencyAbscissa;
size_t m_sampling = 0;
size_t m_nCol = 0;
bool m_hasInputType = false;
bool m_isFirstLineWritten = false;
bool m_isHeaderRead = false;
bool m_isSetInfoCalled = false;
bool m_hasEpoch = false;
size_t m_nSampleOriginal = 0;
size_t m_oPrecision = 10;
bool m_lastMatrixOnly = false;
std::string m_bufferReadFileLine; // Buffer used to store unused read chars.
bool m_hasDataToRead = true;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool m_isCRLFEOL = false; // Is a CRLF end of line
#endif
};
} // namespace CSV
} // namespace OpenViBE
@@ -0,0 +1,50 @@
#include <map>
#include <ovICSV.h>
namespace OpenViBE {
namespace CSV {
static const std::map<ELogErrorCodes, std::string> ERROR_MAP =
{
{ LogErrorCodes_ErrorWhileClosing, "Error while closing the file" },
{ LogErrorCodes_NoFileDefined, "No file defined yet" },
{ LogErrorCodes_CantOpenFile, "Could not open the file" },
{ LogErrorCodes_DurationError, "The duration can't be negative" },
{ LogErrorCodes_DateError, "The date can't be negative" },
{ LogErrorCodes_NoSample, "Their is no sample to add" },
{ LogErrorCodes_NoMatrixLabels, "No matrix labels" },
{ LogErrorCodes_WrongMatrixSize, "Matrix size is different from the initial size" },
{ LogErrorCodes_MatrixEmpty, "A matrix of the buffer is empty" },
{ LogErrorCodes_DimensionSizeZero, "Dimensions can not have size equal to 0" },
{ LogErrorCodes_DimensionSizeEmpty, "Dimension sizes vector is empty" },
{ LogErrorCodes_NoChannelsName, "No channel names in the vector" },
{ LogErrorCodes_SetInfoOnce, "The setTypeInformation function should be called only once" },
{ LogErrorCodes_WrongInputType, "Wrong input type" },
{ LogErrorCodes_WrongHeader, "Error in the header of the file" },
{ LogErrorCodes_WrongLineSize, "There is a difference between the current line size and the size that it should have" },
{ LogErrorCodes_SampleNotEmpty, "Matrix must be empty to store in results of reading" },
{ LogErrorCodes_NotEnoughLines, "Can't reach the line in parameter, not enough lines in the file" },
{ LogErrorCodes_NegativeStimulation, "Stimulations cannot have negatives values" },
{ LogErrorCodes_WrongSampleDate, "Sample dates must be correct (greater than or equal to zero, and start date less than end date)" },
{ LogErrorCodes_InvalidStimulationArgument, "Wrong stimulation entry" },
{ LogErrorCodes_StimulationSize, "Some stimulation(s) has/have incomplete parameter(s)" },
{ LogErrorCodes_EmptyColumn, "A column of the file is empty" },
{ LogErrorCodes_WrongDimensionSize, "Size of dimension sizes vector must be equal to the number of dimensions" },
{ LogErrorCodes_DimensionCountZero, "Number of dimension must be at least 1" },
{ LogErrorCodes_OutOfRangeException, "Convertion from string to number is impossible : string is out of range" },
{ LogErrorCodes_InvalidArgumentException, "An invalid argument exeption have been thrown" },
{ LogErrorCodes_CantWriteHeader, "Can not write header, data already began to be written or header already written" },
{ LogErrorCodes_ErrorWhileWriting, "Error occured while trying to write" },
{ LogErrorCodes_WrongParameters, "Error with (one of )the parameter(s)" },
{ LogErrorCodes_MissingData, "Missing data in file, file may be corrupted" }
};
std::string ICSVHandler::getLogError(const ELogErrorCodes code)
{
if (ERROR_MAP.count(code) != 0) { return ERROR_MAP.at(code); }
return "Unknow error";
}
} // namespace CSV
} // namespace OpenViBE
@@ -0,0 +1,64 @@
PROJECT(openvibe-module-date)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
SET(SRC_FILES
include/date/defines.h
include/date/DateParser.h
src/DateParser.cpp
)
IF(WIN32)
LIST(APPEND SRC_FILES
"include/date/WindowsDateParser.h"
"src/WindowsDateParser.cpp"
)
ENDIF()
INCLUDE_DIRECTORIES(include/date)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DDate_Shared -DDate_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DDate_Static -DDate_Exports")
IF(UNIX)
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF(UNIX)
INCLUDE("FindOpenViBECommon")
INCLUDE("FindThirdPartyBoost")
INCLUDE("FindThirdPartyExpat")
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.h" PATTERN)
@@ -0,0 +1,24 @@
#pragma once
#include "defines.h"
#if defined TARGET_OS_Windows
#include "WindowsDateParser.h"
#else
#include <ctime>
#endif
namespace DateParser {
class Date_API CDateParser
{
public:
/**
* \brief parse a string representing a date along a format given by a char*, and store it in a tm struct
* \param sStringToParse: the string to parse
* \param sDateFormat: the string that gives the format of the date to parse, example "%Y-%m-%d %H:%M"
* \param tmParsed: time parsed
*/
static char* strpTime(const char* sStringToParse, const char* sDateFormat, struct tm* tmParsed);
private:
CDateParser() = default;
};
} // namespace DateParser
@@ -0,0 +1,7 @@
#pragma once
#include "defines.h"
#include <ctime>
namespace DateParser {
extern Date_API char* windowsStrptime(const char* buf, const char* fmt, struct tm* tmParsed);
} // namespace DateParser
@@ -0,0 +1,25 @@
#pragma once
#include <ov_common_defines.h>
#if defined Date_Shared
# if defined TARGET_OS_Windows
# define Date_API_Export __declspec(dllexport)
# define Date_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux
# define Date_API_Export __attribute__((visibility("default")))
# define Date_API_Import __attribute__((visibility("default")))
# else
# define Date_API_Export
# define Date_API_Import
# endif
#else
# define Date_API_Export
# define Date_API_Import
#endif
#if defined Date_Exports
# define Date_API Date_API_Export
#else
# define Date_API Date_API_Import
#endif
@@ -0,0 +1,14 @@
#include "DateParser.h"
namespace DateParser {
char* CDateParser::strpTime(const char* sStringToParse, const char* sDateFormat, struct tm* tmParsed)
{
#if defined TARGET_OS_Windows
return windowsStrptime(sStringToParse, sDateFormat, tmParsed);
#else
return strptime(sStringToParse, sDateFormat, tmParsed);
#endif
}
} // namespace DateParser
@@ -0,0 +1,357 @@
/* $Id$ */
/* $NetBSD: windowsStrptime.c,v 1.18 1999/04/29 02:58:30 tv Exp $ */
/*-
* Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code was contributed to The NetBSD Foundation by Klaus Klein.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <array>
#if defined TARGET_OS_Windows
#include <iostream>
#include "WindowsDateParser.h"
namespace DateParser {
/*
* We do not implement alternate representations. However, we always
* check whether a given modifier is allowed for a certain conversion.
*/
#define ALT_E 0x01
#define ALT_O 0x02
#define LEGAL_ALT(x) if (alt_format & ~(x)) { return 0; }
#define EPOCH_YEAR 1970
#define SECSPERHOUR 3600
#define SECSPERDAY 86400
#define TM_YEAR_BASE 1900
static int ConvNum(const char** buf, int* dest, const int llim, const int ulim);
static const std::array<const std::string, 7> DAY = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
static const std::array<const std::string, 7> AB_DAY = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const std::array<const std::string, 12> MON = {
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
};
static const std::array<const std::string, 12> AB_MON = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static const std::array<const std::string, 2> AM_PM = { "AM", "PM" };
Date_API char* DateParser::windowsStrptime(const char* buf, const char* fmt, struct tm* tmParsed)
{
char c;
const char* bp;
size_t len = 0;
int i, split_year = 0;
bp = buf;
while ((c = *fmt) != '\0')
{
/* Clear `alternate' modifier prior to new conversion. */
int alt_format = 0;
/* Eat up white-space. */
if (isspace(c))
{
while (isspace(*bp)) { bp++; }
fmt++;
continue;
}
if ((c = *fmt++) != '%') { goto literal; }
again: switch (c = *fmt++)
{
case '%': /* "%%" is converted to "%". */
literal :
if (c != *bp++) { return (nullptr); }
break;
/*
* "Alternative" modifiers. Just set the appropriate flag
* and start over again.
*/
case 'E': /* "%E?" alternative conversion modifier. */
LEGAL_ALT(0);
alt_format |= ALT_E;
goto again;
case 'O': /* "%O?" alternative conversion modifier. */
LEGAL_ALT(0);
alt_format |= ALT_O;
goto again;
/*
* "Complex" conversion rules, implemented through recursion.
*/
case 'c': /* Date and time, using the locale's format. */
LEGAL_ALT(ALT_E);
if (!((bp = windowsStrptime(bp, "%x %X", tmParsed)))) { return nullptr; }
break;
case 'D': /* The date as "%m/%d/%y". */
LEGAL_ALT(0);
if (!((bp = windowsStrptime(bp, "%m/%d/%y", tmParsed)))) { return nullptr; }
break;
case 'R': /* The time as "%H:%M". */
LEGAL_ALT(0);
if (!((bp = windowsStrptime(bp, "%H:%M", tmParsed)))) { return nullptr; }
break;
case 'r': /* The time in 12-hour clock representation. */
LEGAL_ALT(0);
if (!((bp = windowsStrptime(bp, "%I:%M:%S %p", tmParsed)))) { return nullptr; }
break;
case 'T': /* The time as "%H:%M:%S". */
LEGAL_ALT(0);
if (!((bp = windowsStrptime(bp, "%H:%M:%S", tmParsed)))) { return nullptr; }
break;
case 'X': /* The time, using the locale's format. */
LEGAL_ALT(ALT_E);
if (!((bp = windowsStrptime(bp, "%H:%M:%S", tmParsed)))) { return nullptr; }
break;
case 'x': /* The date, using the locale's format. */
LEGAL_ALT(ALT_E);
if (!((bp = windowsStrptime(bp, "%m/%d/%y", tmParsed)))) { return nullptr; }
break;
/*
* "Elementary" conversion rules.
*/
case 'A': /* The day of week, using the locale's form. */
case 'a':
LEGAL_ALT(0);
for (i = 0; i < 7; ++i)
{
/* Full name. */
len = DAY[i].size();
if (strncmp(DAY[i].c_str(), bp, len) == 0) { break; }
/* Abbreviated name. */
len = AB_DAY[i].size();
if (strncmp(AB_DAY[i].c_str(), bp, len) == 0) { break; }
}
/* Nothing matched. */
if (i == 7) { return nullptr; }
tmParsed->tm_wday = i;
bp += len;
break;
case 'B': /* The month, using the locale's form. */
case 'b':
case 'h':
LEGAL_ALT(0);
for (i = 0; i < 12; ++i)
{
/* Full name. */
len = MON[i].size();
if (strncmp(MON[i].c_str(), bp, len) == 0) { break; }
/* Abbreviated name. */
len = AB_MON[i].size();
if (strncmp(AB_MON[i].c_str(), bp, len) == 0) { break; }
}
/* Nothing matched. */
if (i == 12) { return (nullptr); }
tmParsed->tm_mon = i;
bp += len;
break;
case 'C': /* The century number. */
LEGAL_ALT(ALT_E);
if (!(ConvNum(&bp, &i, 0, 99))) { return (nullptr); }
if (split_year) { tmParsed->tm_year = (tmParsed->tm_year % 100) + (i * 100); }
else
{
tmParsed->tm_year = i * 100;
split_year = 1;
}
break;
case 'd': /* The day of month. */
case 'e':
LEGAL_ALT(ALT_O);
if (!(ConvNum(&bp, &tmParsed->tm_mday, 1, 31))) { return (nullptr); }
break;
case 'k': /* The hour (24-hour clock representation). */
LEGAL_ALT(0);
/* FALLTHROUGH */
case 'H':
LEGAL_ALT(ALT_O);
if (!(ConvNum(&bp, &tmParsed->tm_hour, 0, 23))) { return (nullptr); }
break;
case 'l': /* The hour (12-hour clock representation). */
LEGAL_ALT(0);
/* FALLTHROUGH */
case 'I':
LEGAL_ALT(ALT_O);
if (!(ConvNum(&bp, &tmParsed->tm_hour, 1, 12))) { return (nullptr); }
if (tmParsed->tm_hour == 12) { tmParsed->tm_hour = 0; }
break;
case 'j': /* The day of year. */
LEGAL_ALT(0);
if (!(ConvNum(&bp, &i, 1, 366))) { return (nullptr); }
tmParsed->tm_yday = i - 1;
break;
case 'M': /* The minute. */
LEGAL_ALT(ALT_O);
if (!(ConvNum(&bp, &tmParsed->tm_min, 0, 59))) { return (nullptr); }
break;
case 'm': /* The month. */
LEGAL_ALT(ALT_O);
if (!(ConvNum(&bp, &i, 1, 12))) { return (nullptr); }
tmParsed->tm_mon = i - 1;
break;
case 'p': /* The locale's equivalent of AM/PM. */
LEGAL_ALT(0);
/* AM? */
if (strcmp(AM_PM[0].c_str(), bp) == 0)
{
if (tmParsed->tm_hour > 11) { return (nullptr); }
bp += AM_PM[0].size();
break;
}
/* PM? */
if (strcmp(AM_PM[1].c_str(), bp) == 0)
{
if (tmParsed->tm_hour > 11) { return (nullptr); }
tmParsed->tm_hour += 12;
bp += AM_PM[1].size();
break;
}
/* Nothing matched. */
return nullptr;
case 'S': /* The seconds. */
LEGAL_ALT(ALT_O);
if (!(ConvNum(&bp, &tmParsed->tm_sec, 0, 61))) { return (nullptr); }
break;
case 'U': /* The week of year, beginning on sunday. */
case 'W': /* The week of year, beginning on monday. */
LEGAL_ALT(ALT_O);
/*
* XXX This is bogus, as we can not assume any valid
* information present in the tm structure at this
* point to calculate a real value, so just check the
* range for now.
*/
if (!(ConvNum(&bp, &i, 0, 53))) { return (nullptr); }
break;
case 'w': /* The day of week, beginning on sunday. */
LEGAL_ALT(ALT_O);
if (!(ConvNum(&bp, &tmParsed->tm_wday, 0, 6))) { return (nullptr); }
break;
case 'Y': /* The year. */
LEGAL_ALT(ALT_E);
if (!(ConvNum(&bp, &i, 0, 9999))) { return (nullptr); }
tmParsed->tm_year = i - TM_YEAR_BASE;
break;
case 'y': /* The year within 100 years of the epoch. */
LEGAL_ALT(ALT_E | ALT_O);
if (!(ConvNum(&bp, &i, 0, 99))) { return (nullptr); }
if (split_year)
{
tmParsed->tm_year = ((tmParsed->tm_year / 100) * 100) + i;
break;
}
split_year = 1;
if (i <= 68) { tmParsed->tm_year = i + 2000 - TM_YEAR_BASE; }
else { tmParsed->tm_year = i + 1900 - TM_YEAR_BASE; }
break;
/*
* Miscellaneous conversions.
*/
case 'n': /* Any kind of white-space. */
case 't':
LEGAL_ALT(0);
while (isspace(*bp)) { bp++; }
break;
default: return nullptr; /* Unknown/unsupported conversion. */
}
}
/* LINTED functional specification */
return const_cast<char*>(bp);
}
static int ConvNum(const char** buf, int* dest, const int llim, const int ulim)
{
int result = 0;
/* The limit also determines the number of valid digits. */
int rulim = ulim;
if (**buf < '0' || **buf > '9') { return (0); }
do
{
result *= 10;
result += *(*buf)++ - '0';
rulim /= 10;
} while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
if (result < llim || result > ulim) { return 0; }
*dest = result;
return 1;
}
} // namespace DateParser
#endif
@@ -0,0 +1,51 @@
PROJECT(openvibe-module-ebml)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl include/*.h)
INCLUDE_DIRECTORIES(include)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DEBML_Shared -DEBML_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DEBML_Static -DEBML_Exports")
IF(UNIX)
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF(UNIX)
INCLUDE("FindOpenViBECommon")
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
@@ -0,0 +1,95 @@
/**
* \page Doc_FormatingEBMLStreams Formating EBML streams
*
* \section Doc_FormatingEBMLStreams_Introduction Introduction
*
* This page tries to teach how a user of this library should use
* the writing functions in order to build and format an EBML
* stream correctly. For those who don't know what EBML is,
* it is basically a binary XML created and used by the matroska
* developpers. The libmatroska is based on the libebml
* implementation of these guys. Fore more details, please read
* the \ref Doc_WhatIsEBML page first and eventually visit the
* EBML web page at http://ebml.sourceforge.net/
*
* \section Doc_FormatingEBMLStreams_Concept Concepts
*
* The idea of this formating library is to transform the data
* provided by the user application into EBML buffers. Those EBML
* buffers are then given to a user callback object which know
* what to do with the computed data (maybe write it to a file,
* send it on a socket, log it in a console, whatever...)
*
* The library is divided into three main components :
* - The writer itself that does the formating stuff
* - An implementation of the callback object (the implementation
* is left to the user application developper)
* - An optionnal helper object that knows more on the
* content of the EBML stream.
*
* Here comes the organisation of the different modules and how
* data go from one to another. Note that the user application
* and the user callback object may share some information so the
* callback object communicates with the application itself.
*
* \image html ebml_formating_concept.png "Concept"
*
* Here comes the UML class diagram, presenting the main classes
* involved in the presented behavior.
*
* \image html ebml_formating_class.png "Class Diagram"
*
* See EBML::IWriter, EBML::IWriterCallback and EBML::IWriterHelper
* for more details on each of these classes.
*
* \section Doc_FormatingEBMLStreams_SampleCode Sample code
*
* In this section, a sample of user application code is presented
* that opens several child nodes and dumps the created stream
* into a file for later use. This file can be parsed using
* the sample code of the page named \ref Doc_ParsingEBMLStreams
*
* The callback object implementation looks something like this :
*
* \code
* class CWriterCallback : public EBML::IWriterCallback
* {
* public:
* CWriterCallback(char* filename) { m_file=fopen(filename, "wb"); }
* virtual ~CWriterCallback() { if(m_file) fclose(m_file); }
* virtual void write(const void* buffer, const size_t size) { if(m_file) fwrite(buffer, size, 1, m_file); }
* FILE* m_file;
* };
* \endcode
*
* Then in the user application code, we can write the
* initialisation this way :
*
* \code
* CWriterCallback oCallback("test.ebml");
* EBML::IWriter* pWriter=EBML::createWriter(oCallback);
* EBML::IWriterHelper* pWriterHelper=EBML::createWriterHelper();
* pWriterHelper->connect(pWriter);
* \endcode
*
* The use of the EBML writer looks something like this :
*
* \code
* pWriterHelper->openChild(EBML_Identifier_Header);
* pWriterHelper->openChild(EBML_Identifier_DocType);
* pWriterHelper->setStr("EBML basic sample");
* pWriterHelper->closeChild();
* pWriterHelper->openChild(EBML_Identifier_DocTypeVersion);
* pWriterHelper->setUInt(1);
* pWriterHelper->closeChild();
* pWriterHelper->closeChild();
* \endcode
*
* Finally, we have to release the objects and to clean memory :
*
* \code
* pWriterHelper->disconnect();
* pWriterHelper->release();
* pWriter->release();
* \endcode
*/
@@ -0,0 +1,129 @@
/**
* \page Doc_ParsingEBMLStreams Parsing EBML streams
*
* \section Doc_ParsingEBMLStreams_Introduction Introduction
*
* This page tries to teach how a user of this library should use
* the reading functions in order to read and parse an EBML
* stream correctly. For those who don't know what EBML is,
* it is basically a binary XML created and used by the matroska
* developpers. The libmatroska is based on the libebml
* implementation of these guys. Fore more details, please read
* the \ref Doc_WhatIsEBML page first and eventually visit the
* EBML web page at http://ebml.sourceforge.net/
*
* \section Doc_ParsingEBMLStreams_Concept Concepts
*
* The idea of this parsing library is to transform the stream
* data provided by the user application into understandable
* EBML interpreted commands. Once the EBML nodes are found and
* parsed, they are sent to a callback object that should know
* what to do with them.
*
* The design of the parsing interface is closed to the one of
* \c eXpat, an XML parser library (see http://expat.sourceforge.net
* for more details on \c eXpat). Using such interface allows light
* code and on-the-fly parsing, that means the parser does not
* need to have all the data ready before starting the parsing
* process... The data can arrive while the parsing is beeing
* done.
*
* It is the responsability of the user application to read
* the EBML stream from a file, a socket, a user input or
* whatever, and then to send this to the parser...
*
* At least, the callback object may use a reader helper that
* knows how to read standard EBML types such as integers,
* floats, strings etc...
*
* The library is divided into three main components :
* - The reader itself that does the parsing stuffs
* - An implementation of the callback object (the implementation
* is left to the user application developper)
* - An optionnal helper object that knows more on the
* content of the EBML stream.
*
* Here comes the organisation of the different modules and how
* data go from one to another. Note that the user application
* and the user callback object may share some information so the
* callback object communicates with the application itself.
*
* \image html ebml_parsing_concept.png "Concept"
*
* Here comes the UML class diagram, presenting the main classes
* involved in the presented behavior.
*
* \image html ebml_parsing_class.png "Class Diagram"
*
* See EBML::IReader, EBML::IReaderCallback and EBML::IReaderHelper
* for more details on each of these classes.
*
* \section Doc_ParsingEBMLStreams_SampleCode Sample code
*
* In this section, a sample of user application code is presented
* that parses the sample file created in the page named :
* \ref Doc_FormatingEBMLStreams
*
* The parsed value are printed in the console.
*
* The callback object looks something like this :
*
* \code
* class CReaderCallback : public EBML::IReaderCallback
* {
* public:
* CReaderCallback()
* {
* m_helper=EBML::createReaderHelper();
* }
* virtual ~CReaderCallback()
* {
* if(m_helper) m_helper->release();
* }
* virtual bool isMasterChild(const EBML::CIdentifier& identifier)
* {
* if(identifier==EBML_Identifier_Header) return true;
* if(identifier==EBML_Identifier_DocType) return true;
* if(identifier==EBML_Identifier_DocTypeVersion) return true;
* return false;
* }
* virtual void openChild(const EBML::CIdentifier& identifier)
* {
* m_oCurrent=identifier;
* }
* virtual void processChildData(const void* buffer, const size_t size)
* {
* if(m_oCurrent==EBML_Identifier_DocType)
* std::cout << "Doc type:" << m_helper->getStr(buffer, size) << std::endl;
* if(m_oCurrent==EBML_Identifier_DocTypeVersion)
* std::cout << "Dox type version:" << m_helper->getUInt(buffer, size) << std::endl;
* }
* virtual void closeChild()
* {
* }
* EBML::IReaderHelper* m_helper;
* };
* \endcode
*
* Then in the user application code, we can write the
* initialisation this way :
*
* \code
* CReaderCallback oCallback;
* EBML::IReader* pReader=EBML::createReader(oCallback);
* \endcode
*
* Now suppose the user application got some data from the file
* in a \c buffer of size \c size ; it is sent to the
* parser this way :
*
* \code
* pReader->processData(buffer, size);
* \endcode
*
* Finally, don't forget to release the object and to clean memory :
*
* \code
* pReader->release();
* \endcode
*/
@@ -0,0 +1,15 @@
/**
* \page Doc_WhatIsEBML What is EBML
*
* This page tries to explain what EBML is and why it is usefull.
*
* - see http://ebml.sourceforge.net/ for more details
* - see http://ebml.sourceforge.net/specs/ for specifications
* - see http://www.matroska.org/technical/specs/ for even more details
*
* - check libebml for a sample implementation of EBML
* - check libmatroska for a sample of library using libebml
* and a concrete use of EBML
*
* \todo Write more documentation on what EBML is
*/
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@@ -0,0 +1,151 @@
#pragma once
#include "defines.h"
#include <climits>
namespace EBML {
/**
* \class CIdentifier
* \author Yann Renard (INRIA/IRISA)
* \date 2006-08-07
* \brief Base class to work with EBML identifiers
*
* This class is used in order to work on EBML identifier.
* It handles 64 bits values for now, but could easily be
* changed to handle bigger values if needed.
*
* Be sure to look at http://ebml.sourceforge.net/specs/ in
* order to understand what an identifier is and how it works
*/
class EBML_API CIdentifier
{
public:
/** \name Constructors */
//@{
/**
* \brief Basic constructor
*
* Initializes the identifier to 0.
*/
CIdentifier() : m_id(ULLONG_MAX) {}
/**
* \brief Integer based constructor
* \param id [in] : The value to use
*
* Initializes the identifier to the given 64 bits value.
*/
CIdentifier(const uint64_t id) : m_id(id) {}
/**
* \brief 32 bits integer based constructor
* \param id1 [in] : the first part of the identifier
* \param id2 [in] : the second part of the identifier
*
* Builds up the 64 bits identifier given its two 32 bits
* components.
*/
CIdentifier(const size_t id1, const size_t id2) : m_id((uint64_t(id1) << 32) + id2) {}
/**
* \brief Copy constructor
* \param id [in] : The source identifier to use
*
* Initializes this identifier to the same value as
* the given source identifier.
*/
CIdentifier(const CIdentifier& id) : m_id(id.m_id) {}
//@}
/** \name Operators */
//@{
/**
* \brief Copy operator
* \param id [in] : The source identifier to copy from
* \return a const reference on this identifier
*
* Initializes this identifier to the same value as
* the given source identifier.
*/
const CIdentifier& operator=(const CIdentifier& id);
/**
* \brief Equality comparison operator
* \param id1 [in] : The first identifier to compare
* \param id2 [in] : The second identifier to compare
* \return \e true when the identifiers are equal, \e false
* when they are different.
*
* This function compares the two 64 bits values.
*/
friend EBML_API bool operator==(const CIdentifier& id1, const CIdentifier& id2);
/**
* \brief Difference comparison operator
* \param id1 [in] : The first identifier to compare
* \param id2 [in] : The second identifier to compare
* \return \e true when the identifiers are different, \e false
* when they are equal.
*
* This function compares the two 64 bits values.
*/
friend EBML_API bool operator!=(const CIdentifier& id1, const CIdentifier& id2);
/**
* \brief Ordering comparison operator
* \param id1 [in] : The first identifier to compare
* \param id2 [in] : The second identifier to compare
* \return \e true when the first identifier is lesser or equal
* to the second identifier, \e false in other cases.
*
* This function compares the two 64 bits values.
*/
friend EBML_API bool operator<=(const CIdentifier& id1, const CIdentifier& id2);
/**
* \brief Ordering comparison operator
* \param id1 [in] : The first identifier to compare
* \param id2 [in] : The second identifier to compare
* \return \e true when the first identifier is greater or equal
* to the second identifier, \e false in other cases.
*
* This function compares the two 64 bits values.
*/
friend EBML_API bool operator>=(const CIdentifier& id1, const CIdentifier& id2);
/**
* \brief Ordering comparison operator
* \param id1 [in] : The first identifier to compare
* \param id2 [in] : The second identifier to compare
* \return \e true when the first identifier is lesser and not equal
* to the second identifier, \e false in other cases.
*
* This function compares the two 64 bits values.
*/
friend EBML_API bool operator<(const CIdentifier& id1, const CIdentifier& id2);
/**
* \brief Ordering comparison operator
* \param id1 [in] : The first identifier to compare
* \param id2 [in] : The second identifier to compare
* \return \e true when the first identifier is greater and not equal
* to the second identifier, \e false in other cases.
*
* This function compares the two 64 bits values.
*/
friend EBML_API bool operator>(const CIdentifier& id1, const CIdentifier& id2);
/**
* \brief Cast operator
* \return \e the 64 bits value contained by this identifier.
*/
operator uint64_t() const { return this->id(); }
/**
* \brief Conversion to 64 bits uint32_t (should be used instead of the cast)
* \return \e the 64 bits value contained by this identifier.
*/
uint64_t id() const { return m_id; }
//@}
protected:
uint64_t m_id = 0; ///< The 64 bits value of this identifier
};
} // namespace EBML
@@ -0,0 +1,25 @@
#pragma once
#include "IReader.h"
namespace EBML {
class EBML_API CReader final : public IReader
{
public:
explicit CReader(IReaderCallback& callback);
~CReader() override;
bool processData(const void* buffer, const size_t size) override;
CIdentifier getCurrentNodeID() const override;
size_t getCurrentNodeSize() const override;
void release() override;
protected:
IReader* m_impl = nullptr;
private:
CReader() = delete;
};
} // namespace EBML
@@ -0,0 +1,22 @@
#pragma once
#include "IReaderHelper.h"
namespace EBML {
class EBML_API CReaderHelper final : public IReaderHelper
{
public:
CReaderHelper();
~CReaderHelper() override;
uint64_t getUInt(const void* buffer, const size_t size) override;
int64_t getInt(const void* buffer, const size_t size) override;
double getDouble(const void* buffer, const size_t size) override;
const char* getStr(const void* buffer, const size_t size) override;
void release() override;
protected:
IReaderHelper* m_impl = nullptr;
};
} // namespace EBML
@@ -0,0 +1,25 @@
#pragma once
#include "IWriter.h"
namespace EBML {
class EBML_API CWriter final : public IWriter
{
public:
explicit CWriter(IWriterCallback& callback);
~CWriter() override;
bool openChild(const CIdentifier& identifier) override;
bool setChildData(const void* buffer, const size_t size) override;
bool closeChild() override;
void release() override;
protected:
IWriter* m_impl = nullptr;
private:
CWriter() = delete;
};
} // namespace EBML
@@ -0,0 +1,28 @@
#pragma once
#include "IWriterHelper.h"
namespace EBML {
class EBML_API CWriterHelper final : public IWriterHelper
{
public:
CWriterHelper();
~CWriterHelper() override;
bool connect(IWriter* writer) override;
bool disconnect() override;
bool openChild(const CIdentifier& identifier) override;
bool closeChild() override;
bool setInt(const int64_t value) override;
bool setUInt(const uint64_t value) override;
bool setFloat(const float value) override;
bool setDouble(const double value) override;
bool setBinary(const void* buffer, const size_t size) override;
bool setStr(const char* value) override;
void release() override { }
protected:
IWriterHelper* m_impl = nullptr;
};
} // namespace EBML
@@ -0,0 +1,135 @@
#pragma once
#include "CIdentifier.h"
#include <cstdlib> // fix Unix compatibility
namespace EBML {
/**
* \class IReaderCallback
* \author Yann Renard (INRIA/IRISA)
* \date 2006-08-07
* \brief Callback class to use when parsing the EBML stream
*
* This class is to be overloaded by the user in order to get rid of the parsing events. It will be notified
* by the EBML::IReader object of what is found in the stream.
*
* \sa EBML::IReader
*/
class EBML_API IReaderCallback
{
public:
/**
* \brief Virtual destructor
*/
virtual ~IReaderCallback() { }
/**
* \brief Kind of child querry
* \param id [in] : The id which type has to be known
* \return \e true when the node is a master node
* \return \e false when it is a simple child node.
*
* When called by the reader, this function should tell it whether the node identified by \c id is a master
* node (has child) or not (has data). For that, it has to return \e true when the node should have children, and \e false in other cases.
*/
virtual bool isMasterChild(const CIdentifier& id) = 0;
/**
* \brief Informs the callback object a new node parsing is starting
* \param id [in] : The idenfier of the newly parsing node
*
* This is called to notify the callback object that the parser has started a new EBML node parsing. This EBML
* node is identified by \c id. After this call will follow whether a new \c openChild if this node is
* a master one, whher a processData if this node is a simple child one.
*/
virtual void openChild(const CIdentifier& id) = 0;
/**
* \brief Gives the callback object the data associated with the currently opened child node
* \param buffer [in] : The buffer corresponding to the current simple child node
* \param size [in] : The buffer size in bytes
*
* This function is called when the data reading is terminated for a simple child node and so the callback
* object can process it. Whether the callback object knows how to process the data, whether it requests
* a reader helper object to do the work... See EBML::IReaderHelper for more details on that subject.
*/
virtual void processChildData(const void* buffer, const size_t size) = 0;
/**
* \brief Informs the callback object the current node parsing is terminated
*
* This function tells the callback object the current node parsing is terminated. Thus the parsing continues for the
* top level node, whether opening a new child or closing itself and so on...
*/
virtual void closeChild() = 0;
};
class EBML_API IReaderCallBack : public IReaderCallback { };
/**
* \class IReader
* \author Yann Renard (INRIA/IRISA)
* \date 2006-08-07
* \brief EBML processing class
*
* This class should be used in order to parse an EBML stream. It should be given a callback object that can handle the
* content of the stream. The parser itself as no understanding of what is included in the EBML structure.
*
* The parsing interface looks like the eXpat XML parser, allowing on-the-fly parsing. See http://expat.sourceforge.net for more details.
*
* To create instances of this class, the user has to call EBML::createReader. To delete instances of this class, the user has to call EBML::IReader::release.
*
* Be sure to look at http://ebml.sourceforge.net/specs/ in order to understand what EBML is and how it should be used.
*/
class EBML_API IReader
{
public:
/**
* \brief Processes a chunk of data
* \param buffer [in] : The buffer of data to process
* \param size [in] : The buffer size in bytes
* \return \e true when data processing was ok
* \return \e false in other cases.
*
* This function has to be called as soon as the stream reader (file, socket, whatever) has data. Those data
* are sent to the parser and parsing for this chunk is started immediatly. The callback object is called
* according to the EBML structure contained in the chunk and the previously parsed chunks. See
* EBML::IReaderCallback for more details on the callback object.
*/
virtual bool processData(const void* buffer, const size_t size) = 0;
/**
* \brief Gets the identifier of the current node
* \return the identifier of the current node
*/
virtual CIdentifier getCurrentNodeID() const = 0;
/**
* \brief Gets the size of the current node
* \return the size of the current node
*/
virtual size_t getCurrentNodeSize() const = 0;
/**
* \brief Tells this object it won't be used anymore
*
* Instances of this class can not be instanciated another way than calling \c createReader. They can
* not be deleted either because the destructor is. protected. The library knows how to create and
* delete an instance of this class... Calling \c release will simply delete this instance and
* handle necessary cleanings when needed.
*
* The current object is invalid after calling this function. It can not be used anymore.
*/
virtual void release() = 0;
protected:
/**
* \brief Virtual destructor - should be overloaded
*/
virtual ~IReader() { }
};
/**
* \brief Instanciation function for EBML reader objects
* \param callback [in] : The callback object the reader should use
* \return a pointer to the created instance on success.
* \return \c NULL when something went wrong.
*/
extern EBML_API IReader* createReader(IReaderCallback& callback);
} // namespace EBML
@@ -0,0 +1,93 @@
#pragma once
#include "IReader.h"
namespace EBML {
/**
* \class IReaderHelper
* \author Yann Renard (INRIA/IRISA)
* \date 2006-08-07
* \brief Helper to read basic EBML types
*
* This class may be used by a IReaderCallback instance
* in order to correctly parse simple types defined in the
* EBML description such as integers, floats, strings etc...
*
* A similar class exists to help formating process...
* See EBML::IWriterHelper for more details.
*
* Be sure to look at http://ebml.sourceforge.net/specs/ in
* order to understand what EBML is and how it should be used.
*
* \todo long double reading implementation
* \todo date reading implementation
* \todo utf8 string reading implementation
*/
class EBML_API IReaderHelper
{
public:
/**
* \name Standard EBML value reading
* \param buffer [in] : The buffer containing data
* \param size [in] : The buffer size in bytes
* \return The value contained in the buffer
*
* Be sure to look at http://ebml.sourceforge.net/specs/ in
* order to understand standard EBML types.
*/
//@{
/**
* \brief Gets an unsigned integer from the given buffer
*/
virtual uint64_t getUInt(const void* buffer, const size_t size) = 0;
/**
* \brief Gets a signed integer from the given buffer
*/
virtual int64_t getInt(const void* buffer, const size_t size) = 0;
/**
* \brief Gets a float from the given buffer
*/
virtual double getDouble(const void* buffer, const size_t size) = 0;
// virtual ??? getFloat80FromChildData(const void* buffer, const size_t size)=0;
// virtual ??? getDateFromChildData(const void* buffer, const size_t size)=0;
/**
* \brief Gets an ASCII string from the given buffer
* \warning The returned value is not permanent. It should be
* copied immediatly somewhere else by the caller.
*/
virtual const char* getStr(const void* buffer, const size_t size) = 0;
// virtual ??? getUTF8StringFromChildData(const void* buffer, const size_t size)=0;
//@}
/**
* \brief Tells this object it won't be used anymore
*
* Instances of this class can not be instanciated
* another way than calling \c createReaderHelper. They
* can not be deleted either because the destructor is.
* protected. The library knows how to create and
* delete an instance of this class... Calling
* \c release will simply delete this instance and
* handle necessary cleanings when needed.
*
* The current object is invalid after calling this
* function. It can not be used anymore.
*/
virtual void release() = 0;
protected:
/**
* \brief Virtual destructor - should be overloaded
*/
virtual ~IReaderHelper() { }
};
/**
* \brief Instanciation function for EBML reader helper objects
* \return a pointer to the created instance on success.
* \return \c NULL when something went wrong.
*/
extern EBML_API IReaderHelper* createReaderHelper();
} // namespace EBML
@@ -0,0 +1,127 @@
#pragma once
#include "CIdentifier.h"
#include <cstdlib> // fix Unix compatibility
namespace EBML {
/**
* \class IWriterCallback
* \author Yann Renard (INRIA/IRISA)
* \date 2006-08-07
* \brief Callback class to use when creating the EBML stream
*
* This class is to be overloaded by the user in order to get rid of the stream writing events. It will be
* notified by the EBML::IWriter object of what is to be written in the stream while the user sends information to the writer.
*
* \sa EBML::IWriter
*/
class EBML_API IWriterCallback
{
public:
/**
* \brief Virtual destructor
*/
virtual ~IWriterCallback() { }
/**
* \brief Gives the callback object a new stream chunk
* \param buffer [in] : The buffer to write in the stream
* \param size [in] : The buffer size in bytes
*
* This function tells the callback object new data are ready to send in the EBML stream. This function
* may be called while the user sends data to the writer.
*/
virtual void write(const void* buffer, const size_t size) = 0;
};
class EBML_API IWriterCallBack : public IWriterCallback { };
/**
* \class IWriter
* \author Yann Renard (INRIA/IRISA)
* \date 2006-08-07
* \brief EBML formating class
*
* This class is used in order to format datas using EBML specifications. It gives a minimalistic interface to
* allow creating new EBML nodes, and to insert data in simple child nodes.
*
* Basic usage of this class consists in :
*
* - Create child node
* - Gives information about the node :
* - Whether created node is master node (Goto 1)
* - Whether created node is simple child node (set data for this child node)
* - Close opened node
*
* The EBML::IWriterHelper class could be used in order to send EBML standard data such as integers, floats, strings, etc...
*
* To create instances of this class, the user has to call EBML::createWriter. To delete instances of this class, the user has to call EBML::IWriter::release.
*
* Be sure to look at http://ebml.sourceforge.net/specs/ in order to understand what EBML is and how it should be used.
*/
class EBML_API IWriter
{
public:
/**
* \brief Starts a new child node
* \param identifier [in] : The identifier of the new child node
* \return \e true on success.
* \return \e false on error.
*
* This function is called when the user wants to add a child node to the currently opened master node.
* A node containing data can not have child so if \c setChildData has been called before, this function returns \e false.
*
* Once the node has been opened, it should be closed calling \c closeChild.
*/
virtual bool openChild(const CIdentifier& identifier) = 0;
/**
* \brief Sets data for simple child node
* \param buffer [in] : The buffer to set as child data
* \param size [in] : The buffer size in bytes
* \return \e true on success.
* \return \e false on error.
*
* This function is called by the user for setting data in the currently opened simple child node.
* If the currently opened node has children, it is a master node so it can't receive data. In such case, the function returns \e false. However,
* it the currently opened node already has data, it returns \e false too.
*/
virtual bool setChildData(const void* buffer, const size_t size) = 0;
/**
* \brief Closes currently opened child node
* \return \e true on success.
* \return \e false on error.
*
* This function is called when the user wants to close a child node. The node should have received
* either child nodes or data depending if it is a master node or a simple child node.
*/
virtual bool closeChild() = 0;
/**
* \brief Tells this object it won't be used anymore
*
* Instances of this class can not be instanciated another way than calling \c createWriter. They can
* not be deleted either because the destructor is. protected. The library knows how to create and
* delete an instance of this class... Calling \c release will simply delete this instance and
* handle necessary cleanings when needed.
*
* The current object is invalid after calling this
* function. It can not be used anymore.
*/
virtual void release() = 0;
protected:
/**
* \brief Virtual destructor - should be overloaded
*/
virtual ~IWriter() { }
};
/**
* \brief Instanciation function for EBML writer objects
* \param callback [in] : The callback object the writer should use
* \return a pointer to the created instance on success.
* \return \c NULL when something went wrong.
*/
extern EBML_API IWriter* createWriter(IWriterCallback& callback);
} // namespace EBML
@@ -0,0 +1,176 @@
#pragma once
#include "IWriter.h"
namespace EBML {
/**
* \class IWriterHelper
* \author Yann Renard (INRIA/IRISA)
* \date 2006-08-07
* \brief Helper to write basic EBML types
*
* This class may be used by the user in order to correctly
* format simple types defined in the EBML description such
* as integers, floats, strings etc... It directly uses the
* EBML::IWriter connected instance so one could simply
* use the helper in order to write his EBML stream.
*
* A similar class exists to help parsing process...
* See EBML::IReaderHelper for more details.
*
* Be sure to look at http://ebml.sourceforge.net/specs/ in
* order to understand what EBML is and how it should be used.
*
* \todo long double formating implementation
* \todo date formating implementation
* \todo utf8 string formating implementation
*/
class EBML_API IWriterHelper
{
public:
/** \name Writer connection */
//@{
/**
* \brief Connects an EBML writer to this helper
* \param writer [in] : The writer to connect
* \return \e true on success.
* \return \e false on error.
*
* This function gives the helper a handle to the writer
* to use in order to forward requests. Thus, when the
* user calls a helper function, the call is forwarded
* to the correct writer that effictively does the work.
* The aim of this helper is simply to transform standard
* EBML types into bytes buffers.
*
* Once a writer is connected, it could be disconnected
* thanks to the \c disconnect function. It must be done
* before calling \c connect again.
*/
virtual bool connect(IWriter* writer) = 0;
/**
* \brief Disconnects the currently connected EBML writer
* \return \e true on success.
* \return \e false on error.
*
* This function should be called to release the EBML
* writer handle. The helper instance may then be used
* with another EBML writer instance calling \c connect
* again.
*/
virtual bool disconnect() = 0;
//@}
/** \name Writer binding functions */
//@{
/**
* \brief Child opening binding
* \param identifier [in] : The identifier of the new child node
* \return \e true on success.
* \return \e false on error.
*
* This function simply forwards the call to the
* corresponding EBML::IWriter function. See
* EBML::IWriter::openChild for more details.
*/
virtual bool openChild(const CIdentifier& identifier) = 0;
/**
* \brief Child closing binding
* \return \e true on success.
* \return \e false on error.
*
* This function simply forwards the call to the
* corresponding EBML::IWriter function. See
* EBML::IWriter::closeChild for more details.
*/
virtual bool closeChild() = 0;
//@}
/**
* \name Standard EBML formating
* \brief EBML::IWriter::setChildData replacement
* \return \e true on success.
* \return \e false on error.
*
* Those functions should be used in place of the
* basic EBML::IWriter::setChildData function. They
* format standard EBML types into corresponding
* buffers and then send those built buffers to
* the writer using the EBML::IWriter::setChildData
* function.
*/
//@{
/**
* \brief Sets a signed integer as child data
* \param value [in] : The integer value to set
*/
virtual bool setInt(const int64_t value) = 0;
/**
* \brief Sets an unsigned integer as child data
* \param value [in] : The integer value to set
*/
virtual bool setUInt(const uint64_t value) = 0;
/**
* \brief Sets a 32 bits float value as child data
* \param value [in] : The 32 bits float value to set
*/
virtual bool setFloat(const float value) = 0;
/**
* \brief Sets a 64 bits float value as child data
* \param value [in] : The 64 bits float value to set
*/
virtual bool setDouble(const double value) = 0;
// virtual bool setFloat80AsChildData( ??? value)=0;
// virtual bool setDateAsChildData( ??? value)=0;
/**
* \brief Sets a buffer as child data
* \param buffer [in] : The buffer to send to the writer
* \param size [in] : The buffer size in bytes
* \note This function simply calls the basic
* EBML::IWriter::setChildData function with the
* same two parameters.
*/
virtual bool setBinary(const void* buffer, const size_t size) = 0;
/**
* \brief Sets an ASCII string as child data
* \param value [in] : The ASCII string value to set
*/
virtual bool setStr(const char* value) = 0;
// virtual bool setUTF8StringAsChildData( ??? value)=0;
//@}
/**
* \brief Tells this object it won't be used anymore
*
* Instances of this class can not be instanciated
* another way than calling \c createWriterHelper. They
* can not be deleted either because the destructor is.
* protected. The library knows how to create and
* delete an instance of this class... Calling
* \c release will simply delete this instance and
* handle necessary cleanings when needed.
*
* The current object is invalid after calling this
* function. It can not be used anymore.
*
* \warning Releasing this obbject does not release the
* connected writer instance !
*/
virtual void release() = 0;
protected:
/**
* \brief Virtual destructor - should be overloaded
*/
virtual ~IWriterHelper() { }
};
/**
* \brief Instanciation function for EBML writer helper objects
* \return a pointer to the created instance on success.
* \return \c NULL when something went wrong.
*/
extern EBML_API IWriterHelper* createWriterHelper();
} // namespace EBML
@@ -0,0 +1,81 @@
#pragma once
#include "IReader.h"
namespace EBML {
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass>
class TReaderCallbackProxy1 final : public IReaderCallback
{
public:
TReaderCallbackProxy1(TOwnerClass& ownerObject, bool (TOwnerClass::*mfpIsMasterChild)(const CIdentifier& identifier),
void (TOwnerClass::*mfpOpenChild)(const CIdentifier& identifier),
void (TOwnerClass::*mfpProcessChildData)(const void* buffer, const size_t size), void (TOwnerClass::*mfpCloseChild)())
: m_ownerObject(ownerObject), m_mfpIsMasterChild(mfpIsMasterChild), m_mfpOpenChild(mfpOpenChild), m_mfpProcessChildData(mfpProcessChildData),
m_mfpCloseChild(mfpCloseChild) { }
bool isMasterChild(const CIdentifier& identifier) override
{
if (m_mfpIsMasterChild) { return (m_ownerObject.*m_mfpIsMasterChild)(identifier); }
return false;
}
void openChild(const CIdentifier& identifier) override { if (m_mfpOpenChild) { (m_ownerObject.*m_mfpOpenChild)(identifier); } }
void processChildData(const void* buffer, const size_t size) override
{
if (m_mfpProcessChildData) { (m_ownerObject.*m_mfpProcessChildData)(buffer, size); }
}
void closeChild() override { if (m_mfpCloseChild) { (m_ownerObject.*m_mfpCloseChild)(); } }
protected:
TOwnerClass& m_ownerObject;
bool (TOwnerClass::*m_mfpIsMasterChild)(const CIdentifier& identifier);
void (TOwnerClass::*m_mfpOpenChild)(const CIdentifier& identifier);
void (TOwnerClass::*m_mfpProcessChildData)(const void* buffer, size_t size);
void (TOwnerClass::*m_mfpCloseChild)();
};
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass, bool (TOwnerClass::*TMfpIsMasterChild)(const CIdentifier& identifier),
void (TOwnerClass::*TMfpOpenChild)(const CIdentifier& identifier),
void (TOwnerClass::*TMfpProcessChildData)(const void* buffer, size_t size), void (TOwnerClass::*TMfpCloseChild)()>
class TReaderCallbackProxy2 final : public IReaderCallback
{
public:
TReaderCallbackProxy2(TOwnerClass& ownerObject)
: m_ownerObject(ownerObject), m_mfpIsMasterChild(TMfpIsMasterChild), m_mfpOpenChild(TMfpOpenChild), m_mfpProcessChildData(TMfpProcessChildData),
m_mfpCloseChild(TMfpCloseChild) { }
bool isMasterChild(const CIdentifier& identifier) override
{
if (m_mfpIsMasterChild) { return (m_ownerObject.*m_mfpIsMasterChild)(identifier); }
return false;
}
void openChild(const CIdentifier& identifier) override { if (m_mfpOpenChild) { (m_ownerObject.*m_mfpOpenChild)(identifier); } }
void processChildData(const void* buffer, const size_t size) override
{
if (m_mfpProcessChildData) { (m_ownerObject.*m_mfpProcessChildData)(buffer, size); }
}
void closeChild() override { if (m_mfpCloseChild) { (m_ownerObject.*m_mfpCloseChild)(); } }
protected:
TOwnerClass& m_ownerObject;
bool (TOwnerClass::*m_mfpIsMasterChild)(const CIdentifier& identifier);
void (TOwnerClass::*m_mfpOpenChild)(const CIdentifier& identifier);
void (TOwnerClass::*m_mfpProcessChildData)(const void* buffer, size_t size);
void (TOwnerClass::*m_mfpCloseChild)();
};
// ________________________________________________________________________________________________________________
//
} // namespace EBML
@@ -0,0 +1,42 @@
#pragma once
#include "IWriter.h"
namespace EBML {
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass>
class TWriterCallbackProxy1 final : public IWriterCallback
{
public:
TWriterCallbackProxy1(TOwnerClass& ownerObject, void (TOwnerClass::*mfpWrite)(const void* buffer, size_t size))
: m_ownerObject(ownerObject), m_mfpWrite(mfpWrite) { }
void write(const void* buffer, const size_t size) override { if (m_mfpWrite) { (m_ownerObject.*m_mfpWrite)(buffer, size); } }
protected:
TOwnerClass& m_ownerObject;
void (TOwnerClass::*m_mfpWrite)(const void* buffer, size_t size);
};
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass, void (TOwnerClass::*TMfpWrite)(const void* buffer, size_t size)>
class TWriterCallbackProxy2 final : public IWriterCallback
{
public:
TWriterCallbackProxy2(TOwnerClass& ownerObject) : m_ownerObject(ownerObject), m_mfpWrite(TMfpWrite) { }
void write(const void* buffer, const size_t size) override { if (m_mfpWrite) { (m_ownerObject.*m_mfpWrite)(buffer, size); } }
protected:
TOwnerClass& m_ownerObject;
void (TOwnerClass::*m_mfpWrite)(const void* buffer, size_t size);
};
// ________________________________________________________________________________________________________________
//
} // namespace EBML
@@ -0,0 +1,117 @@
/**
* \file defines.h
* \author Yann Renard (INRIA/IRISA)
* \date 2006-08-07
* \brief Contains basic type definitions and EBML standard identifiers
*/
#pragma once
#include <ov_common_defines.h>
#include <cstdlib> // For Unix Compatibility
#include <cstdint>
//___________________________________________________________________//
// //
// EBML Standard identifiers definitions //
//___________________________________________________________________//
// //
/**
* \name EBML standard identifiers definitions
*
* Be sure to look at http://ebml.sourceforge.net/specs/ to
* have more information on EBML and its standard identifiers.
*/
//@{
/**
* EBML Header.
* This is a master node.
* It contains several information about the ebml stream.
*/
#define EBML_Identifier_Header EBML::CIdentifier(0x0A45DFA3)
/**
* EBML Version.
* This is a child node of type \c integer.
* It contains the EBML version.
*/
#define EBML_Identifier_EBMLVersion EBML::CIdentifier(0x0286)
/**
* EBML Read Version.
* This is a child node of type \c integer.
* Indicates the EBML version a parser has to support to read this file
*/
#define EBML_Identifier_EBMLReadVersion EBML::CIdentifier(0x02F7)
/**
* EBML Identifier Length.
* This is a child node of type \c integer.
* Indicates the parser the maximum number of bytes an identifier
* can have.
*/
#define EBML_Identifier_EBMLIdLength EBML::CIdentifier(0x02F2)
/**
* EBML Size Length.
* This is a child node of type \c integer.
* Indicates the paser the maximum number of bytes a node
* can have.
*/
#define EBML_Identifier_EBMLSizeLength EBML::CIdentifier(0x02F3)
/**
* EBML Document Type.
* This is a child node of type \c string.
* Indicates the parser the kind of document being parsed.
*/
#define EBML_Identifier_DocType EBML::CIdentifier(0x0282)
/**
* EBML Document Type Version.
* This is a child node of type \c integer.
* Indicates the parser the specification version of the document
* being parsed.
*/
#define EBML_Identifier_DocTypeVersion EBML::CIdentifier(0x0287)
/**
* EBML Document Type Read Version.
* This is a child node of type \c integer.
* Indicates the parser the version of the reader to use in order
* to understand the document content.
*/
#define EBML_Identifier_DocTypeReadVersion EBML::CIdentifier(0x0285)
/**
* EBML Void.
* This is a child node of type \c binary.
* Indicates an ununderstandable chunk of data to skip.
*/
#define EBML_Identifier_Void EBML::CIdentifier(0x6C)
/**
* EBML CRC32.
* This is a child node of type \c binary.
* This is a CRC32 checksum of the content between this CRC32
* and the last one.
*/
#define EBML_Identifier_CRC32 EBML::CIdentifier(0x02FE)
//@}
#if defined EBML_Shared
# if defined TARGET_OS_Windows
# define EBML_API_Export __declspec(dllexport)
# define EBML_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
# define EBML_API_Export __attribute__((visibility("default")))
# define EBML_API_Import __attribute__((visibility("default")))
# else
# define EBML_API_Export
# define EBML_API_Import
# endif
#else
# define EBML_API_Export
# define EBML_API_Import
#endif
#if defined EBML_Exports
# define EBML_API EBML_API_Export
#else
# define EBML_API EBML_API_Import
#endif
@@ -0,0 +1,17 @@
#include "ebml/CIdentifier.h"
namespace EBML {
const CIdentifier& CIdentifier::operator=(const CIdentifier& id)
{
m_id = id.m_id;
return *this;
}
bool operator==(const CIdentifier& id1, const CIdentifier& id2) { return id1.m_id == id2.m_id; }
bool operator!=(const CIdentifier& id1, const CIdentifier& id2) { return id1.m_id != id2.m_id; }
bool operator<=(const CIdentifier& id1, const CIdentifier& id2) { return id1.m_id <= id2.m_id; }
bool operator>=(const CIdentifier& id1, const CIdentifier& id2) { return id1.m_id >= id2.m_id; }
bool operator<(const CIdentifier& id1, const CIdentifier& id2) { return id1.m_id < id2.m_id; }
bool operator>(const CIdentifier& id1, const CIdentifier& id2) { return id1.m_id > id2.m_id; }
} // namespace EBML
@@ -0,0 +1,14 @@
#include "ebml/CReader.h"
namespace EBML {
CReader::CReader(IReaderCallback& callback) { m_impl = createReader(callback); }
CReader::~CReader() { m_impl->release(); }
bool CReader::processData(const void* buffer, const size_t size) { return m_impl->processData(buffer, size); }
CIdentifier CReader::getCurrentNodeID() const { return m_impl->getCurrentNodeID(); }
size_t CReader::getCurrentNodeSize() const { return m_impl->getCurrentNodeSize(); }
void CReader::release() {}
} // namespace EBML
@@ -0,0 +1,15 @@
#include "ebml/CReaderHelper.h"
namespace EBML {
CReaderHelper::CReaderHelper() { m_impl = createReaderHelper(); }
CReaderHelper::~CReaderHelper() { m_impl->release(); }
uint64_t CReaderHelper::getUInt(const void* buffer, const size_t size) { return m_impl->getUInt(buffer, size); }
int64_t CReaderHelper::getInt(const void* buffer, const size_t size) { return m_impl->getInt(buffer, size); }
double CReaderHelper::getDouble(const void* buffer, const size_t size) { return m_impl->getDouble(buffer, size); }
const char* CReaderHelper::getStr(const void* buffer, const size_t size) { return m_impl->getStr(buffer, size); }
void CReaderHelper::release() {}
} // namespace EBML
@@ -0,0 +1,14 @@
#include "ebml/CWriter.h"
namespace EBML {
CWriter::CWriter(IWriterCallback& callback) { m_impl = createWriter(callback); }
CWriter::~CWriter() { m_impl->release(); }
bool CWriter::openChild(const CIdentifier& identifier) { return m_impl->openChild(identifier); }
bool CWriter::setChildData(const void* buffer, const size_t size) { return m_impl->setChildData(buffer, size); }
bool CWriter::closeChild() { return m_impl->closeChild(); }
void CWriter::release() {}
} // namespace EBML
@@ -0,0 +1,21 @@
#include "ebml/CWriterHelper.h"
namespace EBML {
CWriterHelper::CWriterHelper() { m_impl = createWriterHelper(); }
CWriterHelper::~CWriterHelper() { m_impl->release(); }
bool CWriterHelper::connect(IWriter* writer) { return m_impl->connect(writer); }
bool CWriterHelper::disconnect() { return m_impl->disconnect(); }
bool CWriterHelper::openChild(const CIdentifier& identifier) { return m_impl->openChild(identifier); }
bool CWriterHelper::closeChild() { return m_impl->closeChild(); }
bool CWriterHelper::setInt(const int64_t value) { return m_impl->setInt(value); }
bool CWriterHelper::setUInt(const uint64_t value) { return m_impl->setUInt(value); }
bool CWriterHelper::setFloat(const float value) { return m_impl->setFloat(value); }
bool CWriterHelper::setDouble(const double value) { return m_impl->setDouble(value); }
bool CWriterHelper::setBinary(const void* buffer, const size_t size) { return m_impl->setBinary(buffer, size); }
bool CWriterHelper::setStr(const char* value) { return m_impl->setStr(value); }
} // namespace EBML
@@ -0,0 +1,357 @@
#include "ebml/IReader.h"
#include <cstring>
#include <cstdio>
#include <cstring>
#include <iostream>
#if 0
#define _Debug_ _is_in_debug_mode_(m_totalBytes)
static bool _is_in_debug_mode_(uint64_t value)
{
static int i=0;
// bool result=i++>5500000;
bool result=value>29605500;
if (result) std::cout << "Arround " << value << std::endl;
return result;
}
#else
#define _Debug_ false
#endif
// ________________________________________________________________________________________________________________
//
inline bool needsTwoBytesToGetCodedSizeLength(const unsigned char* buffer) { return buffer[0] == 0; }
inline size_t getCodedSizeLength(const unsigned char* buffer)
{
if (buffer[0] >> 7) { return 1; }
if (buffer[0] >> 6) { return 2; }
if (buffer[0] >> 5) { return 3; }
if (buffer[0] >> 4) { return 4; }
if (buffer[0] >> 3) { return 5; }
if (buffer[0] >> 2) { return 6; }
if (buffer[0] >> 1) { return 7; }
if (buffer[0]) { return 8; }
if (buffer[1] >> 7) { return 9; }
return 10;
}
inline uint64_t getValue(unsigned char* buffer)
{
uint64_t result = 0;
const size_t length = getCodedSizeLength(buffer);
size_t ithBit = length;
for (size_t i = 0; i < length; ++i)
{
result = (result << 8) + (buffer[i]);
result &= ~(ithBit > 0 && ithBit <= 8 ? (1 << (8 - ithBit)) : 0);
ithBit -= 8;
}
return result;
}
// ________________________________________________________________________________________________________________
//
namespace EBML {
namespace {
class CReaderNode
{
public:
CReaderNode(const CIdentifier& identifier, CReaderNode* parentNode) : m_ParentNode(parentNode), m_Id(identifier) { }
private:
CReaderNode() = delete;
public:
CReaderNode* m_ParentNode = nullptr;
CIdentifier m_Id;
size_t m_ContentSize = 0;
size_t m_ReadContentSize = 0;
unsigned char* m_Buffer = nullptr;
};
} // namespace
} // namespace EBML
// ________________________________________________________________________________________________________________
//
namespace EBML {
namespace {
class CReader final : public IReader
{
public:
explicit CReader(IReaderCallback& callback) : m_readerCB(callback) { }
~CReader() override;
bool processData(const void* buffer, const size_t size) override;
CIdentifier getCurrentNodeID() const override;
size_t getCurrentNodeSize() const override;
void release() override;
protected:
enum EStatus
{
FillingIdentifier,
FillingContentSize,
FillingContent,
};
IReaderCallback& m_readerCB;
CReaderNode* m_currentNode = nullptr;
size_t m_pendingSize = 0;
size_t m_nPending = 0;
unsigned char* m_pending = nullptr;
EStatus m_status = FillingIdentifier;
EStatus m_lastStatus = FillingIdentifier;
CIdentifier m_currentID = 0;
size_t m_currentContentSize = 0;
size_t m_totalBytes = 0;
};
} // namespace
// ________________________________________________________________________________________________________________
//
CReader::~CReader()
{
delete [] m_pending;
while (m_currentNode)
{
CReaderNode* parentNode = m_currentNode->m_ParentNode;
delete m_currentNode;
m_currentNode = parentNode;
}
}
bool CReader::processData(const void* buffer, const size_t size)
{
m_totalBytes += size;
if (_Debug_)
{
std::cout << "Received " << size << " byte(s) new buffer :";
for (int i = 0; i < int(size) /* && i<4*/; ++i) { printf("[%02X]", ((unsigned char*)buffer)[i]); }
std::cout << "...\n";
}
if (!buffer || !size) { return true; }
unsigned char* tmpBuffer = (unsigned char*)buffer;
size_t currentSize = size;
bool finished = false;
while (!finished)
{
size_t processedPendingBytes = 0;
size_t processedBytes = 0;
m_lastStatus = m_status;
if (_Debug_ && m_nPending)
{
std::cout << m_nPending << " byte(s) pending : ";
for (int i = 0; i < int(m_nPending); ++i) { printf("[%02X]", m_pending[i]); }
std::cout << "\n";
}
// Processes data
switch (m_status)
{
case FillingIdentifier:
case FillingContentSize:
{
if (needsTwoBytesToGetCodedSizeLength(m_nPending ? m_pending : tmpBuffer))
{
if (m_nPending + currentSize < 2)
{
finished = true;
break;
}
if (m_nPending == 1)
{
// assumes (currentSize != 0) because (m_nPending + currentSize >= 2) and (m_nPending == 1)
m_pending[1] = tmpBuffer[0];
tmpBuffer++;
m_nPending++;
currentSize--;
}
}
const size_t length = getCodedSizeLength(m_nPending ? m_pending : tmpBuffer);
if (length > currentSize + m_nPending) { finished = true; }
else
{
unsigned char* encodedBuffer = new unsigned char[length];
const size_t pendingBytesToCopy = (length > m_nPending ? m_nPending : length);
memcpy(encodedBuffer, m_pending, size_t(pendingBytesToCopy));
memcpy(encodedBuffer + pendingBytesToCopy, tmpBuffer, size_t(length - pendingBytesToCopy));
const uint64_t value = getValue(encodedBuffer);
delete [] encodedBuffer;
processedPendingBytes = pendingBytesToCopy;
processedBytes = length;
switch (m_status)
{
case FillingIdentifier:
{
m_currentID = value;
m_status = FillingContentSize;
if (_Debug_)
{
printf("Found identifier 0x%llX - Changing status to FillingContentSize...\n", static_cast<unsigned long long>(m_currentID));
}
}
break;
case FillingContentSize:
{
m_currentContentSize = value;
if (m_readerCB.isMasterChild(m_currentID))
{
m_status = FillingIdentifier;
if (_Debug_)
{
std::cout << "Found content size " << m_currentContentSize << " of master node - Changing status to FillingIdentifier...\n";
}
}
else
{
m_status = FillingContent;
if (_Debug_)
{
std::cout << "Found content size " << m_currentContentSize <<
" of *non* master node - Changing status to FillingContent...\n";
}
}
}
break;
case FillingContent:
// Should never happen - avoids the warning
break;
}
}
}
break;
case FillingContent:
{
if (m_currentNode->m_ContentSize == 0)
{
m_status = FillingIdentifier;
if (_Debug_)
{
std::cout << "Finished with " << m_currentNode->m_ContentSize << " byte(s) content - Changing status to FillingIdentifier...\n";
}
m_readerCB.processChildData(nullptr, 0);
}
else
{
if (m_currentNode->m_ReadContentSize == 0 && m_currentNode->m_ContentSize <= currentSize)
{
m_status = FillingIdentifier;
processedBytes = m_currentNode->m_ContentSize;
if (_Debug_)
{
std::cout << "Optimized processing of " << m_currentNode->m_ContentSize <<
" byte(s) content - Changing status to FillingIdentifier...\n";
}
m_readerCB.processChildData(tmpBuffer, m_currentNode->m_ContentSize);
}
else
{
if (m_currentNode->m_ContentSize - m_currentNode->m_ReadContentSize > currentSize)
{
memcpy(m_currentNode->m_Buffer + m_currentNode->m_ReadContentSize, tmpBuffer, size_t(currentSize));
processedBytes = currentSize;
finished = true;
}
else
{
memcpy(m_currentNode->m_Buffer + m_currentNode->m_ReadContentSize, tmpBuffer,
size_t(m_currentNode->m_ContentSize - m_currentNode->m_ReadContentSize));
processedBytes = m_currentNode->m_ContentSize - m_currentNode->m_ReadContentSize;
m_status = FillingIdentifier;
if (_Debug_)
{
std::cout << "Finished with " << m_currentNode->m_ContentSize << " byte(s) content - Changing status to FillingIdentifier...\n";
}
m_readerCB.processChildData(m_currentNode->m_Buffer, m_currentNode->m_ContentSize);
}
}
}
}
break;
}
// Updates buffer pointer and size
const size_t processedBytesInBuffer = processedBytes - processedPendingBytes;
tmpBuffer += processedBytesInBuffer;
currentSize -= processedBytesInBuffer;
m_nPending -= processedPendingBytes;
// Updates read size
CReaderNode* node = m_currentNode;
while (node)
{
node->m_ReadContentSize += processedBytes;
node = node->m_ParentNode;
}
// Creates new node when needed
if (m_status != FillingContentSize && m_lastStatus == FillingContentSize)
{
m_currentNode = new CReaderNode(m_currentID, m_currentNode);
m_currentNode->m_ContentSize = m_currentContentSize;
m_currentNode->m_Buffer = new unsigned char[m_currentContentSize];
m_readerCB.openChild(m_currentNode->m_Id);
}
else
{
// Closes finished nodes
while (m_currentNode && (m_currentNode->m_ContentSize == m_currentNode->m_ReadContentSize || m_currentNode->m_ContentSize == 0))
{
m_readerCB.closeChild();
CReaderNode* parent = m_currentNode->m_ParentNode;
delete [] m_currentNode->m_Buffer;
delete m_currentNode;
m_currentNode = parent;
}
}
}
// Updates pending data
if (m_nPending + currentSize > m_pendingSize)
{
unsigned char* pending = new unsigned char[m_nPending + currentSize + 1
]; // Ugly hack, reserve 1 more byte on pending data so we are sure we can insert this additional pending byte when only one byte is pending and two bytes are needed for decoding identifier and/or buffer size
memcpy(pending, m_pending, m_nPending);
delete [] m_pending;
m_pending = pending;
m_pendingSize = m_nPending + currentSize;
}
memcpy(m_pending + m_nPending, tmpBuffer, currentSize);
m_nPending += currentSize;
if (_Debug_) { std::cout << "\n"; }
return true;
}
CIdentifier CReader::getCurrentNodeID() const { return m_currentNode ? m_currentNode->m_Id : CIdentifier(); }
size_t CReader::getCurrentNodeSize() const { return m_currentNode ? m_currentNode->m_ContentSize : 0; }
void CReader::release() { delete this; }
// ________________________________________________________________________________________________________________
//
EBML_API IReader* createReader(IReaderCallback& callback) { return new CReader(callback); }
} // namespace EBML
@@ -0,0 +1,85 @@
#include "ebml/IReaderHelper.h"
#include <string>
#include <cstring>
namespace EBML {
namespace {
class CReaderHelper final : public IReaderHelper
{
public:
CReaderHelper() { }
uint64_t getUInt(const void* buffer, const size_t size) override;
int64_t getInt(const void* buffer, const size_t size) override;
double getDouble(const void* buffer, const size_t size) override;
const char* getStr(const void* buffer, const size_t size) override;
void release() override;
std::string m_Str;
};
} // namespace
uint64_t CReaderHelper::getUInt(const void* buffer, const size_t size)
{
uint64_t result = 0;
for (size_t i = 0; i < size; ++i)
{
result <<= 8;
result |= reinterpret_cast<const unsigned char*>(buffer)[i];
}
return result;
}
int64_t CReaderHelper::getInt(const void* buffer, const size_t size)
{
int64_t result = 0;
if (size != 0 && reinterpret_cast<const unsigned char*>(buffer)[0] & 0x80) { result = -1; }
for (size_t i = 0; i < size; ++i)
{
result <<= 8;
result |= reinterpret_cast<const unsigned char*>(buffer)[i];
}
return result;
}
double CReaderHelper::getDouble(const void* buffer, const size_t size)
{
switch (size)
{
case 4:
{
float res;
uint32_t data;
data = uint32_t(getUInt(buffer, size));
memcpy(&res, &data, sizeof(res));
return double(res);
}
case 8:
{
double res;
uint64_t data;
data = getUInt(buffer, size);
memcpy(&res, &data, sizeof(res));
return res;
}
case 0:
case 10:
default: return 0.0;
}
}
const char* CReaderHelper::getStr(const void* buffer, const size_t size)
{
if (size) { m_Str.assign(reinterpret_cast<const char*>(buffer), size_t(size)); }
else { m_Str = ""; }
return m_Str.c_str();
}
void CReaderHelper::release() { delete this; }
EBML_API IReaderHelper* createReaderHelper() { return new CReaderHelper(); }
} // namespace EBML
@@ -0,0 +1,214 @@
#include "ebml/IWriter.h"
#include <vector>
#include <cstring>
inline size_t getCodedSizeLength(const uint64_t value)
{
if (value < 0x000000000000007fLL) { return 1; }
if (value < 0x0000000000003fffLL) { return 2; }
if (value < 0x00000000001fffffLL) { return 3; }
if (value < 0x000000000fffffffLL) { return 4; }
if (value < 0x00000007ffffffffLL) { return 5; }
if (value < 0x000003ffffffffffLL) { return 6; }
if (value < 0x0001ffffffffffffLL) { return 7; }
if (value < 0x00ffffffffffffffLL) { return 8; }
if (value < 0x7fffffffffffffffLL) { return 9; }
return 10;
}
inline bool getCodedBuffer(const uint64_t value, unsigned char* buffer, size_t* size)
{
const size_t length = getCodedSizeLength(value);
if (length > *size) { return false; }
size_t bit = length;
for (size_t i = 0; i < length; ++i)
{
const size_t byteShift = length - i - 1;
size_t byte = (byteShift >= 8 ? 0 : static_cast<unsigned char>((value >> (byteShift * 8)) & 0xff));
byte |= (bit > 0 && bit <= 8 ? (1 << (8 - bit)) : 0);
bit -= 8;
buffer[i] = static_cast<unsigned char>(byte);
}
*size = length;
return true;
}
// ________________________________________________________________________________________________________________
//
namespace EBML {
namespace {
class CWriterNode final
{
public:
CWriterNode(const CIdentifier& identifier, CWriterNode* parentNode) : m_ID(identifier), m_ParentNode(parentNode) {}
~CWriterNode();
void process(IWriterCallback& callback);
protected:
size_t getTotalContentSize(bool identifierAndSize);
private:
CWriterNode() = delete;
public:
CIdentifier m_ID;
CWriterNode* m_ParentNode = nullptr;
size_t m_BufferLength = 0;
unsigned char* m_Buffer = nullptr;
bool m_Buffered = false;
std::vector<CWriterNode*> m_Childrens;
};
} // namespace
// ________________________________________________________________________________________________________________
//
CWriterNode::~CWriterNode()
{
for (auto i = m_Childrens.begin(); i != m_Childrens.end(); ++i) { delete (*i); }
if (m_Buffer)
{
delete [] m_Buffer;
m_Buffer = nullptr;
}
}
void CWriterNode::process(IWriterCallback& callback)
{
unsigned char id[10];
unsigned char pContentSize[10];
size_t contentSizeLength = sizeof(pContentSize);
size_t identifierLength = sizeof(id);
const size_t contentSize = getTotalContentSize(false);
if (!getCodedBuffer(contentSize, pContentSize, &contentSizeLength)) { } // SHOULD NEVER HAPPEN
if (!getCodedBuffer(m_ID, id, &identifierLength)) { } // SHOULD NEVER HAPPEN
callback.write(id, identifierLength);
callback.write(pContentSize, contentSizeLength);
if (m_Childrens.empty()) { callback.write(m_Buffer, m_BufferLength); }
else { for (auto i = m_Childrens.begin(); i != m_Childrens.end(); ++i) { (*i)->process(callback); } }
}
size_t CWriterNode::getTotalContentSize(const bool identifierAndSize)
{
size_t contentSize = 0;
if (m_Childrens.empty()) { contentSize = m_BufferLength; }
else { for (auto i = m_Childrens.begin(); i != m_Childrens.end(); ++i) { contentSize += (*i)->getTotalContentSize(true); } }
size_t res = contentSize;
if (identifierAndSize)
{
res += getCodedSizeLength(m_ID);
res += getCodedSizeLength(contentSize);
}
return res;
}
// ________________________________________________________________________________________________________________
//
namespace {
class CWriter final : public IWriter
{
public:
explicit CWriter(IWriterCallback& callback) : m_callback(callback) {}
bool openChild(const CIdentifier& identifier) override;
bool setChildData(const void* buffer, const size_t size) override;
bool closeChild() override;
void release() override;
protected:
CWriterNode* m_node = nullptr;
IWriterCallback& m_callback;
private:
CWriter() = delete;
};
} // namespace
// ________________________________________________________________________________________________________________
//
bool CWriter::openChild(const CIdentifier& identifier)
{
if (m_node) { if (m_node->m_Buffered) { return false; } }
CWriterNode* pResult = new CWriterNode(identifier, m_node);
if (m_node) { m_node->m_Childrens.push_back(pResult); }
m_node = pResult;
return true;
}
bool CWriter::setChildData(const void* buffer, const size_t size)
{
if (!m_node) { return false; }
if (!m_node->m_Childrens.empty()) { return false; }
unsigned char* bufferCopy = nullptr;
if (size)
{
if (!buffer) { return false; }
bufferCopy = new unsigned char[size];
if (!bufferCopy) { return false; }
memcpy(bufferCopy, buffer, size);
}
delete [] m_node->m_Buffer;
m_node->m_BufferLength = size;
m_node->m_Buffer = bufferCopy;
m_node->m_Buffered = true;
return true;
}
bool CWriter::closeChild()
{
if (!m_node) { return false; }
if ((!m_node->m_Buffered) && (m_node->m_Childrens.empty()))
{
m_node->m_BufferLength = 0;
m_node->m_Buffer = nullptr;
m_node->m_Buffered = true;
}
CWriterNode* parent = m_node->m_ParentNode;
if (!parent)
{
m_node->process(m_callback);
delete m_node;
}
m_node = parent;
return true;
}
void CWriter::release()
{
while (m_node) { closeChild(); }
delete this;
}
// ________________________________________________________________________________________________________________
//
EBML_API IWriter* createWriter(IWriterCallback& callback) { return new CWriter(callback); }
} // namespace EBML
@@ -0,0 +1,129 @@
#include "ebml/IWriterHelper.h"
#include "ebml/IWriter.h"
#include <cstdlib>
#include <cstring>
namespace EBML {
namespace {
class CWriterHelper final : public IWriterHelper
{
public:
CWriterHelper() { }
bool connect(IWriter* writer) override;
bool disconnect() override;
bool openChild(const CIdentifier& identifier) override;
bool closeChild() override;
bool setInt(const int64_t value) override;
bool setUInt(const uint64_t value) override;
bool setFloat(const float value) override;
bool setDouble(const double value) override;
bool setBinary(const void* buffer, const size_t size) override;
bool setStr(const char* value) override;
void release() override;
protected:
IWriter* m_writer = nullptr;
};
} // namespace
bool CWriterHelper::connect(IWriter* writer)
{
m_writer = writer;
return m_writer ? true : false;
}
bool CWriterHelper::disconnect()
{
if (!m_writer) { return false; }
m_writer = nullptr;
return true;
}
// ________________________________________________________________________________________________________________
//
bool CWriterHelper::openChild(const CIdentifier& identifier) { return m_writer ? m_writer->openChild(identifier) : false; }
bool CWriterHelper::closeChild() { return m_writer ? m_writer->closeChild() : false; }
// ________________________________________________________________________________________________________________
//
bool CWriterHelper::setInt(const int64_t value)
{
unsigned char buffer[8];
size_t size = 8;
if (value == 0x00000000000000LL) { size = 0; }
else if (value >= -0x00000000000080LL && value <= 0x0000000000007fLL) { size = 1; }
else if (value >= -0x00000000008000LL && value <= 0x00000000007fffLL) { size = 2; }
else if (value >= -0x00000000800000LL && value <= 0x000000007fffffLL) { size = 3; }
else if (value >= -0x00000080000000LL && value <= 0x0000007fffffffLL) { size = 4; }
else if (value >= -0x00008000000000LL && value <= 0x00007fffffffffLL) { size = 5; }
else if (value >= -0x00800000000000LL && value <= 0x007fffffffffffLL) { size = 6; }
else if (value >= -0x80000000000000LL && value <= 0x7fffffffffffffLL) { size = 7; }
//else { size = 8; }
for (size_t i = 0; i < size; ++i) { buffer[size - i - 1] = static_cast<unsigned char>((value >> (i * 8)) & 0xff); }
return m_writer->setChildData(buffer, size);
}
bool CWriterHelper::setUInt(const uint64_t value)
{
size_t size = 8;
unsigned char buffer[8];
if (value == 0x000000000000000LL) { size = 0; }
else if (value < 0x000000000000100LL) { size = 1; }
else if (value < 0x000000000010000LL) { size = 2; }
else if (value < 0x000000001000000LL) { size = 3; }
else if (value < 0x000000100000000LL) { size = 4; }
else if (value < 0x000010000000000LL) { size = 5; }
else if (value < 0x001000000000000LL) { size = 6; }
else if (value < 0x100000000000000LL) { size = 7; }
for (size_t i = 0; i < size; ++i) { buffer[size - i - 1] = static_cast<unsigned char>((value >> (i * 8)) & 0xff); }
return m_writer->setChildData(buffer, size);
}
bool CWriterHelper::setFloat(const float value)
{
uint32_t res;
unsigned char buffer[8];
memcpy(&res, &value, sizeof(value));
const size_t size = (value != 0 ? 4 : 0);
for (size_t i = 0; i < size; ++i) { buffer[size - i - 1] = static_cast<unsigned char>((res >> (i * 8)) & 0xff); }
return m_writer->setChildData(buffer, size);
}
bool CWriterHelper::setDouble(const double value)
{
uint64_t res;
unsigned char buffer[8];
memcpy(&res, &value, sizeof(value));
const size_t size = (value != 0 ? 8 : 0);
for (size_t i = 0; i < size; ++i) { buffer[size - i - 1] = static_cast<unsigned char>((res >> (i * 8)) & 0xff); }
return m_writer->setChildData(buffer, size);
}
bool CWriterHelper::setBinary(const void* buffer, const size_t size) { return m_writer->setChildData(buffer, size); }
bool CWriterHelper::setStr(const char* value) { return m_writer->setChildData(value, strlen(value)); }
// ________________________________________________________________________________________________________________
//
void CWriterHelper::release() { delete this; }
// ________________________________________________________________________________________________________________
//
EBML_API IWriterHelper* createWriterHelper() { return new CWriterHelper(); }
} // namespace EBML
+52
View File
@@ -0,0 +1,52 @@
PROJECT(openvibe-module-fs)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl include/*.h)
INCLUDE_DIRECTORIES(include/fs)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DFS_Shared -DFS_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DFS_Static -DFS_Exports")
IF(UNIX)
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF()
INCLUDE("FindOpenViBECommon")
INCLUDE("FindThirdPartyBoost")
INCLUDE("FindThirdPartyBoost_FileSystem")
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
@@ -0,0 +1,62 @@
#pragma once
#include "defines.h"
#include <cstdio>
#include <fstream>
namespace FS {
class FS_API Files
{
public:
static FILE* open(const char* file, const char* mode);
static FILE* popen(const char* file, const char* mode);
static void openOFStream(std::ofstream& stream, const char* file, std::ios_base::openmode mode = std::ios_base::out);
static void openIFStream(std::ifstream& stream, const char* file, std::ios_base::openmode mode = std::ios_base::in);
static void openFStream(std::fstream& stream, const char* file, std::ios_base::openmode mode);
static bool equals(const char* pFile1, const char* pFile2);
static bool fileExists(const char* pathToCheck);
static bool directoryExists(const char* pathToCheck);
// Creates all components of a path to the filesystem
static bool createPath(const char* path);
// Creates all components of a path to the filesystem except the last part (i.e. for paths including a filename in the end)
static bool createParentPath(const char* path);
// Returns a path omitting the last part of it (essentially boost::filesystem::parent_path). Output sParentPath needs to be pre-allocated.
static bool getParentPath(const char* path, char* parentPath);
static bool getParentPath(const char* path, char* parentPath, size_t size);
/**
* \brief Fills filename as a component of a path
* \param path [in]: input path
* \param filename [out]: file name, needs to be pre-allocated
*
* (essentially boost::filesystem::filename)
* \return Success status
*/
static bool getFilename(const char* path, char* filename);
static bool getFilename(const char* path, char* filename, size_t size);
/**
* \brief Fills filename as a component of a path without the extension
* \param path [in]: input path
* \param filename [out]: file name without extension, needs to be pre-allocated
*
* (essentially boost::filesystem::filename)
* \return Success status
*/
static bool getFilenameWithoutExtension(const char* path, char* filename);
static bool getFilenameWithoutExtension(const char* path, char* filename, size_t size);
// Returns the extension component of a path
static bool getFilenameExtension(const char* path, char* fileNameExtension);
static bool getFilenameExtension(const char* path, char* extension, size_t size);
static bool copyFile(const char* srcFile, const char* dstPath);
static bool copyDirectory(const char* srcDir, const char* dstDir);
static bool remove(const char* path);
static bool removeAll(const char* path);
private:
Files() = delete;
};
} // namespace FS
@@ -0,0 +1,54 @@
#pragma once
#include "defines.h"
#include <cinttypes>
#include <cstdlib> // size_t for unix
namespace FS {
class IEntryEnumeratorCallBack;
class FS_API IEntryEnumerator
{
public:
class FS_API IAttributes
{
public:
virtual bool isFile() = 0;
virtual bool isDirectory() = 0;
virtual bool isSymbolicLink() = 0;
virtual bool isArchive() = 0;
virtual bool isReadOnly() = 0;
virtual bool isHidden() = 0;
virtual bool isSystem() = 0;
virtual bool isExecutable() = 0;
virtual size_t getSize() = 0;
protected:
virtual ~IAttributes() {}
};
class FS_API IEntry
{
public:
virtual const char* getName() = 0;
protected:
virtual ~IEntry() {}
};
virtual bool enumerate(const char* sWildcard, bool bRecursive = false) = 0;
virtual void release() = 0;
protected:
virtual ~IEntryEnumerator() { }
};
class FS_API IEntryEnumeratorCallBack
{
public:
virtual bool callback(IEntryEnumerator::IEntry& rEntry, IEntryEnumerator::IAttributes& rAttributes) = 0;
virtual ~IEntryEnumeratorCallBack() { }
};
extern FS_API IEntryEnumerator* createEntryEnumerator(IEntryEnumeratorCallBack& rCallBack);
} // namespace FS
@@ -0,0 +1,25 @@
#pragma once
#include <ov_common_defines.h>
#if defined FS_Shared
# if defined TARGET_OS_Windows
# define FS_API_Export __declspec(dllexport)
# define FS_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
# define FS_API_Export __attribute__((visibility("default")))
# define FS_API_Import __attribute__((visibility("default")))
# else
# define FS_API_Export
# define FS_API_Import
# endif
#else
# define FS_API_Export
# define FS_API_Import
#endif
#if defined FS_Exports
# define FS_API FS_API_Export
#else
# define FS_API FS_API_Import
#endif
@@ -0,0 +1,391 @@
#include "Files.h"
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <stdio.h> // For fopen
#include <unistd.h> //For access().
#include <sys/stat.h>
#include <sys/types.h> // For stat().
#include <cstdlib>
#elif defined TARGET_OS_Windows
#include "m_ConverterUtf8.h"
#include <Windows.h>
#else
#endif
#include <boost/filesystem.hpp>
#include <boost/version.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <cstdio>
#include <cstring>
namespace FS {
// * 2006-08-30 YRD - Portability note : using namespace FS confuses windows platform SDK because it defines itself a 'boolean' type. Thus the following define to force the use of FS::boolean !
#if defined TARGET_HAS_Boost && BOOST_VERSION / 100 % 1000 >= 55
/**
* \brief Makes a recursive copy of source folder to target folder.
* Operation can fail in several cases:
* - target path exists
* - bad permission rights
* \param source the source folder path
* \param target the destination folder path
* \return true if succeeded
* \return false if failed
*/
bool recursiveCopy(const boost::filesystem::path& source, const boost::filesystem::path& target)
{
if (exists(target)) { return false; }
if (is_directory(source))
{
if (!create_directories(target)) { return false; }
for (boost::filesystem::directory_entry& item : boost::filesystem::directory_iterator(source))
{
// boost::filesystem::path overlods '/' operator !
if (!recursiveCopy(item.path(), target / item.path().filename())) { return false; }
}
}
else if (is_regular_file(source))
{
try { copy(source, target); }
catch (...) { return false; }
}
else { return false; }
return true;
}
#endif
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
FILE* Files::open(const char* file, const char* mode)
{
FILE* fHandle = fopen(file, mode);
return fHandle;
}
FILE* Files::popen(const char* file, const char* mode)
{
FILE* fHandle = ::popen(file, mode);
return fHandle;
}
void Files::openOFStream(std::ofstream& stream, const char* file, std::ios_base::openmode mode) { stream.open(file, mode); }
void Files::openIFStream(std::ifstream& stream, const char* file, std::ios_base::openmode mode) { stream.open(file, mode); }
void Files::openFStream(std::fstream& stream, const char* file, std::ios_base::openmode mode) { stream.open(file, mode); }
#elif defined TARGET_OS_Windows
FILE* Files::open(const char* file, const char* mode)
{
FILE* fHandle;
try
{
const std::wstring utf16FileName = Common::Converter::Utf8ToUtf16(file);
const std::wstring utf16Mode = Common::Converter::Utf8ToUtf16(mode);
fHandle = _wfopen(utf16FileName.c_str(), utf16Mode.c_str());
}
catch (const std::logic_error&) { fHandle = fopen(file, mode); }
return fHandle;
}
FILE* Files::popen(const char* file, const char* mode)
{
FILE* fHandle;
try
{
const std::wstring utf16FileName = Common::Converter::Utf8ToUtf16(file);
const std::wstring utf16Mode = Common::Converter::Utf8ToUtf16(mode);
fHandle = _wpopen(utf16FileName.c_str(), utf16Mode.c_str());
}
catch (const std::logic_error&) { fHandle = popen(file, mode); }
return fHandle;
}
template <class T>
void openStream(T& stream, const char* file, std::ios_base::openmode mode)
{
try
{
const std::wstring utf16FileName = Common::Converter::Utf8ToUtf16(file);
stream.open(utf16FileName.c_str(), mode);
}
catch (const std::logic_error&) { stream.open(file, mode); }
}
void Files::openOFStream(std::ofstream& stream, const char* file, const std::ios_base::openmode mode) { openStream<std::ofstream>(stream, file, mode); }
void Files::openIFStream(std::ifstream& stream, const char* file, const std::ios_base::openmode mode) { openStream<std::ifstream>(stream, file, mode); }
void Files::openFStream(std::fstream& stream, const char* file, const std::ios_base::openmode mode) { openStream<std::fstream>(stream, file, mode); }
#endif
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool Files::equals(const char* pFile1, const char* pFile2)
{
bool res=true;
if(pFile1 && pFile2)
{
struct stat stat1;
struct stat stat2;
bool bStat1=!stat(pFile1, &stat1);
bool bStat2=!stat(pFile2, &stat2);
if(!bStat1 && !bStat2) { res=true; }
else if(bStat1 && bStat2)
{
res=
(stat1.st_dev==stat2.st_dev)&&
(stat1.st_ino==stat2.st_ino)&&
(stat1.st_size==stat2.st_size)&&
(stat1.st_mtime==stat2.st_mtime);
}
else { res=false; }
}
return res;
}
#elif defined TARGET_OS_Windows
bool Files::equals(const char* pFile1, const char* pFile2)
{
bool res = true;
if (pFile1 && pFile2)
{
const HANDLE handle1 = ::CreateFile(pFile1, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, nullptr);
const HANDLE handle2 = ::CreateFile(pFile2, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (handle1 && handle2)
{
BY_HANDLE_FILE_INFORMATION stat1;
BY_HANDLE_FILE_INFORMATION stat2;
const BOOL bStat1 = GetFileInformationByHandle(handle1, &stat1);
const BOOL bStat2 = GetFileInformationByHandle(handle2, &stat2);
if (!bStat1 && !bStat2) { res = true; }
else if (bStat1 && bStat2)
{
res = (stat1.dwVolumeSerialNumber == stat2.dwVolumeSerialNumber) && (stat1.nFileIndexHigh == stat2.nFileIndexHigh)
&& (stat1.nFileIndexLow == stat2.nFileIndexLow) && (stat1.nFileSizeHigh == stat2.nFileSizeHigh)
&& (stat1.nFileSizeLow == stat2.nFileSizeLow) && (stat1.ftLastWriteTime.dwHighDateTime == stat2.ftLastWriteTime.dwHighDateTime)
&& (stat1.ftLastWriteTime.dwLowDateTime == stat2.ftLastWriteTime.dwLowDateTime);
}
else { res = false; }
CloseHandle(handle1);
CloseHandle(handle2);
}
else
{
if (handle1) { CloseHandle(handle1); }
if (handle2) { CloseHandle(handle2); }
}
}
return res;
}
#else
#endif
bool Files::fileExists(const char* pathToCheck)
{
if (!pathToCheck) { return false; }
FILE* fp = open(pathToCheck, "r");
if (!fp) { return false; }
fclose(fp);
return true;
}
bool Files::directoryExists(const char* pathToCheck)
{
if (!pathToCheck) { return false; }
#if defined TARGET_OS_Windows
const std::wstring pathUTF16 = Common::Converter::Utf8ToUtf16(pathToCheck);
const DWORD ftyp = GetFileAttributesW(pathUTF16.c_str());
if (ftyp == INVALID_FILE_ATTRIBUTES) { return false; }
if (ftyp & FILE_ATTRIBUTE_DIRECTORY) { return true; }
#endif
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
if ( access( pathToCheck, 0 ) == 0 )
{
struct stat status;
stat( pathToCheck, &status );
if ( status.st_mode & S_IFDIR ) { return true; }
}
#endif
return false;
}
bool Files::createPath(const char* path)
{
if (strcmp(path, "") == 0) { return false; }
#if defined TARGET_OS_Windows
std::wstring pathUTF16 = Common::Converter::Utf8ToUtf16(path);
create_directories(boost::filesystem::wpath(pathUTF16));
return is_directory(boost::filesystem::wpath(pathUTF16));
#else
return boost::filesystem::create_directories(boost::filesystem::path(path));
#endif
}
bool Files::createParentPath(const char* path)
{
if (strcmp(path, "") == 0) { return false; }
#if defined TARGET_OS_Windows
std::wstring pathUTF16 = Common::Converter::Utf8ToUtf16(path);
return create_directories(boost::filesystem::wpath(pathUTF16).parent_path());
#else
return boost::filesystem::create_directories(boost::filesystem::path(path).parent_path());
#endif
}
bool Files::getParentPath(const char* path, char* parentPath)
{
if (!path || !parentPath) { return false; }
strcpy(parentPath, boost::filesystem::path(path).parent_path().string().c_str());
return true;
}
bool Files::getParentPath(const char* path, char* parentPath, const size_t size)
{
if (!path || !parentPath) { return false; }
strncpy(parentPath, boost::filesystem::path(path).parent_path().string().c_str(), size);
return true;
}
bool Files::getFilename(const char* path, char* filename)
{
if (!path || !filename) { return false; }
strcpy(filename, boost::filesystem::path(path).filename().string().c_str());
return true;
}
bool Files::getFilename(const char* path, char* filename, const size_t size)
{
if (!path || !filename) { return false; }
strncpy(filename, boost::filesystem::path(path).filename().string().c_str(), size);
return true;
}
bool Files::getFilenameWithoutExtension(const char* path, char* filename)
{
if (!path || !filename) { return false; }
strcpy(filename, boost::filesystem::path(path).filename().replace_extension("").string().c_str());
return true;
}
bool Files::getFilenameWithoutExtension(const char* path, char* filename, const size_t size)
{
if (!path || !filename) { return false; }
strncpy(filename, boost::filesystem::path(path).filename().replace_extension("").string().c_str(), size);
return true;
}
bool Files::getFilenameExtension(const char* path, char* fileNameExtension)
{
if (!path || !fileNameExtension) { return false; }
strcpy(fileNameExtension, boost::filesystem::path(path).extension().string().c_str());
return true;
}
bool Files::getFilenameExtension(const char* path, char* extension, const size_t size)
{
if (!path || !extension) { return false; }
strncpy(extension, boost::filesystem::path(path).extension().string().c_str(), size);
return true;
}
bool Files::remove(const char* path)
{
if (fileExists(path) || directoryExists(path))
{
#if defined TARGET_OS_Windows
const std::wstring pathUTF16 = Common::Converter::Utf8ToUtf16(path);
return boost::filesystem::remove(boost::filesystem::wpath(pathUTF16.c_str()));
#else
return boost::filesystem::remove(boost::filesystem::path(path));
#endif
}
return true;
}
bool Files::removeAll(const char* path)
{
if (fileExists(path) || directoryExists(path))
{
#if defined TARGET_OS_Windows
const std::wstring pathUTF16 = Common::Converter::Utf8ToUtf16(path);
return (remove_all(boost::filesystem::wpath(pathUTF16.c_str())) != 0);
#else
return (boost::filesystem::remove_all(boost::filesystem::path(path)) != 0);
#endif
}
return true;
}
// old boost compliance
// manage cases here
#if defined TARGET_HAS_Boost && BOOST_VERSION / 100 % 1000 >= 55
bool Files::copyFile(const char* srcFile, const char* dstPath)
{
if (!srcFile || !dstPath) { return false; }
#if defined TARGET_OS_Windows
std::wstring pathSourceUTF16 = Common::Converter::Utf8ToUtf16(srcFile);
std::wstring pathDestinationUTF16 = Common::Converter::Utf8ToUtf16(dstPath);
boost::filesystem::copy_file(pathSourceUTF16, pathDestinationUTF16);
#else
boost::filesystem::copy_file(srcFile, dstPath);
#endif
return true;
}
bool Files::copyDirectory(const char* srcDir, const char* dstDir)
{
if (!srcDir || !dstDir) { return false; }
#if defined TARGET_OS_Windows
const std::wstring pathSourceUTF16 = Common::Converter::Utf8ToUtf16(srcDir);
const std::wstring pathTargetUTF16 = Common::Converter::Utf8ToUtf16(dstDir);
const boost::filesystem::path source = boost::filesystem::wpath(pathSourceUTF16.c_str());
const boost::filesystem::path target = boost::filesystem::wpath(pathTargetUTF16.c_str());
#else
boost::filesystem::path source = srcDir;
boost::filesystem::path target = dstDir;
#endif
return recursiveCopy(source, target);
}
#elif defined TARGET_OS_Windows
#error OpenViBE requires at least boost 1.55 to compile on Windows
#else
// ugly hack for old boost on linux ...
bool Files::copyFile(const char* srcFile, const char* dstPath)
{
if(!srcFile || !dstPath || FS::Files::fileExists(dstPath)) { return false; }
std::string command = std::string("cp '") + srcFile + "' '" + dstPath+"'";
return (std::system(command.c_str()) != -1);
}
bool Files::copyDirectory(const char* srcDir, const char* dstDir)
{
if(!srcDir || !srcDir || FS::Files::directoryExists(dstDir)) { return false; }
std::string command = std::string("cp -r '") + srcDir + "' '" + dstDir+"'";
return (std::system(command.c_str()) != -1);
}
#endif
} // namespace FS
@@ -0,0 +1,277 @@
#include "IEntryEnumerator.h"
#include <string>
#include <cstring>
#include <stack>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <glob.h>
#include <sys/stat.h>
#elif defined TARGET_OS_Windows
#ifndef UNICODE
#define UNICODE
#endif
#include <Windows.h>
#include <codecvt>
#include <locale>
#else
#endif
#include <iostream>
namespace FS {
class CEntry final : public IEntryEnumerator::IEntry
{
public:
explicit CEntry(const std::string& name) : m_Name(name) {}
const char* getName() override { return m_Name.c_str(); }
std::string m_Name;
};
class CAttributes final : public IEntryEnumerator::IAttributes
{
public:
CAttributes() {}
~CAttributes() override {}
bool isFile() override { return m_IsFile; }
bool isDirectory() override { return m_IsDirectory; }
bool isSymbolicLink() override { return m_IsSymbolicLink; }
bool isArchive() override { return m_IsArchive; }
bool isReadOnly() override { return m_IsReadOnly; }
bool isHidden() override { return m_IsHidden; }
bool isSystem() override { return m_IsSystem; }
bool isExecutable() override { return m_IsExecutable; }
size_t getSize() override { return m_Size; }
bool m_IsFile = false;
bool m_IsDirectory = false;
bool m_IsSymbolicLink = false;
bool m_IsArchive = false;
bool m_IsReadOnly = false;
bool m_IsHidden = false;
bool m_IsSystem = false;
bool m_IsExecutable = false;
size_t m_Size = 0;
};
class CEntryEnumerator : public IEntryEnumerator
{
public:
explicit CEntryEnumerator(IEntryEnumeratorCallBack& rEntryEnumeratorCallBack) : m_entryEnumeratorCB(rEntryEnumeratorCallBack) {}
void release() override { delete this; }
protected:
IEntryEnumeratorCallBack& m_entryEnumeratorCB;
};
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
class CEntryEnumeratorLinux final : public CEntryEnumerator
{
public:
CEntryEnumeratorLinux(IEntryEnumeratorCallBack& rEntryEnumeratorCallBack) : CEntryEnumerator(rEntryEnumeratorCallBack) { }
virtual bool enumerate(const char* sWildCard, bool bRecursive=false);
};
#elif defined TARGET_OS_Windows
class CEntryEnumeratorWindows final : public CEntryEnumerator
{
public:
explicit CEntryEnumeratorWindows(IEntryEnumeratorCallBack& rEntryEnumeratorCallBack) : CEntryEnumerator(rEntryEnumeratorCallBack) {}
bool enumerate(const char* sWildCard, bool bRecursive = false) override;
};
#else
class CEntryEnumeratorDummy : public CEntryEnumerator
{
public:
explicit CEntryEnumeratorDummy(IEntryEnumeratorCallBack& rEntryEnumeratorCallBack) : CEntryEnumerator(rEntryEnumeratorCallBack) { }
virtual bool enumerate(const char* sWildCard, bool bRecursive=false) { return !sWildCard ? false : true; }
};
#endif
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool CEntryEnumeratorLinux::enumerate(const char* sWildCard, bool bRecursive)
{
if(!sWildCard) { return false; }
glob_t globStruc;
memset(&globStruc, GLOB_NOSORT, sizeof(globStruc));
// Glob can retrn
switch (glob(sWildCard, 0, nullptr, &globStruc))
{
case GLOB_NOSPACE:
case GLOB_ABORTED:
return false;
break;
case GLOB_NOMATCH:
return true;
break;
default:
break;
}
if(globStruc.gl_pathc<=0)
{
// Nothing found
return true;
}
size_t i=0;
bool finished=false;
while(!finished)
{
if(i<globStruc.gl_pathc)
{
char* name=globStruc.gl_pathv[i];
CEntry entry(name);
CAttributes att;
struct stat sL;
struct stat s;
if(!lstat(name, &sL) && !stat(name, &s))
{
att.m_IsDirectory = S_ISDIR(s.st_mode) ? true : false;
att.m_IsFile = S_ISREG(s.st_mode) ? true : false;
att.m_IsSymbolicLink = S_ISLNK(sL.st_mode) ? true : false;
att.m_IsArchive = false;
att.m_IsReadOnly = s.st_mode&S_IWUSR ? false : true;
att.m_IsHidden = false;
att.m_IsSystem = S_ISBLK(s.st_mode)|S_ISFIFO(s.st_mode)|S_ISSOCK(s.st_mode)|S_ISCHR(s.st_mode) ? true : false;
att.m_IsExecutable = s.st_mode&S_IXUSR ? true : false;
att.m_Size=s.st_size;
// Sends to callback
if(!m_entryEnumeratorCB.callback(entry, att)) { finished=true; }
}
i++;
}
else { finished = true; }
}
return true;
}
#elif defined TARGET_OS_Windows
bool CEntryEnumeratorWindows::enumerate(const char* sWildCard, bool bRecursive)
{
if (!sWildCard || strlen(sWildCard) == 0) { return false; }
wchar_t wildCardUtf16[1024];
MultiByteToWideChar(CP_UTF8, 0, sWildCard, -1, wildCardUtf16, 1024);
// $$$ TODO
// $$$ Find better method to enumerate files
// $$$ under windows including their initial path
// $$$ (cFileName member of WIN32_FIND_DATA structure
// $$$ loses the initial path !!)
// $$$ TODO
wchar_t extendedWildCard[1024];
wchar_t* extendedWildCardFilename = nullptr;
GetFullPathName(wildCardUtf16, 1024, extendedWildCard, &extendedWildCardFilename);
std::wstring path(wildCardUtf16, wcslen(wildCardUtf16) - (extendedWildCardFilename ? wcslen(extendedWildCardFilename) : 0));
std::stack<std::wstring> foldersToEnumerate;
foldersToEnumerate.push(path);
// if we need to recurse over subfolders, let's fetch all subfolders in foldersToEnumerate
if (bRecursive)
{
std::stack<std::wstring> temporaryFolderSearchStack;
temporaryFolderSearchStack.push(path);
std::wstring currentSearchPath;
while (! temporaryFolderSearchStack.empty())
{
currentSearchPath = temporaryFolderSearchStack.top();
temporaryFolderSearchStack.pop();
WIN32_FIND_DATA findData;
HANDLE fileHandle;
fileHandle = FindFirstFile((currentSearchPath + L"*").c_str(), &findData);
if (fileHandle != INVALID_HANDLE_VALUE)
{
bool finished = false;
while (!finished)
{
if (std::wstring(findData.cFileName) != L"." && std::wstring(findData.cFileName) != L"..")
{
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
foldersToEnumerate.push(currentSearchPath + findData.cFileName + L"/");
temporaryFolderSearchStack.push(currentSearchPath + findData.cFileName + L"/");
}
}
if (!FindNextFile(fileHandle, &findData)) { finished = true; }
}
FindClose(fileHandle);
}
}
}
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::wstring currentPath;
while (! foldersToEnumerate.empty())
{
currentPath = foldersToEnumerate.top();
foldersToEnumerate.pop();
WIN32_FIND_DATA findData;
HANDLE fileHandle;
fileHandle = FindFirstFile((currentPath + extendedWildCardFilename).c_str(), &findData);
if (fileHandle != INVALID_HANDLE_VALUE)
{
bool finished = false;
while (!finished)
{
std::string entryName = converter.to_bytes(currentPath + findData.cFileName);
CEntry entry(entryName);
CAttributes attributes;
attributes.m_IsDirectory = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;
attributes.m_IsFile = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? false : true;
attributes.m_IsSymbolicLink = false;
attributes.m_IsArchive = (findData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ? true : false;
attributes.m_IsReadOnly = (findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? true : false;
attributes.m_IsHidden = (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? true : false;
attributes.m_IsSystem = (findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? true : false;
attributes.m_IsExecutable = false; // TODO
attributes.m_Size = (findData.nFileSizeHigh << 16) + findData.nFileSizeLow;
// Sends to callback
if (!m_entryEnumeratorCB.callback(entry, attributes)) { finished = true; }
if (!FindNextFile(fileHandle, &findData)) { finished = true; }
}
FindClose(fileHandle);
}
}
return true;
}
#endif
FS_API IEntryEnumerator* createEntryEnumerator(IEntryEnumeratorCallBack& rCallBack)
{
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
IEntryEnumerator* res = new CEntryEnumeratorLinux(rCallBack);
#elif defined TARGET_OS_Windows
IEntryEnumerator* res = new CEntryEnumeratorWindows(rCallBack);
#else
IEntryEnumerator* res = new CEntryEnumeratorDummy(rCallBack);
#endif
return res;
}
} // namespace FS
@@ -0,0 +1,73 @@
PROJECT(openvibe-module-socket)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl include/*.h include/*.hpp)
INCLUDE_DIRECTORIES(include/socket)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DSocket_Shared -DSocket_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DSocket_Static -DSocket_Exports")
IF(UNIX)
SET_PROPERTY(TARGET ${PROJECT_NAME}-static APPEND_STRING PROPERTY COMPILE_FLAGS " -fPIC")
ELSEIF(WIN32)
# SET_PROPERTY(TARGET ${PROJECT_NAME}-static APPEND_STRING PROPERTY COMPILE_FLAGS " /SAFESEH:NO")
# SET_PROPERTY(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY COMPILE_FLAGS " /SAFESEH:NO")
ENDIF()
INCLUDE("FindOpenViBECommon")
#INCLUDE("FindThirdPartyTVicPort")
# ---------------------------------
# Finds standard library winsock
# Adds library to target
# Adds include path
# ---------------------------------
IF(WIN32)
INCLUDE("OvSetWindowsSDKPath")
FIND_LIBRARY(LIB_STANDARD_MODULE_WINSOCK ws2_32 ${OV_MS_SDK_PATH}/lib)
IF(LIB_STANDARD_MODULE_WINSOCK)
MESSAGE(STATUS " Found ws2_32...")
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_STANDARD_MODULE_WINSOCK})
TARGET_LINK_LIBRARIES(${PROJECT_NAME}-static ${LIB_STANDARD_MODULE_WINSOCK})
ELSE(LIB_STANDARD_MODULE_WINSOCK)
MESSAGE(STATUS " FAILED to find ws2_32...")
ENDIF(LIB_STANDARD_MODULE_WINSOCK)
ENDIF(WIN32)
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
@@ -0,0 +1,34 @@
#pragma once
#include "defines.h"
#include <cstdlib> // For Unix Compatibility
namespace Socket {
class Socket_API IConnection
{
protected:
virtual bool open() = 0;
public:
virtual bool close() = 0;
virtual bool isReadyToSend(const size_t timeOut = 0) const = 0;
virtual bool isReadyToReceive(const size_t timeOut = 0) const = 0;
virtual size_t sendBuffer(const void* buffer, const size_t size) = 0;
virtual size_t receiveBuffer(void* buffer, const size_t size) = 0;
virtual bool sendBufferBlocking(const void* buffer, const size_t size) = 0;
virtual bool receiveBufferBlocking(void* buffer, const size_t size) = 0;
virtual bool isConnected() const = 0;
virtual void release() = 0;
protected:
virtual ~IConnection() = default;
};
} // namespace Socket
@@ -0,0 +1,90 @@
#pragma once
#include "IConnection.h"
#include <array>
#include <cstdio>
namespace Socket {
class Socket_API IConnectionBluetooth : public IConnection
{
public:
/**
* \brief Connect to the bluetooth device.
* \param[in] address the MAC address of the Bluetooth device.
* \return If the function succeeds, the return value is true, else false.
*/
virtual bool connect(const uint64_t address) = 0;
/**
* \brief Return the input serial pending byte count.
* \return The number of pending byte.
*/
virtual size_t getPendingByteCount() = 0;
/**
* \brief Flush the input serial buffer.
* \return If the function succeeds, the return value is true, else false.
*/
virtual const char* getLastError() const = 0;
/**
* \brief Clear the last error registered.
*/
virtual void clearError() = 0;
/**
* \brief List the paired bluetooth devices: Name and Bluetooth MAC address.
*
* \param[out] nPairedDevices the Bluetooth devices count.
* \param[out] names an array of Bluetooth names.
* \param[out] addresses an array of Bluetooth addresses.
*
* \return If the function succeeds, the return value is true, else false.
*/
virtual bool listPairedBluetoothDevices(size_t* nPairedDevices, char** names, uint64_t** addresses) = 0;
/**
* \brief Convert string MAC Bluetooth address to hexadecimal.
* \param[out] straddr string MAC Bluetooth address.
* \param[out] btaddr hexadecimal MAC Bluetooth address.
* \retval true in case of success.
* \retval false in case of failure: if the address does not match: %02x:%02x:%02x:%02x:%02x:%02x
*/
static bool string2BluetoothAddress(const char* straddr, uint64_t* btaddr)
{
std::array<uint32_t, 6> aaddr = { 0, 0, 0, 0, 0, 0 };
const int value = sscanf(straddr, "%02x:%02x:%02x:%02x:%02x:%02x", &aaddr[0], &aaddr[1], &aaddr[2], &aaddr[3], &aaddr[4], &aaddr[5]);
if (value != 6) { return false; }
*btaddr = 0;
for (size_t i = 0; i < 6; ++i)
{
const uint64_t tmpaddr = static_cast<uint64_t>(aaddr[i] & 0xff);
*btaddr = (*btaddr << 8) + tmpaddr;
}
return true;
}
protected:
#if defined TARGET_OS_Windows
static const unsigned char WIN_SOCKET_MAJOR_VERSION = 2; // Winsock major version to use
static const unsigned char WIN_SOCKET_MINOR_VERSION = 2; // Winsock minor version to use
static bool m_isWinsockInitialized;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#endif
};
extern Socket_API IConnectionBluetooth* createConnectionBluetooth();
} // namespace Socket
@@ -0,0 +1,14 @@
#pragma once
#include "IConnection.h"
namespace Socket {
class Socket_API IConnectionClient : public IConnection
{
public:
virtual bool connect(const char* serverName, const size_t serverPort, const size_t timeOut = 0xffffffff) = 0;
};
extern Socket_API IConnectionClient* createConnectionClient();
} // namespace Socket
@@ -0,0 +1,41 @@
#pragma once
#include "IConnection.h"
#include <string>
#if defined TARGET_OS_Windows
#include <windows.h>
#endif
namespace Socket {
/**
* \brief The IConnectionParallel class provides the possibility to communicate with a parallel port.
* On Windows, you must have TVicPort library installed (available for free: http://entechtaiwan.com/dev/port/index.shtm).
*/
class Socket_API IConnectionParallel : public IConnection
{
public:
virtual bool connect(const unsigned short port) = 0;
virtual std::string getLastError() = 0;
protected:
#if defined TARGET_OS_Windows
typedef bool (CALLBACK * port_open_t)();
typedef void (CALLBACK * port_close_t)();
typedef bool (CALLBACK * port_is_driver_opened_t)();
typedef bool (CALLBACK * port_write_t)(unsigned short, unsigned char);
HMODULE m_port = nullptr;
port_open_t m_portOpen = nullptr;
port_close_t m_portClose = nullptr;
port_is_driver_opened_t m_isDriverOpened = nullptr;
port_write_t m_portWrite = nullptr;
#endif
};
extern Socket_API IConnectionParallel* createConnectionParallel();
} // namespace Socket
@@ -0,0 +1,54 @@
#pragma once
#include "IConnection.h"
#include <string>
namespace Socket {
class Socket_API IConnectionSerial : public IConnection
{
public:
virtual bool connect(const char* url, const size_t baudRate) = 0;
/**
* \brief Return the input serial pending byte count.
* \return The number of pending byte.
*/
virtual size_t getPendingByteCount() = 0;
/**
* \brief Flush the input serial buffer.
* \return If the function succeeds, the return value is true, else false.
*/
virtual bool flush() = 0;
/**
* \brief Flush the input serial buffer.
* \return If the function succeeds, the return value is true, else false.
*/
virtual const char* getLastError() = 0;
/**
* \brief Checks if an error raised.
* \return True if the m_LastError is not empty, else false.
*/
virtual bool isErrorRaised() = 0;
/**
* \brief Clear the last error registered.
*/
virtual void clearError() = 0;
/**
* \brief Set tiemouts for read and write function.
* \param timeout [in] Timeout in deciseconds.
* \return True if succeed, else false.
*/
virtual bool setTimeouts(const size_t timeout) = 0;
protected:
void saveLastError();
};
extern Socket_API IConnectionSerial* createConnectionSerial();
} // namespace Socket
@@ -0,0 +1,34 @@
#pragma once
#include "TConnectionDelegate.h"
#include "IConnectionSerial.h"
namespace Socket {
struct Socket_API SConnectionSerialDelegate
{
SConnectionDelegate connectionDelegate;
bool (*fpConnect)(void*, const char*, const size_t);
size_t (*fpGetPendingByteCount)(void*);
bool (*fpFlush)(void*);
const char* (*fpGetLastError)(void*);
void (*fpSaveLastError)(void*);
// TODO for Android compatibility
//bool(*fpIsErrorRaised)(void*);
//void(*fpClearError)(void*);
//bool(*fSetTimeouts)(void*, const size_t decisecondsTimeout);
};
class Socket_API IConnectionSerialDelegate : public TConnectionDelegate<IConnectionSerial>
{
public:
IConnectionSerialDelegate(const SConnectionSerialDelegate connectionSerialDelegate)
: TConnectionDelegate<IConnectionSerial>(connectionSerialDelegate.connectionDelegate) { }
~IConnectionSerialDelegate() override { }
protected:
SConnectionSerialDelegate m_connectionSerialDelegate;
};
extern Socket_API IConnectionSerialDelegate* createConnectionSerialDelegate(SConnectionSerialDelegate connectionSerialDelegate);
} // namespace Socket
@@ -0,0 +1,29 @@
#pragma once
#include "IConnection.h"
namespace Socket {
class Socket_API IConnectionServer : public IConnection
{
public:
/*
* \brief Places a socket in a listening state on the specified port.
* \param port [in]: port on the one the socket should listen.
* when set to '0', the socket wil start on
* an available port.
*/
virtual bool listen(const size_t port) = 0;
virtual IConnection* accept() = 0;
/*
* \brief Returns the port on the one the server is listening.
* This is useful if you set the port to '0'.
* \param port [out]: port on the one the server is listening
*/
virtual bool getSocketPort(size_t& port) = 0;
};
extern Socket_API IConnectionServer* createConnectionServer();
} // namespace Socket
@@ -0,0 +1,54 @@
#pragma once
#include "IConnection.h"
namespace Socket {
struct SConnectionDelegate
{
void* data;
bool (*fpOpen)(void*);
bool (*fpClose)(void*);
bool (*fpIsReadyToSend)(void*, size_t);
bool (*fpIsReadyToReceive)(void*, size_t);
size_t (*fpSendBuffer)(void*, const void*, size_t);
size_t (*fpReceiveBuffer)(void*, void*, size_t);
bool (*fpSendBufferBlocking)(void*, const void*, size_t);
bool (*fpReceiveBufferBlocking)(void*, void*, size_t);
bool (*fpIsConnected)(void*);
bool (*fpRelease)(void*);
};
template <class T>
class Socket_API TConnectionDelegate : public T
{
public:
TConnectionDelegate(const SConnectionDelegate connectionDelegate) : m_connectionDelegate(connectionDelegate) { }
virtual bool close() { return m_connectionDelegate.fpClose(m_connectionDelegate.data); }
virtual bool isReadyToSend(const size_t timeOut) const { return m_connectionDelegate.fpIsReadyToSend(m_connectionDelegate.data, timeOut); }
virtual bool isReadyToReceive(const size_t timeOut) const { return m_connectionDelegate.fpIsReadyToReceive(m_connectionDelegate.data, timeOut); }
virtual size_t sendBuffer(const void* buffer, const size_t size) { return m_connectionDelegate.fpSendBuffer(m_connectionDelegate.data, buffer, size); }
virtual size_t receiveBuffer(void* buffer, const size_t size) { return m_connectionDelegate.fpReceiveBuffer(m_connectionDelegate.data, buffer, size); }
virtual bool sendBufferBlocking(const void* buffer, const size_t size)
{
return m_connectionDelegate.fpSendBufferBlocking(m_connectionDelegate.data, buffer, size);
}
virtual bool receiveBufferBlocking(void* buffer, const size_t bufferSize)
{
return m_connectionDelegate.fpReceiveBufferBlocking(m_connectionDelegate.data, buffer, bufferSize);
}
virtual bool isConnected() const { return m_connectionDelegate.fpIsConnected(m_connectionDelegate.data); }
virtual void release() { m_connectionDelegate.fpRelease(m_connectionDelegate.data); }
virtual ~TConnectionDelegate() { }
protected:
virtual bool open() { return m_connectionDelegate.fpOpen(m_connectionDelegate.data); }
SConnectionDelegate m_connectionDelegate;
};
} // namespace Socket
@@ -0,0 +1,28 @@
#pragma once
#include <ov_common_defines.h>
// Taken from
// - http://people.redhat.com/drepper/dsohowto.pdf
// - http://www.nedprod.com/programs/gccvisibility.html
#if defined Socket_Shared
# if defined TARGET_OS_Windows
# define Socket_API_Export __declspec(dllexport)
# define Socket_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
# define Socket_API_Export __attribute__((visibility("default")))
# define Socket_API_Import __attribute__((visibility("default")))
# else
# define Socket_API_Export
# define Socket_API_Import
# endif
#else
# define Socket_API_Export
# define Socket_API_Import
#endif
#if defined Socket_Exports
# define Socket_API Socket_API_Export
#else
# define Socket_API Socket_API_Import
#endif
@@ -0,0 +1,194 @@
#pragma once
#include "IConnection.h"
#include <iostream>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
// #include <netinet/in.h>
// #include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
// #include <netdb.h>
#include <ctime>
#if defined TARGET_OS_MacOS
#define Socket_SendFlags 0
#define Socket_ReceiveFlags 0
#else
#define Socket_SendFlags MSG_NOSIGNAL
#define Socket_ReceiveFlags MSG_NOSIGNAL
#endif
#elif defined TARGET_OS_Windows
#include <WinSock2.h>
#include <Windows.h>
#define Socket_SendFlags 0
#define Socket_ReceiveFlags 0
#else
#endif
namespace Socket {
static bool FD_ISSET_PROXY(const int fd, fd_set* set) { return FD_ISSET(fd, set) ? true : false; }
template <class T>
class TConnection : public T
{
public:
TConnection() : m_socket(-1)
{
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#elif defined TARGET_OS_Windows
const int versionHigh = 2;
const int versionLow = 0;
const WORD winsockVersion = MAKEWORD(versionHigh, versionLow);
WSADATA wsaData;
WSAStartup(winsockVersion, &wsaData);
#else
#endif
}
explicit TConnection(const int socket) : m_socket(socket)
{
#if defined TARGET_OS_Windows
const int versionHigh = 2;
const int versionLow = 0;
const WORD winsockVersion = MAKEWORD(versionHigh, versionLow);
WSADATA wsaData;
WSAStartup(winsockVersion, &wsaData);
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#else
#endif
}
virtual ~TConnection()
{
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#elif defined TARGET_OS_Windows
WSACleanup();
#else
#endif
}
protected:
virtual bool open()
{
if (isConnected()) { return false; }
m_socket = int(socket(AF_INET, SOCK_STREAM, 0));
if (m_socket == size_t(-1)) { return false; }
return true;
}
public:
virtual bool close()
{
if (!isConnected()) { return false; }
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
::shutdown(m_socket, SHUT_RDWR);
::close(m_socket);
#elif defined TARGET_OS_Windows
shutdown(m_socket, SD_BOTH);
closesocket(m_socket);
#else
#endif
m_socket = -1;
return true;
}
virtual bool isReadyToSend(const size_t timeOut = 0) const
{
if (!isConnected()) { return false; }
struct timeval timeVal;
timeVal.tv_sec = int(timeOut / 1000);
timeVal.tv_usec = int((timeOut - timeVal.tv_sec * 1000) * 1000);
fd_set writeFileDesc;
FD_ZERO(&writeFileDesc);
FD_SET(m_socket, &writeFileDesc);
if (select(int(m_socket + 1), nullptr, &writeFileDesc, nullptr, &timeVal) < 0) { return false; }
if (!FD_ISSET_PROXY(int(m_socket), &writeFileDesc)) { return false; }
return true;
}
virtual bool isReadyToReceive(const size_t timeOut = 0) const
{
if (!isConnected()) { return false; }
struct timeval timeVal;
timeVal.tv_sec = int(timeOut / 1000);
timeVal.tv_usec = int((timeOut - timeVal.tv_sec * 1000) * 1000);
fd_set readFileDesc;
FD_ZERO(&readFileDesc);
FD_SET(m_socket, &readFileDesc);
if (select(int(m_socket + 1), &readFileDesc, nullptr, nullptr, &timeVal) < 0) { return false; }
if (!(FD_ISSET_PROXY(int(m_socket), &readFileDesc))) { return false; }
return true;
}
virtual size_t sendBuffer(const void* buffer, const size_t size)
{
if (!isConnected()) { return 0; }
const int res = send(m_socket, static_cast<const char*>(buffer), int(size), Socket_SendFlags);
if (size != 0 && res <= 0) { close(); }
return res <= 0 ? 0 : size_t(res);
}
virtual size_t receiveBuffer(void* buffer, const size_t size)
{
if (!isConnected() || !size) { return 0; }
const int res = recv(m_socket, static_cast<char*>(buffer), int(size), Socket_ReceiveFlags);
if (size != 0 && res <= 0) { close(); }
return res <= 0 ? 0 : size_t(res);
}
virtual bool sendBufferBlocking(const void* buffer, const size_t size)
{
size_t leftBytes = size;
const char* tmpBuffer = static_cast<const char*>(buffer);
do
{
leftBytes -= sendBuffer(tmpBuffer + size - leftBytes, leftBytes);
if (!isConnected()) { return false; }
} while (leftBytes != 0);
return true;
}
virtual bool receiveBufferBlocking(void* buffer, const size_t size)
{
size_t leftBytes = size;
char* tmpBuffer = static_cast<char*>(buffer);
do
{
leftBytes -= receiveBuffer(tmpBuffer + size - leftBytes, leftBytes);
if (!isConnected()) { return false; }
} while (leftBytes != 0);
return true;
}
virtual bool isConnected() const { return m_socket != size_t(-1); }
virtual void release()
{
if (isConnected()) { close(); }
delete this;
}
protected:
size_t m_socket;
};
} // namespace Socket
@@ -0,0 +1,442 @@
#include "IConnectionBluetooth.h"
#if defined TARGET_OS_Windows
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#include <Windows.h>
#include <CommCtrl.h>
#include <codecvt>
#include <WinSock2.h>
#include <ws2bth.h>
#include <bluetoothapis.h>
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#else
#error "Unsupported platform"
#endif
#include <assert.h>
#include <vector>
#include <string>
namespace Socket {
class CConnectionBluetooth final : public IConnectionBluetooth
{
public:
#if defined TARGET_OS_Windows
CConnectionBluetooth() : m_Socket(INVALID_SOCKET) { }
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
CConnectionBluetooth() : m_LastError() { }
#endif
#if defined TARGET_OS_Windows
bool initialize()
{
WSADATA wsaData;
// Ask for Winsock version.
if (_WINSOCK2API_::WSAStartup(MAKEWORD(WIN_SOCKET_MAJOR_VERSION, WIN_SOCKET_MINOR_VERSION), &wsaData) != 0)
{
m_LastError = "Failed to start Winsock " + std::to_string(WIN_SOCKET_MAJOR_VERSION) + "." + std::to_string(WIN_SOCKET_MINOR_VERSION) + ": " + this->
getLastErrorFormated();
return false;
}
// Confirm that the WinSock DLL supports version requested.
// Note that if the DLL supports versions greater than the version requested, in addition to the version requested, it will still return the version requested in wVersion.
if (LOBYTE(wsaData.wVersion) != WIN_SOCKET_MAJOR_VERSION || HIBYTE(wsaData.wVersion) != WIN_SOCKET_MINOR_VERSION)
{
m_LastError = "Could not find a usable version of Winsock.dll.";
_WINSOCK2API_::WSACleanup();
return false;
}
return true;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool initialize() { return false; }
#endif
bool open() override { return false; }
bool close() override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return false;
}
#if defined TARGET_OS_Windows
bool isSuccess = true;
if (m_Socket != INVALID_SOCKET)
{
// shutdown the connection since no more data will be sent or received
if (_WINSOCK2API_::shutdown(m_Socket, SD_BOTH) == SOCKET_ERROR)
{
m_LastError = "Failed to shutdown the bluetooth socket:" + this->getLastErrorFormated();
isSuccess = false;
}
if (_WINSOCK2API_::closesocket(m_Socket) == SOCKET_ERROR)
{
m_LastError = "Failed to close the bluetooth socket:" + this->getLastErrorFormated();
isSuccess = false;
}
if (_WINSOCK2API_::WSACleanup() == SOCKET_ERROR)
{
m_LastError = "Failed to cleanup the bluetooth socket:" + this->getLastErrorFormated();
isSuccess = false;
}
m_Socket = INVALID_SOCKET;
}
return isSuccess;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
bool isReadyToSend(const size_t /*timeOut*/) const override { return this->isConnected(); }
bool isReadyToReceive(const size_t /*timeOut*/) const override
{
if (!this->isConnected()) { return false; }
#if defined TARGET_OS_Windows
unsigned long nPendingBytes = 0;
if (_WINSOCK2API_::ioctlsocket(m_Socket, FIONREAD, &nPendingBytes) == SOCKET_ERROR)
{
//m_LastError = "Failed to get the pending bytes count: " + this->getLastErrorFormated();
return false;
}
return nPendingBytes != 0;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
size_t getPendingByteCount() override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device not connected.";
return 0;
}
#if defined TARGET_OS_Windows
unsigned long nPendingBytes = 0;
if (_WINSOCK2API_::ioctlsocket(m_Socket, FIONREAD, &nPendingBytes) == SOCKET_ERROR)
{
m_LastError = "Failed to get the pending bytes count: " + this->getLastErrorFormated();
return 0;
}
return nPendingBytes;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return 0;
#endif
}
size_t sendBuffer(const void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return 0;
}
#if defined TARGET_OS_Windows
const int nBytesSent = _WINSOCK2API_::send(m_Socket, reinterpret_cast<const char*>(buffer), int(size), 0);
if (nBytesSent == SOCKET_ERROR)
{
m_LastError = "Failed to write on the bluetooth port: " + getLastErrorFormated();
this->close();
return 0;
}
return size_t(nBytesSent);
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return 0;
#endif
}
size_t receiveBuffer(void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return 0;
}
#if defined TARGET_OS_Windows
const int nBytesReceived = _WINSOCK2API_::recv(m_Socket, static_cast<char*>(buffer), int(size), 0);
if (nBytesReceived == SOCKET_ERROR)
{
m_LastError = "Failed to receive data from bluetooth: " + getLastErrorFormated();
this->close();
return 0;
}
return size_t(nBytesReceived);
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return 0;
#endif
}
bool sendBufferBlocking(const void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return false;
}
const char* p = reinterpret_cast<const char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected())
{
bytesLeft -= this->sendBuffer(p + size - bytesLeft, bytesLeft);
if (this->isErrorRaised()) { return false; }
}
return bytesLeft == 0;
}
bool receiveBufferBlocking(void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return false;
}
char* p = reinterpret_cast<char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected())
{
bytesLeft -= this->receiveBuffer(p + size - bytesLeft, bytesLeft);
if (this->isErrorRaised()) { return false; }
}
return bytesLeft == 0;
}
bool isConnected() const override
{
#if defined TARGET_OS_Windows
return m_Socket != INVALID_SOCKET;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
void release() override { delete this; }
bool connect(const uint64_t u64BluetoothAddress) override
{
m_LastError.clear();
if (this->isConnected())
{
m_LastError = "Bluetooth device is already connected";
return false;
}
#if defined TARGET_OS_Windows
if (!this->initialize()) { return false; }
m_Socket = _WINSOCK2API_::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if (m_Socket == INVALID_SOCKET)
{
m_LastError = "Failed to create bluetooth socket: " + getLastErrorFormated();
_WINSOCK2API_::WSACleanup();
return false;
}
SOCKADDR_BTH sockAddressBlutoothServer;
sockAddressBlutoothServer.btAddr = u64BluetoothAddress;
sockAddressBlutoothServer.addressFamily = AF_BTH;
sockAddressBlutoothServer.serviceClassId = RFCOMM_PROTOCOL_UUID;
sockAddressBlutoothServer.port = BT_PORT_ANY;
if (_WINSOCK2API_::connect(m_Socket, reinterpret_cast<SOCKADDR*>(&sockAddressBlutoothServer), sizeof(SOCKADDR_BTH)) == SOCKET_ERROR)
{
m_LastError = "Failed to connect the socket to the bluetooth address [" + std::to_string(sockAddressBlutoothServer.btAddr) + "]: " +
getLastErrorFormated();
_WINSOCK2API_::closesocket(m_Socket); // Returned code not checked.
_WINSOCK2API_::WSACleanup(); // Returned code not checked.
m_Socket = INVALID_SOCKET;
return false;
}
return true;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
bool isErrorRaised() const { return !m_LastError.empty(); }
const char* getLastError() const override { return m_LastError.c_str(); }
static std::string getLastErrorFormated()
{
#if defined TARGET_OS_Windows
LPTSTR text;
const DWORD errCode = GetLastError();
const size_t size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | // use system message tables to retrieve error text
FORMAT_MESSAGE_ALLOCATE_BUFFER | // allocate buffer on local heap for error text
FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
errCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
LPTSTR(&text), // output
0, // minimum size for output buffer
nullptr);
// Converts std::wstring to std::string and returns it.
const std::wstring message(text, size);
LocalFree(text);
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
return converter.to_bytes(message);
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return "";
#endif
}
void clearError() override { m_LastError.clear(); }
bool listPairedBluetoothDevices(size_t* nPairedBluetoothDevices, char** names, uint64_t** addresses) override
{
std::vector<std::string> devicesNames;
std::vector<uint64_t> devicesAddresses;
#if defined TARGET_OS_Windows
HANDLE handle;
WSAQUERYSET wsaQuerySet;
memset((void*)&wsaQuerySet, 0, sizeof(wsaQuerySet));
wsaQuerySet.dwSize = sizeof(wsaQuerySet);
wsaQuerySet.dwNameSpace = NS_BTH;
wsaQuerySet.lpcsaBuffer = nullptr;
if (_WINSOCK2API_::WSALookupServiceBegin(&wsaQuerySet, LUP_CONTAINERS | LUP_RETURN_NAME | LUP_RETURN_ADDR, &handle) == SOCKET_ERROR)
{
m_LastError = "Failed to start the Bluetooth lookup service: " + getLastErrorFormated();
return false;
}
char buffer[5000];
const LPWSAQUERYSET wsaQuerySetW = LPWSAQUERYSET(buffer);
DWORD size = sizeof(buffer);
memset((void*)wsaQuerySetW, 0, sizeof(WSAQUERYSET));
wsaQuerySetW->dwSize = sizeof(WSAQUERYSET);
wsaQuerySetW->dwNameSpace = NS_BTH;
wsaQuerySetW->lpBlob = nullptr;
bool lookup = true;
while (lookup)
{
// Check next bluetooth device
const int res = _WINSOCK2API_::WSALookupServiceNext(handle, LUP_RETURN_NAME | LUP_RETURN_ADDR, &size, wsaQuerySetW);
if (res == SOCKET_ERROR)
{
// If it is a "real" error, we trace it and return false.
if (_WINSOCK2API_::WSAGetLastError() != WSA_E_NO_MORE)
{
m_LastError = "Lookup service next operation failed: " + getLastErrorFormated();
return false;
}
// Else, it is because there is no more Bluetooth devices available.
lookup = false;
break;
}
// Get bluetooth MAC address and name
devicesAddresses.push_back(reinterpret_cast<SOCKADDR_BTH*>(wsaQuerySetW->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr);
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converterX;
devicesNames.push_back(converterX.to_bytes(wsaQuerySetW->lpszServiceInstanceName));
}
if (_WINSOCK2API_::WSALookupServiceEnd(handle) == SOCKET_ERROR)
{
m_LastError = "Failed to stop the Bluetooth lookup service: " + getLastErrorFormated();
return false;
}
*nPairedBluetoothDevices = devicesAddresses.size();
names = new char*[*nPairedBluetoothDevices];
for (size_t i = 0; i < *nPairedBluetoothDevices; ++i)
{
names[i] = new char[devicesNames[i].size() + 1];
std::strcpy(names[i], devicesNames[i].c_str());
}
addresses = new uint64_t*[*nPairedBluetoothDevices];
return true;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
std::string m_LastError;
#if defined TARGET_OS_Windows
SOCKET m_Socket;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#endif
};
IConnectionBluetooth* createConnectionBluetooth() { return new CConnectionBluetooth(); }
} // namespace Socket
@@ -0,0 +1,171 @@
#include "IConnection.h"
#include "IConnectionClient.h"
#include "IConnection.inl"
#include <cstring>
#include <iostream>
#include <fcntl.h>
#include <cerrno>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <unistd.h>
#elif defined TARGET_OS_Windows
#include <cerrno>
#include <WS2tcpip.h>
#else
#endif
namespace Socket {
class CConnectionClient final : public TConnection<IConnectionClient>
{
public:
bool connect(const char* sServerName, const size_t serverPort, const size_t timeOut) override
{
if (!open()) { return false; }
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
long value;
// Sets non blocking
if((value = ::fcntl(m_socket, F_GETFL, nullptr)) < 0)
{
close();
return false;
}
value|=O_NONBLOCK;
if(::fcntl(m_socket, F_SETFL, value)<0)
{
close();
return false;
}
// Looks up host name
struct hostent* serverHostEntry = gethostbyname(sServerName);
if(!serverHostEntry)
{
close();
return false;
}
#elif defined TARGET_OS_Windows
// Sets non blocking
unsigned long mode = 1;
ioctlsocket(m_socket, FIONBIO, &mode);
struct addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
PADDRINFOA addr;
if (getaddrinfo(sServerName, nullptr, &hints, &addr) != 0)
{
close();
return false;
}
const auto sockaddrIPV4 = reinterpret_cast<sockaddr_in*>(addr->ai_addr);
#endif
// Connects
struct sockaddr_in serverAddress;
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(static_cast<unsigned short>(serverPort));
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
serverAddress.sin_addr=*((struct in_addr*)serverHostEntry->h_addr);
#elif defined TARGET_OS_Windows
serverAddress.sin_addr = sockaddrIPV4->sin_addr;
freeaddrinfo(addr);
#endif
errno = 0;
if (::connect(m_socket, reinterpret_cast<struct sockaddr*>(&serverAddress), sizeof(struct sockaddr_in)) < 0)
{
bool inProgress;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
inProgress = (errno==EINPROGRESS);
#elif defined TARGET_OS_Windows
inProgress = (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK);
#else
inProgress = false;
#endif
if (inProgress)
{
const int t = (timeOut == 0xffffffff) ? 125 : int(timeOut);
// Performs time out
struct timeval timeVal;
timeVal.tv_sec = (t / 1000);
timeVal.tv_usec = ((t - timeVal.tv_sec * 1000) * 1000);
fd_set writeFileDesc;
FD_ZERO(&writeFileDesc);
FD_SET(m_socket, &writeFileDesc);
if (select(int(m_socket + 1), nullptr, &writeFileDesc, nullptr, &timeVal) < 0)
{
close();
return false;
}
if (!FD_ISSET_PROXY(int(m_socket), &writeFileDesc))
{
close();
return false;
}
// Checks error status
int option = 0;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
socklen_t length = sizeof(option);
::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (void*)(&option), &length);
#elif defined TARGET_OS_Windows
int length = sizeof(option);
getsockopt(m_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&option), &length);
#endif
if (option != 0)
{
close();
return false;
}
}
else
{
close();
return false;
}
}
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
// Sets back to blocking
if((value=::fcntl(m_socket, F_GETFL, nullptr))<0)
{
close();
return false;
}
value&=~O_NONBLOCK;
if(::fcntl(m_socket, F_SETFL, value)<0)
{
close();
return false;
}
#elif defined TARGET_OS_Windows
// Sets back to blocking
mode = 0;
ioctlsocket(m_socket, FIONBIO, &mode);
#endif
return true;
}
};
IConnectionClient* createConnectionClient() { return new CConnectionClient(); }
} // namespace Socket
@@ -0,0 +1,261 @@
#include "IConnectionParallel.h"
#if defined TARGET_OS_Windows
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <sys/ioctl.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/errno.h>
#else
#error "Unsupported platform"
#endif
#if defined TARGET_OS_Linux && !defined TARGET_OS_Android
#include <linux/ppdev.h>
#include <linux/parport.h>
#endif
#include <assert.h>
namespace Socket {
class CConnectionParallel final : public IConnectionParallel
{
protected:
unsigned short m_portNumber;
std::string m_lastError;
public:
CConnectionParallel()
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
:m_file(0)
#endif
{
#if defined TARGET_OS_Windows
m_portNumber = 0;
m_port = LoadLibrary(TEXT("TVicPort.dll"));
if (m_port != nullptr)
{
m_isDriverOpened = port_is_driver_opened_t(GetProcAddress(m_port, "IsDriverOpened"));
m_portOpen = port_open_t(GetProcAddress(m_port, "OpenTVicPort"));
m_portClose = port_close_t(GetProcAddress(m_port, "CloseTVicPort"));
m_portWrite = port_write_t(GetProcAddress(m_port, "WritePort"));
if (!m_isDriverOpened || !m_portOpen || !m_portClose || !m_portWrite)
{
m_lastError = "Cannot load function from TVicPort.dll: " + this->getLastErrorFormated();
this->release();
}
}
else { m_lastError = "Cannot found or open TVicPort.dll: " + this->getLastErrorFormated(); }
#endif
}
bool open() override
{
// Should never be used
return false;
}
bool close() override
{
#if defined TARGET_OS_Windows
if (m_port != nullptr)
{
if (m_isDriverOpened())
{
m_portNumber = 0;
m_portClose();
return true;
}
m_lastError = "Cannot close the TVicPort library because it is not opened.";
return false;
}
return false;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
/*if (ioctl(fd, PPRELEASE) < 0) { return false; }
if (close(m_file) < 0) { return false; }
else
{
m_file = -1;
}*/
#endif
}
bool isReadyToSend(const size_t /*timeOut*/ = 0) const override { return this->isConnected(); }
bool isReadyToReceive(const size_t /*timeOut*/ = 0) const override { return this->isConnected(); }
size_t getPendingByteCount() const { return (this->isConnected() ? 0 : 1); }
size_t sendBuffer(const void* buffer, const size_t size = 8) override
{
if (!this->isConnected()) { return 0; }
#if defined TARGET_OS_Windows
const uint8_t value = *(static_cast<const uint8_t*>(buffer));
m_portWrite(m_portNumber, value);
return size;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return 0;
/*if (ioctl(m_file, PPWDATA, &value) < 0) { return size; }*/
#endif
}
size_t receiveBuffer(void* /*buffer*/, const size_t /*size*/ = 8) override
{
if (!this->isConnected()) { return 0; }
#if defined TARGET_OS_Windows
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
/*
unsigned char valin;
int dirin=1;
int dirout=0;
if (ioctl (fd, PPDATADIR, &dirin) < 0) { return 0; }
if (ioctl(fd, PPRDATA, &valin) < 0) { return 0; }
int res = ::read(m_file, buffer, size);
if(res < 0)
{
this->close();
return 0;
}
return res;
*/
#endif
return 0;
}
bool sendBufferBlocking(const void* buffer, const size_t size) override
{
const char* p = reinterpret_cast<const char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected()) { bytesLeft -= this->sendBuffer(p + size - bytesLeft, bytesLeft); }
return this->isConnected();
}
bool receiveBufferBlocking(void* buffer, const size_t size) override
{
char* p = reinterpret_cast<char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected()) { bytesLeft -= this->receiveBuffer(p + size - bytesLeft, bytesLeft); }
return this->isConnected();
}
#if defined TARGET_OS_Windows
bool isConnected() const override { return m_portNumber != 0; }
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool isConnected() const override { return m_file != 0; }
#endif
void release() override
{
#if defined TARGET_OS_Windows
if (m_port != nullptr && !FreeLibrary(m_port)) { m_lastError = getLastErrorFormated(); }
#endif
delete this;
}
bool connect(const unsigned short portNumber) override
{
if (this->isConnected()) { return false; }
#if defined TARGET_OS_Windows
if (m_port != nullptr)
{
if (m_portOpen())
{
m_lastError = "No error";
m_portNumber = portNumber;
return true;
}
m_lastError = "Cannot open the TVic library";
m_portNumber = 0;
return false;
}
m_lastError = "TVicPort library is not loaded.";
return false;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
/*
std::string url = "/dev/parport" + std::to_string(portNumber);
if ((m_file = open(url.c_str() , O_RDWR)) < 0)
{
this->close();
return false;
}
else
{
if (ioctl(m_file, PPCLAIM) < 0)
{
this->close();
return false;
}
}
*/
#endif
}
std::string getLastError() override { return m_lastError; }
std::string getLastErrorFormated()
{
#if defined TARGET_OS_Windows
LPTSTR errorText;
const DWORD error = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM // use system message tables to retrieve error text
| FORMAT_MESSAGE_ALLOCATE_BUFFER // allocate buffer on local heap for error text
| FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPTSTR(&errorText), // output
0, // minimum size for output buffer
nullptr); // arguments - see note
return errorText + std::to_string(static_cast<uint64_t>(error));
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return "Not implemented.";
#endif
}
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int m_file;
#endif
};
IConnectionParallel* createConnectionParallel() { return new CConnectionParallel(); }
} // namespace Socket
@@ -0,0 +1,475 @@
#include "IConnectionSerial.h"
#if defined TARGET_OS_Windows
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#else
#error "Unsupported platform"
#endif
#include <assert.h>
#include <iostream>
#if defined TARGET_OS_Linux
// Good resources at http://www.tldp.org/HOWTO/html_single/Serial-HOWTO/
#endif
namespace Socket {
class CConnectionSerial final : public IConnectionSerial
{
public:
#if defined TARGET_OS_Windows
CConnectionSerial() : m_file(nullptr) { }
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
CConnectionSerial() : m_file(0) { }
#endif
bool open() override { return false; } // Should never be used
bool close() override
{
#if defined TARGET_OS_Windows
if (m_file != nullptr)
{
if (!CloseHandle(m_file))
{
m_LastError = "Failed to close the serial port:" + this->getLastErrorFormated();
m_file = nullptr;
return false;
}
m_file = nullptr;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
if(m_file != -1)
{
::close(m_file);
m_file=-1;
}
#endif
return true;
}
#if defined TARGET_OS_Windows
bool isReadyToSend(const size_t /*timeOut*/) const override
{
if (!this->isConnected()) { return false; }
return true;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool isReadyToSend(const size_t timeOut) const override
{
if (!this->isConnected()) { return false; }
fd_set fileDescSet;
struct timeval time;
time.tv_sec = timeOut/1000;
time.tv_usec = (timeOut%1000)*1000;
FD_ZERO(&fileDescSet);
FD_SET(m_file, &fileDescSet);
if(!::select(m_file + 1, nullptr, &fileDescSet, nullptr, &time)) { return false; }
if(FD_ISSET(m_file, &fileDescSet)) { return true; }
return false;
}
#endif
#if defined TARGET_OS_Windows
bool isReadyToReceive(const size_t /*timeOut*/) const override
{
if (!this->isConnected()) { return false; }
struct _COMSTAT status;
DWORD state;
if (ClearCommError(m_file, &state, &status) != 0) { return status.cbInQue != 0; }
return false;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool isReadyToReceive(const size_t timeOut) const override
{
if (!this->isConnected()) { return false; }
fd_set fileDescSet;
struct timeval time;
time.tv_sec=timeOut/1000;
time.tv_usec=(timeOut%1000)*1000;
FD_ZERO(&fileDescSet);
FD_SET(m_file, &fileDescSet);
if(!::select(m_file+1, &fileDescSet, nullptr, nullptr, &time)) { return false; }
if(FD_ISSET(m_file, &fileDescSet)) { return true; }
return false;
}
#endif
size_t getPendingByteCount() override
{
if (!this->isConnected())
{
m_LastError = "Serial port not connected.";
return 0;
}
#if defined TARGET_OS_Windows
struct _COMSTAT status;
DWORD state;
if (ClearCommError(m_file, &state, &status) == 0)
{
m_LastError = "Failed to clear the serial port communication error: " + this->getLastErrorFormated();
return 0;
}
return status.cbInQue;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int byteCount=0;
if(-1 == ::ioctl(m_file, FIONREAD, &byteCount))
{
m_LastError = "Failed to querry pending bytes in ioctl";
return 0;
}
return byteCount;
#endif
}
bool flush() override
{
if (!this->isConnected())
{
m_LastError = "Serial port not connected.";
return false;
}
#if defined TARGET_OS_Windows
if (!FlushFileBuffers(m_file))
{
m_LastError = "Failed to flush serial port buffer: " + this->getLastErrorFormated();
return false;
}
return true;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
if(-1 == ::tcdrain(m_file))
{
m_LastError = "Could not flush connection in tcdrain";
return false;
}
return true;
#else
return false;
#endif
}
size_t sendBuffer(const void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Serial port not connected.";
return 0;
}
#if defined TARGET_OS_Windows
DWORD written = 0;
if (!WriteFile(m_file, buffer, DWORD(size), &written, nullptr))
{
m_LastError = "Failed to write on serial port: " + this->getLastErrorFormated();
this->close();
return 0;
}
if (written == 0)
{
m_LastError = "Serial port timeout when trying to write.";
this->close();
return 0;
}
return written;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int res = ::write(m_file, buffer, size);
if(res < 0)
{
m_LastError = "Could not write on connection";
this->close();
return 0;
}
return res;
#else
return 0;
#endif
}
size_t receiveBuffer(void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Serial port not connected.";
return 0;
}
#if defined TARGET_OS_Windows
DWORD read = 0;
if (!ReadFile(m_file, buffer, DWORD(size), &read, nullptr))
{
m_LastError = "Failed to read on serial port: " + this->getLastErrorFormated();
this->close();
return 0;
}
if (read == 0)
{
m_LastError = "Serial port timeout when trying to read.";
this->close();
return 0;
}
return read;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int res = ::read(m_file, buffer, size);
if (res < 0)
{
m_LastError = "Could not read from connection";
this->close();
return 0;
}
return res;
#else
return 0;
#endif
}
bool sendBufferBlocking(const void* buffer, const size_t size) override
{
const char* p = reinterpret_cast<const char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected())
{
bytesLeft -= this->sendBuffer(p + size - bytesLeft, bytesLeft);
if (this->isErrorRaised()) { return false; }
}
return bytesLeft == 0;
}
bool receiveBufferBlocking(void* buffer, const size_t size) override
{
char* p = reinterpret_cast<char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected())
{
bytesLeft -= this->receiveBuffer(p + size - bytesLeft, bytesLeft);
if (this->isErrorRaised()) { return false; }
}
return bytesLeft == 0;
}
bool isConnected() const override
{
#if defined TARGET_OS_Windows
return ((m_file != nullptr) && (m_file != INVALID_HANDLE_VALUE));
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return m_file != 0;
#endif
}
void release() override { delete this; }
bool connect(const char* sURL, const size_t baudrate) override
{
m_LastError.clear();
if (this->isConnected())
{
m_LastError = "Serial port already connected";
return false;
}
#if defined TARGET_OS_Windows
m_file = ::CreateFile(sURL, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (m_file == INVALID_HANDLE_VALUE || m_file == nullptr)
{
m_LastError = "Failed to open serial port: " + this->getLastErrorFormated();
m_file = nullptr;
return false;
}
DCB dcb = { 0 };
if (!GetCommState(m_file, &dcb))
{
m_LastError = "Failed to get communication state: " + this->getLastErrorFormated();
this->close();
return false;
}
dcb.DCBlength = sizeof(dcb);
dcb.BaudRate = DWORD(baudrate);
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
ClearCommError(m_file, nullptr, nullptr);
if (!SetCommState(m_file, &dcb))
{
m_LastError = "Could not set communication state: " + this->getLastErrorFormated();
this->close();
return false;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
/* $$$ BAUD RATE PARAMETER IS NOT CONSIDERED ON LINUX, MIGHT BE NECESSARY TO CHANGE THIS $$$ */
assert(baudrate == 230400);
if ((m_file = ::open(sURL, O_RDWR)) == -1)
{
m_LastError = "Failed to open serial port.";
return false;
}
struct termios terminalAtt;
if(::tcgetattr(m_file, &terminalAtt)!=0)
{
m_LastError = "Could not get terminal attributes in tcgetattr";
this->close();
return false;
}
terminalAtt.c_cflag = B230400 | CS8 | CLOCAL | CREAD;
terminalAtt.c_iflag = 0;
terminalAtt.c_oflag = OPOST | ONLCR;
terminalAtt.c_lflag = 0;
if (::tcsetattr(m_file, TCSAFLUSH, &terminalAtt) != 0)
{
m_LastError = "Could not set terminal attributes in tcgetattr";
this->close();
return false;
}
#endif
return true;
}
bool setTimeouts(const size_t decisecondsTimeout) override
{
if (!this->isConnected()) { return false; }
#if defined TARGET_OS_Windows
COMMTIMEOUTS timeouts;
if (!GetCommTimeouts(m_file, &timeouts))
{
m_LastError = "Could not get communication timeouts: " + this->getLastErrorFormated();
this->close();
return false;
}
timeouts.ReadTotalTimeoutConstant = decisecondsTimeout * 100; // Deciseconds to milliseconds
timeouts.WriteTotalTimeoutConstant = decisecondsTimeout * 100; // Deciseconds to milliseconds
if (!SetCommTimeouts(m_file, &timeouts))
{
m_LastError = "Could not set communication timeouts: " + this->getLastErrorFormated();
this->close();
return false;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
struct termios terminalAtt;
if(::tcgetattr(m_file, &terminalAtt)!=0)
{
m_LastError = "Could not get terminal attributes in tcgetattr";
this->close();
return false;
}
terminalAtt.c_cc[VTIME] = decisecondsTimeout;
if (::tcsetattr(m_file, TCSAFLUSH, &terminalAtt) != 0)
{
m_LastError = "Could not set terminal attributes in tcgetattr";
this->close();
return false;
}
#endif
return true;
}
const char* getLastError() override { return m_LastError.c_str(); }
static std::string getLastErrorFormated()
{
#if defined TARGET_OS_Windows
LPTSTR errorText;
const DWORD error = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | // use system message tables to retrieve error text
FORMAT_MESSAGE_ALLOCATE_BUFFER | // allocate buffer on local heap for error text
FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPTSTR(&errorText), // output
0, // minimum size for output buffer
nullptr); // arguments - see note
return errorText;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return ""; // TODO
#endif
}
bool isErrorRaised() override { return !m_LastError.empty(); }
void clearError() override { m_LastError.clear(); }
std::string m_LastError;
#if defined TARGET_OS_Windows
void* m_file;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int m_file;
#endif
};
IConnectionSerial* createConnectionSerial() { return new CConnectionSerial(); }
} // namespace Socket
@@ -0,0 +1,36 @@
#include "IConnectionSerialDelegate.h"
namespace Socket {
class CConnectionSerialDelegate final : public IConnectionSerialDelegate
{
public:
explicit CConnectionSerialDelegate(const SConnectionSerialDelegate connectionSerialDelegate) : IConnectionSerialDelegate(connectionSerialDelegate)
{
m_connectionSerialDelegate = connectionSerialDelegate;
}
~CConnectionSerialDelegate() override { }
bool connect(const char* url, const size_t baudRate) override
{
return m_connectionSerialDelegate.fpConnect(m_connectionSerialDelegate.connectionDelegate.data, url, baudRate);
}
size_t getPendingByteCount() override { return m_connectionSerialDelegate.fpGetPendingByteCount(m_connectionSerialDelegate.connectionDelegate.data); }
bool flush() override { return m_connectionSerialDelegate.fpFlush(m_connectionSerialDelegate.connectionDelegate.data); }
const char* getLastError() override { return m_connectionSerialDelegate.fpGetLastError(m_connectionSerialDelegate.connectionDelegate.data); }
bool isErrorRaised()
override { return false; } // return m_connectionSerialDelegate.fpIsErrorRaised(m_connectionSerialDelegate.connectionDelegate.data);
void clearError() override { } // return m_connectionSerialDelegate.fpClearError(m_connectionSerialDelegate.connectionDelegate.data);
bool setTimeouts(const size_t /*timeout*/)
override { return true; } // return m_connectionSerialDelegate.fpSetTimeouts(m_connectionSerialDelegate.connectionDelegate.data, decisecondsTimeout);
};
IConnectionSerialDelegate* createConnectionSerialDelegate(const SConnectionSerialDelegate connectionSerialDelegate)
{
return new CConnectionSerialDelegate(connectionSerialDelegate);
}
} // namespace Socket
@@ -0,0 +1,112 @@
#include "IConnection.h"
#include "IConnectionServer.h"
#include "IConnection.inl"
#include <cstring>
#include <iostream>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <cerrno>
#elif defined TARGET_OS_Windows
#else
#endif
namespace Socket {
class CConnectionServer final : public TConnection<IConnectionServer>
{
public:
bool listen(const size_t port) override
{
if (!open()) { return false; }
int reuseAddress = 1;
#if defined TARGET_OS_Windows
setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&reuseAddress), sizeof(reuseAddress));
#else
::setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddress, sizeof(reuseAddress));
#endif
struct sockaddr_in localHostAddress;
memset(&localHostAddress, 0, sizeof(localHostAddress));
localHostAddress.sin_family = AF_INET;
localHostAddress.sin_port = htons(static_cast<unsigned short>(port));
localHostAddress.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(m_socket, reinterpret_cast<struct sockaddr*>(&localHostAddress), sizeof(localHostAddress)) == -1)
{
/*
switch(errno)
{
case EBADF: std::cout << "EBADF" << std::endl; break;
case ENOTSOCK: std::cout << "ENOTSOCK" << std::endl; break;
case EADDRINUSE: std::cout << "EADDRINUSE" << std::endl; break;
case EINVAL: std::cout << "EINVAL" << std::endl; break;
case EROFS: std::cout << "EROFS" << std::endl; break;
case EFAULT: std::cout << "EFAULT" << std::endl; break;
case ENAMETOOLONG: std::cout << "ENAMETOOLONG" << std::endl; break;
case ENOENT: std::cout << "ENOENT" << std::endl; break;
case ENOMEM: std::cout << "ENOMEM" << std::endl; break;
case ENOTDIR: std::cout << "ENOTDIR" << std::endl; break;
case EACCES: std::cout << "EACCES" << std::endl; break;
case ELOOP: std::cout << "ELOOP" << std::endl; break;
default: std::cout << "Bind_unknown" << std::endl;
}
*/
return false;
}
if (::listen(m_socket, 32) == -1)
{
/*
switch(errno)
{
case EADDRINUSE: std::cout << "EADDRINUSE" << std::endl; break;
case EBADF: std::cout << "EBADF" << std::endl; break;
case ENOTSOCK: std::cout << "ENOTSOCK" << std::endl; break;
case EOPNOTSUPP: std::cout << "EOPNOTSUPP" << std::endl; break;
default: std::cout << "Listen_unknown" << std::endl;
}
*/
return false;
}
return true;
}
IConnection* accept() override
{
struct sockaddr_in clientAddress;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
socklen_t clientAddressSize = sizeof(clientAddress);
#elif defined TARGET_OS_Windows
int clientAddressSize = sizeof(clientAddress);
#else
#endif
const int clientSocket = int(::accept(m_socket, reinterpret_cast<struct sockaddr*>(&clientAddress), &clientAddressSize));
if (clientSocket == -1) { return nullptr; }
return new TConnection<IConnection>(int(clientSocket));
}
bool getSocketPort(size_t& port) override
{
struct sockaddr_in socketInfo;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
socklen_t socketInfoLength = sizeof(socketInfo);
#elif defined TARGET_OS_Windows
int socketInfoLength = sizeof(socketInfo);
#endif
if (getsockname(m_socket, reinterpret_cast<sockaddr*>(&socketInfo), &socketInfoLength) == -1) { return false; }
port = size_t(ntohs(socketInfo.sin_port));
return true;
}
};
IConnectionServer* createConnectionServer() { return new CConnectionServer(); }
} // namespace Socket
@@ -0,0 +1,109 @@
PROJECT(openvibe-module-system)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
SET(SRC_FILES
include/system/defines.h
include/system/ovCChrono.h
include/system/ovCMath.h
include/system/ovCMemory.h
include/system/ovCTime.h
include/system/ovCDynamicModule.h
src/ovCChrono.cpp
src/ovCMath.cpp
src/ovCMemory.cpp
src/ovCTime.cpp
src/ovCDynamicModule.cpp
)
IF(WIN32)
LIST(APPEND SRC_FILES
"include/system/WindowsUtilities.h"
"src/WindowsUtilities.cpp"
)
ENDIF()
INCLUDE_DIRECTORIES(include)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DSystem_Shared -DSystem_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DSystem_Static -DSystem_Exports")
IF(UNIX)
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF()
INCLUDE("FindOpenViBECommon")
IF(WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.0")
INCLUDE("FindThirdPartyBoost")
OV_LINK_BOOST_LIB("system" ${OV_WIN32_BOOST_VERSION})
OV_LINK_BOOST_LIB("thread" ${OV_WIN32_BOOST_VERSION})
INCLUDE("FindThirdPartyBoost_Chrono")
ENDIF()
# ---------------------------------
# Finds standard library winmm
# Adds library to target
# Adds include path
# ---------------------------------
IF(WIN32)
INCLUDE("OvSetWindowsSDKPath")
FIND_LIBRARY(LIB_STANDARD_MODULE_WINMM winmm ${OV_MS_SDK_PATH}/lib)
IF(LIB_STANDARD_MODULE_WINMM)
MESSAGE(STATUS " Found winmm...")
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_STANDARD_MODULE_WINMM})
TARGET_LINK_LIBRARIES(${PROJECT_NAME}-static ${LIB_STANDARD_MODULE_WINMM})
ELSE(LIB_STANDARD_MODULE_WINMM)
MESSAGE(STATUS " FAILED to find winmm...")
ENDIF(LIB_STANDARD_MODULE_WINMM)
FIND_LIBRARY(LIB_STANDARD_MODULE_DBGHELP dbghelp ${OV_MS_SDK_PATH}/lib)
IF(LIB_STANDARD_MODULE_DBGHELP)
MESSAGE(STATUS " Found dbghelp...")
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_STANDARD_MODULE_DBGHELP})
TARGET_LINK_LIBRARIES(${PROJECT_NAME}-static ${LIB_STANDARD_MODULE_DBGHELP})
ELSE(LIB_STANDARD_MODULE_DBGHELP)
MESSAGE(STATUS " FAILED to find dbghelp...")
ENDIF(LIB_STANDARD_MODULE_DBGHELP)
ELSE()
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${CMAKE_DL_LIBS})
ENDIF()
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
@@ -0,0 +1,31 @@
#pragma once
#include "defines.h"
#if defined TARGET_OS_Windows
#include <windows.h>
namespace System {
class System_API WindowsUtilities
{
public:
// Load a library in a matter compliant with non-ascii path
// returns the eventual error code
static void* utf16CompliantLoadLibrary(const char* path, HANDLE file = nullptr, DWORD flags = LOAD_WITH_ALTERED_SEARCH_PATH);
static BOOL utf16CompliantSetEnvironmentVariable(const char* name, const char* value);
// Load a library in a matter compliant with non-ascii path
// returns the eventual error code
static BOOL utf16CompliantCreateProcess(char* applicationName, char* commandLine, LPSECURITY_ATTRIBUTES processAttributes,
LPSECURITY_ATTRIBUTES threadAttributes, BOOL inheritHandles, DWORD creationFlags, LPVOID environment,
char* currentDirectory, LPSTARTUPINFO startupInfo, LPPROCESS_INFORMATION processInformation);
// Load a library in a matter compliant with non-ascii path
// returns the eventual error code
static HINSTANCE utf16CompliantShellExecute(HWND hwnd, LPCTSTR operation, LPCTSTR file, LPCTSTR parameters, LPCTSTR directory, INT nShowCmd);
private:
WindowsUtilities() = delete;
};
} // namespace System
#endif // TARGET_OS_Windows
@@ -0,0 +1,25 @@
#pragma once
#include <ov_common_defines.h>
#if defined System_Shared
# if defined TARGET_OS_Windows
# define System_API_Export __declspec(dllexport)
# define System_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
# define System_API_Export __attribute__((visibility("default")))
# define System_API_Import __attribute__((visibility("default")))
# else
# define System_API_Export
# define System_API_Import
# endif
#else
# define System_API_Export
# define System_API_Import
#endif
#if defined System_Exports
# define System_API System_API_Export
#else
# define System_API System_API_Import
#endif
@@ -0,0 +1,40 @@
#pragma once
#include "defines.h"
#include <cstdlib> // fix Unix compatibility
namespace System {
class System_API CChrono final
{
public:
CChrono() { }
~CChrono();
bool reset(size_t nStep);
bool stepIn();
bool stepOut();
uint64_t getTotalStepInDuration() const;
uint64_t getTotalStepOutDuration() const;
uint64_t getAverageStepInDuration() const;
uint64_t getAverageStepOutDuration() const;
double getStepInPercentage() const;
double getStepOutPercentage() const;
bool hasNewEstimation() const { return m_hasNewEstimation; }
private:
uint64_t* m_stepInTime = nullptr;
uint64_t* m_stepOutTime = nullptr;
size_t m_nStep = 0;
size_t m_stepIdx = 0;
bool m_isInStep = false;
bool m_hasNewEstimation = false;
uint64_t m_totalStepInTime = 0;
uint64_t m_totalStepOutTime = 0;
};
} // namespace System
@@ -0,0 +1,264 @@
#pragma once
#include "defines.h"
#if defined TARGET_OS_Windows
#include <shlobj.h>
#include <Dbghelp.h>
#elif defined TARGET_OS_Linux
#include <linux/limits.h>
#elif defined TARGET_OS_MacOS
#include <sys/syslimits.h>
#endif
#include <string>
namespace System {
class CDynamicModuleSymbolLoader; // forward declare to make function declaration possible
class System_API CDynamicModule final
{
public:
enum ELogErrorCodes : size_t
{
LogErrorCodes_NoError = 0,
LogErrorCodes_ModuleAlreadyLoaded = 1,
LogErrorCodes_NoModuleLoaded = 2,
LogErrorCodes_FilenameEmpty = 3,
LogErrorCodes_FolderPathInvalid = 4,
LogErrorCodes_RegistryQueryFailed = 5,
LogErrorCodes_UnloadModuleFailed = 6,
LogErrorCodes_FailToLoadModule = 7,
LogErrorCodes_InvalidSymbol = 8,
LogErrorCodes_EnvironmentVariableInvalid = 9,
LogErrorCodes_ModuleNotFound = 10
};
CDynamicModule();
~CDynamicModule();
/**
* \brief Load module from a path.
*
* \param modulePath
* \param symbolNameCheck Symbol to check if it is present in the module. It is optionnal and is nullptr by default.
*
* \retval true If the module loaded successfully.
* \retval false If module loading failed.
*/
bool loadFromPath(const char* modulePath, const char* symbolNameCheck = nullptr);
#if defined TARGET_OS_Windows
/**
* \brief Load existing module that was already loaded by the process.
*
* \param modulePath The path to the module.
* \param symbolNameCheck Symbol to check if it is present in the module. It is optionnal and is nullptr by default.
*
* \retval true If the module loaded successfully.
* \retval false If module loading failed.
*/
bool loadFromExisting(const char* modulePath, const char* symbolNameCheck = nullptr);
/**
* \brief Load module from known path. Windows only.
*
* \param standardPath A CSIDL value that identifies the folder whose path is to be retrieved.
* Only real folders are valid. If a virtual folder is specified, this function fails.
* You can force creation of a folder by combining the folder's CSIDL with CSIDL_FLAG_CREATE.
* \param modulePath Path of the module to load.
* \param symbolNameCheck Symbol to check if it is present in the module. It is optional and is nullptr by default.
*
* \retval true If the module loaded successfully.
* \retval false If module loading failed.
*/
bool loadFromKnownPath(int standardPath, const char* modulePath, const char* symbolNameCheck = nullptr);
/**
* \brief Load module from Windows environment. Windows only.
*
* \param environmentPath Environment path.
* \param modulePath Module file path.
* \param symbolNameCheck Symbol to check if it is present in the module. It is optionnal and is nullptr by default.
*
* \retval true If the module loaded successfully.
* \retval false If module loading failed.
*/
bool loadFromEnvironment(const char* environmentPath, const char* modulePath, const char* symbolNameCheck = nullptr);
/**
* \brief Load module from the registry. Windows only.
*
* \param key Registry key. Check https://msdn.microsoft.com/en-us/library/windows/desktop/ms724836
* \param registryPath Registry path.
* \param registryKeyName Key name.
* \param samDesired A mask that specifies the desired access rights to the key to be opened.
* The function fails if the security descriptor of the key does not permit the requested access for the calling process
* Check https://msdn.microsoft.com/fr-fr/library/windows/desktop/ms724878
* \param modulePath sModulePath Module path.
* \param symbolNameCheck Symbol to check if it is present in the module. It is optionnal and is nullptr by default.
*
* \retval true If the module loaded successfully.
* \retval false If module loading failed.
*/
bool loadFromRegistry(HKEY key, const char* registryPath, const char* registryKeyName, REGSAM samDesired, const char* modulePath,
const char* symbolNameCheck = nullptr);
/**
* \brief Check the module architecture. Windows only.
* The architecture type of the computer. An image file can only be run on the specified computer or a system that emulates the specified computer.
* This member can be one of the following values.
* - x86: 0x014c
* - x64: 0x8664
* - ia64: 0x0200
*
* \param filePath Module file path
* \param architecture Architecture code
*
* \retval true If the module architecture is equal to the architecture parameter.
* \retval false If the module is unequal to the architecture parameter.
*/
static bool isModuleCompatible(const char* filePath, int architecture);
#endif
// --------------------------------------
/**
* \brief Unload the module. If setShouldFreeModule(false) is called, the unload() has no effect.
*
* \retval true In case of success.
* \retval false In case of failure.
*
* \sa setShouldFreeModule
* \sa isLoaded
*/
bool unload();
/**
* \brief Check if the module is loaded.
*
* \retval true If the module is loaded.
* \retval false If no module are loaded.
*
* \sa unload
* \sa setShouldFreeModule
*/
bool isLoaded() const { return m_Handle != nullptr; }
/**
* \brief Get the filename of the module.
*
* \return the file name of the module.
*/
const char* getFilename() const { return m_Filename; }
/**
* \brief Should be used to avoid the warning "Missing dll" when loading acquisition server
* This can happen when the loaded library needs a second library that is not detected.
*
* \param errorMode
*/
void setDynamicModuleErrorMode(const size_t errorMode) { m_ErrorMode = errorMode; }
/**
* \brief Set if the module should, or not, be free. By default the module will be free.
*
* \param shouldFreeModule Set to true to free the module when unload is called. False otherwise.
*
* \sa unload
*/
void setShouldFreeModule(const bool shouldFreeModule) { m_ShouldFreeModule = shouldFreeModule; }
/**
* \brief Get the last error code.
*
* \return The error code.
*/
size_t getLastError() const;
/**
* \brief Get the error message corresponding to the error code.
*
* \param errorCode The error code.
*
* \return the message corresponding to the error code.
*/
static const char* getErrorString(size_t errorCode);
/**
* \brief Get the detailed error
*
* \return A string with detailed information about the last error.
*/
const char* getErrorDetails() const;
private:
void* m_Handle = nullptr;
#if defined TARGET_OS_Windows
char m_Filename[MAX_PATH];
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
char m_Filename[PATH_MAX];
#endif
size_t m_ErrorMode = 0;
bool m_ShouldFreeModule = true;
typedef void (*symbol_t)();
char m_ErrorDetails[1024];
mutable ELogErrorCodes m_ErrorCode;
static const size_t m_ErrorModeNull = 0xffffffff;
friend class CDynamicModuleSymbolLoader;
/**
* \brief Set the error code and details.
*
* \param errorCode Error code.
* \param details Detailed string error.
*/
void setError(ELogErrorCodes errorCode, const std::string& details = std::string());
/**
* \brief Get a symbol from the module.
*
* \param symbolName Symbol name.
*
* \return The symbol.
*/
symbol_t getSymbolGeneric(const char* symbolName) const;
#ifdef TARGET_OS_Windows
/**
* \brief Get the image file headers. Windows only.
*
* \param filename The file path.
* \param headers [out] The header.
*
* \retval true In case of success.
* \retval false In case of failure.
*/
static bool getImageFileHeaders(const char* filename, IMAGE_NT_HEADERS& headers);
#endif
};
class CDynamicModuleSymbolLoader
{
public:
/**
* \brief Get a symbol from the module.
*
* \param dynamicModule
* \param symbolName The symbol name.
* \param symbol [out] The symbol.
*
* \retval true If the symbol exists.
* \retval false If the symbol does not exist.
*/
template <typename T>
static bool getSymbol(CDynamicModule& dynamicModule, const char* symbolName, T* symbol)
{
*symbol = reinterpret_cast<T>(dynamicModule.getSymbolGeneric(symbolName));
return *symbol != nullptr;
}
};
} // namespace System
@@ -0,0 +1,24 @@
#pragma once
#include "defines.h"
#include <cmath>
#include <cstdlib> // fix Unix compatibility
namespace System {
class System_API Math
{
public:
static bool initializeRandomMachine(size_t randomSeed);
static size_t randomI();
// returns a value in [0,upperLimit( -- i.e. upperLimit not included in range
static size_t randomWithCeiling(const size_t upperLimit);
static double random0To1();
static uint64_t random();
private:
Math() = delete;
};
} // namespace System
@@ -0,0 +1,52 @@
#pragma once
#include "defines.h"
#include <cstdlib> // fix Unix compatibility
namespace System {
class System_API Memory
{
public:
static bool hostToLittleEndian(uint16_t value, uint8_t* buffer);
static bool hostToLittleEndian(uint32_t value, uint8_t* buffer);
static bool hostToLittleEndian(uint64_t value, uint8_t* buffer);
static bool hostToLittleEndian(int16_t value, uint8_t* buffer);
static bool hostToLittleEndian(int value, uint8_t* buffer);
static bool hostToLittleEndian(int64_t value, uint8_t* buffer);
static bool hostToLittleEndian(float value, uint8_t* buffer);
static bool hostToLittleEndian(double value, uint8_t* buffer);
static bool hostToLittleEndian(long double value, uint8_t* buffer);
static bool hostToBigEndian(uint16_t value, uint8_t* buffer);
static bool hostToBigEndian(uint32_t value, uint8_t* buffer);
static bool hostToBigEndian(uint64_t value, uint8_t* buffer);
static bool hostToBigEndian(int16_t value, uint8_t* buffer);
static bool hostToBigEndian(int value, uint8_t* buffer);
static bool hostToBigEndian(int64_t value, uint8_t* buffer);
static bool hostToBigEndian(float value, uint8_t* buffer);
static bool hostToBigEndian(double value, uint8_t* buffer);
static bool hostToBigEndian(long double value, uint8_t* buffer);
static bool littleEndianToHost(const uint8_t* buffer, uint16_t* value);
static bool littleEndianToHost(const uint8_t* buffer, uint32_t* value);
static bool littleEndianToHost(const uint8_t* buffer, uint64_t* value);
static bool littleEndianToHost(const uint8_t* buffer, int16_t* value);
static bool littleEndianToHost(const uint8_t* buffer, int* value);
static bool littleEndianToHost(const uint8_t* buffer, int64_t* value);
static bool littleEndianToHost(const uint8_t* buffer, float* value);
static bool littleEndianToHost(const uint8_t* buffer, double* value);
static bool littleEndianToHost(const uint8_t* buffer, long double* value);
static bool bigEndianToHost(const uint8_t* buffer, uint16_t* value);
static bool bigEndianToHost(const uint8_t* buffer, uint32_t* value);
static bool bigEndianToHost(const uint8_t* buffer, uint64_t* value);
static bool bigEndianToHost(const uint8_t* buffer, int16_t* value);
static bool bigEndianToHost(const uint8_t* buffer, int* value);
static bool bigEndianToHost(const uint8_t* buffer, int64_t* value);
static bool bigEndianToHost(const uint8_t* buffer, float* value);
static bool bigEndianToHost(const uint8_t* buffer, double* value);
static bool bigEndianToHost(const uint8_t* buffer, long double* value);
private:
Memory() = delete;
};
} // namespace System
@@ -0,0 +1,72 @@
#pragma once
#include "defines.h"
#include <cstdlib> // For Unix Compatibility
namespace System {
/**
* \class Time
* \brief Static functions to handle time within the framework
*
*/
class System_API Time
{
public:
/**
* \brief Make the calling thread sleep
* \param milliSeconds : sleep duration in ms
* \return Always true
*/
static bool sleep(const size_t milliSeconds);
/**
* \brief Make the calling thread sleep
* \param seconds : sleep duration in fixed point 32:32 seconds
* \return Always true
*/
static bool zsleep(const uint64_t seconds);
/**
* \brief Retrieve time in ms (turn the 32:32 fixed point seconds to milliseconds).
* \return Elapsed time in ms since the first call to this function or zgetTime functions
*/
static uint32_t getTime() { return uint32_t((zgetTime() * 1000) >> 32); }
/**
* \brief Retrieve time in fixed point 32:32 seconds
* \return Elapsed time since the first call to the zgetTime functions or getTime.
*/
static uint64_t zgetTime() { return zgetTimeRaw(true); }
/**
* \brief Retrieve time in fixed point 32:32 seconds
* \param sinceFirstCall: If sinceFirstCall is true, returns the time since the first call to the zgetTime function or getTime.
* Otherwise, returns time since epoch of the clock.
* \return Elapsed time
*/
static uint64_t zgetTimeRaw(bool sinceFirstCall = true);
/**
* \brief Check if the internal clock used by the framework is steady
* \return True if the clock is steady, false otherwise
* \note This is a theoretical check that queries the internal
* clock implementation for available services
*/
static bool isClockSteady();
/**
* \brief Check if the internal clock used by the framework has
* a resolution higher than the required one
* \param milliSeconds : Expected clock resolution (period between ticks) in ms (must be non-zero value)
* \return True if the clock meets the requirements, false otherwise
* \note This is a theoretical check that queries the internal
* clock implementation for available services
*/
static bool checkResolution(const size_t milliSeconds);
private:
Time() = delete;
};
} // namespace System
@@ -0,0 +1,41 @@
#include "system/WindowsUtilities.h"
#if defined TARGET_OS_Windows
#include "m_ConverterUtf8.h"
#include <ShellAPI.h>
#ifndef UNICODE
#define UNICODE
#endif
namespace System {
// Load a library in a matter compliant with non-ascii path
// returns the eventual error code
void* WindowsUtilities::utf16CompliantLoadLibrary(const char* path, const HANDLE file, const DWORD flags)
{
//const HMODULE hModule = ::LoadLibraryEx(path, file, flags); // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR|LOAD_LIBRARY_DEFAULT_DIRS);
return ::LoadLibraryEx(path, file, flags); // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR|LOAD_LIBRARY_DEFAULT_DIRS);
}
BOOL WindowsUtilities::utf16CompliantSetEnvironmentVariable(const char* name, const char* value) { return SetEnvironmentVariable(name, value); }
// Load a library in a matter compliant with non-ascii path
// returns the eventual error code
BOOL WindowsUtilities::utf16CompliantCreateProcess(char* applicationName, char* commandLine, LPSECURITY_ATTRIBUTES processAttributes,
LPSECURITY_ATTRIBUTES threadAttributes, const BOOL inheritHandles, const DWORD creationFlags,
LPVOID environment, char* currentDirectory, LPSTARTUPINFO startupInfo,
LPPROCESS_INFORMATION processInformation)
{
return CreateProcess(applicationName, const_cast<char*>(commandLine), processAttributes, threadAttributes,
inheritHandles, creationFlags, environment, currentDirectory, startupInfo, processInformation);
}
// Load a library in a matter compliant with non-ascii path
// returns the eventual error code
HINSTANCE WindowsUtilities::utf16CompliantShellExecute(HWND hwnd, LPCTSTR operation, LPCTSTR file, LPCTSTR parameters, LPCTSTR directory, const INT nShowCmd)
{
return ShellExecute(hwnd, operation, file, parameters, directory, nShowCmd);
}
} // namespace System
#endif // TARGET_OS_Windows
@@ -0,0 +1,101 @@
#include "system/ovCChrono.h"
#include "system/ovCTime.h"
namespace System {
CChrono::~CChrono()
{
delete [] m_stepInTime;
delete [] m_stepOutTime;
}
bool CChrono::reset(const size_t nStep)
{
if (!nStep) { return false; }
uint64_t* stepInTime = new uint64_t[nStep + 1];
uint64_t* stepOutTime = new uint64_t[nStep + 1];
if (!stepInTime || !stepOutTime)
{
delete [] stepInTime;
delete [] stepOutTime;
return false;
}
for (size_t i = 0; i <= nStep; ++i)
{
stepInTime[i] = 0;
stepOutTime[i] = 0;
}
delete [] m_stepInTime;
delete [] m_stepOutTime;
m_stepInTime = stepInTime;
m_stepOutTime = stepOutTime;
m_nStep = nStep;
m_stepIdx = 0;
m_isInStep = false;
m_hasNewEstimation = false;
m_totalStepInTime = 0;
m_totalStepOutTime = 0;
return true;
}
bool CChrono::stepIn()
{
if (m_isInStep || !m_nStep) { return false; }
m_isInStep = !m_isInStep;
m_stepInTime[m_stepIdx] = Time::zgetTime();
if (m_stepIdx == m_nStep)
{
m_totalStepInTime = 0;
m_totalStepOutTime = 0;
for (size_t i = 0; i < m_nStep; ++i)
{
m_totalStepInTime += m_stepOutTime[i] - m_stepInTime[i];
m_totalStepOutTime += m_stepInTime[i + 1] - m_stepOutTime[i];
}
m_stepInTime[0] = m_stepInTime[m_nStep];
m_stepIdx = 0;
m_hasNewEstimation = true;
}
else { m_hasNewEstimation = false; }
return true;
}
bool CChrono::stepOut()
{
if (!m_isInStep || !m_nStep) { return false; }
m_isInStep = !m_isInStep;
m_stepOutTime[m_stepIdx] = Time::zgetTime();
m_stepIdx++;
return true;
}
uint64_t CChrono::getTotalStepInDuration() const { return m_totalStepInTime; }
uint64_t CChrono::getTotalStepOutDuration() const { return m_totalStepOutTime; }
uint64_t CChrono::getAverageStepInDuration() const { return m_nStep ? this->getTotalStepInDuration() / m_nStep : 0; }
uint64_t CChrono::getAverageStepOutDuration() const { return m_nStep ? this->getTotalStepOutDuration() / m_nStep : 0; }
double CChrono::getStepInPercentage() const
{
const uint64_t totalStepDuration = (this->getTotalStepInDuration() + this->getTotalStepOutDuration());
return totalStepDuration ? (this->getTotalStepInDuration() * 100.0) / totalStepDuration : 0;
}
double CChrono::getStepOutPercentage() const
{
const uint64_t totalStepDuration = (this->getTotalStepOutDuration() + this->getTotalStepInDuration());
return totalStepDuration ? (this->getTotalStepOutDuration() * 100.0) / totalStepDuration : 0;
}
} // namespace System
@@ -0,0 +1,396 @@
#include "system/ovCDynamicModule.h"
#if defined TARGET_OS_Windows
#include <system/WindowsUtilities.h> // Allowed to use utf8_to_utf16 function for os that use utf16
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <dlfcn.h>
#endif
#include <map>
#include <vector>
#include <cstring>
namespace System {
static const std::map<CDynamicModule::ELogErrorCodes, std::string> ERROR_MAP =
{
{ CDynamicModule::LogErrorCodes_ModuleAlreadyLoaded, "A module is already loaded." },
{ CDynamicModule::LogErrorCodes_NoModuleLoaded, "No module loaded." },
{ CDynamicModule::LogErrorCodes_FilenameEmpty, "The filename is empty." },
{ CDynamicModule::LogErrorCodes_FolderPathInvalid, "The folder path is invalid." },
{ CDynamicModule::LogErrorCodes_RegistryQueryFailed, "The registry query is invalid." },
{ CDynamicModule::LogErrorCodes_UnloadModuleFailed, "Fail to unload the module." },
{ CDynamicModule::LogErrorCodes_FailToLoadModule, "Fail to load the module." },
{ CDynamicModule::LogErrorCodes_InvalidSymbol, "The symbol is invalid." },
{ CDynamicModule::LogErrorCodes_ModuleNotFound, "Module not found." }
};
#if defined TARGET_OS_Windows
static std::vector<std::string> split(char* str, const char* delim)
{
char* token = strtok(str, delim);
std::vector<std::string> result;
while (token != nullptr)
{
result.push_back(token);
token = strtok(nullptr, delim);
}
return result;
}
static std::string formatWindowsError(const DWORD code)
{
LPTSTR text;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | // use system message tables to retrieve error text
FORMAT_MESSAGE_ALLOCATE_BUFFER | // allocate buffer on local heap for error text
FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPTSTR(&text), // output
0, // minimum size for output buffer
nullptr
); // arguments - see note
return std::string(text);
}
#endif
const char* CDynamicModule::getErrorString(size_t errorCode)
{
if (ERROR_MAP.count(ELogErrorCodes(errorCode)) == 0) { return "Invalid error code"; }
return ERROR_MAP.at(ELogErrorCodes(errorCode)).c_str();
}
const char* CDynamicModule::getErrorDetails() const { return &m_ErrorDetails[0]; }
size_t CDynamicModule::getLastError() const { return m_ErrorCode; }
CDynamicModule::CDynamicModule()
: m_ErrorMode(m_ErrorModeNull), m_ErrorCode(LogErrorCodes_NoError)
{
strcpy(m_ErrorDetails, "");
strcpy(m_Filename, "");
}
CDynamicModule::~CDynamicModule() {}
// --------------------------------------
#if defined TARGET_OS_Windows
bool CDynamicModule::loadFromExisting(const char* modulePath, const char* symbolNameCheck)
{
if (m_Handle)
{
this->setError(LogErrorCodes_ModuleAlreadyLoaded, "Module [" + std::string(m_Filename) + "] is already loaded");
return false;
}
m_Handle = ::GetModuleHandle(modulePath);
if (m_Handle == nullptr)
{
this->setError(LogErrorCodes_FailToLoadModule, "Windows error: " + formatWindowsError(GetLastError()));
return false;
}
if (symbolNameCheck != nullptr)
{
if (GetProcAddress(HMODULE(m_Handle), symbolNameCheck) == nullptr)
{
this->unload();
this->setError(LogErrorCodes_InvalidSymbol, "Windows error: " + formatWindowsError(GetLastError()));
return false;
}
}
strcpy(m_Filename, modulePath);
return true;
}
#endif
bool CDynamicModule::loadFromPath(const char* modulePath, const char* symbolNameCheck)
{
if (m_Handle)
{
this->setError(LogErrorCodes_ModuleAlreadyLoaded, "Module [" + std::string(m_Filename) + "] is already loaded");
return false;
}
// Verify empty filename
if (modulePath == nullptr || (modulePath != nullptr && modulePath[0] == '\0'))
{
this->setError(LogErrorCodes_FilenameEmpty);
return false;
}
#if defined TARGET_OS_Windows
if (m_ErrorMode == m_ErrorModeNull)
{
const UINT mode = SetErrorMode(UINT(m_ErrorModeNull));
SetErrorMode(mode);
}
else { SetErrorMode(UINT(m_ErrorMode)); }
m_Handle = WindowsUtilities::utf16CompliantLoadLibrary(modulePath, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (m_Handle == nullptr)
{
this->setError(LogErrorCodes_FailToLoadModule, "Fail to load [" + std::string(modulePath) + "]. Windows error:" + formatWindowsError(GetLastError()));
return false;
}
if (symbolNameCheck != nullptr)
{
if (GetProcAddress(HMODULE(m_Handle), symbolNameCheck) == nullptr)
{
this->unload();
this->setError(LogErrorCodes_InvalidSymbol,
"Symbol invalid: [" + std::string(symbolNameCheck) + "]. Windows error: " + formatWindowsError(GetLastError()));
return false;
}
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
m_Handle = ::dlopen(modulePath, RTLD_LAZY|RTLD_GLOBAL);
if (m_Handle == nullptr)
{
this->setError(LogErrorCodes_FailToLoadModule);
return false;
}
if(symbolNameCheck != nullptr)
{
if(::dlsym(m_Handle, symbolNameCheck) == nullptr)
{
char* error = ::dlerror();
if(error) { this->setError(LogErrorCodes_InvalidSymbol, "Error: " + std::string(error)); }
else { this->setError(LogErrorCodes_InvalidSymbol); }
::dlclose(m_Handle);
m_Handle = NULL;
return false;
}
}
#endif
strcpy(m_Filename, modulePath);
return true;
}
#if defined TARGET_OS_Windows
bool CDynamicModule::loadFromKnownPath(const int standardPath, const char* modulePath, const char* symbolNameCheck)
{
if (m_Handle)
{
this->setError(LogErrorCodes_ModuleAlreadyLoaded, "Module [" + std::string(m_Filename) + "] is already loaded");
return false;
}
char dllPath[MAX_PATH];
const HRESULT result = ::SHGetFolderPath(nullptr, standardPath, nullptr, SHGFP_TYPE_CURRENT, dllPath);
if (result != S_OK)
{
this->setError(LogErrorCodes_FolderPathInvalid, "Windows error code: " + std::to_string(result));
return false;
}
strcat(dllPath, "\\");
strcat(dllPath, modulePath);
return loadFromPath(dllPath, symbolNameCheck); // Error set in the loadFromPath function
}
bool CDynamicModule::loadFromEnvironment(const char* environmentPath, const char* modulePath, const char* symbolNameCheck)
{
if (m_Handle)
{
this->setError(LogErrorCodes_ModuleAlreadyLoaded, "Module [" + std::string(m_Filename) + "] is already loaded");
return false;
}
char* str = getenv(environmentPath);
if (str == nullptr)
{
this->setError(LogErrorCodes_EnvironmentVariableInvalid);
return false;
}
std::vector<std::string> paths = split(str, ";");
for (const std::string& path : paths) { if (loadFromPath((path + "\\" + modulePath).c_str(), symbolNameCheck)) { return true; } }
this->setError(LogErrorCodes_ModuleNotFound);
return false;
}
bool CDynamicModule::loadFromRegistry(HKEY key, const char* registryPath, const char* registryKeyName, REGSAM samDesired, const char* modulePath,
const char* symbolNameCheck)
{
char dllPath[MAX_PATH];
DWORD size = sizeof(dllPath);
dllPath[0] = '\0';
HKEY lKey = nullptr;
LONG result = RegOpenKeyEx(key, TEXT(registryPath), NULL, samDesired, &lKey);
if (result != ERROR_SUCCESS)
{
this->setError(LogErrorCodes_RegistryQueryFailed, "Fail to open registry key. Windows error code: " + std::to_string(result));
RegCloseKey(lKey);
return false;
}
result = ::RegQueryValueEx(lKey, registryKeyName, nullptr, nullptr, reinterpret_cast<unsigned char*>(dllPath), &size);
if (result == ERROR_SUCCESS)
{
strcat(dllPath, modulePath);
return loadFromPath(dllPath, symbolNameCheck); // Error set in the loadFromPath function
}
this->setError(LogErrorCodes_RegistryQueryFailed, "Fail to query registry value. Windows error code: " + std::to_string(result));
return false;
}
bool CDynamicModule::isModuleCompatible(const char* filePath, const int architecture)
{
IMAGE_NT_HEADERS headers;
if (!getImageFileHeaders(filePath, headers)) { return false; } // Error set in the getImageFileHeaders function
return headers.FileHeader.Machine == architecture;
}
#endif
// --------------------------------------
bool CDynamicModule::unload()
{
if (!m_Handle)
{
this->setError(LogErrorCodes_NoModuleLoaded);
return false;
}
// If the flag m_shouldFreeModule, set to true per default, is set to false,
// the module is not unloaded.
// This flag was first set for Enobio3G driver which dll freezes when unloaded
if (!m_ShouldFreeModule) { return true; }
#if defined TARGET_OS_Windows
if (::FreeModule(reinterpret_cast<HMODULE>(m_Handle)) == 0)
{
this->setError(LogErrorCodes_UnloadModuleFailed, "Windows error code: " + formatWindowsError(GetLastError()));
return false;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
if(::dlclose(m_Handle) != 0)
{
char* error = ::dlerror();
if(error) { this->setError(LogErrorCodes_UnloadModuleFailed, "Error: " + std::string(error)); }
else { this->setError(LogErrorCodes_UnloadModuleFailed); }
return false;
}
#else
#endif
strcpy(m_Filename, "");
m_Handle = nullptr;
return true;
}
CDynamicModule::symbol_t CDynamicModule::getSymbolGeneric(const char* symbolName) const
{
symbol_t res = nullptr;
if (!m_Handle)
{
m_ErrorCode = LogErrorCodes_NoModuleLoaded;
return res;
}
if (m_Handle)
{
#if defined TARGET_OS_Windows
res = symbol_t(GetProcAddress(reinterpret_cast<HMODULE>(m_Handle), symbolName));
if (!res)
{
m_ErrorCode = LogErrorCodes_InvalidSymbol;
return res;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
res = (CDynamicModule::symbol_t)::dlsym(m_Handle, symbolName);
if (!res)
{
m_ErrorCode = LogErrorCodes_InvalidSymbol;
return res;
}
#else
#endif
}
return res;
}
#ifdef TARGET_OS_Windows
bool CDynamicModule::getImageFileHeaders(const char* filename, IMAGE_NT_HEADERS& headers)
{
const HANDLE fileHandle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fileHandle == INVALID_HANDLE_VALUE) { return false; }
const HANDLE imageHandle = CreateFileMapping(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (imageHandle == nullptr)
{
CloseHandle(fileHandle);
return false;
}
void* imagePtr = MapViewOfFile(imageHandle, FILE_MAP_READ, 0, 0, 0);
if (imagePtr == nullptr)
{
CloseHandle(imageHandle);
CloseHandle(fileHandle);
return false;
}
const PIMAGE_NT_HEADERS headersPtr = ImageNtHeader(imagePtr);
if (headersPtr == nullptr)
{
UnmapViewOfFile(imagePtr);
CloseHandle(imageHandle);
CloseHandle(fileHandle);
return false;
}
headers = *headersPtr;
UnmapViewOfFile(imagePtr);
CloseHandle(imageHandle);
CloseHandle(fileHandle);
return true;
}
#endif
void CDynamicModule::setError(const ELogErrorCodes errorCode, const std::string& details)
{
m_ErrorCode = errorCode;
strcpy(m_ErrorDetails, details.c_str());
}
} // namespace System
@@ -0,0 +1,69 @@
/**
*
* @fixme This class could benefit from a serious overhaul, e.g. using randomness from some established library or C11.
*
* - Here Linear Congruential Generator is re-implemented to avoid third-party dependencies messing the up the rand() state.
* This happened before when we used the global srand() / rand(). It made hard to make repeatable experiments on some
* platforms. The generated randomness from the introduced home-made class is not super but it should be sufficient for
* OpenViBE's present use-cases.
*
* Other notes
*
* - Due to generative process, values generated above L_RAND_MAX may not be dense (verify)
* - randomIWithCeiling() may not be dense either
*
*/
#include "system/ovCMath.h"
#include <cstdlib>
#include <cstring>
namespace System {
class RandomGenerator
{
size_t m_nextValue = 0;
public:
static const size_t L_RAND_MAX = 2147483647; // (2^32)/2-1 == 2147483647 (0x7FFFFFFF)
explicit RandomGenerator(const size_t seed = 1) : m_nextValue(seed) {}
int rand()
{
// Pretty much C99 convention and parameters for a Linear Congruential Generator
m_nextValue = (m_nextValue * 1103515245 + 12345) & L_RAND_MAX;
return int(m_nextValue);
}
void setSeed(const size_t seed) { m_nextValue = seed; }
size_t getSeed() const { return m_nextValue; }
};
// Should be only accessed via Math:: calls defined below
static RandomGenerator randomGenerator;
bool Math::initializeRandomMachine(const size_t randomSeed)
{
randomGenerator.setSeed(size_t(randomSeed));
// For safety, we install also the C++ basic random engine (it might be useg by dependencies, old code, etc)
srand(uint32_t(randomSeed));
return true;
}
size_t Math::randomI() { return size_t(random()); }
size_t Math::randomWithCeiling(const size_t upperLimit) { return size_t(random0To1() * double(upperLimit)); }
double Math::random0To1() { return double(randomGenerator.rand()) / double(RandomGenerator::L_RAND_MAX); }
uint64_t Math::random()
{
const uint64_t r1 = randomGenerator.rand();
const uint64_t r2 = randomGenerator.rand();
const uint64_t r3 = randomGenerator.rand();
const uint64_t r4 = randomGenerator.rand();
return (r1 << 24) ^ (r2 << 16) ^ (r3 << 8) ^ (r4);
}
} // namespace System
@@ -0,0 +1,169 @@
#include "system/ovCMemory.h"
#include <cstring>
namespace System {
// ________________________________________________________________________________________________________________
//
template <typename T>
bool BigEndianToHost(const uint8_t* buffer, T* value)
{
if (!buffer || !value) { return false; }
memset(value, 0, sizeof(T));
for (size_t i = 0; i < sizeof(T); ++i) { ((uint8_t*)value)[i] = buffer[sizeof(T) - 1 - i]; }
return true;
}
template <typename T>
bool LittleEndianToHost(const uint8_t* buffer, T* value)
{
if (!buffer || !value) { return false; }
memset(value, 0, sizeof(T));
for (size_t i = 0; i < sizeof(T); ++i) { ((uint8_t*)value)[i] = buffer[i]; }
return true;
}
template <typename T>
bool HostToBigEndian(const T& value, uint8_t* buffer)
{
if (!buffer) { return false; }
memset(buffer, 0, sizeof(T));
for (size_t i = 0; i < sizeof(T); ++i) { buffer[i] = ((uint8_t*)&value)[sizeof(T) - 1 - i]; }
return true;
}
template <typename T>
bool HostToLittleEndian(const T& value, uint8_t* buffer)
{
if (!buffer) { return false; }
for (size_t i = 0; i < sizeof(T); ++i) { buffer[i] = uint8_t((value >> (i * 8)) & 0xff); }
return true;
}
// ________________________________________________________________________________________________________________
//
bool Memory::hostToLittleEndian(const uint16_t value, uint8_t* buffer) { return HostToLittleEndian<uint16_t>(value, buffer); }
bool Memory::hostToLittleEndian(const uint32_t value, uint8_t* buffer) { return HostToLittleEndian<uint32_t>(value, buffer); }
bool Memory::hostToLittleEndian(const uint64_t value, uint8_t* buffer) { return HostToLittleEndian<uint64_t>(value, buffer); }
bool Memory::hostToLittleEndian(const int16_t value, uint8_t* buffer) { return HostToLittleEndian<int16_t>(value, buffer); }
bool Memory::hostToLittleEndian(const int value, uint8_t* buffer) { return HostToLittleEndian<int>(value, buffer); }
bool Memory::hostToLittleEndian(const int64_t value, uint8_t* buffer) { return HostToLittleEndian<int64_t>(value, buffer); }
bool Memory::hostToLittleEndian(const float value, uint8_t* buffer)
{
uint32_t tmp;
memcpy(&tmp, &value, sizeof(tmp));
return hostToLittleEndian(tmp, buffer);
}
bool Memory::hostToLittleEndian(const double value, uint8_t* buffer)
{
uint64_t tmp;
memcpy(&tmp, &value, sizeof(tmp));
return hostToLittleEndian(tmp, buffer);
}
bool Memory::hostToLittleEndian(const long double /*value*/, uint8_t* /*buffer*/)
{
// $$$ TODO
return false;
}
// ________________________________________________________________________________________________________________
//
bool Memory::hostToBigEndian(const uint16_t value, uint8_t* buffer) { return HostToBigEndian<uint16_t>(value, buffer); }
bool Memory::hostToBigEndian(const uint32_t value, uint8_t* buffer) { return HostToBigEndian<uint32_t>(value, buffer); }
bool Memory::hostToBigEndian(const uint64_t value, uint8_t* buffer) { return HostToBigEndian<uint64_t>(value, buffer); }
bool Memory::hostToBigEndian(const int16_t value, uint8_t* buffer) { return HostToBigEndian<int16_t>(value, buffer); }
bool Memory::hostToBigEndian(const int value, uint8_t* buffer) { return HostToBigEndian<int>(value, buffer); }
bool Memory::hostToBigEndian(const int64_t value, uint8_t* buffer) { return HostToBigEndian<int64_t>(value, buffer); }
bool Memory::hostToBigEndian(const float value, uint8_t* buffer)
{
uint32_t tmp;
memcpy(&tmp, &value, sizeof(tmp));
return hostToBigEndian(tmp, buffer);
}
bool Memory::hostToBigEndian(const double value, uint8_t* buffer)
{
uint64_t tmp;
memcpy(&tmp, &value, sizeof(tmp));
return hostToBigEndian(tmp, buffer);
}
bool Memory::hostToBigEndian(const long double /*value*/, uint8_t* /*buffer*/)
{
// $$$ TODO
return false;
}
// ________________________________________________________________________________________________________________
//
bool Memory::littleEndianToHost(const uint8_t* buffer, uint16_t* value) { return LittleEndianToHost<uint16_t>(buffer, value); }
bool Memory::littleEndianToHost(const uint8_t* buffer, uint32_t* value) { return LittleEndianToHost<uint32_t>(buffer, value); }
bool Memory::littleEndianToHost(const uint8_t* buffer, uint64_t* value) { return LittleEndianToHost<uint64_t>(buffer, value); }
bool Memory::littleEndianToHost(const uint8_t* buffer, int16_t* value) { return LittleEndianToHost<int16_t>(buffer, value); }
bool Memory::littleEndianToHost(const uint8_t* buffer, int* value) { return LittleEndianToHost<int>(buffer, value); }
bool Memory::littleEndianToHost(const uint8_t* buffer, int64_t* value) { return LittleEndianToHost<int64_t>(buffer, value); }
bool Memory::littleEndianToHost(const uint8_t* buffer, float* value)
{
uint32_t tmp;
const bool b = LittleEndianToHost<uint32_t>(buffer, &tmp);
memcpy(value, &tmp, sizeof(float));
return b;
}
bool Memory::littleEndianToHost(const uint8_t* buffer, double* value)
{
uint64_t tmp;
const bool b = LittleEndianToHost<uint64_t>(buffer, &tmp);
memcpy(value, &tmp, sizeof(double));
return b;
}
bool Memory::littleEndianToHost(const uint8_t* /*buffer*/, long double* /*value*/)
{
// $$$ TODO
return false;
}
// ________________________________________________________________________________________________________________
//
bool Memory::bigEndianToHost(const uint8_t* buffer, uint16_t* value) { return BigEndianToHost<uint16_t>(buffer, value); }
bool Memory::bigEndianToHost(const uint8_t* buffer, uint32_t* value) { return BigEndianToHost<uint32_t>(buffer, value); }
bool Memory::bigEndianToHost(const uint8_t* buffer, uint64_t* value) { return BigEndianToHost<uint64_t>(buffer, value); }
bool Memory::bigEndianToHost(const uint8_t* buffer, int16_t* value) { return BigEndianToHost<int16_t>(buffer, value); }
bool Memory::bigEndianToHost(const uint8_t* buffer, int* value) { return BigEndianToHost<int>(buffer, value); }
bool Memory::bigEndianToHost(const uint8_t* buffer, int64_t* value) { return BigEndianToHost<int64_t>(buffer, value); }
bool Memory::bigEndianToHost(const uint8_t* buffer, float* value)
{
uint32_t tmp;
const bool b = BigEndianToHost<uint32_t>(buffer, &tmp);
memcpy(value, &tmp, sizeof(float));
return b;
}
bool Memory::bigEndianToHost(const uint8_t* buffer, double* value)
{
uint64_t tmp;
const bool b = BigEndianToHost<uint64_t>(buffer, &tmp);
memcpy(value, &tmp, sizeof(double));
return b;
}
bool Memory::bigEndianToHost(const uint8_t* /*buffer*/, long double* /*value*/)
{
// $$$ TODO
return false;
}
} // namespace System
@@ -0,0 +1,92 @@
#include "system/ovCTime.h"
#include <cmath>
#include <cassert>
// \warning On Windows, avoid "using namespace System;" here as it may cause confusion with stuff coming from windows/boost
// \note Support of C++11 steady clock:
// - From GCC 4.8.1
// - From Visual Studio 2015 (therefore a strategy is needed to handle Visual Studio 2013 version)
// time handling strategy selection
// \note With officialy supported compilers and required boost version
// it should never fallback in a OV_USE_SYSTEM case
#if (defined(_MSC_VER) && _MSC_VER <= 1800 && defined(TARGET_HAS_Boost_Chrono))
#include <boost/chrono/config.hpp>
#ifdef BOOST_CHRONO_HAS_CLOCK_STEADY
#include <boost/chrono.hpp>
#include <boost/thread.hpp>
namespace Timelib = boost;
#else
#error "Please use OpenViBE recommended version of Boost"
#endif // BOOST_CHRONO_HAS_CLOCK_STEADY
#else // defined(_MSC_VER) && _MSC_VER <= 1800 && defined(TARGET_HAS_Boost_Chrono)
#include <chrono>
#include <thread>
namespace Timelib = std;
#endif // defined(_MSC_VER) && _MSC_VER <= 1800 && defined(TARGET_HAS_Boost_Chrono)
using internal_clock = Timelib::chrono::steady_clock;
// using internal_clock = chrono::high_resolution_clock;
namespace System {
bool Time::sleep(const size_t milliSeconds)
{
Timelib::this_thread::sleep_for(Timelib::chrono::milliseconds(milliSeconds));
return true;
}
bool Time::zsleep(const uint64_t seconds)
{
const uint32_t s = uint32_t(seconds >> 32);
// zero the seconds with 0xFFFFFFFF, multiply to get the rest as fixed point microsec, then grab them (now in the 32 msbs)
const uint64_t ms = ((seconds & 0xFFFFFFFFLL) * 1000000LL) >> 32;
const Timelib::chrono::microseconds duration = Timelib::chrono::seconds(s) + Timelib::chrono::microseconds(ms);
Timelib::this_thread::sleep_for(duration);
return true;
}
uint64_t Time::zgetTimeRaw(const bool sinceFirstCall)
{
static bool initialized = false;
static internal_clock::time_point start;
if (!initialized)
{
start = internal_clock::now();
initialized = true;
}
const internal_clock::time_point now = internal_clock::now();
const internal_clock::duration elapsed = (sinceFirstCall ? now - start : now.time_since_epoch());
const Timelib::chrono::microseconds elapsedMs = Timelib::chrono::duration_cast<Timelib::chrono::microseconds>(elapsed);
const uint64_t microsPerSecond = 1000ULL * 1000ULL;
const uint64_t seconds = uint64_t(elapsedMs.count() / microsPerSecond);
const uint64_t fraction = uint64_t(elapsedMs.count() % microsPerSecond);
// below in fraction part, scale [0,microsPerSecond-1] to 32bit integer range
const uint64_t res = (seconds << 32) + fraction * (0xFFFFFFFFLL / (microsPerSecond - 1));
return res;
}
bool Time::isClockSteady() { return internal_clock::is_steady; }
bool Time::checkResolution(const size_t milliSeconds)
{
assert(milliSeconds != 0);
const auto resolution = double(internal_clock::period::num) / internal_clock::period::den;
return (size_t(std::ceil(resolution * 1000)) <= milliSeconds);
}
} // namespace System
@@ -0,0 +1,53 @@
PROJECT(openvibe-module-xml)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl include/*.h)
INCLUDE_DIRECTORIES(include/xml)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DXML_Shared -DXML_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DXML_Static -DXML_Exports")
IF(UNIX)
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF(UNIX)
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEModuleFS")
INCLUDE("FindThirdPartyBoost")
INCLUDE("FindThirdPartyExpat")
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
@@ -0,0 +1,28 @@
#pragma once
#include "defines.h"
#include <cstdlib> // fix Unix compatibility
namespace XML {
class XML_API IReaderCallback
{
public:
virtual ~IReaderCallback() { }
virtual void openChild(const char* name, const char** attributeName, const char** attributeValue, const size_t nAttribute) = 0;
virtual void processChildData(const char* data) = 0;
virtual void closeChild() = 0;
};
class XML_API IReaderCallBack : public IReaderCallback { };
class XML_API IReader
{
public:
virtual bool processData(const void* buffer, const size_t size) = 0;
virtual void release() = 0;
protected:
virtual ~IReader() { }
};
extern XML_API IReader* createReader(IReaderCallback& callback);
} // namespace XML
@@ -0,0 +1,28 @@
#pragma once
#include "defines.h"
namespace XML {
class XML_API IWriterCallback
{
public:
virtual ~IWriterCallback() { }
virtual void write(const char* sString) = 0;
};
class XML_API IWriterCallBack : public IWriterCallback { };
class XML_API IWriter
{
public:
virtual bool openChild(const char* name) = 0;
virtual bool setAttribute(const char* name, const char* value) = 0;
virtual bool setChildData(const char* data) = 0;
virtual bool closeChild() = 0;
virtual void release() = 0;
protected:
virtual ~IWriter() { }
};
extern XML_API IWriter* createWriter(IWriterCallback& callback);
} // namespace XML
@@ -0,0 +1,60 @@
#pragma once
#include "defines.h"
#include "IXMLNode.h"
#include <string>
#include <cstdlib> // fix Unix compatibility
namespace XML {
/**
* @class IXMLHandler
* @author Serrière Guillaume (INRIA/Loria)
* @brief This class is design to help about XML manipulation.
* @sa XML
*/
class XML_API IXMLHandler
{
public:
/**
* @brief Release the handler.
*/
virtual void release() = 0;
//Parsing
/**
* @brief Parse file points by sPath and return the root name of the document.
* @param path [in] : Path to the File
* @return The root node of the document, or NULL if there is an error.
*/
virtual IXMLNode* parseFile(const char* path) = 0;
/**
* @brief Parse the string sString on uiSize caracters and return the root name of the document.
* @param str [in] : String which contains the XML
* @param size [in] : Size of the part to analyze
* @return The root node of the parse part, or NULL if there is an error.
*/
virtual IXMLNode* parseString(const char* str, const size_t& size) = 0;
//XML extraction
/**
* @brief Write the XML corresponding to the node rNode in the file points by sPath. If the file exists
* it will be erase.
* @param node [in] : The node to write.
* @param path [in] : The path to the file.
* @return True on success, false otherwise.
*/
virtual bool writeXMLInFile(const IXMLNode& node, const char* path) const = 0;
/**
* @brief Get the description of the last error that ocurred
* @return A string object containing the error description
*/
virtual std::string getLastErrorString() const = 0;
protected:
virtual ~IXMLHandler() { }
};
extern XML_API IXMLHandler* createXMLHandler();
} // namespace XML
@@ -0,0 +1,110 @@
#pragma once
#include "defines.h"
#include <string>
#include <cstdlib> // fix Unix compatibility
namespace XML {
/**
* @class IXMLNode
* @author Serrière Guillaume (INRIA/Loria)
* @brief Symbolize a node in a XML tree structure.
* @sa XML
*/
class XML_API IXMLNode
{
public:
virtual void release() = 0;
virtual const char* getName() const = 0;
//Attribute
/**
* @brief Add the attribute name with value
* value to the node.
* @param name [in] : Name of the attribute
* @param value [in] : Value of the attribute
* @return true in success, false otherwise
*/
virtual bool addAttribute(const char* name, const char* value) = 0;
/**
* @brief Indicate if an attribute exists or not.
* @param name [in] : Name of the attribute
* @return true if attribute exists, false otherwise
*/
virtual bool hasAttribute(const char* name) const = 0;
/**
* @brief Return the value of an attribute.
* @param name [in] : Name of the attribute
* @return Value of the attribute
*/
virtual const char* getAttribute(const char* name) const = 0;
//PCDATA
/**
* @brief Set the PCDATA of the node.
* @param data [in] : Value of the PCDATA
*/
virtual void setPCData(const char* data) = 0;
/**
* @brief Apppend a string to the current PCDATA of the node
* @param data [in] : Value of teh PCDATA to append
*/
virtual void appendPCData(const char* data) = 0;
/**
* @brief Return the PCDATA of the node.
* @return Value of PCDATA
*/
virtual const char* getPCData() const = 0;
//Child
/**
* @brief Add a node child of the
* @param node [in] : The Node that will became the new child
*/
virtual void addChild(IXMLNode* node) = 0;
/**
* @brief Return the ith child of the node.
* @param index [in] : index of the child.
* @return The ith child of the node.
*/
virtual IXMLNode* getChild(const size_t index) const = 0;
/**
* @brief Return the first child with the name name.
* @param name [in]] : Name of th child
* @return The first child of the node which name is name.
*/
virtual IXMLNode* getChildByName(const char* name) const = 0;
/**
* @brief Return the amount of child the node has.
* @return Amount of child.
*/
virtual size_t getChildCount() const = 0;
//XML generation
/**
* @brief Return a string which contains the XML of the node. The string is dynamically instantiate so
* it requires to be free.
* @param depth [in] : Amount of indentation
* @return XML string describing the node and its childs.
*/
virtual char* getXML(const size_t depth = 0) const = 0;
protected:
virtual ~IXMLNode() {}
};
/**
* @brief Create a new node with the name name. The node is created dynamically and requires to be free.
* @param name [in] : Name of the node
* @return New node
*/
extern XML_API IXMLNode* createNode(const char* name);
} // namespace XML
@@ -0,0 +1,60 @@
#pragma once
#include "IReader.h"
namespace XML {
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass>
class TReaderCallbackProxy1 final : public IReaderCallback
{
public:
TReaderCallbackProxy1(TOwnerClass& ownerObject,
void (TOwnerClass::*mfpOpenChild)(const char* name, const char** attributeName, const char** attributeValue, size_t nAttribute),
void (TOwnerClass::*mfpProcessChildData)(const char* data), void (TOwnerClass::*mfpCloseChild)())
: m_ownerObject(ownerObject), m_mfpOpenChild(mfpOpenChild), m_mfpProcessChildData(mfpProcessChildData), m_mfpCloseChild(mfpCloseChild) { }
void openChild(const char* name, const char** attributeName, const char** attributeValue, const size_t nAttribute) override
{
if (m_mfpOpenChild) { m_ownerObject.m_mfpOpenChild(name, attributeName, attributeValue, nAttribute); }
}
void processChildData(const char* data) override { if (m_mfpProcessChildData) { m_ownerObject.m_mfpProcessChildData(data); } }
void closeChild() override { if (m_mfpCloseChild) { m_ownerObject.m_mfpCloseChild(); } }
protected:
TOwnerClass& m_ownerObject;
void (TOwnerClass::*m_mfpOpenChild)(const char* name, const char** attributeName, const char** attributeValue, size_t nAttribute);
void (TOwnerClass::*m_mfpProcessChildData)(const char* data);
void (TOwnerClass::*m_mfpCloseChild)();
};
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass,
void (TOwnerClass::*TMfpOpenChild)(const char* name, const char** attributeName, const char** attributeValue, size_t nAttribute),
void (TOwnerClass::*TMfpProcessChildData)(const char* data), void (TOwnerClass::*mfpCloseChild)()>
class TReaderCallbackProxy2 final : public IReaderCallback
{
public:
TReaderCallbackProxy2(TOwnerClass ownerObject)
: m_ownerObject(ownerObject), m_mfpOpenChild(TMfpOpenChild), m_mfpProcessChildData(TMfpProcessChildData), m_mfpCloseChild(mfpCloseChild) { }
void openChild(const char* name, const char** attributeName, const char** attributeValue, const size_t nAttribute) override
{
if (TMfpOpenChild) { m_ownerObject.mfpOpenChild(name, attributeName, attributeValue, nAttribute); }
}
void processChildData(const char* data) override { if (TMfpProcessChildData) { m_ownerObject.mfpProcessChildData(data); } }
void closeChild() override { if (mfpCloseChild) { m_ownerObject.mfpCloseChild(); } }
protected:
TOwnerClass& m_ownerObject;
void (TOwnerClass::*m_mfpOpenChild)(const char* name, const char** attributeName, const char** attributeValue, size_t nAttribute);
void (TOwnerClass::*m_mfpProcessChildData)(const char* data);
void (TOwnerClass::*m_mfpCloseChild)();
};
} // namespace XML

Some files were not shown because too many files have changed in this diff Show More