/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: TechPool.java
* Written by: Dmitry Nadezhin, Sun Microsystems.
*
* Copyright (c) 2008 Sun Microsystems and Static Free Software
*
* 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.technology;
import com.sun.electric.database.CellRevision;
import com.sun.electric.database.Environment;
import com.sun.electric.database.ImmutableArcInst;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.id.ArcProtoId;
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.LayerId;
import com.sun.electric.database.id.PrimitiveNodeId;
import com.sun.electric.database.id.PrimitivePortId;
import com.sun.electric.database.id.TechId;
import com.sun.electric.database.text.ArrayIterator;
import com.sun.electric.database.text.Setting;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.text.Version;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.Technology.SizeCorrector;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.technology.technologies.Schematics;
import java.io.IOException;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* A customized Map from TechId to Technolgy.
* All TechIds must belong to same IdManager.
*/
public class TechPool extends AbstractMap<TechId, Technology> {
// public static final TechPool EMPTY = new TechPool(Collections.<Technology>emptySet());
public final IdManager idManager;
private final Technology[] techs;
private final Technology.State[] states;
private final EntrySet entrySet;
private ArcProto[] univList;
/** Generic technology */
private Generic generic;
/** Artwork technology */
private Artwork artwork;
/** Schematic technology */
private Schematics schematics;
/**
* Constructs empty TechPool
* @param idManager pool's IdManager
*/
public TechPool(IdManager idManager) {
this.idManager = idManager;
techs = new Technology[0];
states = new Technology.State[0];
entrySet = new EntrySet();
assert size() == 0;
univList = new ArcProto[0];
}
/**
* Constructs TechPool from Collection of technologies
* All technologies must belong to the same IdManager
* @param technologies Collection of technologies in the Pool
*/
private TechPool(Collection<Technology.State> techStates) {
Iterator<Technology.State> it = techStates.iterator();
TechId techId = it.next().getTechnology().getId();
idManager = techId.idManager;
int maxTechIndex = techId.techIndex;
while (it.hasNext()) {
techId = it.next().getTechnology().getId();
if (techId.idManager != idManager)
throw new IllegalArgumentException();
maxTechIndex = Math.max(maxTechIndex, techId.techIndex);
}
techs = new Technology[maxTechIndex + 1];
states = new Technology.State[maxTechIndex + 1];
for (Technology.State state: techStates) {
Technology tech = state.getTechnology();
techId = tech.getId();
int techIndex = techId.techIndex;
if (techs[techIndex] != null)
throw new IllegalArgumentException();
techs[techIndex] = tech;
states[techIndex] = state;
if (tech instanceof Generic) {
generic = (Generic) tech;
} else if (tech instanceof Artwork) {
artwork = (Artwork) tech;
} else if (tech instanceof Schematics) {
schematics = (Schematics) tech;
}
}
entrySet = new EntrySet();
assert size() == techStates.size();
}
public Map<TechFactory.Param,Object> getTechParams() {
LinkedHashMap<TechFactory.Param,Object> techParams = new LinkedHashMap<TechFactory.Param,Object>();
for (int techIndex = 0; techIndex < states.length; techIndex++) {
Technology.State state = states[techIndex];
if (state == null) continue;
techParams.putAll(state.paramValues);
}
return techParams;
}
public TechPool withTechParams(Map<TechFactory.Param,Object> paramValues) {
assert isActive();
ArrayList<Technology.State> newTechStates = new ArrayList<Technology.State>();
boolean changed = false;
for (int techIndex = 0; techIndex < techs.length; techIndex++) {
Technology tech = techs[techIndex];
if (tech == null) continue;
Technology.State state = states[techIndex];
Technology.State newTechState = tech.withState(paramValues);
if (newTechState != state)
changed = true;
newTechStates.add(newTechState);
}
return changed ? new TechPool(newTechStates) : this;
}
public void activate() {
for (int techIndex = 0; techIndex < states.length; techIndex++) {
Technology.State techState = states[techIndex];
if (techState == null) continue;
techState.activate();
}
}
public boolean isActive() {
for (int techIndex = 0; techIndex < states.length; techIndex++) {
Technology.State techState = states[techIndex];
if (techState == null) continue;
if (techState != techState.getTechnology().getCurrentState())
return false;
}
return true;
}
public TechPool deepClone() {
ArrayList<Technology.State> newTechStates = new ArrayList<Technology.State>();
Generic newGeneric = Generic.newInstance(idManager);
newTechStates.add(newGeneric.getCurrentState());
for (int techIndex = 0; techIndex < states.length; techIndex++) {
Technology tech = techs[techIndex];
if (tech == null || tech == generic) continue;
Technology newTech = tech.techFactory.newInstance(generic, states[techIndex].paramValues);
newTechStates.add(newTech.getCurrentState());
}
return new TechPool(newTechStates);
}
/**
* Returns restriction of this TechPool to specified subset of TechIds.
* A candidate TechPool is a valid result, it is returned to save allocation.
* @param techUsed contains techIndex of those TechIds which are in subset
* @param candidatePool a candidate TechPool to save allocation
* @return restriction of this TechPool to specified subset of TechIds
*/
public TechPool restrict(BitSet techUsed, TechPool candidatePool) {
if (candidatePool != null && candidatePool.isRestriction(this, techUsed))
return candidatePool;
return restrict(techUsed);
}
/**
* Returns restriction of this TechPool to specified subset of TechIds
* @param techUsed contains techIndex of those TechIds which are in subset
* @return restriction of this TechPool to specified subset of TechIds
*/
public TechPool restrict(BitSet techUsed) {
ArrayList<Technology.State> techStates = new ArrayList<Technology.State>();
for (int techIndex = 0; techIndex < techs.length; techIndex++) {
if (techUsed.get(techIndex)) {
techStates.add(states[techIndex]);
}
}
if (techStates.size() != techUsed.cardinality())
throw new IllegalArgumentException();
return techStates.isEmpty() ? new TechPool(idManager) : new TechPool(techStates);
}
/**
* Returns true if this pool is the restriction of specified super pool.
* @param superPool specified super pool
* @return true if this pool is the restriction of specified super pool.
*/
public boolean isRestriction(TechPool superPool) {
return isRestriction(superPool, getTechUsages());
}
private boolean isRestriction(TechPool superPool, BitSet techUsed) {
if (techUsed.length() != techs.length)
return false;
if (techs.length > superPool.techs.length)
return false;
if (idManager != superPool.idManager)
return false;
for (int techIndex = 0; techIndex < techs.length; techIndex++) {
Technology tech = superPool.techs[techIndex];
Technology.State state = superPool.states[techIndex];
if (techUsed.get(techIndex)) {
if (tech == null)
return false;
} else {
tech = null;
state = null;
}
if (techs[techIndex] != tech || states[techIndex] != state)
return false;
}
return true;
}
/**
* Returns a BitSet of techIndices of Technologies from this TechPool.
* @return techIndices of Technologies from this TechPool.
*/
public BitSet getTechUsages() {
BitSet bs = new BitSet(techs.length);
for (int techIndex = 0; techIndex < techs.length; techIndex++) {
if (techs[techIndex] != null)
bs.set(techIndex);
}
return bs;
}
/**
* Returns new TechPool which differs from this TechPool by adding
* new technology
* @param tech Technology to add
* @return TechPool which differs from this TechPool by adding technology
*/
public TechPool withTech(Technology tech) {
TechId techId = tech.getId();
int techIndex = techId.techIndex;
if (techIndex < techs.length && techs[techIndex] == tech && states[techIndex] == tech.getCurrentState()) {
return this;
}
if (techId.idManager != idManager) {
throw new IllegalArgumentException();
}
ArrayList<Technology.State> newStates = new ArrayList<Technology.State>();
for (int i = 0; i < states.length; i++) {
if (i == techIndex || states[i] == null) continue;
newStates.add(states[i]);
}
newStates.add(tech.getCurrentState());
return new TechPool(newStates);
}
/**
* Get Technology by TechId
* TechId must belong to same IdManager as TechPool
* @param techId TechId to find
* @return Technology by given TechId or null
* @throws IllegalArgumentException if TechId is not from this IdManager
*/
public Technology getTech(TechId techId) {
if (techId.idManager != idManager) {
throw new IllegalArgumentException();
}
int techIndex = techId.techIndex;
return techIndex < techs.length ? techs[techIndex] : null;
}
/**
* Find Technology by its name
* @param techName name of technology
* @return Technology by given name or null
*/
public Technology findTechnology(String techName) {
for (Technology tech: values()) {
if (tech.getTechName().equals(techName))
return tech;
}
return null;
}
/**
* Get Technology.State by TechId
* TechId must belong to same IdManager as TechPool
* @param techId TechId to find
* @return Technology by given TechId or null
* @throws IllegalArgumentException if TechId is not from this IdManager
*/
public Technology.State getState(TechId techId) {
if (techId.idManager != idManager) {
throw new IllegalArgumentException();
}
int techIndex = techId.techIndex;
return techIndex < states.length ? states[techIndex] : null;
}
/**
* Get Layer by LayerId
* LayerId must belong to same IdManager as TechPool
* @param layerId LayerId to find
* @return Layer by given LayerId or null
* @throws IllegalArgumentException if TechId is not from this IdManager
*/
public Layer getLayer(LayerId layerId) {
Technology tech = getTech(layerId.techId);
if (tech == null) return null;
return tech.getLayerByChronIndex(layerId.chronIndex);
}
/**
* Get ArcProto by ArcProtoId
* ArcProtoId must belong to same IdManager as TechPool
* @param arcProtoId ArcProtoId to find
* @return ArcProto by given ArcProtoId or null
* @throws IllegalArgumentException if TechId is not from this IdManager
*/
public ArcProto getArcProto(ArcProtoId arcProtoId) {
Technology tech = getTech(arcProtoId.techId);
if (tech == null) return null;
return tech.getArcProtoByChronIndex(arcProtoId.chronIndex);
}
/**
* Get PrimitiveNode by PrimitiveNodeId
* PrimitiveNodeId must belong to same IdManager as TechPool
* @param primitiveNodeId PrimitiveNodeId to find
* @return PrimitiveNode by given PrimitiveNodeId or null
* @throws IllegalArgumentException if TechId is not from this IdManager
*/
public PrimitiveNode getPrimitiveNode(PrimitiveNodeId primitiveNodeId) {
Technology tech = getTech(primitiveNodeId.techId);
if (tech == null) return null;
return tech.getPrimitiveNodeByChronIndex(primitiveNodeId.chronIndex);
}
/**
* Get PrimitivePort by PrimitivePortId
* PrimitivePortId must belong to same IdManager as TechPool
* @param primitivePortId PrimitivePortId to find
* @return PrimitivePort by given PrimitivePortId or null
* @throws IllegalArgumentException if TechId is not from this IdManager
*/
public PrimitivePort getPrimitivePort(PrimitivePortId primitivePortId) {
PrimitiveNode pn = getPrimitiveNode(primitivePortId.getParentId());
if (pn == null) return null;
return pn.getPrimitivePortByChronIndex(primitivePortId.chronIndex);
}
public void correctSizesToDisk(List<CellRevision> cells, Version version, Map<Setting,Object> projectSettings, boolean isJelib, boolean keepExtendOverMin) {
HashMap<TechId,SizeCorrector> sizeCorrectors = new HashMap<TechId,SizeCorrector>();
for (int i = 0; i < cells.size(); i++) {
CellRevision cellRevision = cells.get(i);
boolean needCorrection = false;
for (TechId techId: cellRevision.getTechUsages()) {
SizeCorrector sizeCorrector = sizeCorrectors.get(techId);
if (sizeCorrector == null) {
if (!sizeCorrectors.containsKey(techId)) {
Technology tech = getTech(techId);
sizeCorrector = tech.getSizeCorrector(version, projectSettings, isJelib, keepExtendOverMin);
if (sizeCorrector.isIdentity())
sizeCorrector = null;
sizeCorrectors.put(techId, sizeCorrector);
}
}
if (sizeCorrector != null)
needCorrection = true;
}
if (!needCorrection) continue;
ImmutableNodeInst[] correctedNodes = new ImmutableNodeInst[cellRevision.nodes.size()];
for (int nodeIndex = 0; nodeIndex < correctedNodes.length; nodeIndex++) {
ImmutableNodeInst n = cellRevision.nodes.get(nodeIndex);
if (n.protoId instanceof PrimitiveNodeId) {
PrimitiveNodeId pnId = (PrimitiveNodeId)n.protoId;
SizeCorrector sizeCorrector = sizeCorrectors.get(pnId.techId);
if (sizeCorrector != null)
n = n.withSize(sizeCorrector.getSizeToDisk(n));
}
correctedNodes[nodeIndex] = n;
}
ImmutableArcInst[] correctedArcs = new ImmutableArcInst[cellRevision.arcs.size()];
for (int arcIndex = 0; arcIndex < correctedArcs.length; arcIndex++) {
ImmutableArcInst a = cellRevision.arcs.get(arcIndex);
if (a == null) continue;
ArcProtoId apId = a.protoId;
SizeCorrector sizeCorrector = sizeCorrectors.get(apId.techId);
if (sizeCorrector != null)
a = a.withGridExtendOverMin(sizeCorrector.getExtendToDisk(a));
correctedArcs[arcIndex] = a;
}
cellRevision = cellRevision.with(cellRevision.d, correctedNodes, correctedArcs, null);
cells.set(i, cellRevision);
}
}
/** Returns Artwork technology in this database */
public Artwork getArtwork() {
return artwork;
}
/** Returns Generic technology in this database */
public Generic getGeneric() {
return generic;
}
/** Returns Schematic technology in this database */
public Schematics getSchematics() {
return schematics;
}
/**
* Tests that two TechPools contains the same set of Tehnologies
* @param that second TechPool
* @return true if this and that TechPools are equal
*/
public boolean equals(TechPool that) {
if (idManager != that.idManager || techs.length != that.techs.length) {
return false;
}
for (int techIndex = 0; techIndex < techs.length; techIndex++) {
if (techs[techIndex] != that.techs[techIndex]) {
return false;
}
}
return true;
}
// Implementation of Map<TechId,Technology>
@Override
public boolean containsKey(Object key) {
int techIndex = ((TechId) key).techIndex;
if (techIndex >= techs.length) {
return false;
}
Technology tech = techs[techIndex];
return tech != null && tech.getId() == key;
}
@Override
public boolean containsValue(Object value) {
int techIndex = ((Technology) value).getId().techIndex;
return techIndex < techs.length && techs[techIndex] == value;
}
@Override
public Technology get(Object key) {
int techIndex = ((TechId) key).techIndex;
if (techIndex >= techs.length) {
return null;
}
Technology tech = techs[techIndex];
return tech != null && tech.getId() == key ? tech : null;
}
@Override
public Set<Map.Entry<TechId, Technology>> entrySet() {
return entrySet;
}
@Override
public boolean equals(Object o) {
return o instanceof TechPool ? equals((TechPool) o) : super.equals(o);
}
/**
* Writes this TechPool to IdWriter
* @param writer IdWriter
* @throws java.io.IOException
*/
public void writeDiff(IdWriter writer, TechPool old) throws IOException {
boolean isEmpty = isEmpty();
writer.writeBoolean(isEmpty);
if (isEmpty) return;
writer.writeDiffs();
Generic generic = getGeneric();
assert generic != null;
boolean newGeneric = generic != old.getGeneric();
writer.writeBoolean(newGeneric);
for (Technology tech : values()) {
if (tech == generic) continue;
TechId techId = tech.getId();
writer.writeInt(techId.techIndex);
Technology.State techState = getState(techId);
boolean sameState = techState == old.getState(techId);
writer.writeBoolean(sameState);
if (sameState) continue;
boolean sameTech = tech == old.getTech(techId);
writer.writeBoolean(sameTech);
if (!sameTech)
tech.techFactory.write(writer);
for (TechFactory.Param techParam: tech.techFactory.getTechParams()) {
Object value = techState.paramValues.get(techParam);
writer.writeString(techParam.xmlPath);
Variable.writeObject(writer, value);
}
}
writer.writeInt(-1);
}
/**
* Reads TechPool from IdReader
* @param reader IdReader
* @return TechPool read
* @throws java.io.IOException
*/
public static TechPool read(IdReader reader, TechPool old) throws IOException {
boolean isEmpty = reader.readBoolean();
if (isEmpty)
return new TechPool(reader.idManager);
reader.readDiffs();
ArrayList<Technology.State> technologiesList = new ArrayList<Technology.State>();
boolean newGeneric = reader.readBoolean();
Generic generic = newGeneric ? Generic.newInstance(reader.idManager) : old.getGeneric();
technologiesList.add(generic.getCurrentState());
for (;;) {
int techIndex = reader.readInt();
if (techIndex == -1)
break;
TechId techId = reader.idManager.getTechId(techIndex);
assert techId != null;
boolean sameState = reader.readBoolean();
if (sameState) {
technologiesList.add(old.getState(techId));
continue;
}
boolean sameTech = reader.readBoolean();
TechFactory techFactory = sameTech ? old.getTech(techId).techFactory : TechFactory.read(reader);
HashMap<TechFactory.Param,Object> paramValues = new HashMap<TechFactory.Param,Object>();
for (TechFactory.Param techParam: techFactory.getTechParams()) {
String xmlPath = reader.readString();
assert xmlPath.equals(techParam.xmlPath);
Object value = Variable.readObject(reader);
paramValues.put(techParam, value);
}
Technology.State techState;
if (sameTech) {
Technology tech = old.getTech(techId);
techState = tech.newState(paramValues);
} else {
techState = techFactory.newInstance(generic, paramValues).getCurrentState();
}
technologiesList.add(techState);
}
return new TechPool(technologiesList);
}
/**
* the connecitivity list for universal and invisible pins
*/
ArcProto[] getUnivList() {
if (univList == null)
makeUnivList();
return univList;
}
private void makeUnivList() {
// prepare connectivity list for universal and invisible pins
int univListCount = 0;
for (Technology tech : techs) {
if (tech != null) {
univListCount += tech.getNumArcs();
}
}
univList = new ArcProto[univListCount];
univListCount = 0;
for (Technology tech : techs) {
if (tech == null) {
continue;
}
for (Iterator<ArcProto> ait = tech.getArcs(); ait.hasNext();) {
univList[univListCount++] = ait.next();
}
}
assert univListCount == univList.length;
}
/**
* Checks invariants in this TechPool.
* @exception AssertionError if invariants are not valid
*/
public void check() {
int size = 0;
int arcCount = 0;
for (int techIndex = 0; techIndex < techs.length; techIndex++) {
Technology tech = techs[techIndex];
if (tech == null) {
assert states[techIndex] == null;
continue;
}
assert states[techIndex].getTechnology() == tech;
size++;
TechId techId = tech.getId();
assert techId.idManager == idManager;
assert techId.techIndex == techIndex;
assert idManager.getTechId(techIndex) == techId;
assert get(techId) == tech;
assert containsKey(techId);
assert containsValue(tech);
if (univList != null) {
for (Iterator<ArcProto> it = tech.getArcs(); it.hasNext(); ) {
ArcProto ap = it.next();
assert univList[arcCount++] == ap;
}
}
}
assert size == size();
if (size != 0)
assert getGeneric() != null;
assert size == 0 || techs[techs.length - 1] != null;
if (univList != null)
assert arcCount == univList.length;
TechId prevTechId = null;
for (Entry<TechId,Technology> e: entrySet()) {
assert entrySet.contains(e);
TechId techId = e.getKey();
Technology tech = e.getValue();
assert techId == tech.getId();
assert techs[techId.techIndex] == tech;
if (prevTechId != null)
assert TextUtils.STRING_NUMBER_ORDER.compare(prevTechId.techName, techId.techName) < 0;
prevTechId = techId;
}
}
private class EntrySet extends AbstractSet<Map.Entry<TechId, Technology>> {
private final TechEntry[] entries;
EntrySet() {
TreeSet<Technology> sortedTechs = new TreeSet<Technology>();
for (Technology tech : techs) {
if (tech != null)
sortedTechs.add(tech);
}
entries = new TechEntry[sortedTechs.size()];
int i = 0;
for (Technology tech: sortedTechs) {
TechId techId = tech.getId();
entries[i++] = new TechEntry(techId, tech);
}
}
@Override
public int size() {
return entries.length;
}
@Override
public boolean contains(Object o) {
Entry e = (Entry) o;
Technology tech = (Technology) e.getValue();
return containsValue(tech) && e.getKey().equals(tech.getId());
}
@Override
public Iterator<Entry<TechId, Technology>> iterator() {
return ArrayIterator.<Map.Entry<TechId,Technology>>iterator(entries);
}
}
private static class TechEntry implements Map.Entry<TechId,Technology> {
private final TechId key;
private final Technology value;
public TechEntry(TechId key, Technology value) {
if (key == null || value == null)
throw new NullPointerException();
this.key = key;
this.value = value;
}
public TechId getKey() { return key; }
public Technology getValue() { return value; }
public Technology setValue(Technology value) { throw new UnsupportedOperationException(); }
@Override public int hashCode() { return key.hashCode() ^ value.hashCode(); }
@Override public String toString() { return key + "=" + value; }
@Override
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
return key.equals(e.getKey()) && value.equals(e.getValue());
}
}
/**
* Returns thread-local TechPool
* @return thread-local TechPool
*/
public static TechPool getThreadTechPool() {
return Environment.getThreadEnvironment().techPool;
}
}