Bytestream now behaves more like a cursor. It does not store the data anymore, rather it leaves the user to specify a vector to use for storage.

Fri, 22 Jul 2016 17:50:00 +0300

author
Teemu Piippo <teemu@compsta2.com>
date
Fri, 22 Jul 2016 17:50:00 +0300
changeset 157
42bb29924218
parent 156
ce66d7e374bf
child 158
de7574d292ad

Bytestream now behaves more like a cursor. It does not store the data anymore, rather it leaves the user to specify a vector to use for storage.

sources/basics.h file | annotate | diff | comparison | revisions
sources/list.h file | annotate | diff | comparison | revisions
sources/mystring.cpp file | annotate | diff | comparison | revisions
sources/mystring.h file | annotate | diff | comparison | revisions
sources/network/bytestream.cpp file | annotate | diff | comparison | revisions
sources/network/bytestream.h file | annotate | diff | comparison | revisions
sources/network/rconsession.cpp file | annotate | diff | comparison | revisions
sources/network/rconsession.h file | annotate | diff | comparison | revisions
sources/network/udpsocket.cpp file | annotate | diff | comparison | revisions
sources/network/udpsocket.h file | annotate | diff | comparison | revisions
sources/range.h file | annotate | diff | comparison | revisions
--- a/sources/basics.h	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/basics.h	Fri Jul 22 17:50:00 2016 +0300
@@ -90,6 +90,11 @@
 	return (a < b) ? b : (a > c) ? c : a;
 }
 
+inline const char *plural(int value)
+{
+	return value != 1 ? "s" : "";
+}
+
 template <typename T, size_t N>
 char (&_ArraySizeHelper(T (&array)[N]))[N];
 #define countof(array) (sizeof(_ArraySizeHelper( array )))
--- a/sources/list.h	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/list.h	Fri Jul 22 17:50:00 2016 +0300
@@ -58,12 +58,24 @@
 	Container (const C& other) :
 		m_container (other) {}
 
+	Container(std::initializer_list<T> initializerList) :
+	    m_container(initializerList) {}
+
 	T& append (const T& value)
 	{
 		m_container.push_back (value);
 		return m_container[m_container.size() - 1];
 	}
 
+	void append(const T* values, size_t numValues)
+	{
+		size_t i0 = size();
+		resize(size() + numValues);
+
+		for (size_t i : range(numValues))
+			(*this)[i0 + i] = values[i];
+	}
+
 	Iterator begin()
 	{
 		return m_container.begin();
@@ -256,22 +268,29 @@
 		std::sort (begin(), end());
 	}
 
-	Self splice (int a, int b) const
+	Self splice(int start, int end, int step = 1) const
 	{
-		if (a < 0 or b >= size() or b < a)
-			return Self();
+		start = clamp(start, 0, size() - 1);
+		end = clamp(end, 0, size() - 1);
 
-		Self result;
+		if (end <= start)
+		{
+			return Self();
+		}
+		else
+		{
+			Self result;
 
-		for (int i = a; i < b; ++i)
-			result << operator[] (i);
+			for (int i : range(start, end, step))
+				result << operator[] (i);
 
-		return result;
+			return result;
+		}
 	}
 
-	Self splice (const Range<int>& a) const
+	Self splice(Range<int>& range) const
 	{
-		return splice (a.min(), a.max());
+		return splice(range.min(), range.max(), range.step());
 	}
 
 	Self& operator<< (const T& value)
@@ -358,6 +377,12 @@
 	Vector (T* data, size_t length) :
 		Super (std::vector<T> (data, data + length)) {}
 
+	Vector(std::initializer_list<T> initializerList) :
+	    Super(initializerList) {}
+
+	Vector(const Super& other) :
+		Super(other) {}
+
 	T* data()
 	{
 		return Super::m_container.data();
--- a/sources/mystring.cpp	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/mystring.cpp	Fri Jul 22 17:50:00 2016 +0300
@@ -561,6 +561,16 @@
 }
 
 /*!
+ * \brief Constructs a string from a vector of bytes. The bytes do not have to be null-terminated.
+ * \param bytes Bytes to use for construction
+ * \returns the resulting string.
+ */
+String String::fromBytes(const Vector<unsigned char>& bytes)
+{
+	return String(reinterpret_cast<const Vector<char>&>(bytes));
+}
+
+/*!
  * \returns the MD5-checksum of this string.
  */
 String String::md5() const
--- a/sources/mystring.h	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/mystring.h	Fri Jul 22 17:50:00 2016 +0300
@@ -93,6 +93,7 @@
 	const std::string&          stdString() const;
 	void                        strip(char unwanted);
 	void                        strip(const List<char> &unwanted);
+	const unsigned char*        toBytes() const;
 	double                      toDouble(bool* ok = nullptr) const;
 	float                       toFloat(bool* ok = nullptr) const;
 	long                        toInt(bool* ok = nullptr, int base = 10) const;
@@ -107,6 +108,7 @@
 	static String               fromNumber(unsigned int a);
 	static String               fromNumber(unsigned long int a);
 	static String               fromNumber(double a);
+	static String               fromBytes(const Vector<unsigned char>& bytes);
 
 	String                      operator+(const String& data) const;
 	String                      operator+(const char* data) const;
@@ -553,6 +555,14 @@
 }
 
 /*!
+ * \returns the underlying char-array representation of this string, casted to unsigned chars.
+ */
+inline const unsigned char* String::toBytes() const
+{
+	return reinterpret_cast<const unsigned char*>(chars());
+}
+
+/*!
  * \brief Constructs an empty string list.
  */
 inline StringList::StringList() {}
--- 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
--- a/sources/network/bytestream.h	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/network/bytestream.h	Fri Jul 22 17:50:00 2016 +0300
@@ -40,119 +40,51 @@
 	MAX_NETWORK_STRING = 0x800
 };
 
-// TODO: Make able to handle big-endian too
+class IOError : public std::exception
+{
+	String m_message;
+
+public:
+	IOError(String message) :
+		m_message(message) {}
+
+	const char* what() const throw()
+	{
+		return m_message;
+	}
+};
+
 class Bytestream
 {
 public:
-	class IOError : public std::exception
-	{
-		String m_message;
-
-	public:
-		IOError (String message) :
-			m_message (message) {}
-
-		const char* what() const throw()
-		{
-			return m_message;
-		}
-	};
-
-	Bytestream (unsigned long length = 0x800);
-	Bytestream (const unsigned char* data, unsigned long length);
-	Bytestream (const Vector<unsigned char>& bytes);
-	Bytestream (const Bytestream& other);
-	~Bytestream();
-
-	void clear();
-	void grow_to_fit (unsigned long bytes);
-	void read (unsigned char* buffer, unsigned long length);
-	int8_t read_byte();
-	int32_t read_long();
-	int16_t read_short();
-	String read_string();
-	float read_float();
-	void resize (unsigned long length);
-	void write (const unsigned char* val, unsigned int length);
-	void write_buffer (const Bytestream& other);
-	void write_buffer (const Vector<unsigned char>& other);
-	void write_byte (int8_t val);
-	void write_double (double val);
-	void write_float (float val);
-	void write_long (int32_t val);
-	void write_short (int16_t val);
-	void write_string (const String& val);
-
-	Bytestream& operator= (const Bytestream& other);
-
-	unsigned long allocated_size() const
-	{
-		return m_allocatedSize;
-	}
+	Bytestream(Vector<unsigned char>& data);
 
-	unsigned long bytes_left() const
-	{
-		return m_writtenLength - (m_cursor - &m_data[0]);
-	}
-
-	inline unsigned char* data()
-	{
-		return m_data;
-	}
-	inline const unsigned char* data() const
-	{
-		return m_data;
-	}
-
-	unsigned long position() const
-	{
-		return m_cursor - m_data;
-	}
-
-	void rewind()
-	{
-		m_cursor = m_data;
-	}
-
-	void seek (unsigned long pos)
-	{
-		m_cursor = m_data + pos;
-	}
-
-	Vector<unsigned char> to_vector() const
-	{
-		return Vector<unsigned char> (m_data, m_writtenLength);
-	}
-
-	unsigned long written_length() const
-	{
-		return m_writtenLength;
-	}
-
-	unsigned char& operator[] (unsigned long idx)
-	{
-		return m_data[idx];
-	}
-
-	unsigned char operator[] (unsigned long idx) const
-	{
-		return m_data[idx];
-	}
+	int bytesLeft() const;
+	Vector<unsigned char>::Iterator getCurrentIterator();
+	int position() const;
+	Vector<unsigned char> readBuffer(int length);
+	int8_t readByte();
+	int32_t readLong();
+	int16_t readShort();
+	String readString();
+	float readFloat();
+	void rewind();
+	void seek(int position);
+	void write(const unsigned char* val, unsigned int length);
+	void writeBuffer(const Vector<unsigned char>& other);
+	void writeByte(int8_t value);
+	void writeDouble(double val);
+	void writeFloat(float value);
+	void writeLong(int32_t value);
+	void writeShort(int16_t value);
+	void writeString(const String& string);
 
 private:
-	unsigned char* m_data;
-	unsigned char* m_cursor;
-	unsigned long m_allocatedSize;
-	unsigned long m_writtenLength;
+	Vector<unsigned char>& m_data;
+	int m_position;
 
-	void init (const unsigned char* data, unsigned long length);
-	void write (unsigned char val);
-	void ensure_read_space (unsigned int bytes);
-
-	unsigned long space_left() const
-	{
-		return m_allocatedSize - m_writtenLength;
-	}
+	int8_t read();
+	void ensureReadSpace(int bytes);
 };
 
 END_ZFC_NAMESPACE
--- a/sources/network/rconsession.cpp	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/network/rconsession.cpp	Fri Jul 22 17:50:00 2016 +0300
@@ -70,9 +70,7 @@
 	if (m_state > RCON_CONNECTING)
 	{
 		// Say goodbye to remote
-		Bytestream packet;
-		packet.write_byte(CLRC_DISCONNECT);
-		this->send(packet);
+		send({CLRC_DISCONNECT});
 		m_interface->disconnected();
 	}
 
@@ -81,7 +79,7 @@
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::send(const Bytestream& packet)
+void RCONSession::send(const Vector<unsigned char>& packet)
 {
 	m_socket.send(m_address, packet);
 }
@@ -108,9 +106,7 @@
 		}
 		else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now)
 		{
-			Bytestream packet;
-			packet.write_byte(CLRC_PONG);
-			send(packet);
+			send({CLRC_PONG});
 			bumpLastPing();
 		}
 	}
@@ -126,11 +122,13 @@
 	if (datagram.address != m_address)
 		return;
 
+	Bytestream stream(datagram.message);
+
 	try
 	{
-		while (datagram.message.bytes_left() > 0)
+		while (stream.bytesLeft() > 0)
 		{
-			int header = datagram.message.read_byte();
+			int header = stream.readByte();
 
 			switch (ServerResponse(header))
 			{
@@ -145,7 +143,7 @@
 				break;
 
 			case SVRC_SALT:
-				m_salt = datagram.message.read_string();
+				m_salt = stream.readString();
 				m_state = RCON_AUTHENTICATING;
 				sendPassword();
 				break;
@@ -157,7 +155,7 @@
 
 			case SVRC_MESSAGE:
 				{
-					String message = datagram.message.read_string();
+					String message = stream.readString();
 					message.normalize();
 					m_interface->printText("%s\n", message.chars());
 				}
@@ -165,19 +163,19 @@
 
 			case SVRC_LOGGEDIN:
 				m_interface->print("Login successful!\n");
-				m_serverProtocol = datagram.message.read_byte();
-				m_hostname = datagram.message.read_string();
+				m_serverProtocol = stream.readByte();
+				m_hostname = stream.readString();
 				m_interface->setTitle(m_hostname);
 				m_state = RCON_CONNECTED;
 
-				for (int i = datagram.message.read_byte(); i > 0; --i)
-					processServerUpdates(datagram.message);
+				for (int i = stream.readByte(); i > 0; --i)
+					processServerUpdates(stream);
 
 				m_interface->print("Previous messages:\n");
 
-				for (int i = datagram.message.read_byte(); i > 0; --i)
+				for (int i = stream.readByte(); i > 0; --i)
 				{
-					String message = datagram.message.read_string();
+					String message = stream.readString();
 					message.normalize();
 					m_interface->printText("--- %s\n", message.chars());
 				}
@@ -186,12 +184,12 @@
 				break;
 
 			case SVRC_UPDATE:
-				processServerUpdates(datagram.message);
+				processServerUpdates(stream);
 				break;
 
 			case SVRC_TOOMANYTABCOMPLETES:
 				{
-					unsigned int numCompletions = datagram.message.read_short();
+					unsigned int numCompletions = stream.readShort();
 					m_interface->print("%d completions for '%s'.\n",
 						int(numCompletions), m_lastTabComplete.chars());
 				}
@@ -200,10 +198,10 @@
 			case SVRC_TABCOMPLETE:
 				{
 					StringList completes;
-					completes.resize(datagram.message.read_byte());
+					completes.resize(stream.readByte());
 
 					for (String& completion : completes)
-						completion = datagram.message.read_string();
+						completion = stream.readString();
 
 					if (completes.size() == 1)
 					{
@@ -233,7 +231,7 @@
 
 void RCONSession::processServerUpdates(Bytestream& packet)
 {
-	int header = packet.read_byte();
+	int header = packet.readByte();
 
 	switch (RCONUpdateType(header))
 	{
@@ -241,20 +239,20 @@
 		{
 			StringList players;
 
-			for (int i = packet.read_byte(); i > 0; --i)
-				players.append(packet.read_string());
+			for (int i = packet.readByte(); i > 0; --i)
+				players.append(packet.readString());
 
 			m_interface->setPlayerNames(players);
 		}
 		break;
 
 	case SVRCU_ADMINCOUNT:
-		m_adminCount = packet.read_byte();
+		m_adminCount = packet.readByte();
 		m_interface->updateStatusBar();
 		break;
 
 	case SVRCU_MAP:
-		m_level = packet.read_string();
+		m_level = packet.readString();
 		m_interface->updateStatusBar();
 		break;
 
@@ -276,10 +274,7 @@
 void RCONSession::sendHello()
 {
 	m_interface->print("Connecting to %s...\n", m_address.to_string(IPAddress::WITH_PORT).chars());
-	Bytestream packet;
-	packet.write_byte(CLRC_BEGINCONNECTION);
-	packet.write_byte(RCON_PROTOCOL_VERSION);
-	send(packet);
+	send({CLRC_BEGINCONNECTION, RCON_PROTOCOL_VERSION});
 	bumpLastPing();
 }
 
@@ -288,10 +283,11 @@
 void RCONSession::sendPassword()
 {
 	m_interface->print("Authenticating...\n");
-	Bytestream packet;
-	packet.write_byte(CLRC_PASSWORD);
-	packet.write_string((m_salt + m_password).md5());
-	send(packet);
+	Vector<unsigned char> message;
+	Bytestream stream(message);
+	stream.writeByte(CLRC_PASSWORD);
+	stream.writeString((m_salt + m_password).md5());
+	send(message);
 	bumpLastPing();
 }
 
@@ -321,15 +317,16 @@
 // -------------------------------------------------------------------------------------------------
 // Returns true if the message was successfully sent.
 //
-bool RCONSession::sendCommand(const String& message)
+bool RCONSession::sendCommand(const String& commandString)
 {
-	if (m_state != RCON_CONNECTED or message.isEmpty())
+	if (m_state != RCON_CONNECTED or commandString.isEmpty())
 		return false;
 
-	Bytestream packet;
-	packet.write_byte(CLRC_COMMAND);
-	packet.write_string(message);
-	send(packet);
+	Vector<unsigned char> message;
+	Bytestream stream(message);
+	stream.writeByte(CLRC_COMMAND);
+	stream.writeString(commandString);
+	send(message);
 	bumpLastPing();
 	return true;
 }
@@ -368,10 +365,11 @@
 {
 	if (m_serverProtocol >= 4)
 	{
-		Bytestream packet;
-		packet.write_byte(CLRC_TABCOMPLETE);
-		packet.write_string(part);
-		send(packet);
+		Vector<unsigned char> message;
+		Bytestream stream(message);
+		stream.writeByte(CLRC_TABCOMPLETE);
+		stream.writeString(part);
+		send(message);
 		bumpLastPing();
 		m_lastTabComplete = part;
 	}
--- a/sources/network/rconsession.h	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/network/rconsession.h	Fri Jul 22 17:50:00 2016 +0300
@@ -107,8 +107,8 @@
 	bool                        isActive() const;
 	void                        processServerUpdates(Bytestream& packet);
 	void                        requestTabCompletion(const String& part);
-	void                        send(const Bytestream& packet);
-	bool                        sendCommand(const String& message);
+	void                        send(const Vector<unsigned char>& packet);
+	bool                        sendCommand(const String& commandString);
 	void                        sendHello();
 	void                        sendPassword();
 	void                        setInterface(class Interface* interface);
--- a/sources/network/udpsocket.cpp	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/network/udpsocket.cpp	Fri Jul 22 17:50:00 2016 +0300
@@ -135,17 +135,16 @@
 		decodedPacket, length, &decodedLength);
 	datagram.address.host = ntohl (claddr.sin_addr.s_addr);
 	datagram.address.port = ntohs (claddr.sin_port);
-	datagram.message = Bytestream (decodedPacket, decodedLength);
+	datagram.message = Vector<unsigned char>(decodedPacket, decodedLength);
 	return true;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-bool UDPSocket::send (const IPAddress& address, const Bytestream& data)
+bool UDPSocket::send (const IPAddress& address, const Vector<unsigned char>& data)
 {
 	int encodedlength = sizeof HuffmanBuffer;
-	HUFFMAN_Encode (data.data(), reinterpret_cast<unsigned char*> (HuffmanBuffer),
-		data.written_length(), &encodedlength);
+	HUFFMAN_Encode (data.data(), reinterpret_cast<unsigned char*> (HuffmanBuffer), data.size(), &encodedlength);
 	sockaddr_in claddr = address.to_sockaddr_in();
 	int res = ::sendto (m_socket, HuffmanBuffer, encodedlength, 0,
 		reinterpret_cast<sockaddr*> (&claddr), sizeof claddr);
--- a/sources/network/udpsocket.h	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/network/udpsocket.h	Fri Jul 22 17:50:00 2016 +0300
@@ -38,7 +38,7 @@
 
 struct Datagram
 {
-	Bytestream message;
+	Vector<unsigned char> message;
 	IPAddress address;
 };
 
@@ -52,7 +52,7 @@
 
 	bool bind (unsigned short port);
 	bool read (Datagram& datagram);
-	bool send (const IPAddress& address, const Bytestream& data);
+	bool send (const IPAddress& address, const Vector<unsigned char>& data);
 	bool set_blocking (bool a);
 	const String& error_string() const { return m_error; }
 	int file_descriptor() const { return m_socket; }
--- a/sources/range.h	Wed Jul 20 22:56:16 2016 +0300
+++ b/sources/range.h	Fri Jul 22 17:50:00 2016 +0300
@@ -108,6 +108,11 @@
 		return m_b;
 	}
 
+	T step() const
+	{
+		return m_step;
+	}
+
 	void check_bounds()
 	{
 		if (m_b < m_a)

mercurial