jjg@113: /* ohair@554: * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. jjg@113: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@113: * jjg@113: * This code is free software; you can redistribute it and/or modify it jjg@113: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this jjg@113: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. jjg@113: * jjg@113: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@113: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@113: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@113: * version 2 for more details (a copy is included in the LICENSE file that jjg@113: * accompanied this code). jjg@113: * jjg@113: * You should have received a copy of the GNU General Public License version jjg@113: * 2 along with this work; if not, write to the Free Software Foundation, jjg@113: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@113: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. jjg@113: */ jjg@113: jjg@113: package com.sun.tools.javac.util; jjg@113: jjg@113: import java.lang.ref.SoftReference; jjg@113: jjg@113: /** jjg@113: * Implementation of Name.Table that stores all names in a single shared jjg@113: * byte array, expanding it as needed. This avoids the overhead incurred jjg@113: * by using an array of bytes for each name. jjg@113: * jjg@581: *

This is NOT part of any supported API. jjg@581: * If you write code that depends on this, you do so at your own risk. jjg@113: * This code and its internal interfaces are subject to change or jjg@113: * deletion without notice. jjg@113: */ jjg@113: public class SharedNameTable extends Name.Table { jjg@113: // maintain a freelist of recently used name tables for reuse. jjg@113: private static List> freelist = List.nil(); jjg@113: jjg@113: static public synchronized SharedNameTable create(Names names) { jjg@113: while (freelist.nonEmpty()) { jjg@113: SharedNameTable t = freelist.head.get(); jjg@113: freelist = freelist.tail; jjg@113: if (t != null) { jjg@113: return t; jjg@113: } jjg@113: } jjg@113: return new SharedNameTable(names); jjg@113: } jjg@113: jjg@113: static private synchronized void dispose(SharedNameTable t) { jjg@113: freelist = freelist.prepend(new SoftReference(t)); jjg@113: } jjg@113: jjg@113: /** The hash table for names. jjg@113: */ jjg@113: private NameImpl[] hashes; jjg@113: jjg@113: /** The shared byte array holding all encountered names. jjg@113: */ jjg@113: public byte[] bytes; jjg@113: jjg@113: /** The mask to be used for hashing jjg@113: */ jjg@113: private int hashMask; jjg@113: jjg@113: /** The number of filled bytes in `names'. jjg@113: */ jjg@113: private int nc = 0; jjg@113: jjg@113: /** Allocator jjg@113: * @param names The main name table jjg@113: * @param hashSize the (constant) size to be used for the hash table jjg@113: * needs to be a power of two. jjg@113: * @param nameSize the initial size of the name table. jjg@113: */ jjg@113: public SharedNameTable(Names names, int hashSize, int nameSize) { jjg@113: super(names); jjg@113: hashMask = hashSize - 1; jjg@113: hashes = new NameImpl[hashSize]; jjg@113: bytes = new byte[nameSize]; jjg@113: jjg@113: } jjg@113: jjg@113: public SharedNameTable(Names names) { jjg@113: this(names, 0x8000, 0x20000); jjg@113: } jjg@113: jjg@113: @Override jjg@113: public Name fromChars(char[] cs, int start, int len) { jjg@113: int nc = this.nc; jjg@113: byte[] bytes = this.bytes; jjg@113: while (nc + len * 3 >= bytes.length) { jjg@113: // System.err.println("doubling name buffer of length " + names.length + " to fit " + len + " chars");//DEBUG jjg@113: byte[] newnames = new byte[bytes.length * 2]; jjg@113: System.arraycopy(bytes, 0, newnames, 0, bytes.length); jjg@113: bytes = this.bytes = newnames; jjg@113: } jjg@113: int nbytes = Convert.chars2utf(cs, start, bytes, nc, len) - nc; jjg@113: int h = hashValue(bytes, nc, nbytes) & hashMask; jjg@113: NameImpl n = hashes[h]; jjg@113: while (n != null && jjg@113: (n.getByteLength() != nbytes || jjg@113: !equals(bytes, n.index, bytes, nc, nbytes))) { jjg@113: n = n.next; jjg@113: } jjg@113: if (n == null) { jjg@113: n = new NameImpl(this); jjg@113: n.index = nc; jjg@113: n.length = nbytes; jjg@113: n.next = hashes[h]; jjg@113: hashes[h] = n; jjg@113: this.nc = nc + nbytes; jjg@113: if (nbytes == 0) { jjg@113: this.nc++; jjg@113: } jjg@113: } jjg@113: return n; jjg@113: } jjg@113: jjg@113: @Override jjg@113: public Name fromUtf(byte[] cs, int start, int len) { jjg@113: int h = hashValue(cs, start, len) & hashMask; jjg@113: NameImpl n = hashes[h]; jjg@113: byte[] names = this.bytes; jjg@113: while (n != null && jjg@113: (n.getByteLength() != len || !equals(names, n.index, cs, start, len))) { jjg@113: n = n.next; jjg@113: } jjg@113: if (n == null) { jjg@113: int nc = this.nc; jjg@113: while (nc + len > names.length) { jjg@113: // System.err.println("doubling name buffer of length + " + names.length + " to fit " + len + " bytes");//DEBUG jjg@113: byte[] newnames = new byte[names.length * 2]; jjg@113: System.arraycopy(names, 0, newnames, 0, names.length); jjg@113: names = this.bytes = newnames; jjg@113: } jjg@113: System.arraycopy(cs, start, names, nc, len); jjg@113: n = new NameImpl(this); jjg@113: n.index = nc; jjg@113: n.length = len; jjg@113: n.next = hashes[h]; jjg@113: hashes[h] = n; jjg@113: this.nc = nc + len; jjg@113: if (len == 0) { jjg@113: this.nc++; jjg@113: } jjg@113: } jjg@113: return n; jjg@113: } jjg@113: jjg@113: @Override jjg@113: public void dispose() { jjg@113: dispose(this); jjg@113: } jjg@113: jjg@113: static class NameImpl extends Name { jjg@113: /** The next name occupying the same hash bucket. jjg@113: */ jjg@113: NameImpl next; jjg@113: jjg@113: /** The index where the bytes of this name are stored in the global name jjg@113: * buffer `byte'. jjg@113: */ jjg@113: int index; jjg@113: jjg@113: /** The number of bytes in this name. jjg@113: */ jjg@113: int length; jjg@113: jjg@113: NameImpl(SharedNameTable table) { jjg@113: super(table); jjg@113: } jjg@113: jjg@113: @Override jjg@113: public int getIndex() { jjg@113: return index; jjg@113: } jjg@113: jjg@113: @Override jjg@113: public int getByteLength() { jjg@113: return length; jjg@113: } jjg@113: jjg@113: @Override jjg@113: public byte getByteAt(int i) { jjg@113: return getByteArray()[index + i]; jjg@113: } jjg@113: jjg@113: @Override jjg@113: public byte[] getByteArray() { jjg@113: return ((SharedNameTable) table).bytes; jjg@113: } jjg@113: jjg@113: @Override jjg@113: public int getByteOffset() { jjg@113: return index; jjg@113: } jjg@113: jjg@113: /** Return the hash value of this name. jjg@113: */ jjg@113: public int hashCode() { jjg@113: return index; jjg@113: } jjg@113: jjg@113: /** Is this name equal to other? jjg@113: */ jjg@113: public boolean equals(Object other) { jjg@113: if (other instanceof Name) jjg@113: return jjg@113: table == ((Name)other).table && index == ((Name) other).getIndex(); jjg@113: else return false; jjg@113: } jjg@113: jjg@113: } jjg@113: jjg@113: }