• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 #include "tensorflow/contrib/android/asset_manager_filesystem.h"
17 
18 #include <unistd.h>
19 
20 #include "tensorflow/core/lib/strings/str_util.h"
21 #include "tensorflow/core/platform/env.h"
22 #include "tensorflow/core/platform/file_system_helper.h"
23 
24 namespace tensorflow {
25 namespace {
26 
RemoveSuffix(const string & name,const string & suffix)27 string RemoveSuffix(const string& name, const string& suffix) {
28   string output(name);
29   StringPiece piece(output);
30   str_util::ConsumeSuffix(&piece, suffix);
31   return string(piece);
32 }
33 
34 // Closes the given AAsset when variable is destructed.
35 class ScopedAsset {
36  public:
ScopedAsset(AAsset * asset)37   ScopedAsset(AAsset* asset) : asset_(asset) {}
~ScopedAsset()38   ~ScopedAsset() {
39     if (asset_ != nullptr) {
40       AAsset_close(asset_);
41     }
42   }
43 
get() const44   AAsset* get() const { return asset_; }
45 
46  private:
47   AAsset* asset_;
48 };
49 
50 // Closes the given AAssetDir when variable is destructed.
51 class ScopedAssetDir {
52  public:
ScopedAssetDir(AAssetDir * asset_dir)53   ScopedAssetDir(AAssetDir* asset_dir) : asset_dir_(asset_dir) {}
~ScopedAssetDir()54   ~ScopedAssetDir() {
55     if (asset_dir_ != nullptr) {
56       AAssetDir_close(asset_dir_);
57     }
58   }
59 
get() const60   AAssetDir* get() const { return asset_dir_; }
61 
62  private:
63   AAssetDir* asset_dir_;
64 };
65 
66 class ReadOnlyMemoryRegionFromAsset : public ReadOnlyMemoryRegion {
67  public:
ReadOnlyMemoryRegionFromAsset(std::unique_ptr<char[]> data,uint64 length)68   ReadOnlyMemoryRegionFromAsset(std::unique_ptr<char[]> data, uint64 length)
69       : data_(std::move(data)), length_(length) {}
70   ~ReadOnlyMemoryRegionFromAsset() override = default;
71 
data()72   const void* data() override { return reinterpret_cast<void*>(data_.get()); }
length()73   uint64 length() override { return length_; }
74 
75  private:
76   std::unique_ptr<char[]> data_;
77   uint64 length_;
78 };
79 
80 // Note that AAssets are not thread-safe and cannot be used across threads.
81 // However, AAssetManager is. Because RandomAccessFile must be thread-safe and
82 // used across threads, new AAssets must be created for every access.
83 // TODO(tylerrhodes): is there a more efficient way to do this?
84 class RandomAccessFileFromAsset : public RandomAccessFile {
85  public:
RandomAccessFileFromAsset(AAssetManager * asset_manager,const string & name)86   RandomAccessFileFromAsset(AAssetManager* asset_manager, const string& name)
87       : asset_manager_(asset_manager), file_name_(name) {}
88   ~RandomAccessFileFromAsset() override = default;
89 
Read(uint64 offset,size_t to_read,StringPiece * result,char * scratch) const90   Status Read(uint64 offset, size_t to_read, StringPiece* result,
91               char* scratch) const override {
92     auto asset = ScopedAsset(AAssetManager_open(
93         asset_manager_, file_name_.c_str(), AASSET_MODE_RANDOM));
94     if (asset.get() == nullptr) {
95       return errors::NotFound("File ", file_name_, " not found.");
96     }
97 
98     off64_t new_offset = AAsset_seek64(asset.get(), offset, SEEK_SET);
99     off64_t length = AAsset_getLength64(asset.get());
100     if (new_offset < 0) {
101       *result = StringPiece(scratch, 0);
102       return errors::OutOfRange("Read after file end.");
103     }
104     const off64_t region_left =
105         std::min(length - new_offset, static_cast<off64_t>(to_read));
106     int read = AAsset_read(asset.get(), scratch, region_left);
107     if (read < 0) {
108       return errors::Internal("Error reading from asset.");
109     }
110     *result = StringPiece(scratch, region_left);
111     return (region_left == to_read)
112                ? Status::OK()
113                : errors::OutOfRange("Read less bytes than requested.");
114   }
115 
116  private:
117   AAssetManager* asset_manager_;
118   string file_name_;
119 };
120 
121 }  // namespace
122 
AssetManagerFileSystem(AAssetManager * asset_manager,const string & prefix)123 AssetManagerFileSystem::AssetManagerFileSystem(AAssetManager* asset_manager,
124                                                const string& prefix)
125     : asset_manager_(asset_manager), prefix_(prefix) {}
126 
FileExists(const string & fname)127 Status AssetManagerFileSystem::FileExists(const string& fname) {
128   string path = RemoveAssetPrefix(fname);
129   auto asset = ScopedAsset(
130       AAssetManager_open(asset_manager_, path.c_str(), AASSET_MODE_RANDOM));
131   if (asset.get() == nullptr) {
132     return errors::NotFound("File ", fname, " not found.");
133   }
134   return Status::OK();
135 }
136 
NewRandomAccessFile(const string & fname,std::unique_ptr<RandomAccessFile> * result)137 Status AssetManagerFileSystem::NewRandomAccessFile(
138     const string& fname, std::unique_ptr<RandomAccessFile>* result) {
139   string path = RemoveAssetPrefix(fname);
140   auto asset = ScopedAsset(
141       AAssetManager_open(asset_manager_, path.c_str(), AASSET_MODE_RANDOM));
142   if (asset.get() == nullptr) {
143     return errors::NotFound("File ", fname, " not found.");
144   }
145   result->reset(new RandomAccessFileFromAsset(asset_manager_, path));
146   return Status::OK();
147 }
148 
NewReadOnlyMemoryRegionFromFile(const string & fname,std::unique_ptr<ReadOnlyMemoryRegion> * result)149 Status AssetManagerFileSystem::NewReadOnlyMemoryRegionFromFile(
150     const string& fname, std::unique_ptr<ReadOnlyMemoryRegion>* result) {
151   string path = RemoveAssetPrefix(fname);
152   auto asset = ScopedAsset(
153       AAssetManager_open(asset_manager_, path.c_str(), AASSET_MODE_STREAMING));
154   if (asset.get() == nullptr) {
155     return errors::NotFound("File ", fname, " not found.");
156   }
157 
158   off64_t start, length;
159   int fd = AAsset_openFileDescriptor64(asset.get(), &start, &length);
160   std::unique_ptr<char[]> data;
161   if (fd >= 0) {
162     data.reset(new char[length]);
163     ssize_t result = pread(fd, data.get(), length, start);
164     if (result < 0) {
165       return errors::Internal("Error reading from file ", fname,
166                               " using 'read': ", result);
167     }
168     if (result != length) {
169       return errors::Internal("Expected size does not match size read: ",
170                               "Expected ", length, " vs. read ", result);
171     }
172     close(fd);
173   } else {
174     length = AAsset_getLength64(asset.get());
175     data.reset(new char[length]);
176     const void* asset_buffer = AAsset_getBuffer(asset.get());
177     if (asset_buffer == nullptr) {
178       return errors::Internal("Error reading ", fname, " from asset manager.");
179     }
180     memcpy(data.get(), asset_buffer, length);
181   }
182   result->reset(new ReadOnlyMemoryRegionFromAsset(std::move(data), length));
183   return Status::OK();
184 }
185 
GetChildren(const string & prefixed_dir,std::vector<string> * r)186 Status AssetManagerFileSystem::GetChildren(const string& prefixed_dir,
187                                            std::vector<string>* r) {
188   std::string path = NormalizeDirectoryPath(prefixed_dir);
189   auto dir =
190       ScopedAssetDir(AAssetManager_openDir(asset_manager_, path.c_str()));
191   if (dir.get() == nullptr) {
192     return errors::NotFound("Directory ", prefixed_dir, " not found.");
193   }
194   const char* next_file = AAssetDir_getNextFileName(dir.get());
195   while (next_file != nullptr) {
196     r->push_back(next_file);
197     next_file = AAssetDir_getNextFileName(dir.get());
198   }
199   return Status::OK();
200 }
201 
GetFileSize(const string & fname,uint64 * s)202 Status AssetManagerFileSystem::GetFileSize(const string& fname, uint64* s) {
203   // If fname corresponds to a directory, return early. It doesn't map to an
204   // AAsset, and would otherwise return NotFound.
205   if (DirectoryExists(fname)) {
206     *s = 0;
207     return Status::OK();
208   }
209   string path = RemoveAssetPrefix(fname);
210   auto asset = ScopedAsset(
211       AAssetManager_open(asset_manager_, path.c_str(), AASSET_MODE_RANDOM));
212   if (asset.get() == nullptr) {
213     return errors::NotFound("File ", fname, " not found.");
214   }
215   *s = AAsset_getLength64(asset.get());
216   return Status::OK();
217 }
218 
Stat(const string & fname,FileStatistics * stat)219 Status AssetManagerFileSystem::Stat(const string& fname, FileStatistics* stat) {
220   uint64 size;
221   stat->is_directory = DirectoryExists(fname);
222   TF_RETURN_IF_ERROR(GetFileSize(fname, &size));
223   stat->length = size;
224   return Status::OK();
225 }
226 
NormalizeDirectoryPath(const string & fname)227 string AssetManagerFileSystem::NormalizeDirectoryPath(const string& fname) {
228   return RemoveSuffix(RemoveAssetPrefix(fname), "/");
229 }
230 
RemoveAssetPrefix(const string & name)231 string AssetManagerFileSystem::RemoveAssetPrefix(const string& name) {
232   StringPiece piece(name);
233   str_util::ConsumePrefix(&piece, prefix_);
234   return string(piece);
235 }
236 
DirectoryExists(const std::string & fname)237 bool AssetManagerFileSystem::DirectoryExists(const std::string& fname) {
238   std::string path = NormalizeDirectoryPath(fname);
239   auto dir =
240       ScopedAssetDir(AAssetManager_openDir(asset_manager_, path.c_str()));
241   // Note that openDir will return something even if the directory doesn't
242   // exist. Therefore, we need to ensure one file exists in the folder.
243   return AAssetDir_getNextFileName(dir.get()) != NULL;
244 }
245 
GetMatchingPaths(const string & pattern,std::vector<string> * results)246 Status AssetManagerFileSystem::GetMatchingPaths(const string& pattern,
247                                                 std::vector<string>* results) {
248   return internal::GetMatchingPaths(this, Env::Default(), pattern, results);
249 }
250 
NewWritableFile(const string & fname,std::unique_ptr<WritableFile> * result)251 Status AssetManagerFileSystem::NewWritableFile(
252     const string& fname, std::unique_ptr<WritableFile>* result) {
253   return errors::Unimplemented("Asset storage is read only.");
254 }
NewAppendableFile(const string & fname,std::unique_ptr<WritableFile> * result)255 Status AssetManagerFileSystem::NewAppendableFile(
256     const string& fname, std::unique_ptr<WritableFile>* result) {
257   return errors::Unimplemented("Asset storage is read only.");
258 }
DeleteFile(const string & f)259 Status AssetManagerFileSystem::DeleteFile(const string& f) {
260   return errors::Unimplemented("Asset storage is read only.");
261 }
CreateDir(const string & d)262 Status AssetManagerFileSystem::CreateDir(const string& d) {
263   return errors::Unimplemented("Asset storage is read only.");
264 }
DeleteDir(const string & d)265 Status AssetManagerFileSystem::DeleteDir(const string& d) {
266   return errors::Unimplemented("Asset storage is read only.");
267 }
RenameFile(const string & s,const string & t)268 Status AssetManagerFileSystem::RenameFile(const string& s, const string& t) {
269   return errors::Unimplemented("Asset storage is read only.");
270 }
271 
272 }  // namespace tensorflow
273