BadgerDB
|
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 }