content: package org.apache.solr.request;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.concurrent.atomic.AtomicLong;
  
  import org.apache.solr.request.uninverted.RamTermNumValue;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.alimama.mdrill.utils.UniqConfig;
  
  public class BlockBufferPool<T> {
  	private static Logger log = LoggerFactory.getLogger(BlockBufferPool.class);
  	public static BlockBufferPool<Integer> INT_POOL=new BlockBufferPool<Integer>();
  	public static BlockBufferPool<Short> SHORT_POOL=new BlockBufferPool<Short>();
  	public static BlockBufferPool<Byte> BYTE_POOL=new BlockBufferPool<Byte>();
  	public static BlockBufferPool<Long> LONG_POOL=new BlockBufferPool<Long>();
  	public static BlockBufferPool<Double> DOUBLE_POOL=new BlockBufferPool<Double>();
  	
  	private final static int intervalBits = 12; 
  	private final static int intervalMask = 0xffffffff >>> (32 - intervalBits);
  	public final static int interval = 1 << intervalBits;// 1024*256
  	
  	private AtomicLong mallocTimes = new AtomicLong(0l);
  	private AtomicLong reusedTimes = new AtomicLong(0l);
  
  	private ArrayList<BlockInterface<T>> freeByteBlocks = new ArrayList<BlockInterface<T>>();
  
  	public void recycleByteBlocks(BlockArray<T> data) {
  		this.recycleByteBlocks(data,0);
  		
  	}
  	
  	public void recycleByteBlocks(BlockArray<T> data,int start) {
  		synchronized (BlockBufferPool.this) {
  			int allowsize = UniqConfig.getBlockBufferPoolSize() - this.freeByteBlocks.size();
  			final int size = data.data.length;
  			for (int i = start; i < size; i++) {
  				if (i < allowsize) {
  					freeByteBlocks.add(data.data[i]);
  				}
  				data.data[i] = null;
  			}
  			data.data = null;
  			data.size = 0;
  		}
  	}
  	
  	private BlockInterface<T> getByteBlock(CreateArr<T> c) {
        synchronized(BlockBufferPool.this) {
          final int size = freeByteBlocks.size();
          final  BlockInterface<T> b;
  			if (0 == size) {
  				b = c.create();
  				mallocTimes.incrementAndGet();
  			} else {
  				b = freeByteBlocks.remove(size - 1);
  				reusedTimes.incrementAndGet();
  			}
          return b;
        }
      }
  	
  	public BlockArray<T> calloc(int size, CreateArr<T> c, T init) {
  		int block = size / interval + 1;
  		BlockInterface<T>[] data = c.createBlocks(block);
  		for (int i = 0; i < data.length; i++) {
  			data[i]=getByteBlock(c);
  			data[i].allset(init);
  		}
  		
  		return new BlockArray<T>(data, size);
  	}
  	
  	
  	public BlockArray<T> reCalloc(BlockArray<T> old,int size, CreateArr<T> c, T init) {
  		int block = size / interval + 1;
  		BlockInterface<T>[] data = c.createBlocks(block);
  		for (int i = 0; i < data.length; i++) {
  			if(i<old.data.length)
  			{
  				data[i]=old.data[i];
  			}else{
  				data[i]=getByteBlock(c);
  				data[i].allset(init);
  			}
  		}
  		
  		this.recycleByteBlocks(old,data.length);
  		return new BlockArray<T>(data, size);
  	}
  	
  	
  	public void allset(BlockArray<T> last, CreateArr<T> c, T init) {
  		for (int i = 0; i < last.data.length; i++) {
  			last.data[i].allset(init);
  		}
  	}
  	
  	
  
  	public static CreateArr<Byte> BYTE_CREATE = new CreateArr<Byte>() {
  		@Override
  		public BlockInterface<Byte> create() {
  			return new BlockByte();
  		}
  
  		@Override
  		public BlockInterface<Byte>[] createBlocks(int size) {
  			return new BlockByte[size];
  		}
  	};
  
  	public static CreateArr<Integer> INT_CREATE = new CreateArr<Integer>() {
  
  		@Override
  		public BlockInterface<Integer> create() {
  			return new BlockInteger();
  		}
  
  		@Override
  		public BlockInterface<Integer>[] createBlocks(int size) {
  			return new BlockInteger[size];
  		}
  
  	};
  
  	public static CreateArr<Short> SHORT_CREATE = new CreateArr<Short>() {
  
  		@Override
  		public BlockInterface<Short> create() {
  			return new BlockShort();
  		}
  
  		@Override
  		public BlockInterface<Short>[] createBlocks(int size) {
  			return new BlockShort[size];
  		}
  
  	};
  
  	public static CreateArr<Long> LONG_CREATE = new CreateArr<Long>() {
  
  		@Override
  		public BlockInterface<Long> create() {
  			return new BlockLong();
  
  		}
  
  		@Override
  		public BlockInterface<Long>[] createBlocks(int size) {
  			return new BlockLong[size];
  
  		}
  
  	};
  
  	public static CreateArr<Double> DOUBLE_CREATE = new CreateArr<Double>() {
  		@Override
  		public BlockInterface<Double> create() {
  			return new BlockDouble();
  		}
  
  		@Override
  		public BlockInterface<Double>[] createBlocks(int size) {
  			return new BlockDouble[size];
  		}
  
  	};
  
  	public static interface CreateArr<T> {
  		public BlockInterface<T> create();
  
  		public BlockInterface<T>[] createBlocks(int size);
  	}
  
  	private static class QuickArrayFillInt
  	{
  		private static int[] data_1=make(-1);
  
  		private static int[] make(int def)
  		{
  			int[] data=new int[BlockBufferPool.interval];
  			Arrays.fill(data, def);	
  			return data;
  		}
  		
  		public static void fill(int[] data,int v)
  		{
  			if(v==-1)
  			{
  				System.arraycopy(data_1, 0, data, 0,data_1.length);
  			}else{
  				Arrays.fill(data, v);	
  			}
  		}
  	}
  	
  	
  	private static class QuickArrayFillShort
  	{
  		private static short cmp=(short)-1;
  
  		private static short[] data_1=make(cmp);
  
  		private static short[] make(short def)
  		{
  			short[] data=new short[BlockBufferPool.interval];
  			Arrays.fill(data, def);	
  			return data;
  		}
  		
  		public static void fill(short[] data,short v)
  		{
  			if(v==cmp)
  			{
  				System.arraycopy(data_1, 0, data, 0,data_1.length);
  			}else{
  				Arrays.fill(data, v);	
  			}
  		}
  	}
  	
  	
  	private static class QuickArrayFillByte
  	{
  		private static byte cmp=(byte)-1;
  
  		private static byte[] data_1=make((byte)-1);
  
  		private static byte[] make(byte def)
  		{
  			byte[] data=new byte[BlockBufferPool.interval];
  			Arrays.fill(data, def);	
  			return data;
  		}
  		
  		public static void fill(byte[] data,byte v)
  		{
  			if(v==cmp)
  			{
  				System.arraycopy(data_1, 0, data, 0,data_1.length);
  			}else{
  				Arrays.fill(data, v);	
  			}
  		}
  	}
  	
  	private static class QuickArrayFillLong
  	{
  
  		private static long[] data_1=make(-1l);
  		private static long[] data_2=make(RamTermNumValue.TERMNUM_NAN_VALUE);
  
  
  		private static long[] make(long def)
  		{
  			long[] data=new long[BlockBufferPool.interval];
  			Arrays.fill(data, def);	
  			return data;
  		}
  		
  		public static void fill(long[] data,long v)
  		{
  			if(v==-1l)
  			{
  				System.arraycopy(data_1, 0, data, 0,data_1.length);
  			}else if(v==RamTermNumValue.TERMNUM_NAN_VALUE)
  			{
  				System.arraycopy(data_2, 0, data, 0,data_2.length);
  			}else{
  				Arrays.fill(data, v);	
  			}
  		}
  	
  	}
  	
  	private static class QuickArrayFillDouble
  	{
  
  		private static double[] data_1=make(-1d);
  		private static double dbl=(double)RamTermNumValue.TERMNUM_NAN_VALUE;
  		private static double[] data_2=make(dbl);
  
  
  		private static double[] make(double def)
  		{
  			double[] data=new double[BlockBufferPool.interval];
  			Arrays.fill(data, def);	
  			return data;
  		}
  		
  		public static void fill(double[] data,double v)
  		{
  			if(v==-1l)
  			{
  				System.arraycopy(data_1, 0, data, 0,data_1.length);
  			}else if(v==dbl)
  			{
  				System.arraycopy(data_2, 0, data, 0,data_2.length);
  			}else{
  				Arrays.fill(data, v);	
  			}
  		}
  	
  	}
  	
  	
  	public static class BlockInteger implements BlockInterface<Integer>
  	{
  		private boolean hasInit=false;
  		private int def=0;
  		private int[] data=null;
  		
  		@Override
  		public Integer get(int i) {
  			if(hasInit)
  			{
  				return data[i];
  			}
  			return def;
  		}
  
  		@Override
  		public void set(int i, Integer v) {
  			this.maybeinit();
  			data[i]=v;
  		}
  
  		@Override
  		public void allset(Integer v) {
  			this.def=v;
  			hasInit=false;
  		}
  		
  		private void maybeinit()
  		{
  			if(hasInit)
  			{
  				return ;
  			}
  			if(this.data==null)
  			{
  				data=new int[BlockBufferPool.interval];
  			}
  			
  			QuickArrayFillInt.fill(data, def);	
  			hasInit=true;
  		}
  
  		@Override
  		public void replace(Integer a, Integer b) {
  			if(this.hasInit)
  			{
  				for(int i=0;i<this.data.length;i++)
  				{
  					if(this.data[i]==a)
  					{
  						this.data[i]=b;
  					}
  				}
  			}else if(this.def==a){
  				this.allset(b);
  			}
  					
  		}
  		
  		@Override
  		public void fillByInt(BlockInteger d,int skip){
  			if(d.hasInit)
  			{
  				if(this.hasInit)
  				{
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=d.data[i];
  						}
  					}
  				}else{
  					data=new int[BlockBufferPool.interval];
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=d.data[i];
  						}else{
  							data[i]=this.def;
  						}
  					}
  					hasInit=true;
  				}
  			}else if(d.def!=skip){
  				this.allset(d.def);
  			}
  		}
  
  	}
  	
  	public static class BlockShort implements BlockInterface<Short>
  	{
  		private boolean hasInit=false;
  		private short def=0;
  		
  		short[] data=null;
  
  		@Override
  		public Short get(int i) {
  			if(hasInit)
  			{
  				return data[i];
  			}
  			return def;
  		}
  		
  		@Override
  		public void fillByInt(BlockInteger d,int skip){
  
  			if(d.hasInit)
  			{
  				if(this.hasInit)
  				{
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=(short)d.data[i];
  						}
  					}
  				}else{
  					data=new short[BlockBufferPool.interval];
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=(short)d.data[i];
  						}else{
  							data[i]=this.def;
  						}
  					}
  					hasInit=true;
  				}
  			}else if(d.def!=skip){
  				this.allset((short)d.def);
  			}
  		
  		
  		}
  
  		@Override
  		public void set(int i, Short v) {
  			this.maybeinit();
  			data[i]=v;
  		}
  
  		@Override
  		public void allset(Short v) {
  			this.def=v;
  			hasInit=false;
  		
  		}
  		
  		private void maybeinit()
  		{
  			if(hasInit)
  			{
  				return ;
  			}
  			if(this.data==null)
  			{
  				data=new short[BlockBufferPool.interval];
  			}
  			
  			QuickArrayFillShort.fill(data, def);	
  			hasInit=true;
  		}
  
  		@Override
  		public void replace(Short a, Short b) {
  
  			if(this.hasInit)
  			{
  				for(int i=0;i<this.data.length;i++)
  				{
  					if(this.data[i]==a)
  					{
  						this.data[i]=b;
  					}
  				}
  			}else if(this.def==a){
  				this.allset(b);
  			}
  					
  		
  			
  		}
  		
  	
  	}
  	
  	public static class BlockLong implements BlockInterface<Long>
  	{
  		private boolean hasInit=false;
  		private long def=0;
  		
  		long[] data=null;
  
  
  		@Override
  		public Long get(int i) {
  			if(hasInit)
  			{
  				return data[i];
  			}
  			return def;
  		}
  		
  		@Override
  		public void fillByInt(BlockInteger d,int skip){
  
  
  			if(d.hasInit)
  			{
  				if(this.hasInit)
  				{
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=(long)d.data[i];
  						}
  					}
  				}else{
  					data=new long[BlockBufferPool.interval];
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=(long)d.data[i];
  						}else{
  							data[i]=this.def;
  						}
  					}
  					hasInit=true;
  				}
  			}else if(d.def!=skip){
  				this.allset((long)d.def);
  			}
  		
  		}
  
  		@Override
  		public void set(int i, Long v) {
  			this.maybeinit();
  			data[i]=v;
  		}
  
  		@Override
  		public void allset(Long v) {
  			this.def=v;
  			hasInit=false;
  		}
  		
  		private void maybeinit()
  		{
  			if(hasInit)
  			{
  				return ;
  			}
  			if(this.data==null)
  			{
  				data=new long[BlockBufferPool.interval];
  			}
  			
  			QuickArrayFillLong.fill(data, def);	
  			hasInit=true;
  		}
  
  		@Override
  		public void replace(Long a, Long b) {
  
  			if(this.hasInit)
  			{
  				for(int i=0;i<this.data.length;i++)
  				{
  					if(this.data[i]==a)
  					{
  						this.data[i]=b;
  					}
  				}
  			}else if(this.def==a){
  				this.allset(b);
  			}
  					
  		
  		}
  	
  	}
  	
  	
  	public static class BlockDouble implements BlockInterface<Double>
  	{
  		private boolean hasInit=false;
  		private double def=0;
  		
  		double[] data=null;
  
  		
  		@Override
  		public Double get(int i) {
  			if(hasInit)
  			{
  				return data[i];
  			}
  			return def;
  		}
  
  		@Override
  		public void set(int i, Double v) {
  			this.maybeinit();
  			data[i]=v;
  		}
  
  		@Override
  		public void allset(Double v) {
  			this.def=v;
  			hasInit=false;
  		}
  		
  		@Override
  		public void fillByInt(BlockInteger d,int skip){
  
  
  			if(d.hasInit)
  			{
  				if(this.hasInit)
  				{
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=(double)d.data[i];
  						}
  					}
  				}else{
  					data=new double[BlockBufferPool.interval];
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=(double)d.data[i];
  						}else{
  							data[i]=this.def;
  						}
  					}
  					hasInit=true;
  				}
  			}else if(d.def!=skip){
  				this.allset((double)d.def);
  			}
  		
  		}
  		
  		private void maybeinit()
  		{
  			if(hasInit)
  			{
  				return ;
  			}
  			if(this.data==null)
  			{
  				data=new double[BlockBufferPool.interval];
  			}
  			
  			QuickArrayFillDouble.fill(data, def);	
  			hasInit=true;
  		}
  
  		@Override
  		public void replace(Double a, Double b) {
  
  			if(this.hasInit)
  			{
  				for(int i=0;i<this.data.length;i++)
  				{
  					if(this.data[i]==a)
  					{
  						this.data[i]=b;
  					}
  				}
  			}else if(this.def==a){
  				this.allset(b);
  			}
  					
  		
  			
  		}
  	}
  	
  	public static class BlockByte implements BlockInterface<Byte>
  	{
  		private boolean hasInit=false;
  		private byte def=0;
  		
  		public byte[] data=null;
  	
  		
  		@Override
  		public Byte get(int i) {
  			if(hasInit)
  			{
  				return data[i];
  			}
  			return def;
  		}
  
  		@Override
  		public void set(int i, Byte v) {
  			this.maybeinit();
  			data[i]=v;
  		}
  		
  		@Override
  		public void fillByInt(BlockInteger d,int skip){
  
  			if(d.hasInit)
  			{
  				if(this.hasInit)
  				{
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=(byte)d.data[i];
  						}
  					}
  				}else{
  					data=new byte[BlockBufferPool.interval];
  					for(int i=0;i<d.data.length;i++)
  					{
  						if(d.data[i]!=skip)
  						{
  							data[i]=(byte)d.data[i];
  						}else{
  							data[i]=this.def;
  						}
  					}
  					hasInit=true;
  				}
  			}else if(d.def!=skip){
  				this.allset((byte)d.def);
  			}
  		
  		
  		}
  
  		@Override
  		public void allset(Byte v) {
  			this.def=v;
  			hasInit=false;
  		}
  		
  		private void maybeinit()
  		{
  			if(hasInit)
  			{
  				return ;
  			}
  			if(this.data==null)
  			{
  				data=new byte[BlockBufferPool.interval];
  			}
  			
  			QuickArrayFillByte.fill(data, def);	
  			hasInit=true;
  		}
  
  		@Override
  		public void replace(Byte a, Byte b) {
  
  			if(this.hasInit)
  			{
  				for(int i=0;i<this.data.length;i++)
  				{
  					if(this.data[i]==a)
  					{
  						this.data[i]=b;
  					}
  				}
  			}else if(this.def==a){
  				this.allset(b);
  			}
  		}
  	}
  	
  	
  	public static interface BlockInterface<K>{
  		public  K get(int i);
  		public  void set(int i,K v);
  		public  void allset(K v);
  		
  		public void fillByInt(BlockInteger d,int skip);
  		public void replace(K a,K b);
  
  
  	}
  	
  	public static class BlockArray<K> {
  		private int size = 0;
  		public BlockInterface<K>[] data;
  		public BlockArray(BlockInterface<K>[] data, int size) {
  			this.data = data;
  			this.size = size;
  		}
  		
  		public void fillByInt(BlockArray<Integer> d,int skip)
  		{
  			for(int i=0;i<d.data.length&&i<this.data.length;i++)
  			{
  				this.data[i].fillByInt((BlockInteger)(d.data[i]), skip);
  			}
  		}
  		
  		public void replace(K a,K b)
  		{
  			for(int i=0;i<this.data.length;i++)
  			{
  				this.data[i].replace(a,b);
  			}
  		}
  		
  		public K get(int i) {
  			int block = i >>> intervalBits;
  			int pos = i & intervalMask;
  			return data[block].get(pos);
  		}
  		public void set(int i, K v) {
  			int block = i >>> intervalBits;
  			int pos = i & intervalMask;
  			data[block].set(pos, v);
  		}
  		public int getSize() {
  			return size;
  		}
  		
  		public int getMemSize() {
  			return Math.max(data.length*interval, size);
  		}
  	}
  
  }
  