diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeExpensiveTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeExpensiveTests.java new file mode 100644 index 00000000000..afa27b7e1a9 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeExpensiveTests.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2006 Symbian 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: + * Symbian - Initial implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.pdom.tests; + +/** + * Tests which are too expensive to run as part of normal testing, but + * should be run after B-tree related development. + *

+ * The 'Full Checking' tests perform a full validation of the B-tree + * invariants after each B-tree operation, and so are especially + * expensive and cpu hungry. + */ +/* + * JUnit only invokes methods declared in the class itself (not super-classes) + */ +public class BTreeExpensiveTests extends BTreeTests { + + public void testBySortedSetMirror() throws Exception { + sortedMirrorTest(100); + } + + // @Override + public void testInsertion() throws Exception { + super.testInsertion(); + } + + /* + * N.B. Each of the following tests are quite expensive (i.e. > 10mins each on a 2Ghz machine) + */ + + public void testBySortedSetMirror1682762087() throws Exception { + System.out.println("1682762087 Full Checking"); + trial(1682762087, true); // exposed bugs in 2a,b + } + + public void testBySortedSetMirror322922974() throws Exception { + System.out.println("322922974 Full Checking"); + trial(322922974, true); // exposed bugs in 3b(ii) + } + + public void testBySortedSetMirror_588448152() throws Exception { + System.out.println("-588448152 Full Checking"); + trial(-588448152, true); // exposed root-delete-on-merge problems + } +} diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeTests.java new file mode 100644 index 00000000000..3f7406e413f --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeTests.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2006 Symbian 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: + * Symbian - Initial implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.pdom.tests; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.SortedSet; +import java.util.TreeSet; + +import junit.framework.TestCase; + +import org.eclipse.cdt.internal.core.pdom.db.BTree; +import org.eclipse.cdt.internal.core.pdom.db.Database; +import org.eclipse.cdt.internal.core.pdom.db.IBTreeComparator; +import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor; +import org.eclipse.core.runtime.CoreException; + +/** + * Test insertion/deletion of records of a mock record type in a B-tree + * + * @author aferguso + * + */ +public class BTreeTests extends TestCase { + protected File dbFile; + protected Database db; + protected BTree btree; + protected int rootRecord; + protected IBTreeComparator comparator; + + protected boolean debugMode = false; + + // setUp is not used since we need to parameterize this method, + // and invoke it multiple times per Junit test + protected void init(int degree) throws Exception { + dbFile = File.createTempFile("pdomtest", "db"); + db = new Database(dbFile.getAbsolutePath()); + rootRecord = Database.DATA_AREA; + btree = new BTree(db, rootRecord, degree); + comparator = new BTMockRecordComparator(); + } + + // tearDown is not used for the same reason as above + protected void finish() throws Exception { + db.close(); + dbFile.deleteOnExit(); + } + + + public void testBySortedSetMirrorLite() throws Exception { + sortedMirrorTest(8); + } + + /** + * Test random (but reproducible via known seed) sequences of insertions/deletions + * and use TreeSet as a reference implementation to check behaviour against. + * @throws Exception + */ + protected void sortedMirrorTest(int noTrials) throws Exception { + Random seeder = new Random(90210); + + for(int i=0; i= iParent; --i) { + for (int i = MAX_RECORDS - 2; i >= iParent; --i) { int r = getRecord(pChunk, parent, i); if (r != 0) { putRecord(pChunk, parent, i + 1, r); @@ -118,9 +145,9 @@ public class BTree { } putRecord(pChunk, parent, iParent, median); putChild(pChunk, parent, iParent + 1, newnode); - + putRecord(chunk, node, MEDIAN_RECORD, 0); - + // set the node to the correct one to follow if (comparator.compare(record, median) > 0) { node = newnode; @@ -131,7 +158,7 @@ public class BTree { // search to find the insert point int i; - for (i = 0; i < NUM_RECORDS; ++i) { + for (i = 0; i < MAX_RECORDS; ++i) { int record1 = getRecord(chunk, node, i); if (record1 == 0) { // past the end @@ -154,7 +181,7 @@ public class BTree { } else { // were at the leaf, add us in. // first copy everything after over one - for (int j = NUM_RECORDS - 2; j >= i; --j) { + for (int j = MAX_RECORDS - 2; j >= i; --j) { int r = getRecord(chunk, node, j); if (r != 0) putRecord(chunk, node, j + 1, r); @@ -171,18 +198,316 @@ public class BTree { // put the record in the first slot of the node putRecord(db.getChunk(root), root, 0, record); } - + private int allocateNode() throws CoreException { - return db.malloc((2 * NUM_RECORDS + 1) * Database.INT_SIZE); + return db.malloc((2 * MAX_RECORDS + 1) * Database.INT_SIZE); } - + /** - * Deletes the record from the b-tree. - * - * @param offset of the record + * Deletes the specified record from the B-tree. + *

+ * If the specified record is not present then this routine has no effect. + *

+ * Specifying a record r for which there is another record q existing in the B-tree + * where cmp.compare(r,q)==0 && r!=q will also have no effect + *

+ * N.B. The record is not deleted itself - its storage is not deallocated. + * The reference to the record in the btree is deleted. + * + * @param record the record to delete + * @param cmp the comparator for locating the record + * @throws CoreException */ - public void delete(int record) { - // TODO some day + public void delete(int record, IBTreeComparator cmp) throws CoreException { + try { + deleteImp(record, getRoot(), DELMODE_NORMAL, cmp); + } catch(BTreeKeyNotFoundException e) { + // contract of this method is to NO-OP upon this event + } + } + + private class BTreeKeyNotFoundException extends Exception { + private static final long serialVersionUID = 9065438266175091670L; + public BTreeKeyNotFoundException(String msg) { + super(msg); + } + } + + /** + * Used in implementation of delete routines + */ + private class BTNode { + final int node; + final int keyCount; + final Chunk chunk; + + BTNode(int node) throws CoreException { + this.node = node; + this.chunk = db.getChunk(node); + int i=0; + while(i + * There is no distinction between keys and records. + *

+ * This implements a single downward pass (with minor exceptions) deletion + *

+ * @param key the address of the record to delete + * @param nodeRecord a node that (directly or indirectly) contains the specified key/record + * @param mode one of DELMODE_NORMAL, DELMODE_DELETE_MINIMUM, DELMODE_DELETE_MAXIMUM + * where DELMODE_NORMAL: locates the specified key/record using the comparator provided + * DELMODE_DELETE_MINIMUM: locates and deletes the minimum element in the subtree rooted at nodeRecord + * DELMODE_DELETE_MAXIMUM: locates and deletes the maximum element in the subtree rooted at nodeRecord + * @param cmp the comparator used to locate the record in the tree + * @return the address of the record removed from the B-tree + * @throws CoreException + */ + private int deleteImp(int key, int nodeRecord, int mode, IBTreeComparator cmp) + throws CoreException, BTreeKeyNotFoundException { + BTNode node = new BTNode(nodeRecord); + + // Determine index of key in current node, or -1 if its not in this node + int keyIndexInNode = -1; + if(mode==DELMODE_NORMAL) + for(int i=0; i MIN_RECORDS) { + /* Case 2a: Delete key by overwriting it with its successor (which occurs in a leaf node) */ + int subst = deleteImp(-1, succ.node, DELMODE_DELETE_MINIMUM, cmp); + putRecord(node.chunk, node.node, keyIndexInNode, subst); + return key; + } + + BTNode pred = node.getChild(keyIndexInNode); + if(pred!=null && pred.keyCount > MIN_RECORDS) { + /* Case 2b: Delete key by overwriting it with its predecessor (which occurs in a leaf node) */ + int subst = deleteImp(-1, pred.node, DELMODE_DELETE_MAXIMUM, cmp); + putRecord(node.chunk, node.node, keyIndexInNode, subst); + return key; + } + + /* Case 2c: Merge successor and predecessor */ + // assert(pred!=null && succ!=null); + mergeNodes(succ, node, keyIndexInNode, pred); + return deleteImp(key, pred.node, mode, cmp); + } else { + /* Case 3: non-leaf node which does not itself contain the key */ + + /* Determine root of subtree that should contain the key */ + int subtreeIndex; + switch(mode) { + case DELMODE_NORMAL: + subtreeIndex = node.keyCount; + for(int i=0; i0) { + subtreeIndex = i; + break; + } + break; + case DELMODE_DELETE_MINIMUM: subtreeIndex = 0; break; + case DELMODE_DELETE_MAXIMUM: subtreeIndex = node.keyCount; break; + default: throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, IStatus.OK, Messages.getString("BTree.UnknownMode"), null)); //$NON-NLS-1$ + } + + BTNode child = node.getChild(subtreeIndex); + if(child==null) { + throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, IStatus.OK, Messages.getString("BTree.IntegrityError"), null)); //$NON-NLS-1$ + } + + if(child.keyCount > MIN_RECORDS) { + return deleteImp(key, child.node, mode, cmp); + } else { + BTNode sibR = node.getChild(subtreeIndex+1); + if(sibR!=null && sibR.keyCount > MIN_RECORDS) { + /* Case 3a (i): child will underflow upon deletion, take a key from rightSibling */ + int rightKey = getRecord(node.chunk, node.node, subtreeIndex); + int leftmostRightSiblingKey = getRecord(sibR.chunk, sibR.node, 0); + append(child, rightKey, getChild(sibR.chunk, sibR.node, 0)); + nodeContentDelete(sibR, 0, 1); + putRecord(node.chunk, node.node, subtreeIndex, leftmostRightSiblingKey); + return deleteImp(key, child.node, mode, cmp); + } + + BTNode sibL = node.getChild(subtreeIndex-1); + if(sibL!=null && sibL.keyCount > MIN_RECORDS) { + /* Case 3a (ii): child will underflow upon deletion, take a key from leftSibling */ + int leftKey = getRecord(node.chunk, node.node, subtreeIndex-1); + prepend(child, leftKey, getChild(sibL.chunk, sibL.node, sibL.keyCount)); + int rightmostLeftSiblingKey = getRecord(sibL.chunk, sibL.node, sibL.keyCount-1); + putRecord(sibL.chunk, sibL.node, sibL.keyCount-1, 0); + putChild(sibL.chunk, sibL.node, sibL.keyCount, 0); + putRecord(node.chunk, node.node, subtreeIndex-1, rightmostLeftSiblingKey); + return deleteImp(key, child.node, mode, cmp); + } + + /* Case 3b (i,ii): leftSibling, child, rightSibling all have minimum number of keys */ + + if(sibL!=null) { // merge child into leftSibling + mergeNodes(child, node, subtreeIndex-1, sibL); + return deleteImp(key, sibL.node, mode, cmp); + } + + if(sibR!=null) { // merge rightSibling into child + mergeNodes(sibR, node, subtreeIndex, child); + return deleteImp(key, child.node, mode, cmp); + } + + throw new BTreeKeyNotFoundException( + MessageFormat.format(Messages.getString("BTree.DeletionOnAbsentKey"), //$NON-NLS-1$ + new Object[]{new Integer(key), new Integer(mode)})); + } + } + } + } + + /** + * Merge node 'src' onto the right side of node 'dst' using node + * 'keyProvider' as the source of the median key. Bounds checking is not + * performed. + * @param src the key to merge into dst + * @param mid the node that provides the median key for the new node + * @param kIndex the index of the key in the node mid which is to become the new node's median key + * @param dst the node which is the basis and result of the merge + */ + public void mergeNodes(BTNode src, BTNode keyProvider, int kIndex, BTNode dst) + throws CoreException { + nodeContentCopy(src, 0, dst, dst.keyCount+1, src.keyCount+1); + int midKey = getRecord(keyProvider.chunk, keyProvider.node, kIndex); + putRecord(dst.chunk, dst.node, dst.keyCount, midKey); + int keySucc = kIndex+1 == MAX_RECORDS ? 0 : getRecord(keyProvider.chunk, keyProvider.node, kIndex+1); + db.free(getChild(keyProvider.chunk, keyProvider.node, kIndex+1)); + nodeContentDelete(keyProvider, kIndex+1, 1); + putRecord(keyProvider.chunk, keyProvider.node, kIndex, keySucc); + if(kIndex == 0 && keySucc == 0) { + /* + * The root node is excused from the property that a node must have a least MIN keys + * This means we must special case it at the point when its had all of its keys deleted + * entirely during merge operations (which push one of its keys down as a pivot) + */ + int rootNode = getRoot(); + if(rootNode == keyProvider.node) { + db.putInt(rootPointer, dst.node); + db.free(rootNode); + } + } + } + + /** + * Insert the key and (its predecessor) child at the left side of the specified node. Bounds checking + * is not performed. + * @param node the node to prepend to + * @param key the new leftmost (least) key + * @param child the new leftmost (least) subtree root + */ + private void prepend(BTNode node, int key, int child) { + nodeContentCopy(node, 0, node, 1, node.keyCount+1); + putRecord(node.chunk, node.node, 0, key); + putChild(node.chunk, node.node, 0, child); + } + + /** + * Insert the key and (its successor) child at the right side of the specified node. Bounds checking + * is not performed. + * @param node + * @param key + * @param child + */ + private void append(BTNode node, int key, int child) { + putRecord(node.chunk, node.node, node.keyCount, key); + putChild(node.chunk, node.node, node.keyCount + 1, child); + } + + /** + * Overwrite a section of the specified node (dst) with the specified section of the source node. Bounds checking + * is not performed. To allow just copying of the final child (which has no corresponding key) the routine + * behaves as though there were a corresponding key existing with value zero.

+ * Copying from a node to itself is permitted. + * @param src the node to read from + * @param srcPos the initial index to read from (inclusive) + * @param dst the node to write to + * @param dstPos the intial index to write to (inclusive) + * @param length the number of (key,(predecessor)child) nodes to write + */ + private void nodeContentCopy(BTNode src, int srcPos, BTNode dst, int dstPos, int length) { + for(int i=length-1; i>=0; i--) { // this order is important when src==dst! + int srcIndex = srcPos + i; + int dstIndex = dstPos + i; + + if(srcIndex + * Content is deleted and remaining content is moved leftward the appropriate amount. + * @param node the node to delete content from + * @param i the start index (inclusive) to delete from + * @param length the length of the sequence to delete + */ + private void nodeContentDelete(BTNode node, int i, int length) { + for(int index=i; index<=MAX_RECORDS; index++) { + int newKey = (index+length) < node.keyCount ? getRecord(node.chunk, node.node, index+length) : 0; + int newChild = (index+length) < node.keyCount+1 ? getChild(node.chunk, node.node, index+length) : 0; + if(index 0) { - // start point is to the left - if (!accept(getChild(chunk, node, i), visitor, false)) + int child = getChild(chunk, node, 0); + if (child != 0) + if (!accept(child, visitor, true)) return false; + } + + int i; + for (i = 0; i < MAX_RECORDS; ++i) { + int record = getRecord(chunk, node, i); + if (record == 0) + break; + + if (found) { if (!visitor.visit(record)) return false; if (!accept(getChild(chunk, node, i + 1), visitor, true)) return false; - found = true; - } else if (compare == 0) { - if (!visitor.visit(record)) - return false; - if (!accept(getChild(chunk, node, i + 1), visitor, true)) + } else { + int compare = visitor.compare(record); + if (compare > 0) { + // start point is to the left + if (!accept(getChild(chunk, node, i), visitor, false)) return false; - found = true; + if (!visitor.visit(record)) + return false; + if (!accept(getChild(chunk, node, i + 1), visitor, true)) + return false; + found = true; + } else if (compare == 0) { + if (!visitor.visit(record)) + return false; + if (!accept(getChild(chunk, node, i + 1), visitor, true)) + return false; + found = true; + } + } + } + + if (!found) + return accept(getChild(chunk, node, i), visitor, false); + + return true; + } finally { + if(visitor instanceof IBTreeVisitor2) { + ((IBTreeVisitor2)visitor).postNode(node); + } + } + } + + /* + * TODO: It would be good to move these into IBTreeVisitor and eliminate + * IBTreeVisitor2 if this is acceptable. + */ + private interface IBTreeVisitor2 extends IBTreeVisitor { + void preNode(int node) throws CoreException; + void postNode(int node) throws CoreException; + } + + /** + * Debugging method for checking B-tree invariants + * @return the empty String if B-tree invariants hold, otherwise + * a human readable report + * @throws CoreException + */ + public String getInvariantsErrorReport() throws CoreException { + InvariantsChecker checker = new InvariantsChecker(); + accept(checker); + return checker.isValid() ? "" : checker.getMsg(); //$NON-NLS-1$ + } + + /** + * A B-tree visitor for checking some B-tree invariants. + * Note ordering invariants are not checked here. + */ + private class InvariantsChecker implements IBTreeVisitor2 { + boolean valid = true; + String msg = ""; //$NON-NLS-1$ + Integer leafDepth; + int depth; + + public String getMsg() { return msg; } + public boolean isValid() { return valid; } + public void postNode(int node) throws CoreException { depth--; } + public int compare(int record) throws CoreException { return 1; } + public boolean visit(int record) throws CoreException { return true; } + + public void preNode(int node) throws CoreException { + depth++; + + // collect information for checking + int keyCount = 0; + int indexFirstBlankKey = MAX_RECORDS; + int indexLastNonBlankKey = 0; + for(int i=0; i MAX_RECORDS) { + valid = false; + msg += MessageFormat.format(Messages.getString("BTree.IntegrityErrorC"), new Object[]{new Integer(node)}); //$NON-NLS-1$ + } + + // Check: All leaf nodes are at the same depth + if(childCount==0) { + if(leafDepth==null) { + leafDepth = new Integer(depth); + } + if(depth!=leafDepth.intValue()) { + valid = false; + msg += Messages.getString("BTree.IntegrityErrorD"); //$NON-NLS-1$ } } } - - if (!found) - return accept(getChild(chunk, node, i), visitor, false); - - return true; } - } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java index 53df4759c5f..730ef2c7bac 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java @@ -7,6 +7,7 @@ * * Contributors: * QNX - Initial API and implementation + * Symbian - Add some non-javadoc implementation notes *******************************************************************************/ package org.eclipse.cdt.internal.core.pdom.db; @@ -19,8 +20,36 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; /** - * @author Doug Schaefer + * Database encapsulates access to a flat binary format file with a memory-manager-like API for + * obtaining and releasing areas of storage (memory). * + * @author Doug Schaefer + */ +/* + * The file encapsulated is divided into Chunks of size CHUNK_SIZE, and a table of contents + * mapping chunk index to chunk address is maintained. Chunk structure exists only conceptually - + * its not a structure that appears in the file. + * + * ===== The first chunk is used by Database itself for house-keeping purposes and has structure + * + * offset content + * _____________________________ + * 0 | version number + * INT_SIZE | pointer to head of linked list of blocks of size MIN_SIZE + * .. | ... + * INT_SIZE * m (1) | pointer to head of linked list of blocks of size MIN_SIZE * m + * DATA_AREA | undefined (PDOM stores its own house-keeping data in this area) + * + * (1) where m <= (CHUNK_SIZE / MIN_SIZE) + * + * ===== block structure + * + * offset content + * _____________________________ + * 0 | size of block (negative indicates in use, positive unused) + * PREV_OFFSET | pointer to prev block (of same size) + * NEXT_OFFSET | pointer to next block (of same size) + * */ public class Database { @@ -214,7 +243,7 @@ public class Database { int blocksize = - chunk.getInt(block); if (blocksize < 0) // already freed - throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, 0, "Already Freed", new Exception())); + throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, 0, "Already Freed", new Exception())); //$NON-NLS-1$ addBlock(chunk, blocksize, block); freed += blocksize; } @@ -276,11 +305,11 @@ public class Database { } public void reportFreeBlocks() throws CoreException { - System.out.println("Allocated size: " + toc.length * CHUNK_SIZE); - System.out.println("malloc'ed: " + malloced); - System.out.println("free'd: " + freed); - System.out.println("wasted: " + (toc.length * CHUNK_SIZE - (malloced - freed))); - System.out.println("Free blocks"); + System.out.println("Allocated size: " + toc.length * CHUNK_SIZE); //$NON-NLS-1$ + System.out.println("malloc'ed: " + malloced); //$NON-NLS-1$ + System.out.println("free'd: " + freed); //$NON-NLS-1$ + System.out.println("wasted: " + (toc.length * CHUNK_SIZE - (malloced - freed))); //$NON-NLS-1$ + System.out.println("Free blocks"); //$NON-NLS-1$ for (int bs = MIN_SIZE; bs <= CHUNK_SIZE; bs += MIN_SIZE) { int count = 0; int block = getFirstBlock(bs); @@ -289,7 +318,17 @@ public class Database { block = getInt(block + NEXT_OFFSET); } if (count != 0) - System.out.println("Block size: " + bs + "=" + count); + System.out.println("Block size: " + bs + "=" + count); //$NON-NLS-1$ //$NON-NLS-2$ } } + + /** + * Closes the database, releasing the file lock. This is public for testing purposes only. + *

+ * The behaviour of any further calls to the Database is undefined + * @throws IOException + */ + public void close() throws IOException { + file.close(); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Messages.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Messages.java new file mode 100644 index 00000000000..2399a46381a --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Messages.java @@ -0,0 +1,22 @@ +package org.eclipse.cdt.internal.core.pdom.db; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages { + private static final String BUNDLE_NAME = "org.eclipse.cdt.internal.core.pdom.db.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle + .getBundle(BUNDLE_NAME); + + private Messages() { + } + + public static String getString(String key) { + try { + return RESOURCE_BUNDLE.getString(key); + } catch (MissingResourceException e) { + return '!' + key + '!'; + } + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/messages.properties b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/messages.properties new file mode 100644 index 00000000000..fe42cc21dee --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/messages.properties @@ -0,0 +1,8 @@ +BTree.IllegalDegree=BTree degree must be >=2 +BTree.DeletionOnAbsentKey=Deletion of key not in btree: {0} mode={1} +BTree.UnknownMode=BTree unknown deletion mode error +BTree.IntegrityError=BTree integrity error +BTree.IntegrityErrorA=[{0} blanks inconsistent b={1} nb={2}] +BTree.IntegrityErrorB=[{0} wrong number of children w.r.t. key count] +BTree.IntegrityErrorC=[{0} key count out of range] +BTree.IntegrityErrorD=Leaf nodes at differing depths