BadgerDB
 All Classes Namespaces Functions Variables Typedefs Friends Pages
page.cpp
1 
8 #include <cassert>
9 
10 #include "exceptions/insufficient_space_exception.h"
11 #include "exceptions/invalid_record_exception.h"
12 #include "exceptions/invalid_slot_exception.h"
13 #include "exceptions/slot_in_use_exception.h"
14 #include "page_iterator.h"
15 #include "page.h"
16 
17 namespace badgerdb {
18 
20  initialize();
21 }
22 
23 void Page::initialize() {
24  header_.free_space_lower_bound = 0;
26  header_.num_slots = 0;
27  header_.num_free_slots = 0;
30  data_.assign(DATA_SIZE, char());
31 }
32 
33 RecordId Page::insertRecord(const std::string& record_data) {
34  if (!hasSpaceForRecord(record_data)) {
36  page_number(), record_data.length(), getFreeSpace());
37  }
38  const SlotId slot_number = getAvailableSlot();
39  insertRecordInSlot(slot_number, record_data);
40  return {page_number(), slot_number};
41 }
42 
43 std::string Page::getRecord(const RecordId& record_id) const {
44  validateRecordId(record_id);
45  const PageSlot& slot = getSlot(record_id.slot_number);
46  return data_.substr(slot.item_offset, slot.item_length);
47 }
48 
49 void Page::updateRecord(const RecordId& record_id,
50  const std::string& record_data) {
51  validateRecordId(record_id);
52  const PageSlot* slot = getSlot(record_id.slot_number);
53  const std::size_t free_space_after_delete =
54  getFreeSpace() + slot->item_length;
55  if (record_data.length() > free_space_after_delete) {
57  page_number(), record_data.length(), free_space_after_delete);
58  }
59  // We have to disallow slot compaction here because we're going to place the
60  // record data in the same slot, and compaction might delete the slot if we
61  // permit it.
62  deleteRecord(record_id, false /* allow_slot_compaction */);
63  insertRecordInSlot(record_id.slot_number, record_data);
64 }
65 
66 void Page::deleteRecord(const RecordId& record_id) {
67  deleteRecord(record_id, true /* allow_slot_compaction */);
68 }
69 
70 void Page::deleteRecord(const RecordId& record_id,
71  const bool allow_slot_compaction) {
72  validateRecordId(record_id);
73  PageSlot* slot = getSlot(record_id.slot_number);
74  data_.replace(slot->item_offset, slot->item_length, slot->item_length, '\0');
75 
76  // Compact the data by removing the hole left by this record (if necessary).
77  std::uint16_t move_offset = slot->item_offset;
78  std::size_t move_bytes = 0;
79  for (SlotId i = 1; i <= header_.num_slots; ++i) {
80  PageSlot* other_slot = getSlot(i);
81  if (other_slot->used && other_slot->item_offset < slot->item_offset) {
82  if (other_slot->item_offset < move_offset) {
83  move_offset = other_slot->item_offset;
84  }
85  move_bytes += other_slot->item_length;
86  // Update the slot for the other data to reflect the soon-to-be-new
87  // location.
88  other_slot->item_offset += slot->item_length;
89  }
90  }
91  // If we have data to move, shift it to the right.
92  if (move_bytes > 0) {
93  const std::string& data_to_move = data_.substr(move_offset, move_bytes);
94  data_.replace(move_offset + slot->item_length, move_bytes, data_to_move);
95  }
96  header_.free_space_upper_bound += slot->item_length;
97 
98  // Mark slot as unused.
99  slot->used = false;
100  slot->item_offset = 0;
101  slot->item_length = 0;
102  ++header_.num_free_slots;
103 
104  if (allow_slot_compaction && record_id.slot_number == header_.num_slots) {
105  // Last slot in the list, so we need to free any unused slots that are at
106  // the end of the slot list.
107  int num_slots_to_delete = 1;
108  for (SlotId i = 1; i < header_.num_slots; ++i) {
109  // Traverse list backwards, looking for unused slots.
110  const PageSlot* other_slot = getSlot(header_.num_slots - i);
111  if (!other_slot->used) {
112  ++num_slots_to_delete;
113  } else {
114  // Stop at the first used slot we find, since we can't move used slots
115  // without affecting record IDs.
116  break;
117  }
118  }
119  header_.num_slots -= num_slots_to_delete;
120  header_.num_free_slots -= num_slots_to_delete;
121  header_.free_space_lower_bound -= sizeof(PageSlot) * num_slots_to_delete;
122  }
123 }
124 
125 bool Page::hasSpaceForRecord(const std::string& record_data) const {
126  std::size_t record_size = record_data.length();
127  if (header_.num_free_slots == 0) {
128  record_size += sizeof(PageSlot);
129  }
130  return record_size <= getFreeSpace();
131 }
132 
133 PageSlot* Page::getSlot(const SlotId slot_number) {
134  return reinterpret_cast<PageSlot*>(
135  &data_[(slot_number - 1) * sizeof(PageSlot)]);
136 }
137 
138 const PageSlot& Page::getSlot(const SlotId slot_number) const {
139  return *reinterpret_cast<const PageSlot*>(
140  &data_[(slot_number - 1) * sizeof(PageSlot)]);
141 }
142 
143 SlotId Page::getAvailableSlot() {
144  SlotId slot_number = INVALID_SLOT;
145  if (header_.num_free_slots > 0) {
146  // Have an allocated but unused slot that we can reuse.
147  for (SlotId i = 1; i <= header_.num_slots; ++i) {
148  const PageSlot* slot = getSlot(i);
149  if (!slot->used) {
150  // We don't decrement the number of free slots until someone actually
151  // puts data in the slot.
152  slot_number = i;
153  break;
154  }
155  }
156  } else {
157  // Have to allocate a new slot.
158  slot_number = header_.num_slots + 1;
159  ++header_.num_slots;
160  ++header_.num_free_slots;
161  header_.free_space_lower_bound = sizeof(PageSlot) * header_.num_slots;
162  }
163  assert(slot_number != INVALID_SLOT);
164  return static_cast<SlotId>(slot_number);
165 }
166 
167 void Page::insertRecordInSlot(const SlotId slot_number,
168  const std::string& record_data) {
169  if (slot_number > header_.num_slots ||
170  slot_number == INVALID_SLOT) {
171  throw InvalidSlotException(page_number(), slot_number);
172  }
173  PageSlot* slot = getSlot(slot_number);
174  if (slot->used) {
175  throw SlotInUseException(page_number(), slot_number);
176  }
177  const int record_length = record_data.length();
178  slot->used = true;
179  slot->item_length = record_length;
180  slot->item_offset = header_.free_space_upper_bound - record_length;
181  header_.free_space_upper_bound = slot->item_offset;
182  --header_.num_free_slots;
183  data_.replace(slot->item_offset, slot->item_length, record_data);
184 }
185 
186 void Page::validateRecordId(const RecordId& record_id) const {
187  if (record_id.page_number != page_number()) {
188  throw InvalidRecordException(record_id, page_number());
189  }
190  const PageSlot& slot = getSlot(record_id.slot_number);
191  if (!slot.used) {
192  throw InvalidRecordException(record_id, page_number());
193  }
194 }
195 
197  return PageIterator(this);
198 }
199 
201  const RecordId& end_record_id = {page_number(), Page::INVALID_SLOT};
202  return PageIterator(this, end_record_id);
203 }
204 
205 }
std::uint16_t item_offset
Definition: page.h:87
SlotId num_free_slots
Definition: page.h:48
SlotId num_slots
Definition: page.h:43
void updateRecord(const RecordId &record_id, const std::string &record_data)
Definition: page.cpp:49
std::string getRecord(const RecordId &record_id) const
Definition: page.cpp:43
PageId next_page_number
Definition: page.h:58
std::uint16_t free_space_upper_bound
Definition: page.h:36
Iterator for iterating over the records in a page.
Definition: page_iterator.h:23
static const std::size_t DATA_SIZE
Definition: page.h:118
std::uint16_t free_space_lower_bound
Definition: page.h:30
std::uint16_t item_length
Definition: page.h:92
SlotId slot_number
Definition: types.h:39
PageId page_number() const
Definition: page.h:193
PageIterator begin()
Definition: page.cpp:196
PageIterator end()
Definition: page.cpp:200
Slot metadata that tracks where a record is in the data space.
Definition: page.h:77
Identifier for a record in a page.
Definition: types.h:30
static const SlotId INVALID_SLOT
Definition: page.h:128
std::uint16_t getFreeSpace() const
Definition: page.h:185
static const PageId INVALID_NUMBER
Definition: page.h:123
An exception that is thrown when a record is attempted to be inserted into a page that doesn't have s...
std::uint16_t SlotId
Identifier for a slot in a page.
Definition: types.h:20
bool hasSpaceForRecord(const std::string &record_data) const
Definition: page.cpp:125
PageId current_page_number
Definition: page.h:53
void deleteRecord(const RecordId &record_id)
Definition: page.cpp:66
RecordId insertRecord(const std::string &record_data)
Definition: page.cpp:33