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