Home | History | Annotate | Download | only in util
      1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
      2 
      3 Licensed under the Apache License, Version 2.0 (the "License");
      4 you may not use this file except in compliance with the License.
      5 You may obtain a copy of the License at
      6 
      7     http://www.apache.org/licenses/LICENSE-2.0
      8 
      9 Unless required by applicable law or agreed to in writing, software
     10 distributed under the License is distributed on an "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 See the License for the specific language governing permissions and
     13 limitations under the License.
     14 ==============================================================================*/
     15 #include "tensorflow/core/util/memmapped_file_system.h"
     16 
     17 #include "tensorflow/core/lib/core/errors.h"
     18 #include "tensorflow/core/platform/protobuf.h"
     19 #include "tensorflow/core/util/memmapped_file_system.pb.h"
     20 
     21 namespace tensorflow {
     22 
     23 namespace {
     24 
     25 uint64 DecodeUint64LittleEndian(const uint8* buffer) {
     26   uint64 result = 0;
     27   for (int i = 0; i < static_cast<int>(sizeof(uint64)); ++i) {
     28     result |= static_cast<uint64>(buffer[i]) << (8 * i);
     29   }
     30   return result;
     31 }
     32 
     33 }  // namespace
     34 
     35 namespace {
     36 
     37 class ReadOnlyMemoryRegionFromMemmapped : public ReadOnlyMemoryRegion {
     38  public:
     39   ReadOnlyMemoryRegionFromMemmapped(const void* data, uint64 length)
     40       : data_(data), length_(length) {}
     41   ~ReadOnlyMemoryRegionFromMemmapped() override = default;
     42   const void* data() override { return data_; }
     43   uint64 length() override { return length_; }
     44 
     45  private:
     46   const void* const data_;
     47   const uint64 length_;
     48   // intentionally copyable
     49 };
     50 
     51 class RandomAccessFileFromMemmapped : public RandomAccessFile {
     52  public:
     53   RandomAccessFileFromMemmapped(const void* data, uint64 length)
     54       : data_(data), length_(length) {}
     55 
     56   ~RandomAccessFileFromMemmapped() override = default;
     57 
     58   Status Read(uint64 offset, size_t to_read, StringPiece* result,
     59               char* scratch) const override {
     60     if (offset >= length_) {
     61       *result = StringPiece(scratch, 0);
     62       return Status(error::OUT_OF_RANGE, "Read after file end");
     63     }
     64     const uint64 region_left =
     65         std::min(length_ - offset, static_cast<uint64>(to_read));
     66     *result =
     67         StringPiece(reinterpret_cast<const char*>(data_) + offset, region_left);
     68     return (region_left == to_read)
     69                ? Status::OK()
     70                : Status(error::OUT_OF_RANGE, "Read less bytes than requested");
     71   }
     72 
     73  private:
     74   const void* const data_;
     75   const uint64 length_;
     76   // intentionally copyable
     77 };
     78 
     79 }  // namespace
     80 
     81 MemmappedFileSystem::MemmappedFileSystem() {}
     82 
     83 Status MemmappedFileSystem::FileExists(const string& fname) {
     84   if (!mapped_memory_) {
     85     return errors::FailedPrecondition("MemmappedEnv is not initialized");
     86   }
     87   const auto dir_element = directory_.find(fname);
     88   if (dir_element != directory_.end()) {
     89     return Status::OK();
     90   }
     91   return errors::NotFound(fname, " not found");
     92 }
     93 
     94 Status MemmappedFileSystem::NewRandomAccessFile(
     95     const string& filename, std::unique_ptr<RandomAccessFile>* result) {
     96   if (!mapped_memory_) {
     97     return errors::FailedPrecondition("MemmappedEnv is not initialized");
     98   }
     99   const auto dir_element = directory_.find(filename);
    100   if (dir_element == directory_.end()) {
    101     return errors::NotFound("Region ", filename, " is not found");
    102   }
    103   result->reset(new RandomAccessFileFromMemmapped(
    104       GetMemoryWithOffset(dir_element->second.offset),
    105       dir_element->second.length));
    106   return Status::OK();
    107 }
    108 
    109 Status MemmappedFileSystem::NewReadOnlyMemoryRegionFromFile(
    110     const string& filename, std::unique_ptr<ReadOnlyMemoryRegion>* result) {
    111   if (!mapped_memory_) {
    112     return errors::FailedPrecondition("MemmappedEnv is not initialized");
    113   }
    114   const auto dir_element = directory_.find(filename);
    115   if (dir_element == directory_.end()) {
    116     return errors::NotFound("Region ", filename, " is not found");
    117   }
    118   result->reset(new ReadOnlyMemoryRegionFromMemmapped(
    119       GetMemoryWithOffset(dir_element->second.offset),
    120       dir_element->second.length));
    121   return Status::OK();
    122 }
    123 
    124 Status MemmappedFileSystem::GetFileSize(const string& filename, uint64* size) {
    125   if (!mapped_memory_) {
    126     return errors::FailedPrecondition("MemmappedEnv is not initialized");
    127   }
    128   const auto dir_element = directory_.find(filename);
    129   if (dir_element == directory_.end()) {
    130     return errors::NotFound("Region ", filename, " is not found");
    131   }
    132   *size = dir_element->second.length;
    133   return Status::OK();
    134 }
    135 
    136 Status MemmappedFileSystem::Stat(const string& fname, FileStatistics* stat) {
    137   uint64 size;
    138   auto status = GetFileSize(fname, &size);
    139   if (status.ok()) {
    140     stat->length = size;
    141   }
    142   return status;
    143 }
    144 
    145 Status MemmappedFileSystem::NewWritableFile(const string& filename,
    146                                             std::unique_ptr<WritableFile>* wf) {
    147   return errors::Unimplemented("memmapped format doesn't support writing");
    148 }
    149 
    150 Status MemmappedFileSystem::NewAppendableFile(
    151     const string& filename, std::unique_ptr<WritableFile>* result) {
    152   return errors::Unimplemented("memmapped format doesn't support writing");
    153 }
    154 
    155 Status MemmappedFileSystem::GetChildren(const string& filename,
    156                                         std::vector<string>* strings) {
    157   return errors::Unimplemented("memmapped format doesn't support GetChildren");
    158 }
    159 
    160 Status MemmappedFileSystem::DeleteFile(const string& filename) {
    161   return errors::Unimplemented("memmapped format doesn't support DeleteFile");
    162 }
    163 
    164 Status MemmappedFileSystem::CreateDir(const string& dirname) {
    165   return errors::Unimplemented("memmapped format doesn't support CreateDir");
    166 }
    167 
    168 Status MemmappedFileSystem::DeleteDir(const string& dirname) {
    169   return errors::Unimplemented("memmapped format doesn't support DeleteDir");
    170 }
    171 
    172 Status MemmappedFileSystem::RenameFile(const string& filename_from,
    173                                        const string& filename_to) {
    174   return errors::Unimplemented("memmapped format doesn't support RenameFile");
    175 }
    176 
    177 const void* MemmappedFileSystem::GetMemoryWithOffset(uint64 offset) const {
    178   return reinterpret_cast<const uint8*>(mapped_memory_->data()) + offset;
    179 }
    180 
    181 #if defined(COMPILER_MSVC)
    182 constexpr char* MemmappedFileSystem::kMemmappedPackagePrefix;
    183 constexpr char* MemmappedFileSystem::kMemmappedPackageDefaultGraphDef;
    184 #else
    185 constexpr char MemmappedFileSystem::kMemmappedPackagePrefix[];
    186 constexpr char MemmappedFileSystem::kMemmappedPackageDefaultGraphDef[];
    187 #endif
    188 
    189 Status MemmappedFileSystem::InitializeFromFile(Env* env,
    190                                                const string& filename) {
    191   TF_RETURN_IF_ERROR(
    192       env->NewReadOnlyMemoryRegionFromFile(filename, &mapped_memory_));
    193   directory_.clear();
    194   if (mapped_memory_->length() <= sizeof(uint64)) {
    195     return errors::DataLoss("Corrupted memmapped model file: ", filename,
    196                             " Invalid package size");
    197   }
    198   const auto memory_start =
    199       reinterpret_cast<const uint8*>(mapped_memory_->data());
    200   const uint64 directory_offset = DecodeUint64LittleEndian(
    201       memory_start + mapped_memory_->length() - sizeof(uint64));
    202   if (directory_offset > mapped_memory_->length() - sizeof(uint64)) {
    203     return errors::DataLoss("Corrupted memmapped model file: ", filename,
    204                             " Invalid directory offset");
    205   }
    206   MemmappedFileSystemDirectory proto_directory;
    207   if (!ParseProtoUnlimited(
    208           &proto_directory, memory_start + directory_offset,
    209           mapped_memory_->length() - directory_offset - sizeof(uint64))) {
    210     return errors::DataLoss("Corrupted memmapped model file: ", filename,
    211                             " Can't parse its internal directory");
    212   }
    213 
    214   // Iterating in reverse order to get lengths of elements;
    215   uint64 prev_element_offset = directory_offset;
    216   for (auto element_iter = proto_directory.element().rbegin();
    217        element_iter != proto_directory.element().rend(); ++element_iter) {
    218     // Check that the element offset is in the right range.
    219     if (element_iter->offset() >= prev_element_offset) {
    220       return errors::DataLoss("Corrupted memmapped model file: ", filename,
    221                               " Invalid offset of internal component");
    222     }
    223     if (!directory_
    224              .insert(std::make_pair(
    225                  element_iter->name(),
    226                  FileRegion(element_iter->offset(),
    227                             prev_element_offset - element_iter->offset())))
    228              .second) {
    229       return errors::DataLoss("Corrupted memmapped model file: ", filename,
    230                               " Duplicate name of internal component ",
    231                               element_iter->name());
    232     }
    233     prev_element_offset = element_iter->offset();
    234   }
    235   return Status::OK();
    236 }
    237 
    238 bool MemmappedFileSystem::IsMemmappedPackageFilename(const string& filename) {
    239   return StringPiece(filename).starts_with(kMemmappedPackagePrefix);
    240 }
    241 
    242 namespace {
    243 bool IsValidRegionChar(char c) {
    244   return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
    245          (c >= '0' && c <= '9') || c == '_' || c == '.';
    246 }
    247 }  // namespace
    248 
    249 bool MemmappedFileSystem::IsWellFormedMemmappedPackageFilename(
    250     const string& filename) {
    251   if (!IsMemmappedPackageFilename(filename)) {
    252     return false;
    253   }
    254   for (char c :
    255        filename.substr(strlen(kMemmappedPackagePrefix),
    256                        filename.length() - strlen(kMemmappedPackagePrefix))) {
    257     if (!IsValidRegionChar(c)) {
    258       return false;
    259     }
    260   }
    261   return true;
    262 }
    263 
    264 MemmappedEnv::MemmappedEnv(Env* env) : EnvWrapper(env) {}
    265 
    266 Status MemmappedEnv::GetFileSystemForFile(const string& fname,
    267                                           FileSystem** result) {
    268   if (MemmappedFileSystem::IsMemmappedPackageFilename(fname)) {
    269     if (!memmapped_file_system_) {
    270       return errors::FailedPrecondition(
    271           "MemmappedEnv is not initialized from a file.");
    272     }
    273     *result = memmapped_file_system_.get();
    274     return Status::OK();
    275   }
    276   return EnvWrapper::GetFileSystemForFile(fname, result);
    277 }
    278 
    279 Status MemmappedEnv::GetRegisteredFileSystemSchemes(
    280     std::vector<string>* schemes) {
    281   const auto status = EnvWrapper::GetRegisteredFileSystemSchemes(schemes);
    282   if (status.ok()) {
    283     schemes->emplace_back(MemmappedFileSystem::kMemmappedPackagePrefix);
    284   }
    285   return status;
    286 }
    287 
    288 Status MemmappedEnv::InitializeFromFile(const string& package_filename) {
    289   std::unique_ptr<MemmappedFileSystem> file_system_ptr(new MemmappedFileSystem);
    290   const auto status =
    291       file_system_ptr->InitializeFromFile(target(), package_filename);
    292   if (status.ok()) {
    293     memmapped_file_system_ = std::move(file_system_ptr);
    294   }
    295   return status;
    296 }
    297 
    298 }  // namespace tensorflow
    299