/
Mr.Stalin
/
FOnline-Engine
Обзор
Документация
Войти
/
Mr.Stalin
/
FOnline-Engine
Код
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Source/Essentials/Compressor.cpp
267 строк
9 KB
cvet
Non const locals (#190)
24 июл 2026, 10:46
Не верифицирован
24 июл 2026, 10:46
4883d25
Код
Авторство
О чём код?
// __________ ___ ______ _ // / ____/ __ \____ / (_)___ ___ / ____/___ ____ _(_)___ ___ // / /_ / / / / __ \/ / / __ \/ _ \ / __/ / __ \/ __ `/ / __ \/ _ ` // / __/ / /_/ / / / / / / / / / __/ / /___/ / / / /_/ / / / / / __/ // /_/ \____/_/ /_/_/_/_/ /_/\___/ /_____/_/ /_/\__, /_/_/ /_/\___/ // /____/ // FOnline Engine // https://fonline.ru // https://github.com/cvet/fonline // // MIT License // // Copyright (c) 2006 - 2026, Anton Tsvetinskiy aka cvet <cvet@tut.by> // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #include "Compressor.h" #include "SafeArithmetics.h" #include "StackTrace.h" #include "zlib.h" FO_BEGIN_NAMESPACE auto Compressor::CalculateMaxCompressedBufSize(size_t initial_size) noexcept -> size_t { FO_NO_STACK_TRACE_ENTRY(); return initial_size * 110 / 100 + 12; } auto Compressor::Compress(const_span<uint8_t> data) -> vector<uint8_t> { FO_STACK_TRACE_ENTRY(); auto buf_len = numeric_cast<uLongf>(CalculateMaxCompressedBufSize(data.size())); auto buf = vector<uint8_t>(buf_len); int32_t result = compress2(buf.data(), &buf_len, data.data(), numeric_cast<uLong>(data.size()), Z_BEST_SPEED); if (result != Z_OK) { throw CompressionException("Compression failed", result); } buf.resize(buf_len); return buf; } auto Compressor::Decompress(const_span<uint8_t> data, size_t mul_approx) -> vector<uint8_t> { FO_STACK_TRACE_ENTRY(); auto buf_len = numeric_cast<uLongf>(data.size() * mul_approx); auto buf = vector<uint8_t>(buf_len); while (true) { int32_t result = uncompress(buf.data(), &buf_len, data.data(), numeric_cast<uLong>(data.size())); if (result == Z_BUF_ERROR) { buf_len *= 2; buf.resize(buf_len); } else if (result != Z_OK) { throw DecompressException("Decompression failed", result); } else { break; } } buf.resize(buf_len); return buf; } struct StreamCompressor::Impl { z_stream ZStream {}; }; StreamCompressor::StreamCompressor() noexcept = default; StreamCompressor::StreamCompressor(StreamCompressor&&) noexcept = default; auto StreamCompressor::operator=(StreamCompressor&& other) noexcept -> StreamCompressor& { FO_NO_STACK_TRACE_ENTRY(); if (this != &other) { Reset(); _impl = std::move(other._impl); } return *this; } StreamCompressor::~StreamCompressor() { FO_STACK_TRACE_ENTRY(); Reset(); } void StreamCompressor::Compress(const_span<uint8_t> buf, vector<uint8_t>& result) { FO_STACK_TRACE_ENTRY(); if (!_impl) { _impl = SafeAlloc::MakeUnique<Impl>(); MemFill(&_impl->ZStream, 0, sizeof(z_stream)); _impl->ZStream.zalloc = [](voidpf, uInt items, uInt size) -> void* { constexpr SafeAllocator<uint8_t> allocator; return allocator.allocate(numeric_cast<size_t>(items) * size); }; _impl->ZStream.zfree = [](voidpf, voidpf address) { constexpr SafeAllocator<uint8_t> allocator; allocator.deallocate(static_cast<uint8_t*>(address), 0); }; int32_t deflate_init = deflateInit(&_impl->ZStream, Z_BEST_SPEED); FO_VERIFY_AND_THROW(deflate_init == Z_OK, "Failed to initialize zlib deflate stream", deflate_init); } result.resize(std::max(result.capacity(), Compressor::CalculateMaxCompressedBufSize(buf.size()))); _impl->ZStream.next_in = const_cast<Bytef*>(buf.data()); _impl->ZStream.avail_in = numeric_cast<uInt>(buf.size()); auto output_begin = make_nptr(result.data()); _impl->ZStream.next_out = output_begin.get(); _impl->ZStream.avail_out = numeric_cast<uInt>(result.size()); int32_t deflate_result = deflate(&_impl->ZStream, Z_SYNC_FLUSH); FO_VERIFY_AND_THROW(deflate_result == Z_OK, "Zlib deflate did not finish with Z_OK", deflate_result, Z_OK, buf.size(), result.size()); size_t writed_len = numeric_cast<size_t>(_impl->ZStream.next_in - buf.data()); FO_VERIFY_AND_THROW(writed_len == buf.size(), "Zlib deflate did not consume the full input buffer", writed_len, buf.size()); size_t compr_len = numeric_cast<size_t>(_impl->ZStream.next_out - output_begin.get()); result.resize(compr_len); } void StreamCompressor::Reset() noexcept { FO_STACK_TRACE_ENTRY(); if (_impl) { deflateEnd(&_impl->ZStream); _impl.reset(); } } struct StreamDecompressor::Impl { z_stream ZStream {}; }; StreamDecompressor::StreamDecompressor() noexcept = default; StreamDecompressor::StreamDecompressor(StreamDecompressor&&) noexcept = default; auto StreamDecompressor::operator=(StreamDecompressor&& other) noexcept -> StreamDecompressor& { FO_NO_STACK_TRACE_ENTRY(); if (this != &other) { Reset(); _impl = std::move(other._impl); } return *this; } StreamDecompressor::~StreamDecompressor() { FO_STACK_TRACE_ENTRY(); Reset(); } void StreamDecompressor::Decompress(const_span<uint8_t> buf, vector<uint8_t>& result) { FO_STACK_TRACE_ENTRY(); if (!_impl) { _impl = SafeAlloc::MakeUnique<Impl>(); MemFill(&_impl->ZStream, 0, sizeof(z_stream)); _impl->ZStream.zalloc = [](voidpf, uInt items, uInt size) -> void* { constexpr SafeAllocator<uint8_t> allocator; return allocator.allocate(numeric_cast<size_t>(items) * size); }; _impl->ZStream.zfree = [](voidpf, voidpf address) { constexpr SafeAllocator<uint8_t> allocator; allocator.deallocate(static_cast<uint8_t*>(address), 0); }; int32_t inflate_init = inflateInit(&_impl->ZStream); FO_VERIFY_AND_THROW(inflate_init == Z_OK, "Failed to initialize zlib inflate stream", inflate_init); } result.resize(std::max(result.capacity(), buf.size() * 2)); _impl->ZStream.next_in = const_cast<Bytef*>(buf.data()); _impl->ZStream.avail_in = numeric_cast<uInt>(buf.size()); auto output_begin = make_nptr(result.data()); _impl->ZStream.next_out = output_begin.get(); _impl->ZStream.avail_out = numeric_cast<uInt>(result.size()); int32_t first_inflate = ::inflate(&_impl->ZStream, Z_SYNC_FLUSH); bool malformed_initial_stream = first_inflate == Z_NEED_DICT || first_inflate == Z_DATA_ERROR || first_inflate == Z_BUF_ERROR || first_inflate == Z_STREAM_END; if (malformed_initial_stream) { throw DecompressException("Malformed compressed transport stream during initial inflate", first_inflate, buf.size(), result.size()); } FO_VERIFY_AND_THROW(first_inflate == Z_OK, "Initial zlib inflate step did not finish with Z_OK", first_inflate, Z_OK, buf.size(), result.size()); size_t uncompr_len = numeric_cast<size_t>(_impl->ZStream.next_out - output_begin.get()); while (_impl->ZStream.avail_in != 0) { result.resize(result.size() * 2); output_begin = result.data(); _impl->ZStream.next_out = output_begin.get() + uncompr_len; _impl->ZStream.avail_out = numeric_cast<uInt>(result.size() - uncompr_len); int32_t next_inflate = ::inflate(&_impl->ZStream, Z_SYNC_FLUSH); bool malformed_continuation_stream = next_inflate == Z_NEED_DICT || next_inflate == Z_DATA_ERROR || next_inflate == Z_BUF_ERROR || next_inflate == Z_STREAM_END; if (malformed_continuation_stream) { throw DecompressException("Malformed compressed transport stream during continuation inflate", next_inflate, _impl->ZStream.avail_in, result.size()); } FO_VERIFY_AND_THROW(next_inflate == Z_OK, "Continuation zlib inflate step did not finish with Z_OK", next_inflate, Z_OK, _impl->ZStream.avail_in, result.size()); uncompr_len = numeric_cast<size_t>(_impl->ZStream.next_out - output_begin.get()); } result.resize(uncompr_len); } void StreamDecompressor::Reset() noexcept { FO_STACK_TRACE_ENTRY(); if (_impl) { inflateEnd(&_impl->ZStream); _impl.reset(); } } FO_END_NAMESPACE