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