All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.electric.database.CellRevision Maven / Gradle / Ivy

The newest version!
/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: CellRevision.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.id.CellId;
import com.sun.electric.database.id.CellUsage;
import com.sun.electric.database.id.ExportId;
import com.sun.electric.database.id.IdReader;
import com.sun.electric.database.id.IdWriter;
import com.sun.electric.database.id.PortProtoId;
import com.sun.electric.database.id.PrimitiveNodeId;
import com.sun.electric.database.id.TechId;
import com.sun.electric.database.text.CellName;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.tool.Job;
import com.sun.electric.util.collections.ImmutableArrayList;

import java.io.IOException;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * This class represents Cell data (with all arcs/nodes/exports) as it is saved to disk.
 * This representation should be technology-independent
 */
public abstract class CellRevision {

    public static final CellRevision[] NULL_ARRAY = {};
    public static final ImmutableArrayList EMPTY_LIST = new ImmutableArrayList(NULL_ARRAY);
    protected static final BitSet EMPTY_BITSET = new BitSet();
    protected static final int[] NULL_INT_ARRAY = {};
    static final CellUsageInfo[] NULL_CELL_USAGE_INFO_ARRAY = {};
    static int cellRevisionsCreated = 0;
    /** Cell persistent data. */
    public final ImmutableCell d;
    /** A list of NodeInsts in this Cell. */
    public final ImmutableNodeInst.Iterable nodes;
    /** Map from chronIndex of Nodes to sortIndex. */
    final int nodeIndex[];
    /** A list of ArcInsts in this Cell. */
    public final ImmutableArcInst.Iterable arcs;
    /** Map from chronIndex of Arcs to sortIndex. */
    final int arcIndex[];
    /** An array of Exports on the Cell by chronological index. */
    public final ImmutableExport.Iterable exports;
    /** Map from chronIndex of Exports to sortIndex. */
    int exportIndex[];
    /** TechId usage counts. */
    final BitSet techUsages;
    /** CellUsageInfos indexed by CellUsage.indefInParent */
    final CellUsageInfo[] cellUsages;
    /** definedExport == [0..definedExportLength) - deletedExports . */
    /** Bitmap of defined exports. */
    final BitSet definedExports;
    /** Length of defined exports. */
    final int definedExportsLength;
    /** Bitmap of deleted exports. */
    final BitSet deletedExports;

    /** Creates a new instance of CellRevision */
    protected CellRevision(ImmutableCell d,
            ImmutableNodeInst.Iterable nodes, int[] nodeIndex,
            ImmutableArcInst.Iterable arcs, int[] arcIndex,
            ImmutableExport.Iterable exports, int[] exportIndex,
            BitSet techUsages,
            CellUsageInfo[] cellUsages, BitSet definedExports, int definedExportsLength, BitSet deletedExports) {
        this.d = d;
        this.nodes = nodes;
        this.nodeIndex = nodeIndex;
        this.arcs = arcs;
        this.arcIndex = arcIndex;
        this.exports = exports;
        this.exportIndex = exportIndex;
        this.techUsages = techUsages;
        this.cellUsages = cellUsages;
        this.definedExports = definedExports;
        this.definedExportsLength = definedExportsLength;
        this.deletedExports = deletedExports;
        cellRevisionsCreated++;
    }

    protected static BitSet makeTechUsages(TechId techId) {
        BitSet techUsages = new BitSet();
        techUsages.set(techId.techIndex);
        return techUsages;
    }

    protected static CellRevisionProvider getProvider() {
        return CellRevisionProvider.INSTANCE;
    }

    /** Creates a new instance of CellRevision */
    public static CellRevision newInstance(ImmutableCell d) {
        return getProvider().createCellRevision(d);
    }

    protected abstract CellRevision lowLevelWith(ImmutableCell d,
            ImmutableNodeInst.Iterable nodes, int[] nodeIndex,
            ImmutableArcInst.Iterable arcs, int[] arcIndex,
            ImmutableExport.Iterable exports, int[] exportIndex,
            BitSet techUsages,
            CellUsageInfo[] cellUsages, BitSet definedExports, int definedExportsLength, BitSet deletedExports);

    /**
     * Creates a new instance of CellRevision which differs from this CellRevision by revision date.
     * @param revisionDate new revision date.
     * @return new CellRevision which differs from this CellRevision by revision date.
     */
    public CellRevision withRevisionDate(long revisionDate) {
        if (d.revisionDate == revisionDate) {
            return this;
        }
        return lowLevelWith(this.d.withRevisionDate(revisionDate),
                this.nodes, this.nodeIndex,
                this.arcs, this.arcIndex,
                this.exports, this.exportIndex,
                this.techUsages, this.cellUsages,
                this.definedExports, this.definedExportsLength, this.deletedExports);
    }

    /**
     * Creates a new instance of CellRevision which differs from this CellRevision.
     * 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
     * @return new snapshot which differs froms this Snapshot or this Snapshot.
     * @throws IllegalArgumentException on invariant violation.
     * @throws ArrayOutOfBoundsException on some invariant violations.
     */
    public CellRevision with(ImmutableCell d,
            ImmutableNodeInst[] nodesArray, ImmutableArcInst[] arcsArray, ImmutableExport[] exportsArray) {
        ImmutableNodeInst.Iterable nodes = getProvider().createNodeList(nodesArray, this.nodes);
        ImmutableArcInst.Iterable arcs = getProvider().createArcList(arcsArray, this.arcs);
        ImmutableExport.Iterable exports = getProvider().createExportList(exportsArray, this.exports);
        if (this.d == d && this.nodes == nodes && this.arcs == arcs && this.exports == exports) {
            return this;
        }

        CellId cellId = d.cellId;
        boolean busNamesAllowed = d.busNamesAllowed();
        if (this.d != d) {
            if (d.techId == null) {
                throw new NullPointerException("tech");
            }
//            if (cellId != this.d.cellId)
//                throw new IllegalArgumentException("cellId");
        }

        BitSet techUsages = this.techUsages;
        CellUsageInfo[] cellUsages = this.cellUsages;
        if (this.d.cellId != d.cellId || this.d.techId != d.techId || this.d.getVars() != d.getVars() || nodes != this.nodes || arcs != this.arcs || exports != this.exports) {
            UsageCollector uc = new UsageCollector(d, nodes, arcs, exports);
            techUsages = uc.getTechUsages(this.techUsages);
            cellUsages = uc.getCellUsages(this.cellUsages);
        }
        if (cellId.isIcon() && cellUsages.length != 0) {
            throw new IllegalArgumentException("Icon contains subcells");
        }

        int[] nodeIndex = this.nodeIndex;
        if (nodes != this.nodes) {
            boolean sameNodeIdAndIndex = true;
            boolean sameNodeIndex = nodes.size() == this.nodes.size();
            int nodeIndexLength = 0;
            boolean hasCellCenter = false;
            int nodeInd = 0;
            Iterator oldNodes = this.nodes.iterator();
            for (ImmutableNodeInst n : nodes) {
                sameNodeIdAndIndex = sameNodeIdAndIndex && n.nodeId == nodeInd;
                sameNodeIndex = sameNodeIndex && n.nodeId == oldNodes.next().nodeId;
                nodeIndexLength = Math.max(nodeIndexLength, n.nodeId + 1);
                if (ImmutableNodeInst.isCellCenter(n.protoId)) {
                    if (hasCellCenter) {
                        throw new IllegalArgumentException("Duplicate cell center");
                    }
                    hasCellCenter = true;
                }
                if (!busNamesAllowed && n.name.isBus()) {
                    throw new IllegalArgumentException("arrayedName " + n.name);
                }
                nodeInd++;
            }
            if (sameNodeIdAndIndex) {
                nodeIndex = null;
            } else if (!sameNodeIndex) {
                nodeIndex = new int[nodeIndexLength];
                Arrays.fill(nodeIndex, -1);
                nodeInd = 0;
                for (ImmutableNodeInst n : nodes) {
                    int nodeId = n.nodeId;
                    if (nodeIndex[nodeId] >= 0) {
                        throw new IllegalArgumentException("nodeChronIndex");
                    }
                    nodeIndex[nodeId] = nodeInd;
                    nodeInd++;
                }
                assert !Arrays.equals(this.nodeIndex, nodeIndex);
            }
        }

        int[] arcIndex = this.arcIndex;
        if (arcs != this.arcs) {
            boolean sameArcIdAndIndex = true;
            boolean sameArcIndex = arcs.size() == this.arcs.size();
            int arcIndexLength = 0;
            int arcInd = 0;
            Iterator oldArcs = this.arcs.iterator();
            for (ImmutableArcInst a : arcs) {
                sameArcIdAndIndex = sameArcIdAndIndex && a.arcId == arcInd;
                sameArcIndex = sameArcIndex && a.arcId == oldArcs.next().arcId;
                arcIndexLength = Math.max(arcIndexLength, a.arcId + 1);
                if (!busNamesAllowed && a.name.isBus()) {
                    throw new IllegalArgumentException("arrayedName " + a.name);
                }
                arcInd++;
            }
            if (sameArcIdAndIndex) {
                arcIndex = null;
            } else if (!sameArcIndex) {
                arcIndex = new int[arcIndexLength];
                Arrays.fill(arcIndex, -1);
                arcInd = 0;
                for (ImmutableArcInst a : arcs) {
                    int arcId = a.arcId;
                    if (arcIndex[arcId] >= 0) {
                        throw new IllegalArgumentException("arcChronIndex");
                    }
                    arcIndex[arcId] = arcInd;
                    arcInd++;
                }
                assert !Arrays.equals(this.arcIndex, arcIndex);
            }
        }

        int[] exportIndex = this.exportIndex;
        BitSet definedExports = this.definedExports;
        int definedExportsLength = this.definedExportsLength;
        BitSet deletedExports = this.deletedExports;
        if (exports != this.exports) {
            boolean sameExportIndex = exports.size() == this.exports.size();
            int exportIndexLength = 0;
            int exportInd = 0;
            Iterator oldExports = this.exports.iterator();
            for (ImmutableExport e : exports) {
                if (e.exportId.parentId != cellId) {
                    throw new IllegalArgumentException("exportId");
                }
                if (!busNamesAllowed && e.name.isBus()) {
                    throw new IllegalArgumentException("arrayedName " + e.name);
                }
                int chronIndex = e.exportId.chronIndex;
                sameExportIndex = sameExportIndex && chronIndex == oldExports.next().exportId.chronIndex;
                exportIndexLength = Math.max(exportIndexLength, chronIndex + 1);
                exportInd++;
            }
            if (!sameExportIndex) {
                exportIndex = new int[exportIndexLength];
                Arrays.fill(exportIndex, -1);
                exportInd = 0;
                for (ImmutableExport e : exports) {
                    int chronIndex = e.exportId.chronIndex;
                    if (exportIndex[chronIndex] >= 0) {
                        throw new IllegalArgumentException("exportChronIndex");
                    }
                    exportIndex[chronIndex] = exportInd;
                    //checkPortInst(nodesById.get(e.originalNodeId), e.originalPortId);
                    exportInd++;
                }
                assert !Arrays.equals(this.exportIndex, exportIndex);
                definedExports = new BitSet();
                for (int chronIndex = 0; chronIndex < exportIndex.length; chronIndex++) {
                    if (exportIndex[chronIndex] < 0) {
                        continue;
                    }
                    definedExports.set(chronIndex);
                }
                definedExports = UsageCollector.bitSetWith(this.definedExports, definedExports);
                if (definedExports != this.definedExports) {
                    definedExportsLength = definedExports.length();
                    deletedExports = new BitSet();
                    deletedExports.set(0, definedExportsLength);
                    deletedExports.andNot(definedExports);
                    deletedExports = UsageCollector.bitSetWith(this.deletedExports, deletedExports);
                }
            }
        }

        return lowLevelWith(d, nodes, nodeIndex,
                arcs, arcIndex,
                exports, exportIndex,
                techUsages, cellUsages, definedExports, definedExportsLength, deletedExports);
    }

    /**
     * Returns CellRevision which differs from this CellRevision by renamed Ids.
     * @param idMapper a map from old Ids to new Ids.
     * @return CellRevision with renamed Ids.
     */
    CellRevision withRenamedIds(IdMapper idMapper, CellName newGroupName) {
        ImmutableCell d = this.d.withRenamedIds(idMapper).withGroupName(newGroupName);

        ImmutableNodeInst[] nodesArray = null;
        for (int i = 0; i < nodes.size(); i++) {
            ImmutableNodeInst oldNode = nodes.get(i);
            ImmutableNodeInst newNode = oldNode.withRenamedIds(idMapper);
            if (newNode != oldNode && nodesArray == null) {
                nodesArray = new ImmutableNodeInst[nodes.size()];
                for (int j = 0; j < i; j++) {
                    nodesArray[j] = nodes.get(j);
                }
            }
            if (nodesArray != null) {
                nodesArray[i] = newNode;
            }
        }

        ImmutableArcInst[] arcsArray = null;
        for (int i = 0; i < arcs.size(); i++) {
            ImmutableArcInst oldArc = arcs.get(i);
            ImmutableArcInst newArc = oldArc.withRenamedIds(idMapper);
            if (newArc != oldArc && arcsArray == null) {
                arcsArray = new ImmutableArcInst[arcs.size()];
                for (int j = 0; j < i; j++) {
                    arcsArray[j] = arcs.get(j);
                }
            }
            if (arcsArray != null) {
                arcsArray[i] = newArc;
            }
        }

        ImmutableExport[] exportsArray = null;
        for (int i = 0; i < exports.size(); i++) {
            ImmutableExport oldExport = exports.get(i);
            ImmutableExport newExport = oldExport.withRenamedIds(idMapper);
            if (newExport != oldExport && exportsArray == null) {
                exportsArray = new ImmutableExport[exports.size()];
                for (int j = 0; j < i; j++) {
                    exportsArray[j] = exports.get(j);
                }
            }
            if (exportsArray != null) {
                exportsArray[i] = newExport;
            }
        }

        if (this.d == d && nodesArray == null && arcsArray == null && exportsArray == null) {
            return this;
        }
        CellRevision newRevision = with(d, nodesArray, arcsArray, exportsArray);
//        newRevision.check();
        return newRevision;
    }

    /**
     * Returns ImmutableNodeInst by its nodeId.
     * @param nodeId of ImmutableNodeInst.
     * @return ImmutableNodeInst with given nodeId
     * @throws IndexOutOfBoundsException if nodeId is negative
     */
    public ImmutableNodeInst getNodeById(int nodeId) {
        if (nodeIndex == null) {
            return nodeId < nodes.size() ? nodes.get(nodeId) : null;
        }
        if (nodeId >= nodeIndex.length) {
            return null;
        }
        int nodeInd = nodeIndex[nodeId];
        return nodeInd >= 0 ? nodes.get(nodeInd) : null;
    }

    /**
     * Returns sort order index of ImmutableNodeInst by its nodeId.
     * @param nodeId of ImmutableNodeInst.
     * @return sort order index of node
     */
    public int getNodeIndexByNodeId(int nodeId) {
        int nodeInd = nodeIndex != null ? nodeIndex[nodeId] : nodeId;
        assert 0 <= nodeInd && nodeInd < nodes.size();
        return nodeInd;
    }

    /**
     * Returns maximum nodeId used by nodes of this CellReversion.
     * Returns -1 if CellRevsison doesn't contatin nodes
     * @return maximum nodeId
     */
    public int getMaxNodeId() {
        return (nodeIndex != null ? nodeIndex.length : nodes.size()) - 1;
    }

    /**
     * Returns ImmutableArcInst by its arcId.
     * @param arcId of ImmutableArcInst.
     * @return ImmutableArcInst with given arcId
     * @throws IndexOutOfBoundsException if arcId is negative
     */
    public ImmutableArcInst getArcById(int arcId) {
        if (arcIndex == null) {
            return arcId < arcs.size() ? arcs.get(arcId) : null;
        }
        if (arcId >= arcIndex.length) {
            return null;
        }
        int arcInd = arcIndex[arcId];
        return arcInd >= 0 ? arcs.get(arcInd) : null;
    }

    /**
     * Returns sort order index of ImmutableArcInst by its arcId.
     * @param arcId of ImmutableArcInst.
     * @return sort order index of arc
     */
    public int getArcIndexByArcId(int arcId) {
        int arcInd = arcIndex != null ? arcIndex[arcId] : arcId;
        assert 0 <= arcInd && arcInd < arcs.size();
        return arcInd;
    }

    /**
     * Returns maximum arcId used by arcs of this CellReversion.
     * Returns -1 if CellRevsison doesn't contatin arcs
     * @return maximum arcId
     */
    public int getMaxArcId() {
        return (arcIndex != null ? arcIndex.length : arcs.size()) - 1;
    }

    /**
     * Returns ImmutableExport by its export id.
     * @param exportId id of export.
     * @return ImmutableExport with this id or null if node doesn't exist.
     */
    public ImmutableExport getExport(ExportId exportId) {
        if (exportId.parentId != d.cellId) {
            throw new IllegalArgumentException();
        }
        int chronIndex = exportId.chronIndex;
        if (chronIndex >= exportIndex.length) {
            return null;
        }
        int portIndex = exportIndex[chronIndex];
        return portIndex >= 0 ? exports.get(portIndex) : null;
    }

    /**
     * Returns sort order index of ImmutableExport by its export id.
     * @param exportId id of export.
     * @return sort order index of export
     */
    public int getExportIndexByExportId(ExportId exportId) {
        if (exportId.parentId != d.cellId) {
            throw new IllegalArgumentException();
        }
        int chronIndex = exportId.chronIndex;
        return chronIndex < exportIndex.length ? exportIndex[chronIndex] : -1;
    }

    /**
     * Returns maximum chronIndex used by exports of this CellReversion.
     * Returns -1 if CellRevsison doesn't contatin exports
     * @return maximum exportChronIndexId
     */
    public int getMaxExportChronIndex() {
        return exportIndex.length - 1;
    }

    /**
     * Returns subcell instance counts, indexed by CellUsage.indexInParent.
     * @return subcell instance counts, indexed by CellUsage.indexInParent.
     */
    public int[] getInstCounts() {
        int l = cellUsages.length;
        while (l > 0 && (cellUsages[l - 1] == null || cellUsages[l - 1].instCount == 0)) {
            l--;
        }
        if (l == 0) {
            return NULL_INT_ARRAY;
        }
        int[] instCounts = new int[l];
        for (int indexInParent = 0; indexInParent < l; indexInParent++) {
            if (cellUsages[indexInParent] != null) {
                instCounts[indexInParent] = cellUsages[indexInParent].instCount;
            }
        }
        return instCounts;
    }

    /**
     * For given CellUsage in this cell returns count of subcell instances.
     * @param u CellUsage.
     * @return count of subcell instances.
     * @throws IllegalArgumentException if CellUsage's parent is not this cell.
     */
    public int getInstCount(CellUsage u) {
        if (u.parentId != d.cellId) {
            throw new IllegalArgumentException();
        }
        if (u.indexInParent >= cellUsages.length) {
            return 0;
        }
        CellUsageInfo cui = cellUsages[u.indexInParent];
        if (cui == null) {
            return 0;
        }
        return cui.instCount;
    }

    /**
     * Returns Set of Technologies used in this CellRevision
     */
    public Set getTechUsages() {
        LinkedHashSet techUsagesSet = new LinkedHashSet();
        for (int techIndex = 0; techIndex < techUsages.length(); techIndex++) {
            if (techUsages.get(techIndex)) {
                techUsagesSet.add(d.cellId.idManager.getTechId(techIndex));
            }
        }
        return techUsagesSet;
    }

    /**
     * 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 abstract List getConnections(BitSet headEnds, ImmutableNodeInst n, PortProtoId portId);

    /**
     * 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 abstract boolean hasConnections(ImmutableNodeInst n, PortProtoId portId);

    /**
     * Method to return the number of Connections on specified ImmutableNodeInst.
     * @param n specified ImmutableNodeInst
     * @return the number of Connections on specified ImmutableNodeInst.
     */
    public abstract int getNumConnections(ImmutableNodeInst n);

    /**
     * Returns true of there are Exports on specified NodeInst.
     * @param originalNode specified NodeInst.
     * @return true if there are Exports on specified NodeInst.
     */
    public abstract boolean hasExportsOnNode(ImmutableNodeInst originalNode);

    /**
     * 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 abstract int getNumExportsOnNode(int originalNodeId);

    /**
     * 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 abstract Iterator getExportsOnNode(int originalNodeId);

    /**
     * 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 (hasExportsOnNode(pin)) {
            return true;
        }
        if (numConnections == 0) {
            return false;
        }
        return true;
    }

    /**
     * Writes this CellRevision to IdWriter.
     * @param writer where to write.
     */
    void write(IdWriter writer) throws IOException {
        d.write(writer);
        writer.writeInt(nodes.size());
        for (ImmutableNodeInst n : nodes) {
            n.write(writer);
        }
        writer.writeInt(arcs.size());
        for (ImmutableArcInst a : arcs) {
            a.write(writer);
        }
        writer.writeInt(exports.size());
        for (ImmutableExport e : exports) {
            e.write(writer);
        }
    }

    /**
     * Reads CellRevision from SnapshotReader.
     * @param reader where to read.
     */
    static CellRevision read(IdReader reader) throws IOException {
        ImmutableCell d = ImmutableCell.read(reader);
        CellRevision revision = CellRevision.newInstance(d.withoutVariables());

        int nodesLength = reader.readInt();
        ImmutableNodeInst[] nodes = new ImmutableNodeInst[nodesLength];
        for (int i = 0; i < nodesLength; i++) {
            nodes[i] = ImmutableNodeInst.read(reader);
        }

        int arcsLength = reader.readInt();
        ImmutableArcInst[] arcs = new ImmutableArcInst[arcsLength];
        for (int i = 0; i < arcsLength; i++) {
            arcs[i] = ImmutableArcInst.read(reader);
        }

        int exportsLength = reader.readInt();
        ImmutableExport[] exports = new ImmutableExport[exportsLength];
        for (int i = 0; i < exportsLength; i++) {
            exports[i] = ImmutableExport.read(reader);
        }

        revision = revision.with(d, nodes, arcs, exports);
        return revision;
    }

    /**
     * Checks invariant of this CellRevision.
     * @throws AssertionError if invariant is broken.
     */
    public void check() {
        d.check();
        CellId cellId = d.cellId;
        boolean busNamesAllowed = d.busNamesAllowed();
        BitSet checkTechUsages = new BitSet();
        checkTechUsages.set(d.techId.techIndex);
        int[] checkCellUsages = getInstCounts();
        // Check nodes
        ImmutableNodeInst.checkList(nodes);
        boolean hasCellCenter = false;
        if (nodeIndex != null && nodeIndex.length > 0) {
            assert nodeIndex[nodeIndex.length - 1] >= 0;
            for (int nodeId = 0; nodeId < nodeIndex.length; nodeId++) {
                int nodeInd = nodeIndex[nodeId];
                if (nodeInd == -1) {
                    continue;
                }
                assert nodes.get(nodeInd).nodeId == nodeId;
            }
        }
        int nodeInd = 0;
        for (ImmutableNodeInst n : nodes) {
            assert nodeIndex != null ? nodeIndex[n.nodeId] == nodeInd : n.nodeId == nodeInd;
            assert getNodeById(n.nodeId) == n;
            if (ImmutableNodeInst.isCellCenter(n.protoId)) {
                assert !hasCellCenter;
                hasCellCenter = true;
            }
            assert busNamesAllowed || !n.name.isBus();
            if (n.protoId instanceof CellId) {
                CellId subCellId = (CellId) n.protoId;
                CellUsage u = cellId.getUsageIn(subCellId);
                checkCellUsages[u.indexInParent]--;
                CellUsageInfo cui = cellUsages[u.indexInParent];
                assert cui != null;
                for (int j = 0; j < n.ports.length; j++) {
                    ImmutablePortInst pid = n.ports[j];
                    if (pid == ImmutablePortInst.EMPTY) {
                        continue;
                    }
                    checkPortInst(n, subCellId.getPortId(j));
                }
                if (subCellId.isIcon()) {
                    for (Variable param : n.getDefinedParams()) {
                        assert cui.usedAttributes.get((Variable.AttrKey) param.getKey()) == param.getUnit();
                    }
                    for (Iterator it = n.getVariables(); it.hasNext();) {
                        Variable.Key varKey = it.next().getKey();
                        if (varKey.isAttribute()) {
                            assert cui.usedAttributes.get(varKey) == null;
                        }
                    }
                }
            } else {
                TechId techId = ((PrimitiveNodeId) n.protoId).techId;
                checkTechUsages.set(techId.techIndex);
            }
            nodeInd++;
        }
        for (int i = 0; i < checkCellUsages.length; i++) {
            assert checkCellUsages[i] == 0;
        }
        // Check arcs
        ImmutableArcInst.checkList(arcs);
        if (arcIndex != null && arcIndex.length > 0) {
            assert arcIndex[arcIndex.length - 1] >= 0;
            for (int arcId = 0; arcId < arcIndex.length; arcId++) {
                int arcInd = arcIndex[arcId];
                if (arcInd == -1) {
                    continue;
                }
                assert arcs.get(arcInd).arcId == arcId;
            }
        }
        int arcInd = 0;
        for (ImmutableArcInst a : arcs) {
            assert arcIndex != null ? arcIndex[a.arcId] == arcInd : a.arcId == arcInd;
            assert getArcById(a.arcId) == a;
            assert busNamesAllowed || !a.name.isBus();
            checkPortInst(getNodeById(a.tailNodeId), a.tailPortId);
            checkPortInst(getNodeById(a.headNodeId), a.headPortId);

            checkTechUsages.set(a.protoId.techId.techIndex);
            arcInd++;
        }
        // Check exports
        ImmutableExport.checkList(exports);
        if (exportIndex.length > 0) {
            assert exportIndex[exportIndex.length - 1] >= 0;
            for (int chronIndex = 0; chronIndex < exportIndex.length; chronIndex++) {
                int exportInd = exportIndex[chronIndex];
                if (exportInd == -1) {
                    continue;
                }
                assert exports.get(exportInd).exportId.chronIndex == chronIndex;
            }
        }
        assert exportIndex.length == definedExportsLength;
        assert definedExports.length() == definedExportsLength;
        int exportInd = 0;
        for (ImmutableExport e : exports) {
            assert e.exportId.parentId == cellId;
            assert exportIndex[e.exportId.chronIndex] == exportInd;
            assert busNamesAllowed || !e.name.isBus();
            checkPortInst(getNodeById(e.originalNodeId), e.originalPortId);
            exportInd++;
        }
        int exportCount = 0;
        for (int chronIndex = 0; chronIndex < exportIndex.length; chronIndex++) {
            int portIndex = exportIndex[chronIndex];
            if (portIndex == -1) {
                assert !definedExports.get(chronIndex);
                continue;
            }
            assert definedExports.get(chronIndex);
            exportCount++;
            assert exports.get(portIndex).exportId.chronIndex == chronIndex;
        }
        assert exports.size() == exportCount;
        BitSet checkDeleted = new BitSet();
        checkDeleted.set(0, definedExportsLength);
        checkDeleted.andNot(definedExports);
        assert deletedExports.equals(checkDeleted);
        if (definedExports.isEmpty()) {
            assert definedExports == EMPTY_BITSET;
        }
        if (deletedExports.isEmpty()) {
            assert deletedExports == EMPTY_BITSET;
        }
        assert techUsages.equals(checkTechUsages);

        if (d.cellId.isIcon()) {
            assert cellUsages.length == 0;
        }
        for (int i = 0; i < cellUsages.length; i++) {
            CellUsageInfo cui = cellUsages[i];
            if (cui == null) {
                continue;
            }
            cui.check(d.cellId.getUsageIn(i));
        }
    }

    private void checkPortInst(ImmutableNodeInst node, PortProtoId portId) {
        assert node != null;
        assert portId.getParentId() == node.protoId;
        if (portId instanceof ExportId) {
            checkExportId((ExportId) portId);
        }
    }

    private void checkExportId(ExportId exportId) {
        CellUsage u = d.cellId.getUsageIn(exportId.getParentId());
        assert cellUsages[u.indexInParent].usedExports.get(exportId.getChronIndex());
    }

    public boolean sameExports(CellRevision thatRevision) {
        if (thatRevision == this) {
            return true;
        }
        if (exports.size() != thatRevision.exports.size()) {
            return false;
        }
        for (int i = 0; i < exports.size(); i++) {
            if (exports.get(i).exportId != thatRevision.exports.get(i).exportId) {
                return false;
            }
        }
        return true;
    }

    protected static class CellUsageInfo {

        final int instCount;
        final BitSet usedExports;
        final int usedExportsLength;
        final TreeMap usedAttributes;

        CellUsageInfo(int instCount, BitSet usedExports, TreeMap usedAttributes) {
            this.instCount = instCount;
            usedExportsLength = usedExports.length();
            this.usedExports = usedExportsLength > 0 ? usedExports : EMPTY_BITSET;
            this.usedAttributes = usedAttributes;
        }

        CellUsageInfo with(int instCount, BitSet usedExports, TreeMap usedAttributes) {
            usedExports = UsageCollector.bitSetWith(this.usedExports, usedExports);
            usedAttributes = UsageCollector.usedAttributesWith(this.usedAttributes, usedAttributes);
            if (this.instCount == instCount && this.usedExports == usedExports
                    && this.usedAttributes == usedAttributes) {
                return this;
            }
            return new CellUsageInfo(instCount, usedExports, usedAttributes);
        }

        void checkUsage(CellRevision subCellRevision) {
            if (subCellRevision == null) {
                throw new IllegalArgumentException("subCell deleted");
            }
            if (subCellRevision.definedExportsLength < usedExportsLength || subCellRevision.deletedExports.intersects(usedExports)) {
                throw new IllegalArgumentException("exportUsages");
            }
            if (isIcon()) {
                for (Map.Entry e : usedAttributes.entrySet()) {
                    Variable.AttrKey paramKey = e.getKey();
                    Variable param = subCellRevision.d.getParameter(paramKey);
                    TextDescriptor.Unit unit = e.getValue();
                    if (unit != null) {
                        if (param == null || param.getUnit() != unit) {
                            throw new IllegalArgumentException("param " + paramKey);
                        }
                    } else {
                        if (param != null) {
                            throw new IllegalArgumentException("param " + paramKey);
                        }
                    }
                }
            }
        }

        private boolean isIcon() {
            return usedAttributes != null;
        }

        private void check(CellUsage u) {
            assert instCount > 0;
            assert usedExportsLength == usedExports.length();
            if (usedExportsLength == 0) {
                assert usedExports == EMPTY_BITSET;
            }
            assert isIcon() == u.protoId.isIcon();
            assert !u.parentId.isIcon();
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof CellUsageInfo) {
                CellUsageInfo that = (CellUsageInfo) o;
                return this.instCount == that.instCount && this.usedExports == that.usedExports
                        && this.usedExportsLength == that.usedExportsLength
                        && (this.usedAttributes == null && that.usedAttributes == null
                        || this.usedAttributes.equals(that.usedAttributes));
            }
            return false;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 41 * hash + this.instCount;
            hash = 41 * hash + this.usedExportsLength;
            return hash;
        }
    }

    @Override
    public String toString() {
        return d.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy