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 { static final ThreadPool SLOTS = new ThreadPool<>(1000, DataSlot::new, DataSlot::clear); final IDynamicDataHandler manager; Int2ObjectMap slots = Int2ObjectMap.builder().linkedMap(); Set emptySlots = new ObjectAVLTreeSet<>(); IntSet changedSlots = new IntAVLTreeSet(); DataSlot lastSlot = null; public DynamicDataManager(IDynamicDataHandler 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 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 iterator(int byteOffset) { return new Iterator() { 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> 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(); } }