Repository: duckdb/duckdb
File: src/include/duckdb/common/lru_cache.hpp (C++)

Code before:
//===----------------------------------------------------------------------===//
//                         DuckDB
//
// duckdb/common/lru_cache.hpp
//
//
//===----------------------------------------------------------------------===//

#pragma once

#include <functional>
#include <list>

#include "duckdb/common/common.hpp"
#include "duckdb/common/hash_utils.hpp"
#include "duckdb/common/mutex.hpp"
#include "duckdb/common/shared_ptr.hpp"
#include "duckdb/common/string.hpp"
#include "duckdb/common/unordered_map.hpp"

namespace duckdb {

// A LRU cache implementation, whose value could be accessed in a shared manner with shared pointer.
// Notice, it's not thread-safe.
template <typename Key, typename Val, typename KeyHash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>>
class SharedLruCache {
public:
	using key_type = Key;
	using mapped_type = shared_ptr<Val>;
	using hasher = KeyHash;
	using key_equal = KeyEqual;

	// @param max_memory_p: Maximum total memory (in bytes) of entries. 0 means unlimited.
	explicit SharedLruCache(idx_t max_memory_p) : max_memory(max_memory_p), current_memory(0) {
	}

	// Disable copy and move
	SharedLruCache(const SharedLruCache &) = delete;
	SharedLruCache &operator=(const SharedLruCache &) = delete;

	~SharedLruCache() = default;

	// Insert `value` with key `key`. This will replace any previous entry with the same key.
	// Rough memory consumption is fetched from the given value, which is used in eviction.
	void Put(Key key, shared_ptr<Val> value) {
		if (!value) {
			return;
		}
		auto memory = value->GetRoughCacheMemory();
		Put(std::move(key), std::move(value), memory);

Review comment (security):
Instead of templating with 4 parameters, I think it makes more sense to template with the entire map type. Then there is only one template parameter, and this class becomes potentially more flexible (e.g., you can use it with an `ordered_map` if you want, or a `case_insensitive_map_t`).

Then you can still do this for convenience:
```c++
template <typename MAP_TYPE>
class SharedLRUCache {
public:
    using key_type = MAP_TYPE::key_type;
    // etc.
};
```
And then you can template this with `reference_map<string, shared_ptr<ObjectCacheEntry>>` in `object_cache.hpp`.

Code after:
//===----------------------------------------------------------------------===//
//                         DuckDB
//
// duckdb/common/lru_cache.hpp
//
//
//===----------------------------------------------------------------------===//

#pragma once

#include <functional>

#include "duckdb/common/common.hpp"
#include "duckdb/common/list.hpp"
#include "duckdb/common/mutex.hpp"
#include "duckdb/common/shared_ptr.hpp"
#include "duckdb/common/string.hpp"
#include "duckdb/common/unordered_map.hpp"

namespace duckdb {

// A LRU cache implementation, whose value could be accessed in a shared manner with shared pointer.
// Notice, it's not thread-safe.
template <typename Key, typename Val, typename KeyHash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>>
class SharedLruCache {
public:
	using key_type = Key;
	using mapped_type = shared_ptr<Val>;
	using hasher = KeyHash;
	using key_equal = KeyEqual;

	// @param max_memory_p: Maximum total memory (in bytes) of entries. 0 means unlimited.
	explicit SharedLruCache(idx_t max_memory_p) : max_memory(max_memory_p), current_memory(0) {
	}

	// Disable copy and move
	SharedLruCache(const SharedLruCache &) = delete;
	SharedLruCache &operator=(const SharedLruCache &) = delete;

	~SharedLruCache() = default;

	// Insert `value` with key `key` and explicit memory size. This will replace any previous entry with the same key.
	void Put(Key key, shared_ptr<Val> value, idx_t memory_size) {
		// Remove existing entry if present
		auto existing_it = entry_map.find(key);
		if (existing_it != entry_map.end()) {
			DeleteImpl(existing_it);
		}

		// Evict entries if needed to make room