package com.plectix.simulator.staticanalysis.stories.compressions; import java.util.ArrayList; import java.util.Map; import java.util.Stack; import java.util.TreeMap; import com.plectix.simulator.staticanalysis.stories.ActionOfAEvent; import com.plectix.simulator.staticanalysis.stories.MarkOfEvent; import com.plectix.simulator.staticanalysis.stories.State; import com.plectix.simulator.staticanalysis.stories.storage.AtomicEvent; import com.plectix.simulator.staticanalysis.stories.storage.Event; import com.plectix.simulator.staticanalysis.stories.storage.EventInterface; import com.plectix.simulator.staticanalysis.stories.storage.EventIteratorInterface; import com.plectix.simulator.staticanalysis.stories.storage.MasterInformationAboutWires; import com.plectix.simulator.staticanalysis.stories.storage.StateOfLink; import com.plectix.simulator.staticanalysis.stories.storage.StoryStorageException; import com.plectix.simulator.staticanalysis.stories.storage.WireHashKey; import com.plectix.simulator.staticanalysis.stories.storage.WireStorageInterface; enum WalkResult { DESIRED, FREE, NULL, FAILED } /* package */final class WeakCompression { private final WireStorageInterface storage; private final MasterInformationAboutWires information; // For strong compression public final static long ghostEventId = -100; private int maxQueueSize; private boolean maxQueueSizeReached; private Long upperGhostId = null; private Long lowerGhostId = null; private final ArrayList<QueueEntry> uninvestigatedQueue = new ArrayList<QueueEntry>(); private final Stack<StackEntry> wireStack = new Stack<StackEntry>(); private final ArrayList<EventInterface> candidatesToDelete = new ArrayList<EventInterface>(); private int currentNodeIdx; private QueueEntry currentNode = null; private StackEntry topEntry = null; public WeakCompression(WireStorageInterface storage) { this.storage = storage; information = storage.getInformationAboutWires(); } public final boolean process() throws StoryStorageException { return doProcess(null); } public final boolean processInconsistent(EventInterface boundaryEvent, EventInterface neiEvent) throws StoryStorageException { return doProcess(createGhostEvent(boundaryEvent, neiEvent)); } private final boolean doProcess(EventInterface ghostEvent) throws StoryStorageException { Stack<EventInterface> searchStack = new Stack<EventInterface>(); EventInterface initialEvent = storage.initialEvent(); EventInterface observableEvent = storage.observableEvent(); boolean compressed = false; if (storage.extractPassport().eventCount() < 256) maxQueueSize = 256; else maxQueueSize = 16; do { maxQueueSize *= 2; maxQueueSizeReached = false; storage.markAllNull(); storage.markAllUnresolved(); searchStack.clear(); // Add initial event to stack if (initialEvent.getMark() != null) searchStack.add(initialEvent); // Add observable event to stack observableEvent.setMark(MarkOfEvent.KEPT, information); searchStack.add(observableEvent); if (ghostEvent != null) { if (!propagate(ghostEvent)) { if (maxQueueSizeReached) continue; return false; } ghostEvent = null; if (uninvestigatedQueue.size() > 1) compressed = true; } while (!searchStack.empty()) { EventInterface top = searchStack.peek(); EventInterface nextEvent = selectEventToBranch(top); if (nextEvent == null) { searchStack.pop(); continue; } nextEvent.setMark(MarkOfEvent.DELETED, information); if (!propagate(nextEvent)) { nextEvent.setMark(MarkOfEvent.KEPT, information); searchStack.push(nextEvent); } else if (!compressed) compressed = true; } if (storage.markAllUnresolvedAsDeleted() && !compressed) compressed = true; storage.extractPassport().removeEventWithMarkDelete(); } while (maxQueueSize < storage.extractPassport().eventCount()); return compressed; } @SuppressWarnings("unchecked") private final EventInterface createGhostEvent(EventInterface boundaryEvent, EventInterface neiEvent) throws StoryStorageException { Event event = new Event(ghostEventId, 0, MarkOfEvent.DELETED); Map<WireHashKey, TreeMap<Long, AtomicEvent<?>>> wiresMap = storage .getStorageWires(); if (boundaryEvent.getStepId() > neiEvent.getStepId()) { upperGhostId = neiEvent.getStepId(); lowerGhostId = boundaryEvent.getStepId(); } else { upperGhostId = boundaryEvent.getStepId(); lowerGhostId = neiEvent.getStepId(); } Map.Entry<Long, AtomicEvent<?>> upperEvent, lowerEvent; // TODO: iterate only on swapped agents for (Map.Entry<WireHashKey, TreeMap<Long, AtomicEvent<?>>> wire : wiresMap .entrySet()) { upperEvent = wire.getValue().floorEntry(upperGhostId); lowerEvent = wire.getValue().ceilingEntry(lowerGhostId); if (lowerEvent == null || upperEvent == null) continue; Object upperValue = null, lowerValue = null; switch (upperEvent.getValue().getType()) { case MODIFICATION: case TEST_AND_MODIFICATION: upperValue = upperEvent.getValue().getState().getAfterState(); break; case TEST: upperValue = upperEvent.getValue().getState().getBeforeState(); break; } lowerValue = lowerEvent.getValue().getState().getBeforeState(); if ((lowerValue == null && upperValue == null) || (upperValue != null && upperValue.equals(lowerValue))) continue; AtomicEvent<?> newAEvent = null; switch (wire.getKey().getTypeOfWire()) { case AGENT: newAEvent = new AtomicEvent<State>( event, upperValue != null ? ActionOfAEvent.TEST_AND_MODIFICATION : ActionOfAEvent.MODIFICATION); if (upperEvent.getValue().getType() != ActionOfAEvent.TEST && lowerEvent.getValue().getType() != ActionOfAEvent.TEST && upperValue == null && lowerEvent.getValue().getState().getAfterState() != null) { ((AtomicEvent<State>) newAEvent).getState().setBeforeState( State.CHECK_AGENT); ((AtomicEvent<State>) newAEvent).getState().setAfterState( State.CHECK_AGENT); } else { ((AtomicEvent<State>) newAEvent).getState().setBeforeState( (State) upperValue); ((AtomicEvent<State>) newAEvent).getState().setAfterState( (State) lowerValue); } break; case BOUND_FREE: newAEvent = new AtomicEvent<State>( event, upperValue != null ? ActionOfAEvent.TEST_AND_MODIFICATION : ActionOfAEvent.MODIFICATION); ((AtomicEvent<State>) newAEvent).getState().setBeforeState( (State) upperValue); ((AtomicEvent<State>) newAEvent).getState().setAfterState( (State) lowerValue); break; case INTERNAL_STATE: newAEvent = new AtomicEvent<String>( event, upperValue != null ? ActionOfAEvent.TEST_AND_MODIFICATION : ActionOfAEvent.MODIFICATION); ((AtomicEvent<String>) newAEvent).getState().setBeforeState( (String) upperValue); ((AtomicEvent<String>) newAEvent).getState().setAfterState( (String) lowerValue); break; case LINK_STATE: newAEvent = new AtomicEvent<StateOfLink>( event, upperValue != null ? ActionOfAEvent.TEST_AND_MODIFICATION : ActionOfAEvent.MODIFICATION); ((AtomicEvent<StateOfLink>) newAEvent).getState() .setBeforeState((StateOfLink) upperValue); ((AtomicEvent<StateOfLink>) newAEvent).getState() .setAfterState((StateOfLink) lowerValue); break; } event.getAtomicEvents().put(wire.getKey(), newAEvent); } if (event.getAtomicEventCount() == 0) return null; return event; } private final EventInterface selectEventToBranch(EventInterface keptEvent) throws StoryStorageException { WireHashKey selectedWire = keptEvent .getWireWithMinimumUresolvedEvent(information); if (selectedWire == null) return null; EventIteratorInterface eventIterator = storage.eventIterator( selectedWire, true); while (eventIterator.hasNext()) { eventIterator.next(); EventInterface nextEvent = eventIterator.value(); if (nextEvent.getMark() == MarkOfEvent.UNRESOLVED) return nextEvent; } throw new StoryStorageException( "selectEventToBranch(): no UNRESOLVED event"); } private final boolean propagate(EventInterface event) throws StoryStorageException { uninvestigatedQueue.clear(); wireStack.clear(); uninvestigatedQueue.add(new QueueEntry(event, 0, 0, 0)); currentNodeIdx = 0; currentNode = uninvestigatedQueue.get(currentNodeIdx); pushEntry(); while (currentNodeIdx != uninvestigatedQueue.size()) { currentNode = uninvestigatedQueue.get(currentNodeIdx); // Shift to next event in queue if (currentNode.currentWireIdx == currentNode.getWireCount()) { currentNodeIdx++; // currentWireIdx may be not zero if we backtracked if (currentNodeIdx < uninvestigatedQueue.size()) uninvestigatedQueue.get(currentNodeIdx).currentWireIdx = 0; continue; } topEntry = wireStack.peek(); // Push next wire from event to stack if (currentNode.currentWireIdx != topEntry.getWireIdx()) { pushEntry(); continue; } // Add new events to queue if (topEntry.hasNextState()) { // Find block of events on current wire to delete if (addDeletedEvents()) currentNode.currentWireIdx++; // Go to next wire state continue; } // Restore stack and queue // Backtrack to previous wire if (currentNode.currentWireIdx > 0) { currentNode.currentWireIdx--; decreaseStack(wireStack.size() - 1); QueueEntry last = uninvestigatedQueue.get(uninvestigatedQueue .size() - 1); int previousPos = last.getQueuePosIdx(); // If queue has events added from previous wire if (previousPos == currentNodeIdx && uninvestigatedQueue.get(previousPos).getStackSize() == wireStack .size()) decreaseQueue(last.getQueueSize()); continue; } // Backtrack to source event and wire decreaseQueue(currentNode.getQueueSize()); decreaseStack(currentNode.getStackSize()); currentNodeIdx = currentNode.getQueuePosIdx(); if (uninvestigatedQueue.size() > 0) uninvestigatedQueue.get(currentNodeIdx).currentWireIdx = wireStack .peek().getWireIdx(); } if (currentNodeIdx > 0) return true; return false; } private final void pushEntry() throws StoryStorageException { if (getEvent().getAtomicEventCount() == 0) throw new StoryStorageException( "pushEntry(): event has no non-BOUND/FREE atomic events"); topEntry = new StackEntry(getWireIdx()); topEntry.checkFrozenState(this); wireStack.push(topEntry); } public final WireStorageInterface getStorage() { return storage; } public final EventInterface getEvent() { return currentNode.getEvent(); } public final int getWireIdx() { return currentNode.currentWireIdx; } private final void decreaseQueue(int size) throws StoryStorageException { while (uninvestigatedQueue.size() != size) { QueueEntry last = uninvestigatedQueue.get(uninvestigatedQueue .size() - 1); if (last.getEvent().getStepId() != ghostEventId) last.getEvent().setMark(MarkOfEvent.UNRESOLVED, information); uninvestigatedQueue.remove(uninvestigatedQueue.size() - 1); } } private final void decreaseStack(int size) { while (wireStack.size() != size) wireStack.pop(); } final long getFirstEventId(boolean upwards) throws StoryStorageException { EventInterface event = currentNode.getEvent(); if (event.getStepId() != ghostEventId) return event.getStepId(); if (upwards) return storage.getStorageWires().get( event.getWireKey(topEntry.getWireIdx())).floorKey( upperGhostId); return storage.getStorageWires().get( event.getWireKey(topEntry.getWireIdx())).ceilingKey( lowerGhostId); } public static final ActionOfAEvent getRealType(AtomicEvent<?> event) { if (event.getType() == ActionOfAEvent.MODIFICATION && event.getState().getBeforeState() != null) return ActionOfAEvent.TEST_AND_MODIFICATION; return event.getType(); } @SuppressWarnings("unchecked") final <E> WalkResult walk(E state, WalkResult tillValue) throws StoryStorageException { EventInterface event = currentNode.getEvent(); WireHashKey wireKey = event.getWireKey(topEntry.getWireIdx()); boolean upwards = (tillValue == null); Long firstId = getFirstEventId(upwards); if (firstId == null) return WalkResult.NULL; EventIteratorInterface eventIterator = storage.eventIterator(wireKey, firstId, upwards); while (eventIterator.hasNext()) { eventIterator.next(); EventInterface curEvent = eventIterator.value(); // DELETED if (curEvent.getMark() == MarkOfEvent.DELETED) continue; AtomicEvent<E> atomicEvent = (AtomicEvent<E>) curEvent .getAtomicEvent(wireKey); // KEPT and UNRESOLVED switch (getRealType(atomicEvent)) { case TEST: if (state != null && state .equals(atomicEvent.getState().getBeforeState())) { return calcWalkResult(tillValue); } break; case TEST_AND_MODIFICATION: if (upwards) { if (state != null && state.equals(atomicEvent.getState() .getAfterState())) { return calcWalkResult(tillValue); } } else if (state != null && state .equals(atomicEvent.getState().getBeforeState()) || atomicEvent.getState().getBeforeState() == null) { return calcWalkResult(tillValue); } break; case MODIFICATION: if (!upwards) return WalkResult.DESIRED; if (state != null && state.equals(atomicEvent.getState().getAfterState())) { return calcWalkResult(tillValue); } break; } if (!deleteEvent(curEvent)) return WalkResult.FAILED; } return WalkResult.NULL; } @SuppressWarnings("unchecked") final boolean walkOnAgentWire() throws StoryStorageException { EventInterface event = currentNode.getEvent(); AtomicEvent<State> atomicEvent = (AtomicEvent<State>) event .getAtomicEvent(topEntry.getWireIdx()); boolean upwards = false; if (atomicEvent.getState().getAfterState() == null) return true; if (atomicEvent.getState().getBeforeState() != null) { if (event.getStepId() != ghostEventId) throw new StoryStorageException( "walkOnAgentWire(): wire doesn't have null state before/after modification"); WalkResult wr = walkOnAgentWireSingleDiretion(true, true, null); return wr != WalkResult.FAILED && walkOnAgentWireSingleDiretion(false, true, wr) != WalkResult.FAILED; } return walkOnAgentWireSingleDiretion(upwards, false, null) != WalkResult.FAILED; } public final boolean isLastEvent() throws StoryStorageException { EventInterface event = currentNode.getEvent(); WireHashKey wireKey = event.getWireKey(topEntry.getWireIdx()); Long firstId = getFirstEventId(false); if (firstId == null) return true; EventIteratorInterface eventIterator = storage.eventIterator(wireKey, firstId, false); while (eventIterator.hasNext()) { eventIterator.next(); EventInterface curEvent = eventIterator.value(); // DELETED if (curEvent.getMark() == MarkOfEvent.DELETED) continue; return false; } return true; } @SuppressWarnings("unchecked") private final WalkResult walkOnAgentWireSingleDiretion(boolean upwards, boolean inconsistent, WalkResult tillValue) throws StoryStorageException { EventInterface event = currentNode.getEvent(); WireHashKey wireKey = event.getWireKey(topEntry.getWireIdx()); Long firstId = getFirstEventId(upwards); if (firstId == null) return WalkResult.NULL; EventIteratorInterface eventIterator = storage.eventIterator(wireKey, firstId, upwards); while (eventIterator.hasNext()) { eventIterator.next(); EventInterface curEvent = eventIterator.value(); // DELETED if (curEvent.getMark() == MarkOfEvent.DELETED) continue; AtomicEvent<State> curAtomicEvent = (AtomicEvent<State>) curEvent .getAtomicEvent(wireKey); // KEPT and UNRESOLVED State state = null; switch (curAtomicEvent.getType()) { case TEST: state = curAtomicEvent.getState().getBeforeState(); break; case TEST_AND_MODIFICATION: case MODIFICATION: if (upwards) { state = curAtomicEvent.getState().getAfterState(); } else { state = curAtomicEvent.getState().getBeforeState(); } break; } if (!inconsistent) { if (state == null) return WalkResult.DESIRED; } else if (state != null) { return calcWalkResult(tillValue); } if (!deleteEvent(curEvent)) return WalkResult.FAILED; } return WalkResult.NULL; } private final WalkResult calcWalkResult(WalkResult tillValue) { return tillValue == WalkResult.NULL ? WalkResult.FAILED : WalkResult.DESIRED; } private final boolean deleteEvent(EventInterface currentEvent) throws StoryStorageException { if (currentEvent.getMark() == MarkOfEvent.KEPT) return false; candidatesToDelete.add(currentEvent); return true; } private final boolean addDeletedEvents() throws StoryStorageException { candidatesToDelete.clear(); if (currentNode.getEvent().getAtomicEvent(topEntry.getWireIdx()) .getType() == ActionOfAEvent.TEST || isLastEvent()) { topEntry.nextState(); return true; } switch (currentNode.getWireType()) { case INTERNAL_STATE: String currentInternalState = (String) topEntry.nextState(); if (!isWalkSucceeded(currentInternalState)) { return false; } break; case BOUND_FREE: State currentBoundState = (State) topEntry.nextState(); if (!isWalkSucceeded(currentBoundState)) { return false; } break; case LINK_STATE: StateOfLink currentLinkState = (StateOfLink) topEntry.nextState(); if (!isWalkSucceeded(currentLinkState)) { return false; } break; case AGENT: topEntry.nextState(); if (!walkOnAgentWire()) return false; break; } int srcQueueSize = uninvestigatedQueue.size(); if (candidatesToDelete.size() + uninvestigatedQueue.size() > maxQueueSize) { maxQueueSizeReached = true; return false; } for (EventInterface e : candidatesToDelete) { e.setMark(MarkOfEvent.DELETED, information); uninvestigatedQueue.add(new QueueEntry(e, srcQueueSize, wireStack .size(), currentNodeIdx)); } return true; } private final <E> boolean isWalkSucceeded(E state) throws StoryStorageException { WalkResult walkResult = walk(state, null); if (walkResult == WalkResult.FAILED || walk(state, walkResult) == WalkResult.FAILED) return false; return true; } }