Home | History | Annotate | Download | only in bsdiff
      1 // Copyright 2015 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "extents_file.h"
      6 
      7 #include <string.h>
      8 
      9 #include <algorithm>
     10 
     11 // Extent files implementation extending FileInterface.
     12 //
     13 // This class allows to map linear positions in a file to a list of regions in
     14 // another file. All the reads and writes are unbuffered, passed directly to the
     15 // underlying file. Seeking is done in O(log(N)), where N is the number of
     16 // extents in the file, but sequential reads jump to the next extent in O(1).
     17 
     18 namespace bsdiff {
     19 
     20 ExtentsFile::ExtentsFile(std::unique_ptr<FileInterface> file,
     21                          const std::vector<ex_t>& extents)
     22     : file_(std::move(file)), extents_(extents) {
     23   acc_len_.reserve(extents.size());
     24   for (const ex_t& extent : extents) {
     25     acc_len_.push_back(total_ex_len_);
     26     total_ex_len_ += extent.len;
     27   }
     28 }
     29 
     30 ExtentsFile::~ExtentsFile() {
     31   Close();
     32 }
     33 
     34 bool ExtentsFile::Read(void* buf, size_t count, size_t* bytes_read) {
     35   return IOOperation(&FileInterface::Read, buf, count, bytes_read);
     36 }
     37 
     38 
     39 bool ExtentsFile::Write(const void* buf, size_t count, size_t* bytes_written) {
     40   return IOOperation(&FileInterface::Write, buf, count, bytes_written);
     41 }
     42 
     43 bool ExtentsFile::Seek(off_t pos) {
     44   if (pos < 0 || static_cast<uint64_t>(pos) > total_ex_len_)
     45     return false;
     46   if (acc_len_.empty())
     47     return true;
     48   // Note that the first element of acc_len_ is always 0, and pos is at least 0,
     49   // so the upper_bound will never return acc_len_.begin().
     50   curr_pos_ = pos;
     51   curr_ex_idx_ = std::upper_bound(acc_len_.begin(), acc_len_.end(), pos) -
     52                  acc_len_.begin();
     53   // We handle the corner case where |pos| is the size of all the extents by
     54   // leaving the value of curr_ex_idx_ the same way AdvancePos(0) would leave it
     55   // after the seek.
     56   if (curr_pos_ < total_ex_len_)
     57     curr_ex_idx_--;
     58   return true;
     59 }
     60 
     61 bool ExtentsFile::Close() {
     62   return file_->Close();
     63 }
     64 
     65 bool ExtentsFile::GetSize(uint64_t* size) {
     66   *size = total_ex_len_;
     67   return true;
     68 }
     69 
     70 void ExtentsFile::AdvancePos(uint64_t size) {
     71   curr_pos_ += size;
     72   for (; curr_ex_idx_ < extents_.size(); curr_ex_idx_++) {
     73     if (curr_pos_ < acc_len_[curr_ex_idx_] + extents_[curr_ex_idx_].len)
     74       return;
     75   }
     76   return;
     77 }
     78 
     79 template <typename T>
     80 bool ExtentsFile::IOOperation(bool (FileInterface::*io_op)(T*, size_t, size_t*),
     81                               T* buf,
     82                               size_t count,
     83                               size_t* bytes_processed) {
     84   bool result = true;
     85   size_t processed = 0;
     86   AdvancePos(0);
     87   while (count > 0 && curr_ex_idx_ < extents_.size()) {
     88     const ex_t& ex = extents_[curr_ex_idx_];
     89     off_t curr_ex_off = curr_pos_ - acc_len_[curr_ex_idx_];
     90     size_t chunk_size =
     91         std::min(static_cast<uint64_t>(count), ex.len - curr_ex_off);
     92     size_t chunk_processed = 0;
     93     if (ex.off < 0) {
     94       chunk_processed = chunk_size;
     95     } else {
     96       if (!file_->Seek(ex.off + curr_ex_off) ||
     97           !(file_.get()->*io_op)(buf, chunk_size, &chunk_processed)) {
     98         processed += chunk_processed;
     99         result = processed > 0;
    100         break;
    101       }
    102     }
    103     processed += chunk_processed;
    104     count -= chunk_processed;
    105     // T can be either const void* or void*. We const_cast it back to void* and
    106     // then char* to do the arithmetic operation, but |buf| will continue to be
    107     // const void* if it was defined that way.
    108     buf = static_cast<char*>(const_cast<void*>(buf)) + chunk_processed;
    109     AdvancePos(chunk_processed);
    110     if (!chunk_processed)
    111       break;
    112   }
    113   *bytes_processed = processed;
    114   return result;
    115 }
    116 
    117 }  // namespace bsdiff
    118