/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * **************************************************************************/ /** * \file Packet.hh * Classes for building and interpreting datagram packets. */ #ifndef _MOOF_PACKET_HH_ #define _MOOF_PACKET_HH_ #include #include #include #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif namespace Mf { /** * Represents a packet of serialized variables ready for transfer over the * network. This method is most suitable for representing datagram * packets, but it may also be useful for seralizing data for persistent * state storage. The semantics are similar to that of a FIFO queue or * stream where packets are written and read by inserting and extracting * variables to and from the packet, although the actual order of the * variables in the buffer may be different. At any time, a pointer to a * buffer and the size of the buffer can be retrieved. This class also * handles endian differences by serializing variables in network byte * order (big endian). */ class Packet { public: /** * Packet flags. */ enum Flags { PEEK = 0x01 /// Do not actually remove anything. }; /** * Construct a packet with an initial capacity. * \param capacity Initial capacity of the packet. */ Packet(size_t capacity = PAGE_SIZE); /** * Construct a packet with some bytes from a buffer. The bytes will be * copied into the packet, so you don't need to keep the original * buffer. * \param data The bytes. * \param size The number of bytes. */ Packet(const char* data, size_t size); /** * Insert a variable into the packet, serializing it. This usually * increases the size of the packet by the size of the data type. * \param value The value to insert. * \return This. */ bool put(bool value); bool put(int8_t value); bool put(int16_t value); bool put(int32_t value); bool put(int64_t value); bool put(uint8_t value); bool put(uint16_t value); bool put(uint32_t value); bool put(uint64_t value); bool put(float value); bool put(double value); bool put(const char* value) { uint16_t length = strlen(value); if (put(length)) { size_t numBytes = write(value, length); return numBytes == length; } return false; } template bool put(const std::basic_string& value) { if (put(uint16_t(value.length()))) { size_t byteLength = value.length() * sizeof(T); size_t numBytes = write(value.data(), byteLength); return numBytes == byteLength; } return false; } template bool put(const std::vector& value) { if (put(uint16_t(value.size()))) { typename std::vector::const_iterator it; for (it = value.begin(); it != value.end(); ++it) { if (!put(*it)) return false; } return true; } return false; } /** * Write some bytes to the packet. * \param bytes The bytes. * \param size The number of bytes. * return The number of bytes actually written. */ size_t write(const void* bytes, size_t size); /** * Extract a variable from the packet. This usually decreases the size * of the packet by the size of the data type. * \param value Reference to the variable to extract. * \return This. */ bool get(bool& value, int flags = 0); bool get(int8_t& value, int flags = 0); bool get(int16_t& value, int flags = 0); bool get(int32_t& value, int flags = 0); bool get(int64_t& value, int flags = 0); bool get(uint8_t& value, int flags = 0); bool get(uint16_t& value, int flags = 0); bool get(uint32_t& value, int flags = 0); bool get(uint64_t& value, int flags = 0); bool get(float& value, int flags = 0); bool get(double& value, int flags = 0); template bool get(std::basic_string& value, int flags = 0) { uint16_t length = 0; if (get(length, flags)) { size_t byteLength = length * sizeof(T); T str[length]; size_t numBytes = read(str, byteLength, flags); value.assign(str, numBytes); return numBytes == byteLength; } return false; } template bool get(std::vector& value, int flags = 0) { uint16_t size = 0; if (get(size, flags)) { value.clear(); for (uint16_t i = 0; i < size; ++i) { T item; if (get(item, flags)) value.push_back(item); else return false; } return true; } return false; } /** * Read some bytes from the packet. * \param bytes The buffer to hold the bytes read. * \param size The size of the read buffer. * \return The number of bytes actually read. */ size_t read(void* bytes, size_t size, int flags = 0); /** * Clear the contents of the packet, setting the size of the packet to * zero. */ void clear(); /** * Reset the read/write markers to their initial positions, putting the * packet in the state it was at right after construction. */ void reset(); /** * Get a pointer to an internal structure holding the serialized bytes * of the packet. * return The pointer. */ const char* bytes() const { return mBuffer + mR; } /** * Get the size of the buffer holding the serialized bytes of the * packet. * \return The number of bytes. */ size_t size() const { return mW - mR; } // The rest of this stuff is just to implement correct copy semantics. Packet(const Packet& copy); Packet& operator=(const Packet& copy); ~Packet(); private: char* mBuffer; size_t mSize; size_t mR; size_t mW; size_t mOriginalW; size_t mBoolR; size_t mBoolW; size_t mBoolNumR; size_t mBoolNumW; }; } // namespace Mf #endif // _MOOF_PACKET_HH_