blob: d3a8006de32218017b10874dcad42997ef6844af [file] [log] [blame]
/*
* Copyright 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Log.h"
#include "GazerConnection.h"
#include "ResourceDiskCache.h"
#include "ResourceProvider.h"
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#if TARGET_OS == CAZE_OS_WINDOWS
#include <direct.h>
#define mkdir(path, mode) _mkdir(path)
#else
static const mode_t MKDIR_MODE = 0755;
#endif
namespace android {
namespace caze {
namespace {
int mkdirAll(const std::string& path) {
if (0 != mkdir(path.c_str(), MKDIR_MODE)) {
switch (errno) {
case ENOENT: { // Non-existent parent(s).
size_t pos = path.find_last_of("/\\");
if (pos == std::string::npos) {
return -1;
}
mkdirAll(path.substr(0, pos));
return mkdir(path.c_str(), MKDIR_MODE); // Retry.
}
case EEXIST: // Already exists, return success.
return 0;
default: // Something went wrong, return failure.
return -1;
}
}
return 0;
}
} // end of anonymous namespace
std::unique_ptr<ResourceProvider> ResourceDiskCache::create(
std::unique_ptr<ResourceProvider> fallbackProvider, const std::string& path) {
if (0 != mkdirAll(path)) {
CAZE_WARNING("Couldn't access/create cache directory; disabling disk cache.");
return fallbackProvider; // Disk path was inaccessible.
} else {
std::string diskPath = path;
if (diskPath.back() != PATH_DELIMITER) {
diskPath.push_back(PATH_DELIMITER);
}
return std::unique_ptr<ResourceProvider>(
new ResourceDiskCache(std::move(fallbackProvider), std::move(diskPath)));
}
}
ResourceDiskCache::ResourceDiskCache(std::unique_ptr<ResourceProvider> fallbackProvider,
const std::string& path) :
mFallbackProvider(std::move(fallbackProvider)), mPath(path) {
}
bool ResourceDiskCache::get(const ResourceId& id, const GazerConnection& gazer, void* target,
uint32_t size) {
const std::string filepath(mPath + id);
if (FILE* fi = fopen(filepath.c_str(), "rb")) {
// Resource already in the disk cache. Load it from disk
const size_t length = fread(target, 1, size, fi);
fclose(fi);
// Check the amount of data read from the disk against the amount of data requested
if (length == size) {
return true;
}
}
// Fetch the resource from the fall back provider if it isn't in the disk cache or the size
// doesn't match with the requested size (mostly corrupted writes)
if (mFallbackProvider->get(id, gazer, target, size)) {
// Save the resource to the disk if possible
if (FILE* fo = fopen(filepath.c_str(), "wb")) {
fwrite(target, size, 1, fo);
fclose(fo);
}
return true;
} else {
return false;
}
}
bool ResourceDiskCache::prefetch(const ResourceList& resources,
const GazerConnection& gazer, void* buffer, uint32_t size) {
size_t querySumSize = 0;
ResourceList query;
// Batch the resource requests into batches where the sum size of each batch fit into the size
// of the buffer available for prefetching
for (const auto& res : resources) {
const std::string filepath(mPath + res.first);
// Check if the resource is already in the disk cache
if (FILE* fi = fopen(filepath.c_str(), "rb")) {
fclose(fi);
} else if (res.second > size) {
CAZE_WARNING("Can't prefetch resource because of limited buffer size");
} else {
// If next resource not fit into this batch then fetch the current batch
if (querySumSize + res.second > size) {
if (!fetch(gazer, buffer, query)) {
return false;
}
query.clear();
querySumSize = 0;
}
query.push_back(res);
querySumSize += res.second;
}
}
// Fetch the last batch if it isn't empty
if (query.size() > 0) {
return fetch(gazer, buffer, query);
}
return true;
}
bool ResourceDiskCache::fetch(const GazerConnection& gazer, void* buffer,
const ResourceList& query) {
// Request the resources from the fall back provider
if (!mFallbackProvider->get(query, gazer, buffer)) {
return false;
}
// Save each resources to the disk from the correct offset
for (size_t i = 0, offset = 0; i < query.size(); ++i) {
const std::string filepath(mPath + query[i].first);
if (FILE* fo = fopen(filepath.c_str(), "wb")) {
fwrite(static_cast<uint8_t*>(buffer) + offset, query[i].second, 1, fo);
fclose(fo);
}
offset += query[i].second;
}
return true;
}
} // end of namespace caze
} // end of namespace android