/*
* Copyright 2013 Websquared, Inc.
*
* 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.
*/
package org.fastcatsearch.ir.io.cache;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LRUBlockCache {
private static Logger logger = LoggerFactory.getLogger(LRUBlockCache.class);
private static AtomicInteger totalCacheSize = new AtomicInteger();
private CachedBlock head; //circular linked list
private CachedBlock[] dataList;
private int bucketSize;
// private static int BLOCK_SIZE = IOUtil.FILEBLOCKSIZE;
private int count;
private int limitSize;
public LRUBlockCache(){
bucketSize = 1024;
dataList = new CachedBlock[bucketSize];
head = new CachedBlock();
count = 0;
limitSize = 1024;
}
public synchronized CachedBlock getBlock(int block){
int key = block & (bucketSize - 1);
// key = block % bucketSize;
CachedBlock data = null;
CachedBlock prev = null;
for(data = dataList[key]; data != null && data.block != block; prev = data, data = data.next){
}
if(data == null){
return null;
}
//assert data.pos == pos
//data는 중간에서 빠진다.
if(prev != null)
prev.next = data.next;
//data는 맨앞으로 이동한다.
if(dataList[key] != data){
CachedBlock tmp = dataList[key];
dataList[key] = data;
dataList[key].next = tmp;
}
data.before.after = data.after;
CachedBlock tmp = head.after;
head.after = data;
data.before = head;
data.after = tmp;
tmp.before = data;
return data;
}
public synchronized void putBlock(CachedBlock newData){
int key = newData.block & (bucketSize - 1);
//logger.info("block = "+newData.block+", key="+key+", newData="+newData+", count="+count+", mem="+Runtime.getRuntime().totalMemory());
System.out.println(dataList.length+", "+bucketSize);
CachedBlock data = dataList[key];
if(data == null){
dataList[key] = newData;
}else{
dataList[key] = newData;
dataList[key].next = data;
}
//circular list
//head before put!!
if (count == 0) {
head.before = newData;
head.after = newData;
newData.after = head;
newData.before = head;
}else{
CachedBlock temp = head.before;
head.before = newData;
newData.after = head;
newData.before = temp;
temp.after = newData;
}
count++;
totalCacheSize.getAndAdd(1);
this.check();
}
public synchronized void reduce(){
int targetCount = (int) (count * 0.9);
logger.info("Reduce "+targetCount);
for (; count > targetCount; count--) {
CachedBlock data = head.after;
if(data != null){
data.remove();
totalCacheSize.getAndAdd(-1);
System.out.println(totalCacheSize.get()+">>"+dataList.length);
}
}
}
public void rehash(){
//TODO
int newBucketSize = (int) (bucketSize * 1.5);
System.out.println("REHASH to "+newBucketSize);
CachedBlock[] newDataList = new CachedBlock[newBucketSize];
for (int i = 0; i < dataList.length; i++) {
CachedBlock data = dataList[i];
while (data != null) {
int hashPos = data.block & (bucketSize - 1);
CachedBlock nextData = data.next;
data.next = newDataList[hashPos];
newDataList[hashPos] = data;
data = nextData;
}
}
bucketSize = newBucketSize;
dataList = newDataList;
}
public int size(){
int size = 0;
while(head.next != null){
size += head.after.size();
}
return size;
}
public void check() {
if (totalCacheSize.get() >= this.getLimitSize()) {
this.reduce();
}
if (count >= bucketSize) {
this.rehash();
}
}
public int getLimitSize() {
return limitSize;
}
public void setLimitSize(int limitSize) {
this.limitSize = limitSize;
}
}