216 lines
5.1 KiB
Java
216 lines
5.1 KiB
Java
package speiger.src.coreengine.utils.collections.managers.dynamic;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
import speiger.src.collections.ints.collections.IntIterator;
|
|
import speiger.src.collections.ints.maps.abstracts.AbstractInt2IntMap.BasicEntry;
|
|
import speiger.src.collections.ints.maps.interfaces.Int2IntMap;
|
|
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
|
|
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
|
|
import speiger.src.collections.ints.sets.IntAVLTreeSet;
|
|
import speiger.src.collections.ints.sets.IntSet;
|
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
|
import speiger.src.collections.objects.sets.ObjectAVLTreeSet;
|
|
import speiger.src.collections.objects.utils.ObjectLists;
|
|
import speiger.src.coreengine.utils.collections.pools.ThreadPool;
|
|
|
|
public class DynamicDataManager<T>
|
|
{
|
|
static final ThreadPool<DataSlot> SLOTS = new ThreadPool<>(1000, DataSlot::new, DataSlot::clear);
|
|
final IDynamicDataHandler<T> manager;
|
|
Int2ObjectMap<DataSlot> slots = Int2ObjectMap.builder().linkedMap();
|
|
Set<DataSlot> emptySlots = new ObjectAVLTreeSet<>();
|
|
IntSet changedSlots = new IntAVLTreeSet();
|
|
DataSlot lastSlot = null;
|
|
|
|
public DynamicDataManager(IDynamicDataHandler<T> manager)
|
|
{
|
|
this.manager = manager;
|
|
}
|
|
|
|
public boolean add(int dataIndex, T value)
|
|
{
|
|
if(slots.containsKey(dataIndex)) return false;
|
|
return add(dataIndex, manager.toBytes(value));
|
|
}
|
|
|
|
protected boolean add(int dataIndex, byte[] data)
|
|
{
|
|
if(!emptySlots.isEmpty())
|
|
{
|
|
for(Iterator<DataSlot> iter = emptySlots.iterator();iter.hasNext();)
|
|
{
|
|
DataSlot slot = iter.next();
|
|
if(slot.getFreeBytes() >= data.length)
|
|
{
|
|
slot.setData(dataIndex, data);
|
|
slots.put(dataIndex, slot);
|
|
changedSlots.add(dataIndex);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
DataSlot next = SLOTS.get();
|
|
next.set(lastSlot, dataIndex, data);
|
|
slots.put(dataIndex, next);
|
|
changedSlots.add(dataIndex);
|
|
lastSlot = next;
|
|
return true;
|
|
}
|
|
|
|
public boolean contains(int dataIndex)
|
|
{
|
|
return slots.containsKey(dataIndex);
|
|
}
|
|
|
|
public boolean update(int dataIndex, T value)
|
|
{
|
|
DataSlot slot = slots.get(dataIndex);
|
|
if(slot != null)
|
|
{
|
|
byte[] data = manager.toBytes(value);
|
|
if(slot.getFreeBytes() >= data.length)
|
|
{
|
|
slot.setData(dataIndex, data);
|
|
changedSlots.add(dataIndex);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
slot.setData(-1, null);
|
|
slots.remove(dataIndex);
|
|
add(dataIndex, data);
|
|
emptySlots.add(slot);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public Iterator<Int2IntMap.Entry> iterator(int byteOffset)
|
|
{
|
|
return new Iterator<Int2IntMap.Entry>() {
|
|
BasicEntry entry = new BasicEntry();
|
|
DataSlot current = findNextSlot(lastSlot);
|
|
@Override
|
|
public boolean hasNext()
|
|
{
|
|
return current != null;
|
|
}
|
|
|
|
DataSlot findNextSlot(DataSlot input)
|
|
{
|
|
for(input = input.previous; input != null && input.value == null;input = input.previous);
|
|
return input;
|
|
}
|
|
|
|
@Override
|
|
public Int2IntMap.Entry next()
|
|
{
|
|
if(!hasNext()) throw new IllegalStateException();
|
|
entry.set(current.dataIndex, current.byteOffset / byteOffset);
|
|
current = findNextSlot(current);
|
|
return entry;
|
|
}
|
|
};
|
|
}
|
|
|
|
public boolean remove(int dataIndex)
|
|
{
|
|
DataSlot slot = slots.remove(dataIndex);
|
|
if(slot != null)
|
|
{
|
|
slot.setData(-1, null);
|
|
emptySlots.add(slot);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected void clearEmptySlots()
|
|
{
|
|
while(lastSlot != null && lastSlot.value == null)
|
|
{
|
|
DataSlot prev = lastSlot;
|
|
emptySlots.remove(prev);
|
|
lastSlot = prev.previous;
|
|
SLOTS.accept(prev);
|
|
}
|
|
}
|
|
|
|
public void clear()
|
|
{
|
|
slots.clear();
|
|
emptySlots.clear();
|
|
changedSlots.clear();
|
|
while(lastSlot != null)
|
|
{
|
|
DataSlot prev = lastSlot;
|
|
lastSlot = prev.previous;
|
|
SLOTS.accept(prev);
|
|
}
|
|
}
|
|
|
|
public void update()
|
|
{
|
|
int last = getEndSize();
|
|
clearEmptySlots();
|
|
if(changedSlots.isEmpty())
|
|
{
|
|
if(last != getEndSize())
|
|
{
|
|
manager.uploadBytes(ObjectLists.empty(), getEndSize());
|
|
}
|
|
return;
|
|
}
|
|
List<IntObjectPair<byte[]>> list = new ObjectArrayList<>();
|
|
while(!changedSlots.isEmpty())
|
|
{
|
|
DataSlot start = null;
|
|
DataSlot current = null;
|
|
int bytesAllocated = 0;
|
|
for(IntIterator iter = changedSlots.iterator();iter.hasNext();)
|
|
{
|
|
DataSlot slot = slots.get(iter.nextInt());
|
|
iter.remove();
|
|
if(slot == null) break;
|
|
if(current == null || current.next == slot)
|
|
{
|
|
current = slot;
|
|
if(start == null) start = slot;
|
|
bytesAllocated += slot.getUsedBytes();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if(bytesAllocated <= 0) continue;
|
|
byte[] data = new byte[bytesAllocated];
|
|
int byteOffset = start.byteOffset;
|
|
bytesAllocated = 0;
|
|
while(start.byteOffset <= current.byteOffset);
|
|
{
|
|
System.arraycopy(start.value, 0, data, bytesAllocated, start.value.length);
|
|
bytesAllocated += start.getUsedBytes();
|
|
start = start.next;
|
|
}
|
|
list.add(IntObjectPair.of(byteOffset, data));
|
|
}
|
|
if(!list.isEmpty())
|
|
{
|
|
manager.uploadBytes(list, getEndSize());
|
|
}
|
|
}
|
|
|
|
private int getEndSize()
|
|
{
|
|
return lastSlot == null ? 0 : lastSlot.getNextByteOffset();
|
|
}
|
|
|
|
public int size()
|
|
{
|
|
return slots.size();
|
|
}
|
|
}
|