BadgerDB
/afs/cs.wisc.edu/p/course/cs564-jignesh/public/html/projects/BadgerDB/bufmgr/src/file.cpp
00001 
00008 #include "file.h"
00009 
00010 #include <fstream>
00011 #include <iostream>
00012 #include <memory>
00013 #include <string>
00014 #include <cstdio>
00015 #include <cassert>
00016 
00017 #include "exceptions/file_exists_exception.h"
00018 #include "exceptions/file_not_found_exception.h"
00019 #include "exceptions/file_open_exception.h"
00020 #include "exceptions/invalid_page_exception.h"
00021 #include "file_iterator.h"
00022 #include "page.h"
00023 
00024 namespace badgerdb {
00025 
00026 File::StreamMap File::open_streams_;
00027 File::CountMap File::open_counts_;
00028 
00029 File File::create(const std::string& filename) {
00030   return File(filename, true /* create_new */);
00031 }
00032 
00033 File File::open(const std::string& filename) {
00034   return File(filename, false /* create_new */);
00035 }
00036 
00037 void File::remove(const std::string& filename) {
00038   if (!exists(filename)) {
00039     throw FileNotFoundException(filename);
00040   }
00041   if (isOpen(filename)) {
00042     throw FileOpenException(filename);
00043   }
00044   std::remove(filename.c_str());
00045 }
00046 
00047 bool File::isOpen(const std::string& filename) {
00048   if (!exists(filename)) {
00049     return false;
00050   }
00051   return open_counts_.find(filename) != open_counts_.end();
00052 }
00053 
00054 bool File::exists(const std::string& filename) {
00055   std::fstream file(filename);
00056   if(file)
00057   {
00058     file.close();
00059     return true;
00060   }
00061 
00062   return false;
00063 }
00064 
00065 File::File(const File& other)
00066   : filename_(other.filename_),
00067     stream_(open_streams_[filename_]) {
00068   ++open_counts_[filename_];
00069 }
00070 
00071 File& File::operator=(const File& rhs) {
00072   // This accounts for self-assignment and assignment of a File object for the
00073   // same file.
00074   close();  //close my file and associate me with the new one
00075   filename_ = rhs.filename_;
00076   openIfNeeded(false /* create_new */);
00077   return *this;
00078 }
00079 
00080 File::~File() {
00081   close();
00082 }
00083 
00084 Page File::allocatePage() {
00085   FileHeader header = readHeader();
00086   Page new_page;
00087   Page existing_page;
00088   if (header.num_free_pages > 0) {
00089     new_page = readPage(header.first_free_page, true /* allow_free */);
00090     new_page.set_page_number(header.first_free_page);
00091     header.first_free_page = new_page.next_page_number();
00092     --header.num_free_pages;
00093 
00094     if (header.first_used_page == Page::INVALID_NUMBER ||
00095         header.first_used_page > new_page.page_number()) {
00096       // Either have no pages used or the head of the used list is a page later
00097       // than the one we just allocated, so add the new page to the head.
00098       if (header.first_used_page > new_page.page_number()) {
00099         new_page.set_next_page_number(header.first_used_page);
00100       }
00101       header.first_used_page = new_page.page_number();
00102     } else {
00103       // New page is reused from somewhere after the beginning, so we need to
00104       // find where in the used list to insert it.
00105       PageId next_page_number = Page::INVALID_NUMBER;
00106       for (FileIterator iter = begin(); iter != end(); ++iter) {
00107         next_page_number = (*iter).next_page_number();
00108         if (next_page_number > new_page.page_number() ||
00109             next_page_number == Page::INVALID_NUMBER) {
00110           existing_page = *iter;
00111           break;
00112         }
00113       }
00114       existing_page.set_next_page_number(new_page.page_number());
00115       new_page.set_next_page_number(next_page_number);
00116     }
00117 
00118     assert((header.num_free_pages == 0) ==
00119            (header.first_free_page == Page::INVALID_NUMBER));
00120   } else {
00121     new_page.set_page_number(header.num_pages);
00122     if (header.first_used_page == Page::INVALID_NUMBER) {
00123       header.first_used_page = new_page.page_number();
00124     } else {
00125       // If we have pages allocated, we need to add the new page to the tail
00126       // of the linked list.
00127       for (FileIterator iter = begin(); iter != end(); ++iter) {
00128         if ((*iter).next_page_number() == Page::INVALID_NUMBER) {
00129           existing_page = *iter;
00130           break;
00131         }
00132       }
00133       assert(existing_page.isUsed());
00134       existing_page.set_next_page_number(new_page.page_number());
00135     }
00136     ++header.num_pages;
00137   }
00138   writePage(new_page.page_number(), new_page);
00139   if (existing_page.page_number() != Page::INVALID_NUMBER) {
00140     // If we updated an existing page by inserting the new page into the
00141     // used list, we need to write it out.
00142     writePage(existing_page.page_number(), existing_page);
00143   }
00144   writeHeader(header);
00145 
00146   return new_page;
00147 }
00148 
00149 Page File::readPage(const PageId page_number) const {
00150   FileHeader header = readHeader();
00151   if (page_number >= header.num_pages) {
00152     throw InvalidPageException(page_number, filename_);
00153   }
00154   return readPage(page_number, false /* allow_free */);
00155 }
00156 
00157 Page File::readPage(const PageId page_number, const bool allow_free) const {
00158   Page page;
00159   stream_->seekg(pagePosition(page_number), std::ios::beg);
00160   stream_->read(reinterpret_cast<char*>(&page.header_), sizeof(page.header_));
00161   stream_->read(reinterpret_cast<char*>(&page.data_[0]), Page::DATA_SIZE);
00162   if (!allow_free && !page.isUsed()) {
00163     throw InvalidPageException(page_number, filename_);
00164   }
00165 
00166   return page;
00167 }
00168 
00169 void File::writePage(const Page& new_page) {
00170   PageHeader header = readPageHeader(new_page.page_number());
00171   if (header.current_page_number == Page::INVALID_NUMBER) {
00172     // Page has been deleted since it was read.
00173     throw InvalidPageException(new_page.page_number(), filename_);
00174   }
00175   // Page on disk may have had its next page pointer updated since it was read;
00176   // we don't modify that, but we do keep all the other modifications to the
00177   // page header.
00178   const PageId next_page_number = header.next_page_number;
00179   header = new_page.header_;
00180   header.next_page_number = next_page_number;
00181   writePage(new_page.page_number(), header, new_page);
00182 }
00183 
00184 void File::deletePage(const PageId page_number) {
00185   FileHeader header = readHeader();
00186   Page existing_page = readPage(page_number);
00187   Page previous_page;
00188   // If this page is the head of the used list, update the header to point to
00189   // the next page in line.
00190   if (page_number == header.first_used_page) {
00191     header.first_used_page = existing_page.next_page_number();
00192   } else {
00193     // Walk the used list so we can update the page that points to this one.
00194     for (FileIterator iter = begin(); iter != end(); ++iter) {
00195       previous_page = *iter;
00196       if (previous_page.next_page_number() == existing_page.page_number()) {
00197         previous_page.set_next_page_number(existing_page.next_page_number());
00198         break;
00199       }
00200     }
00201   }
00202   // Clear the page and add it to the head of the free list.
00203   existing_page.initialize();
00204   existing_page.set_next_page_number(header.first_free_page);
00205   header.first_free_page = page_number;
00206   ++header.num_free_pages;
00207   if (previous_page.isUsed()) {
00208     writePage(previous_page.page_number(), previous_page);
00209   }
00210   writePage(page_number, existing_page);
00211   writeHeader(header);
00212 }
00213 
00214 FileIterator File::begin() {
00215   const FileHeader& header = readHeader();
00216   return FileIterator(this, header.first_used_page);
00217 }
00218 
00219 FileIterator File::end() {
00220   return FileIterator(this, Page::INVALID_NUMBER);
00221 }
00222 
00223 File::File(const std::string& name, const bool create_new) : filename_(name) {
00224   openIfNeeded(create_new);
00225 
00226   if (create_new) {
00227     // File starts with 1 page (the header).
00228     FileHeader header = {1 /* num_pages */, 0 /* first_used_page */,
00229                          0 /* num_free_pages */, 0 /* first_free_page */};
00230     writeHeader(header);
00231   }
00232 }
00233 
00234 void File::openIfNeeded(const bool create_new) {
00235   if (open_counts_.find(filename_) != open_counts_.end()) { //exists an entry already
00236     ++open_counts_[filename_];
00237     stream_ = open_streams_[filename_];
00238   } else {
00239     std::ios_base::openmode mode =
00240         std::fstream::in | std::fstream::out | std::fstream::binary;
00241     const bool already_exists = exists(filename_);
00242     if (create_new) {
00243       // Error if we try to overwrite an existing file.
00244       if (already_exists) {
00245         throw FileExistsException(filename_);
00246       }
00247       // New files have to be truncated on open.
00248       mode = mode | std::fstream::trunc;
00249     } else {
00250       // Error if we try to open a file that doesn't exist.
00251       if (!already_exists) {
00252         throw FileNotFoundException(filename_);
00253       }
00254     }
00255     stream_.reset(new std::fstream(filename_, mode));
00256     open_streams_[filename_] = stream_;
00257     open_counts_[filename_] = 1;
00258   }
00259 }
00260 
00261 void File::close() {
00262   --open_counts_[filename_];
00263   stream_.reset();
00264   if (open_counts_[filename_] == 0) {
00265     open_streams_.erase(filename_);
00266     open_counts_.erase(filename_);
00267   }
00268 }
00269 
00270 void File::writePage(const PageId page_number, const Page& new_page) {
00271   writePage(page_number, new_page.header_, new_page);
00272 }
00273 
00274 void File::writePage(const PageId page_number, const PageHeader& header,
00275                      const Page& new_page) {
00276   stream_->seekp(pagePosition(page_number), std::ios::beg);
00277   stream_->write(reinterpret_cast<const char*>(&header), sizeof(header));
00278   stream_->write(reinterpret_cast<const char*>(&new_page.data_[0]),
00279                  Page::DATA_SIZE);
00280   stream_->flush();
00281 }
00282 
00283 FileHeader File::readHeader() const {
00284   FileHeader header;
00285   stream_->seekg(0 /* pos */, std::ios::beg);
00286   stream_->read(reinterpret_cast<char*>(&header), sizeof(header));
00287 
00288   return header;
00289 }
00290 
00291 void File::writeHeader(const FileHeader& header) {
00292   stream_->seekp(0 /* pos */, std::ios::beg);
00293   stream_->write(reinterpret_cast<const char*>(&header), sizeof(header));
00294   stream_->flush();
00295 }
00296 
00297 PageHeader File::readPageHeader(PageId page_number) const {
00298   PageHeader header;
00299   stream_->seekg(pagePosition(page_number), std::ios::beg);
00300   stream_->read(reinterpret_cast<char*>(&header), sizeof(header));
00301 
00302   return header;
00303 }
00304 
00305 }
 All Classes Namespaces Functions Variables Typedefs Friends