| 1 | // Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | // Licensed under the MIT license. |
| 3 | |
| 4 | #pragma once |
| 5 | |
| 6 | #include <cassert> |
| 7 | #include <cstdint> |
| 8 | #include "address.h" |
| 9 | #include "auto_ptr.h" |
| 10 | |
| 11 | namespace FASTER { |
| 12 | namespace core { |
| 13 | |
| 14 | /// Record header, internal to FASTER. |
| 15 | class RecordInfo { |
| 16 | public: |
| 17 | RecordInfo(uint16_t checkpoint_version_, bool final_bit_, bool tombstone_, bool invalid_, |
| 18 | Address previous_address) |
| 19 | : checkpoint_version{ checkpoint_version_ } |
| 20 | , final_bit{ final_bit_ } |
| 21 | , tombstone{ tombstone_ } |
| 22 | , invalid{ invalid_ } |
| 23 | , previous_address_{ previous_address.control() } { |
| 24 | } |
| 25 | |
| 26 | RecordInfo(const RecordInfo& other) |
| 27 | : control_{ other.control_ } { |
| 28 | } |
| 29 | |
| 30 | inline bool IsNull() const { |
| 31 | return control_ == 0; |
| 32 | } |
| 33 | inline Address previous_address() const { |
| 34 | return Address{ previous_address_ }; |
| 35 | } |
| 36 | |
| 37 | union { |
| 38 | struct { |
| 39 | uint64_t previous_address_ : 48; |
| 40 | uint64_t checkpoint_version : 13; |
| 41 | uint64_t invalid : 1; |
| 42 | uint64_t tombstone : 1; |
| 43 | uint64_t final_bit : 1; |
| 44 | }; |
| 45 | |
| 46 | uint64_t control_; |
| 47 | }; |
| 48 | }; |
| 49 | static_assert(sizeof(RecordInfo) == 8, "sizeof(RecordInfo) != 8" ); |
| 50 | |
| 51 | /// A record stored in the log. The log starts at 0 (mod 64), and consists of Records, one after |
| 52 | /// the other. Each record's header is 8 bytes. |
| 53 | template <class key_t, class value_t> |
| 54 | struct Record { |
| 55 | // To support records with alignment > 64, modify the persistent-memory allocator to allocate |
| 56 | // a larger NULL page on startup. |
| 57 | static_assert(alignof(key_t) <= Constants::kCacheLineBytes, |
| 58 | "alignof(key_t) > Constants::kCacheLineBytes)" ); |
| 59 | static_assert(alignof(value_t) <= Constants::kCacheLineBytes, |
| 60 | "alignof(value_t) > Constants::kCacheLineBytes)" ); |
| 61 | |
| 62 | /// For placement new() operator. Can't set value, since it might be set by value = input (for |
| 63 | /// upsert), or rmw_initial(...) (for RMW). |
| 64 | Record(RecordInfo , const key_t& key_) |
| 65 | : header{ header_ } { |
| 66 | void* buffer = const_cast<key_t*>(&key()); |
| 67 | new(buffer)key_t{ key_ }; |
| 68 | } |
| 69 | |
| 70 | /// Key appears immediately after record header (subject to alignment padding). Keys are |
| 71 | /// immutable. |
| 72 | inline constexpr const key_t& key() const { |
| 73 | const uint8_t* head = reinterpret_cast<const uint8_t*>(this); |
| 74 | size_t offset = pad_alignment(sizeof(RecordInfo), alignof(key_t)); |
| 75 | return *reinterpret_cast<const key_t*>(head + offset); |
| 76 | } |
| 77 | |
| 78 | /// Value appears immediately after key (subject to alignment padding). Values can be modified. |
| 79 | inline constexpr const value_t& value() const { |
| 80 | const uint8_t* head = reinterpret_cast<const uint8_t*>(this); |
| 81 | size_t offset = pad_alignment(key().size() + |
| 82 | pad_alignment(sizeof(RecordInfo), alignof(key_t)), |
| 83 | alignof(value_t)); |
| 84 | return *reinterpret_cast<const value_t*>(head + offset); |
| 85 | } |
| 86 | inline constexpr value_t& value() { |
| 87 | uint8_t* head = reinterpret_cast<uint8_t*>(this); |
| 88 | size_t offset = pad_alignment(key().size() + |
| 89 | pad_alignment(sizeof(RecordInfo), alignof(key_t)), |
| 90 | alignof(value_t)); |
| 91 | return *reinterpret_cast<value_t*>(head + offset); |
| 92 | } |
| 93 | |
| 94 | /// Size of a record to be created, in memory. (Includes padding, if any, after the value, so |
| 95 | /// that the next record stored in the log is properly aligned.) |
| 96 | static inline constexpr uint32_t size(const key_t& key_, uint32_t value_size) { |
| 97 | return static_cast<uint32_t>( |
| 98 | // --plus Value size, all padded to Header alignment. |
| 99 | pad_alignment(value_size + |
| 100 | // --plus Key size, all padded to Value alignment. |
| 101 | pad_alignment(key_.size() + |
| 102 | // Header, padded to Key alignment. |
| 103 | pad_alignment(sizeof(RecordInfo), alignof(key_t)), |
| 104 | alignof(value_t)), |
| 105 | alignof(RecordInfo))); |
| 106 | } |
| 107 | /// Size of the existing record, in memory. (Includes padding, if any, after the value.) |
| 108 | inline constexpr uint32_t size() const { |
| 109 | return size(key(), value().size()); |
| 110 | } |
| 111 | |
| 112 | /// Minimum size of a read from disk that is guaranteed to include the record's header + whatever |
| 113 | /// information class key_t needs to determine its key size. |
| 114 | static inline constexpr uint32_t min_disk_key_size() { |
| 115 | return static_cast<uint32_t>( |
| 116 | // -- plus sizeof(key_t). |
| 117 | sizeof(key_t) + |
| 118 | // Header size, padded to Key alignment. |
| 119 | pad_alignment(sizeof(RecordInfo), alignof(key_t))); |
| 120 | } |
| 121 | |
| 122 | /// Minimum size of a read from disk that is guaranteed to include the record's header, key, |
| 123 | // and whatever information the host needs to determine the value size. |
| 124 | inline constexpr uint32_t min_disk_value_size() const { |
| 125 | return static_cast<uint32_t>( |
| 126 | // -- plus size of the Value's header. |
| 127 | sizeof(value_t) + |
| 128 | // --plus Key size, padded to Base Value alignment. |
| 129 | pad_alignment(key().size() + |
| 130 | // Header, padded to Key alignment. |
| 131 | pad_alignment(sizeof(RecordInfo), alignof(key_t)), |
| 132 | alignof(value_t)) |
| 133 | ); |
| 134 | } |
| 135 | |
| 136 | /// Size of a record, on disk. (Excludes padding, if any, after the value.) |
| 137 | inline constexpr uint32_t disk_size() const { |
| 138 | return static_cast<uint32_t>(value().size() + |
| 139 | pad_alignment(key().size() + |
| 140 | // Header, padded to Key alignment. |
| 141 | pad_alignment(sizeof(RecordInfo), alignof(key_t)), |
| 142 | alignof(value_t))); |
| 143 | } |
| 144 | |
| 145 | public: |
| 146 | RecordInfo ; |
| 147 | }; |
| 148 | |
| 149 | } |
| 150 | } // namespace FASTER::core |
| 151 | |