org.eclipse.jdt.internal.compiler.util.CharDeduplication Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Joerg Kubitz - threadlocal refactoring, all ASCII chars
* - (copied content from PublicScanner.java / Scanner.java)
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.util;
import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.function.Supplier;
public class CharDeduplication {
// ----- immutable static part (thread safe): ----
static final char[] ASCII_CHARS[] = new char[128][];
static {
for (int i = 0; i < ASCII_CHARS.length; i++) {
ASCII_CHARS[i] = new char[] { (char) i };
}
}
public static final int TABLE_SIZE = 30; // XXX thats not a prime -> bad for hashing, nor a power of 2 -> expensive
// modulo computation
public static final int INTERNAL_TABLE_SIZE = 6; // 30*6 =180 entries
public static final int OPTIMIZED_LENGTH = 6;
private final static char[] CHAR_ARRAY0 = new char[0];
/** avoid OOME by additional CharDeduplication memory **/
static final class CacheReference {
private SoftReference reference;
private final Supplier extends T> supplier;
CacheReference(Supplier extends T> supplier) {
this.supplier = supplier;
this.reference = new SoftReference<>(supplier.get());
}
T get() {
T referent = this.reference.get();
if (referent == null) {
referent = this.supplier.get();
this.reference = new SoftReference<>(referent);
}
return referent;
}
}
private final static ThreadLocal> mutableCache = ThreadLocal.withInitial(()->new CacheReference<>(CharDeduplication::new));
private static final char[] optimizedCurrentTokenSource1(char[] source, int startPosition) {
// optimization at no speed cost of 99.5 % of the singleCharIdentifier
char charOne = source[startPosition];
if (charOne < ASCII_CHARS.length) {
return ASCII_CHARS[charOne];
}
return new char[] { charOne };
}
/** @return an instance that is *not* thread safe. To be used in a single thread only. **/
public static CharDeduplication getThreadLocalInstance() {
return mutableCache.get().get();
}
// ----- mutable non-static part (not thread safe!): ----
/** single threaded only **/
public final char[][][][] charArray_length = new char[OPTIMIZED_LENGTH - 1][TABLE_SIZE][INTERNAL_TABLE_SIZE][];
int newEntry2 = 0;
int newEntry3 = 0;
int newEntry4 = 0;
int newEntry5 = 0;
int newEntry6 = 0;
private CharDeduplication() {
init();
}
private void init() {
for (int i = 0; i < OPTIMIZED_LENGTH - 1; i++) {
final char[] initCharArray = new char[i + 2];
for (int j = 0; j < TABLE_SIZE; j++) {
for (int k = 0; k < INTERNAL_TABLE_SIZE; k++) {
this.charArray_length[i][j][k] = initCharArray;
}
}
}
}
/** public for test purpose only **/
@Deprecated
public void reset() {
init();
}
/**
* like Arrays.copyOfRange(source, from, to) but returns a cached instance of the former result if
* available
*
* @param from
* start index (inclusive)
* @param to
* end index (exclusive)
* @return source[from..to-1]
* @see java.util.Arrays#copyOfRange(char[], int, int)
**/
public char[] sharedCopyOfRange(char[] source, int from, int to) {
int length = to - from;
switch (length) { // see OptimizedLength
case 1:
return optimizedCurrentTokenSource1(source, from);
case 2:
return optimizedCurrentTokenSource2(source, from);
case 3:
return optimizedCurrentTokenSource3(source, from);
case 4:
return optimizedCurrentTokenSource4(source, from);
case 5:
return optimizedCurrentTokenSource5(source, from);
case 6:
return optimizedCurrentTokenSource6(source, from);
case 0:
return CHAR_ARRAY0;
}
return Arrays.copyOfRange(source, from, to);
}
private final char[] optimizedCurrentTokenSource2(char[] source, int startPosition) {
char[] src = source;
int start = startPosition;
char c0, c1;
int hash = (((c0 = src[start]) << 6) + (c1 = src[start + 1])) % TABLE_SIZE;
char[][] table = this.charArray_length[0][hash];
int i = this.newEntry2;
while (++i < INTERNAL_TABLE_SIZE) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]))
return charArray;
}
// ---------other side---------
i = -1;
int max = this.newEntry2;
while (++i <= max) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]))
return charArray;
}
// --------add the entry-------
if (++max >= INTERNAL_TABLE_SIZE)
max = 0;
char[] r;
System.arraycopy(src, start, r = new char[2], 0, 2);
return table[this.newEntry2 = max] = r;
}
private final char[] optimizedCurrentTokenSource3(char[] source, int startPosition) {
char[] src = source;
int start = startPosition;
char c0, c1 = src[start + 1], c2;
int hash = (((c0 = src[start]) << 6) + (c2 = src[start + 2])) % TABLE_SIZE;
char[][] table = this.charArray_length[1][hash];
int i = this.newEntry3;
while (++i < INTERNAL_TABLE_SIZE) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]))
return charArray;
}
// ---------other side---------
i = -1;
int max = this.newEntry3;
while (++i <= max) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]))
return charArray;
}
// --------add the entry-------
if (++max >= INTERNAL_TABLE_SIZE)
max = 0;
char[] r;
System.arraycopy(src, start, r = new char[3], 0, 3);
return table[this.newEntry3 = max] = r;
}
private final char[] optimizedCurrentTokenSource4(char[] source, int startPosition) {
char[] src = source;
int start = startPosition;
char c0, c1 = src[start + 1], c2, c3 = src[start + 3];
int hash = (((c0 = src[start]) << 6) + (c2 = src[start + 2])) % TABLE_SIZE;
char[][] table = this.charArray_length[2][hash];
int i = this.newEntry4;
while (++i < INTERNAL_TABLE_SIZE) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3]))
return charArray;
}
// ---------other side---------
i = -1;
int max = this.newEntry4;
while (++i <= max) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3]))
return charArray;
}
// --------add the entry-------
if (++max >= INTERNAL_TABLE_SIZE)
max = 0;
char[] r;
System.arraycopy(src, start, r = new char[4], 0, 4);
return table[this.newEntry4 = max] = r;
}
private final char[] optimizedCurrentTokenSource5(char[] source, int startPosition) {
char[] src = source;
int start = startPosition;
char c0, c1 = src[start + 1], c2, c3 = src[start + 3], c4;
int hash = (((c0 = src[start]) << 12) + ((c2 = src[start + 2]) << 6) + (c4 = src[start + 4])) % TABLE_SIZE;
char[][] table = this.charArray_length[3][hash];
int i = this.newEntry5;
while (++i < INTERNAL_TABLE_SIZE) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3])
&& (c4 == charArray[4]))
return charArray;
}
// ---------other side---------
i = -1;
int max = this.newEntry5;
while (++i <= max) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3])
&& (c4 == charArray[4]))
return charArray;
}
// --------add the entry-------
if (++max >= INTERNAL_TABLE_SIZE)
max = 0;
char[] r;
System.arraycopy(src, start, r = new char[5], 0, 5);
return table[this.newEntry5 = max] = r;
}
private final char[] optimizedCurrentTokenSource6(char[] source, int startPosition) {
char[] src = source;
int start = startPosition;
char c0, c1 = src[start + 1], c2, c3 = src[start + 3], c4, c5 = src[start + 5];
int hash = (((c0 = src[start]) << 12) + ((c2 = src[start + 2]) << 6) + (c4 = src[start + 4])) % TABLE_SIZE;
char[][] table = this.charArray_length[4][hash];
int i = this.newEntry6;
while (++i < INTERNAL_TABLE_SIZE) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3])
&& (c4 == charArray[4]) && (c5 == charArray[5]))
return charArray;
}
// ---------other side---------
i = -1;
int max = this.newEntry6;
while (++i <= max) {
char[] charArray = table[i];
if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3])
&& (c4 == charArray[4]) && (c5 == charArray[5]))
return charArray;
}
// --------add the entry-------
if (++max >= INTERNAL_TABLE_SIZE)
max = 0;
char[] r;
System.arraycopy(src, start, r = new char[6], 0, 6);
return table[this.newEntry6 = max] = r;
}
}