hashtable_database_writer::hashtable_database_writer(mmfile& file) : file_(file) { BITCOIN_ASSERT(file_.data() != nullptr); // [ version ] 8 // [ buckets ] 8 // [ values size ] 8 // ... // ... BITCOIN_ASSERT(file_.size() > 24); auto deserial = make_deserializer(file_.data(), file_.data() + 24); version_ = deserial.read_8_bytes(); buckets_ = deserial.read_8_bytes(); total_records_size_ = deserial.read_8_bytes(); BITCOIN_ASSERT(version_ == 1); const size_t header_size = 24 + buckets_ * 8; BITCOIN_ASSERT(file_.size() >= header_size + total_records_size_); // Advise the kernel that our access patterns for the tx records // will be random without pattern. //madvise(file_.data(), 24, POSIX_MADV_DONTNEED); //madvise(file_.data() + 24, buckets_ * 8, // POSIX_MADV_WILLNEED | POSIX_MADV_RANDOM); //madvise(file_.data() + header_size, file_.size() - header_size, // POSIX_MADV_DONTNEED | POSIX_MADV_RANDOM); }
void receive_history_result(const data_chunk& data, blockchain::fetch_handler_history handle_fetch) { BITCOIN_ASSERT(data.size() >= 4); std::error_code ec; auto deserial = make_deserializer(data.begin(), data.end()); if (!read_error_code(deserial, data.size(), ec)) return; BITCOIN_ASSERT(deserial.iterator() == data.begin() + 4); size_t row_size = 36 + 4 + 8 + 36 + 4; if ((data.size() - 4) % row_size != 0) { log_error() << "Malformed response for *.fetch_history"; return; } size_t number_rows = (data.size() - 4) / row_size; blockchain::history_list history(number_rows); for (size_t i = 0; i < history.size(); ++i) { blockchain::history_row& row = history[i]; row.output.hash = deserial.read_hash(); row.output.index = deserial.read_4_bytes(); row.output_height = deserial.read_4_bytes(); row.value = deserial.read_8_bytes(); row.spend.hash = deserial.read_hash(); row.spend.index = deserial.read_4_bytes(); row.spend_height = deserial.read_4_bytes(); } BITCOIN_ASSERT(deserial.iterator() == data.end()); handle_fetch(ec, history); }
network_address_type read_network_address() { network_address_type addr; addr.services = read_8_bytes(); // Read IP address read_bytes<16>(iter_, end_, addr.ip); addr.port = read_data_impl<uint16_t>(iter_, end_, true); return addr; }
uint64_t hashtable_database_writer::read_bucket_value(uint64_t bucket_index) { BITCOIN_ASSERT(file_.size() > 24 + buckets_ * 8); BITCOIN_ASSERT(bucket_index < buckets_); uint8_t* bucket_begin = file_.data() + bucket_offset(bucket_index); // Read current record stored in the bucket. auto deserial = make_deserializer(bucket_begin, bucket_begin + 8); return deserial.read_8_bytes(); }
uint64_t read_variable_uint() { uint8_t length = read_byte(); uint64_t value = 0; if (length < 0xfd) value = length; else if (length == 0xfd) value += read_2_bytes(); else if (length == 0xfe) value += read_4_bytes(); else if (length == 0xff) value += read_8_bytes(); return value; }
const hashtable_database_reader::get_result hashtable_database_reader::get( const hash_digest& key_hash) const { uint64_t bucket_index = remainder(key_hash.data(), writer_.buckets()); BITCOIN_ASSERT(bucket_index < writer_.buckets()); uint64_t record_offset = read_record_offset(file_.data(), bucket_index); const uint64_t header_size = 24 + writer_.buckets() * 8; const uint8_t* all_records_begin = file_.data() + header_size; const uint8_t* all_records_end = all_records_begin + writer_.records_size(); const uint8_t* record_begin = all_records_begin + record_offset; // We don't know the end of a record, so we use the end of all records // for the deserializer. // We will be jumping around the records since it's a chained // list per bucket. // Begin iterating the list. while (true) { auto deserial = make_deserializer(record_begin, all_records_end); const hash_digest current_hash = deserial.read_hash(); uint64_t value_size = deserial.read_variable_uint(); if (current_hash != key_hash) { // Move to next record in bucket. // Skip the transaction data. deserial.set_iterator(deserial.iterator() + value_size); uint64_t next_record = deserial.read_8_bytes(); if (next_record == record_doesnt_exist) return {nullptr, nullptr}; record_begin = all_records_begin + next_record; continue; } // We have the record! return {deserial.iterator(), deserial.iterator() + value_size}; } BITCOIN_ASSERT_MSG(false, "Broke out of unbreakable loop!"); return {nullptr, nullptr}; }
void history_scan_database::scan(const address_bitset& key, read_function read_func, size_t from_height) const { BITCOIN_ASSERT(key.size() >= settings_.sharded_bitsize); const hsdb_shard& shard = lookup(key); address_bitset sub_key = drop_prefix(key); auto read_wrapped = [&read_func](const uint8_t* data) { auto deserial = make_deserializer_unsafe(data); history_row row{ // output or spend? marker_to_id(deserial.read_byte()), // point deserial.read_hash(), deserial.read_4_bytes(), // height deserial.read_4_bytes(), // value or checksum deserial.read_8_bytes()}; read_func(row); }; shard.scan(sub_key, read_wrapped, from_height); }
uint64_t read_record_offset(const uint8_t* data, uint64_t bucket_index) { const uint8_t* bucket_begin = data + bucket_offset(bucket_index); auto deserial = make_deserializer(bucket_begin, bucket_begin + 8); return deserial.read_8_bytes(); }