001    /*
002     * Copyright 1999-2008 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.javac.util;
027    
028    import java.lang.ref.SoftReference;
029    
030    /**
031     * Implementation of Name.Table that stores all names in a single shared
032     * byte array, expanding it as needed. This avoids the overhead incurred
033     * by using an array of bytes for each name.
034     *
035     *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
036     *  you write code that depends on this, you do so at your own risk.
037     *  This code and its internal interfaces are subject to change or
038     *  deletion without notice.</b>
039     */
040    public class SharedNameTable extends Name.Table {
041        // maintain a freelist of recently used name tables for reuse.
042        private static List<SoftReference<SharedNameTable>> freelist = List.nil();
043    
044        static public synchronized SharedNameTable create(Names names) {
045            while (freelist.nonEmpty()) {
046                SharedNameTable t = freelist.head.get();
047                freelist = freelist.tail;
048                if (t != null) {
049                    return t;
050                }
051            }
052            return new SharedNameTable(names);
053        }
054    
055        static private synchronized void dispose(SharedNameTable t) {
056            freelist = freelist.prepend(new SoftReference<SharedNameTable>(t));
057        }
058    
059        /** The hash table for names.
060         */
061        private NameImpl[] hashes;
062    
063        /** The shared byte array holding all encountered names.
064         */
065        public byte[] bytes;
066    
067        /** The mask to be used for hashing
068         */
069        private int hashMask;
070    
071        /** The number of filled bytes in `names'.
072         */
073        private int nc = 0;
074    
075        /** Allocator
076         *  @param names The main name table
077         *  @param hashSize the (constant) size to be used for the hash table
078         *                  needs to be a power of two.
079         *  @param nameSize the initial size of the name table.
080         */
081        public SharedNameTable(Names names, int hashSize, int nameSize) {
082            super(names);
083            hashMask = hashSize - 1;
084            hashes = new NameImpl[hashSize];
085            bytes = new byte[nameSize];
086    
087        }
088    
089        public SharedNameTable(Names names) {
090            this(names, 0x8000, 0x20000);
091        }
092    
093        @Override
094        public Name fromChars(char[] cs, int start, int len) {
095            int nc = this.nc;
096            byte[] bytes = this.bytes;
097            while (nc + len * 3 >= bytes.length) {
098                //          System.err.println("doubling name buffer of length " + names.length + " to fit " + len + " chars");//DEBUG
099                byte[] newnames = new byte[bytes.length * 2];
100                System.arraycopy(bytes, 0, newnames, 0, bytes.length);
101                bytes = this.bytes = newnames;
102            }
103            int nbytes = Convert.chars2utf(cs, start, bytes, nc, len) - nc;
104            int h = hashValue(bytes, nc, nbytes) & hashMask;
105            NameImpl n = hashes[h];
106            while (n != null &&
107                    (n.getByteLength() != nbytes ||
108                    !equals(bytes, n.index, bytes, nc, nbytes))) {
109                n = n.next;
110            }
111            if (n == null) {
112                n = new NameImpl(this);
113                n.index = nc;
114                n.length = nbytes;
115                n.next = hashes[h];
116                hashes[h] = n;
117                this.nc = nc + nbytes;
118                if (nbytes == 0) {
119                    this.nc++;
120                }
121            }
122            return n;
123        }
124    
125        @Override
126        public Name fromUtf(byte[] cs, int start, int len) {
127            int h = hashValue(cs, start, len) & hashMask;
128            NameImpl n = hashes[h];
129            byte[] names = this.bytes;
130            while (n != null &&
131                    (n.getByteLength() != len || !equals(names, n.index, cs, start, len))) {
132                n = n.next;
133            }
134            if (n == null) {
135                int nc = this.nc;
136                while (nc + len > names.length) {
137                    //              System.err.println("doubling name buffer of length + " + names.length + " to fit " + len + " bytes");//DEBUG
138                    byte[] newnames = new byte[names.length * 2];
139                    System.arraycopy(names, 0, newnames, 0, names.length);
140                    names = this.bytes = newnames;
141                }
142                System.arraycopy(cs, start, names, nc, len);
143                n = new NameImpl(this);
144                n.index = nc;
145                n.length = len;
146                n.next = hashes[h];
147                hashes[h] = n;
148                this.nc = nc + len;
149                if (len == 0) {
150                    this.nc++;
151                }
152            }
153            return n;
154        }
155    
156        @Override
157        public void dispose() {
158            dispose(this);
159        }
160    
161        static class NameImpl extends Name {
162            /** The next name occupying the same hash bucket.
163             */
164            NameImpl next;
165    
166            /** The index where the bytes of this name are stored in the global name
167             *  buffer `byte'.
168             */
169            int index;
170    
171            /** The number of bytes in this name.
172             */
173            int length;
174    
175            NameImpl(SharedNameTable table) {
176                super(table);
177            }
178    
179            @Override
180            public int getIndex() {
181                return index;
182            }
183    
184            @Override
185            public int getByteLength() {
186                return length;
187            }
188    
189            @Override
190            public byte getByteAt(int i) {
191                return getByteArray()[index + i];
192            }
193    
194            @Override
195            public byte[] getByteArray() {
196                return ((SharedNameTable) table).bytes;
197            }
198    
199            @Override
200            public int getByteOffset() {
201                return index;
202            }
203    
204            /** Return the hash value of this name.
205             */
206            public int hashCode() {
207                return index;
208            }
209    
210            /** Is this name equal to other?
211             */
212            public boolean equals(Object other) {
213                if (other instanceof Name)
214                    return
215                        table == ((Name)other).table && index == ((Name) other).getIndex();
216                else return false;
217            }
218    
219        }
220    
221    }