BadgerDB
/afs/cs.wisc.edu/u/n/w/nwilliam/private/workspace/Quut/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 void File::remove(const std::string& filename) {
00030   if (!exists(filename)) {
00031     throw FileNotFoundException(filename);
00032   }
00033   if (isOpen(filename)) {
00034     throw FileOpenException(filename);
00035   }
00036   std::remove(filename.c_str());
00037 }
00038 
00039 bool File::isOpen(const std::string& filename) {
00040   if (!exists(filename)) {
00041     return false;
00042   }
00043   return open_counts_.find(filename) != open_counts_.end();
00044 }
00045 
00046 bool File::exists(const std::string& filename) {
00047   std::fstream file(filename);
00048   if(file)
00049   {
00050     file.close();
00051     return true;
00052   }
00053 
00054   return false;
00055 }
00056 
00057 File::~File() {
00058   close();
00059 }
00060 
00061 
00062 PageId File::getFirstPageNo() {
00063   const FileHeader& header = readHeader();
00064   return header.first_used_page;
00065 }
00066 
00067 File::File(const std::string& name, const bool create_new) : filename_(name) {
00068   openIfNeeded(create_new);
00069 
00070   if (create_new) {
00071     // File starts with 1 page (the header).
00072     FileHeader header = {1 /* num_pages */, 0 /* first_used_page */,
00073                          0 /* num_free_pages */, 0 /* first_free_page */};
00074     writeHeader(header);
00075   }
00076 }
00077 
00078 void File::openIfNeeded(const bool create_new) {
00079   if (open_counts_.find(filename_) != open_counts_.end()) { //exists an entry already
00080     ++open_counts_[filename_];
00081     stream_ = open_streams_[filename_];
00082   } else {
00083     std::ios_base::openmode mode =
00084         std::fstream::in | std::fstream::out | std::fstream::binary;
00085     const bool already_exists = exists(filename_);
00086     if (create_new) {
00087       // Error if we try to overwrite an existing file.
00088       if (already_exists) {
00089         throw FileExistsException(filename_);
00090       }
00091       // New files have to be truncated on open.
00092       mode = mode | std::fstream::trunc;
00093     } else {
00094       // Error if we try to open a file that doesn't exist.
00095       if (!already_exists) {
00096         throw FileNotFoundException(filename_);
00097       }
00098     }
00099     stream_.reset(new std::fstream(filename_, mode));
00100     open_streams_[filename_] = stream_;
00101     open_counts_[filename_] = 1;
00102   }
00103 }
00104 
00105 void File::close() {
00106   if(open_counts_[filename_] > 0)
00107     --open_counts_[filename_];
00108 
00109   stream_.reset();
00110   assert(open_counts_[filename_] >= 0);
00111 
00112   if (open_counts_[filename_] == 0) {
00113     open_streams_.erase(filename_);
00114     open_counts_.erase(filename_);
00115   }
00116 }
00117 
00118 FileHeader File::readHeader() const {
00119   FileHeader header;
00120   stream_->seekg(0 /* pos */, std::ios::beg);
00121   stream_->read(reinterpret_cast<char*>(&header), sizeof(FileHeader));
00122   return header;
00123 }
00124 
00125 void File::writeHeader(const FileHeader& header) {
00126   stream_->seekp(0 /* pos */, std::ios::beg);
00127   stream_->write(reinterpret_cast<const char*>(&header), sizeof(FileHeader));
00128   stream_->flush();
00129 }
00130 
00131 
00132 
00133 
00134 
00135 PageFile PageFile::create(const std::string& filename) {
00136   return PageFile(filename, true /* create_new */);
00137 }
00138 
00139 PageFile PageFile::open(const std::string& filename) {
00140   return PageFile(filename, false /* create_new */);
00141 }
00142 
00143 PageFile::PageFile(const std::string& name, const bool create_new)
00144 : File(name, create_new)
00145 {
00146 }
00147 
00148 PageFile::~PageFile() {
00149 }
00150 
00151 PageFile::PageFile(const PageFile& other)
00152 : File(other.filename_, false /* create_new */)
00153 {
00154 }
00155 
00156 PageFile& PageFile::operator=(const PageFile& rhs) {
00157   // This accounts for self-assignment and assignment of a File object for the
00158   // same file.
00159   close();  //close my file and associate me with the new one
00160   filename_ = rhs.filename_;
00161   openIfNeeded(false /* create_new */);
00162   return *this;
00163 }
00164 
00165 Page PageFile::allocatePage(PageId &new_page_number) {
00166   FileHeader header = readHeader();
00167   Page new_page;
00168   Page existing_page;
00169   if (header.num_free_pages > 0) {
00170     new_page = readPage(header.first_free_page, true /* allow_free */);
00171     new_page.set_page_number(header.first_free_page);
00172     new_page_number = new_page.page_number();
00173     header.first_free_page = new_page.next_page_number();
00174     --header.num_free_pages;
00175 
00176     if (header.first_used_page == Page::INVALID_NUMBER ||
00177         header.first_used_page > new_page.page_number()) {
00178       // Either have no pages used or the head of the used list is a page later
00179       // than the one we just allocated, so add the new page to the head.
00180       if (header.first_used_page > new_page.page_number()) {
00181         new_page.set_next_page_number(header.first_used_page);
00182       }
00183       header.first_used_page = new_page.page_number();
00184     } else {
00185       // New page is reused from somewhere after the beginning, so we need to
00186       // find where in the used list to insert it.
00187       PageId next_page_number = Page::INVALID_NUMBER;
00188       for (FileIterator iter = begin(); iter != end(); ++iter) {
00189         next_page_number = (*iter).next_page_number();
00190         if (next_page_number > new_page.page_number() ||
00191             next_page_number == Page::INVALID_NUMBER) {
00192           existing_page = *iter;
00193           break;
00194         }
00195       }
00196       existing_page.set_next_page_number(new_page.page_number());
00197       new_page.set_next_page_number(next_page_number);
00198     }
00199 
00200     assert((header.num_free_pages == 0) ==
00201            (header.first_free_page == Page::INVALID_NUMBER));
00202   }
00203   else
00204   {
00205     new_page.set_page_number(header.num_pages);
00206     new_page_number = new_page.page_number();
00207 
00208     if (header.first_used_page == Page::INVALID_NUMBER)
00209     {
00210       header.first_used_page = new_page.page_number();
00211     }
00212     else
00213     {
00214       // If we have pages allocated, we need to add the new page to the tail
00215       // of the linked list.
00216       for (FileIterator iter = begin(); iter != end(); ++iter) {
00217         if ((*iter).next_page_number() == Page::INVALID_NUMBER) {
00218           existing_page = *iter;
00219           break;
00220         }
00221       }
00222       assert(existing_page.isUsed());
00223       existing_page.set_next_page_number(new_page.page_number());
00224     }
00225     ++header.num_pages;
00226   }
00227   writePage(new_page_number, new_page.header_, new_page);
00228   if (existing_page.page_number() != Page::INVALID_NUMBER) {
00229     // If we updated an existing page by inserting the new page into the
00230     // used list, we need to write it out.
00231     writePage(existing_page.page_number(), existing_page.header_, existing_page);
00232   }
00233   writeHeader(header);
00234 
00235   return new_page;
00236 }
00237 
00238 Page PageFile::readPage(const PageId page_number) const {
00239   FileHeader header = readHeader();
00240 
00241   if (page_number >= header.num_pages)
00242   {
00243     throw InvalidPageException(page_number, filename_);
00244   }
00245   return readPage(page_number, false /* allow_free */);
00246 }
00247 
00248 Page PageFile::readPage(const PageId page_number, const bool allow_free) const {
00249   Page page;
00250   stream_->seekg(pagePosition(page_number), std::ios::beg);
00251   stream_->read(reinterpret_cast<char*>(&page.header_), sizeof(PageHeader));
00252   stream_->read(reinterpret_cast<char*>(&page.data_[0]), Page::DATA_SIZE);
00253   if (!allow_free && !page.isUsed()) {
00254     throw InvalidPageException(page_number, filename_);
00255   }
00256 
00257   return page;
00258 }
00259 
00260 void PageFile::writePage(const PageId new_page_number, const Page& new_page) {
00261   PageHeader header = readPageHeader(new_page_number);
00262   if (header.current_page_number == Page::INVALID_NUMBER)
00263   {
00264     // Page has been deleted since it was read.
00265     throw InvalidPageException(new_page_number, filename_);
00266   }
00267   // Page on disk may have had its next page pointer updated since it was read;
00268   // we don't modify that, but we do keep all the other modifications to the
00269   // page header.
00270   const PageId next_page_number = header.next_page_number;
00271   header = new_page.header_;
00272   header.next_page_number = next_page_number;
00273   writePage(new_page_number, header, new_page);
00274 }
00275 
00276 void PageFile::deletePage(const PageId page_number) {
00277   FileHeader header = readHeader();
00278 
00279   Page existing_page = readPage(page_number);
00280   Page previous_page;
00281   // If this page is the head of the used list, update the header to point to
00282   // the next page in line.
00283   if (page_number == header.first_used_page) {
00284     header.first_used_page = existing_page.next_page_number();
00285   } else {
00286     // Walk the used list so we can update the page that points to this one.
00287     for (FileIterator iter = begin(); iter != end(); ++iter) {
00288       previous_page = *iter;
00289       if (previous_page.next_page_number() == existing_page.page_number()) {
00290         previous_page.set_next_page_number(existing_page.next_page_number());
00291         break;
00292       }
00293     }
00294   }
00295   // Clear the page and add it to the head of the free list.
00296   existing_page.initialize();
00297   existing_page.set_next_page_number(header.first_free_page);
00298   header.first_free_page = page_number;
00299   ++header.num_free_pages;
00300   if (previous_page.isUsed()) {
00301     writePage(previous_page.page_number(), previous_page.header_, previous_page);
00302   }
00303   writePage(page_number, existing_page.header_, existing_page);
00304   writeHeader(header);
00305 }
00306 
00307 FileIterator PageFile::begin() {
00308   const FileHeader& header = readHeader();
00309   return FileIterator(this, header.first_used_page);
00310 }
00311 
00312 FileIterator PageFile::beginAt(PageId pageId) {
00313   return FileIterator(this, pageId);
00314 }
00315 
00316 FileIterator PageFile::end() {
00317   return FileIterator(this, Page::INVALID_NUMBER);
00318 }
00319 
00320 void PageFile::writePage(const PageId page_number, const PageHeader& header,
00321                      const Page& new_page) {
00322   stream_->seekp(pagePosition(page_number), std::ios::beg);
00323   stream_->write(reinterpret_cast<const char*>(&header), sizeof(PageHeader));
00324   stream_->write(reinterpret_cast<const char*>(&new_page.data_[0]),
00325                  Page::DATA_SIZE);
00326   stream_->flush();
00327 }
00328 
00329 PageHeader PageFile::readPageHeader(PageId page_number) const {
00330   PageHeader header;
00331   stream_->seekg(pagePosition(page_number), std::ios::beg);
00332   stream_->read(reinterpret_cast<char*>(&header), sizeof(PageHeader));
00333   return header;
00334 }
00335 
00336 
00337 
00338 
00339 BlobFile BlobFile::create(const std::string& filename) {
00340   return BlobFile(filename, true /* create_new */);
00341 }
00342 
00343 BlobFile BlobFile::open(const std::string& filename) {
00344   return BlobFile(filename, false /* create_new */);
00345 }
00346 
00347 BlobFile::BlobFile(const std::string& name, const bool create_new)
00348 : File(name, create_new) {
00349 }
00350 
00351 BlobFile::~BlobFile() {
00352 }
00353 
00354 BlobFile::BlobFile(const BlobFile& other)
00355 : File(other.filename_, false /* create_new */)
00356 {
00357 }
00358 
00359 BlobFile& BlobFile::operator=(const BlobFile& rhs) {
00360   // This accounts for self-assignment and assignment of a File object for the
00361   // same file.
00362   close();  //close my file and associate me with the new one
00363   filename_ = rhs.filename_;
00364   openIfNeeded(false /* create_new */);
00365   return *this;
00366 }
00367 
00368 Page BlobFile::allocatePage(PageId &new_page_number) {
00369   FileHeader header = readHeader();
00370   Page new_page;
00371 
00372   new_page_number = header.num_pages;
00373 
00374   if (header.first_used_page == Page::INVALID_NUMBER) {
00375     header.first_used_page = header.num_pages;
00376   }
00377 
00378   ++header.num_pages;
00379 
00380   writePage(new_page_number, new_page);
00381   writeHeader(header);
00382 
00383   return new_page;
00384 }
00385 
00386 Page BlobFile::readPage(const PageId page_number) const {
00387   Page page;
00388   stream_->seekg(pagePosition(page_number), std::ios::beg);
00389   stream_->read(reinterpret_cast<char*>(&page), Page::SIZE);
00390   return page;
00391 }
00392 
00393 void BlobFile::writePage(const PageId new_page_number, const Page& new_page) {
00394   stream_->seekp(pagePosition(new_page_number), std::ios::beg);
00395   stream_->write(reinterpret_cast<const char*>(&new_page), Page::SIZE);
00396   stream_->flush();
00397 }
00398 
00399 //delePage should not be called for a blob_file, not supported
00400 void BlobFile::deletePage(const PageId page_number) {
00401   throw InvalidPageException(page_number, filename_);
00402 }
00403 
00404 }
 All Classes Namespaces Functions Variables Typedefs Enumerations Friends