blob: f30eeb68986b74062873ce0b4ca8efc9c5486b24 [file] [log] [blame]
/*
* Copyright (C) 2015 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 "resource_disk_cache.h"
#include "resource_provider.h"
#include "server_connection.h"
#include <gapic/log.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#if TARGET_OS == GAPID_OS_WINDOWS
#include <direct.h>
#define mkdir(path, mode) _mkdir(path)
#else
static const mode_t MKDIR_MODE = 0755;
#endif
namespace gapir {
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;
}
} // anonymous namespace
std::unique_ptr<ResourceProvider> ResourceDiskCache::create(
std::unique_ptr<ResourceProvider> fallbackProvider, const std::string& path) {
if (0 != mkdirAll(path)) {
GAPID_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)), mArchive(path + "resources") {
}
bool ResourceDiskCache::get(const ResourceId& id, const ServerConnection& gazer, void* target,
uint32_t size) {
// Try loading the resource from the archive.
if (mArchive.read(id, target, size)) {
return true;
}
// Fetch the resource from the fall back provider if it's not in the archive or if its size
// doesn't match the requested size.
if (mFallbackProvider->get(id, gazer, target, size)) {
// Try saving the resource to the archive and return success.
mArchive.write(id, target, size);
return true;
}
return false;
}
bool ResourceDiskCache::getUncached(const ResourceId& id, const ServerConnection& gazer,
void* target, uint32_t size) {
return mFallbackProvider->getUncached(id, gazer, target, size);
}
bool ResourceDiskCache::prefetch(const ResourceList& resources,
const ServerConnection& 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) {
// Check if the resource is already in the archive.
if (mArchive.contains(res.first)) continue;
if (res.second > size) {
GAPID_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, size, 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, size, query);
}
return true;
}
bool ResourceDiskCache::fetch(const ServerConnection& gazer,
void* buffer, uint32_t size,
const ResourceList& query) {
// Request the resources from the fall back provider
if (!mFallbackProvider->get(query, gazer, buffer, size)) {
return false;
}
// Save each resources to the archive from the correct buffer offset.
for (size_t i = 0, offset = 0; i < query.size(); ++i) {
mArchive.write(query[i].first, static_cast<uint8_t*>(buffer) + offset, query[i].second);
offset += query[i].second;
}
return true;
}
} // namespace gapir