diff --git a/src/saveload/saveload_buffer.h b/src/saveload/saveload_buffer.h new file mode 100644 --- /dev/null +++ b/src/saveload/saveload_buffer.h @@ -0,0 +1,266 @@ +/* $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 . + */ + +/** @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 +#include + +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(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 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(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(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 StopAutoLength(); +}; + +#endif