sources/network/bytestream.cpp

Wed, 27 Jan 2021 12:39:00 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 27 Jan 2021 12:39:00 +0200
branch
protocol5
changeset 174
78ee24fd3321
parent 167
0150f86e68f0
child 175
18f2d2de1929
permissions
-rw-r--r--

reduce delta with default

/*
	Copyright 2014 - 2016 Teemu Piippo
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions
	are met:

	1. Redistributions of source code must retain the above copyright
	   notice, this list of conditions and the following disclaimer.
	2. Redistributions in binary form must reproduce the above copyright
	   notice, this list of conditions and the following disclaimer in the
	   documentation and/or other materials provided with the distribution.
	3. Neither the name of the copyright holder nor the names of its
	   contributors may be used to endorse or promote products derived from
	   this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
	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,
	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
	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "bytestream.h"
#include <string.h>
BEGIN_ZFC_NAMESPACE

/*!
 * \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) {}

/*!
 * \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)
{
	if (bytesLeft() < bytes)
	{
		int bytesPast = bytes - bytesLeft();
		String message;
		message.sprintf("attempted to read %d byte%s past the end of bytestream", bytesPast, plural(bytesPast));
		throw IOError (message);
	}
}

/*!
 * \returns the amount of bytes remaining for reading.
 */
int Bytestream::bytesLeft() const
{
	return m_data.size() - m_position;
}

/*!
 * \returns an iterator to the current data position.
 */
ByteArray::Iterator Bytestream::getCurrentIterator()
{
	return m_data.begin() + m_position;
}

/*!
 * \brief Reads in a byte.
 * \returns the read byte.
 */
int8_t Bytestream::readByte()
{
	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 |= read() << (i * 8);

	return result;
}

/*!
 * \brief Reads in four bytes to form an integer value.
 * \returns the read value.
 */
int32_t Bytestream::readLong()
{
	ensureReadSpace (4);
	int32_t result = 0;

	for (int i : range(4))
		result |= read() << (i * 8);

	return result;
}

/*!
 * \brief Reads in four bytes to form a floating point number.
 * \returns the read value.
 */
float Bytestream::readFloat()
{
	float value;
	int intvalue = readLong();
	memcpy(&value, &intvalue, sizeof intvalue);
	return value;
}

/*!
 * \brief Reads in characters until a null terminator is encountered.
 * \returns the read string.
 */
String Bytestream::readString()
{
	ByteArray::Iterator stringEndIterator;

	// Where's the end of the string?
	for (stringEndIterator = getCurrentIterator(); *stringEndIterator != '\0'; ++stringEndIterator)
	{
		if (stringEndIterator == m_data.end())
		{
			// Past the end of the buffer
			throw IOError("unterminated or too long string in packet");
		}
	}

	// Skip past the null terminator.
	if (*stringEndIterator == '\0')
		stringEndIterator += 1;

	// 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));
}

/*!
 * \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)
{
	ensureReadSpace(length);
	ByteArray result(length);
	memcpy(result.data(), m_data.data() + m_position, length);
	m_position += length;
	return result;
}

/*!
 * \brief Writes an integer to the end of the data as one byte.
 * \param value Value to write
 */
void Bytestream::writeByte(int8_t value)
{
	m_data.append(value);
}

/*!
 * \brief Writes an integer to the end of the data as 2 bytes.
 * \param value Value to write
 */
void Bytestream::writeShort(int16_t value)
{
	for (int i : range(2))
		m_data.append((value >> (i * 8)) & 0xFF);
}

/*!
 * \brief Writes an integer to the end of the data as 4 bytes.
 * \param value Value to write
 */
void Bytestream::writeLong(int32_t value)
{
	for (int i : range(4))
		m_data.append((value >> (i * 8)) & 0xFF);
}

/*!
 * \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, &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;
}

/*!
 * \brief Seeks the stream cursor to the beginning of the data.
 */
void Bytestream::rewind()
{
	m_position = 0;
}

/*!
 * \brief Seeks the stream cursor to a custom location.
 * \param position Position to seek too.
 */
void Bytestream::seek(int position)
{
	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

mercurial