diff options
Diffstat (limited to 'org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ShortString.java')
-rw-r--r-- | org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ShortString.java | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ShortString.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ShortString.java new file mode 100644 index 000000000..09992a564 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ShortString.java @@ -0,0 +1,296 @@ +/******************************************************************************* + * Copyright (c) 2006, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + * Andrew Ferguson (Symbian) + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; + +/** + * This is for strings that fit inside a single chunk. + */ +public class ShortString implements IString { + private final Database db; + private final long record; + private int hash; + + private static final int LENGTH = 0; + private static final int CHARS = 4; + + public static final int MAX_BYTE_LENGTH = Database.MAX_MALLOC_SIZE - CHARS; + + public ShortString(Database db, long offset) { + this.db = db; + this.record = offset; + } + + public ShortString(Database db, char[] chars, boolean useBytes) throws IndexException { + final int n = chars.length; + this.db = db; + + this.record = db.malloc(CHARS + (useBytes ? n : 2 * n), Database.POOL_STRING_SHORT); + Chunk chunk = db.getChunk(this.record); + chunk.putInt(this.record + LENGTH, useBytes ? -n : n); + long p = this.record + CHARS; + if (useBytes) { + chunk.putCharsAsBytes(p, chars, 0, n); + } else { + chunk.putChars(p, chars, 0, n); + } + } + + @Override + public long getRecord() { + return this.record; + } + + @Override + public void delete() throws IndexException { + this.db.free(this.record, Database.POOL_STRING_SHORT); + } + + @Override + public char[] getChars() throws IndexException { + final Chunk chunk = this.db.getChunk(this.record); + final int l = chunk.getInt(this.record + LENGTH); + final int length = Math.abs(l); + final char[] chars = new char[length]; + if (l < 0) { + chunk.getCharsFromBytes(this.record + CHARS, chars, 0, length); + } else { + chunk.getChars(this.record + CHARS, chars, 0, length); + } + return chars; + } + + @Override + public String getString() throws IndexException { + return new String(getChars()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + + try { + if (obj instanceof ShortString) { + ShortString string = (ShortString)obj; + if (this.db == string.db && this.record == string.record) + return true; + + Chunk chunk1 = this.db.getChunk(this.record); + Chunk chunk2 = string.db.getChunk(string.record); + + int n1 = chunk1.getInt(this.record); + int n2 = chunk2.getInt(string.record); + if (n1 != n2) + return false; + + return CharArrayUtils.equals(getChars(), string.getChars()); + } + if (obj instanceof char[]) { + char[] chars = (char[])obj; + + // Make sure size is the same + if (length() != chars.length) + return false; + + return CharArrayUtils.equals(getChars(), chars); + } else if (obj instanceof String) { + String string = (String)obj; + if (length() != string.length()) + return false; + + return CharArrayUtils.equals(getChars(), string.toCharArray()); + } + } catch (IndexException e) { + Package.log(e); + } + return false; + } + + /** + * Compatible with {@link String#hashCode()} + */ + @Override + public int hashCode() { + int h = this.hash; + if (h == 0) { + char chars[]; + chars = getChars(); + final int len = chars.length; + for (int i = 0; i < len; i++) { + h = 31 * h + chars[i]; + } + this.hash = h; + } + return h; + } + + public static int compare(final char[] chars, char[] other, boolean caseSensitive) { + final int n = Math.min(chars.length, other.length); + for (int i = 0; i < n; i++) { + int cmp= compareChars(chars[i], other[i], caseSensitive); + if (cmp != 0) + return cmp; + } + return chars.length - other.length; + } + + @Override + public int compare(char[] other, boolean caseSensitive) throws IndexException { + return compare(getChars(), other, caseSensitive); + } + + @Override + public int compare(IString string, boolean caseSensitive) throws IndexException { + return compare(getChars(), string.getChars(), caseSensitive); + } + + @Override + public int compare(String other, boolean caseSensitive) throws IndexException { + return compare(getChars(), other.toCharArray(), caseSensitive); + } + + @Override + public int compareCompatibleWithIgnoreCase(IString string) throws IndexException { + return compareCompatibleWithIgnoreCase(string.getChars()); + } + + @Override + public int compareCompatibleWithIgnoreCase(char[] other) throws IndexException { + return compareCompatibleWithIgnoreCase(getChars(), other); + } + + public static int compareCompatibleWithIgnoreCase(final char[] chars, char[] other) { + final int n = Math.min(chars.length, other.length); + int sensitiveCmp= 0; + + for (int i = 0; i < n; i++) { + final char c1= chars[i]; + final char c2= other[i]; + if (c1 != c2) { + int cmp= compareChars(c1, c2, false); // insensitive + if (cmp != 0) + return cmp; + + if (sensitiveCmp == 0) { + if (c1 < c2) { + sensitiveCmp= -1; + } else { + sensitiveCmp= 1; + } + } + } + } + int cmp= chars.length - other.length; + if (cmp != 0) + return cmp; + + return sensitiveCmp; + } + + @Override + public int comparePrefix(char[] other, boolean caseSensitive) throws IndexException { + return comparePrefix(getChars(), other, caseSensitive); + } + + public static int comparePrefix(final char[] chars, char[] other, boolean caseSensitive) { + final int n = Math.min(chars.length, other.length); + + for (int i = 0; i < n; i++) { + int cmp= compareChars(chars[i], other[i], caseSensitive); + if (cmp != 0) + return cmp; + } + if (chars.length < other.length) + return -1; + + return 0; + } + + /** + * Compare characters case-sensitively, or case-insensitively. + * + * <b>Limitation</b> This only maps the range a-z,A-Z onto each other + * @param a a character + * @param b a character + * @param caseSensitive whether to compare case-sensitively + * @return + * <ul> + * <li>-1 if a < b + * <li>0 if a == b + * <li>1 if a > b + * </ul> + */ + public static int compareChars(char a, char b, boolean caseSensitive) { + if (caseSensitive) { + if (a < b) + return -1; + if (a > b) + return 1; + } else { + if (a != b) { + a= a >= 'a' && a <='z' ? (char) (a - 32) : a; + b= b >= 'a' && b <='z' ? (char) (b - 32) : b; + if (a < b) + return -1; + if (a > b) + return 1; + } + } + return 0; + } + +/* TODO - this is more correct than the above implementation, but we need to + * benchmark first. + * + * public static int compareChars(char a, char b, boolean caseSensitive) { + if (caseSensitive) { + if (a < b) + return -1; + if (a > b) + return 1; + } else { + if (a != b) { + a = Character.toUpperCase(a); + b = Character.toUpperCase(b); + if (a != b) { + a = Character.toLowerCase(a); + b = Character.toLowerCase(b); + if (a != b) { + if (a < b) + return -1; + if (a > b) + return 1; + } + } + } + } + return 0; + } +*/ + + @Override + public String toString() { + try { + return getString(); + } catch (IndexException e) { + return super.toString(); + } + } + + @Override + public int length() { + return Math.abs(this.db.getInt(this.record + LENGTH)); + } +} |