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