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
11namespace FASTER {
12namespace core {
13
14/// Record header, internal to FASTER.
15class 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};
49static_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.
53template <class key_t, class value_t>
54struct 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 header_, 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 header;
147};
148
149}
150} // namespace FASTER::core
151