Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251data.resize(m_header.len);252s.read(std::as_writable_bytes(std::span(data.data(), data.size())));253}254};256/** Class for records representing internal nodes of the BTree. */257class InternalRecord258{259public:260InternalRecord(const RecordHeader& header) : m_header(header) {}261InternalRecord() = delete;263RecordHeader m_header;265uint8_t unused; // Padding, unused266uint32_t page_num; // Page number of referenced page267uint32_t records; // Subtree record count268std::vector<std::byte> data; // Variable length key item270static constexpr size_t FIXED_SIZE = 9; // Size of fixed data is 9 bytes272template <typename Stream>273void Unserialize(Stream& s)274{275s >> unused;276s >> page_num;277s >> records;279data.resize(m_header.len);280s.read(std::as_writable_bytes(std::span(data.data(), data.size())));282if (m_header.other_endian) {283page_num = internal_bswap_32(page_num);284records = internal_bswap_32(records);285}286}287};289/** Class for records representing overflow records of the BTree.290* Overflow records point to a page which contains the data in the record.291* Those pages may point to further pages with the rest of the data if it does not fit292* in one page */293class OverflowRecord294{295public:296OverflowRecord(const RecordHeader& header) : m_header(header) {}297OverflowRecord() = delete;299RecordHeader m_header;301uint8_t unused2; // Padding, unused302uint32_t page_number; // Page number where data begins303uint32_t item_len; // Total length of item305static constexpr size_t SIZE = 9; // Overflow record is always 9 bytes307template <typename Stream>308void Unserialize(Stream& s)309{310s >> unused2;311s >> page_number;312s >> item_len;314if (m_header.other_endian) {315page_number = internal_bswap_32(page_number);316item_len = internal_bswap_32(item_len);317}318}319};321/** A generic data page in the database. Contains fields common to all data pages. */322class PageHeader323{324public:325uint32_t lsn_file; // Log Sequence Number file326uint32_t lsn_offset; // Log Sequence Number offset327uint32_t page_num; // Current page number328uint32_t prev_page; // Previous page number329uint32_t next_page; // Next page number330uint16_t entries; // Number of items on the page331uint16_t hf_offset; // High free byte page offset332uint8_t level; // Btree page level333PageType type; // Page type335static constexpr int64_t SIZE = 26; // The header is 26 bytes337uint32_t expected_page_num;338bool other_endian;340PageHeader(uint32_t page_num, bool other_endian) : expected_page_num(page_num), other_endian(other_endian) {}341PageHeader() = delete;343template <typename Stream>344void Unserialize(Stream& s)345{346s >> lsn_file;347s >> lsn_offset;348s >> page_num;349s >> prev_page;350s >> next_page;351s >> entries;352s >> hf_offset;353s >> level;355uint8_t uint8_type;356s >> uint8_type;357type = static_cast<PageType>(uint8_type);359if (other_endian) {360lsn_file = internal_bswap_32(lsn_file);361lsn_offset = internal_bswap_32(lsn_offset);362page_num = internal_bswap_32(page_num);363prev_page = internal_bswap_32(prev_page);364next_page = internal_bswap_32(next_page);365entries = internal_bswap_16(entries);366hf_offset = internal_bswap_16(hf_offset);367}369if (expected_page_num != page_num) {370throw std::runtime_error("Page number mismatch");371}372if ((type != PageType::OVERFLOW_DATA && level < 1) || (type == PageType::OVERFLOW_DATA && level != 0)) {373throw std::runtime_error("Bad btree level");374}375}376};378/** A page of records in the database */379class RecordsPage380{381public:382RecordsPage(const PageHeader& header) : m_header(header) {}383RecordsPage() = delete;385PageHeader m_header;387std::vector<uint16_t> indexes;388std::vector<std::variant<DataRecord, OverflowRecord>> records;390template <typename Stream>391void Unserialize(Stream& s)392{393// Current position within the page394int64_t pos = PageHeader::SIZE;396// Get the items397for (uint32_t i = 0; i < m_header.entries; ++i) {398// Get the index399uint16_t index;400s >> index;401if (m_header.other_endian) {402index = internal_bswap_16(index);403}404indexes.push_back(index);405pos += sizeof(uint16_t);407// Go to the offset from the index408int64_t to_jump = index - pos;409if (to_jump < 0) {410throw std::runtime_error("Data record position not in page");411}412s.ignore(to_jump);414// Read the record415RecordHeader rec_hdr(m_header.other_endian);416s >> rec_hdr;417to_jump += RecordHeader::SIZE;419switch (rec_hdr.type) {420case RecordType::KEYDATA: {421DataRecord record(rec_hdr);422s >> record;423records.emplace_back(record);424to_jump += rec_hdr.len;425break;426}427case RecordType::OVERFLOW_DATA: {428OverflowRecord record(rec_hdr);429s >> record;430records.emplace_back(record);431to_jump += OverflowRecord::SIZE;432break;433}434default:435throw std::runtime_error("Unknown record type in records page");436}438// Go back to the indexes439s.seek(-to_jump, SEEK_CUR);440}441}442};444/** A page containing overflow data */445class OverflowPage446{447public:448OverflowPage(const PageHeader& header) : m_header(header) {}449OverflowPage() = delete;451PageHeader m_header;453// BDB overloads some page fields to store overflow page data454// hf_offset contains the length of the overflow data stored on this page455// entries contains a reference count for references to this item457// The overflow data itself. Begins immediately following header458std::vector<std::byte> data;460template <typename Stream>461void Unserialize(Stream& s)462{463data.resize(m_header.hf_offset);464s.read(std::as_writable_bytes(std::span(data.data(), data.size())));465}466};468/** A page of records in the database */469class InternalPage470{471public:472InternalPage(const PageHeader& header) : m_header(header) {}473InternalPage() = delete;475PageHeader m_header;477std::vector<uint16_t> indexes;478std::vector<InternalRecord> records;480template <typename Stream>481void Unserialize(Stream& s)482{483// Current position within the page484int64_t pos = PageHeader::SIZE;486// Get the items487for (uint32_t i = 0; i < m_header.entries; ++i) {488// Get the index489uint16_t index;490s >> index;491if (m_header.other_endian) {492index = internal_bswap_16(index);493}494indexes.push_back(index);495pos += sizeof(uint16_t);497// Go to the offset from the index498int64_t to_jump = index - pos;499if (to_jump < 0) {500throw std::runtime_error("Internal record position not in page");