BadgerDB
/afs/cs.wisc.edu/p/course/cs564-jignesh/public/html/projects/BadgerDB/bufmgr/src/page.cpp
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   // We have to disallow slot compaction here because we're going to place the
00060   // record data in the same slot, and compaction might delete the slot if we
00061   // permit it.
00062   deleteRecord(record_id, false /* allow_slot_compaction */);
00063   insertRecordInSlot(record_id.slot_number, record_data);
00064 }
00065 
00066 void Page::deleteRecord(const RecordId& record_id) {
00067   deleteRecord(record_id, true /* allow_slot_compaction */);
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   // Compact the data by removing the hole left by this record (if necessary).
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       // Update the slot for the other data to reflect the soon-to-be-new
00087       // location.
00088       other_slot->item_offset += slot->item_length;
00089     }
00090   }
00091   // If we have data to move, shift it to the right.
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   // Mark slot as unused.
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     // Last slot in the list, so we need to free any unused slots that are at
00106     // the end of the slot list.
00107     int num_slots_to_delete = 1;
00108     for (SlotId i = 1; i < header_.num_slots; ++i) {
00109       // Traverse list backwards, looking for unused slots.
00110       const PageSlot* other_slot = getSlot(header_.num_slots - i);
00111       if (!other_slot->used) {
00112         ++num_slots_to_delete;
00113       } else {
00114         // Stop at the first used slot we find, since we can't move used slots
00115         // without affecting record IDs.
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     // Have an allocated but unused slot that we can reuse.
00147     for (SlotId i = 1; i <= header_.num_slots; ++i) {
00148       const PageSlot* slot = getSlot(i);
00149       if (!slot->used) {
00150         // We don't decrement the number of free slots until someone actually
00151         // puts data in the slot.
00152         slot_number = i;
00153         break;
00154       }
00155     }
00156   } else {
00157     // Have to allocate a new slot.
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 }
 All Classes Namespaces Functions Variables Typedefs Friends