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

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

There is a newer version: 9.02-e
Show 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 static com.sun.electric.database.UsageCollector.EMPTY_BITSET;
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.TextUtils;
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.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 class CellRevision {

    public static final CellRevision[] NULL_ARRAY = {};
    public static final ImmutableArrayList EMPTY_LIST = new ImmutableArrayList(NULL_ARRAY);
    private 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 ImmutableArrayList nodes;
    /** Map from chronIndex of Nodes to sortIndex. */
    final int nodeIndex[];
    /** A list of ArcInsts in this Cell. */
    public final ImmutableArrayList arcs;
    /** Map from chronIndex of Arcs to sortIndex. */
    final int arcIndex[];
    /** An array of Exports on the Cell by chronological index. */
    public final ImmutableArrayList exports;
    /** Map from chronIndex of Exports to sortIndex. */
    final 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 */
    private CellRevision(ImmutableCell d,
            ImmutableArrayList nodes, int[] nodeIndex,
            ImmutableArrayList arcs, int[] arcIndex,
            ImmutableArrayList 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++;
        if (Job.getDebug())
            check();
    }

    /** Creates a new instance of CellRevision */
    public CellRevision(ImmutableCell d) {
        this(d, ImmutableNodeInst.EMPTY_LIST, NULL_INT_ARRAY,
                ImmutableArcInst.EMPTY_LIST, NULL_INT_ARRAY,
                ImmutableExport.EMPTY_LIST, NULL_INT_ARRAY,
                makeTechUsages(d.techId), NULL_CELL_USAGE_INFO_ARRAY, EMPTY_BITSET, 0, EMPTY_BITSET);
        if (d.techId == null) {
            throw new NullPointerException("techId");
        }
    }

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

    /**
     * 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 new CellRevision(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) {
        ImmutableArrayList nodes = copyArray(nodesArray, this.nodes);
        ImmutableArrayList arcs = copyArray(arcsArray, this.arcs);
        ImmutableArrayList exports = copyArray(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 && !nodes.isEmpty()) {
            boolean sameNodeIndex = nodes.size() == this.nodes.size();
            int nodeIndexLength = 0;
            boolean hasCellCenter = false;
            ImmutableNodeInst prevN = null;
            for (int nodeInd = 0; nodeInd < nodes.size(); nodeInd++) {
                ImmutableNodeInst n = nodes.get(nodeInd);
                sameNodeIndex = sameNodeIndex && n.nodeId == this.nodes.get(nodeInd).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);
                }
                if (prevN != null && TextUtils.STRING_NUMBER_ORDER.compare(prevN.name.toString(), n.name.toString()) >= 0) {
                    throw new IllegalArgumentException("nodes order");
                }
                prevN = n;
            }
            if (!sameNodeIndex) {
                nodeIndex = new int[nodeIndexLength];
                Arrays.fill(nodeIndex, -1);
                for (int nodeInd = 0; nodeInd < nodes.size(); nodeInd++) {
                    ImmutableNodeInst n = nodes.get(nodeInd);
                    int nodeId = n.nodeId;
                    if (nodeIndex[nodeId] >= 0) {
                        throw new IllegalArgumentException("nodeChronIndex");
                    }
                    nodeIndex[nodeId] = nodeInd;
                }
                assert !Arrays.equals(this.nodeIndex, nodeIndex);
            }
        }

        int[] arcIndex = this.arcIndex;
        if (arcs != this.arcs && !arcs.isEmpty()) {
            boolean sameArcIndex = arcs.size() == this.arcs.size();
            int arcIndexLength = 0;
            ImmutableArcInst prevA = null;
            for (int arcInd = 0; arcInd < arcs.size(); arcInd++) {
                ImmutableArcInst a = arcs.get(arcInd);
                sameArcIndex = sameArcIndex && a.arcId == this.arcs.get(arcInd).arcId;
                arcIndexLength = Math.max(arcIndexLength, a.arcId + 1);
                if (!busNamesAllowed && a.name.isBus()) {
                    throw new IllegalArgumentException("arrayedName " + a.name);
                }
                if (prevA != null) {
                    int cmp = TextUtils.STRING_NUMBER_ORDER.compare(prevA.name.toString(), a.name.toString());
                    if (cmp > 0 || cmp == 0 && (a.name.isTempname() || prevA.arcId >= a.arcId)) {
                        throw new IllegalArgumentException("arcs order");
                    }
                }
                prevA = a;
            }
            if (!sameArcIndex) {
                arcIndex = new int[arcIndexLength];
                Arrays.fill(arcIndex, -1);
                for (int arcInd = 0; arcInd < arcs.size(); arcInd++) {
                    ImmutableArcInst a = arcs.get(arcInd);
                    int arcId = a.arcId;
                    if (arcIndex[arcId] >= 0) {
                        throw new IllegalArgumentException("arcChronIndex");
                    }
                    arcIndex[arcId] = 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;
            String prevExportName = null;
            for (int exportInd = 0; exportInd < exports.size(); exportInd++) {
                ImmutableExport e = exports.get(exportInd);
                if (e.exportId.parentId != cellId) {
                    throw new IllegalArgumentException("exportId");
                }
                String exportName = e.name.toString();
                if (!busNamesAllowed && e.name.isBus()) {
                    throw new IllegalArgumentException("arrayedName " + e.name);
                }
                if (prevExportName != null && TextUtils.STRING_NUMBER_ORDER.compare(prevExportName, exportName) >= 0) {
                    throw new IllegalArgumentException("exportName");
                }
                prevExportName = exportName;
                int chronIndex = e.exportId.chronIndex;
                sameExportIndex = sameExportIndex && chronIndex == this.exports.get(exportInd).exportId.chronIndex;
                exportIndexLength = Math.max(exportIndexLength, chronIndex + 1);
            }
            if (!sameExportIndex) {
                exportIndex = new int[exportIndexLength];
                Arrays.fill(exportIndex, -1);
                for (int portIndex = 0; portIndex < exports.size(); portIndex++) {
                    ImmutableExport e = exports.get(portIndex);
                    int chronIndex = e.exportId.chronIndex;
                    if (exportIndex[chronIndex] >= 0) {
                        throw new IllegalArgumentException("exportChronIndex");
                    }
                    exportIndex[chronIndex] = portIndex;
                    //checkPortInst(nodesById.get(e.originalNodeId), e.originalPortId);
                }
                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);
                }
            }
        }

        CellRevision revision = new CellRevision(d, nodes, nodeIndex,
                arcs, arcIndex,
                exports, exportIndex,
                techUsages, cellUsages, definedExports, definedExportsLength, deletedExports);
        return revision;
    }

    private static  ImmutableArrayList copyArray(T[] newArray, ImmutableArrayList oldList) {
        return newArray != null ? new ImmutableArrayList(newArray) : oldList;
    }

    /**
     * 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
     */
    public ImmutableNodeInst getNodeById(int nodeId) {
        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) {
        return nodeId < nodeIndex.length ? nodeIndex[nodeId] : -1;
    }

    /**
     * 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.length - 1;
    }

    /**
     * Returns ImmutableArcInst by its arcId.
     * @param arcId of ImmutableArcInst.
     * @return ImmutableArcInst with given arcId
     */
    public ImmutableArcInst getArcById(int arcId) {
        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) {
        return arcId < arcIndex.length ? arcIndex[arcId] : -1;
    }

    /**
     * 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.length - 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 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;
    }

    /**
     * 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 = new CellRevision(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
        boolean hasCellCenter = false;
        if (nodeIndex.length > 0) {
            assert nodeIndex[nodeIndex.length - 1] >= 0;
        }
        ImmutableNodeInst prevN = null;
        for (int nodeInd = 0; nodeInd < nodes.size(); nodeInd++) {
            ImmutableNodeInst n = nodes.get(nodeInd);
            n.check();
            assert nodeIndex[n.nodeId] == nodeInd;
            assert getNodeById(n.nodeId) == n;
            if (ImmutableNodeInst.isCellCenter(n.protoId)) {
                assert !hasCellCenter;
                hasCellCenter = true;
            }
            assert busNamesAllowed || !n.name.isBus();
            if (prevN != null) {
                assert TextUtils.STRING_NUMBER_ORDER.compare(prevN.name.toString(), n.name.toString()) < 0;
            }
            prevN = n;
            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);
            }
        }
        for (int i = 0; i < checkCellUsages.length; i++) {
            assert checkCellUsages[i] == 0;
        }
        // Check arcs
        if (arcIndex.length > 0) {
            assert arcIndex[arcIndex.length - 1] >= 0;
        }
        ImmutableArcInst prevA = null;
        for (int arcInd = 0; arcInd < arcs.size(); arcInd++) {
            ImmutableArcInst a = arcs.get(arcInd);
            assert arcIndex[a.arcId] == arcInd;
            assert getArcById(a.arcId) == a;
            assert busNamesAllowed || !a.name.isBus();
            if (prevA != null) {
                int cmp = TextUtils.STRING_NUMBER_ORDER.compare(prevA.name.toString(), a.name.toString());
                assert cmp <= 0;
                if (cmp == 0) {
                    assert !a.name.isTempname();
                    assert prevA.arcId < a.arcId;
                }
            }
            prevA = a;

            a.check();
            checkPortInst(getNodeById(a.tailNodeId), a.tailPortId);
            checkPortInst(getNodeById(a.headNodeId), a.headPortId);

            checkTechUsages.set(a.protoId.techId.techIndex);
        }
        // Check exports
        if (exportIndex.length > 0) {
            assert exportIndex[exportIndex.length - 1] >= 0;
        }
        assert exportIndex.length == definedExportsLength;
        assert definedExports.length() == definedExportsLength;
        for (int exportInd = 0; exportInd < exports.size(); exportInd++) {
            ImmutableExport e = exports.get(exportInd);
            e.check();
            assert e.exportId.parentId == cellId;
            assert exportIndex[e.exportId.chronIndex] == exportInd;
            assert busNamesAllowed || !e.name.isBus();
            if (exportInd > 0) {
                assert (TextUtils.STRING_NUMBER_ORDER.compare(exports.get(exportInd - 1).name.toString(), e.name.toString()) < 0) : exportInd;
            }
            checkPortInst(getNodeById(e.originalNodeId), e.originalPortId);
        }
        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;
    }

    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 String toString() {
        return d.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy