/** * */ package net.varkhan.base.containers.list; import net.varkhan.base.containers.Indexed; import java.io.IOException; import java.io.InvalidClassException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Field; import java.security.PrivilegedActionException; /** * <b>Abstract IndexedList using resizable arrays of blocks</b>. * <p/> * This abstract list framework is used as a common base for type-specific * concrete implementations, that store objects in arbitrary positions (their * index, in the sense of {@link Indexed}). * <p/> * The entries are kept in a resizable array of storage blocks, thus bypassing * the normal size limits implied by the {@code int} subscript, enabling some * degree of sparseness when whole blocks are empty, and limiting reallocation * costs. * <p/> * * @author varkhan * @date Mar 23, 2009 * @time 9:38:09 PM */ abstract class AbstractBlockIndexedList { /** * The list block size 2-logarithm */ protected final int blockshift; /** * The list block size */ protected final int blocksize; /** * The list block mask ({@code blocksize - 1}) */ protected final int blockmask; /** * The list growth factor > 1.0 */ protected final double growthfact; /** * The number of elements contained in the list */ protected long size=0; /** * The index of the last slot used + 1 */ protected long head=0; /** * The list occupancy storage */ protected int[] bits=null; /********************************************************************************** ** List constructors **/ /** * Creates a new BlockIndexedList, specifying the reallocation strategy. * * @param blockshift the node reference storage block size 2-logarithm * @param growthfact the node reference storage growth factor */ public AbstractBlockIndexedList(int blockshift, double growthfact) { if(blockshift<=3) blockshift=3; if(growthfact<=1) growthfact=1.5; this.growthfact=growthfact; this.blockshift=blockshift; this.blocksize=(1<<blockshift); this.blockmask=(1<<blockshift)-1; } /********************************************************************************** ** List statistics accessors **/ /** * Returns the number of elements in this list. * * @return the number of entries (objects and related index) stored in this list */ public final long size() { return size; } /** * Indicates whether this list is empty. * * @return {@literal true} if this list contains no entry, * {@literal false} otherwise */ public final boolean isEmpty() { return size<=0; } /** * Returns the smallest position higher that any valid index in this list. * * @return the highest valid index plus one */ public final long head() { return head; } /** * Deletes all elements from this list. */ public void clear() { size=0; head=0; bits=null; } /********************************************************************************** ** Externalization **/ /** * Write an BlockIndexedList to a stream. * * @param out the stream to write the object to * * @throws IOException if I/O errors occur * @serialData <li/> {@code Object defVal} - the default value * <li/> {@code byte blockshift} - the block size 2-logarithm * <li/> {@code double growthfact} - the buffer growth factor * <li/> {@code long size} - the number of set entries * <li/> {@code long head} - the highest allocated index + 1 * <li/> all the data block, as an occupancy count on one {@code int}, * followed by the values ({@code blocksize} {@code Object}s) */ public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(blockshift); out.writeDouble(growthfact); out.writeLong(size); out.writeLong(head); } /** * Read an BlockIndexedList from a stream. * * @param in the stream to read the object from * * @throws IOException if I/O errors occur */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int block_shift=in.readByte(); double growth_fact=in.readDouble(); if(block_shift<=3) block_shift=3; if(growth_fact<=1) growth_fact=1.5; final Class<?> klass=AbstractBlockIndexedList.class; final Object target=this; // Set block* fields, bypassing final protection try { final Field field_BS=klass.getDeclaredField("blockshift"); final int value_BS=block_shift; final Field field_BZ=klass.getDeclaredField("blocksize"); final int value_BZ=1<<block_shift; final Field field_BM=klass.getDeclaredField("blockmask"); final int value_BM=(1<<block_shift)-1; final Field field_GF=klass.getDeclaredField("growthfact"); final double value_GF=growth_fact; java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction<Object>() { public Object run() throws IllegalAccessException { field_BS.setAccessible(true); field_BS.setInt(target, value_BS); field_BS.setAccessible(false); field_BZ.setAccessible(true); field_BZ.setInt(target, value_BZ); field_BZ.setAccessible(false); field_BM.setAccessible(true); field_BM.setInt(target, value_BM); field_BM.setAccessible(false); field_GF.setAccessible(true); field_GF.setDouble(target, value_GF); field_GF.setAccessible(false); return null; } } ); } catch(NoSuchFieldException e) { // This can never happen throw new InvalidClassException(BlockIndexedList.class.getSimpleName(), "Invalid class structure"); } catch(PrivilegedActionException e) { InvalidClassException t=new InvalidClassException( BlockIndexedList.class.getSimpleName(), "Invalid class structure" ); t.initCause(e.getCause()); throw t; } catch(SecurityException e) { InvalidClassException t=new InvalidClassException( BlockIndexedList.class.getSimpleName(), "Invalid class structure" ); t.initCause(e.getCause()); throw t; } /** * This is equivalent to: * blockshift = in.readByte(); blocksize = 1 << blockshift; blockmask = (1 << blockshift) - 1; * but bypasses the final keyword */ size=in.readLong(); head=in.readLong(); } /** * ******************************************************************************* * * Object method overrides */ @Override public int hashCode() { int hash=blockshift; hash^=size^(size>>>32); hash^=head^(head>>>32); return hash; } protected AbstractBlockIndexedList clone() throws CloneNotSupportedException { AbstractBlockIndexedList clone=(AbstractBlockIndexedList) super.clone(); if(this.bits!=null) { clone.bits=new int[this.bits.length]; System.arraycopy(this.bits, 0, clone.bits, 0, this.bits.length); } return clone; } }