/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: CellBackup.java
* Written by: Dmitry Nadezhin, Sun Microsystems.
*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.database;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.id.IdManager;
import com.sun.electric.database.id.IdReader;
import com.sun.electric.database.id.IdWriter;
import com.sun.electric.database.id.NodeProtoId;
import com.sun.electric.database.id.PortProtoId;
import com.sun.electric.database.id.PrimitiveNodeId;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.text.ArrayIterator;
import com.sun.electric.database.text.CellName;
import com.sun.electric.database.text.ImmutableArrayList;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.AbstractShapeBuilder;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.BoundsBuilder;
import com.sun.electric.technology.TechPool;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.util.math.DBMath;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
/**
* CellBackup is a pair of CellRevision and TechPool.
* It caches data that can be calculated when Technology is already
* known, but subcells are unknown.
*/
public class CellBackup {
private static final int BIN_SORT_THRESHOLD = 32;
public static final CellBackup[] NULL_ARRAY = {};
public static final ImmutableArrayList<CellBackup> EMPTY_LIST = new ImmutableArrayList<CellBackup>(NULL_ARRAY);
static int cellBackupsCreated = 0;
static int cellBackupsMemoized = 0;
/** Cell data. */
public final CellRevision cellRevision;
/** Technologies mapping */
public final TechPool techPool;
/** "Modified" flag of the Cell. */
public final boolean modified;
/** Memoized data for size computation (connectivity etc). */
private volatile Memoization m;
/** Arc shrinkage data */
private AbstractShapeBuilder.Shrinkage shrinkage;
/** Bounds of primitive arcs in this Cell. */
private ERectangle primitiveBounds;
/** Creates a new instance of CellBackup */
private CellBackup(CellRevision cellRevision, TechPool techPool, boolean modified) {
this.cellRevision = cellRevision;
this.techPool = techPool;
this.modified = modified;
cellBackupsCreated++;
if (Job.getDebug()) {
check();
}
}
/** Creates a new instance of CellBackup */
public static CellBackup newInstance(ImmutableCell d, TechPool techPool) {
if (d.cellId.idManager != techPool.idManager) {
throw new IllegalArgumentException();
}
if (techPool.getTech(d.techId) == null) {
throw new IllegalArgumentException();
}
CellRevision cellRevision = new CellRevision(d);
TechPool restrictedPool = techPool.restrict(cellRevision.techUsages, techPool);
return new CellBackup(cellRevision, restrictedPool, true);
}
/**
* Creates a new instance of CellBackup which differs from this CellBackup.
* Four array parameters are supplied. Each parameter may be null if its contents is the same as in this Snapshot.
* @param d new persistent data of a cell.
* @param nodesArray new array of nodes
* @param arcsArray new array of arcs
* @param exportsArray new array of exports
* @param superPool TechPool which defines all used technologies
* @return new snapshot which differs froms this Snapshot or this Snapshot.
* @throws IllegalArgumentException on invariant violation.
* @throws ArrayOutOfBoundsException on some invariant violations.
*/
public CellBackup with(ImmutableCell d,
ImmutableNodeInst[] nodesArray, ImmutableArcInst[] arcsArray, ImmutableExport[] exportsArray,
TechPool superPool) {
CellRevision newRevision = cellRevision.with(d, nodesArray, arcsArray, exportsArray);
TechPool restrictedPool = superPool.restrict(newRevision.techUsages, techPool);
if (newRevision == cellRevision && restrictedPool == techPool) {
return this;
}
if (arcsArray != null) {
for (ImmutableArcInst a : arcsArray) {
if (a != null && !a.check(restrictedPool)) {
throw new IllegalArgumentException("arc " + a.name + " is not compatible with TechPool");
}
}
}
return new CellBackup(newRevision, restrictedPool, modified || newRevision != cellRevision);
}
/**
* Creates a new instance of CellBackup which differs from this CellBackup by revision date.
* @param revisionDate new revision date.
* @return new CellBackup which differs from this CellBackup by revision date.
*/
public CellBackup withRevisionDate(long revisionDate) {
CellRevision newRevision = cellRevision.withRevisionDate(revisionDate);
if (newRevision == cellRevision) {
return this;
}
return new CellBackup(newRevision, this.techPool, true);
}
/**
* Creates a new instance of CellBackup with modified flag off.
* @return new snapshot which differs froms this Snapshot or this Snapshot.
*/
public CellBackup withoutModified() {
if (!this.modified) {
return this;
}
return new CellBackup(this.cellRevision, this.techPool, false);
}
/**
* Returns CellBackup which differs from this CellBackup by TechPool.
* @param techPool technology map.
* @return CellBackup with new TechPool.
*/
public CellBackup withTechPool(TechPool techPool) {
TechPool restrictedPool = techPool.restrict(cellRevision.techUsages, this.techPool);
if (this.techPool == restrictedPool) {
return this;
}
if (techPool.idManager != this.techPool.idManager) {
throw new IllegalArgumentException();
}
// for (Technology tech: this.techPool.values()) {
// if (techPool.get(tech.getId()) != tech)
// throw new IllegalArgumentException();
// }
return new CellBackup(this.cellRevision, restrictedPool, this.modified);
}
/**
* Returns CellBackup which differs from this CellBackup by renamed Ids.
* @param idMapper a map from old Ids to new Ids.
* @return CellBackup with renamed Ids.
*/
CellBackup withRenamedIds(IdMapper idMapper, CellName newGroupName) {
CellRevision newRevision = cellRevision.withRenamedIds(idMapper, newGroupName);
if (newRevision == cellRevision) {
return this;
}
return new CellBackup(newRevision, this.techPool, true);
}
/**
* Writes this CellBackup to IdWriter.
* @param writer where to write.
*/
void write(IdWriter writer) throws IOException {
cellRevision.write(writer);
writer.writeBoolean(modified);
}
/**
* Reads CellBackup from SnapshotReader.
* @param reader where to read.
*/
static CellBackup read(IdReader reader, TechPool techPool) throws IOException {
CellRevision newRevision = CellRevision.read(reader);
boolean modified = reader.readBoolean();
TechPool restrictedPool = techPool.restrict(newRevision.techUsages, techPool);
return new CellBackup(newRevision, restrictedPool, modified);
}
/**
* Checks invariant of this CellBackup.
* @throws AssertionError if invariant is broken.
*/
public void check() {
cellRevision.check();
IdManager idManager = cellRevision.d.cellId.idManager;
assert techPool.idManager == idManager;
BitSet techUsages = new BitSet();
for (Technology tech : techPool.values()) {
int techIndex = tech.getId().techIndex;
assert !techUsages.get(techIndex);
techUsages.set(techIndex);
}
assert techUsages.equals(cellRevision.techUsages);
// for (int techIndex = 0; techIndex < cellRevision.techUsages.length(); techIndex++) {
// if (cellRevision.techUsages.get(techIndex))
// assert techPool.getTech(idManager.getTechId(techIndex)) != null;
// }
for (ImmutableArcInst a : cellRevision.arcs) {
if (a != null) {
a.check(techPool);
}
}
if (m != null) {
m.check();
}
}
@Override
public String toString() {
return cellRevision.toString();
}
/**
* Returns data for size computation (connectivity etc).
* @return data for size computation.
*/
public Memoization getMemoization() {
Memoization m = this.m;
if (m != null) {
return m;
}
return this.m = new Memoization();
}
public Memoization computeMemoization() {
return new Memoization();
}
/**
* Returns data for arc shrinkage computation.
* @return data for arc shrinkage computation.
*/
public AbstractShapeBuilder.Shrinkage getShrinkage() {
if (shrinkage == null) {
shrinkage = new AbstractShapeBuilder.Shrinkage(this);
}
return shrinkage;
}
/**
* Returns bounds of all primitive arcs in this Cell or null if there are not primitives.
* @return bounds of all primitive arcs or null.
*/
public ERectangle getPrimitiveBounds() {
ERectangle primitiveBounds = this.primitiveBounds;
if (primitiveBounds != null) {
return primitiveBounds;
}
return this.primitiveBounds = computePrimitiveBounds();
}
public ERectangle computePrimitiveBounds() {
double cellLowX = 0, cellHighX = 0, cellLowY = 0, cellHighY = 0;
boolean boundsEmpty = true;
BoundsBuilder b = new BoundsBuilder(this);
Rectangle2D.Double bounds = new Rectangle2D.Double();
for (ImmutableNodeInst n : cellRevision.nodes) {
if (!(n.protoId instanceof PrimitiveNodeId)) {
continue;
}
NodeProto np = techPool.getPrimitiveNode((PrimitiveNodeId) n.protoId);
n.computeBounds(b, bounds);
// special case: do not include "cell center" primitives from Generic
if (np == Generic.tech().cellCenterNode) {
continue;
}
// special case for invisible pins: do not include if inheritable or interior-only
if (np == Generic.tech().invisiblePinNode) {
boolean found = false;
for (Iterator<Variable> it = n.getVariables(); it.hasNext();) {
Variable var = it.next();
if (var.isDisplay()) {
TextDescriptor td = var.getTextDescriptor();
if (td.isInterior() || td.isInherit()) {
found = true;
break;
}
}
}
if (found) {
continue;
}
}
double lowx = bounds.getMinX();
double highx = bounds.getMaxX();
double lowy = bounds.getMinY();
double highy = bounds.getMaxY();
if (boundsEmpty) {
boundsEmpty = false;
cellLowX = lowx;
cellHighX = highx;
cellLowY = lowy;
cellHighY = highy;
} else {
if (lowx < cellLowX) {
cellLowX = lowx;
}
if (highx > cellHighX) {
cellHighX = highx;
}
if (lowy < cellLowY) {
cellLowY = lowy;
}
if (highy > cellHighY) {
cellHighY = highy;
}
}
}
long gridMinX = DBMath.lambdaToGrid(cellLowX);
long gridMinY = DBMath.lambdaToGrid(cellLowY);
long gridMaxX = DBMath.lambdaToGrid(cellHighX);
long gridMaxY = DBMath.lambdaToGrid(cellHighY);
ERectangle primitiveArcBounds = computePrimitiveBoundsOfArcs();
if (boundsEmpty) {
return primitiveArcBounds;
}
if (primitiveArcBounds != null) {
assert !boundsEmpty;
gridMinX = Math.min(gridMinX, primitiveArcBounds.getGridMinX());
gridMaxX = Math.max(gridMaxX, primitiveArcBounds.getGridMaxX());
gridMinY = Math.min(gridMinY, primitiveArcBounds.getGridMinY());
gridMaxY = Math.max(gridMaxY, primitiveArcBounds.getGridMaxY());
}
return ERectangle.fromGrid(gridMinX, gridMinY, gridMaxX - gridMinX, gridMaxY - gridMinY);
}
/**
* Method to return a map from arcId of arcs in the Cell to their arcIndex in alphanumerical order.
* @return a map from arcId to arcIndex.
*/
public int[] getArcIndexByArcIdMap() {
return getMemoization().arcIndexByArcId.clone();
}
private ERectangle computePrimitiveBoundsOfArcs() {
ImmutableArrayList<ImmutableArcInst> arcs = cellRevision.arcs;
if (arcs.isEmpty()) {
return null;
}
int intMinX = Integer.MAX_VALUE, intMinY = Integer.MAX_VALUE, intMaxX = Integer.MIN_VALUE, intMaxY = Integer.MIN_VALUE;
int[] intCoords = new int[4];
BoundsBuilder boundsBuilder = new BoundsBuilder(this);
for (ImmutableArcInst a : arcs) {
if (boundsBuilder.genBoundsEasy(a, intCoords)) {
int x1 = intCoords[0];
if (x1 < intMinX) {
intMinX = x1;
}
int y1 = intCoords[1];
if (y1 < intMinY) {
intMinY = y1;
}
int x2 = intCoords[2];
if (x2 > intMaxX) {
intMaxX = x2;
}
int y2 = intCoords[3];
if (y2 > intMaxY) {
intMaxY = y2;
}
continue;
}
boundsBuilder.genShapeOfArc(a);
}
ERectangle bounds = boundsBuilder.makeBounds();
if (bounds == null) {
assert intMinX <= intMaxX && intMinY <= intMaxY;
int iw = intMaxX - intMinX;
int ih = intMaxY - intMinY;
return ERectangle.fromGrid(intMinX, intMinY,
iw >= 0 ? iw : (long) intMaxX - (long) intMinX,
ih >= 0 ? ih : (long) intMaxY - (long) intMinY);
}
if (intMinX > intMaxX) {
return bounds;
}
long longMinX = Math.min(bounds.getGridMinX(), intMinX);
long longMinY = Math.min(bounds.getGridMinY(), intMinY);
long longMaxX = Math.max(bounds.getGridMaxX(), intMaxX);
long longMaxY = Math.max(bounds.getGridMaxY(), intMaxY);
return ERectangle.fromGrid(longMinX, longMinY, longMaxX - longMinX, longMaxY - longMinY);
}
/**
* Class which memoizes data for size computation (connectivity etc).
*/
public class Memoization {
/**
* nodeIndex by nodeId.
*/
private final int[] nodeIndexByNodeId;
/**
* arcIndex by arcId.
*/
private final int[] arcIndexByArcId;
// /**
// * Base of a segment in portCounts array for valid nodeIds
// */
// private int[] portBasesForNodes;
// /**
// * Array which has an entry for every port instance.
// * The index for port instance (nodeInd, portProtoId) is
// * portBasesForNodes[nodeId] + portProtoId.chronIndex
// */
// private int[] portCounts;
private final int[] connections;
/** ImmutableExports sorted by original PortInst. */
private final ImmutableExport[] exportIndexByOriginalPort;
private final BitSet wiped = new BitSet();
private final BitSet hardArcs = new BitSet();
Memoization() {
// long startTime = System.currentTimeMillis();
cellBackupsMemoized++;
nodeIndexByNodeId = makeNodeIndexByNodeId();
arcIndexByArcId = makeArcIndexByArcId();
connections = makeConnections1();
exportIndexByOriginalPort = makeExportIndexByOriginalPort1();
// if (USE_COUNTS_FOR_CONNECTIONS || USE_COUNTS_FOR_EXPORTS) {
// initPortCounts();
// }
// connections = USE_COUNTS_FOR_CONNECTIONS ? makeConnections2() : makeConnections1();
// exportIndexByOriginalPort = USE_COUNTS_FOR_EXPORTS ? makeExportIndexByOriginalPort2() : makeExportIndexByOriginalPort1();
// portBasesForNodes = null;
// portCounts = null;
for (ImmutableArcInst a : cellRevision.arcs) {
ArcProto ap = techPool.getArcProto(a.protoId);
// wipe status
if (ap.isWipable()) {
wiped.set(a.tailNodeId);
wiped.set(a.headNodeId);
}
// hard arcs
if (!ap.getTechnology().isEasyShape(a, false)) {
hardArcs.set(a.arcId);
}
}
ImmutableArrayList<ImmutableNodeInst> nodes = cellRevision.nodes;
for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {
ImmutableNodeInst n = nodes.get(nodeIndex);
NodeProtoId np = n.protoId;
if (!(np instanceof PrimitiveNodeId && techPool.getPrimitiveNode((PrimitiveNodeId) np).isArcsWipe())) {
wiped.clear(n.nodeId);
}
}
// long stopTime = System.currentTimeMillis();
// System.out.println("Memoization " + cellRevision.d.cellId + " took " + (stopTime - startTime) + " msec");
check();
}
private int[] makeNodeIndexByNodeId() {
ImmutableArrayList<ImmutableNodeInst> nodes = cellRevision.nodes;
int maxNodeId = -1;
for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {
maxNodeId = Math.max(maxNodeId, nodes.get(nodeIndex).nodeId);
}
int[] nodesById = new int[maxNodeId + 1];
Arrays.fill(nodesById, -1);
for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {
ImmutableNodeInst n = nodes.get(nodeIndex);
nodesById[n.nodeId] = nodeIndex;
}
return nodesById;
}
private int[] makeArcIndexByArcId() {
ImmutableArrayList<ImmutableArcInst> arcs = cellRevision.arcs;
int maxArcId = -1;
for (int arcIndex = 0; arcIndex < arcs.size(); arcIndex++) {
maxArcId = Math.max(maxArcId, arcs.get(arcIndex).arcId);
}
int[] arcsById = new int[maxArcId + 1];
Arrays.fill(arcsById, -1);
for (int arcIndex = 0; arcIndex < arcs.size(); arcIndex++) {
ImmutableArcInst a = arcs.get(arcIndex);
arcsById[a.arcId] = arcIndex;
}
return arcsById;
}
/**
* Compute connections using sortConnections
* @return connections
*/
private int[] makeConnections1() {
ImmutableArrayList<ImmutableArcInst> arcs = cellRevision.arcs;
// Put connections into buckets by nodeId.
int[] connections = new int[arcs.size() * 2];
int[] connectionsByNodeId = new int[nodeIndexByNodeId.length];
for (ImmutableArcInst a : arcs) {
connectionsByNodeId[a.headNodeId]++;
connectionsByNodeId[a.tailNodeId]++;
}
int sum = 0;
for (int nodeId = 0; nodeId < connectionsByNodeId.length; nodeId++) {
int start = sum;
sum += connectionsByNodeId[nodeId];
connectionsByNodeId[nodeId] = start;
}
for (int i = 0; i < arcs.size(); i++) {
ImmutableArcInst a = arcs.get(i);
connections[connectionsByNodeId[a.tailNodeId]++] = i * 2;
connections[connectionsByNodeId[a.headNodeId]++] = i * 2 + 1;
}
// Sort each bucket by portId.
sum = 0;
for (int nodeId = 0; nodeId < connectionsByNodeId.length; nodeId++) {
int start = sum;
sum = connectionsByNodeId[nodeId];
if (sum - 1 > start) {
sortConnections(connections, start, sum - 1);
}
}
return connections;
}
/**
* Compute exportIndexByOriginalPort using Arrays.sort
* @return exportIndexByOriginalPort
*/
private ImmutableExport[] makeExportIndexByOriginalPort1() {
ImmutableExport[] exportIndexByOriginalPort = cellRevision.exports.toArray(new ImmutableExport[cellRevision.exports.size()]);
Arrays.sort(exportIndexByOriginalPort, BY_ORIGINAL_PORT);
return exportIndexByOriginalPort;
}
// /**
// * Compute connections using portBasesForNodes and portCounts
// * @return connections
// */
// private int[] makeConnections2() {
// ImmutableArrayList<ImmutableArcInst> arcs = cellRevision.arcs;
//
// clearPortCounts();
// for (int arcIndex = 0; arcIndex < arcs.size(); arcIndex++) {
// ImmutableArcInst a = arcs.get(arcIndex);
// incrementPortCount(a.tailNodeId, a.tailPortId);
// incrementPortCount(a.headNodeId, a.headPortId);
// }
// portCountsToPortOffsets();
// int[] connections = new int[arcs.size() * 2];
// Arrays.fill(connections, -1);
// for (int arcIndex = 0; arcIndex < arcs.size(); arcIndex++) {
// ImmutableArcInst a = arcs.get(arcIndex);
// connections[incrementPortCount(a.tailNodeId, a.tailPortId)] = arcIndex << 1;
// connections[incrementPortCount(a.headNodeId, a.headPortId)] = (arcIndex << 1) | 1;
// }
// return connections;
// }
//
// /**
// * Compute exportIndexByOriginalPort using portBasesForNodes and portCounts
// * @return exportIndexByOriginalPort
// */
// private ImmutableExport[] makeExportIndexByOriginalPort2() {
// clearPortCounts();
// for (ImmutableExport e: cellRevision.exports) {
// incrementPortCount(e.originalNodeId, e.originalPortId);
// }
// portCountsToPortOffsets();
// ImmutableExport[] exportIndexByOriginalPort = new ImmutableExport[cellRevision.exports.size()];
// for (int exportId = 0; exportId < cellRevision.exportIndex.length; exportId++) {
// int exportIndex = cellRevision.exportIndex[exportId];
// if (exportIndex < 0) continue;
// ImmutableExport e = cellRevision.exports.get(exportIndex);
// exportIndexByOriginalPort[incrementPortCount(e.originalNodeId, e.originalPortId)] = e;
// }
// return exportIndexByOriginalPort;
// }
//
// /**
// * Initialize portBasesForNodes and portCounts
// */
// private void initPortCounts() {
// // Prepare port map
// CellId cellId = cellRevision.d.cellId;
// IdentityHashMap<NodeProtoId,Integer> maxPortIds = new IdentityHashMap<NodeProtoId,Integer>();
// for (int i = 0; i < cellRevision.cellUsages.length; i++) {
// CellRevision.CellUsageInfo cui = cellRevision.cellUsages[i];
// if (cui == null) continue;
// assert cui.instCount > 0;
// maxPortIds.put(cellId.getUsageIn(i).protoId, cui.usedExportsLength);
// }
// int totalPortCount = 0;
// portBasesForNodes = new int[nodeIndexByNodeId.length];
// Arrays.fill(portBasesForNodes, Integer.MIN_VALUE);
// for (int nodeId = 0; nodeId < nodeIndexByNodeId.length; nodeId++) {
// int nodeIndex = nodeIndexByNodeId[nodeId];
// if (nodeIndex < 0) continue;
// ImmutableNodeInst n = cellRevision.nodes.get(nodeIndex);
// Integer portCount = maxPortIds.get(n.protoId);
// if (portCount == null) {
// PrimitiveNode pn = techPool.getPrimitiveNode((PrimitiveNodeId)n.protoId);
// int maxPortId = -1;
// for (Iterator<PortProto> it = pn.getPorts(); it.hasNext(); )
// maxPortId = Math.max(maxPortId, it.next().getId().chronIndex);
// portCount = maxPortId + 1;
// maxPortIds.put(n.protoId, portCount);
// }
// portBasesForNodes[n.nodeId] = totalPortCount;
// totalPortCount += portCount;
// }
// portCounts = new int[totalPortCount];
// }
//
// /*
// * Clear portCounts
// */
// private void clearPortCounts() {
// Arrays.fill(portCounts, 0);
// }
//
// /**
// * Increment portCounts entry for specified port intance
// * @param nodeId nodeId of port instance
// * @param portId PortProtoId of port instance
// * @return the value of portCounts entry before increment
// */
// private int incrementPortCount(int nodeId, PortProtoId portId) {
// return portCounts[portBasesForNodes[nodeId] + portId.chronIndex]++;
// }
//
// /**
// * Convert port counts to port offsets
// */
// private void portCountsToPortOffsets() {
// int connOffset = 0;
// for (int i = 0; i < portCounts.length; i++) {
// int numConns = portCounts[i];
// portCounts[i] = connOffset;
// connOffset += numConns;
// }
// }
/**
* Returns arcIndex of specified ImmutableArcInst.
* @param a specified ImmutableArcInst.
* @return arcIndex of specified ImmutableArcInst.
*/
public int getArcIndex(ImmutableArcInst a) {
return arcIndexByArcId[a.arcId];
}
/**
* Returns true of there are Exports on specified NodeInst.
* @param originalNode specified NodeInst.
* @return true if there are Exports on specified NodeInst.
*/
public boolean hasExports(ImmutableNodeInst originalNode) {
int startIndex = searchExportByOriginalPort(originalNode.nodeId, 0);
if (startIndex >= exportIndexByOriginalPort.length) {
return false;
}
ImmutableExport e = exportIndexByOriginalPort[startIndex];
return e.originalNodeId == originalNode.nodeId;
}
/**
* Method to return the number of Exports on specified NodeInst.
* @param originalNodeId nodeId of specified NodeInst.
* @return the number of Exports on specified NodeInst.
*/
public int getNumExports(int originalNodeId) {
int startIndex = searchExportByOriginalPort(originalNodeId, 0);
int j = startIndex;
for (; j < exportIndexByOriginalPort.length; j++) {
ImmutableExport e = exportIndexByOriginalPort[j];
if (e.originalNodeId != originalNodeId) {
break;
}
}
return j - startIndex;
}
/**
* Method to return an Iterator over all ImmutableExports on specified NodeInst.
* @param originalNodeId nodeId of specified NodeInst.
* @return an Iterator over all ImmutableExports on specified NodeInst.
*/
public Iterator<ImmutableExport> getExports(int originalNodeId) {
int startIndex = searchExportByOriginalPort(originalNodeId, 0);
int j = startIndex;
for (; j < exportIndexByOriginalPort.length; j++) {
ImmutableExport e = exportIndexByOriginalPort[j];
if (e.originalNodeId != originalNodeId) {
break;
}
}
return ArrayIterator.iterator(exportIndexByOriginalPort, startIndex, j);
}
/**
* Method to determine whether the display of specified pin NodeInst should be supressed.
* In Schematics technologies, pins are not displayed if there are 1 or 2 connections,
* but are shown for 0 or 3 or more connections (called "Steiner points").
* @param pin specified pin ImmutableNodeInst
* @return true if specieifed pin NodeInst should be supressed.
*/
public boolean pinUseCount(ImmutableNodeInst pin) {
int numConnections = getNumConnections(pin);
if (numConnections > 2) {
return false;
}
if (hasExports(pin)) {
return true;
}
if (numConnections == 0) {
return false;
}
return true;
}
/**
* Method to return a list of arcs connected to speciefed or all ports of
* specified ImmutableNodeInst.
* @param headEnds true if i-th arc connects by head end
* @param n specified ImmutableNodeInst
* @param portId specified port or null
* @return a List of connected ImmutableArcInsts
*/
public List<ImmutableArcInst> getConnections(BitSet headEnds, ImmutableNodeInst n, PortProtoId portId) {
ArrayList<ImmutableArcInst> result = null;
if (headEnds != null) {
headEnds.clear();
}
int myNodeId = n.nodeId;
int chronIndex = 0;
if (portId != null) {
assert portId.parentId == n.protoId;
chronIndex = portId.chronIndex;
}
int i = searchConnectionByPort(myNodeId, chronIndex);
int j = i;
for (; j < connections.length; j++) {
int con = connections[j];
ImmutableArcInst a = getArcs().get(con >>> 1);
boolean end = (con & 1) != 0;
int nodeId = end ? a.headNodeId : a.tailNodeId;
if (nodeId != myNodeId) {
break;
}
if (portId != null) {
PortProtoId endProtoId = end ? a.headPortId : a.tailPortId;
if (endProtoId.getChronIndex() != chronIndex) {
break;
}
}
if (result == null) {
result = new ArrayList<ImmutableArcInst>();
}
if (headEnds != null && end) {
headEnds.set(result.size());
}
result.add(a);
}
return result != null ? result : Collections.<ImmutableArcInst>emptyList();
}
/**
* Returns true of there are Connections on specified ImmutableNodeInst
* connected either to specified port or to all ports
* @param n specified ImmutableNodeInst
* @param portId specified port or null
* @return true if there are Connections on specified ImmutableNodeInst amd specified port.
*/
public boolean hasConnections(ImmutableNodeInst n, PortProtoId portId) {
int chronIndex = 0;
if (portId != null) {
assert portId.parentId == n.protoId;
chronIndex = portId.chronIndex;
}
int i = searchConnectionByPort(n.nodeId, chronIndex);
if (i >= connections.length) {
return false;
}
int con = connections[i];
ImmutableArcInst a = getArcs().get(con >>> 1);
boolean end = (con & 1) != 0;
int nodeId = end ? a.headNodeId : a.tailNodeId;
if (nodeId != n.nodeId) {
return false;
}
return portId == null || portId == (end ? a.headPortId : a.tailPortId);
}
/**
* Method to return the number of Connections on specified ImmutableNodeInst.
* @param n specified ImmutableNodeInst
* @return the number of Connections on specified ImmutableNodeInst.
*/
public int getNumConnections(ImmutableNodeInst n) {
int myNodeId = n.nodeId;
int i = searchConnectionByPort(myNodeId, 0);
int j = i;
for (; j < connections.length; j++) {
int con = connections[j];
ImmutableArcInst a = getArcs().get(con >>> 1);
boolean end = (con & 1) != 0;
int nodeId = end ? a.headNodeId : a.tailNodeId;
if (nodeId != myNodeId) {
break;
}
}
return j - i;
}
private int searchExportByOriginalPort(int originalNodeId, int originalChronIndex) {
int low = 0;
int high = exportIndexByOriginalPort.length - 1;
while (low <= high) {
int mid = (low + high) >> 1; // try in a middle
ImmutableExport e = exportIndexByOriginalPort[mid];
int cmp = e.originalNodeId - originalNodeId;
if (cmp == 0) {
cmp = e.originalPortId.getChronIndex() >= originalChronIndex ? 1 : -1;
}
if (cmp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return low;
}
private int searchConnectionByPort(int nodeId, int chronIndex) {
int low = 0;
int high = connections.length - 1;
while (low <= high) {
int mid = (low + high) >> 1; // try in a middle
int con = connections[mid];
ImmutableArcInst a = cellRevision.arcs.get(con >>> 1);
boolean end = (con & 1) != 0;
int endNodeId = end ? a.headNodeId : a.tailNodeId;
int cmp = endNodeId - nodeId;
if (cmp == 0) {
PortProtoId portId = end ? a.headPortId : a.tailPortId;
cmp = portId.getChronIndex() - chronIndex;
}
if (cmp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return low;
}
public CellBackup getCellBackup() {
return CellBackup.this;
}
public TechPool getTechPool() {
return techPool;
}
/**
* Returns ImmutableNodeInst by its node id.
* @param nodeId id of node.
* @return ImmutableNodeInst with this id or null if node doesn't exist.
*/
public ImmutableNodeInst getNodeById(int nodeId) {
if (nodeId >= nodeIndexByNodeId.length)
return null;
int nodeIndex = nodeIndexByNodeId[nodeId];
return nodeIndex >= 0 ? cellRevision.nodes.get(nodeIndex) : null;
}
public int getNodeIndexByNodeId(int nodeId) {
return nodeIndexByNodeId[nodeId];
}
/**
* Returns ImmutableArcInst by its arc id.
* @param arcId id of node.
* @return ImmutableArcInst with this id or null if node doesn't exist.
*/
public ImmutableArcInst getArcById(int arcId) {
if (arcId >= arcIndexByArcId.length)
return null;
int arcIndex = arcIndexByArcId[arcId];
return arcIndex >= 0 ? cellRevision.arcs.get(arcIndex) : null;
}
public ImmutableArrayList<ImmutableArcInst> getArcs() {
return cellRevision.arcs;
}
/**
* Method to tell whether the specified ImmutableNodeInst is wiped.
* Wiped ImmutableNodeInsts are erased. Typically, pin ImmutableNodeInsts can be wiped.
* This means that when an arc connects to the pin, it is no longer drawn.
* In order for a ImmutableNodeInst to be wiped, its prototype must have the "setArcsWipe" state,
* and the arcs connected to it must have "setWipable" in their prototype.
* @param n specified ImmutableNodeInst
* @return true if specified ImmutableNodeInst is wiped.
*/
public boolean isWiped(ImmutableNodeInst n) {
return wiped.get(n.nodeId);
}
public boolean isHardArc(int arcId) {
return hardArcs.get(arcId);
}
/**
* Checks invariant of this CellBackup.
* @throws AssertionError if invariant is broken.
*/
private void check() {
for (int nodeIndex = 0; nodeIndex < cellRevision.nodes.size(); nodeIndex++) {
ImmutableNodeInst n = cellRevision.nodes.get(nodeIndex);
assert nodeIndexByNodeId[n.nodeId] == nodeIndex;
}
int numNodes = 0;
for (int nodeId = 0; nodeId < nodeIndexByNodeId.length; nodeId++) {
if (nodeIndexByNodeId[nodeId] == -1) continue;
numNodes++;
}
assert numNodes == cellRevision.nodes.size();
for (int arcIndex = 0; arcIndex < cellRevision.arcs.size(); arcIndex++) {
ImmutableArcInst a = cellRevision.arcs.get(arcIndex);
assert arcIndexByArcId[a.arcId] == arcIndex;
}
int numArcs = 0;
for (int arcId = 0; arcId < arcIndexByArcId.length; arcId++) {
if (arcIndexByArcId[arcId] == -1) continue;
numArcs++;
}
assert numArcs == cellRevision.arcs.size();
assert exportIndexByOriginalPort.length == cellRevision.exports.size();
ImmutableExport prevE = null;
for (ImmutableExport e : exportIndexByOriginalPort) {
if (prevE != null) {
assert BY_ORIGINAL_PORT.compare(prevE, e) < 0;
}
assert e == cellRevision.getExport(e.exportId);
prevE = e;
}
assert connections.length == cellRevision.arcs.size() * 2;
for (int i = 1; i < connections.length; i++) {
assert compareConnections(connections[i - 1], connections[i]) < 0;
}
}
private void sortConnections(int[] connections, int l, int r) {
ImmutableArrayList<ImmutableArcInst> arcs = cellRevision.arcs;
while (r - l > BIN_SORT_THRESHOLD) {
int x = connections[(l + r) >>> 1];
ImmutableArcInst ax = arcs.get(x >>> 1);
boolean endx = (x & 1) != 0;
PortProtoId portIdX = endx ? ax.headPortId : ax.tailPortId;
int chronIndexX = portIdX.getChronIndex();
int i = l, j = r;
do {
// while (compareConnections(connections[i], x) < 0) i++;
for (;;) {
int con = connections[i];
ImmutableArcInst a = arcs.get(con >>> 1);
boolean end = (con & 1) != 0;
PortProtoId portId = end ? a.headPortId : a.tailPortId;
if (portId.getChronIndex() > chronIndexX) {
break;
}
if (portId.getChronIndex() == chronIndexX && con >= x) {
break;
}
i++;
}
// while (compareConnections(x, connections[j]) < 0) j--;
for (;;) {
int con = connections[j];
ImmutableArcInst a = arcs.get(con >>> 1);
boolean end = (con & 1) != 0;
PortProtoId portId = end ? a.headPortId : a.tailPortId;
if (chronIndexX > portId.getChronIndex()) {
break;
}
if (chronIndexX == portId.getChronIndex() && x >= con) {
break;
}
j--;
}
if (i <= j) {
int w = connections[i];
connections[i] = connections[j];
connections[j] = w;
i++;
j--;
}
} while (i <= j);
if (j - l < r - i) {
sortConnections(connections, l, j);
l = i;
} else {
sortConnections(connections, i, r);
r = j;
}
}
binarySort(connections, l, r + 1);
}
/**
* This is a specifalized version of {@link java.utils.TimSort#binarySort}.
*
* Sorts the specified portion of the specified array using a binary
* insertion sort. This is the best method for sorting small numbers
* of elements. It requires O(n log n) compares, but O(n^2) data
* movement (worst case).
*
* If the initial part of the specified range is already sorted,
* this method can take advantage of it: the method assumes that the
* elements from index {@code lo}, inclusive, to {@code start},
* exclusive are already sorted.
*
* @param a the array in which a range is to be sorted
* @param lo the index of the first element in the range to be sorted
* @param hi the index after the last element in the range to be sorted
* @param start the index of the first element in the range that is
* not already known to be sorted (@code lo <= start <= hi}
* @param c comparator to used for the sort
*/
@SuppressWarnings("fallthrough")
private void binarySort(int[] connections, int lo, int hi) {
ImmutableArrayList<ImmutableArcInst> arcs = cellRevision.arcs;
assert lo <= hi;
int start = lo + 1;
if (start >= hi) return;
int conS;
ImmutableArcInst aS;
PortProtoId portIdS;
int conL = connections[lo];
ImmutableArcInst aL = arcs.get(conL >>> 1);
PortProtoId portIdL = (conL & 1) != 0 ? aL.headPortId : aL.tailPortId;
for (;;) {
conS = connections[start];
aS = arcs.get(conS >>> 1);
portIdS = (conS & 1) != 0 ? aS.headPortId : aS.tailPortId;
int cmp = portIdS.chronIndex - portIdL.chronIndex;
if (cmp < 0) break;
if (cmp == 0 && conS < conL) break;
start++;
if (start >= hi) return;
conL = conS;
aL = aS;
portIdL = portIdS;
}
for (;;) {
assert start < hi;
// Set left (and right) to the index where a[start] (pivot) belongs
int left = lo;
int right = start;
assert left <= right;
/*
* Invariants:
* pivot >= all in [lo, left).
* pivot < all in [right, start).
*/
while (left < right) {
int mid = (left + right) >>> 1;
int conM = connections[mid];
ImmutableArcInst aM = arcs.get(conM >>> 1);
PortProtoId portIdM = (conM & 1) != 0 ? aM.headPortId : aM.tailPortId;
int cmp = portIdS.chronIndex - portIdM.chronIndex;
if (cmp == 0) {
cmp = conS - conM;
}
if (cmp < 0)
right = mid;
else
left = mid + 1;
}
assert left == right;
/*
* The invariants still hold: pivot >= all in [lo, left) and
* pivot < all in [left, start), so pivot belongs at left. Note
* that if there are elements equal to pivot, left points to the
* first slot after them -- that's why this sort is stable.
* Slide elements over to make room for pivot.
*/
int n = start - left; // The number of elements to move
// Switch is just an optimization for arraycopy in default case
switch(n) {
case 2: connections[left + 2] = connections[left + 1];
case 1: connections[left + 1] = connections[left];
break;
default: System.arraycopy(connections, left, connections, left + 1, n);
}
connections[left] = conS;
start++;
if (start >= hi) return;
conS = connections[start];
aS = arcs.get(conS >>> 1);
portIdS = (conS & 1) != 0 ? aS.headPortId : aS.tailPortId;
}
}
private int compareConnectionsSameNode(int con1, int con2) {
ImmutableArcInst a1 = cellRevision.arcs.get(con1 >>> 1);
ImmutableArcInst a2 = cellRevision.arcs.get(con2 >>> 1);
PortProtoId portId1 = (con1 & 1) != 0 ? a1.headPortId : a1.tailPortId;
PortProtoId portId2 = (con2 & 1) != 0 ? a2.headPortId : a2.tailPortId;
int cmp = portId1.chronIndex - portId2.chronIndex;
return cmp != 0 ? cmp : con1 - con2;
}
private int compareConnections(int con1, int con2) {
ImmutableArcInst a1 = cellRevision.arcs.get(con1 >>> 1);
ImmutableArcInst a2 = cellRevision.arcs.get(con2 >>> 1);
boolean end1 = (con1 & 1) != 0;
boolean end2 = (con2 & 1) != 0;
int nodeId1 = end1 ? a1.headNodeId : a1.tailNodeId;
int nodeId2 = end2 ? a2.headNodeId : a2.tailNodeId;
int cmp = nodeId1 - nodeId2;
if (cmp != 0) {
return cmp;
}
PortProtoId portId1 = end1 ? a1.headPortId : a1.tailPortId;
PortProtoId portId2 = end2 ? a2.headPortId : a2.tailPortId;
cmp = portId1.getChronIndex() - portId2.getChronIndex();
if (cmp != 0) {
return cmp;
}
return con1 - con2;
}
}
private static final Comparator<ImmutableExport> BY_ORIGINAL_PORT = new Comparator<ImmutableExport>() {
public int compare(ImmutableExport e1, ImmutableExport e2) {
int result = e1.originalNodeId - e2.originalNodeId;
if (result != 0) {
return result;
}
result = e1.originalPortId.getChronIndex() - e2.originalPortId.getChronIndex();
if (result != 0) {
return result;
}
return e1.exportId.chronIndex - e2.exportId.chronIndex;
}
};
}