/
smychkov
/
SStorage
Обзор
Документация
Войти
/
smychkov
/
SStorage
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/sstable/block.cpp
182 строки
7 KB
Андрей Смычков
docs: перевод оставшихся комментариев на русский + актуализация AGENTS
25 апр 2026, 10:08
25 апр 2026, 10:08
f2b9fd9
Код
Авторство
О чём код?
#include "block.hpp" #include "../util/crc32c.hpp" #include "../util/varint.hpp" #include <cstring> namespace sstorage { namespace { void writeU32LE(char* out, uint32_t v) { for (int i = 0; i < 4; ++i) { out[i] = static_cast<char>((v >> (i * 8)) & 0xFF); } } uint32_t readU32LE(const char* in) { uint32_t v = 0; for (int i = 0; i < 4; ++i) { v |= static_cast<uint32_t>(static_cast<uint8_t>(in[i])) << (i * 8); } return v; } } //============================================================================ // BlockBuilder — построитель одного data-блока //============================================================================ BlockBuilder::BlockBuilder() { // Резервируем место для numRecords varint (placeholder 10 байт) buffer_.reserve(4096); } void BlockBuilder::add(const Record& r) { if (count_ == 0) { firstKey_ = r.key(); } lastKey_ = r.key(); // Сериализуем Record прямо в буфер r.serialize(buffer_); ++count_; } std::string BlockBuilder::finish() { if (finalized_) { return std::string(); } // Собираем итоговый buffer: [varint: numRecords][raw records] std::string out; out.reserve(util::varintLength(count_) + buffer_.size()); util::encodeVarint(count_, out); out.append(buffer_); finalized_ = true; return out; } void BlockBuilder::reset() { buffer_.clear(); firstKey_.clear(); lastKey_.clear(); count_ = 0; finalized_ = false; } //============================================================================ // Сжатие и запись блока в on-disk формат //============================================================================ util::CompressionType buildBlockForDisk(const std::string& raw, util::CompressionType type, std::string& out) { std::string compressed; util::CompressionType actualType = util::compress( raw.data(), raw.size(), compressed, type); // Формируем итоговый блок: [header][compressed body] out.clear(); out.reserve(kBlockHeaderSize + compressed.size()); char header[kBlockHeaderSize]; header[0] = static_cast<char>(actualType); writeU32LE(header + 1, static_cast<uint32_t>(raw.size())); writeU32LE(header + 5, static_cast<uint32_t>(compressed.size())); // CRC считается по compressed bytes uint32_t crc = util::crc32c(compressed); writeU32LE(header + 9, crc); out.append(header, kBlockHeaderSize); out.append(compressed); return actualType; } //============================================================================ // Чтение блока с диска //============================================================================ bool readBlockFromDisk(const char* data, size_t len, std::string& out) { if (data == nullptr) return false; if (len < kBlockHeaderSize) return false; uint8_t typeRaw = static_cast<uint8_t>(data[0]); // Валидация типа компрессии: принимаем только известные значения. // Неизвестный тип — потенциально вредоносный файл, отвергаем. if (typeRaw != static_cast<uint8_t>(util::CompressionType::kNone) && typeRaw != static_cast<uint8_t>(util::CompressionType::kSnappy)) { return false; } util::CompressionType type = static_cast<util::CompressionType>(typeRaw); uint32_t uncompressedSize = readU32LE(data + 1); uint32_t compressedSize = readU32LE(data + 5); uint32_t expectedCrc = readU32LE(data + 9); // Жёсткий лимит на размер блока (разумный максимум — 64 МБ). // Это защита от zip-bomb и вредоносных заголовков с огромными значениями. constexpr uint32_t kMaxBlockSize = 64u * 1024u * 1024u; if (uncompressedSize > kMaxBlockSize) return false; if (compressedSize > kMaxBlockSize) return false; // Защита от integer overflow: проверяем через вычитание, без сложения. // len >= kBlockHeaderSize уже гарантировано выше. if (compressedSize > len - kBlockHeaderSize) return false; const char* body = data + kBlockHeaderSize; // Проверка CRC по compressed bytes uint32_t actualCrc = util::crc32c(body, compressedSize); if (actualCrc != expectedCrc) return false; // Разжатие if (!util::decompress(body, compressedSize, out, type)) { return false; } // Проверка: размер после разжатия соответствует заявленному. // Это защищает от ситуации, когда compressed body содержит валидный // для Snappy поток, но его распакованный размер не совпадает с заявленным. if (out.size() != uncompressedSize) { return false; } return true; } //============================================================================ // Парсинг записей блока //============================================================================ bool parseBlockRecords(const char* data, size_t len, std::vector<Record>& out) { out.clear(); if (len == 0) return true; if (data == nullptr) return false; size_t offset = 0; uint64_t numRecords; size_t n; if (!util::decodeVarint(data + offset, len - offset, numRecords, n)) return false; offset += n; // Защита от невалидно большого numRecords. Консервативный лимит: // один блок имеет разумный максимум ~1M записей (при среднем размере // записи 10 байт это 10 МБ — больше чем разумный размер блока). if (numRecords > 1000000) return false; // Дополнительная проверка: если numRecords * 1 байт (минимальная запись) // уже больше оставшегося буфера — явно битые данные. if (numRecords > (len - offset)) return false; out.reserve(static_cast<size_t>(numRecords)); for (uint64_t i = 0; i < numRecords; ++i) { size_t consumed; auto rec = Record::deserialize(data + offset, len - offset, consumed); if (!rec.has_value()) return false; offset += consumed; out.push_back(std::move(*rec)); } return true; } }