mirror of https://github.com/procxx/kepka.git
Store first slice in the header cache key.
This commit is contained in:
parent
95954c4b1f
commit
84b09795f3
|
@ -21,6 +21,7 @@ constexpr auto kPartsInSlice = 64;
|
||||||
constexpr auto kInSlice = kPartsInSlice * kPartSize;
|
constexpr auto kInSlice = kPartsInSlice * kPartSize;
|
||||||
constexpr auto kMaxPartsInHeader = 16;
|
constexpr auto kMaxPartsInHeader = 16;
|
||||||
constexpr auto kMaxOnlyInHeader = 80 * kPartSize;
|
constexpr auto kMaxOnlyInHeader = 80 * kPartSize;
|
||||||
|
constexpr auto kPartsOutsideFirstSliceGood = 8;
|
||||||
constexpr auto kSlicesInMemory = 2;
|
constexpr auto kSlicesInMemory = 2;
|
||||||
|
|
||||||
// 1 MB of header parts can be outside the first slice for us to still
|
// 1 MB of header parts can be outside the first slice for us to still
|
||||||
|
@ -30,6 +31,10 @@ constexpr auto kSlicesInMemory = 2;
|
||||||
// 1 MB of parts are requested from cloud ahead of reading demand.
|
// 1 MB of parts are requested from cloud ahead of reading demand.
|
||||||
constexpr auto kPreloadPartsAhead = 8;
|
constexpr auto kPreloadPartsAhead = 8;
|
||||||
|
|
||||||
|
bool IsContiguousSerialization(int serializedSize, int maxSliceSize) {
|
||||||
|
return !(serializedSize % kPartSize) || (serializedSize == maxSliceSize);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Range> // Range::value_type is Pair<int, QByteArray>
|
template <typename Range> // Range::value_type is Pair<int, QByteArray>
|
||||||
int FindNotLoadedStart(Range &&parts, int offset) {
|
int FindNotLoadedStart(Range &&parts, int offset) {
|
||||||
auto result = offset;
|
auto result = offset;
|
||||||
|
@ -108,7 +113,9 @@ Storage::Cache::Key Reader::CacheHelper::key(int sliceNumber) const {
|
||||||
return Storage::Cache::Key{ baseKey.high, baseKey.low + sliceNumber };
|
return Storage::Cache::Key{ baseKey.high, baseKey.low + sliceNumber };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Reader::Slice::processCacheData(QByteArray &&data, int maxSliceSize) {
|
bytes::const_span Reader::Slice::processCacheData(
|
||||||
|
bytes::const_span data,
|
||||||
|
int maxSize) {
|
||||||
Expects((flags & Flag::LoadingFromCache) != 0);
|
Expects((flags & Flag::LoadingFromCache) != 0);
|
||||||
Expects(!(flags & Flag::LoadedFromCache));
|
Expects(!(flags & Flag::LoadedFromCache));
|
||||||
|
|
||||||
|
@ -117,24 +124,28 @@ bool Reader::Slice::processCacheData(QByteArray &&data, int maxSliceSize) {
|
||||||
flags &= ~Flag::LoadingFromCache;
|
flags &= ~Flag::LoadingFromCache;
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto size = data.size();
|
const auto size = int(data.size());
|
||||||
if (!(size % kPartSize) || (size == maxSliceSize)) {
|
if (IsContiguousSerialization(size, maxSize)) {
|
||||||
if (size > maxSliceSize) {
|
if (size > maxSize) {
|
||||||
return false;
|
return {};
|
||||||
}
|
}
|
||||||
for (auto offset = 0; offset < size; offset += kPartSize) {
|
for (auto offset = 0; offset < size; offset += kPartSize) {
|
||||||
parts.emplace(offset, data.mid(offset, kPartSize));
|
const auto part = data.subspan(
|
||||||
|
offset,
|
||||||
|
std::min(kPartSize, size - offset));
|
||||||
|
parts.try_emplace(
|
||||||
|
offset,
|
||||||
|
reinterpret_cast<const char*>(part.data()),
|
||||||
|
part.size());
|
||||||
}
|
}
|
||||||
return true;
|
return {};
|
||||||
}
|
}
|
||||||
return processComplexCacheData(
|
return processComplexCacheData(bytes::make_span(data), maxSize);
|
||||||
bytes::make_span(data),
|
|
||||||
maxSliceSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Reader::Slice::processComplexCacheData(
|
bytes::const_span Reader::Slice::processComplexCacheData(
|
||||||
bytes::const_span data,
|
bytes::const_span data,
|
||||||
int maxSliceSize) {
|
int maxSize) {
|
||||||
const auto takeInt = [&]() -> std::optional<int> {
|
const auto takeInt = [&]() -> std::optional<int> {
|
||||||
if (data.size() < sizeof(int32)) {
|
if (data.size() < sizeof(int32)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -144,35 +155,42 @@ bool Reader::Slice::processComplexCacheData(
|
||||||
data = data.subspan(sizeof(int32));
|
data = data.subspan(sizeof(int32));
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
const auto takeBytes = [&](int count) -> std::optional<QByteArray> {
|
const auto takeBytes = [&](int count) {
|
||||||
if (count <= 0 || data.size() < count) {
|
if (count <= 0 || data.size() < count) {
|
||||||
return std::nullopt;
|
return bytes::const_span();
|
||||||
}
|
}
|
||||||
auto result = QByteArray(
|
const auto result = data.subspan(0, count);
|
||||||
reinterpret_cast<const char*>(data.data()),
|
|
||||||
count);
|
|
||||||
data = data.subspan(count);
|
data = data.subspan(count);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
const auto count = takeInt().value_or(0);
|
const auto maybeCount = takeInt();
|
||||||
if (count <= 0) {
|
if (!maybeCount) {
|
||||||
return false;
|
return {};
|
||||||
|
}
|
||||||
|
const auto count = *maybeCount;
|
||||||
|
if (count < 0) {
|
||||||
|
return {};
|
||||||
|
} else if (!count) {
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
for (auto i = 0; i != count; ++i) {
|
for (auto i = 0; i != count; ++i) {
|
||||||
const auto offset = takeInt().value_or(0);
|
const auto offset = takeInt().value_or(0);
|
||||||
const auto size = takeInt().value_or(0);
|
const auto size = takeInt().value_or(0);
|
||||||
auto bytes = takeBytes(size).value_or(QByteArray());
|
const auto bytes = takeBytes(size);
|
||||||
if (offset < 0
|
if (offset < 0
|
||||||
|| offset >= maxSliceSize
|
|| offset >= maxSize
|
||||||
|| size <= 0
|
|| size <= 0
|
||||||
|| size > maxSliceSize
|
|| size > maxSize
|
||||||
|| offset + size > maxSliceSize
|
|| offset + size > maxSize
|
||||||
|| bytes.size() != size) {
|
|| bytes.size() != size) {
|
||||||
return false;
|
return {};
|
||||||
}
|
}
|
||||||
parts.emplace(offset, std::move(bytes));
|
parts.try_emplace(
|
||||||
|
offset,
|
||||||
|
reinterpret_cast<const char*>(bytes.data()),
|
||||||
|
bytes.size());
|
||||||
}
|
}
|
||||||
return true;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reader::Slice::addPart(int offset, QByteArray bytes) {
|
void Reader::Slice::addPart(int offset, QByteArray bytes) {
|
||||||
|
@ -258,26 +276,50 @@ Reader::Slices::Slices(int size, bool useCache)
|
||||||
: _size(size) {
|
: _size(size) {
|
||||||
Expects(size > 0);
|
Expects(size > 0);
|
||||||
|
|
||||||
_header.flags |= Slice::Flag::Header;
|
|
||||||
if (useCache) {
|
if (useCache) {
|
||||||
_header.flags |= Slice::Flag::LoadingFromCache;
|
_header.flags |= Slice::Flag::LoadingFromCache;
|
||||||
} else {
|
} else {
|
||||||
_headerMode = HeaderMode::NoCache;
|
_headerMode = HeaderMode::NoCache;
|
||||||
}
|
}
|
||||||
if (!fullInHeader()) {
|
if (!isFullInHeader()) {
|
||||||
_data.resize((_size + kInSlice - 1) / kInSlice);
|
_data.resize((_size + kInSlice - 1) / kInSlice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Reader::Slices::fullInHeader() const {
|
bool Reader::Slices::headerModeUnknown() const {
|
||||||
|
return (_headerMode == HeaderMode::Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Reader::Slices::isFullInHeader() const {
|
||||||
return (_size <= kMaxOnlyInHeader);
|
return (_size <= kMaxOnlyInHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Reader::Slices::isGoodHeader() const {
|
||||||
|
return (_headerMode == HeaderMode::Good);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Reader::Slices::computeIsGoodHeader() const {
|
||||||
|
if (isFullInHeader()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto outsideFirstSliceIt = ranges::lower_bound(
|
||||||
|
_header.parts,
|
||||||
|
kInSlice,
|
||||||
|
ranges::less(),
|
||||||
|
&base::flat_map<int, QByteArray>::value_type::first);
|
||||||
|
const auto outsideFirstSlice = end(_header.parts) - outsideFirstSliceIt;
|
||||||
|
return (outsideFirstSlice <= kPartsOutsideFirstSliceGood);
|
||||||
|
}
|
||||||
|
|
||||||
void Reader::Slices::headerDone(bool fromCache) {
|
void Reader::Slices::headerDone(bool fromCache) {
|
||||||
if (_headerMode != HeaderMode::Unknown) {
|
if (_headerMode != HeaderMode::Unknown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_headerMode = HeaderMode::Small;
|
_headerMode = isFullInHeader()
|
||||||
|
? HeaderMode::Full
|
||||||
|
: computeIsGoodHeader()
|
||||||
|
? HeaderMode::Good
|
||||||
|
: HeaderMode::Small;
|
||||||
if (!fromCache) {
|
if (!fromCache) {
|
||||||
for (auto &slice : _data) {
|
for (auto &slice : _data) {
|
||||||
using Flag = Slice::Flag;
|
using Flag = Slice::Flag;
|
||||||
|
@ -289,14 +331,14 @@ void Reader::Slices::headerDone(bool fromCache) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Reader::Slices::headerWontBeFilled() const {
|
bool Reader::Slices::headerWontBeFilled() const {
|
||||||
return (_headerMode == HeaderMode::Unknown)
|
return headerModeUnknown()
|
||||||
&& (_header.parts.size() >= kMaxPartsInHeader);
|
&& (_header.parts.size() >= kMaxPartsInHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reader::Slices::applyHeaderCacheData() {
|
void Reader::Slices::applyHeaderCacheData() {
|
||||||
if (_header.parts.empty()) {
|
if (_header.parts.empty() || _headerMode != HeaderMode::Unknown) {
|
||||||
return;
|
return;
|
||||||
} else if (fullInHeader()) {
|
} else if (isFullInHeader()) {
|
||||||
headerDone(true);
|
headerDone(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -311,28 +353,43 @@ void Reader::Slices::applyHeaderCacheData() {
|
||||||
|
|
||||||
void Reader::Slices::processCacheResult(
|
void Reader::Slices::processCacheResult(
|
||||||
int sliceNumber,
|
int sliceNumber,
|
||||||
QByteArray &&result) {
|
bytes::const_span result) {
|
||||||
Expects(sliceNumber >= 0 && sliceNumber <= _data.size());
|
Expects(sliceNumber >= 0 && sliceNumber <= _data.size());
|
||||||
|
|
||||||
auto &slice = (sliceNumber ? _data[sliceNumber - 1] : _header);
|
auto &slice = (sliceNumber ? _data[sliceNumber - 1] : _header);
|
||||||
if (!(slice.flags &Slice::Flag::LoadingFromCache)) {
|
if (!sliceNumber && isGoodHeader()) {
|
||||||
|
// We've loaded header slice because really we wanted first slice.
|
||||||
|
if (!(_data[0].flags & Slice::Flag::LoadingFromCache)) {
|
||||||
// We could've already unloaded this slice using LRU _usedSlices.
|
// We could've already unloaded this slice using LRU _usedSlices.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto success = slice.processCacheData(
|
// So just process whole result even if we didn't want header really.
|
||||||
std::move(result),
|
slice.flags |= Slice::Flag::LoadingFromCache;
|
||||||
|
}
|
||||||
|
if (!(slice.flags & Slice::Flag::LoadingFromCache)) {
|
||||||
|
// We could've already unloaded this slice using LRU _usedSlices.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto remaining = slice.processCacheData(
|
||||||
|
result,
|
||||||
maxSliceSize(sliceNumber));
|
maxSliceSize(sliceNumber));
|
||||||
if (!sliceNumber) {
|
if (!sliceNumber) {
|
||||||
applyHeaderCacheData();
|
applyHeaderCacheData();
|
||||||
|
if (isGoodHeader()) {
|
||||||
|
// When we first read header we don't request the first slice.
|
||||||
|
// But we get it, so let's apply it anyway.
|
||||||
|
_data[0].flags |= Slice::Flag::LoadingFromCache;
|
||||||
|
processCacheResult(1, remaining);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reader::Slices::processPart(
|
void Reader::Slices::processPart(
|
||||||
int offset,
|
int offset,
|
||||||
QByteArray &&bytes) {
|
QByteArray &&bytes) {
|
||||||
Expects(fullInHeader() || (offset / kInSlice < _data.size()));
|
Expects(isFullInHeader() || (offset / kInSlice < _data.size()));
|
||||||
|
|
||||||
if (fullInHeader()) {
|
if (isFullInHeader()) {
|
||||||
_header.addPart(offset, bytes);
|
_header.addPart(offset, bytes);
|
||||||
return;
|
return;
|
||||||
} else if (_headerMode == HeaderMode::Unknown) {
|
} else if (_headerMode == HeaderMode::Unknown) {
|
||||||
|
@ -357,8 +414,9 @@ auto Reader::Slices::fill(int offset, bytes::span buffer) -> FillResult {
|
||||||
if (_headerMode != HeaderMode::NoCache
|
if (_headerMode != HeaderMode::NoCache
|
||||||
&& !(_header.flags & Flag::LoadedFromCache)) {
|
&& !(_header.flags & Flag::LoadedFromCache)) {
|
||||||
// Waiting for initial cache query.
|
// Waiting for initial cache query.
|
||||||
|
Assert(_header.flags & Flag::LoadingFromCache);
|
||||||
return {};
|
return {};
|
||||||
} else if (fullInHeader()) {
|
} else if (isFullInHeader()) {
|
||||||
return fillFromHeader(offset, buffer);
|
return fillFromHeader(offset, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,25 +557,61 @@ Reader::SerializedSlice Reader::Slices::serializeAndUnloadSlice(
|
||||||
int sliceNumber) {
|
int sliceNumber) {
|
||||||
Expects(sliceNumber >= 0 && sliceNumber <= _data.size());
|
Expects(sliceNumber >= 0 && sliceNumber <= _data.size());
|
||||||
|
|
||||||
|
if (isGoodHeader() && (sliceNumber == 1)) {
|
||||||
|
return serializeAndUnloadSlice(0);
|
||||||
|
}
|
||||||
|
const auto writeHeaderAndSlice = isGoodHeader() && !sliceNumber;
|
||||||
|
|
||||||
auto &slice = sliceNumber ? _data[sliceNumber - 1] : _header;
|
auto &slice = sliceNumber ? _data[sliceNumber - 1] : _header;
|
||||||
const auto count = slice.parts.size();
|
const auto count = slice.parts.size();
|
||||||
Assert(count > 0);
|
Assert(count > 0);
|
||||||
|
|
||||||
auto result = SerializedSlice();
|
auto result = SerializedSlice();
|
||||||
result.number = sliceNumber;
|
result.number = sliceNumber;
|
||||||
const auto continuous = FindNotLoadedStart(slice.parts, 0);
|
|
||||||
if (continuous > slice.parts.back().first) {
|
// We always use complex serialization for header + first slice.
|
||||||
|
const auto continuousTill = writeHeaderAndSlice
|
||||||
|
? 0
|
||||||
|
: FindNotLoadedStart(slice.parts, 0);
|
||||||
|
const auto continuous = (continuousTill > slice.parts.back().first);
|
||||||
|
if (continuous) {
|
||||||
// All data is continuous.
|
// All data is continuous.
|
||||||
result.data.reserve(count * kPartSize);
|
result.data.reserve(count * kPartSize);
|
||||||
for (const auto &[offset, part] : slice.parts) {
|
for (const auto &[offset, part] : slice.parts) {
|
||||||
result.data.append(part);
|
result.data.append(part);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
result.data = serializeComplexSlice(slice);
|
||||||
|
if (writeHeaderAndSlice) {
|
||||||
|
result.data.append(serializeAndUnloadFirstSliceNoHeader());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure this data won't be taken for full continuous data.
|
||||||
|
const auto maxSize = maxSliceSize(sliceNumber);
|
||||||
|
while (IsContiguousSerialization(result.data.size(), maxSize)) {
|
||||||
|
result.data.push_back(char(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may serialize header in the middle of streaming, if we use
|
||||||
|
// HeaderMode::Good and we unload first slice. We still require
|
||||||
|
// header data to continue working, so don't really unload the header.
|
||||||
|
if (sliceNumber) {
|
||||||
|
slice = Slice();
|
||||||
|
} else {
|
||||||
|
slice.flags &= ~Slice::Flag::ChangedSinceCache;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Reader::Slices::serializeComplexSlice(const Slice &slice) const {
|
||||||
|
auto result = QByteArray();
|
||||||
|
const auto count = slice.parts.size();
|
||||||
const auto intSize = sizeof(int32);
|
const auto intSize = sizeof(int32);
|
||||||
result.data.reserve(count * kPartSize + 2 * intSize * (count + 1));
|
result.reserve(count * kPartSize + 2 * intSize * (count + 1));
|
||||||
const auto appendInt = [&](int value) {
|
const auto appendInt = [&](int value) {
|
||||||
auto serialized = int32(value);
|
auto serialized = int32(value);
|
||||||
result.data.append(
|
result.append(
|
||||||
reinterpret_cast<const char*>(&serialized),
|
reinterpret_cast<const char*>(&serialized),
|
||||||
intSize);
|
intSize);
|
||||||
};
|
};
|
||||||
|
@ -525,13 +619,19 @@ Reader::SerializedSlice Reader::Slices::serializeAndUnloadSlice(
|
||||||
for (const auto &[offset, part] : slice.parts) {
|
for (const auto &[offset, part] : slice.parts) {
|
||||||
appendInt(offset);
|
appendInt(offset);
|
||||||
appendInt(part.size());
|
appendInt(part.size());
|
||||||
result.data.append(part);
|
result.append(part);
|
||||||
}
|
|
||||||
if (result.data.size() == maxSliceSize(sliceNumber)) {
|
|
||||||
// Make sure this data won't be taken for full continuous data.
|
|
||||||
appendInt(0);
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Reader::Slices::serializeAndUnloadFirstSliceNoHeader() {
|
||||||
|
Expects(_data[0].flags & Slice::Flag::LoadedFromCache);
|
||||||
|
|
||||||
|
auto &slice = _data[0];
|
||||||
|
for (const auto &[offset, part] : _header.parts) {
|
||||||
|
slice.parts.erase(offset);
|
||||||
}
|
}
|
||||||
|
auto result = serializeComplexSlice(slice);
|
||||||
slice = Slice();
|
slice = Slice();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -595,7 +695,11 @@ std::shared_ptr<Reader::CacheHelper> Reader::InitCacheHelper(
|
||||||
// 0 is for headerData, slice index = sliceNumber - 1.
|
// 0 is for headerData, slice index = sliceNumber - 1.
|
||||||
void Reader::readFromCache(int sliceNumber) {
|
void Reader::readFromCache(int sliceNumber) {
|
||||||
Expects(_cacheHelper != nullptr);
|
Expects(_cacheHelper != nullptr);
|
||||||
|
Expects(!sliceNumber || !_slices.headerModeUnknown());
|
||||||
|
|
||||||
|
if (sliceNumber == 1 && _slices.isGoodHeader()) {
|
||||||
|
return readFromCache(1);
|
||||||
|
}
|
||||||
LOG(("READING FROM CACHE: %1").arg(sliceNumber));
|
LOG(("READING FROM CACHE: %1").arg(sliceNumber));
|
||||||
const auto key = _cacheHelper->key(sliceNumber);
|
const auto key = _cacheHelper->key(sliceNumber);
|
||||||
const auto weak = std::weak_ptr<CacheHelper>(_cacheHelper);
|
const auto weak = std::weak_ptr<CacheHelper>(_cacheHelper);
|
||||||
|
@ -703,8 +807,12 @@ bool Reader::fillFromSlices(int offset, bytes::span buffer) {
|
||||||
|
|
||||||
++several;
|
++several;
|
||||||
|
|
||||||
if (_cacheHelper && result.toCache.number > 0) {
|
if (_cacheHelper && result.toCache.number >= 0) {
|
||||||
const auto index = result.toCache.number - 1;
|
// If we put to cache the header (number == 0) that means we're in
|
||||||
|
// HeaderMode::Good and really are putting the first slice to cache.
|
||||||
|
Assert(result.toCache.number > 0 || _slices.isGoodHeader());
|
||||||
|
|
||||||
|
const auto index = std::min(result.toCache.number, 1) - 1;
|
||||||
cancelLoadInRange(index * kInSlice, (index + 1) * kInSlice);
|
cancelLoadInRange(index * kInSlice, (index + 1) * kInSlice);
|
||||||
putToCache(std::move(result.toCache));
|
putToCache(std::move(result.toCache));
|
||||||
}
|
}
|
||||||
|
@ -747,10 +855,8 @@ bool Reader::processCacheResults() {
|
||||||
auto loaded = base::take(_cacheHelper->results);
|
auto loaded = base::take(_cacheHelper->results);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
for (auto &&part : loaded) {
|
for (const auto &[sliceNumber, result] : loaded) {
|
||||||
const auto sliceNumber = part.first;
|
_slices.processCacheResult(sliceNumber, bytes::make_span(result));
|
||||||
auto &serialized = part.second;
|
|
||||||
_slices.processCacheResult(sliceNumber, std::move(serialized));
|
|
||||||
}
|
}
|
||||||
return !loaded.empty();
|
return !loaded.empty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,6 @@ private:
|
||||||
|
|
||||||
struct Slice {
|
struct Slice {
|
||||||
enum class Flag : uchar {
|
enum class Flag : uchar {
|
||||||
Header = 0x01,
|
|
||||||
LoadingFromCache = 0x02,
|
LoadingFromCache = 0x02,
|
||||||
LoadedFromCache = 0x04,
|
LoadedFromCache = 0x04,
|
||||||
ChangedSinceCache = 0x08,
|
ChangedSinceCache = 0x08,
|
||||||
|
@ -92,10 +91,12 @@ private:
|
||||||
bool ready = true;
|
bool ready = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool processCacheData(QByteArray &&data, int maxSliceSize);
|
bytes::const_span processCacheData(
|
||||||
bool processComplexCacheData(
|
|
||||||
bytes::const_span data,
|
bytes::const_span data,
|
||||||
int maxSliceSize);
|
int maxSize);
|
||||||
|
bytes::const_span processComplexCacheData(
|
||||||
|
bytes::const_span data,
|
||||||
|
int maxSize);
|
||||||
void addPart(int offset, QByteArray bytes);
|
void addPart(int offset, QByteArray bytes);
|
||||||
PrepareFillResult prepareFill(int from, int till);
|
PrepareFillResult prepareFill(int from, int till);
|
||||||
|
|
||||||
|
@ -115,8 +116,11 @@ private:
|
||||||
|
|
||||||
void headerDone(bool fromCache);
|
void headerDone(bool fromCache);
|
||||||
[[nodiscard]] bool headerWontBeFilled() const;
|
[[nodiscard]] bool headerWontBeFilled() const;
|
||||||
|
[[nodiscard]] bool headerModeUnknown() const;
|
||||||
|
[[nodiscard]] bool isFullInHeader() const;
|
||||||
|
[[nodiscard]] bool isGoodHeader() const;
|
||||||
|
|
||||||
void processCacheResult(int sliceNumber, QByteArray &&result);
|
void processCacheResult(int sliceNumber, bytes::const_span result);
|
||||||
void processPart(int offset, QByteArray &&bytes);
|
void processPart(int offset, QByteArray &&bytes);
|
||||||
|
|
||||||
[[nodiscard]] FillResult fill(int offset, bytes::span buffer);
|
[[nodiscard]] FillResult fill(int offset, bytes::span buffer);
|
||||||
|
@ -126,16 +130,24 @@ private:
|
||||||
enum class HeaderMode {
|
enum class HeaderMode {
|
||||||
Unknown,
|
Unknown,
|
||||||
Small,
|
Small,
|
||||||
|
Good,
|
||||||
|
Full,
|
||||||
NoCache,
|
NoCache,
|
||||||
};
|
};
|
||||||
|
|
||||||
void applyHeaderCacheData();
|
void applyHeaderCacheData();
|
||||||
int maxSliceSize(int sliceNumber) const;
|
[[nodiscard]] int maxSliceSize(int sliceNumber) const;
|
||||||
SerializedSlice serializeAndUnloadSlice(int sliceNumber);
|
[[nodiscard]] SerializedSlice serializeAndUnloadSlice(
|
||||||
SerializedSlice serializeAndUnloadUnused();
|
int sliceNumber);
|
||||||
|
[[nodiscard]] SerializedSlice serializeAndUnloadUnused();
|
||||||
|
[[nodiscard]] QByteArray serializeComplexSlice(
|
||||||
|
const Slice &slice) const;
|
||||||
|
[[nodiscard]] QByteArray serializeAndUnloadFirstSliceNoHeader();
|
||||||
void markSliceUsed(int sliceIndex);
|
void markSliceUsed(int sliceIndex);
|
||||||
bool fullInHeader() const;
|
[[nodiscard]] bool computeIsGoodHeader() const;
|
||||||
FillResult fillFromHeader(int offset, bytes::span buffer);
|
[[nodiscard]] FillResult fillFromHeader(
|
||||||
|
int offset,
|
||||||
|
bytes::span buffer);
|
||||||
|
|
||||||
std::vector<Slice> _data;
|
std::vector<Slice> _data;
|
||||||
Slice _header;
|
Slice _header;
|
||||||
|
|
Loading…
Reference in New Issue