blob: f5da0523384528c68a848747b7680b4f6d5da4c4 [file] [log] [blame]
/*
* Copyright (C) 2016 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_in_memory_cache.h"
#include <gapic/assert.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
namespace gapir {
std::unique_ptr<ResourceInMemoryCache> ResourceInMemoryCache::create(
std::unique_ptr<ResourceProvider> fallbackProvider, void* buffer) {
return std::unique_ptr<ResourceInMemoryCache>(
new ResourceInMemoryCache(std::move(fallbackProvider), buffer));
}
ResourceInMemoryCache::ResourceInMemoryCache(std::unique_ptr<ResourceProvider> fallbackProvider,
void* buffer)
: ResourceCache(std::move(fallbackProvider))
, mBuffer(static_cast<uint8_t*>(buffer))
, mBufferSize(0)
, mHead(nullptr) {
mHead = new Block(0, mBufferSize);
mHead->next = mHead->prev = mHead;
}
ResourceInMemoryCache::~ResourceInMemoryCache() {
while (mHead->next != mHead) {
Block* next = mHead->next;
next->unlink();
delete next;
}
delete mHead;
}
void ResourceInMemoryCache::prefetch(const Resource* resources,
size_t count,
const ServerConnection& server,
void* temp,
size_t tempSize) {
// Nuke the cache.
// This will leave us with one free block for the entire cache size.
clear();
// Calulate the maximum number of resources we can hold in the cache.
size_t size = 0;
for (size_t i = 0; i < count; i++) {
const Resource& resource = resources[i];
if (size + resource.size > mBufferSize) {
count = i;
break;
}
size += resource.size;
}
if (count == 0) {
return; // Can't fit any.
}
// Fetch them.
if (!mFallbackProvider->get(resources, count, server, mBuffer, size)) {
return; // Couldn't get them.
}
// Create a block for each resource.
uint32_t offset = 0;
for (size_t i = 0; i < count; i++) {
const Resource& resource = resources[i];
auto block = new Block(offset, resource.size, resource.id);
block->linkBefore(mHead);
mCache.emplace(resource.id, offset);
offset += resource.size;
}
if (mBufferSize > offset) {
// We've got some left-over space.
// Adjust head to be the empty space.
mHead->offset = offset;
mHead->size = mBufferSize - offset;
} else {
// Filled. Re-point head to next block.
auto block = mHead;
mHead = block->next;
block->unlink();
delete block;
}
}
void ResourceInMemoryCache::clear() {
mCache.clear();
while (mHead->next != mHead) {
Block* next = mHead->next;
next->unlink();
delete next;
}
*mHead = Block(0, mBufferSize);
mHead->next = mHead->prev = mHead;
}
void ResourceInMemoryCache::resize(size_t newSize) {
// Nuke the cache.
mBufferSize = newSize;
clear();
}
void ResourceInMemoryCache::putCache(const Resource& resource, const void* data) {
if (resource.size > mBufferSize) {
return; // Wouldn't fit even if everything was evicted.
}
// Merge mHead into next block(s) until it is big enough to hold our resource.
while (mHead->size < resource.size) {
auto next = mHead->next;
mHead->size += next->size;
mCache.erase(next->id);
next->unlink();
delete next;
}
if (mHead->size > resource.size) {
// We've got some left-over space in this block. Split it.
size_t space = mHead->size - resource.size;
size_t offset = (mHead->offset + resource.size) % mBufferSize;
auto next = new Block(offset, space);
next->linkAfter(mHead);
mHead->size = resource.size;
}
// Update mCache.
mCache.erase(mHead->id);
mCache.emplace(resource.id, mHead->offset);
mHead->id = resource.id;
// Copy data.
if (mHead->offset + resource.size <= mBufferSize) {
memcpy(mBuffer + mHead->offset, data, resource.size);
} else {
// Wraps the end of the buffer
const uint8_t* dst = reinterpret_cast<const uint8_t*>(data);
size_t a = mBufferSize - mHead->offset;
size_t b = resource.size - a;
memcpy(mBuffer + mHead->offset, dst, a);
memcpy(mBuffer, dst + a, b);
}
// Move head on to the next block.
mHead = mHead->next;
}
bool ResourceInMemoryCache::getCache(const Resource& resource, void* data) {
auto iter = mCache.find(resource.id);
if (iter == mCache.end()) {
return false;
}
// Cached resource found. Copy data.
size_t offset = iter->second;
if (offset + resource.size <= mBufferSize) {
memcpy(data, mBuffer + offset, resource.size);
} else {
// Wraps the end of the buffer
uint8_t* dst = reinterpret_cast<uint8_t*>(data);
size_t a = mBufferSize - offset;
size_t b = resource.size - a;
memcpy(dst, mBuffer + offset, a);
memcpy(dst + a, mBuffer, b);
}
return true;
}
} // namespace gapir