--- a/sources/network/bytestream.cpp Wed Jul 20 22:56:16 2016 +0300 +++ b/sources/network/bytestream.cpp Fri Jul 22 17:50:00 2016 +0300 @@ -32,270 +32,224 @@ #include <string.h> BEGIN_ZFC_NAMESPACE -// ------------------------------------------------------------------------------------------------- -// -Bytestream::Bytestream (unsigned long length) : - m_data (nullptr) -{ - resize (length); - clear(); -} - -// ------------------------------------------------------------------------------------------------- -// -Bytestream::Bytestream (const unsigned char* data, unsigned long length) : - m_data (nullptr) -{ - init (data, length); -} - -// ------------------------------------------------------------------------------------------------- -// -Bytestream::Bytestream (const Vector<unsigned char>& bytes) : - m_data (nullptr) -{ - init (bytes.data(), bytes.size()); -} - -// ------------------------------------------------------------------------------------------------- -// -Bytestream::Bytestream (const Bytestream& other) : - m_data (nullptr) -{ - init (other.data(), other.written_length()); -} - -// ------------------------------------------------------------------------------------------------- -// -Bytestream::~Bytestream() -{ - delete[] m_data; -} - -// ------------------------------------------------------------------------------------------------- -Bytestream& Bytestream::operator= (const Bytestream& other) -{ - init (other.data(), other.written_length()); - return *this; -} +/*! + * \brief Constructs a byte cursor. The cursor is placed to the beginning of the stream. + * \param data + */ +Bytestream::Bytestream(Vector<unsigned char>& data) : + m_data(data), + m_position(0) {} -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::resize (unsigned long newsize) -{ - Vector<unsigned char> olddata; - unsigned long oldsize = 0L; - - if (m_data != nullptr) - { - oldsize = allocated_size(); - olddata.resize (oldsize); - memcpy (olddata.data(), m_data, oldsize); - } - - delete[] m_data; - m_allocatedSize = newsize; - m_data = new unsigned char[newsize]; - - if (oldsize > 0L) - memcpy (m_data, olddata, min (oldsize, newsize)); -} - -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::init (const unsigned char* data, unsigned long length) +/*! + * \brief Ensures that the specified amount of bytes can be read. Raises IOError if this is not the case. + * \param bytes Amount of bytes to check. + */ +void Bytestream::ensureReadSpace(int bytes) { - resize (length); - memcpy (m_data, data, length); - m_cursor = &m_data[0]; - m_writtenLength = length; -} - -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::clear() -{ - m_cursor = &m_data[0]; - m_writtenLength = 0; -} - -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::ensure_read_space (unsigned int bytes) -{ - if (bytes_left() < bytes) + if (bytesLeft() < bytes) { - int bytesPast = bytes - bytes_left(); - + int bytesPast = bytes - bytesLeft(); String message; - message.sprintf ("attempted to read %d byte%s past the end of bytestream", - bytesPast, bytesPast != -1 ? "s" : ""); + message.sprintf("attempted to read %d byte%s past the end of bytestream", bytesPast, plural(bytesPast)); throw IOError (message); } } -// ------------------------------------------------------------------------------------------------- -// -int8_t Bytestream::read_byte() +/*! + * \returns the amount of bytes remaining for reading. + */ +int Bytestream::bytesLeft() const { - ensure_read_space (1); - return *m_cursor++; + return m_data.size() - m_position; +} + +/*! + * \returns an iterator to the current data position. + */ +Vector<unsigned char>::Iterator Bytestream::getCurrentIterator() +{ + return m_data.begin() + m_position; } -// ------------------------------------------------------------------------------------------------- -// -int16_t Bytestream::read_short() +/*! + * \brief Reads in a byte. + * \returns the read byte. + */ +int8_t Bytestream::readByte() { - ensure_read_space (2); - short int result = 0; + ensureReadSpace(1); + return read(); +} + +/*! + * \brief Reads in two bytes to form an integer value. + * \returns the read value. + */ +int16_t Bytestream::readShort() +{ + ensureReadSpace (2); + int16_t result = 0; for (int i : range(2)) - result |= m_cursor[i] << (i * 8); + result |= read() << (i * 8); - m_cursor += 2; return result; } -// ------------------------------------------------------------------------------------------------- -// -int32_t Bytestream::read_long() +/*! + * \brief Reads in four bytes to form an integer value. + * \returns the read value. + */ +int32_t Bytestream::readLong() { - ensure_read_space (4); - long int result = 0; + ensureReadSpace (4); + int32_t result = 0; for (int i : range(4)) - result |= m_cursor[i] << (i * 8); + result |= read() << (i * 8); - m_cursor += 4; return result; } -// ------------------------------------------------------------------------------------------------- -// -float Bytestream::read_float() +/*! + * \brief Reads in four bytes to form a floating point number. + * \returns the read value. + */ +float Bytestream::readFloat() { float value; - int intvalue = read_long(); - memcpy (&value, &intvalue, sizeof intvalue); + int intvalue = readLong(); + memcpy(&value, &intvalue, sizeof intvalue); return value; } -// ------------------------------------------------------------------------------------------------- -// -String Bytestream::read_string() +/*! + * \brief Reads in characters until a null terminator is encountered. + * \returns the read string. + */ +String Bytestream::readString() { - // Zandronum sends strings of maximum 2048 characters, though it only - // reads 2047-character long ones so I guess we can follow up and do - // the same :-) - static char buffer[MAX_NETWORK_STRING]; - unsigned char* stringEnd; - unsigned char* stringBegin = m_cursor; - unsigned char* end = m_data + allocated_size(); + Vector<unsigned char>::Iterator stringEndIterator; // Where's the end of the string? - for (stringEnd = m_cursor; *stringEnd != '\0'; ++stringEnd) + for (stringEndIterator = getCurrentIterator(); *stringEndIterator != '\0'; ++stringEndIterator) { - if (stringEnd == end) + if (stringEndIterator == m_data.end()) { // Past the end of the buffer - throw IOError ("unterminated or too long string in packet"); + throw IOError("unterminated or too long string in packet"); } } - unsigned int length = stringEnd - m_cursor; - m_cursor = stringEnd + 1; - memcpy (buffer, stringBegin, length); - buffer[length] = '\0'; - return String (buffer); -} + // Skip past the null terminator. + if (*stringEndIterator == '\0') + stringEndIterator += 1; -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::read (unsigned char* buffer, unsigned long length) -{ - ensure_read_space (length); - memcpy (buffer, m_cursor, length); - m_cursor += length; + // Build and return the string, and advance the position. + int stringStart = m_position; + unsigned int length = stringEndIterator - getCurrentIterator(); + length = min<int>(length, MAX_NETWORK_STRING); + m_position += length; + return String::fromBytes(m_data.splice(stringStart, stringStart + length)); } -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::write (unsigned char val) +/*! + * \brief Reads in a buffer of the specified length. + * \param length Amount of bytes to read. + * \returns the read buffer. + */ +Vector<unsigned char> Bytestream::readBuffer(int length) { - *m_cursor++ = val; - m_writtenLength++; -} - -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::write (const unsigned char* val, unsigned int length) -{ - grow_to_fit (length); - memcpy (m_cursor, val, length); - m_cursor += length; - m_writtenLength += length; + ensureReadSpace(length); + Vector<unsigned char> result(length); + memcpy(result.data(), m_data.data() + m_position, length); + m_position += length; + return result; } -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::grow_to_fit (unsigned long bytes) +/*! + * \brief Writes an integer to the end of the data as one byte. + * \param value Value to write + */ +void Bytestream::writeByte(int8_t value) { - if (space_left() < bytes) - resize (allocated_size() + bytes + 128); -} - -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::write_byte (int8_t val) -{ - grow_to_fit (1); - write (val); + m_data.append(value); } -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::write_short (int16_t val) +/*! + * \brief Writes an integer to the end of the data as 2 bytes. + * \param value Value to write + */ +void Bytestream::writeShort(int16_t value) { - grow_to_fit (2); - for (int i : range(2)) - write ((val >> (i * 8)) & 0xFF); + m_data.append((value >> (i * 8)) & 0xFF); } -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::write_long (int32_t val) +/*! + * \brief Writes an integer to the end of the data as 4 bytes. + * \param value Value to write + */ +void Bytestream::writeLong(int32_t value) { - grow_to_fit (4); - for (int i : range(4)) - write ((val >> (i * 8)) & 0xFF); + m_data.append((value >> (i * 8)) & 0xFF); } -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::write_float (float val) +/*! + * \brief Writes a floating-point number to the end of the data. + * \param value Value to write. + */ +void Bytestream::writeFloat(float value) { // I know this is probably dangerous but this is what Zandronum does so yeah int intvalue; - memcpy (&intvalue, &val, sizeof val); - write_long (intvalue); + memcpy (&intvalue, &value, sizeof value); + writeLong (intvalue); +} + +/*! + * \brief Writes the given string to the end of the data. + * \param text String to write. + */ +void Bytestream::writeString(const String& string) +{ + m_data.append(string.toBytes(), string.length()); + m_data.append(0); +} + +/*! + * \returns the current position the stream cursor in the data. + */ +int Bytestream::position() const +{ + return m_position; } -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::write_string (const String& val) +/*! + * \brief Seeks the stream cursor to the beginning of the data. + */ +void Bytestream::rewind() { - grow_to_fit (val.length() + 1); - write (reinterpret_cast<const unsigned char*> (val.chars()), val.length()); - write (0); + m_position = 0; } -// ------------------------------------------------------------------------------------------------- -// -void Bytestream::write_buffer (const Bytestream& other) +/*! + * \brief Seeks the stream cursor to a custom location. + * \param position Position to seek too. + */ +void Bytestream::seek(int position) { - write (other.data(), other.written_length()); + m_position = position; +} + +/*! + * \brief Reads a byte and advances the cursor. No safety checks are done. + * \returns the read byte. + */ +int8_t Bytestream::read() +{ + return m_data[m_position++]; } END_ZFC_NAMESPACE \ No newline at end of file