blob: 50dc2fa909979ebd19ba97f8e7bc5e14211b5de0 [file] [log] [blame]
/*******************************************************************************
* 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<T> {
private SoftReference<T> 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<CacheReference<CharDeduplication>> 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;
}
}