#pragma clang system_header // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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. #pragma once #include #include #include #include #include #include #include #include #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/functional.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/logging.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/macros.h" namespace arrow20 { namespace internal { // A LRU (Least recently used) replacement cache template class LruCache { public: explicit LruCache(int32_t capacity) : capacity_(capacity) { // The map size can temporarily exceed the cache capacity, see Replace() map_.reserve(capacity_ + 1); } ARROW_DISALLOW_COPY_AND_ASSIGN(LruCache); ARROW_DEFAULT_MOVE_AND_ASSIGN(LruCache); void Clear() { items_.clear(); map_.clear(); // The C++ spec doesn't tell whether map_.clear() will shrink the map capacity map_.reserve(capacity_ + 1); } int32_t size() const { DCHECK_EQ(items_.size(), map_.size()); return static_cast(items_.size()); } template Value* Find(K&& key) { const auto it = map_.find(key); if (it == map_.end()) { return nullptr; } else { // Found => move item at front of the list auto list_it = it->second; items_.splice(items_.begin(), items_, list_it); return &list_it->value; } } template std::pair Replace(K&& key, V&& value) { // Try to insert temporary iterator auto pair = map_.emplace(std::forward(key), ListIt{}); const auto it = pair.first; const bool inserted = pair.second; if (inserted) { // Inserted => push item at front of the list, and update iterator items_.push_front(Item{&it->first, std::forward(value)}); it->second = items_.begin(); // Did we exceed the cache capacity? If so, remove least recently used item if (static_cast(items_.size()) > capacity_) { const bool erased = map_.erase(*items_.back().key); DCHECK(erased); ARROW_UNUSED(erased); items_.pop_back(); } return {true, &it->second->value}; } else { // Already exists => move item at front of the list, and update value auto list_it = it->second; items_.splice(items_.begin(), items_, list_it); list_it->value = std::forward(value); return {false, &list_it->value}; } } private: struct Item { // Pointer to the key inside the unordered_map const Key* key; Value value; }; using List = std::list; using ListIt = typename List::iterator; const int32_t capacity_; // In most to least recently used order std::list items_; std::unordered_map map_; }; namespace detail { template struct ThreadSafeMemoizer { using RetType = Value; template ThreadSafeMemoizer(F&& func, int32_t cache_capacity) : func_(std::forward(func)), cache_(cache_capacity) {} // The memoizer can't return a pointer to the cached value, because // the cache entry may be evicted by another thread. Value operator()(const Key& key) { std::unique_lock lock(mutex_); const Value* value_ptr; value_ptr = cache_.Find(key); if (ARROW_PREDICT_TRUE(value_ptr != nullptr)) { return *value_ptr; } lock.unlock(); Value v = func_(key); lock.lock(); return *cache_.Replace(key, std::move(v)).second; } private: std::mutex mutex_; Func func_; Cache cache_; }; template struct ThreadUnsafeMemoizer { using RetType = const Value&; template ThreadUnsafeMemoizer(F&& func, int32_t cache_capacity) : func_(std::forward(func)), cache_(cache_capacity) {} const Value& operator()(const Key& key) { const Value* value_ptr; value_ptr = cache_.Find(key); if (ARROW_PREDICT_TRUE(value_ptr != nullptr)) { return *value_ptr; } return *cache_.Replace(key, func_(key)).second; } private: Func func_; Cache cache_; }; template