/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/ package org.apache.tomcat.util.buf;
publicstatic String toString(ByteChunk bc) { try { return toString(bc, CodingErrorAction.REPLACE, CodingErrorAction.REPLACE);
} catch (CharacterCodingException e) { // Unreachable code. Use of REPLACE above means the exception will never be thrown. thrownew IllegalStateException(e);
}
}
// If the cache is null, then either caching is disabled, or we're // still training if (bcCache == null) {
String value = bc.toStringInternal(malformedInputAction, unmappableCharacterAction); if (byteEnabled && (value.length() < maxStringSize)) { // If training, everything is synced synchronized (bcStats) { // If the cache has been generated on a previous invocation // while waiting for the lock, just return the toString // value we just calculated if (bcCache != null) { return value;
} // Two cases: either we just exceeded the train count, in // which case the cache must be created, or we just update // the count for the string if (bcCount > trainThreshold) { long t1 = System.currentTimeMillis(); // Sort the entries according to occurrence
TreeMap<Integer,ArrayList<ByteEntry>> tempMap = new TreeMap<>(); for (Entry<ByteEntry,int[]> item : bcStats.entrySet()) {
ByteEntry entry = item.getKey(); int[] countA = item.getValue();
Integer count = Integer.valueOf(countA[0]); // Add to the list for that count
tempMap.computeIfAbsent(count, k -> new ArrayList<>()).add(entry);
} // Allocate array of the right size int size = bcStats.size(); if (size > cacheSize) {
size = cacheSize;
}
ByteEntry[] tempbcCache = new ByteEntry[size]; // Fill it up using an alphabetical order // and a dumb insert sort
ByteChunk tempChunk = new ByteChunk(); int n = 0; while (n < size) {
Object key = tempMap.lastKey();
ArrayList<ByteEntry> list = tempMap.get(key); for (int i = 0; i < list.size() && n < size; i++) {
ByteEntry entry = list.get(i);
tempChunk.setBytes(entry.name, 0, entry.name.length); int insertPos = findClosest(tempChunk, tempbcCache, n); if (insertPos == n) {
tempbcCache[n + 1] = entry;
} else {
System.arraycopy(tempbcCache, insertPos + 1, tempbcCache, insertPos + 2,
n - insertPos - 1);
tempbcCache[insertPos + 1] = entry;
}
n++;
}
tempMap.remove(key);
}
bcCount = 0;
bcStats.clear();
bcCache = tempbcCache; if (log.isDebugEnabled()) { long t2 = System.currentTimeMillis();
log.debug("ByteCache generation time: " + (t2 - t1) + "ms");
}
} else {
bcCount++; // Allocate new ByteEntry for the lookup
ByteEntry entry = new ByteEntry();
entry.value = value; int[] count = bcStats.get(entry); if (count == null) { int end = bc.getEnd(); int start = bc.getStart(); // Create byte array and copy bytes
entry.name = newbyte[bc.getLength()];
System.arraycopy(bc.getBuffer(), start, entry.name, 0, end - start); // Set encoding
entry.charset = bc.getCharset();
entry.malformedInputAction = malformedInputAction;
entry.unmappableCharacterAction = unmappableCharacterAction; // Initialize occurrence count to one
count = newint[1];
count[0] = 1; // Set in the stats hash map
bcStats.put(entry, count);
} else {
count[0] = count[0] + 1;
}
}
}
} return value;
} else {
accessCount++; // Find the corresponding String
String result = find(bc, malformedInputAction, unmappableCharacterAction); if (result == null) { return bc.toStringInternal(malformedInputAction, unmappableCharacterAction);
} // Note: We don't care about safety for the stats
hitCount++; return result;
}
}
publicstatic String toString(CharChunk cc) {
// If the cache is null, then either caching is disabled, or we're // still training if (ccCache == null) {
String value = cc.toStringInternal(); if (charEnabled && (value.length() < maxStringSize)) { // If training, everything is synced synchronized (ccStats) { // If the cache has been generated on a previous invocation // while waiting for the lock, just return the toString // value we just calculated if (ccCache != null) { return value;
} // Two cases: either we just exceeded the train count, in // which case the cache must be created, or we just update // the count for the string if (ccCount > trainThreshold) { long t1 = System.currentTimeMillis(); // Sort the entries according to occurrence
TreeMap<Integer,ArrayList<CharEntry>> tempMap = new TreeMap<>(); for (Entry<CharEntry,int[]> item : ccStats.entrySet()) {
CharEntry entry = item.getKey(); int[] countA = item.getValue();
Integer count = Integer.valueOf(countA[0]); // Add to the list for that count
ArrayList<CharEntry> list = tempMap.computeIfAbsent(count, k -> new ArrayList<>());
list.add(entry);
} // Allocate array of the right size int size = ccStats.size(); if (size > cacheSize) {
size = cacheSize;
}
CharEntry[] tempccCache = new CharEntry[size]; // Fill it up using an alphabetical order // and a dumb insert sort
CharChunk tempChunk = new CharChunk(); int n = 0; while (n < size) {
Object key = tempMap.lastKey();
ArrayList<CharEntry> list = tempMap.get(key); for (int i = 0; i < list.size() && n < size; i++) {
CharEntry entry = list.get(i);
tempChunk.setChars(entry.name, 0, entry.name.length); int insertPos = findClosest(tempChunk, tempccCache, n); if (insertPos == n) {
tempccCache[n + 1] = entry;
} else {
System.arraycopy(tempccCache, insertPos + 1, tempccCache, insertPos + 2,
n - insertPos - 1);
tempccCache[insertPos + 1] = entry;
}
n++;
}
tempMap.remove(key);
}
ccCount = 0;
ccStats.clear();
ccCache = tempccCache; if (log.isDebugEnabled()) { long t2 = System.currentTimeMillis();
log.debug("CharCache generation time: " + (t2 - t1) + "ms");
}
} else {
ccCount++; // Allocate new CharEntry for the lookup
CharEntry entry = new CharEntry();
entry.value = value; int[] count = ccStats.get(entry); if (count == null) { int end = cc.getEnd(); int start = cc.getStart(); // Create char array and copy chars
entry.name = newchar[cc.getLength()];
System.arraycopy(cc.getBuffer(), start, entry.name, 0, end - start); // Initialize occurrence count to one
count = newint[1];
count[0] = 1; // Set in the stats hash map
ccStats.put(entry, count);
} else {
count[0] = count[0] + 1;
}
}
}
} return value;
} else {
accessCount++; // Find the corresponding String
String result = find(cc); if (result == null) { return cc.toStringInternal();
} // Note: We don't care about safety for the stats
hitCount++; return result;
}
/** * Compare given byte chunk with byte array. * * @param name The name to compare * @param compareTo The compared to data * * @return -1, 0 or +1 if inferior, equal, or superior to the String.
*/ protectedstaticfinalint compare(ByteChunk name, byte[] compareTo) { int result = 0;
byte[] b = name.getBuffer(); int start = name.getStart(); int end = name.getEnd(); int len = compareTo.length;
if ((end - start) < len) {
len = end - start;
} for (int i = 0; (i < len) && (result == 0); i++) { if (b[i + start] > compareTo[i]) {
result = 1;
} elseif (b[i + start] < compareTo[i]) {
result = -1;
}
} if (result == 0) { if (compareTo.length > (end - start)) {
result = -1;
} elseif (compareTo.length < (end - start)) {
result = 1;
}
} return result;
}
/** * Find an entry given its name in the cache and return the associated String. * * @param name The name to find * * @return the corresponding value * * @deprecated Unused. Will be removed in Tomcat 11. * Use {@link #find(ByteChunk, CodingErrorAction, CodingErrorAction)}
*/
@Deprecated protectedstaticfinal String find(ByteChunk name) { return find(name, CodingErrorAction.REPLACE, CodingErrorAction.REPLACE);
}
/** * Find an entry given its name in the cache and return the associated String. * * @param name The name to find * @param malformedInputAction Action to take if an malformed input is encountered * @param unmappableCharacterAction Action to take if an unmappable character is encountered * * @return the corresponding value
*/ protectedstaticfinal String find(ByteChunk name, CodingErrorAction malformedInputAction,
CodingErrorAction unmappableCharacterAction) { int pos = findClosest(name, bcCache, bcCache.length); if ((pos < 0) || (compare(name, bcCache[pos].name) != 0) || !(name.getCharset().equals(bcCache[pos].charset)) ||
!malformedInputAction.equals(bcCache[pos].malformedInputAction) ||
!unmappableCharacterAction.equals(bcCache[pos].unmappableCharacterAction)) { returnnull;
} else { return bcCache[pos].value;
}
}
/** * Find an entry given its name in a sorted array of map elements. This will return the index for the closest * inferior or equal item in the given array. * * @param name The name to find * @param array The array in which to look * @param len The effective length of the array * * @return the position of the best match
*/ protectedstaticfinalint findClosest(ByteChunk name, ByteEntry[] array, int len) {
int a = 0; int b = len - 1;
// Special cases: -1 and 0 if (b == -1) { return -1;
}
if (compare(name, array[0].name) < 0) { return -1;
} if (b == 0) { return 0;
}
int i = 0; while (true) {
i = (b + a) >>> 1; int result = compare(name, array[i].name); if (result == 1) {
a = i;
} elseif (result == 0) { return i;
} else {
b = i;
} if ((b - a) == 1) { int result2 = compare(name, array[b].name); if (result2 < 0) { return a;
} else { return b;
}
}
}
}
/** * Compare given char chunk with char array. * * @param name The name to compare * @param compareTo The compared to data * * @return -1, 0 or +1 if inferior, equal, or superior to the String.
*/ protectedstaticfinalint compare(CharChunk name, char[] compareTo) { int result = 0;
char[] c = name.getBuffer(); int start = name.getStart(); int end = name.getEnd(); int len = compareTo.length;
if ((end - start) < len) {
len = end - start;
} for (int i = 0; (i < len) && (result == 0); i++) { if (c[i + start] > compareTo[i]) {
result = 1;
} elseif (c[i + start] < compareTo[i]) {
result = -1;
}
} if (result == 0) { if (compareTo.length > (end - start)) {
result = -1;
} elseif (compareTo.length < (end - start)) {
result = 1;
}
} return result;
}
/** * Find an entry given its name in the cache and return the associated String. * * @param name The name to find * * @return the corresponding value
*/ protectedstaticfinal String find(CharChunk name) { int pos = findClosest(name, ccCache, ccCache.length); if ((pos < 0) || (compare(name, ccCache[pos].name) != 0)) { returnnull;
} else { return ccCache[pos].value;
}
}
/** * Find an entry given its name in a sorted array of map elements. This will return the index for the closest * inferior or equal item in the given array. * * @param name The name to find * @param array The array in which to look * @param len The effective length of the array * * @return the position of the best match
*/ protectedstaticfinalint findClosest(CharChunk name, CharEntry[] array, int len) {
int a = 0; int b = len - 1;
// Special cases: -1 and 0 if (b == -1) { return -1;
}
if (compare(name, array[0].name) < 0) { return -1;
} if (b == 0) { return 0;
}
int i = 0; while (true) {
i = (b + a) >>> 1; int result = compare(name, array[i].name); if (result == 1) {
a = i;
} elseif (result == 0) { return i;
} else {
b = i;
} if ((b - a) == 1) { int result2 = compare(name, array[b].name); if (result2 < 0) { return a;
} else { return b;
}
}
}
}
// -------------------------------------------------- ByteEntry Inner Class
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.