BadgerDB
|
00001 00008 #include <cassert> 00009 00010 #include <iostream> 00011 #include "exceptions/insufficient_space_exception.h" 00012 #include "exceptions/invalid_record_exception.h" 00013 #include "exceptions/invalid_slot_exception.h" 00014 #include "exceptions/slot_in_use_exception.h" 00015 #include "page_iterator.h" 00016 #include "page.h" 00017 #include "string.h" 00018 00019 namespace badgerdb { 00020 00021 Page::Page() { 00022 initialize(); 00023 } 00024 00025 void Page::initialize() { 00026 header_.free_space_lower_bound = 0; 00027 header_.free_space_upper_bound = DATA_SIZE; 00028 header_.num_slots = 0; 00029 header_.num_free_slots = 0; 00030 header_.current_page_number = INVALID_NUMBER; 00031 header_.next_page_number = INVALID_NUMBER; 00032 //data_.assign(DATA_SIZE, char()); 00033 memset(data_, '\0', DATA_SIZE); 00034 } 00035 00036 RecordId Page::insertRecord(const std::string& record_data) { 00037 if (!hasSpaceForRecord(record_data)) { 00038 throw InsufficientSpaceException( 00039 page_number(), record_data.length(), getFreeSpace()); 00040 } 00041 const SlotId slot_number = getAvailableSlot(); 00042 insertRecordInSlot(slot_number, record_data); 00043 return {page_number(), slot_number}; 00044 } 00045 00046 std::string Page::getRecord(const RecordId& record_id) const { 00047 validateRecordId(record_id); 00048 const PageSlot& slot = getSlot(record_id.slot_number); 00049 std::string retStr = std::string(data_, DATA_SIZE).substr(slot.item_offset, slot.item_length); 00050 00051 return retStr; 00052 } 00053 00054 void Page::updateRecord(const RecordId& record_id, 00055 const std::string& record_data) { 00056 validateRecordId(record_id); 00057 const PageSlot* slot = getSlot(record_id.slot_number); 00058 const std::size_t free_space_after_delete = 00059 getFreeSpace() + slot->item_length; 00060 if (record_data.length() > free_space_after_delete) { 00061 throw InsufficientSpaceException( 00062 page_number(), record_data.length(), free_space_after_delete); 00063 } 00064 // We have to disallow slot compaction here because we're going to place the 00065 // record data in the same slot, and compaction might delete the slot if we 00066 // permit it. 00067 deleteRecord(record_id, false /* allow_slot_compaction */); 00068 insertRecordInSlot(record_id.slot_number, record_data); 00069 } 00070 00071 void Page::deleteRecord(const RecordId& record_id) { 00072 deleteRecord(record_id, true /* allow_slot_compaction */); 00073 } 00074 00075 void Page::deleteRecord(const RecordId& record_id, 00076 const bool allow_slot_compaction) { 00077 validateRecordId(record_id); 00078 PageSlot* slot = getSlot(record_id.slot_number); 00079 00080 for(int i = 0; i < slot->item_length; i++) 00081 data_[i + slot->item_offset] = '\0'; 00082 00083 //data_.replace(slot->item_offset, slot->item_length, slot->item_length, '\0'); 00084 00085 // Compact the data by removing the hole left by this record (if necessary). 00086 std::uint16_t move_offset = slot->item_offset; 00087 std::size_t move_bytes = 0; 00088 for (SlotId i = 1; i <= header_.num_slots; ++i) { 00089 PageSlot* other_slot = getSlot(i); 00090 if (other_slot->used && other_slot->item_offset < slot->item_offset) { 00091 if (other_slot->item_offset < move_offset) { 00092 move_offset = other_slot->item_offset; 00093 } 00094 move_bytes += other_slot->item_length; 00095 // Update the slot for the other data to reflect the soon-to-be-new 00096 // location. 00097 other_slot->item_offset += slot->item_length; 00098 } 00099 } 00100 // If we have data to move, shift it to the right. 00101 if (move_bytes > 0) { 00102 const std::string& data_to_move = std::string(data_, DATA_SIZE).substr(move_offset, move_bytes); 00103 00104 for(std::uint16_t i = 0; i < move_bytes; i++) 00105 data_[i + move_offset + slot->item_length] = data_to_move[i]; 00106 00107 //data_.replace(move_offset + slot->item_length, move_bytes, data_to_move); 00108 } 00109 header_.free_space_upper_bound += slot->item_length; 00110 00111 // Mark slot as unused. 00112 slot->used = false; 00113 slot->item_offset = 0; 00114 slot->item_length = 0; 00115 ++header_.num_free_slots; 00116 00117 if (allow_slot_compaction && record_id.slot_number == header_.num_slots) { 00118 // Last slot in the list, so we need to free any unused slots that are at 00119 // the end of the slot list. 00120 int num_slots_to_delete = 1; 00121 for (SlotId i = 1; i < header_.num_slots; ++i) { 00122 // Traverse list backwards, looking for unused slots. 00123 const PageSlot* other_slot = getSlot(header_.num_slots - i); 00124 if (!other_slot->used) { 00125 ++num_slots_to_delete; 00126 } else { 00127 // Stop at the first used slot we find, since we can't move used slots 00128 // without affecting record IDs. 00129 break; 00130 } 00131 } 00132 header_.num_slots -= num_slots_to_delete; 00133 header_.num_free_slots -= num_slots_to_delete; 00134 header_.free_space_lower_bound -= sizeof(PageSlot) * num_slots_to_delete; 00135 } 00136 } 00137 00138 bool Page::hasSpaceForRecord(const std::string& record_data) const { 00139 std::size_t record_size = record_data.length(); 00140 if (header_.num_free_slots == 0) { 00141 record_size += sizeof(PageSlot); 00142 } 00143 return record_size <= getFreeSpace(); 00144 } 00145 00146 PageSlot* Page::getSlot(const SlotId slot_number) { 00147 return reinterpret_cast<PageSlot*>(&data_[(slot_number - 1) * sizeof(PageSlot)]); 00148 } 00149 00150 const PageSlot& Page::getSlot(const SlotId slot_number) const { 00151 return *reinterpret_cast<const PageSlot*>(&data_[(slot_number - 1) * sizeof(PageSlot)]); 00152 } 00153 00154 SlotId Page::getAvailableSlot() { 00155 SlotId slot_number = INVALID_SLOT; 00156 if (header_.num_free_slots > 0) { 00157 // Have an allocated but unused slot that we can reuse. 00158 for (SlotId i = 1; i <= header_.num_slots; ++i) { 00159 const PageSlot* slot = getSlot(i); 00160 if (!slot->used) { 00161 // We don't decrement the number of free slots until someone actually 00162 // puts data in the slot. 00163 slot_number = i; 00164 break; 00165 } 00166 } 00167 } else { 00168 // Have to allocate a new slot. 00169 slot_number = header_.num_slots + 1; 00170 ++header_.num_slots; 00171 ++header_.num_free_slots; 00172 header_.free_space_lower_bound = sizeof(PageSlot) * header_.num_slots; 00173 } 00174 assert(slot_number != INVALID_SLOT); 00175 return static_cast<SlotId>(slot_number); 00176 } 00177 00178 void Page::insertRecordInSlot(const SlotId slot_number, 00179 const std::string& record_data) { 00180 if (slot_number > header_.num_slots || 00181 slot_number == INVALID_SLOT) { 00182 throw InvalidSlotException(page_number(), slot_number); 00183 } 00184 PageSlot* slot = getSlot(slot_number); 00185 if (slot->used) { 00186 throw SlotInUseException(page_number(), slot_number); 00187 } 00188 const int record_length = record_data.length(); 00189 slot->used = true; 00190 slot->item_length = record_length; 00191 slot->item_offset = header_.free_space_upper_bound - record_length; 00192 header_.free_space_upper_bound = slot->item_offset; 00193 --header_.num_free_slots; 00194 00195 for(int i = 0; i < slot->item_length; i++) 00196 data_[i + slot->item_offset] = record_data[i]; 00197 00198 //data_.replace(slot->item_offset, slot->item_length, record_data); 00199 } 00200 00201 void Page::validateRecordId(const RecordId& record_id) const { 00202 if (record_id.page_number != page_number()) { 00203 throw InvalidRecordException(record_id, page_number()); 00204 } 00205 const PageSlot& slot = getSlot(record_id.slot_number); 00206 if (!slot.used) { 00207 throw InvalidRecordException(record_id, page_number()); 00208 } 00209 } 00210 00211 PageIterator Page::begin() { 00212 return PageIterator(this); 00213 } 00214 00215 PageIterator Page::end() { 00216 const RecordId& end_record_id = {page_number(), Page::INVALID_SLOT}; 00217 return PageIterator(this, end_record_id); 00218 } 00219 00220 }