@NotNull public ValueContainerImpl<Value> copy() { ValueContainerImpl<Value> container = new ValueContainerImpl<Value>(); if (myInputIdMapping instanceof THashMap) { final THashMap<Value, Object> mapping = (THashMap<Value, Object>) myInputIdMapping; final THashMap<Value, Object> newMapping = new THashMap<Value, Object>(mapping.size()); container.myInputIdMapping = newMapping; mapping.forEachEntry( new TObjectObjectProcedure<Value, Object>() { @Override public boolean execute(Value key, Object val) { if (val instanceof ChangeBufferingList) { newMapping.put(key, ((ChangeBufferingList) val).clone()); } else { newMapping.put(key, val); } return true; } }); } else { container.myInputIdMapping = myInputIdMapping; container.myInputIdMappingValue = myInputIdMappingValue instanceof ChangeBufferingList ? ((ChangeBufferingList) myInputIdMappingValue).clone() : myInputIdMappingValue; } return container; }
@Override public boolean removeValue(int inputId, Value value) { ValueContainerImpl<Value> merged = myMerged; if (merged != null) { merged.removeValue(inputId, value); } ValueContainerImpl<Value> added = myAdded; if (added != null) added.removeValue(inputId, value); return true; }
// need 'synchronized' to ensure atomic initialization of merged data // because several threads that acquired read lock may simultaneously execute the method private ValueContainerImpl<Value> getMergedData() { ValueContainerImpl<Value> merged = myMerged; if (merged != null) { return merged; } synchronized (myInitializer.getLock()) { merged = myMerged; if (merged != null) { return merged; } final ValueContainer<Value> fromDisk = myInitializer.compute(); final ValueContainerImpl<Value> newMerged; if (fromDisk instanceof ValueContainerImpl) { newMerged = ((ValueContainerImpl<Value>) fromDisk).copy(); } else { newMerged = ((ChangeTrackingValueContainer<Value>) fromDisk).getMergedData().copy(); } TIntHashSet invalidated = myInvalidated; if (invalidated != null) { invalidated.forEach( new TIntProcedure() { @Override public boolean execute(int inputId) { newMerged.removeAssociatedValue(inputId); return true; } }); } ValueContainerImpl<Value> added = myAdded; if (added != null) { added.forEach( new ContainerAction<Value>() { @Override public boolean perform(final int id, final Value value) { newMerged.removeAssociatedValue( id); // enforcing "one-value-per-file for particular key" invariant newMerged.addValue(id, value); return true; } }); } setNeedsCompacting(fromDisk.needsCompacting()); myMerged = newMerged; return newMerged; } }
@Override public ValueContainerImpl<Value> clone() { try { final ValueContainerImpl clone = (ValueContainerImpl) super.clone(); if (myInputIdMapping instanceof THashMap) { clone.myInputIdMapping = mapCopy((THashMap<Value, Object>) myInputIdMapping); } else if (myInputIdMappingValue instanceof ChangeBufferingList) { clone.myInputIdMappingValue = ((ChangeBufferingList) myInputIdMappingValue).clone(); } return clone; } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } }
@Override public void addValue(int inputId, Value value) { ValueContainerImpl<Value> merged = myMerged; if (merged != null) { merged.addValue(inputId, value); } ValueContainerImpl<Value> added = myAdded; if (added == null) { myAdded = added = new ValueContainerImpl<Value>(); } added.addValue( inputId, value); // will flush the changes & caller should ensure exclusiveness to avoid intermediate // visibility issues }
@Override public void removeAssociatedValue(int inputId) { ValueContainerImpl<Value> merged = myMerged; if (merged != null) { merged.removeAssociatedValue(inputId); } ValueContainerImpl<Value> added = myAdded; if (added != null) added.removeAssociatedValue(inputId); TIntHashSet invalidated = myInvalidated; if (invalidated == null) { invalidated = new TIntHashSet(1); } invalidated.add(inputId); myInvalidated = invalidated; // volatile write }
public ValueContainerImpl<T> read(final DataInput in) throws IOException { DataInputStream stream = (DataInputStream) in; final ValueContainerImpl<T> valueContainer = new ValueContainerImpl<T>(); while (stream.available() > 0) { final int valueCount = DataInputOutputUtil.readSINT(in); if (valueCount < 0) { valueContainer.removeAllValues(-valueCount); valueContainer.setNeedsCompacting(true); } else { for (int valueIdx = 0; valueIdx < valueCount; valueIdx++) { final T value = myExternalizer.read(in); final int idCount = DataInputOutputUtil.readSINT(in); for (int i = 0; i < idCount; i++) { final int id = DataInputOutputUtil.readSINT(in); if (id < 0) { valueContainer.removeValue(-id, value); valueContainer.setNeedsCompacting(true); } else { valueContainer.addValue(id, value); } } } } } return valueContainer; }
public boolean isDirty() { return (myAdded != null && myAdded.size() > 0) || (myInvalidated != null && !myInvalidated.isEmpty()) || needsCompacting(); }