| /******************************************************************************* |
| * 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; |
| |
| /** |
| * This is for strings that take up more than on chunk. |
| * The string will need to be broken up into sections and then |
| * reassembled when necessary. |
| * |
| * @author Doug Schaefer |
| */ |
| public class LongString implements IString { |
| private final Database db; |
| private final long record; |
| private int hash; |
| |
| // Additional fields of first record. |
| private static final int LENGTH = 0; // Must be first to match ShortString. |
| private static final int NEXT1 = 4; |
| private static final int CHARS1 = 8; |
| |
| private static final int NUM_CHARS1 = (Database.MAX_MALLOC_SIZE - CHARS1) / 2; |
| |
| // Additional fields of subsequent records. |
| private static final int NEXTN = 0; |
| private static final int CHARSN = 4; |
| |
| private static final int NUM_CHARSN = (Database.MAX_MALLOC_SIZE - CHARSN) / 2; |
| |
| public LongString(Database db, long record) { |
| this.db = db; |
| this.record = record; |
| } |
| |
| public LongString(Database db, final char[] chars, boolean useBytes) throws IndexException { |
| final int numChars1 = useBytes ? NUM_CHARS1 * 2 : NUM_CHARS1; |
| final int numCharsn = useBytes ? NUM_CHARSN * 2 : NUM_CHARSN; |
| |
| this.db = db; |
| this.record = db.malloc(Database.MAX_MALLOC_SIZE, Database.POOL_STRING_LONG); |
| |
| // Write the first record. |
| final int length = chars.length; |
| db.putInt(this.record, useBytes ? -length : length); |
| Chunk chunk= db.getChunk(this.record); |
| |
| if (useBytes) { |
| chunk.putCharsAsBytes(this.record + CHARS1, chars, 0, numChars1); |
| } else { |
| chunk.putChars(this.record + CHARS1, chars, 0, numChars1); |
| } |
| |
| // Write the subsequent records. |
| long lastNext = this.record + NEXT1; |
| int start = numChars1; |
| while (length - start > numCharsn) { |
| long nextRecord = db.malloc(Database.MAX_MALLOC_SIZE, Database.POOL_STRING_LONG); |
| db.putRecPtr(lastNext, nextRecord); |
| chunk= db.getChunk(nextRecord); |
| if (useBytes) { |
| chunk.putCharsAsBytes(nextRecord + CHARSN, chars, start, numCharsn); |
| } else { |
| chunk.putChars(nextRecord + CHARSN, chars, start, numCharsn); |
| } |
| start += numCharsn; |
| lastNext = nextRecord + NEXTN; |
| } |
| |
| // Write the last record. |
| int remaining= length - start; |
| long nextRecord = db.malloc(CHARSN + (useBytes ? remaining : remaining * 2), Database.POOL_STRING_LONG); |
| db.putRecPtr(lastNext, nextRecord); |
| chunk= db.getChunk(nextRecord); |
| if (useBytes) { |
| chunk.putCharsAsBytes(nextRecord + CHARSN, chars, start, remaining); |
| } else { |
| chunk.putChars(nextRecord + CHARSN, chars, start, remaining); |
| } |
| } |
| |
| @Override |
| public long getRecord() { |
| return this.record; |
| } |
| |
| @Override |
| public char[] getChars() throws IndexException { |
| int length = this.db.getInt(this.record + LENGTH); |
| final boolean useBytes = length < 0; |
| int numChars1 = NUM_CHARS1; |
| int numCharsn = NUM_CHARSN; |
| if (useBytes) { |
| length= -length; |
| numChars1 *= 2; |
| numCharsn *= 2; |
| } |
| |
| final char[] chars = new char[length]; |
| |
| // First record |
| long p = this.record; |
| Chunk chunk= this.db.getChunk(p); |
| if (useBytes) { |
| chunk.getCharsFromBytes(p + CHARS1, chars, 0, numChars1); |
| } else { |
| chunk.getChars(p + CHARS1, chars, 0, numChars1); |
| } |
| |
| int start= numChars1; |
| p= this.record + NEXT1; |
| |
| // Other records |
| while (start < length) { |
| p = this.db.getRecPtr(p); |
| int partLen= Math.min(length - start, numCharsn); |
| chunk= this.db.getChunk(p); |
| if (useBytes) { |
| chunk.getCharsFromBytes(p + CHARSN, chars, start, partLen); |
| } else { |
| chunk.getChars(p + CHARSN, chars, start, partLen); |
| } |
| start += partLen; |
| p= p + NEXTN; |
| } |
| return chars; |
| } |
| |
| @Override |
| public void delete() throws IndexException { |
| int length = this.db.getInt(this.record + LENGTH); |
| final boolean useBytes = length < 0; |
| int numChars1 = NUM_CHARS1; |
| int numCharsn = NUM_CHARSN; |
| if (useBytes) { |
| length= -length; |
| numChars1 *= 2; |
| numCharsn *= 2; |
| } |
| long nextRecord = this.db.getRecPtr(this.record + NEXT1); |
| this.db.free(this.record, Database.POOL_STRING_LONG); |
| length -= numChars1; |
| |
| // Middle records. |
| while (length > numCharsn) { |
| length -= numCharsn; |
| long nextnext = this.db.getRecPtr(nextRecord + NEXTN); |
| this.db.free(nextRecord, Database.POOL_STRING_LONG); |
| nextRecord = nextnext; |
| } |
| |
| // Last record. |
| this.db.free(nextRecord, Database.POOL_STRING_LONG); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj == this) |
| return true; |
| try { |
| if (obj instanceof LongString) { |
| LongString lstr = (LongString)obj; |
| if (this.db == lstr.db && this.record == lstr.record) |
| return true; |
| return compare(lstr, true) == 0; |
| } |
| if (obj instanceof char[]) { |
| return compare((char[]) obj, true) == 0; |
| } |
| if (obj instanceof String) { |
| return compare((String) obj, true) == 0; |
| } |
| } 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; |
| } |
| |
| @Override |
| public int compare(IString string, boolean caseSensitive) throws IndexException { |
| return ShortString.compare(getChars(), string.getChars(), caseSensitive); |
| } |
| |
| @Override |
| public int compare(String other, boolean caseSensitive) throws IndexException { |
| return ShortString.compare(getChars(), other.toCharArray(), caseSensitive); |
| } |
| |
| @Override |
| public int compare(char[] other, boolean caseSensitive) throws IndexException { |
| return ShortString.compare(getChars(), other, caseSensitive); |
| } |
| |
| @Override |
| public int compareCompatibleWithIgnoreCase(IString string) throws IndexException { |
| return ShortString.compareCompatibleWithIgnoreCase(getChars(), string.getChars()); |
| } |
| |
| @Override |
| public int comparePrefix(char[] other, boolean caseSensitive) throws IndexException { |
| return ShortString.comparePrefix(getChars(), other, caseSensitive); |
| } |
| |
| @Override |
| public String getString() throws IndexException { |
| return new String(getChars()); |
| } |
| |
| @Override |
| public int compareCompatibleWithIgnoreCase(char[] other) throws IndexException { |
| return ShortString.compareCompatibleWithIgnoreCase(getChars(), other); |
| } |
| |
| @Override |
| public int length() { |
| return this.db.getInt(this.record + LENGTH); |
| } |
| } |