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 | |