/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package org.visage.runtime.sequence;
import org.visage.runtime.VisageBase;
import org.visage.runtime.VisageObject;
/**
*
* @param <T> Result element type
* @param <PT> Induction type
*/
public abstract class BoundFor<T, PT> extends VisageBase {
/**
* The bfElem class in the design document implements this interface.
*/
public static interface VisageForPart<PT> extends VisageObject {
/**
* Get the indexof variable
*/
public int getIndex$();
/**
* Adjust the indexof variable
* May cause re-calculation.
*/
public void adjustIndex$(int value$);
/**
* Set the induction variable
* May cause re-calculation.
*/
public void setInductionVar$(PT value$);
};
protected static final int BOUND_FOR_STATE_UNINITIALIZED = 0;
protected static final int BOUND_FOR_STATE_PARTS_STABLE = 1;
protected static final int BOUND_FOR_STATE_PARTS_INVALID = 2;
protected static final int BOUND_FOR_STATE_PARTS_UPDATED = 3;
protected byte state = BOUND_FOR_STATE_UNINITIALIZED;
protected final boolean dependsOnIndex;
protected boolean inWholesaleUpdate = true; // ignore initial individual updates
protected final VisageObject container;
protected final int forVarNum;
protected final int inductionSeqVarNum;
public int partResultVarNum; // This gets magically assigned when a part is created
protected VisageForPart<PT>[] parts;
protected int numParts;
protected int lowestInvalidPart; // lowest part index that is invalid
protected int highestInvalidPart = -1; // highest part index that is invalid, negative means none
protected int pendingTriggers = 0; // number of invalidations seen minus number of triggers seen
protected int sizeAtLastTrigger = 0; // size the previous time we did trigger phase invalidate
protected static final boolean DEBUG = false;
public BoundFor(VisageObject container, int forVarNum, int inductionSeqVarNum, boolean dependsOnIndex) {
this.container = container;
this.forVarNum = forVarNum;
this.inductionSeqVarNum = inductionSeqVarNum;
this.dependsOnIndex = dependsOnIndex;
}
// Required public interface
public abstract VisageForPart makeForPart$(int index);
/**
* Called by invalidate when the result of a part changes.
*/
@Override
public boolean update$(VisageObject src, final int depNum, int startPos, int endPos, int newLength, final int phase) {
if (state == BOUND_FOR_STATE_UNINITIALIZED || inWholesaleUpdate)
return true;
int ipart = ((VisageForPart) src).getIndex$();
if ((phase & PHASE_TRANS$PHASE) == PHASE$INVALIDATE) {
if (DEBUG) System.err.println("inv update$ id: " + forVarNum + ", ipart: " + ipart + ", " + lowestInvalidPart + " ... " + highestInvalidPart);
if (highestInvalidPart < 0) {
// No outstanding invalid region, mark this as the beginning and end of region
// and send a blanket invalidation
highestInvalidPart = lowestInvalidPart = ipart;
pendingTriggers = 1;
if (state == BOUND_FOR_STATE_PARTS_STABLE) {
blanketInvalidationOfBoundFor();
}
} else {
// Already have invalid parts, encompass ours
++pendingTriggers;
if (ipart < lowestInvalidPart) {
lowestInvalidPart = ipart;
}
if (ipart > highestInvalidPart) {
highestInvalidPart = ipart;
}
}
return true;
}
if (highestInvalidPart < 0) {
if (DEBUG) System.err.println("*trig spurious trailing id: " + forVarNum + ", ipart: " + ipart);
return true;
}
--pendingTriggers;
if (DEBUG) System.err.println("+trig update$ id: " + forVarNum + ", ipart: " + ipart + ", " + lowestInvalidPart + " ... " + highestInvalidPart);
if (pendingTriggers <= 0) {
// All the part triggers have come in
assert pendingTriggers == 0;
if (DEBUG) System.err.println(".trig update$ id: " + forVarNum + ", ipart: " + ipart + ", " + lowestInvalidPart + " ... " + highestInvalidPart);
if (state == BOUND_FOR_STATE_PARTS_INVALID) {
// Replace parts will handle
return true;
} else if (state == BOUND_FOR_STATE_PARTS_UPDATED) {
triggerAll();
return true;
}
return trigger(ipart, startPos, endPos, newLength);
}
return true;
}
/**
* Do invalidation for all currently invalid parts
* Sequence version can override to optimize
*/
protected boolean trigger(int ignoreIpart, int ignoreStartPos, int ignoreEndPos, int ignoreNewLength) {
int oldStartPos = cachedCumLength(lowestInvalidPart);
int oldEndPos = cachedCumLength(highestInvalidPart + 1);
restoreValidState(lowestInvalidPart, highestInvalidPart + 1);
decacheLengths();
int newEndPos = cumLength(highestInvalidPart + 1);
fireTrigger(oldStartPos, oldEndPos, newEndPos);
return true;
}
// Trying to change parts and do individual update at the same time -- invalidate everything
protected final void triggerAll() {
assert pendingTriggers == 0;
int previousSize = decacheLengths();
restoreValidState(0, numParts);
if (DEBUG) System.err.println("!trig all id: " + forVarNum + ", previousSize: " + previousSize + ", sizeAtLastTrigger: " + sizeAtLastTrigger);
fireTrigger(0, previousSize, sizeAtLastTrigger);
}
/**
* Set-up for exit
* Fire the trigger
*/
protected final void fireTrigger(int invStartPos, int invEndPos, int newEndPos) {
if (DEBUG) System.err.println("-fireTrigger id: " + forVarNum + ", invStartPos: " + invStartPos + ", invEndPos: " + invEndPos + ", len: " + (newEndPos - invStartPos));
state = BOUND_FOR_STATE_PARTS_STABLE;
highestInvalidPart = -1;
inWholesaleUpdate = false;
pendingTriggers = 0; // just in case
container.invalidate$(forVarNum,
invStartPos,
invEndPos,
newEndPos - invStartPos,
VisageObject.PHASE_TRANS$CASCADE_TRIGGER);
}
/**]
* Called by invalidate when the input sequence changes.
*/
public void replaceParts(int startPart, int endPart, int insertedParts, int phase) {
if (state == BOUND_FOR_STATE_UNINITIALIZED)
return;
boolean outstandingInvalidations = highestInvalidPart >= 0;
if ((phase & PHASE_TRANS$PHASE) == PHASE$INVALIDATE) {
if (DEBUG) System.err.println("inv replaceParts id: " + forVarNum + ", state: " + state);
if (state == BOUND_FOR_STATE_PARTS_STABLE && !outstandingInvalidations) {
blanketInvalidationOfBoundFor();
}
state = BOUND_FOR_STATE_PARTS_INVALID;
return;
}
if (DEBUG) System.err.println("+trig replaceParts id: " + forVarNum + ", startPart: " + startPart + ", endPart: " + endPart + ", insertedParts: " + insertedParts);
if (startPart < 0) {
// This is a no-change trigger
if (outstandingInvalidations) {
// We collected part updates during this no-change invalidation, proceed using them
startPart = lowestInvalidPart;
endPart = highestInvalidPart;
insertedParts = highestInvalidPart - lowestInvalidPart;
} else {
// Pass on this no-change trigger
container.invalidate$(forVarNum, SequencesBase.UNDEFINED_MARKER_INT, SequencesBase.UNDEFINED_MARKER_INT, 0, VisageObject.PHASE_TRANS$CASCADE_INVALIDATE);
return;
}
}
int newEndPart = startPart + insertedParts;
int deltaParts = newEndPart - endPart;
int newNumParts = numParts + deltaParts;
int oldStartPos;
int oldEndPos;
int trailingLength;
// Don't generate individual updates
inWholesaleUpdate = true;
if (parts == null) {
assert startPart == 0;
assert endPart == 0;
oldStartPos = 0;
oldEndPos = 0;
trailingLength = 0;
// Allocate the new elements
VisageForPart<PT>[] newParts = (VisageForPart<PT>[]) new VisageForPart[newNumParts];
// Install new parts
parts = newParts;
numParts = newNumParts;
// Fill in the new parts
buildParts(0, insertedParts);
} else {
// Remember old positions (for invalidate)
oldStartPos = cachedCumLength(startPart);
oldEndPos = cachedCumLength(endPart);
trailingLength = numParts - endPart;
if (DEBUG) System.err.println(".trig replaceParts id: " + forVarNum + ", parts.len: " + parts.length + ", start: " + startPart + ", end: " + endPart +
", newNumParts: " + newNumParts + ", s+i: " + (startPart + insertedParts) + ", trail: " + trailingLength);
int endPartCopy = (newEndPart < endPart)? newEndPart : endPart;
// In-place modification. Update reused induction vars (if any) Invalidation from parts.
for (int ips = startPart; ips < endPartCopy; ++ips) {
syncInductionVar(ips);
}
if (newNumParts != numParts) {
for (int ips = endPartCopy; ips < endPart; ++ips) {
removeDependent$(parts[ips], partResultVarNum, this);
}
// Allocate the new elements
VisageForPart<PT>[] newParts = (VisageForPart<PT>[]) new VisageForPart[newNumParts];
// Copy the existing parts
System.arraycopy(parts, 0, newParts, 0, endPartCopy);
System.arraycopy(parts, endPart, newParts, newEndPart, trailingLength);
// Install new parts
parts = newParts;
numParts = newNumParts;
// Fill in the new parts (if any)
buildParts(endPartCopy, newEndPart);
}
}
// Update the trailing indices -- need indices for internal bookkeeping, always update
assert startPart + insertedParts + trailingLength == numParts;
for (int ips = newEndPart; ips < numParts; ++ips) {
getPart(ips).adjustIndex$(deltaParts);
}
state = BOUND_FOR_STATE_PARTS_UPDATED;
inWholesaleUpdate = false;
if (pendingTriggers == 0) {
assert pendingTriggers == 0;
if (outstandingInvalidations) {
// Trying to change parts and do individual update at the same time -- invalidate everything
triggerAll();
} else {
int previousSize = decacheLengths();
if (dependsOnIndex) {
// We depend on indices, everything after the start point is invalid
restoreValidState(startPart, numParts);
fireTrigger(oldStartPos, previousSize, sizeAtLastTrigger);
} else {
// Calculate the inserted length (in the new parts)
restoreValidState(startPart, newEndPart);
fireTrigger(oldStartPos, oldEndPos, cumLength(newEndPart));
}
}
}
}
protected abstract int decacheLengths();
void showStates(String label) {
for (int ips = 0; ips < numParts; ++ips) {
System.err.print(getPart(ips).getFlags$(partResultVarNum) & VFLGS$STATE_MASK);
}
System.err.println(" - " + label);
}
// Shared implementation interface (optional)
protected abstract int cumLength(int ipart);
protected abstract int cachedCumLength(int ipart);
protected abstract void restoreValidState(int lowPart, int highPart);
protected void initializeIfNeeded() {
if (state == BOUND_FOR_STATE_UNINITIALIZED) {
// Init the induction sequence
int sz = container.size$(inductionSeqVarNum);
state = BOUND_FOR_STATE_PARTS_STABLE;
sizeAtLastTrigger = 0;
replaceParts(0, 0, sz, VisageObject.PHASE_TRANS$CASCADE_INVALIDATE);
replaceParts(0, 0, sz, VisageObject.PHASE_TRANS$CASCADE_TRIGGER);
}
}
protected VisageForPart<PT> getPart(int ipart) {
return parts[ipart];
}
protected final void blanketInvalidationOfBoundFor() {
container.invalidate$(forVarNum, 0, SequencesBase.UNDEFINED_MARKER_INT, SequencesBase.UNDEFINED_MARKER_INT, VisageObject.PHASE_TRANS$CASCADE_INVALIDATE);
}
protected void syncInductionVar(int ipart) {
VisageForPart part = getPart(ipart);
part.setInductionVar$(container.elem$(inductionSeqVarNum, ipart));
}
protected void buildParts(int ipFrom, int ipTo) {
for (int ips = ipFrom; ips < ipTo; ++ips) {
VisageForPart part = makeForPart$(ips);
parts[ips] = part;
syncInductionVar(ips);
addDependent$(part, partResultVarNum, this, 0);
}
}
}