From 9ca4e915baea6956c1cdb9e3a5ca420561286df5 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 28 May 2018 02:20:30 +0100 Subject: [PATCH 1/6] Add 64 bit byte swapping function --- src/core/bitmath_func.hpp | 19 +++++++++++++++++++ src/core/endian_func.hpp | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/src/core/bitmath_func.hpp b/src/core/bitmath_func.hpp index 31e679b005..fdcb310b06 100644 --- a/src/core/bitmath_func.hpp +++ b/src/core/bitmath_func.hpp @@ -365,13 +365,32 @@ static inline T ROR(const T x, const uint8 n) * (since it will use hardware swapping if available). * Even though they should return uint16 and uint32, we get * warnings if we don't cast those (why?) */ + #define BSWAP64(x) ((uint32)CFSwapInt64(x)) #define BSWAP32(x) ((uint32)CFSwapInt32(x)) #define BSWAP16(x) ((uint16)CFSwapInt16(x)) #elif defined(_MSC_VER) /* MSVC has intrinsics for swapping, resulting in faster code */ + #define BSWAP64(x) (_byteswap_uint64(x)) #define BSWAP32(x) (_byteswap_ulong(x)) #define BSWAP16(x) (_byteswap_ushort(x)) #else + /** + * Perform a 64 bits endianness bitswap on x. + * @param x the variable to bitswap + * @return the bitswapped value. + */ + static inline uint64 BSWAP64(uint64 x) + { +#if !defined(__ICC) && (defined(__GNUC__) || defined(__clang__)) + /* GCC >= 4.3 provides a builtin, resulting in faster code */ + return (uint64)__builtin_bswap64((uint64)x); +#else + return ((x >> 56) & 0xFFULL) | ((x >> 40) & 0xFF00ULL) | ((x >> 24) & 0xFF0000ULL) | ((x >> 8) & 0xFF000000ULL) | + ((x << 8) & 0xFF00000000ULL) | ((x << 24) & 0xFF0000000000ULL) | ((x << 40) & 0xFF000000000000ULL) | ((x << 56) & 0xFF000000000000ULL); + ; +#endif /* __GNUC__ || __clang__ */ + } + /** * Perform a 32 bits endianness bitswap on x. * @param x the variable to bitswap diff --git a/src/core/endian_func.hpp b/src/core/endian_func.hpp index ab5b181500..48060bd1ad 100644 --- a/src/core/endian_func.hpp +++ b/src/core/endian_func.hpp @@ -19,25 +19,33 @@ #if TTD_ENDIAN == TTD_BIG_ENDIAN #define FROM_BE16(x) (x) #define FROM_BE32(x) (x) + #define FROM_BE64(x) (x) #define TO_BE16(x) (x) #define TO_BE32(x) (x) #define TO_BE32X(x) (x) + #define TO_BE64(x) (x) #define FROM_LE16(x) BSWAP16(x) #define FROM_LE32(x) BSWAP32(x) + #define FROM_LE64(x) BSWAP64(x) #define TO_LE16(x) BSWAP16(x) #define TO_LE32(x) BSWAP32(x) #define TO_LE32X(x) BSWAP32(x) + #define TO_LE64(x) BSWAP64(x) #else #define FROM_BE16(x) BSWAP16(x) #define FROM_BE32(x) BSWAP32(x) + #define FROM_BE64(x) BSWAP64(x) #define TO_BE16(x) BSWAP16(x) #define TO_BE32(x) BSWAP32(x) #define TO_BE32X(x) BSWAP32(x) + #define TO_BE64(x) BSWAP64(x) #define FROM_LE16(x) (x) #define FROM_LE32(x) (x) + #define FROM_LE64(x) (x) #define TO_LE16(x) (x) #define TO_LE32(x) (x) #define TO_LE32X(x) (x) + #define TO_LE64(x) (x) #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */ static inline uint16 ReadLE16Aligned(const void *x) From 3b83a9e18693cdbfafb3fb964823daf08624f16f Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 28 May 2018 02:22:26 +0100 Subject: [PATCH 2/6] Performance improvements to savegame loading --- src/saveload/saveload.cpp | 139 ++++++++++++++++++++++++++++++++++++-- src/saveload/saveload.h | 31 ++------- 2 files changed, 136 insertions(+), 34 deletions(-) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 16e94261f6..cfe81e937b 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -320,18 +320,115 @@ struct ReadBuffer { { } - inline byte ReadByte() + void SkipBytesSlowPath(size_t bytes) { - if (this->bufp == this->bufe) { + bytes -= (this->bufe - this->bufp); + while (true) { size_t len = this->reader->Read(this->buf, lengthof(this->buf)); if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); - this->read += len; - this->bufp = this->buf; - this->bufe = this->buf + len; + if (len >= bytes) { + this->bufp = this->buf + bytes; + this->bufe = this->buf + len; + return; + } else { + bytes -= len; + } + } + } + + inline void SkipBytes(size_t bytes) + { + byte *b = this->bufp + bytes; + if (likely(b <= this->bufe)) { + this->bufp = b; + } else { + SkipBytesSlowPath(bytes); + } + } + + void AcquireBytes() + { + size_t remainder = this->bufe - this->bufp; + if (remainder) { + memmove(this->buf, this->bufp, remainder); + } + size_t len = this->reader->Read(this->buf + remainder, lengthof(this->buf) - remainder); + if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); + + this->read += len; + this->bufp = this->buf; + this->bufe = this->buf + remainder + len; + } + + inline byte RawReadByte() + { + return *this->bufp++; + } + + inline byte ReadByte() + { + if (unlikely(this->bufp == this->bufe)) { + this->AcquireBytes(); } - return *this->bufp++; + 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 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 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 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 = min(this->bufe - this->bufp, length); + memcpy(ptr, this->bufp, to_copy); + this->bufp += to_copy; + ptr += to_copy; + length -= to_copy; + } } /** @@ -648,6 +745,34 @@ byte SlReadByte() return _sl.reader->ReadByte(); } +/** + * Read in bytes from the file/data structure but don't do + * anything with them, discarding them in effect + * @param length The amount of bytes that is being treated this way + */ +void SlSkipBytes(size_t length) +{ + return _sl.reader->SkipBytes(length); +} + +int SlReadUint16() +{ + _sl.reader->CheckBytes(2); + return _sl.reader->RawReadUint16(); +} + +uint32 SlReadUint32() +{ + _sl.reader->CheckBytes(4); + return _sl.reader->RawReadUint32(); +} + +uint64 SlReadUint64() +{ + _sl.reader->CheckBytes(8); + return _sl.reader->RawReadUint64(); +} + /** * Wrapper for writing a byte to the dumper. * @param b The byte to write. @@ -946,7 +1071,7 @@ static void SlCopyBytes(void *ptr, size_t length) switch (_sl.action) { case SLA_LOAD_CHECK: case SLA_LOAD: - for (; length != 0; length--) *p++ = SlReadByte(); + _sl.reader->CopyBytes(p, length); break; case SLA_SAVE: for (; length != 0; length--) SlWriteByte(*p++); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 45968bf7ff..e4a26dcd71 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -679,24 +679,9 @@ size_t SlCalcObjLength(const void *object, const SaveLoad *sld); byte SlReadByte(); void SlWriteByte(byte b); -static inline int SlReadUint16() -{ - int x = SlReadByte() << 8; - return x | SlReadByte(); -} - -static inline uint32 SlReadUint32() -{ - uint32 x = SlReadUint16() << 16; - return x | SlReadUint16(); -} - -static inline uint64 SlReadUint64() -{ - uint32 x = SlReadUint32(); - uint32 y = SlReadUint32(); - return (uint64)x << 32 | y; -} +int SlReadUint16(); +uint32 SlReadUint32(); +uint64 SlReadUint64(); static inline void SlWriteUint16(uint16 v) { @@ -716,15 +701,7 @@ static inline void SlWriteUint64(uint64 x) SlWriteUint32((uint32)x); } -/** - * Read in bytes from the file/data structure but don't do - * anything with them, discarding them in effect - * @param length The amount of bytes that is being treated this way - */ -static inline void SlSkipBytes(size_t length) -{ - for (; length != 0; length--) SlReadByte(); -} +void SlSkipBytes(size_t length); size_t SlGetBytesRead(); size_t SlGetBytesWritten(); From c71ed22e7a03a012fbd1e4ec3c9181185ee3f292 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 28 May 2018 13:51:18 +0100 Subject: [PATCH 3/6] Save/memory dumper performance improvements --- src/saveload/saveload.cpp | 133 +++++++++++++++++++++++++++++++++----- src/saveload/saveload.h | 20 +----- 2 files changed, 121 insertions(+), 32 deletions(-) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index cfe81e937b..89b343d579 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -444,15 +444,50 @@ struct ReadBuffer { /** Container for dumping the savegame (quickly) to memory. */ struct MemoryDumper { - AutoFreeSmallVector blocks; ///< Buffer with blocks of allocated memory. + 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; ///< Buffer we're going to write to. byte *bufe; ///< End of the buffer we write to. + size_t completed_block_bytes; ///< Total byte count of completed blocks /** Initialise our variables. */ - MemoryDumper() : buf(NULL), bufe(NULL) + MemoryDumper() : buf(NULL), bufe(NULL), completed_block_bytes(0) { } + void FinaliseBlock() + { + if (!this->blocks.empty()) { + size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf); + this->blocks.back().size = s; + this->completed_block_bytes += s; + } + this->buf = this->bufe = nullptr; + } + + void AllocateBuffer() + { + this->FinaliseBlock(); + this->buf = CallocT(MEMORY_CHUNK_SIZE); + this->blocks.emplace_back(this->buf); + this->bufe = this->buf + MEMORY_CHUNK_SIZE; + } + + 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. @@ -460,29 +495,79 @@ struct MemoryDumper { inline void WriteByte(byte b) { /* Are we at the end of this chunk? */ - if (this->buf == this->bufe) { - this->buf = CallocT(MEMORY_CHUNK_SIZE); - *this->blocks.Append() = this->buf; - this->bufe = this->buf + MEMORY_CHUNK_SIZE; + 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 = min(this->bufe - this->buf, length); + memcpy(this->buf, ptr, to_copy); + this->buf += to_copy; + ptr += to_copy; + length -= to_copy; + } + } + + inline void RawWriteUint16(uint16 v) + { +#if OTTD_ALIGNMENT == 0 + *((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 + *((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 + *((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; + } + /** * Flush this dumper into a writer. * @param writer The filter we want to use. */ void Flush(SaveFilter *writer) { - uint i = 0; - size_t t = this->GetSize(); + this->FinaliseBlock(); - while (t > 0) { - size_t to_write = min(MEMORY_CHUNK_SIZE, t); - - writer->Write(this->blocks[i++], to_write); - t -= to_write; + uint block_count = this->blocks.size(); + for (uint i = 0; i < block_count; i++) { + writer->Write(this->blocks[i].data, this->blocks[i].size); } writer->Finish(); @@ -494,7 +579,7 @@ struct MemoryDumper { */ size_t GetSize() const { - return this->blocks.Length() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf); + return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0); } }; @@ -782,6 +867,24 @@ void SlWriteByte(byte b) _sl.dumper->WriteByte(b); } +void SlWriteUint16(uint16 v) +{ + _sl.dumper->CheckBytes(2); + _sl.dumper->RawWriteUint16(v); +} + +void SlWriteUint32(uint32 v) +{ + _sl.dumper->CheckBytes(4); + _sl.dumper->RawWriteUint32(v); +} + +void SlWriteUint64(uint64 v) +{ + _sl.dumper->CheckBytes(8); + _sl.dumper->RawWriteUint64(v); +} + /** * Returns number of bytes read so far * May only be called during a load/load check action @@ -1074,7 +1177,7 @@ static void SlCopyBytes(void *ptr, size_t length) _sl.reader->CopyBytes(p, length); break; case SLA_SAVE: - for (; length != 0; length--) SlWriteByte(*p++); + _sl.dumper->CopyBytes(p, length); break; default: NOT_REACHED(); } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index e4a26dcd71..5bc710d7a8 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -683,23 +683,9 @@ int SlReadUint16(); uint32 SlReadUint32(); uint64 SlReadUint64(); -static inline void SlWriteUint16(uint16 v) -{ - SlWriteByte(GB(v, 8, 8)); - SlWriteByte(GB(v, 0, 8)); -} - -static inline void SlWriteUint32(uint32 v) -{ - SlWriteUint16(GB(v, 16, 16)); - SlWriteUint16(GB(v, 0, 16)); -} - -static inline void SlWriteUint64(uint64 x) -{ - SlWriteUint32((uint32)(x >> 32)); - SlWriteUint32((uint32)x); -} +void SlWriteUint16(uint16 v); +void SlWriteUint32(uint32 v); +void SlWriteUint64(uint64 v); void SlSkipBytes(size_t length); From 7c4bd7d3a3c6fdb73a60ce5aed0891742aed63bb Mon Sep 17 00:00:00 2001 From: innocenat Date: Mon, 29 May 2017 19:08:15 +0700 Subject: [PATCH 4/6] Add variable std::vector save type SL_VEC in the game currenty only support SlRefType, not VarType. This add another save type, SL_VARVEC, to support saving std::vector with POD type. It supports all integer type. (cherry picked from commit 2895b1921d00fd8af0cc3f85f5b8e2f53989092f) Fix bug in new SL_VARVEC save/load code (cherry picked from commit 59554a5dd268d97ad357e5e24c17b4d26c3cd5f9) --- src/saveload/saveload.cpp | 84 +++++++++++++++++++++++++++++++++++++++ src/saveload/saveload.h | 14 +++++++ 2 files changed, 98 insertions(+) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 89b343d579..0f012943d1 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -1657,6 +1657,18 @@ static inline size_t SlCalcListLen(const void *list) return l->size() * type_size + type_size; } +/** + * Return the size in bytes of a list + * @param list The std::list to find the size of + */ + template +static inline size_t SlCalcVarListLen(const void *list, size_t item_size) +{ + const PtrList *l = (const PtrList *) list; + /* Each entry is saved as item_size bytes, plus 4 bytes are used for the length + * of the list */ + return l->size() * item_size + 4; +} /** * Save/Load a list. @@ -1715,6 +1727,55 @@ static void SlList(void *list, SLRefType conv) } } +/** + * Save/Load a list. + * @param list The list being manipulated + * @param conv VarType type of the list + */ +template +static void SlVarList(void *list, VarType conv) +{ + const size_t size_len = SlCalcConvMemLen(conv); + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlCalcVarListLen(list, size_len)); + /* Determine length only? */ + if (_sl.need_length == NL_CALCLENGTH) return; + } + + PtrList *l = (PtrList *)list; + + switch (_sl.action) { + case SLA_SAVE: { + SlWriteUint32((uint32)l->size()); + + typename PtrList::iterator iter; + for (iter = l->begin(); iter != l->end(); ++iter) { + SlSaveLoadConv(&(*iter), conv); + } + break; + } + case SLA_LOAD_CHECK: + case SLA_LOAD: { + size_t length = SlReadUint32(); + l->resize(length); + + typename PtrList::iterator iter; + iter = l->begin(); + + for (size_t i = 0; i < length; i++) { + SlSaveLoadConv(&(*iter), conv); + ++iter; + } + break; + } + case SLA_PTRS: break; + case SLA_NULL: + l->clear(); + break; + default: NOT_REACHED(); + } +} /** Are we going to save this object or not? */ static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld) @@ -1770,6 +1831,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_DEQ: case SL_VEC: case SL_STDSTR: + case SL_VARVEC: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) break; @@ -1781,6 +1843,16 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_LST: return SlCalcListLen>(GetVariableAddress(object, sld)); case SL_DEQ: return SlCalcListLen>(GetVariableAddress(object, sld)); case SL_VEC: return SlCalcListLen>(GetVariableAddress(object, sld)); + case SL_VARVEC: { + const size_t size_len = SlCalcConvMemLen(sld->conv); + switch (size_len) { + case 1: return SlCalcVarListLen>(GetVariableAddress(object, sld), 1); + case 2: return SlCalcVarListLen>(GetVariableAddress(object, sld), 2); + case 4: return SlCalcVarListLen>(GetVariableAddress(object, sld), 4); + case 8: return SlCalcVarListLen>(GetVariableAddress(object, sld), 8); + default: NOT_REACHED(); + } + } case SL_STDSTR: return SlCalcStdStrLen(*static_cast(GetVariableAddress(object, sld))); default: NOT_REACHED(); } @@ -1856,6 +1928,7 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_DEQ: case SL_VEC: case SL_STDSTR: + case SL_VARVEC: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) return false; if (SlSkipVariableOnLoad(sld)) return false; @@ -1885,6 +1958,17 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_LST: SlList>(ptr, (SLRefType)conv); break; case SL_DEQ: SlList>(ptr, (SLRefType)conv); break; case SL_VEC: SlList>(ptr, (SLRefType)conv); break; + case SL_VARVEC: { + const size_t size_len = SlCalcConvMemLen(sld->conv); + switch (size_len) { + case 1: SlVarList>(ptr, conv); break; + case 2: SlVarList>(ptr, conv); break; + case 4: SlVarList>(ptr, conv); break; + case 8: SlVarList>(ptr, conv); break; + default: NOT_REACHED(); + } + break; + } case SL_STDSTR: SlStdString(*static_cast(ptr), sld->conv); break; default: NOT_REACHED(); } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 5bc710d7a8..39d11e30d5 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -212,6 +212,8 @@ enum SaveLoadTypes { SL_WRITEBYTE = 8, SL_VEH_INCLUDE = 9, SL_ST_INCLUDE = 10, + /* primitive type vector */ + SL_VARVEC = 14, SL_END = 15 }; @@ -349,6 +351,18 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLE_CONDVEC_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VEC, base, variable, type, 0, from, to, extver) #define SLE_CONDVEC(base, variable, type, from, to) SLE_CONDVEC_X(base, variable, type, from, to, SlXvFeatureTest()) +/** + * Storage of a variable vector in some savegame versions. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDVARVEC_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VARVEC, base, variable, type, 0, from, to, extver) +#define SLE_CONDVARVEC(base, variable, type, from, to) SLE_CONDVARVEC_X(base, variable, type, from, to, SlXvFeatureTest()) + /** * Storage of a variable in every version of a savegame. * @param base Name of the class or struct containing the variable. From bbec436a74239b9ec7b0e8ab986e2246e248a873 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 28 May 2018 19:50:20 +0100 Subject: [PATCH 5/6] Change SlAutolength to only call proc once --- src/saveload/saveload.cpp | 86 +++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 0f012943d1..6b5a726347 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -298,7 +298,6 @@ enum SaveLoadAction { enum NeedLength { NL_NONE = 0, ///< not working in NeedLength mode NL_WANTLENGTH = 1, ///< writing length and data - NL_CALCLENGTH = 2, ///< need to calculate the length }; /** Save in chunks of 128 KiB. */ @@ -456,17 +455,30 @@ struct MemoryDumper { }; std::vector blocks; ///< Buffer with blocks of allocated memory. - byte *buf; ///< Buffer we're going to write to. - byte *bufe; ///< End of the buffer we write to. - size_t completed_block_bytes; ///< Total byte count of completed blocks + 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. - /** Initialise our variables. */ - MemoryDumper() : buf(NULL), bufe(NULL), completed_block_bytes(0) + 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); } void FinaliseBlock() { + assert(this->saved_buf == nullptr); if (!this->blocks.empty()) { size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf); this->blocks.back().size = s; @@ -477,6 +489,15 @@ struct MemoryDumper { void AllocateBuffer() { + if (this->saved_buf) { + const size_t offset = this->buf - this->autolen_buf; + const size_t size = (this->autolen_buf_end - this->autolen_buf) * 2; + this->autolen_buf = ReallocT(this->autolen_buf, size); + this->autolen_buf_end = this->autolen_buf + size; + this->buf = this->autolen_buf + offset; + this->bufe = this->autolen_buf_end; + return; + } this->FinaliseBlock(); this->buf = CallocT(MEMORY_CHUNK_SIZE); this->blocks.emplace_back(this->buf); @@ -579,8 +600,30 @@ struct MemoryDumper { */ size_t GetSize() const { + assert(this->saved_buf == nullptr); return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0); } + + void StartAutoLength() + { + assert(this->saved_buf == nullptr); + + this->saved_buf = this->buf; + this->saved_bufe = this->bufe; + this->buf = this->autolen_buf; + this->bufe = this->autolen_buf_end; + } + + std::pair StopAutoLength() + { + assert(this->saved_buf != nullptr); + auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf); + + this->buf = this->saved_buf; + this->bufe = this->saved_bufe; + this->saved_buf = this->saved_bufe = nullptr; + return res; + } }; /** The saveload struct, containing reader-writer functions, buffer, version, etc. */ @@ -1153,10 +1196,6 @@ void SlSetLength(size_t length) } break; - case NL_CALCLENGTH: - _sl.obj_len += (int)length; - break; - default: NOT_REACHED(); } } @@ -1490,8 +1529,6 @@ void SlArray(void *array, size_t length, VarType conv) /* Automatically calculate the length? */ if (_sl.need_length != NL_NONE) { SlSetLength(SlCalcArrayLen(length, conv)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; } /* NOTICE - handle some buggy stuff, in really old versions everything was saved @@ -1681,8 +1718,6 @@ static void SlList(void *list, SLRefType conv) /* Automatically calculate the length? */ if (_sl.need_length != NL_NONE) { SlSetLength(SlCalcListLen(list)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; } PtrList *l = (PtrList *)list; @@ -1739,8 +1774,6 @@ static void SlVarList(void *list, VarType conv) /* Automatically calculate the length? */ if (_sl.need_length != NL_NONE) { SlSetLength(SlCalcVarListLen(list, size_len)); - /* Determine length only? */ - if (_sl.need_length == NL_CALCLENGTH) return; } PtrList *l = (PtrList *)list; @@ -2014,7 +2047,6 @@ void SlObject(void *object, const SaveLoad *sld) /* Automatically calculate the length? */ if (_sl.need_length != NL_NONE) { SlSetLength(SlCalcObjLength(object, sld)); - if (_sl.need_length == NL_CALCLENGTH) return; } for (; sld->cmd != SL_END; sld++) { @@ -2039,25 +2071,17 @@ void SlGlobList(const SaveLoadGlobVarList *sldg) */ void SlAutolength(AutolengthProc *proc, void *arg) { - size_t offs; - assert(_sl.action == SLA_SAVE); + assert(_sl.need_length == NL_WANTLENGTH); - /* Tell it to calculate the length */ - _sl.need_length = NL_CALCLENGTH; - _sl.obj_len = 0; + _sl.need_length = NL_NONE; + _sl.dumper->StartAutoLength(); proc(arg); - + auto result = _sl.dumper->StopAutoLength(); /* Setup length */ _sl.need_length = NL_WANTLENGTH; - SlSetLength(_sl.obj_len); - - offs = _sl.dumper->GetSize() + _sl.obj_len; - - /* And write the stuff */ - proc(arg); - - if (offs != _sl.dumper->GetSize()) SlErrorCorrupt("Invalid chunk size"); + SlSetLength(result.second); + _sl.dumper->CopyBytes(result.first, result.second); } /* From aae7806d5dfabf1baf07713af1c4dd5c634f6347 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 28 May 2018 21:09:34 +0100 Subject: [PATCH 6/6] Move save/load ReadBuffer and MemoryDumper to separate header Add static accessors --- projects/openttd_vs100.vcxproj | 1 + projects/openttd_vs100.vcxproj.filters | 3 + projects/openttd_vs140.vcxproj | 1 + projects/openttd_vs140.vcxproj.filters | 3 + projects/openttd_vs141.vcxproj | 1 + projects/openttd_vs141.vcxproj.filters | 3 + projects/openttd_vs80.vcproj | 4 + projects/openttd_vs90.vcproj | 4 + source.list | 1 + src/saveload/saveload.cpp | 413 ++++++------------------- src/saveload/saveload_buffer.h | 266 ++++++++++++++++ 11 files changed, 389 insertions(+), 311 deletions(-) create mode 100644 src/saveload/saveload_buffer.h diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index db0809a0bf..818e1d7bdb 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -890,6 +890,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index c5c9b1df8f..7347cd74e5 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -1827,6 +1827,9 @@ Save/Load handlers + + Save/Load handlers + Save/Load handlers diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj index 64f420b379..cd0cc55af8 100644 --- a/projects/openttd_vs140.vcxproj +++ b/projects/openttd_vs140.vcxproj @@ -911,6 +911,7 @@ + diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters index c5c9b1df8f..7347cd74e5 100644 --- a/projects/openttd_vs140.vcxproj.filters +++ b/projects/openttd_vs140.vcxproj.filters @@ -1827,6 +1827,9 @@ Save/Load handlers + + Save/Load handlers + Save/Load handlers diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj index cfedf98265..a6d422be18 100644 --- a/projects/openttd_vs141.vcxproj +++ b/projects/openttd_vs141.vcxproj @@ -908,6 +908,7 @@ + diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters index 3349f99548..ccced4f931 100644 --- a/projects/openttd_vs141.vcxproj.filters +++ b/projects/openttd_vs141.vcxproj.filters @@ -1818,6 +1818,9 @@ Save/Load handlers + + Save/Load handlers + Save/Load handlers diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index b06f80ed5b..e81ce76562 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -2766,6 +2766,10 @@ RelativePath=".\..\src\saveload\saveload_internal.h" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 0319afc65c..51416e5e3c 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -2763,6 +2763,10 @@ RelativePath=".\..\src\saveload\saveload_internal.h" > + + diff --git a/source.list b/source.list index 3804495d9c..da88dd8fdc 100644 --- a/source.list +++ b/source.list @@ -628,6 +628,7 @@ saveload/saveload.cpp saveload/saveload.h saveload/saveload_filter.h saveload/saveload_internal.h +saveload/saveload_buffer.h saveload/signs_sl.cpp saveload/station_sl.cpp saveload/storage_sl.cpp diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 6b5a726347..efefa95720 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -49,6 +49,7 @@ #include "saveload_internal.h" #include "saveload_filter.h" +#include "saveload_buffer.h" #include "extended_ver_sl.h" #include "../safeguards.h" @@ -300,331 +301,111 @@ enum NeedLength { NL_WANTLENGTH = 1, ///< writing length and data }; -/** 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(NULL), bufe(NULL), reader(reader), read(0) - { - } - - void SkipBytesSlowPath(size_t bytes) - { - bytes -= (this->bufe - this->bufp); - while (true) { - size_t len = this->reader->Read(this->buf, lengthof(this->buf)); - if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); - this->read += len; - if (len >= bytes) { - this->bufp = this->buf + bytes; - this->bufe = this->buf + len; - return; - } else { - bytes -= len; - } - } - } - - inline void SkipBytes(size_t bytes) - { - byte *b = this->bufp + bytes; - if (likely(b <= this->bufe)) { - this->bufp = b; - } else { - SkipBytesSlowPath(bytes); - } - } - - void AcquireBytes() - { - size_t remainder = this->bufe - this->bufp; - if (remainder) { - memmove(this->buf, this->bufp, remainder); - } - size_t len = this->reader->Read(this->buf + remainder, lengthof(this->buf) - remainder); +void ReadBuffer::SkipBytesSlowPath(size_t bytes) +{ + bytes -= (this->bufe - this->bufp); + while (true) { + size_t len = this->reader->Read(this->buf, lengthof(this->buf)); if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); - this->read += len; - this->bufp = this->buf; - this->bufe = this->buf + remainder + len; - } - - 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 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 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 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 = 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. - */ - 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); - } - - void FinaliseBlock() - { - assert(this->saved_buf == nullptr); - if (!this->blocks.empty()) { - size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf); - this->blocks.back().size = s; - this->completed_block_bytes += s; - } - this->buf = this->bufe = nullptr; - } - - void AllocateBuffer() - { - if (this->saved_buf) { - const size_t offset = this->buf - this->autolen_buf; - const size_t size = (this->autolen_buf_end - this->autolen_buf) * 2; - this->autolen_buf = ReallocT(this->autolen_buf, size); - this->autolen_buf_end = this->autolen_buf + size; - this->buf = this->autolen_buf + offset; - this->bufe = this->autolen_buf_end; + if (len >= bytes) { + this->bufp = this->buf + bytes; + this->bufe = this->buf + len; return; - } - this->FinaliseBlock(); - this->buf = CallocT(MEMORY_CHUNK_SIZE); - this->blocks.emplace_back(this->buf); - this->bufe = this->buf + MEMORY_CHUNK_SIZE; - } - - 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 = min(this->bufe - this->buf, length); - memcpy(this->buf, ptr, to_copy); - this->buf += to_copy; - ptr += to_copy; - length -= to_copy; + } else { + bytes -= len; } } +} - inline void RawWriteUint16(uint16 v) - { -#if OTTD_ALIGNMENT == 0 - *((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; +void ReadBuffer::AcquireBytes() +{ + size_t remainder = this->bufe - this->bufp; + if (remainder) { + memmove(this->buf, this->bufp, remainder); } + size_t len = this->reader->Read(this->buf + remainder, lengthof(this->buf) - remainder); + if (len == 0) SlErrorCorrupt("Unexpected end of chunk"); - inline void RawWriteUint32(uint32 v) - { -#if OTTD_ALIGNMENT == 0 - *((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; + this->read += len; + this->bufp = this->buf; + this->bufe = this->buf + remainder + len; +} + +void MemoryDumper::FinaliseBlock() +{ + assert(this->saved_buf == nullptr); + if (!this->blocks.empty()) { + size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf); + this->blocks.back().size = s; + this->completed_block_bytes += s; } + this->buf = this->bufe = nullptr; +} - inline void RawWriteUint64(uint64 v) - { -#if OTTD_ALIGNMENT == 0 - *((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; - } - - /** - * Flush this dumper into a writer. - * @param writer The filter we want to use. - */ - void Flush(SaveFilter *writer) - { - this->FinaliseBlock(); - - uint block_count = this->blocks.size(); - for (uint i = 0; i < block_count; i++) { - writer->Write(this->blocks[i].data, this->blocks[i].size); - } - - writer->Finish(); - } - - /** - * Get the size of the memory dump made so far. - * @return The size. - */ - size_t GetSize() const - { - assert(this->saved_buf == nullptr); - return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0); - } - - void StartAutoLength() - { - assert(this->saved_buf == nullptr); - - this->saved_buf = this->buf; - this->saved_bufe = this->bufe; - this->buf = this->autolen_buf; +void MemoryDumper::AllocateBuffer() +{ + if (this->saved_buf) { + const size_t offset = this->buf - this->autolen_buf; + const size_t size = (this->autolen_buf_end - this->autolen_buf) * 2; + this->autolen_buf = ReallocT(this->autolen_buf, size); + this->autolen_buf_end = this->autolen_buf + size; + this->buf = this->autolen_buf + offset; this->bufe = this->autolen_buf_end; + return; + } + this->FinaliseBlock(); + this->buf = CallocT(MEMORY_CHUNK_SIZE); + this->blocks.emplace_back(this->buf); + this->bufe = this->buf + MEMORY_CHUNK_SIZE; +} + +/** + * Flush this dumper into a writer. + * @param writer The filter we want to use. + */ +void MemoryDumper::Flush(SaveFilter *writer) +{ + this->FinaliseBlock(); + + uint block_count = this->blocks.size(); + for (uint i = 0; i < block_count; i++) { + writer->Write(this->blocks[i].data, this->blocks[i].size); } - std::pair StopAutoLength() - { - assert(this->saved_buf != nullptr); - auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf); + writer->Finish(); +} - this->buf = this->saved_buf; - this->bufe = this->saved_bufe; - this->saved_buf = this->saved_bufe = nullptr; - return res; - } -}; +void MemoryDumper::StartAutoLength() +{ + assert(this->saved_buf == nullptr); + + this->saved_buf = this->buf; + this->saved_bufe = this->bufe; + this->buf = this->autolen_buf; + this->bufe = this->autolen_buf_end; +} + +std::pair MemoryDumper::StopAutoLength() +{ + assert(this->saved_buf != nullptr); + auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf); + + this->buf = this->saved_buf; + this->bufe = this->saved_bufe; + this->saved_buf = this->saved_bufe = nullptr; + return res; +} + +/** + * Get the size of the memory dump made so far. + * @return The size. + */ +size_t MemoryDumper::GetSize() const +{ + assert(this->saved_buf == nullptr); + return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0); +} /** The saveload struct, containing reader-writer functions, buffer, version, etc. */ struct SaveLoadParams { @@ -651,6 +432,16 @@ struct SaveLoadParams { static SaveLoadParams _sl; ///< Parameters used for/at saveload. +ReadBuffer *ReadBuffer::GetCurrent() +{ + return _sl.reader; +} + +MemoryDumper *MemoryDumper::GetCurrent() +{ + return _sl.dumper; +} + /* these define the chunks */ extern const ChunkHandler _version_ext_chunk_handlers[]; extern const ChunkHandler _gamelog_chunk_handlers[]; diff --git a/src/saveload/saveload_buffer.h b/src/saveload/saveload_buffer.h new file mode 100644 index 0000000000..1ca754cead --- /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(NULL), bufe(NULL), 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 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 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 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 = 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 = 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 + *((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 + *((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 + *((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