File diff r26193:4bc7915a2156 → r26194:f7347205838e
src/saveload/saveload_buffer.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file saveload_buffer.h Functions/types related to buffers used for saving and loading games. */
 

	
 
#ifndef SAVELOAD_BUFFER_H
 
#define SAVELOAD_BUFFER_H
 

	
 
#include "../core/alloc_func.hpp"
 
#include "../core/endian_type.hpp"
 
#include "../core/endian_func.hpp"
 
#include "../core/math_func.hpp"
 

	
 
#include <vector>
 
#include <utility>
 

	
 
struct LoadFilter;
 
struct SaveFilter;
 

	
 
/** Save in chunks of 128 KiB. */
 
static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
 

	
 
/** A buffer for reading (and buffering) savegame data. */
 
struct ReadBuffer {
 
	byte buf[MEMORY_CHUNK_SIZE]; ///< Buffer we're going to read from.
 
	byte *bufp;                  ///< Location we're at reading the buffer.
 
	byte *bufe;                  ///< End of the buffer we can read from.
 
	LoadFilter *reader;          ///< The filter used to actually read.
 
	size_t read;                 ///< The amount of read bytes so far from the filter.
 

	
 
	/**
 
	 * Initialise our variables.
 
	 * @param reader The filter to actually read data.
 
	 */
 
	ReadBuffer(LoadFilter *reader) : bufp(nullptr), bufe(nullptr), reader(reader), read(0)
 
	{
 
	}
 

	
 
	static ReadBuffer *GetCurrent();
 

	
 
	void SkipBytesSlowPath(size_t bytes);
 
	void AcquireBytes();
 

	
 
	inline void SkipBytes(size_t bytes)
 
	{
 
		byte *b = this->bufp + bytes;
 
		if (likely(b <= this->bufe)) {
 
			this->bufp = b;
 
		} else {
 
			SkipBytesSlowPath(bytes);
 
		}
 
	}
 

	
 
	inline byte RawReadByte()
 
	{
 
		return *this->bufp++;
 
	}
 

	
 
	inline byte ReadByte()
 
	{
 
		if (unlikely(this->bufp == this->bufe)) {
 
			this->AcquireBytes();
 
		}
 

	
 
		return RawReadByte();
 
	}
 

	
 
	inline void CheckBytes(size_t bytes)
 
	{
 
		while (unlikely(this->bufp + bytes > this->bufe)) this->AcquireBytes();
 
	}
 

	
 
	inline int RawReadUint16()
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		int x = FROM_BE16(*((const unaligned_uint16*) this->bufp));
 
		this->bufp += 2;
 
		return x;
 
#else
 
		int x = this->RawReadByte() << 8;
 
		return x | this->RawReadByte();
 
#endif
 
	}
 

	
 
	inline uint32 RawReadUint32()
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		uint32 x = FROM_BE32(*((const unaligned_uint32*) this->bufp));
 
		this->bufp += 4;
 
		return x;
 
#else
 
		uint32 x = this->RawReadUint16() << 16;
 
		return x | this->RawReadUint16();
 
#endif
 
	}
 

	
 
	inline uint64 RawReadUint64()
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		uint64 x = FROM_BE64(*((const unaligned_uint64*) this->bufp));
 
		this->bufp += 8;
 
		return x;
 
#else
 
		uint32 x = this->RawReadUint32();
 
		uint32 y = this->RawReadUint32();
 
		return (uint64)x << 32 | y;
 
#endif
 
	}
 

	
 
	inline void CopyBytes(byte *ptr, size_t length)
 
	{
 
		while (length) {
 
			if (unlikely(this->bufp == this->bufe)) {
 
				this->AcquireBytes();
 
			}
 
			size_t to_copy = std::min<size_t>(this->bufe - this->bufp, length);
 
			memcpy(ptr, this->bufp, to_copy);
 
			this->bufp += to_copy;
 
			ptr += to_copy;
 
			length -= to_copy;
 
		}
 
	}
 

	
 
	/**
 
	 * Get the size of the memory dump made so far.
 
	 * @return The size.
 
	 */
 
	inline size_t GetSize() const
 
	{
 
		return this->read - (this->bufe - this->bufp);
 
	}
 
};
 

	
 

	
 
/** Container for dumping the savegame (quickly) to memory. */
 
struct MemoryDumper {
 
	struct BufferInfo {
 
		byte *data;
 
		size_t size = 0;
 

	
 
		BufferInfo(byte *d) : data(d) {}
 
		~BufferInfo() { free(this->data); }
 

	
 
		BufferInfo(const BufferInfo &) = delete;
 
		BufferInfo(BufferInfo &&other) : data(other.data), size(other.size) { other.data = nullptr; };
 
	};
 

	
 
	std::vector<BufferInfo> blocks;         ///< Buffer with blocks of allocated memory.
 
	byte *buf = nullptr;                    ///< Buffer we're going to write to.
 
	byte *bufe = nullptr;                   ///< End of the buffer we write to.
 
	size_t completed_block_bytes = 0;       ///< Total byte count of completed blocks.
 

	
 
	byte *autolen_buf = nullptr;
 
	byte *autolen_buf_end = nullptr;
 
	byte *saved_buf = nullptr;
 
	byte *saved_bufe = nullptr;
 

	
 
	MemoryDumper()
 
	{
 
		const size_t size = 8192;
 
		this->autolen_buf = CallocT<byte>(size);
 
		this->autolen_buf_end = this->autolen_buf + size;
 
	}
 

	
 
	~MemoryDumper()
 
	{
 
		free(this->autolen_buf);
 
	}
 

	
 
	static MemoryDumper *GetCurrent();
 

	
 
	void FinaliseBlock();
 
	void AllocateBuffer();
 

	
 
	inline void CheckBytes(size_t bytes)
 
	{
 
		if (unlikely(this->buf + bytes > this->bufe)) this->AllocateBuffer();
 
	}
 

	
 
	/**
 
	 * Write a single byte into the dumper.
 
	 * @param b The byte to write.
 
	 */
 
	inline void WriteByte(byte b)
 
	{
 
		/* Are we at the end of this chunk? */
 
		if (unlikely(this->buf == this->bufe)) {
 
			this->AllocateBuffer();
 
		}
 

	
 
		*this->buf++ = b;
 
	}
 

	
 
	inline void CopyBytes(byte *ptr, size_t length)
 
	{
 
		while (length) {
 
			if (unlikely(this->buf == this->bufe)) {
 
				this->AllocateBuffer();
 
			}
 
			size_t to_copy = std::min<size_t>(this->bufe - this->buf, length);
 
			memcpy(this->buf, ptr, to_copy);
 
			this->buf += to_copy;
 
			ptr += to_copy;
 
			length -= to_copy;
 
		}
 
	}
 

	
 
	inline void RawWriteByte(byte b)
 
	{
 
		*this->buf++ = b;
 
	}
 

	
 
	inline void RawWriteUint16(uint16 v)
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		*((unaligned_uint16 *) this->buf) = TO_BE16(v);
 
#else
 
		this->buf[0] = GB(v, 8, 8);
 
		this->buf[1] = GB(v, 0, 8);
 
#endif
 
		this->buf += 2;
 
	}
 

	
 
	inline void RawWriteUint32(uint32 v)
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		*((unaligned_uint32 *) this->buf) = TO_BE32(v);
 
#else
 
		this->buf[0] = GB(v, 24, 8);
 
		this->buf[1] = GB(v, 16, 8);
 
		this->buf[2] = GB(v, 8, 8);
 
		this->buf[3] = GB(v, 0, 8);
 
#endif
 
		this->buf += 4;
 
	}
 

	
 
	inline void RawWriteUint64(uint64 v)
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		*((unaligned_uint64 *) this->buf) = TO_BE64(v);
 
#else
 
		this->buf[0] = GB(v, 56, 8);
 
		this->buf[1] = GB(v, 48, 8);
 
		this->buf[2] = GB(v, 40, 8);
 
		this->buf[3] = GB(v, 32, 8);
 
		this->buf[4] = GB(v, 24, 8);
 
		this->buf[5] = GB(v, 16, 8);
 
		this->buf[6] = GB(v, 8, 8);
 
		this->buf[7] = GB(v, 0, 8);
 
#endif
 
		this->buf += 8;
 
	}
 

	
 
	void Flush(SaveFilter *writer);
 
	size_t GetSize() const;
 
	void StartAutoLength();
 
	std::pair<byte *, size_t> StopAutoLength();
 
};
 

	
 
#endif