/*] 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. * **************************************************************************/ #ifndef _MOOF_PACKET_HH_ #define _MOOF_PACKET_HH_ /** * \file packet.hh * Classes for building and interpreting datagram packets. */ #include #include #include #include #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif namespace moof { /** * 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: /** * Construct a packet with an initial capacity. * \param capacity Initial capacity of the packet. */ explicit packet(size_t size = 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. */ packet& operator << (bool value); packet& operator << (int8_t value); packet& operator << (int16_t value); packet& operator << (int32_t value); packet& operator << (int64_t value); packet& operator << (uint8_t value); packet& operator << (uint16_t value); packet& operator << (uint32_t value); packet& operator << (uint64_t value); packet& operator << (float value); packet& operator << (double value); /** * 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. */ packet& operator >> (bool& value); packet& operator >> (int8_t& value); packet& operator >> (int16_t& value); packet& operator >> (int32_t& value); packet& operator >> (int64_t& value); packet& operator >> (uint8_t& value); packet& operator >> (uint16_t& value); packet& operator >> (uint32_t& value); packet& operator >> (uint64_t& value); packet& operator >> (float& value); packet& operator >> (double& value); /** * 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); /** * Clear the contents of the packet, setting the size of the packet to * zero. */ void clear(); /** * Save the current state internally, allowing it to be reverted to * later using revert(). */ void save(); /** * Revert the packet to a previously saved state, or to that state * immediately after construction if none other state has been * explicitly saved using save(). */ void revert(); /** * Get a pointer to an internal structure holding the serialized bytes * of the packet. * \return The pointer. */ const char* bytes() const { return buffer_ + state_.read_mark; } /** * Get the size of the buffer holding the serialized bytes of the * packet. * \return The number of bytes. */ size_t size() const { return state_.write_mark - state_.read_mark; } // The rest of this stuff is just to implement correct copy semantics. packet(const packet& copy); packet& operator = (const packet& copy); ~packet(); private: char* buffer_; size_t size_; struct state { size_t read_mark; size_t read_bool_mark; size_t read_bool_num; size_t write_mark; size_t write_bool_mark; size_t write_bool_num; state(size_t size = 0) : read_mark(0), read_bool_mark(0), read_bool_num(0), write_mark(size), write_bool_mark(0), write_bool_num(0) {} }; state state_; state saved_; }; template inline packet& operator << (packet& packet, const T& value) { value.pack(packet); return packet; } template inline packet& operator >> (packet& packet, T& value) { value.unpack(packet); return packet; } inline packet& operator << (packet& packet, const char* value) { uint16_t length = strlen(value); packet << length; if (packet.write(value, length) != length) { throw std::length_error("out of memory"); } return packet; } template inline packet& operator << (packet& packet, const std::basic_string& value) { packet << static_cast(value.length()); size_t num_bytes = value.length() * sizeof(T); if (packet.write(value.data(), num_bytes) != num_bytes) { throw std::length_error("out of memory"); } return packet; } template inline packet& operator >> (packet& packet, std::basic_string& value) { uint16_t length = 0; packet >> length; T str[length]; size_t num_bytes = length * sizeof(T); if (packet.read(str, num_bytes) != num_bytes) { throw std::out_of_range("end of packet"); } value.assign(str, length); return packet; } template inline packet& operator << (packet& packet, const std::vector& value) { packet << static_cast(value.size()); typename std::vector::const_iterator it; for (it = value.begin(); it != value.end(); ++it) { packet << *it; } return packet; } template inline packet& operator >> (packet& packet, std::vector& value) { uint16_t size = 0; packet >> size; value.clear(); for (uint16_t i = 0; i < size; ++i) { T item; packet >> item; value.push_back(item); } return packet; } } // namespace moof #endif // _MOOF_PACKET_HH_