BadgerDB
/afs/cs.wisc.edu/u/m/o/mohan/private/cs564/btree/btree/src/page.cpp
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 }
 All Classes Namespaces Functions Variables Typedefs Enumerations Friends