| // Copyright 2022 The Cobalt Authors. All Rights Reserved. |
| // |
| // 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 "cobalt/cache/memory_capped_directory.h" |
| |
| #include <algorithm> |
| |
| #include "base/files/file_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "starboard/directory.h" |
| |
| namespace cobalt { |
| namespace cache { |
| |
| MemoryCappedDirectory::FileInfo::FileInfo( |
| base::FilePath directory_path, base::FileEnumerator::FileInfo file_info) |
| : file_path_(directory_path.Append(file_info.GetName())), |
| last_modified_time_(file_info.GetLastModifiedTime()), |
| size_(static_cast<uint32_t>(file_info.GetSize())) {} |
| |
| MemoryCappedDirectory::FileInfo::FileInfo(base::FilePath file_path, |
| base::Time last_modified_time, |
| uint32_t size) |
| : file_path_(file_path), |
| last_modified_time_(last_modified_time), |
| size_(size) {} |
| |
| bool MemoryCappedDirectory::FileInfo::OldestFirst::operator()( |
| const MemoryCappedDirectory::FileInfo& left, |
| const MemoryCappedDirectory::FileInfo& right) const { |
| return left.last_modified_time_ > right.last_modified_time_; |
| } |
| |
| // static |
| std::unique_ptr<MemoryCappedDirectory> MemoryCappedDirectory::Create( |
| const base::FilePath& directory_path, uint32_t max_size) { |
| if (!SbDirectoryCreate(directory_path.value().c_str())) { |
| return nullptr; |
| } |
| auto memory_capped_directory = std::unique_ptr<MemoryCappedDirectory>( |
| new MemoryCappedDirectory(directory_path, max_size)); |
| auto* heap = &memory_capped_directory->file_info_heap_; |
| base::AutoLock auto_lock(memory_capped_directory->lock_); |
| base::FileEnumerator file_enumerator(directory_path, false, |
| base::FileEnumerator::FILES); |
| uint32_t size = 0u; |
| while (!file_enumerator.Next().empty()) { |
| MemoryCappedDirectory::FileInfo file_info(directory_path, |
| file_enumerator.GetInfo()); |
| heap->push_back(file_info); |
| memory_capped_directory->file_sizes_[file_info.file_path_] = |
| file_info.size_; |
| memory_capped_directory->size_ += file_info.size_; |
| } |
| if (heap->size() > 1) { |
| std::make_heap(heap->begin(), heap->end(), |
| MemoryCappedDirectory::FileInfo::OldestFirst()); |
| } |
| return memory_capped_directory; |
| } |
| |
| void MemoryCappedDirectory::Delete(uint32_t key) { |
| base::AutoLock auto_lock(lock_); |
| auto file_path = GetFilePath(key); |
| if (base::PathExists(file_path)) { |
| base::DeleteFile(file_path, false); |
| } |
| file_sizes_.erase(file_path); |
| auto* heap = &file_info_heap_; |
| for (auto it = heap->begin(); it != heap->end(); ++it) { |
| if (it->file_path_ == file_path) { |
| size_ -= it->size_; |
| heap->erase(it); |
| if (heap->size() > 1) { |
| std::make_heap(heap->begin(), heap->end(), |
| MemoryCappedDirectory::FileInfo::OldestFirst()); |
| } |
| return; |
| } |
| } |
| } |
| |
| std::unique_ptr<std::vector<uint8_t>> MemoryCappedDirectory::Retrieve( |
| uint32_t key) { |
| auto file_path = GetFilePath(key); |
| auto it = file_sizes_.find(file_path); |
| if (it == file_sizes_.end()) { |
| return nullptr; |
| } |
| auto size = it->second; |
| auto data = std::make_unique<std::vector<uint8_t>>(static_cast<size_t>(size)); |
| int bytes_read = base::ReadFile( |
| file_path, reinterpret_cast<char*>(data->data()), static_cast<int>(size)); |
| if (bytes_read != size) { |
| return nullptr; |
| } |
| return data; |
| } |
| |
| void MemoryCappedDirectory::Store(uint32_t key, |
| const std::vector<uint8_t>& data) { |
| base::AutoLock auto_lock(lock_); |
| auto file_path = GetFilePath(key); |
| uint32_t new_entry_size = static_cast<uint32_t>(data.size()); |
| if (!EnsureEnoughSpace(new_entry_size)) { |
| return; |
| } |
| int bytes_written = base::WriteFile( |
| file_path, reinterpret_cast<const char*>(data.data()), data.size()); |
| if (bytes_written != data.size()) { |
| base::DeleteFile(file_path, false); |
| return; |
| } |
| size_ += new_entry_size; |
| auto* heap = &file_info_heap_; |
| heap->push_back(MemoryCappedDirectory::FileInfo(file_path, base::Time::Now(), |
| new_entry_size)); |
| std::push_heap(heap->begin(), heap->end(), |
| MemoryCappedDirectory::FileInfo::OldestFirst()); |
| file_sizes_[file_path] = new_entry_size; |
| } |
| |
| MemoryCappedDirectory::MemoryCappedDirectory( |
| const base::FilePath& directory_path, uint32_t max_size) |
| : directory_path_(directory_path), max_size_(max_size), size_(0u) {} |
| |
| base::FilePath MemoryCappedDirectory::GetFilePath(uint32_t key) const { |
| return directory_path_.Append(base::UintToString(key)); |
| } |
| |
| bool MemoryCappedDirectory::EnsureEnoughSpace( |
| uint32_t additional_size_required) { |
| if (additional_size_required > max_size_) { |
| return false; |
| } |
| auto* heap = &file_info_heap_; |
| while (size_ + additional_size_required > max_size_) { |
| if (heap->size() == 0) { |
| return false; |
| } |
| std::pop_heap(heap->begin(), heap->end(), |
| MemoryCappedDirectory::FileInfo::OldestFirst()); |
| auto removed = heap->back(); |
| size_ -= removed.size_; |
| base::DeleteFile(removed.file_path_, false); |
| file_sizes_.erase(removed.file_path_); |
| heap->pop_back(); |
| } |
| return true; |
| } |
| |
| } // namespace cache |
| } // namespace cobalt |