Merged with default protocol5

Fri, 22 Jul 2016 17:59:55 +0300

author
Teemu Piippo <teemu@compsta2.com>
date
Fri, 22 Jul 2016 17:59:55 +0300
branch
protocol5
changeset 159
970d58a01e8b
parent 155
9f71f854474a (current diff)
parent 158
de7574d292ad (diff)
child 160
cf514fa0f1cc

Merged with default

sources/interface.cpp file | annotate | diff | comparison | revisions
sources/mystring.cpp file | annotate | diff | comparison | revisions
sources/mystring.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
--- a/sources/basics.h	Wed Jul 20 18:31:19 2016 +0300
+++ b/sources/basics.h	Fri Jul 22 17:59:55 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/interface.cpp	Wed Jul 20 18:31:19 2016 +0300
+++ b/sources/interface.cpp	Fri Jul 22 17:59:55 2016 +0300
@@ -168,7 +168,7 @@
 	m_inputHistory << "";
 	m_outputLines.clear();
 	m_outputLines << ColoredLine();
-	m_session.set_interface(this);
+	m_session.setInterface(this);
 	resetTitle();
 
 	if (::has_colors())
@@ -230,7 +230,7 @@
 //
 void Interface::safeDisconnect(std::function<void(bool)> afterwards)
 {
-	if (m_session.is_active())
+	if (m_session.isActive())
 	{
 		m_disconnectCallback = afterwards;
 		setInputState(INPUTSTATE_CONFIRM_DISCONNECTION);
@@ -480,7 +480,7 @@
 {
 	String text;
 
-	switch (m_session.state())
+	switch (m_session.getState())
 	{
 	case RCON_DISCONNECTED:
 		text = "Disconnected.";
@@ -495,19 +495,19 @@
 		{
 			String adminText;
 
-			if (m_session.num_admins() == 0)
+			if (m_session.getAdminCount() == 0)
 			{
 				adminText = "No other admins";
 			}
 			else
 			{
-				adminText.sprintf("%d other admin%s", m_session.num_admins(),
-					m_session.num_admins() != 1 ? "s" : "");
+				adminText.sprintf("%d other admin%s", m_session.getAdminCount(),
+					m_session.getAdminCount() != 1 ? "s" : "");
 			}
 
 			text.sprintf("%s | %s | %s",
 				m_session.address().to_string(IPAddress::WITH_PORT).chars(),
-				m_session.level().chars(),
+				m_session.getLevel().chars(),
 				adminText.chars());
 		}
 		break;
@@ -517,7 +517,7 @@
 		text += " | ";
 
 	text += "Ctrl+N to connect, Ctrl+Q to ";
-	text +=(m_session.state() == RCON_DISCONNECTED) ? "quit" : "disconnect";
+	text +=(m_session.getState() == RCON_DISCONNECTED) ? "quit" : "disconnect";
 
 	if (text != m_statusBarText)
 	{
@@ -775,7 +775,7 @@
 				and(space == -1 or space >= m_cursorPosition))
 			{
 				String start = getCurrentInput().mid(0, m_cursorPosition);
-				m_session.request_tab_complete(start);
+				m_session.requestTabCompletion(start);
 			}
 		}
 		break;
@@ -809,7 +809,7 @@
 			if (m_inputState == INPUTSTATE_PASSWORD and not getCurrentInput().isEmpty())
 			{
 				m_session.disconnect();
-				m_session.set_password(getCurrentInput());
+				m_session.setPassword(getCurrentInput());
 				m_session.connect(m_remoteAddress);
 				setInputState(INPUTSTATE_NORMAL);
 			}
@@ -821,7 +821,7 @@
 				handleCommand(getCurrentInput());
 				flushInput();
 			}
-			else if (m_session.send_command(getCurrentInput()))
+			else if (m_session.sendCommand(getCurrentInput()))
 			{
 				flushInput();
 			}
@@ -1007,7 +1007,7 @@
 		m_remoteAddress.port = 10666;
 
 	m_session.disconnect();
-	m_session.set_password(password);
+	m_session.setPassword(password);
 	m_session.connect(m_remoteAddress);
 }
 
@@ -1079,7 +1079,7 @@
 			if (address.port == 0)
 				address.port = 10666;
 
-			m_session.set_password(args[1]);
+			m_session.setPassword(args[1]);
 			m_session.disconnect();
 			m_session.connect(m_remoteAddress = address);
 		}
--- a/sources/list.h	Wed Jul 20 18:31:19 2016 +0300
+++ b/sources/list.h	Fri Jul 22 17:59:55 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();
@@ -374,4 +399,6 @@
 	}
 };
 
+typedef Vector<unsigned char> ByteArray;
+
 END_ZFC_NAMESPACE
--- a/sources/main.cpp	Wed Jul 20 18:31:19 2016 +0300
+++ b/sources/main.cpp	Fri Jul 22 17:59:55 2016 +0300
@@ -84,7 +84,7 @@
 			FD_ZERO (&fdset);
 			FD_SET (0, &fdset);
 
-			int fd = iface.getSession()->socket()->file_descriptor();
+			int fd = iface.getSession()->getSocket()->file_descriptor();
 			highest = zfc::max (highest, fd);
 			FD_SET (fd, &fdset);
 
--- a/sources/mystring.cpp	Wed Jul 20 18:31:19 2016 +0300
+++ b/sources/mystring.cpp	Fri Jul 22 17:59:55 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 ByteArray& 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 18:31:19 2016 +0300
+++ b/sources/mystring.h	Fri Jul 22 17:59:55 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 ByteArray& 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 18:31:19 2016 +0300
+++ b/sources/network/bytestream.cpp	Fri Jul 22 17:59:55 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(ByteArray& 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.
+ */
+ByteArray::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();
+	ByteArray::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.
+ */
+ByteArray 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);
+	ByteArray 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 18:31:19 2016 +0300
+++ b/sources/network/bytestream.h	Fri Jul 22 17:59:55 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(ByteArray& 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;
+	ByteArray::Iterator getCurrentIterator();
+	int position() const;
+	ByteArray 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 ByteArray& 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;
+	ByteArray& 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 18:31:19 2016 +0300
+++ b/sources/network/rconsession.cpp	Fri Jul 22 17:59:55 2016 +0300
@@ -36,16 +36,16 @@
 // -------------------------------------------------------------------------------------------------
 //
 RCONSession::RCONSession() :
-	m_state (RCON_DISCONNECTED),
-	m_lastPing (0),
-	m_numAdmins (0),
-	m_interface (nullptr)
+	m_state(RCON_DISCONNECTED),
+	m_lastPing(0),
+	m_adminCount(0),
+	m_interface(nullptr)
 {
-	if (not m_socket.set_blocking (false))
+	if (not m_socket.set_blocking(false))
 	{
-		fprintf (stderr, "unable to set socket as non-blocking: %s\n",
+		fprintf(stderr, "unable to set socket as non-blocking: %s\n",
 			m_socket.error_string().chars());
-		exit (EXIT_FAILURE);
+		exit(EXIT_FAILURE);
 	}
 }
 
@@ -55,12 +55,12 @@
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::connect (IPAddress address)
+void RCONSession::connect(IPAddress address)
 {
 	m_address = address;
 	m_state = RCON_CONNECTING;
 	m_interface->updateStatusBar();
-	send_hello();
+	sendHello();
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -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,9 +79,9 @@
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::send (const Bytestream& packet)
+void RCONSession::send(const ByteArray& packet)
 {
-	m_socket.send (m_address, packet);
+	m_socket.send(m_address, packet);
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -94,38 +92,38 @@
 		return;
 
 	time_t now;
-	time (&now);
+	time(&now);
 
 	if (m_lastPing < now)
 	{
 		if (m_state == RCON_CONNECTING)
 		{
-			send_hello();
+			sendHello();
 		}
 		else if (m_state == RCON_AUTHENTICATING)
 		{
-			send_password();
+			sendPassword();
 		}
 		else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now)
 		{
-			Bytestream packet;
-			packet.write_byte (CLRC_PONG);
-			send (packet);
-			bump_last_ping();
+			send({CLRC_PONG});
+			bumpLastPing();
 		}
 	}
 
-	for (Datagram datagram; m_socket.read (datagram);)
-		handle_packet (datagram);
+	for (Datagram datagram; m_socket.read(datagram);)
+		handlePacket(datagram);
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::handle_packet (Datagram& datagram)
+void RCONSession::handlePacket(Datagram& datagram)
 {
 	if (datagram.address != m_address)
 		return;
 
+	Bytestream stream(datagram.message);
+
 	try
 	{
 		int32_t header = datagram.message.read_long();
@@ -134,59 +132,59 @@
 
 		while (datagram.message.bytes_left() > 0)
 		{
-			int header = datagram.message.read_byte();
+			int header = stream.readByte();
 
-			switch (ServerResponse (header))
+			switch (ServerResponse(header))
 			{
 			case SVRC_OLDPROTOCOL:
-				m_interface->printError ("Your RCON client is using outdated protocol.\n");
+				m_interface->printError("Your RCON client is using outdated protocol.\n");
 				m_state = RCON_DISCONNECTED;
 				break;
 
 			case SVRC_BANNED:
-				m_interface->printError ("You have been banned from the server.\n");
+				m_interface->printError("You have been banned from the server.\n");
 				m_state = RCON_DISCONNECTED;
 				break;
 
 			case SVRC_SALT:
-				m_salt = datagram.message.read_string();
+				m_salt = stream.readString();
 				m_state = RCON_AUTHENTICATING;
-				send_password();
+				sendPassword();
 				break;
 
 			case SVRC_INVALIDPASSWORD:
-				m_interface->printError ("Login failed.\n");
+				m_interface->printError("Login failed.\n");
 				m_state = RCON_DISCONNECTED;
 				break;
 
 			case SVRC_MESSAGE:
 				{
-					String message = datagram.message.read_string();
+					String message = stream.readString();
 					message.normalize();
-					m_interface->printText ("%s\n", message.chars());
+					m_interface->printText("%s\n", message.chars());
 				}
 				break;
 
 			case SVRC_LOGGEDIN:
-				m_interface->print ("Login successful!\n");
-				m_serverProtocol = datagram.message.read_byte();
-				m_hostname = datagram.message.read_string();
-				m_interface->setTitle (m_hostname);
+				m_interface->print("Login successful!\n");
+				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)
-					process_server_updates (datagram.message);
+				for (int i = stream.readByte(); i > 0; --i)
+					processServerUpdates(stream);
 
-				m_interface->print ("Previous messages:\n");
+				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());
+					m_interface->printText("--- %s\n", message.chars());
 				}
 
-				m_interface->print ("End of previous messages.\n");
+				m_interface->print("End of previous messages.\n");
 
 				// Watch sv_hostname so that we can update the titlebar when it changes.
 				request_watch("sv_hostname");
@@ -194,38 +192,38 @@
 				break;
 
 			case SVRC_UPDATE:
-				process_server_updates (datagram.message);
+				processServerUpdates(stream);
 				break;
 
 			case SVRC_TOOMANYTABCOMPLETES:
 				{
-					unsigned int numCompletions = datagram.message.read_short();
-					m_interface->print ("%d completions for '%s'.\n",
-						int (numCompletions), m_lastTabComplete.chars());
+					unsigned int numCompletions = stream.readShort();
+					m_interface->print("%d completions for '%s'.\n",
+						int(numCompletions), m_lastTabComplete.chars());
 				}
 				break;
 
 			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)
 					{
-						m_interface->tabComplete (m_lastTabComplete, completes[0]);
+						m_interface->tabComplete(m_lastTabComplete, completes[0]);
 					}
 					else if (not completes.is_empty())
 					{
-						m_interface->print ("Completions for '%s':\n", m_lastTabComplete.chars());
+						m_interface->print("Completions for '%s':\n", m_lastTabComplete.chars());
 
 						for (int i : range(0, completes.size(), 8))
 						{
-							Range<int> spliceRange (i, min (i + 8, completes.size()));
-							StringList splice (completes.splice (spliceRange));
-							m_interface->print ("- %s\n", splice.join (", ").chars());
+							Range<int> spliceRange(i, min(i + 8, completes.size()));
+							StringList splice(completes.splice(spliceRange));
+							m_interface->print("- %s\n", splice.join(", ").chars());
 						}
 					}
 				}
@@ -269,117 +267,115 @@
 	}
 	catch (std::exception& e)
 	{
-		m_interface->printWarning ("Couldn't process packet: %s\n", e.what());
+		m_interface->printWarning("Couldn't process packet: %s\n", e.what());
 	}
 }
 
-void RCONSession::process_server_updates (Bytestream& packet)
+void RCONSession::processServerUpdates(Bytestream& packet)
 {
-	int header = packet.read_byte();
+	int header = packet.readByte();
 
-	switch (RCONUpdateType (header))
+	switch (RCONUpdateType(header))
 	{
 	case SVRCU_PLAYERDATA:
 		{
 			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);
+			m_interface->setPlayerNames(players);
 		}
 		break;
 
 	case SVRCU_ADMINCOUNT:
-		m_numAdmins = 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;
 
 	default:
-		m_interface->printWarning ("Unknown server update type: %d\n", header);
+		m_interface->printWarning("Unknown server update type: %d\n", header);
 		break;
 	}
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-UDPSocket* RCONSession::socket()
+UDPSocket* RCONSession::getSocket()
 {
 	return &m_socket;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::send_hello()
+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);
-	bump_last_ping();
+	m_interface->print("Connecting to %s...\n", m_address.to_string(IPAddress::WITH_PORT).chars());
+	send({CLRC_BEGINCONNECTION, RCON_PROTOCOL_VERSION});
+	bumpLastPing();
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::send_password()
+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);
-	bump_last_ping();
+	m_interface->print("Authenticating...\n");
+	ByteArray message;
+	Bytestream stream(message);
+	stream.writeByte(CLRC_PASSWORD);
+	stream.writeString((m_salt + m_password).md5());
+	send(message);
+	bumpLastPing();
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::set_password (const String& password)
+void RCONSession::setPassword(const String& password)
 {
 	m_password = password;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::bump_last_ping()
+void RCONSession::bumpLastPing()
 {
 	time_t now;
-	time (&now);
+	time(&now);
 	m_lastPing = now;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-bool RCONSession::is_active() const
+bool RCONSession::isActive() const
 {
-	return state() != RCON_DISCONNECTED;
+	return getState() != RCON_DISCONNECTED;
 }
 
 // -------------------------------------------------------------------------------------------------
 // Returns true if the message was successfully sent.
 //
-bool RCONSession::send_command (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);
-	bump_last_ping();
+	ByteArray message;
+	Bytestream stream(message);
+	stream.writeByte(CLRC_COMMAND);
+	stream.writeString(commandString);
+	send(message);
+	bumpLastPing();
 	return true;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-RCONSessionState RCONSession::state() const
+RCONSessionState RCONSession::getState() const
 {
 	return m_state;
 }
@@ -393,42 +389,43 @@
 
 // -------------------------------------------------------------------------------------------------
 //
-int RCONSession::num_admins() const
+int RCONSession::getAdminCount() const
 {
-	return m_numAdmins;
+	return m_adminCount;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-const String& RCONSession::level() const
+const String& RCONSession::getLevel() const
 {
 	return m_level;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::request_tab_complete (const String& part)
+void RCONSession::requestTabCompletion(const String& part)
 {
 	if (m_serverProtocol >= 4)
 	{
-		Bytestream packet;
-		packet.write_byte (CLRC_TABCOMPLETE);
-		packet.write_string (part);
-		send (packet);
-		bump_last_ping();
+		ByteArray message;
+		Bytestream stream(message);
+		stream.writeByte(CLRC_TABCOMPLETE);
+		stream.writeString(part);
+		send(message);
+		bumpLastPing();
 		m_lastTabComplete = part;
 	}
 	else
 	{
-		m_interface->print ("This server does not support tab-completion\n", m_serverProtocol);
+		m_interface->print("This server does not support tab-completion\n", m_serverProtocol);
 	}
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::set_interface (Interface* iface)
+void RCONSession::setInterface(Interface* interface)
 {
-	m_interface = iface;
+	m_interface = interface;
 }
 
 // -------------------------------------------------------------------------------------------------
--- a/sources/network/rconsession.h	Wed Jul 20 18:31:19 2016 +0300
+++ b/sources/network/rconsession.h	Fri Jul 22 17:59:55 2016 +0300
@@ -20,10 +20,10 @@
 	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	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
+	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.
 */
@@ -101,25 +101,25 @@
 	RCONSession();
 	~RCONSession();
 
-	const IPAddress& address() const;
-	void connect (IPAddress address);
-	void disconnect();
-	void handle_packet (Datagram& datagram);
-	void process_server_updates (Bytestream& packet);
-	int num_admins() const;
-	void send (const Bytestream& packet);
-	void send_hello();
-	void send_password();
-	void set_password (const String& password);
-	UDPSocket* socket();
-	void tick();
-	void bump_last_ping();
-	bool send_command (const String& message);
-	RCONSessionState state() const;
-	const String& level() const;
-	bool is_active() const;
-	void request_tab_complete (const String& part);
-	void set_interface (class Interface* iface);
+	const IPAddress&            address() const;
+	void                        bumpLastPing();
+	void                        connect(IPAddress address);
+	void                        disconnect();
+	int                         getAdminCount() const;
+	const String&               getLevel() const;
+	UDPSocket*                  getSocket();
+	RCONSessionState            getState() const;
+	void                        handlePacket(Datagram& datagram);
+	bool                        isActive() const;
+	void                        processServerUpdates(Bytestream& packet);
+	void                        requestTabCompletion(const String& part);
+	void                        send(const ByteArray& packet);
+	bool                        sendCommand(const String& commandString);
+	void                        sendHello();
+	void                        sendPassword();
+	void                        setInterface(class Interface* interface);
+	void                        setPassword(const String& password);
+	void                        tick();
 	void request_watch (const String& cvar);
 	void request_watch (const StringList& cvars);
 
@@ -132,7 +132,7 @@
 	String m_salt;
 	int m_serverProtocol;
 	String m_hostname;
-	int m_numAdmins;
+	int m_adminCount;
 	String m_level;
 	String m_lastTabComplete;
 	class Interface* m_interface;
--- a/sources/network/udpsocket.cpp	Wed Jul 20 18:31:19 2016 +0300
+++ b/sources/network/udpsocket.cpp	Fri Jul 22 17:59:55 2016 +0300
@@ -141,17 +141,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 = ByteArray(decodedPacket, decodedLength);
 	return true;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-bool UDPSocket::send (const IPAddress& address, const Bytestream& data)
+bool UDPSocket::send (const IPAddress& address, const ByteArray& 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 18:31:19 2016 +0300
+++ b/sources/network/udpsocket.h	Fri Jul 22 17:59:55 2016 +0300
@@ -38,7 +38,7 @@
 
 struct Datagram
 {
-	Bytestream message;
+	ByteArray 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 ByteArray& 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 18:31:19 2016 +0300
+++ b/sources/range.h	Fri Jul 22 17:59:55 2016 +0300
@@ -108,6 +108,11 @@
 		return m_b;
 	}
 
+	T step() const
+	{
+		return m_step;
+	}
+
 	void check_bounds()
 	{
 		if (m_b < m_a)

mercurial