optimisation: avoid recalculating hashcode if possible

This commit is contained in:
Alexey Kudravtsev
2015-07-07 13:53:44 +03:00
parent fbbdf07874
commit 005e3fbbae
8 changed files with 105 additions and 276 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package com.intellij.openapi.diff.impl.string;
import com.intellij.openapi.diff.LineTokenizerBase;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayCharSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -25,14 +26,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DiffString implements CharSequence {
public class DiffString extends CharArrayCharSequence {
@NotNull public static final DiffString EMPTY = new DiffString(new char[0], 0, 0);
@NotNull private final char[] myData;
private final int myStart;
private final int myLength;
private int myHash;
@Nullable
public static DiffString createNullable(@Nullable String string) {
if (string == null) return null;
@@ -58,63 +54,43 @@ public class DiffString implements CharSequence {
}
private DiffString(@NotNull char[] data, int start, int length) {
myData = data;
myStart = start;
myLength = length;
}
@Override
public int length() {
return myLength;
super(data, start, start + length);
}
public boolean isEmpty() {
return myLength == 0;
return length() == 0;
}
@Override
public char charAt(int index) {
if (index < 0 || index >= myLength) {
throw new StringIndexOutOfBoundsException(index);
}
return data(index);
}
public char data(int index) {
return myData[myStart + index];
private char data(int index) {
return charAt(index);
}
@NotNull
public DiffString substring(int start) {
return substring(start, myLength);
return substring(start, length());
}
@NotNull
public DiffString substring(int start, int end) {
if (start == 0 && end == myLength) return this;
checkBounds(start, end - start, myLength);
return create(myData, myStart + start, end - start);
if (start == 0 && end == length()) return this;
checkBounds(start, end - start, length());
return create(myChars, myStart + start, end - start);
}
@NotNull
@Override
public DiffString subSequence(int start, int end) {
return substring(start, end);
}
@NotNull
@Override
public String toString() {
return new String(myData, myStart, myLength);
}
@NotNull
public DiffString copy() {
return create(Arrays.copyOfRange(myData, myStart, myStart + myLength));
return create(Arrays.copyOfRange(myChars, myStart, myStart + length()));
}
public void copyData(@NotNull char[] dst, int start) {
checkBounds(start, myLength, dst.length);
System.arraycopy(myData, myStart, dst, start, myLength);
checkBounds(start, length(), dst.length);
System.arraycopy(myChars, myStart, dst, start, length());
}
@Override
@@ -124,26 +100,15 @@ public class DiffString implements CharSequence {
DiffString that = (DiffString)o;
if (myLength != that.myLength) return false;
if (length() != that.length()) return false;
if (hashCode() != that.hashCode()) return false;
for (int i = 0; i < myLength; i++) {
for (int i = 0; i < length(); i++) {
if (data(i) != that.data(i)) return false;
}
return true;
}
@Override
public int hashCode() {
int h = myHash;
if (h == 0) {
h = StringUtil.stringHashCode(myData, myStart, myStart + myLength);
if (h == 0) h = 1;
myHash = h;
}
return h;
}
@Nullable
public static DiffString concatenateNullable(@Nullable DiffString s1, @Nullable DiffString s2) {
if (s1 == null || s2 == null) {
@@ -160,13 +125,13 @@ public class DiffString implements CharSequence {
if (s1.isEmpty()) return s2;
if (s2.isEmpty()) return s1;
if (s1.myData == s2.myData && s1.myStart + s1.myLength == s2.myStart) {
return create(s1.myData, s1.myStart, s1.myLength + s2.myLength);
if (s1.myChars == s2.myChars && s1.myStart + s1.length() == s2.myStart) {
return create(s1.myChars, s1.myStart, s1.length() + s2.length());
}
char[] data = new char[s1.myLength + s2.myLength];
System.arraycopy(s1.myData, s1.myStart, data, 0, s1.myLength);
System.arraycopy(s2.myData, s2.myStart, data, s1.myLength, s2.myLength);
char[] data = new char[s1.length() + s2.length()];
System.arraycopy(s1.myChars, s1.myStart, data, 0, s1.length());
System.arraycopy(s2.myChars, s2.myStart, data, s1.length(), s2.length());
return create(data);
}
@@ -174,7 +139,7 @@ public class DiffString implements CharSequence {
if (s1.isEmpty()) return true;
if (s2.isEmpty()) return true;
if (s1.myData == s2.myData && s1.myStart + s1.myLength == s2.myStart) {
if (s1.myChars == s2.myChars && s1.myStart + s1.length() == s2.myStart) {
return true;
}
@@ -193,7 +158,7 @@ public class DiffString implements CharSequence {
int len = 0;
for (int i = 0; i < length; i++) {
DiffString string = strings[start + i];
len += string == null ? 0 : string.myLength;
len += string == null ? 0 : string.length();
}
if (len == 0) return EMPTY;
@@ -203,32 +168,32 @@ public class DiffString implements CharSequence {
for (int i = 0; i < length; i++) {
DiffString string = strings[start + i];
if (string == null || string.isEmpty()) continue;
System.arraycopy(string.myData, string.myStart, data, index, string.myLength);
index += string.myLength;
System.arraycopy(string.myChars, string.myStart, data, index, string.length());
index += string.length();
}
return create(data);
}
@NotNull
public static DiffString concatenate(@NotNull DiffString s, char c) {
if (s.myStart + s.myLength < s.myData.length && s.data(s.myLength) == c) {
return create(s.myData, s.myStart, s.myLength + 1);
if (s.myStart + s.length() < s.myChars.length && s.data(s.length()) == c) {
return create(s.myChars, s.myStart, s.length() + 1);
}
char[] data = new char[s.myLength + 1];
System.arraycopy(s.myData, s.myStart, data, 0, s.myLength);
data[s.myLength] = c;
char[] data = new char[s.length() + 1];
System.arraycopy(s.myChars, s.myStart, data, 0, s.length());
data[s.length()] = c;
return create(data);
}
@NotNull
public static DiffString concatenate(char c, @NotNull DiffString s) {
if (s.myStart > 0 && s.data(-1) == c) {
return create(s.myData, s.myStart - 1, s.myLength + 1);
return create(s.myChars, s.myStart - 1, s.length() + 1);
}
char[] data = new char[s.myLength + 1];
System.arraycopy(s.myData, s.myStart, data, 1, s.myLength);
char[] data = new char[s.length() + 1];
System.arraycopy(s.myChars, s.myStart, data, 1, s.length());
data[0] = c;
return create(data);
}
@@ -251,16 +216,16 @@ public class DiffString implements CharSequence {
DiffString string = strings[start + i];
if (string == null || string.isEmpty()) continue;
if (data == null) {
data = string.myData;
data = string.myChars;
startIndex = string.myStart;
endIndex = string.myStart + string.myLength;
endIndex = string.myStart + string.length();
continue;
}
if (data != string.myData || string.myStart != endIndex) {
if (data != string.myChars || string.myStart != endIndex) {
linearized = false;
break;
}
endIndex += string.myLength;
endIndex += string.length();
}
if (linearized) {
@@ -288,7 +253,7 @@ public class DiffString implements CharSequence {
public boolean isEmptyOrSpaces() {
if (isEmpty()) return true;
for (int i = 0; i < myLength; i++) {
for (int i = 0; i < length(); i++) {
if (!isWhiteSpace(data(i))) return false;
}
return true;
@@ -297,7 +262,7 @@ public class DiffString implements CharSequence {
@NotNull
public DiffString trim() {
int start = 0;
int end = myLength;
int end = length();
while (start < end && isWhiteSpace(data(start))) start++;
while (end > start && isWhiteSpace(data(end - 1))) end--;
@@ -309,14 +274,14 @@ public class DiffString implements CharSequence {
public DiffString trimLeading() {
int i = 0;
while (i < myLength && isWhiteSpace(data(i))) i++;
while (i < length() && isWhiteSpace(data(i))) i++;
return substring(i, myLength);
return substring(i, length());
}
@NotNull
public DiffString trimTrailing() {
int end = myLength;
int end = length();
while (end > 0 && isWhiteSpace(data(end - 1))) end--;
@@ -327,7 +292,7 @@ public class DiffString implements CharSequence {
public DiffString getLeadingSpaces() {
int i = 0;
while (i < myLength && data(i) == ' ') i++;
while (i < length() && data(i) == ' ') i++;
return substring(0, i);
}
@@ -336,14 +301,14 @@ public class DiffString implements CharSequence {
public DiffString skipSpaces() {
DiffString s = trim();
int count = 0;
for (int i = 0; i < s.myLength; i++) {
for (int i = 0; i < s.length(); i++) {
if (isWhiteSpace(s.data(i))) count++;
}
if (count == 0) return s;
char[] data = new char[s.myLength - count];
char[] data = new char[s.length() - count];
int index = 0;
for (int i = 0; i < s.myLength; i++) {
for (int i = 0; i < s.length(); i++) {
if (isWhiteSpace(s.data(i))) continue;
data[index] = s.data(i);
index++;
@@ -357,7 +322,7 @@ public class DiffString implements CharSequence {
public boolean endsWith(char c) {
if (isEmpty()) return false;
return data(myLength - 1) == c;
return data(length() - 1) == c;
}
public static void checkBounds(int start, int length, int maxLength) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,4 +20,6 @@ package com.intellij.openapi.util.text;
* @see com.intellij.openapi.util.text.StringUtil#stringHashCode(CharSequence)
*/
public interface CharSequenceWithStringHash extends CharSequence{
@Override
int hashCode();
}
@@ -21,7 +21,6 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayCharSequence;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.CharSequenceSubSequence;
import com.intellij.util.text.StringFactory;
@@ -859,9 +858,10 @@ public class StringUtil extends StringUtilRt {
@Contract(pure = true)
public static int stringHashCode(@NotNull CharSequence chars) {
if (chars instanceof String) return chars.hashCode();
if (chars instanceof CharSequenceWithStringHash) return chars.hashCode();
if (chars instanceof CharArrayCharSequence) return chars.hashCode();
if (chars instanceof String || chars instanceof CharSequenceWithStringHash) {
// we know for sure these classes have conformant (and maybe faster) hashCode()
return chars.hashCode();
}
return stringHashCode(chars, 0, chars.length());
}
@@ -966,6 +966,15 @@ public class StringUtil extends StringUtilRt {
return s;
}
@NotNull
@Contract(pure = true)
public static String trimEnd(@NotNull String s, char suffix) {
if (endsWithChar(s, suffix)) {
return s.substring(0, s.length() - 1);
}
return s;
}
@NotNull
@Contract(pure = true)
public static String trimLog(@NotNull final String text, final int limit) {
@@ -16,6 +16,7 @@
package com.intellij.util.text;
import com.intellij.openapi.util.text.CharSequenceWithStringHash;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
public class ByteArrayCharSequence implements CharSequenceWithStringHash {
@@ -30,12 +31,7 @@ public class ByteArrayCharSequence implements CharSequenceWithStringHash {
public int hashCode() {
int h = hash;
if (h == 0) {
byte[] chars = myChars;
for (byte aChar : chars) {
h = 31 * h + aChar;
}
hash = h;
hash = h = StringUtil.stringHashCode(this, 0, length());
}
return h;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,10 +15,11 @@
*/
package com.intellij.util.text;
import com.intellij.openapi.util.text.CharSequenceWithStringHash;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
public class CharArrayCharSequence implements CharSequenceBackedByArray {
public class CharArrayCharSequence implements CharSequenceBackedByArray, CharSequenceWithStringHash {
protected final char[] myChars;
protected final int myStart;
protected final int myEnd;
@@ -29,9 +30,7 @@ public class CharArrayCharSequence implements CharSequenceBackedByArray {
public CharArrayCharSequence(@NotNull char[] chars, int start, int end) {
if (start < 0 || end > chars.length || start > end) {
throw new IndexOutOfBoundsException("chars.length:" + chars.length +
", start:" + start +
", end:" + end);
throw new IndexOutOfBoundsException("chars.length:" + chars.length + ", start:" + start + ", end:" + end);
}
myChars = chars;
myStart = start;
@@ -48,6 +47,7 @@ public class CharArrayCharSequence implements CharSequenceBackedByArray {
return myChars[index + myStart];
}
@NotNull
@Override
public CharSequence subSequence(int start, int end) {
return start == 0 && end == length() ? this : new CharArrayCharSequence(myChars, myStart + start, myStart + end);
@@ -62,17 +62,12 @@ public class CharArrayCharSequence implements CharSequenceBackedByArray {
@Override
@NotNull
public char[] getChars() {
if (myStart == 0 /*&& myEnd == myChars.length*/) return myChars;
if (myStart == 0) return myChars;
char[] chars = new char[length()];
System.arraycopy(myChars, myStart, chars, 0, length());
return chars;
}
@Override
public int hashCode() {
return StringUtil.stringHashCode(myChars, myStart, myEnd);
}
@Override
public void getChars(@NotNull char[] dst, int dstOffset) {
System.arraycopy(myChars, myStart, dst, dstOffset, length());
@@ -108,4 +103,14 @@ public class CharArrayCharSequence implements CharSequenceBackedByArray {
System.arraycopy(myChars, myStart + start, cbuf, off, readChars);
return readChars;
}
private int hash;
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
hash = h = StringUtil.stringHashCode(this, 0, length());
}
return h;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,9 +16,11 @@
package com.intellij.util.text;
import com.intellij.openapi.util.text.CharSequenceWithStringHash;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
public class CharSequenceSubSequence implements CharSequence, CharArrayExternalizable {
public class CharSequenceSubSequence implements CharSequence, CharArrayExternalizable, CharSequenceWithStringHash {
private final CharSequence myChars;
private final int myStart;
private final int myEnd;
@@ -48,6 +50,7 @@ public class CharSequenceSubSequence implements CharSequence, CharArrayExternali
return myChars.charAt(index + myStart);
}
@NotNull
@Override
public CharSequence subSequence(int start, int end) {
if (start == myStart && end == myEnd) return this;
@@ -60,6 +63,7 @@ public class CharSequenceSubSequence implements CharSequence, CharArrayExternali
return StringFactory.createShared(CharArrayUtil.fromSequence(myChars, myStart, myEnd));
}
@NotNull
public CharSequence getBaseSequence() {
return myChars;
}
@@ -69,4 +73,14 @@ public class CharSequenceSubSequence implements CharSequence, CharArrayExternali
assert end - start <= myEnd - myStart;
CharArrayUtil.getChars(myChars, dest, start + myStart, destPos, end - start);
}
private int hash;
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
hash = h = StringUtil.stringHashCode(this, 0, length());
}
return h;
}
}
@@ -27,6 +27,8 @@
*/
package com.intellij.util.text;
import com.intellij.openapi.util.text.CharSequenceWithStringHash;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -53,7 +55,7 @@ import org.jetbrains.annotations.Nullable;
* @version 5.3, January 10, 2007
*/
@SuppressWarnings({"AssignmentToForLoopParameter","UnnecessaryThis"})
public final class ImmutableText extends ImmutableCharSequence implements CharArrayExternalizable {
public final class ImmutableText extends ImmutableCharSequence implements CharArrayExternalizable, CharSequenceWithStringHash {
/**
* Holds the default size for primitive blocks of characters.
*/
@@ -248,6 +250,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
return true;
}
private int hash;
/**
* Returns the hash code for this text.
*
@@ -255,10 +258,9 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
*/
@Override
public int hashCode() {
int h = 0;
final int length = this.length();
for (int i = 0; i < length;) {
h = 31 * h + charAt(i++);
int h = hash;
if (h == 0) {
hash = h = StringUtil.stringHashCode(this, 0, length());
}
return h;
}
@@ -1,164 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed 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.jetbrains.plugins.groovy.refactoring.convertToJava;
import gnu.trove.TIntArrayList;
import java.util.ArrayList;
import java.util.List;
/**
* String builder for only append and remove from the end without insert.
*
* @author Medvedev Max
*/
public class MyStringBuilder implements CharSequence {
private char[] arr = new char[16];
private int innerLen = 0;
private int realLength = 0;
List<CharSequence> builders = new ArrayList<CharSequence>(4);
TIntArrayList positions = new TIntArrayList(4);
@Override
public int length() {
return realLength;
}
@Override
public char charAt(int index) {
throw new UnsupportedOperationException();
}
@Override
public CharSequence subSequence(int start, int end) {
throw new UnsupportedOperationException();
}
public MyStringBuilder append(char c) {
if (innerLen == arr.length) {
final char[] chars = new char[innerLen * 2];
System.arraycopy(arr, 0, chars, 0, arr.length);
arr = chars;
}
arr[innerLen] = c;
innerLen++;
realLength++;
return this;
}
public MyStringBuilder append(CharSequence s) {
builders.add(s);
positions.add(innerLen);
realLength += s.length();
return this;
}
public MyStringBuilder removeFromTheEnd(int count) {
if (positions.isEmpty()) {
innerLen -= count;
realLength -= count;
return this;
}
final int lastIndex = positions.size() - 1;
final int lastPos = positions.getQuick(lastIndex);
if (lastPos == innerLen) {
final CharSequence last = builders.get(lastIndex);
final int lastLength = last.length();
if (lastLength <= count) {
positions.remove(lastIndex);
builders.remove(lastIndex);
realLength -= lastLength;
count -= lastLength;
if (count > 0) removeFromTheEnd(count);
}
else {
if (last instanceof MyStringBuilder) {
((MyStringBuilder)last).removeFromTheEnd(count);
realLength -= count;
}
else {
positions.remove(lastIndex);
builders.remove(lastIndex);
realLength -= lastLength;
int toAppend = lastLength - count;
for (int i = 0; i < toAppend; i++) {
append(last.charAt(i));
}
}
}
}
else {
if (lastPos+count<=innerLen) {
innerLen-=count;
realLength-=count;
}
else {
final int removed = innerLen - lastPos;
innerLen -= removed;
realLength -= removed;
count -= removed;
if (count > 0) removeFromTheEnd(count);
}
}
return this;
}
MyStringBuilder append(Object value) {
return append(String.valueOf(value));
}
@Override
public String toString() {
if (positions.isEmpty()) {
return new String(arr);
}
char[] chars = new char[realLength];
innerToString(chars, 0);
return new String(chars);
}
private int innerToString(char[] chars, int posInChars) {
int posInInner = 0;
final int size = positions.size();
for (int i = 0; i < size; i++) {
final int pos = positions.get(i);
final int length = pos - posInInner;
System.arraycopy(arr, posInInner, chars, posInChars, length);
posInChars += length;
posInInner += length;
CharSequence seq = builders.get(i);
if (seq instanceof MyStringBuilder) {
posInChars = ((MyStringBuilder)seq).innerToString(chars, posInChars);
}
else {
final int seqLength = seq.length();
for (int j = 0; j < seqLength; j++) {
chars[posInChars] = seq.charAt(j);
posInChars++;
}
}
}
final int length = innerLen - posInInner;
System.arraycopy(arr, posInInner, chars, posInChars, length);
return posInChars + length;
}
}