#include "btree_map.h"
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include "murmurhash2.h"
|
|
#include "binary.h"
|
|
#include "erl_nif.h"
|
|
|
|
// extend std::hash to understand Binary type
|
|
namespace std {
|
|
template <>
|
|
struct hash<Binary>
|
|
{
|
|
size_t operator()(const Binary& b) const
|
|
{
|
|
return MurmurHash2(b.bin, b.size, 4242);
|
|
}
|
|
};
|
|
}
|
|
|
|
template <typename K, typename V>
|
|
struct LRUNode
|
|
{
|
|
K key;
|
|
V data;
|
|
void *kvenv;
|
|
LRUNode<K,V> *prev;
|
|
LRUNode<K,V> *next;
|
|
size_t size;
|
|
LRUNode(void *kvenv = NULL, size_t size=0) : kvenv(kvenv), prev(NULL), next(NULL), size(size) { }
|
|
|
|
/*
|
|
static void *LRUNode<ErlTerm,ErlTerm>::operator new(size_t size) {
|
|
return enif_alloc(size);
|
|
}
|
|
|
|
static void operator delete(void *block) {
|
|
enif_free(block);
|
|
}
|
|
*/
|
|
|
|
void printChain() {
|
|
LRUNode<K,V>* node;
|
|
int i=11;
|
|
std::cout << "(";
|
|
for(node = this; node && i; node = node->next, i--) {
|
|
std::cout << node->key << " -> ";
|
|
}
|
|
if (node) {
|
|
std::cout << " loop detection end ";
|
|
} else {
|
|
std::cout << " end ";
|
|
}
|
|
std::cout << ")" << std::endl;
|
|
}
|
|
|
|
void printNextPrevKey() {
|
|
std::cout << "(";
|
|
printNextKey();
|
|
printPrevKey();
|
|
std::cout << ")";
|
|
}
|
|
|
|
void printNextKey() {
|
|
if (next) {
|
|
std::cout << "next key " << next->key << " ";
|
|
}
|
|
}
|
|
|
|
void printPrevKey() {
|
|
if (prev) {
|
|
std::cout << "prev key " << prev->key << " ";
|
|
}
|
|
}
|
|
};
|
|
|
|
template <class K,class V>
|
|
class LRUBtree {
|
|
private:
|
|
LRUNode<K,V> *oldest;
|
|
LRUNode<K,V> *latest;
|
|
unsigned long size;
|
|
unsigned long max_size;
|
|
void (*node_free)(LRUBtree<K,V> *lru, LRUNode<K,V> *node);
|
|
void (*node_kickout)(LRUBtree<K,V> *lru, LRUNode<K,V> *node, void *call_env);
|
|
typedef btree::btree_map<K, LRUNode<K,V>*> LRUBtree_map;
|
|
|
|
public:
|
|
LRUBtree_map bmap;
|
|
bool pid_set = false;
|
|
ErlNifPid pid;
|
|
typedef typename LRUBtree_map::iterator iterator;
|
|
typedef typename LRUBtree_map::reverse_iterator reverse_iterator;
|
|
|
|
void printLatest() {
|
|
if (latest) {
|
|
std::cout << " latest " << latest->key;
|
|
} else {
|
|
std::cout << " no data in lru ";
|
|
}
|
|
}
|
|
|
|
private:
|
|
LRUNode<K,V>* erase(LRUNode<K,V> *node) {
|
|
if (node->next) {
|
|
node->next->prev = node->prev;
|
|
}
|
|
if (node->prev) {
|
|
node->prev->next = node->next;
|
|
}
|
|
|
|
if (node == oldest) {
|
|
oldest = node->prev;
|
|
}
|
|
|
|
if (node == latest) {
|
|
latest = node->next;
|
|
}
|
|
|
|
if (node_free) {
|
|
node_free(this, node);
|
|
}
|
|
|
|
node->next = NULL;
|
|
node->prev = NULL;
|
|
return node;
|
|
}
|
|
|
|
void printOldest() {
|
|
if(oldest) {
|
|
std::cout << " oldest " << oldest->key;
|
|
} else {
|
|
std::cout << " no data in lru ";
|
|
}
|
|
}
|
|
|
|
void check_size(void *call_env) {
|
|
if (size > max_size) {
|
|
if (oldest) { // remove check if oldest exist and rely on max_size always being positive
|
|
if (node_kickout)
|
|
node_kickout(this, oldest, call_env);
|
|
erase(oldest->key);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SIZE_100MB 100*1024*1024
|
|
public:
|
|
LRUBtree(unsigned long max_size = SIZE_100MB,
|
|
void (*node_free)(LRUBtree<K,V> *lru, LRUNode<K,V> *node) = NULL,
|
|
void (*node_kickout)(LRUBtree<K,V> *lru, LRUNode<K,V> *node, void *call_env) = NULL)
|
|
: oldest(NULL), latest(NULL), size(0), max_size(max_size), node_free(node_free),
|
|
node_kickout(node_kickout) { }
|
|
|
|
~LRUBtree() {
|
|
LRUNode<K,V> *node;
|
|
LRUNode<K,V> *next;
|
|
node = latest;
|
|
while(node) {
|
|
if (node_free) {
|
|
node_free(this, node);
|
|
}
|
|
next = node->next;
|
|
delete node;
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
void printSize() {
|
|
std::cout << "size " << size << std::endl;
|
|
}
|
|
|
|
unsigned long getSize() {
|
|
return size;
|
|
}
|
|
|
|
unsigned long getMaxSize() {
|
|
return max_size;
|
|
}
|
|
|
|
void setMaxSize(unsigned long max_size) {
|
|
this->max_size = max_size;
|
|
}
|
|
|
|
void erase(K key) {
|
|
LRUNode<K,V> *node;
|
|
if ((node = bmap[key])) {
|
|
erase(node);
|
|
bmap.erase(key);
|
|
size -= node->size;
|
|
delete node;
|
|
}
|
|
}
|
|
|
|
inline void put(K key, V data,
|
|
void *kvenv = NULL, void *call_env = NULL,
|
|
size_t size = 1) {
|
|
LRUNode<K,V> *node;
|
|
|
|
this->size += size;
|
|
check_size(call_env);
|
|
|
|
// overwrite already existing key
|
|
if ((node = bmap[key])) {
|
|
this->size -= node->size;
|
|
erase(node);
|
|
node->kvenv = kvenv;
|
|
node->next = latest;
|
|
node->size = size;
|
|
if (node->next) {
|
|
node->next->prev = node;
|
|
}
|
|
if (!oldest) {
|
|
oldest = node;
|
|
}
|
|
latest = node;
|
|
node->key = key;
|
|
node->data = data;
|
|
}
|
|
|
|
else if (!oldest) {
|
|
node = new LRUNode<K,V>;
|
|
node->key = key;
|
|
node->data = data;
|
|
node->kvenv = kvenv;
|
|
node->size = size;
|
|
oldest = node;
|
|
latest = node;
|
|
bmap[node->key] = node;
|
|
}
|
|
|
|
else {
|
|
node = new LRUNode<K,V>;
|
|
node->key = key;
|
|
node->data = data;
|
|
node->kvenv = kvenv;
|
|
node->size = size;
|
|
latest->prev = node;
|
|
node->next = latest;
|
|
latest = node;
|
|
bmap[node->key] = node;
|
|
}
|
|
}
|
|
|
|
LRUNode<K,V>* get(K key) {
|
|
return bmap[key];
|
|
}
|
|
|
|
LRUNode<K,V>* getOldest() {
|
|
return oldest;
|
|
}
|
|
|
|
LRUNode<K,V>* getLatest() {
|
|
return latest;
|
|
}
|
|
|
|
LRUNode<K,V>* getNext(LRUNode<K,V> *node) {
|
|
return node->next;
|
|
}
|
|
|
|
LRUNode<K,V>* getPrev(LRUNode<K,V> *node) {
|
|
return node->prev;
|
|
|
|
}
|
|
};
|
|
|
|
|