8269957: facilitate alternate impls of NameTable and Name

Reviewed-by: jjg, vromero
This commit is contained in:
Archie Cobbs 2023-08-28 14:48:45 +00:00 committed by Vicente Romero
parent 725ec0ce1b
commit 11da15d142
11 changed files with 678 additions and 269 deletions

@ -5192,7 +5192,7 @@ public class Types {
append(rawOuter ? '$' : '.');
Assert.check(c.flatname.startsWith(c.owner.enclClass().flatname));
append(rawOuter
? c.flatname.subName(c.owner.enclClass().flatname.getByteLength() + 1, c.flatname.getByteLength())
? c.flatname.subName(c.owner.enclClass().flatname.length() + 1)
: c.name);
} else {
append(externalize(c.flatname));

@ -73,7 +73,7 @@ public class Gen extends JCTree.Visitor {
private final TreeMaker make;
private final Names names;
private final Target target;
private final Name accessDollar;
private final String accessDollar;
private final Types types;
private final Lower lower;
private final Annotate annotate;
@ -112,8 +112,7 @@ public class Gen extends JCTree.Visitor {
concat = StringConcat.instance(context);
methodType = new MethodType(null, null, null, syms.methodClass);
accessDollar = names.
fromString("access" + target.syntheticNameChar());
accessDollar = "access" + target.syntheticNameChar();
lower = Lower.instance(context);
Options options = Options.instance(context);
@ -341,9 +340,10 @@ public class Gen extends JCTree.Visitor {
/** Does given name start with "access$" and end in an odd digit?
*/
private boolean isOddAccessName(Name name) {
final String string = name.toString();
return
name.startsWith(accessDollar) &&
(name.getByteAt(name.getByteLength() - 1) & 1) == 1;
string.startsWith(accessDollar) &&
(string.charAt(string.length() - 1) & 1) != 0;
}
/* ************************************************************************

@ -27,7 +27,6 @@ package com.sun.tools.javac.jvm;
import com.sun.tools.javac.util.ByteBuffer;
import com.sun.tools.javac.util.ByteBuffer.UnderflowException;
import com.sun.tools.javac.util.Convert;
import com.sun.tools.javac.util.Name.NameMapper;
import java.io.IOException;
import java.io.InputStream;

@ -146,10 +146,13 @@ public class ByteBuffer {
}
}
/** Append a name.
/** Append a name encoded in Modified UTF-8.
*/
public void appendName(Name name) {
appendBytes(name.getByteArray(), name.getByteOffset(), name.getByteLength());
int utf8len = name.getUtf8Length();
elems = ArrayUtils.ensureCapacity(elems, length + utf8len);
name.getUtf8Bytes(elems, length);
length += utf8len;
}
/** Append the content of the given input stream.

@ -214,6 +214,24 @@ public class Convert {
return new String(dst, 0, len1);
}
/** Count the number of characters encoded in a Modified UTF-8 encoding.
* This method does not check for invalid data.
* @param buf data buffer
* @param off starting offset of UTF-8 data
* @param len number of bytes of UTF-8 data
* @return the number of encoded characters
*/
public static int utfNumChars(byte[] buf, int off, int len) {
int numChars = 0;
while (len-- > 0) {
int byte1 = buf[off++];
if (byte1 < 0)
len -= ((byte1 & 0xe0) == 0xc0) ? 1 : 2;
numChars++;
}
return numChars;
}
/** Copy characters in source array to bytes in target array,
* converting them to Utf8 representation.
* The target array must be large enough to hold the result.
@ -346,17 +364,14 @@ public class Convert {
/* Conversion routines for qualified name splitting
*/
/** Return the last part of a qualified name.
* @param name the qualified name
* @return the last part of the qualified name
*/
public static Name shortName(Name name) {
int start = name.lastIndexOf((byte)'.') + 1;
int end = name.getByteLength();
if (start == 0 && end == name.length()) {
return name;
}
return name.subName(start, end);
int start = name.lastIndexOfAscii('.') + 1;
return start > 0 ? name.subName(start) : name;
}
/** Return the last part of a qualified name from its string representation
@ -371,7 +386,8 @@ public class Convert {
* "" if not existent.
*/
public static Name packagePart(Name classname) {
return classname.subName(0, classname.lastIndexOf((byte)'.'));
int end = Math.max(classname.lastIndexOfAscii('.'), 0);
return classname.subName(0, end);
}
public static String packagePart(String classname) {
@ -382,7 +398,7 @@ public class Convert {
public static List<Name> enclosingCandidates(Name name) {
List<Name> names = List.nil();
int index;
while ((index = name.lastIndexOf((byte)'$')) > 0) {
while ((index = name.lastIndexOfAscii('$')) > 0) {
name = name.subName(0, index);
names = names.prepend(name);
}

@ -27,11 +27,13 @@ package com.sun.tools.javac.util;
import com.sun.tools.javac.jvm.ClassFile;
import com.sun.tools.javac.jvm.PoolConstant;
import com.sun.tools.javac.jvm.PoolReader;
import com.sun.tools.javac.util.DefinedBy.Api;
/** An abstraction for internal compiler strings. They are stored in
* Utf8 format. Names are stored in a Name.Table, and are unique within
* that table.
/** An abstraction for internal compiler strings.
*
* <p>
* Names are stored in a {@link Name.Table}, and are unique within that table.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
@ -40,76 +42,102 @@ import com.sun.tools.javac.util.DefinedBy.Api;
*/
public abstract class Name implements javax.lang.model.element.Name, PoolConstant, Comparable<Name> {
/**
* The {@link Table} that contains this instance.
*/
public final Table table;
/**
* Constructor.
*
* @param table the {@link Table} that will contain this instance
*/
protected Name(Table table) {
this.table = table;
}
/**
* {@inheritDoc}
*/
@DefinedBy(Api.LANGUAGE_MODEL)
public boolean contentEquals(CharSequence cs) {
return toString().equals(cs.toString());
}
// PoolConstant
@Override
public int poolTag() {
public final int poolTag() {
return ClassFile.CONSTANT_Utf8;
}
/**
* {@inheritDoc}
*/
// CharSequence
@Override
public int length() {
return toString().length();
}
/**
* {@inheritDoc}
* {@inheritDoc}
*
* <p>
* The implementation in {@link Name} invokes {@link Object#toString}
* on this instance and then delegates to {@link String#charAt}.
*/
@Override
public char charAt(int index) {
return toString().charAt(index);
}
/**
* {@inheritDoc}
* {@inheritDoc}
*
* <p>
* The implementation in {@link Name} invokes {@link Object#toString}
* on this instance and then delegates to {@link String#subSequence}.
*/
@Override
public CharSequence subSequence(int start, int end) {
return toString().subSequence(start, end);
}
/** Return the concatenation of this name and name `n'.
@Override
public abstract String toString();
// javax.lang.model.element.Name
/**
* {@inheritDoc}
*
* <p>
* The implementation in {@link Name} invokes {@link Object#toString}
* on this instance and then delegates to {@link String#equals}.
*/
public Name append(Name n) {
int len = getByteLength();
byte[] bs = new byte[len + n.getByteLength()];
getBytes(bs, 0);
n.getBytes(bs, len);
try {
return table.fromUtf(bs, 0, bs.length, Convert.Validation.NONE);
} catch (InvalidUtfException e) {
throw new AssertionError(e);
}
@DefinedBy(Api.LANGUAGE_MODEL)
@Override
public boolean contentEquals(CharSequence cs) {
return toString().equals(cs.toString());
}
/** Return the concatenation of this name, the given ASCII
* character, and name `n'.
*/
public Name append(char c, Name n) {
int len = getByteLength();
byte[] bs = new byte[len + 1 + n.getByteLength()];
getBytes(bs, 0);
bs[len] = (byte) c;
n.getBytes(bs, len+1);
try {
return table.fromUtf(bs, 0, bs.length, Convert.Validation.NONE);
} catch (InvalidUtfException e) {
throw new AssertionError(e);
}
@DefinedBy(Api.LANGUAGE_MODEL)
@Override
public final boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || obj.getClass() != getClass())
return false;
final Name that = (Name)obj;
return table == that.table && nameEquals(that);
}
@DefinedBy(Api.LANGUAGE_MODEL)
@Override
public abstract int hashCode();
/**
* Subclass check for equality.
*
* <p>
* This method can assume that the given instance is the same type
* as this instance and is associated to the same {@link Table}.
*/
protected abstract boolean nameEquals(Name that);
// Comparable
/** Order names lexicographically.
*
* <p>
@ -119,168 +147,161 @@ public abstract class Name implements javax.lang.model.element.Name, PoolConstan
*/
@Override
public int compareTo(Name name) {
byte[] buf1 = getByteArray();
byte[] buf2 = name.getByteArray();
int off1 = getByteOffset();
int off2 = name.getByteOffset();
int len1 = getByteLength();
int len2 = name.getByteLength();
while (len1 > 0 && len2 > 0) {
int val1 = buf1[off1++] & 0xff;
int val2 = buf2[off2++] & 0xff;
if (val1 == 0xc0 && (buf1[off1] & 0x3f) == 0) {
val1 = 0; // char 0x0000 encoded in two bytes
off1++;
len1--;
}
if (val2 == 0xc0 && (buf2[off2] & 0x3f) == 0) {
val2 = 0; // char 0x0000 encoded in two bytes
off2++;
len2--;
}
int diff = val1 - val2;
if (diff != 0)
return diff;
len1--;
len2--;
}
return len1 > 0 ? 1 : len2 > 0 ? -1 : 0;
return toString().compareTo(name.toString());
}
/** Return true if this is the empty name.
// Other methods
/** Return the concatenation of this name and the given name.
* The given name must come from the same table as this one.
*/
public Name append(Name name) {
return table.fromString(toString() + name.toString());
}
/** Return the concatenation of this name, the given ASCII
* character, and the given name.
* The given name must come from the same table as this one.
*/
public Name append(char c, Name name) {
return table.fromString(toString() + c + name.toString());
}
/** Determine if this is the empty name.
* <p>
* The implementation in {@link Name} compares {@link #length()} to zero.
*/
public boolean isEmpty() {
return getByteLength() == 0;
return length() == 0;
}
/** Returns last occurrence of byte b in this name, -1 if not found.
/** Returns last occurrence of the given ASCII character in this name, -1 if not found.
* <p>
* The implementation in {@link Name} converts this instance to {@link String}
* and then delegates to {@link String#lastIndexOf(int)}.
*/
public int lastIndexOf(byte b) {
byte[] bytes = getByteArray();
int offset = getByteOffset();
int i = getByteLength() - 1;
while (i >= 0 && bytes[offset + i] != b) i--;
return i;
public int lastIndexOfAscii(char ch) {
return toString().lastIndexOf(ch);
}
/** Does this name start with prefix?
/** Determine whether this name has the given Name as a prefix.
* <p>
* The implementation in {@link Name} converts this and the given instance
* to {@link String} and then delegates to {@link String#startsWith}.
*/
public boolean startsWith(Name prefix) {
byte[] thisBytes = this.getByteArray();
int thisOffset = this.getByteOffset();
int thisLength = this.getByteLength();
byte[] prefixBytes = prefix.getByteArray();
int prefixOffset = prefix.getByteOffset();
int prefixLength = prefix.getByteLength();
if (thisLength < prefixLength)
return false;
int i = 0;
while (i < prefixLength &&
thisBytes[thisOffset + i] == prefixBytes[prefixOffset + i])
i++;
return i == prefixLength;
return toString().startsWith(prefix.toString());
}
/** Returns the sub-name starting at position start, up to and
* excluding position end.
/** Returns the sub-name extending between two character positions.
* <p>
* The implementation in {@link Name} converts this instance to {@link String},
* delegates to {@link String#substring(int, int)} and then {@link Table#fromString}.
* @param start starting character offset, inclusive
* @param end ending character offset, exclusive
* @throws IndexOutOfBoundsException if bounds are out of range or invalid
*/
public Name subName(int start, int end) {
if (end < start) end = start;
try {
return table.fromUtf(getByteArray(), getByteOffset() + start, end - start, Convert.Validation.NONE);
} catch (InvalidUtfException e) {
throw new AssertionError(e);
}
return table.fromString(toString().substring(start, end));
}
/** Return the string representation of this name.
/** Returns the suffix of this name starting at the given offset.
* <p>
* The implementation in {@link Name} converts this instance to {@link String},
* delegates to {@link String#substring(int)}, and then to {@link Table#fromString}.
* @param off starting character offset
* @throws IndexOutOfBoundsException if {@code off} is out of range or invalid
*/
@Override
public String toString() {
try {
return Convert.utf2string(getByteArray(), getByteOffset(), getByteLength(), Convert.Validation.NONE);
} catch (InvalidUtfException e) {
throw new AssertionError(e);
}
public Name subName(int off) {
return table.fromString(toString().substring(off));
}
/** Return the Utf8 representation of this name.
/** Return the Modified UTF-8 encoding of this name.
* <p>
* The implementation in {@link Name} populates a new byte array of length
* {@link #getUtf8Length} with data from {@link #getUtf8Bytes}.
*/
public byte[] toUtf() {
byte[] bs = new byte[getByteLength()];
getBytes(bs, 0);
byte[] bs = new byte[getUtf8Length()];
getUtf8Bytes(bs, 0);
return bs;
}
/* Get a "reasonably small" value that uniquely identifies this name
* within its name table.
/** Get the length of the Modified UTF-8 encoding of this name.
*/
public abstract int getIndex();
public abstract int getUtf8Length();
/** Get the length (in bytes) of this name.
/** Write the Modified UTF-8 encoding of this name into the given
* buffer starting at the specified offset.
*/
public abstract int getByteLength();
public abstract void getUtf8Bytes(byte buf[], int off);
/** Returns i'th byte of this name.
// Mapping
/** Maps the Modified UTF-8 encoding of a {@link Name} to something.
*/
public abstract byte getByteAt(int i);
/** Copy all bytes of this name to buffer cs, starting at start.
*/
public void getBytes(byte cs[], int start) {
System.arraycopy(getByteArray(), getByteOffset(), cs, start, getByteLength());
}
/** Get the underlying byte array for this name. The contents of the
* array must not be modified.
*/
public abstract byte[] getByteArray();
/** Get the start offset of this name within its byte array.
*/
public abstract int getByteOffset();
@FunctionalInterface
public interface NameMapper<X> {
X map(byte[] bytes, int offset, int len);
}
/** Decode this name's Modified UTF-8 encoding into something.
*/
public <X> X map(NameMapper<X> mapper) {
return mapper.map(getByteArray(), getByteOffset(), getByteLength());
byte[] buf = toUtf();
return mapper.map(buf, 0, buf.length);
}
/** An abstraction for the hash table used to create unique Name instances.
// Table
/** An abstraction for the hash table used to create unique {@link Name} instances.
*/
public abstract static class Table {
/** Standard name table.
*/
public final Names names;
Table(Names names) {
protected Table(Names names) {
this.names = names;
}
/** Get the name from the characters in cs[start..start+len-1].
/** Get the unique {@link Name} corresponding to the given characters.
* @param buf character buffer
* @param off starting offset
* @param len number of characters
* @return the corresponding {@link Name}
*/
public abstract Name fromChars(char[] cs, int start, int len);
public abstract Name fromChars(char[] cs, int off, int len);
/** Get the name for the characters in string s.
/** Get the unique {@link Name} corresponding to the given string.
* <p>
* The implementation in {@link Table} delegates to {@link String#toCharArray}
* and then {@link #fromChars}.
* @param s character string
*/
public Name fromString(String s) {
char[] cs = s.toCharArray();
return fromChars(cs, 0, cs.length);
}
/** Get the name for the bytes in array cs.
* Assume that bytes are in strictly valid "Modified UTF-8" format.
/** Get the unique {@link Name} corresponding to the given Modified UTF-8 encoding.
* <p>
* The implementation in {@link Table} delegates to {@link #fromUtf(byte[], int, int)}.
* @param buf character string
* @return the corresponding {@link Name}
* @throws IllegalArgumentException if the data is not valid Modified UTF-8
*/
public Name fromUtf(byte[] cs) throws InvalidUtfException {
return fromUtf(cs, 0, cs.length, Convert.Validation.STRICT);
}
/** get the name for the bytes in cs[start..start+len-1].
* Assume that bytes are in utf8 format.
/** Get the unique {@link Name} corresponding to the given Modified UTF-8 encoding.
* @param buf character buffer
* @param off starting offset
* @param len number of bytes
* @return the corresponding {@link Name}
* @throws IllegalArgumentException if the data is not valid Modified UTF-8
* @throws InvalidUtfException if invalid Modified UTF-8 is encountered
*/
public abstract Name fromUtf(byte[] cs, int start, int len, Convert.Validation validation)
@ -289,28 +310,5 @@ public abstract class Name implements javax.lang.model.element.Name, PoolConstan
/** Release any resources used by this table.
*/
public abstract void dispose();
/** The hashcode of a name.
*/
protected static int hashValue(byte bytes[], int offset, int length) {
int h = 0;
int off = offset;
for (int i = 0; i < length; i++) {
h = (h << 5) - h + bytes[off++];
}
return h;
}
/** Compare two subarrays
*/
protected static boolean equals(byte[] bytes1, int offset1,
byte[] bytes2, int offset2, int length) {
int i = 0;
while (i < length && bytes1[offset1 + i] == bytes2[offset2 + i]) {
i++;
}
return i == length;
}
}
}

@ -428,11 +428,25 @@ public class Names {
}
protected Name.Table createTable(Options options) {
boolean useStringTable = options.isSet("useStringTable");
if (useStringTable)
return newStringNameTable();
boolean useUnsharedTable = options.isSet("useUnsharedTable");
if (useUnsharedTable)
return UnsharedNameTable.create(this);
else
return SharedNameTable.create(this);
return newUnsharedNameTable();
return newSharedNameTable();
}
public StringNameTable newStringNameTable() {
return StringNameTable.create(this);
}
public SharedNameTable newSharedNameTable() {
return SharedNameTable.create(this);
}
public UnsharedNameTable newUnsharedNameTable() {
return UnsharedNameTable.create(this);
}
public void dispose() {

@ -39,7 +39,7 @@ import com.sun.tools.javac.util.DefinedBy.Api;
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SharedNameTable extends Name.Table {
public class SharedNameTable extends Utf8NameTable {
// maintain a freelist of recently used name tables for reuse.
private static List<SoftReference<SharedNameTable>> freelist = List.nil();
@ -79,19 +79,23 @@ public class SharedNameTable extends Name.Table {
* @param hashSize the (constant) size to be used for the hash table
* needs to be a power of two.
* @param nameSize the initial size of the name table.
* @throws IllegalArgumentException if {@code hashSize} is not a power of two
*/
public SharedNameTable(Names names, int hashSize, int nameSize) {
super(names);
if (Integer.bitCount(hashSize) != 1)
throw new IllegalArgumentException(); // hashSize is not a power of two
hashMask = hashSize - 1;
hashes = new NameImpl[hashSize];
bytes = new byte[nameSize];
}
public SharedNameTable(Names names) {
this(names, 0x8000, 0x20000);
}
// Name.Table
@Override
public Name fromChars(char[] cs, int start, int len) {
int nc = this.nc;
@ -104,17 +108,8 @@ public class SharedNameTable extends Name.Table {
!equals(bytes, n.index, bytes, nc, nbytes))) {
n = n.next;
}
if (n == null) {
n = new NameImpl(this);
n.index = nc;
n.length = nbytes;
n.next = hashes[h];
hashes[h] = n;
this.nc = nc + nbytes;
if (nbytes == 0) {
this.nc++;
}
}
if (n == null)
n = addName(nc, nbytes, h);
return n;
}
@ -130,86 +125,71 @@ public class SharedNameTable extends Name.Table {
n = n.next;
}
if (n == null) {
int nc = this.nc;
names = this.bytes = ArrayUtils.ensureCapacity(names, nc + len);
System.arraycopy(cs, start, names, nc, len);
n = new NameImpl(this);
n.index = nc;
n.length = len;
n.next = hashes[h];
hashes[h] = n;
this.nc = nc + len;
if (len == 0) {
this.nc++;
}
n = addName(nc, len, h);
}
return n;
}
private NameImpl addName(int index, int len, int hash) {
NameImpl name = new NameImpl(this, index, len, hashes[hash]);
hashes[hash] = name;
this.nc = index + Math.max(len, 1);
return name;
}
@Override
public void dispose() {
dispose(this);
}
static class NameImpl extends Name {
// NameImpl
static final class NameImpl extends Utf8NameTable.NameImpl {
/** The next name occupying the same hash bucket.
*/
NameImpl next;
final NameImpl next;
/** The index where the bytes of this name are stored in the global name
* buffer `byte'.
*/
int index;
final int index;
/** The number of bytes in this name.
*/
int length;
final int length;
NameImpl(SharedNameTable table) {
// Constructor
NameImpl(SharedNameTable table, int index, int length, NameImpl next) {
super(table);
this.index = index;
this.length = length;
this.next = next;
}
// Utf8NameTable.NameImpl
@Override
protected byte[] getByteData() {
return ((SharedNameTable)table).bytes;
}
@Override
public int getIndex() {
protected int getByteOffset() {
return index;
}
@Override
public int getByteLength() {
protected int getByteLength() {
return length;
}
@Override
public byte getByteAt(int i) {
return getByteArray()[index + i];
}
@Override
public byte[] getByteArray() {
return ((SharedNameTable) table).bytes;
}
@Override
public int getByteOffset() {
protected int getNameIndex() {
return index;
}
/** Return the hash value of this name.
*/
@DefinedBy(Api.LANGUAGE_MODEL)
public int hashCode() {
return index;
}
/** Is this name equal to other?
*/
@DefinedBy(Api.LANGUAGE_MODEL)
public boolean equals(Object other) {
return (other instanceof Name name)
&& table == name.table
&& index == name.getIndex();
}
}
}

@ -0,0 +1,134 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.util;
import java.util.HashMap;
/**
* Straightforward implementation of {@link Name.Table} using {@link String}s.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class StringNameTable extends Name.Table {
private final HashMap<String, Name> nameMap;
// Factory
public static StringNameTable create(Names names) {
return new StringNameTable(names);
}
// Constructors
public StringNameTable(Names names) {
this(names, 0x8000);
}
public StringNameTable(Names names, int initialCapacity) {
super(names);
this.nameMap = new HashMap<>(initialCapacity);
}
// Name.Table
@Override
public Name fromString(String string) {
return this.nameMap.computeIfAbsent(string, s -> new NameImpl(this, s));
}
@Override
public Name fromChars(char[] buf, int off, int len) {
return this.fromString(new String(buf, off, len));
}
@Override
public Name fromUtf(byte[] buf, int off, int len, Convert.Validation validation) throws InvalidUtfException {
return this.fromString(Convert.utf2string(buf, off, len, validation));
}
@Override
public void dispose() {
this.nameMap.clear();
}
// NameImpl
private static final class NameImpl extends Name {
private final String string;
// Constructor
NameImpl(StringNameTable table, String string) {
super(table);
this.string = string;
}
@Override
public String toString() {
return string;
}
@Override
public boolean contentEquals(CharSequence cs) {
return string.contentEquals(cs);
}
@Override
protected boolean nameEquals(Name that) {
return ((NameImpl)that).string.equals(string);
}
@Override
public int hashCode() {
return string.hashCode();
}
@Override
public int getUtf8Length() {
int slen = string.length();
int extra = 0;
for (int i = 0; i < slen; i++) {
int ch = string.charAt(i);
if (ch > 0x007f || ch == 0x0000) {
extra++;
if (ch > 0x07ff)
extra++;
}
}
return slen + extra;
}
@Override
public void getUtf8Bytes(byte buf[], int off) {
Convert.chars2utf(string.toCharArray(), 0, buf, off, string.length());
}
}
}

@ -37,8 +37,8 @@ import java.lang.ref.WeakReference;
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class UnsharedNameTable extends Name.Table {
public static Name.Table create(Names names) {
public class UnsharedNameTable extends Utf8NameTable {
public static UnsharedNameTable create(Names names) {
return new UnsharedNameTable(names);
}
@ -61,13 +61,18 @@ public class UnsharedNameTable extends Name.Table {
*/
public int index;
// Constructors
/** Allocator
* @param names The main name table
* @param hashSize the (constant) size to be used for the hash table
* needs to be a power of two.
* @throws IllegalArgumentException if {@code hashSize} is not a power of two
*/
public UnsharedNameTable(Names names, int hashSize) {
super(names);
if (Integer.bitCount(hashSize) != 1)
throw new IllegalArgumentException(); // hashSize is not a power of two
hashMask = hashSize - 1;
hashes = new HashEntry[hashSize];
}
@ -76,6 +81,7 @@ public class UnsharedNameTable extends Name.Table {
this(names, 0x8000);
}
// Name.Table
@Override
public Name fromChars(char[] cs, int start, int len) {
@ -145,41 +151,41 @@ public class UnsharedNameTable extends Name.Table {
hashes = null;
}
static class NameImpl extends Name {
// NameImpl
static final class NameImpl extends Utf8NameTable.NameImpl {
final byte[] bytes;
final int index;
// Constructor
NameImpl(UnsharedNameTable table, byte[] bytes, int index) {
super(table);
this.bytes = bytes;
this.index = index;
}
final byte[] bytes;
final int index;
// Utf8NameTable.NameImpl
@Override
public int getIndex() {
return index;
}
@Override
public int getByteLength() {
return bytes.length;
}
@Override
public byte getByteAt(int i) {
return bytes[i];
}
@Override
public byte[] getByteArray() {
protected byte[] getByteData() {
return bytes;
}
@Override
public int getByteOffset() {
protected int getByteOffset() {
return 0;
}
}
@Override
protected int getByteLength() {
return bytes.length;
}
@Override
protected int getNameIndex() {
return index;
}
}
}

@ -0,0 +1,259 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.util;
import java.lang.ref.SoftReference;
import com.sun.tools.javac.util.DefinedBy.Api;
/**
* Support superclass for {@link Name.Table} implementations that store
* names as Modified UTF-8 data in {@code byte[]} arrays.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public abstract class Utf8NameTable extends Name.Table {
/** Constructor.
* @param names The main name table
*/
protected Utf8NameTable(Names names) {
super(names);
}
// Utility methods
/** Generate a hash value for a subarray.
*/
protected static int hashValue(byte buf[], int off, int len) {
int hash = 0;
while (len-- > 0)
hash = (hash << 5) - hash + buf[off++];
return hash;
}
/** Compare two subarrays.
*/
protected static boolean equals(byte[] buf1, int off1, byte[] buf2, int off2, int len) {
while (len-- > 0) {
if (buf1[off1++] != buf2[off2++])
return false;
}
return true;
}
// NameImpl
protected abstract static class NameImpl extends Name {
// Constructor
NameImpl(Utf8NameTable table) {
super(table);
}
// Subclass hooks
/**
* Get the {@code byte[]} array in which the Modified UTF-8 data is stored.
*/
protected abstract byte[] getByteData();
/**
* Get the Modified UTF-8 data offset into the byte array.
*/
protected abstract int getByteOffset();
/**
* Get the Modified UTF-8 data length in the byte array.
*/
protected abstract int getByteLength();
/**
* Get a unique index corresponding to this instance.
*/
protected abstract int getNameIndex();
// CharSequence
@Override
public int length() {
return Convert.utfNumChars(getByteData(), getByteOffset(), getByteLength());
}
@Override
public String toString() {
try {
return Convert.utf2string(getByteData(), getByteOffset(), getByteLength(), Convert.Validation.NONE);
} catch (InvalidUtfException e) {
throw new AssertionError();
}
}
// javax.lang.model.element.Name
@Override
protected boolean nameEquals(Name that) {
return ((NameImpl)that).getNameIndex() == getNameIndex();
}
@Override
public int hashCode() {
return getNameIndex();
}
// Comparable
@Override
public int compareTo(Name name0) {
NameImpl name = (NameImpl)name0;
Assert.check(name.table == table);
byte[] buf1 = getByteData();
byte[] buf2 = name.getByteData();
int off1 = getByteOffset();
int off2 = name.getByteOffset();
int len1 = getByteLength();
int len2 = name.getByteLength();
while (len1 > 0 && len2 > 0) {
int val1 = buf1[off1++] & 0xff;
int val2 = buf2[off2++] & 0xff;
if (val1 == 0xc0 && (buf1[off1] & 0x3f) == 0) {
val1 = 0; // char 0x0000 encoded in two bytes
off1++;
len1--;
}
if (val2 == 0xc0 && (buf2[off2] & 0x3f) == 0) {
val2 = 0; // char 0x0000 encoded in two bytes
off2++;
len2--;
}
int diff = val1 - val2;
if (diff != 0)
return diff;
len1--;
len2--;
}
return len1 > 0 ? 1 : len2 > 0 ? -1 : 0;
}
// Name
@Override
public Name append(Name name0) {
NameImpl name = (NameImpl)name0;
Assert.check(name.table == table);
byte[] buf1 = getByteData();
byte[] buf2 = name.getByteData();
int off1 = getByteOffset();
int off2 = name.getByteOffset();
int len1 = getByteLength();
int len2 = name.getByteLength();
byte[] result = new byte[len1 + len2];
System.arraycopy(buf1, off1, result, 0, len1);
System.arraycopy(buf2, off2, result, len1, len2);
try {
return table.fromUtf(result, 0, result.length, Convert.Validation.NONE);
} catch (InvalidUtfException e) {
throw new AssertionError();
}
}
@Override
public Name append(char ch, Name name0) {
Assert.check((ch & ~0x7f) == 0);
NameImpl name = (NameImpl)name0;
Assert.check(name.table == table);
byte[] buf1 = getByteData();
byte[] buf2 = name.getByteData();
int off1 = getByteOffset();
int off2 = name.getByteOffset();
int len1 = getByteLength();
int len2 = name.getByteLength();
byte[] result = new byte[len1 + 1 + len2];
System.arraycopy(buf1, off1, result, 0, len1);
result[len1] = (byte)ch;
System.arraycopy(buf2, off2, result, len1 + 1, len2);
try {
return table.fromUtf(result, 0, result.length, Convert.Validation.NONE);
} catch (InvalidUtfException e) {
throw new AssertionError();
}
}
@Override
public int lastIndexOfAscii(char ch) {
Assert.check((ch & ~0x7f) == 0);
// Find the last *byte* index of 'ch'
byte b = (byte)ch;
byte[] buf = getByteData();
int off = getByteOffset();
int len = getByteLength();
int pos = len - 1;
while (pos >= 0 && buf[off + pos] != b)
pos--;
// Not found, or index is zero?
if (pos <= 0)
return pos;
// Convert the byte index into a char index
return Convert.utfNumChars(buf, off, pos);
}
@Override
public boolean startsWith(Name prefix0) {
NameImpl prefix = (NameImpl)prefix0;
Assert.check(prefix.table == table);
int thisLen = getByteLength();
int prefLen = prefix.getByteLength();
if (thisLen < prefLen)
return false;
byte[] thisData = getByteData();
byte[] prefData = prefix.getByteData();
int thisOff = getByteOffset() + prefLen;
int prefOff = prefix.getByteOffset() + prefLen;
while (prefLen-- > 0) {
if (thisData[--thisOff] != prefData[--prefOff])
return false;
}
return true;
}
@Override
public int getUtf8Length() {
return getByteLength();
}
@Override
public void getUtf8Bytes(byte buf[], int off) {
System.arraycopy(getByteData(), getByteOffset(), buf, off, getByteLength());
}
}
}