1 /*
2  * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.tools.javac.util;
27 
28 import java.lang.ref.SoftReference;
29 
30 import com.sun.tools.javac.util.DefinedBy.Api;
31 
32 /**
33  * Implementation of Name.Table that stores all names in a single shared
34  * byte array, expanding it as needed. This avoids the overhead incurred
35  * by using an array of bytes for each name.
36  *
37  *  <p><b>This is NOT part of any supported API.
38  *  If you write code that depends on this, you do so at your own risk.
39  *  This code and its internal interfaces are subject to change or
40  *  deletion without notice.</b>
41  */
42 public class SharedNameTable extends Name.Table {
43     // maintain a freelist of recently used name tables for reuse.
44     private static List<SoftReference<SharedNameTable>> freelist = List.nil();
45 
create(Names names)46     static public synchronized SharedNameTable create(Names names) {
47         while (freelist.nonEmpty()) {
48             SharedNameTable t = freelist.head.get();
49             freelist = freelist.tail;
50             if (t != null) {
51                 return t;
52             }
53         }
54         return new SharedNameTable(names);
55     }
56 
dispose(SharedNameTable t)57     static private synchronized void dispose(SharedNameTable t) {
58         freelist = freelist.prepend(new SoftReference<>(t));
59     }
60 
61     /** The hash table for names.
62      */
63     private NameImpl[] hashes;
64 
65     /** The shared byte array holding all encountered names.
66      */
67     public byte[] bytes;
68 
69     /** The mask to be used for hashing
70      */
71     private int hashMask;
72 
73     /** The number of filled bytes in `names'.
74      */
75     private int nc = 0;
76 
77     /** Allocator
78      *  @param names The main name table
79      *  @param hashSize the (constant) size to be used for the hash table
80      *                  needs to be a power of two.
81      *  @param nameSize the initial size of the name table.
82      */
SharedNameTable(Names names, int hashSize, int nameSize)83     public SharedNameTable(Names names, int hashSize, int nameSize) {
84         super(names);
85         hashMask = hashSize - 1;
86         hashes = new NameImpl[hashSize];
87         bytes = new byte[nameSize];
88 
89     }
90 
SharedNameTable(Names names)91     public SharedNameTable(Names names) {
92         this(names, 0x8000, 0x20000);
93     }
94 
95     @Override
fromChars(char[] cs, int start, int len)96     public Name fromChars(char[] cs, int start, int len) {
97         int nc = this.nc;
98         byte[] bytes = this.bytes = ArrayUtils.ensureCapacity(this.bytes, nc + len * 3);
99         int nbytes = Convert.chars2utf(cs, start, bytes, nc, len) - nc;
100         int h = hashValue(bytes, nc, nbytes) & hashMask;
101         NameImpl n = hashes[h];
102         while (n != null &&
103                 (n.getByteLength() != nbytes ||
104                 !equals(bytes, n.index, bytes, nc, nbytes))) {
105             n = n.next;
106         }
107         if (n == null) {
108             n = new NameImpl(this);
109             n.index = nc;
110             n.length = nbytes;
111             n.next = hashes[h];
112             hashes[h] = n;
113             this.nc = nc + nbytes;
114             if (nbytes == 0) {
115                 this.nc++;
116             }
117         }
118         return n;
119     }
120 
121     @Override
fromUtf(byte[] cs, int start, int len)122     public Name fromUtf(byte[] cs, int start, int len) {
123         int h = hashValue(cs, start, len) & hashMask;
124         NameImpl n = hashes[h];
125         byte[] names = this.bytes;
126         while (n != null &&
127                 (n.getByteLength() != len || !equals(names, n.index, cs, start, len))) {
128             n = n.next;
129         }
130         if (n == null) {
131             int nc = this.nc;
132             names = this.bytes = ArrayUtils.ensureCapacity(names, nc + len);
133             System.arraycopy(cs, start, names, nc, len);
134             n = new NameImpl(this);
135             n.index = nc;
136             n.length = len;
137             n.next = hashes[h];
138             hashes[h] = n;
139             this.nc = nc + len;
140             if (len == 0) {
141                 this.nc++;
142             }
143         }
144         return n;
145     }
146 
147     @Override
dispose()148     public void dispose() {
149         dispose(this);
150     }
151 
152     static class NameImpl extends Name {
153         /** The next name occupying the same hash bucket.
154          */
155         NameImpl next;
156 
157         /** The index where the bytes of this name are stored in the global name
158          *  buffer `byte'.
159          */
160         int index;
161 
162         /** The number of bytes in this name.
163          */
164         int length;
165 
NameImpl(SharedNameTable table)166         NameImpl(SharedNameTable table) {
167             super(table);
168         }
169 
170         @Override
getIndex()171         public int getIndex() {
172             return index;
173         }
174 
175         @Override
getByteLength()176         public int getByteLength() {
177             return length;
178         }
179 
180         @Override
getByteAt(int i)181         public byte getByteAt(int i) {
182             return getByteArray()[index + i];
183         }
184 
185         @Override
getByteArray()186         public byte[] getByteArray() {
187             return ((SharedNameTable) table).bytes;
188         }
189 
190         @Override
getByteOffset()191         public int getByteOffset() {
192             return index;
193         }
194 
195         /** Return the hash value of this name.
196          */
197         @DefinedBy(Api.LANGUAGE_MODEL)
hashCode()198         public int hashCode() {
199             return index;
200         }
201 
202         /** Is this name equal to other?
203          */
204         @DefinedBy(Api.LANGUAGE_MODEL)
equals(Object other)205         public boolean equals(Object other) {
206             if (other instanceof Name)
207                 return
208                     table == ((Name)other).table && index == ((Name) other).getIndex();
209             else return false;
210         }
211 
212     }
213 
214 }
215