00001
00008 #include <cassert>
00009
00010 #include "exceptions/insufficient_space_exception.h"
00011 #include "exceptions/invalid_record_exception.h"
00012 #include "exceptions/invalid_slot_exception.h"
00013 #include "exceptions/slot_in_use_exception.h"
00014 #include "page_iterator.h"
00015 #include "page.h"
00016
00017 namespace badgerdb {
00018
00019 Page::Page() {
00020 initialize();
00021 }
00022
00023 void Page::initialize() {
00024 header_.free_space_lower_bound = 0;
00025 header_.free_space_upper_bound = DATA_SIZE;
00026 header_.num_slots = 0;
00027 header_.num_free_slots = 0;
00028 header_.current_page_number = INVALID_NUMBER;
00029 header_.next_page_number = INVALID_NUMBER;
00030 data_.assign(DATA_SIZE, char());
00031 }
00032
00033 RecordId Page::insertRecord(const std::string& record_data) {
00034 if (!hasSpaceForRecord(record_data)) {
00035 throw InsufficientSpaceException(
00036 page_number(), record_data.length(), getFreeSpace());
00037 }
00038 const SlotId slot_number = getAvailableSlot();
00039 insertRecordInSlot(slot_number, record_data);
00040 return {page_number(), slot_number};
00041 }
00042
00043 std::string Page::getRecord(const RecordId& record_id) const {
00044 validateRecordId(record_id);
00045 const PageSlot& slot = getSlot(record_id.slot_number);
00046 return data_.substr(slot.item_offset, slot.item_length);
00047 }
00048
00049 void Page::updateRecord(const RecordId& record_id,
00050 const std::string& record_data) {
00051 validateRecordId(record_id);
00052 const PageSlot* slot = getSlot(record_id.slot_number);
00053 const std::size_t free_space_after_delete =
00054 getFreeSpace() + slot->item_length;
00055 if (record_data.length() > free_space_after_delete) {
00056 throw InsufficientSpaceException(
00057 page_number(), record_data.length(), free_space_after_delete);
00058 }
00059
00060
00061
00062 deleteRecord(record_id, false );
00063 insertRecordInSlot(record_id.slot_number, record_data);
00064 }
00065
00066 void Page::deleteRecord(const RecordId& record_id) {
00067 deleteRecord(record_id, true );
00068 }
00069
00070 void Page::deleteRecord(const RecordId& record_id,
00071 const bool allow_slot_compaction) {
00072 validateRecordId(record_id);
00073 PageSlot* slot = getSlot(record_id.slot_number);
00074 data_.replace(slot->item_offset, slot->item_length, slot->item_length, '\0');
00075
00076
00077 std::uint16_t move_offset = slot->item_offset;
00078 std::size_t move_bytes = 0;
00079 for (SlotId i = 1; i <= header_.num_slots; ++i) {
00080 PageSlot* other_slot = getSlot(i);
00081 if (other_slot->used && other_slot->item_offset < slot->item_offset) {
00082 if (other_slot->item_offset < move_offset) {
00083 move_offset = other_slot->item_offset;
00084 }
00085 move_bytes += other_slot->item_length;
00086
00087
00088 other_slot->item_offset += slot->item_length;
00089 }
00090 }
00091
00092 if (move_bytes > 0) {
00093 const std::string& data_to_move = data_.substr(move_offset, move_bytes);
00094 data_.replace(move_offset + slot->item_length, move_bytes, data_to_move);
00095 }
00096 header_.free_space_upper_bound += slot->item_length;
00097
00098
00099 slot->used = false;
00100 slot->item_offset = 0;
00101 slot->item_length = 0;
00102 ++header_.num_free_slots;
00103
00104 if (allow_slot_compaction && record_id.slot_number == header_.num_slots) {
00105
00106
00107 int num_slots_to_delete = 1;
00108 for (SlotId i = 1; i < header_.num_slots; ++i) {
00109
00110 const PageSlot* other_slot = getSlot(header_.num_slots - i);
00111 if (!other_slot->used) {
00112 ++num_slots_to_delete;
00113 } else {
00114
00115
00116 break;
00117 }
00118 }
00119 header_.num_slots -= num_slots_to_delete;
00120 header_.num_free_slots -= num_slots_to_delete;
00121 header_.free_space_lower_bound -= sizeof(PageSlot) * num_slots_to_delete;
00122 }
00123 }
00124
00125 bool Page::hasSpaceForRecord(const std::string& record_data) const {
00126 std::size_t record_size = record_data.length();
00127 if (header_.num_free_slots == 0) {
00128 record_size += sizeof(PageSlot);
00129 }
00130 return record_size <= getFreeSpace();
00131 }
00132
00133 PageSlot* Page::getSlot(const SlotId slot_number) {
00134 return reinterpret_cast<PageSlot*>(
00135 &data_[(slot_number - 1) * sizeof(PageSlot)]);
00136 }
00137
00138 const PageSlot& Page::getSlot(const SlotId slot_number) const {
00139 return *reinterpret_cast<const PageSlot*>(
00140 &data_[(slot_number - 1) * sizeof(PageSlot)]);
00141 }
00142
00143 SlotId Page::getAvailableSlot() {
00144 SlotId slot_number = INVALID_SLOT;
00145 if (header_.num_free_slots > 0) {
00146
00147 for (SlotId i = 1; i <= header_.num_slots; ++i) {
00148 const PageSlot* slot = getSlot(i);
00149 if (!slot->used) {
00150
00151
00152 slot_number = i;
00153 break;
00154 }
00155 }
00156 } else {
00157
00158 slot_number = header_.num_slots + 1;
00159 ++header_.num_slots;
00160 ++header_.num_free_slots;
00161 header_.free_space_lower_bound = sizeof(PageSlot) * header_.num_slots;
00162 }
00163 assert(slot_number != INVALID_SLOT);
00164 return static_cast<SlotId>(slot_number);
00165 }
00166
00167 void Page::insertRecordInSlot(const SlotId slot_number,
00168 const std::string& record_data) {
00169 if (slot_number > header_.num_slots ||
00170 slot_number == INVALID_SLOT) {
00171 throw InvalidSlotException(page_number(), slot_number);
00172 }
00173 PageSlot* slot = getSlot(slot_number);
00174 if (slot->used) {
00175 throw SlotInUseException(page_number(), slot_number);
00176 }
00177 const int record_length = record_data.length();
00178 slot->used = true;
00179 slot->item_length = record_length;
00180 slot->item_offset = header_.free_space_upper_bound - record_length;
00181 header_.free_space_upper_bound = slot->item_offset;
00182 --header_.num_free_slots;
00183 data_.replace(slot->item_offset, slot->item_length, record_data);
00184 }
00185
00186 void Page::validateRecordId(const RecordId& record_id) const {
00187 if (record_id.page_number != page_number()) {
00188 throw InvalidRecordException(record_id, page_number());
00189 }
00190 const PageSlot& slot = getSlot(record_id.slot_number);
00191 if (!slot.used) {
00192 throw InvalidRecordException(record_id, page_number());
00193 }
00194 }
00195
00196 PageIterator Page::begin() {
00197 return PageIterator(this);
00198 }
00199
00200 PageIterator Page::end() {
00201 const RecordId& end_record_id = {page_number(), Page::INVALID_SLOT};
00202 return PageIterator(this, end_record_id);
00203 }
00204
00205 }