Wed, 27 Jan 2021 19:48:41 +0200
merged with default
/* Copyright 2014 - 2021 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(std::vector<unsigned char>& 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(); std::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. */ std::vector<unsigned char>::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 : {0, 1}) 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 = 0; i < 4; i += 1) 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. */ std::string Bytestream::readString() { std::string result; for (char byte; (byte = readByte()) != '\0';) { if (result.length() < MAX_NETWORK_STRING) result += byte; } return result; } /*! * \brief Reads in a buffer of the specified length. * \param length Amount of bytes to read. * \returns the read buffer. */ std::vector<unsigned char> Bytestream::readBuffer(int length) { ensureReadSpace(length); std::vector<unsigned char> 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.push_back(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) { m_data.reserve(m_data.size() + 2); for (int i : {0, 1}) m_data.push_back((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) { m_data.reserve(m_data.size() + 4); for (int i = 0; i < 4; i += 1) m_data.push_back((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 std::string& string) { const int oldSize = m_data.size(); m_data.reserve(m_data.size() + string.length() + 1); m_data.resize(m_data.size() + string.length()); std::copy(string.begin(), string.end(), m_data.begin() + oldSize); m_data.push_back(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