overflowList; // overflow pointer list
20 |
21 | /**
22 | * Constructor for our Internal node
23 | *
24 | * @param nextPagePointer the next leaf pointer
25 | * @param prevPagePointer the previous leaf pointer
26 | * @param nodeType the node type
27 | * @param pageIndex the index of the page
28 | */
29 | TreeLeaf(long nextPagePointer, long prevPagePointer,
30 | TreeNodeType nodeType, long pageIndex) {
31 | super(nodeType, pageIndex);
32 | if(nodeType == TreeNodeType.TREE_ROOT_LEAF && nextPagePointer > 0)
33 | {throw new IllegalArgumentException("Can't have leaf " +
34 | "root with non-null next pointer");}
35 | this.nextPagePointer = nextPagePointer;
36 | this.prevPagePointer = prevPagePointer;
37 | this.overflowList = new LinkedList<>();
38 | this.valueList = new LinkedList<>();
39 | }
40 |
41 | void addToOverflowList(int index, long value)
42 | {overflowList.add(index, value);}
43 |
44 | void addLastToOverflowList(long value)
45 | {overflowList.addLast(value);}
46 |
47 | void addLastToValueList(String value)
48 | {valueList.addLast(value);}
49 |
50 | long getOverflowPointerAt(int index)
51 | {return overflowList.get(index);}
52 |
53 | void pushToOverflowList(long overflowPointer)
54 | {overflowList.push(overflowPointer);}
55 |
56 | long popOverflowPointer()
57 | {return(overflowList.pop());}
58 |
59 | void setOverflowPointerAt(int index, long value)
60 | {overflowList.set(index, value);}
61 |
62 | long removeLastOverflowPointer()
63 | {return(overflowList.removeLast());}
64 |
65 | long getLastOverflowPointer()
66 | {return(overflowList.getLast());}
67 |
68 | void addToValueList(int index, String value)
69 | {valueList.add(index, value);}
70 |
71 | String getValueAt(int index)
72 | {return valueList.get(index);}
73 |
74 | void pushToValueList(String value)
75 | {valueList.push(value);}
76 |
77 | String popValue()
78 | {return valueList.pop();}
79 |
80 | String removeLastValue()
81 | {return valueList.removeLast();}
82 |
83 | long getNextPagePointer()
84 | {return(nextPagePointer);}
85 |
86 | void setNextPagePointer(long next)
87 | {nextPagePointer = next;}
88 |
89 | long getPrevPagePointer()
90 | {return prevPagePointer;}
91 |
92 | void setPrevPagePointer(long prevPagePointer) {
93 | this.prevPagePointer = prevPagePointer;
94 | }
95 |
96 | String removeEntryAt(int index, BPlusConfiguration conf)
97 | throws InvalidBTreeStateException {
98 | keyArray.remove(index);
99 | overflowList.remove(index);
100 | String s = valueList.remove(index);
101 | decrementCapacity(conf);
102 | return(s);
103 | }
104 |
105 | /**
106 | *
107 | * Leaf node write structure is as follows:
108 | *
109 | * -- node type -- (2 bytes)
110 | * -- next pointer -- (8 bytes)
111 | * -- prev pointer -- (8 bytes)
112 | * -- key/value pairs -- (max size * (key size + satellite size))
113 | *
114 | * @param r pointer to *opened* B+ tree file
115 | * @param conf configuration parameter
116 | * @throws IOException is thrown when an I/O operation fails
117 | */
118 | @Override
119 | public void writeNode(RandomAccessFile r, BPlusConfiguration conf,
120 | BPlusTreePerformanceCounter bPerf)
121 | throws IOException {
122 |
123 | // update root index in the file
124 | if(this.isRoot()) {
125 | r.seek(conf.getHeaderSize()-16L);
126 | r.writeLong(getPageIndex());
127 | }
128 |
129 | // account for the header page as well.
130 | r.seek(getPageIndex());
131 |
132 | // now write the node type
133 | r.writeShort(getPageType());
134 |
135 | // write the next pointer
136 | r.writeLong(nextPagePointer);
137 |
138 | // write the prev pointer
139 | r.writeLong(prevPagePointer);
140 |
141 | // then write the current capacity
142 | r.writeInt(getCurrentCapacity());
143 |
144 | // now write the Key/Value pairs
145 | for(int i = 0; i < getCurrentCapacity(); i++) {
146 | r.writeLong(getKeyAt(i));
147 | r.writeLong(getOverflowPointerAt(i));
148 | r.write(valueList.get(i).getBytes(StandardCharsets.UTF_8));
149 | }
150 |
151 | // annoying correction
152 | if(r.length() < getPageIndex()+conf.getPageSize())
153 | {r.setLength(getPageIndex()+conf.getPageSize());}
154 |
155 | bPerf.incrementTotalLeafNodeWrites();
156 | }
157 |
158 | @Override
159 | public void printNode() {
160 | System.out.println("\nPrinting node of type: " + getNodeType().toString() +
161 | " with index: " + getPageIndex());
162 | System.out.println("Current node capacity is: " + getCurrentCapacity());
163 |
164 | System.out.println("Next pointer (index): " + getNextPagePointer());
165 | System.out.println("Prev pointer (index): " + getPrevPagePointer());
166 |
167 | System.out.println("\nPrinting stored (Key, Value, ovf) tuples:");
168 | for(int i = 0; i < keyArray.size(); i++) {
169 | System.out.print(" (" +
170 | keyArray.get(i).toString() + ", " +
171 | valueList.get(i) + ", " +
172 | overflowList.get(i) + ") ");
173 | }
174 | System.out.println("\n");
175 | }
176 |
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/bptree/TreeLookupOverflowNode.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.bptree;
2 |
3 | import java.io.IOException;
4 | import java.io.RandomAccessFile;
5 |
6 | @SuppressWarnings("unused")
7 | class TreeLookupOverflowNode extends TreeNode {
8 |
9 | private long next; // next pointer
10 |
11 | /**
12 | * Constructor which takes into the node type as well as the
13 | * page index
14 | *
15 | * @param pageIndex the page index in the file
16 | */
17 | TreeLookupOverflowNode(long pageIndex, long nextPointer) {
18 | super(TreeNodeType.TREE_LOOKUP_OVERFLOW, pageIndex);
19 | this.next = nextPointer;
20 | }
21 |
22 | /**
23 | * Write a lookup page overflow to the page index; the node should
24 | * have the following structure:
25 | *
26 | * -- node type -- (2 bytes)
27 | * -- next pointer -- (8 bytes)
28 | * -- current capacity -- (4 bytes)
29 | *
30 | * -- page indexes (in place of keys) (8 bytes)
31 | *
32 | * @param r an *already* open pointer which points to our B+ Tree file
33 | * @param conf B+ Tree configuration
34 | * @param bPerf instance of performance counter class
35 | * @throws IOException is thrown when an I/O operation fails
36 | */
37 | @Override
38 | public void writeNode(RandomAccessFile r,
39 | BPlusConfiguration conf,
40 | BPlusTreePerformanceCounter bPerf)
41 | throws IOException {
42 |
43 | // account for the header page as well
44 | r.seek(getPageIndex());
45 |
46 | // write the node type
47 | r.writeShort(getPageType());
48 |
49 | // write the next pointer
50 | r.writeLong(next);
51 |
52 | // write current capacity
53 | r.writeInt(getCurrentCapacity());
54 |
55 | // now write the index values
56 | for (int i = 0; i < getCurrentCapacity(); i++) {
57 | r.writeLong(getKeyAt(i));
58 | }
59 |
60 | }
61 |
62 |
63 | /**
64 | * Get the next pointer of the node
65 | *
66 | * @return the next pointer value
67 | */
68 | long getNextPointer() {
69 | return next;
70 | }
71 |
72 | /**
73 | * Set the next pointer of the node
74 | *
75 | * @param nextPointer the new next pointer
76 | */
77 | public void setNextPointer(long nextPointer) {
78 | this.next = nextPointer;
79 | }
80 |
81 | @Override
82 | public void printNode() {
83 | System.out.println("\nPrinting node of type: " + getNodeType().toString() +
84 | " with index: " + getPageIndex());
85 | System.out.println("Current node capacity is: " + getCurrentCapacity());
86 |
87 | System.out.println("\nPrinting tuples: \n");
88 | for (Long key : keyArray) {
89 | System.out.print(key);
90 | }
91 |
92 | System.out.println("\n");
93 |
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/bptree/TreeNode.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.bptree;
2 |
3 | import ds.bplus.util.InvalidBTreeStateException;
4 |
5 | import java.io.IOException;
6 | import java.io.RandomAccessFile;
7 | import java.util.InvalidPropertiesFormatException;
8 | import java.util.LinkedList;
9 |
10 | /**
11 | *
12 | * Class that describes all the common properties that
13 | * each of the node types have.
14 | *
15 | */
16 | @SuppressWarnings("unused")
17 | abstract class TreeNode {
18 | final LinkedList keyArray; // key array
19 | private TreeNodeType nodeType; // actual node type
20 | private long pageIndex; // node page index
21 | private int currentCapacity; // current capacity
22 | private boolean beingDeleted; // deleted flag
23 |
24 |
25 | /**
26 | * Constructor which takes into the node type as well as the
27 | * page index
28 | * @param nodeType the actual node type
29 | * @param pageIndex the page index in the file
30 | */
31 | TreeNode(TreeNodeType nodeType, long pageIndex) {
32 | this.nodeType = nodeType; // actual node type
33 | this.pageIndex = pageIndex; // node page index
34 | this.currentCapacity = 0; // current capacity
35 | this.keyArray = new LinkedList<>(); // instantiate the linked list
36 | this.beingDeleted = true;
37 | }
38 |
39 | /**
40 | * Check if the node is full (and needs splitting)
41 | * @param conf configuration to deduce which degree to use
42 | *
43 | * @return true is the node is full false if it's not.
44 | */
45 | boolean isFull(BPlusConfiguration conf) {
46 | if(isLeaf()) {
47 | return(isOverflow() ?
48 | (conf.getMaxOverflowNodeCapacity() == currentCapacity) :
49 | (conf.getMaxLeafNodeCapacity() == currentCapacity));}
50 | else
51 | // internal
52 | {return(conf.getMaxInternalNodeCapacity() == currentCapacity);}
53 | }
54 |
55 | /**
56 | * Check if the node is underutilized and needs to be merged
57 | *
58 | * @param conf B+ Tree configuration reference
59 | * @return true is the node needs to be merged or false if it's not
60 | */
61 | boolean isTimeToMerge(BPlusConfiguration conf) {
62 | // for roots (internal or leaf) return true only when empty
63 | if(isRoot())
64 | {return(getCurrentCapacity() <= 1);}
65 | else if(isLeaf()) {
66 | // for overflow pages return true only if empty
67 | if (isOverflow())
68 | {return (isEmpty());}
69 | // otherwise return based on degree
70 | else
71 | {return (conf.getMinLeafNodeCapacity() >= currentCapacity);}
72 | } else // internal
73 | {
74 | return (conf.getMinInternalNodeCapacity() >= currentCapacity);
75 | }
76 | }
77 |
78 | /**
79 | * Returns the current node capacity
80 | *
81 | * @return the newCap variable value.
82 | */
83 | int getCurrentCapacity() {
84 | return (currentCapacity);
85 | }
86 |
87 | /**
88 | * Set the current capacity
89 | *
90 | * @param newCap replace node capacity with this argument.
91 | */
92 | void setCurrentCapacity(int newCap) {
93 | currentCapacity = newCap;
94 | }
95 |
96 | /**
97 | * Increment the node capacity by one.
98 | *
99 | * @param conf configuration instance for validating the limits.
100 | * @throws InvalidBTreeStateException is thrown when the capacity limits are violated after incrementing.
101 | */
102 | void incrementCapacity(BPlusConfiguration conf) throws InvalidBTreeStateException {
103 | currentCapacity++;
104 | validateNodeCapacityLimits(conf);
105 | }
106 |
107 | /**
108 | * Decrement the node capacity by one.
109 | *
110 | * @param conf configuration instance for validating the limits.
111 | * @throws InvalidBTreeStateException is thrown when the capacity limits are violated after decrementing.
112 | */
113 | void decrementCapacity(BPlusConfiguration conf)
114 | throws InvalidBTreeStateException {
115 | currentCapacity--;
116 | validateNodeCapacityLimits(conf);
117 | }
118 |
119 | /**
120 | * Function that validates the node capacity invariants based on the current configuration instance.
121 | *
122 | * @param conf configuration instance for validating the limits.
123 | * @throws InvalidBTreeStateException is thrown when the capacity limits are violated upon checking.
124 | */
125 | private void validateNodeCapacityLimits(BPlusConfiguration conf)
126 | throws InvalidBTreeStateException {
127 |
128 | if(isRoot()) {
129 | if(currentCapacity < 0) {
130 | throw new InvalidBTreeStateException("Cannot have less than zero elements");
131 | } else if(isLeaf() && currentCapacity > conf.getMaxLeafNodeCapacity()) {
132 | throw new InvalidBTreeStateException("Exceeded leaf node " +
133 | "allowed capacity at root");
134 | } else if(isInternalNode() && currentCapacity > conf.getMaxInternalNodeCapacity()) {
135 | throw new InvalidBTreeStateException("Exceeded internal node " +
136 | "allowed capacity at root");
137 | }
138 | } else {
139 | if (isLookupPageOverflowNode()) {
140 | if (beingDeleted && currentCapacity < 0) {
141 | throw new InvalidBTreeStateException("Cannot have less than " +
142 | 0 + " elements in a lookup overflow node when deleting it");
143 | } else if (currentCapacity > conf.getMaxLookupPageOverflowCapacity()) {
144 | throw new InvalidBTreeStateException("Exceeded lookup overflow node " +
145 | "allowed capacity (node)");
146 | }
147 | }
148 | if(isOverflow()) {
149 | if(beingDeleted && currentCapacity < 0) {
150 | throw new InvalidBTreeStateException("Cannot have less than " +
151 | 0 + " elements in a overflow node when deleting it");
152 | }
153 | else if(currentCapacity > conf.getMaxOverflowNodeCapacity()) {
154 | throw new InvalidBTreeStateException("Exceeded overflow node " +
155 | "allowed capacity (node)");
156 | }
157 | }
158 | else if(isLeaf()) {
159 | if(beingDeleted && currentCapacity < 0) {
160 | throw new InvalidBTreeStateException("Cannot have less than " +
161 | 0 + " elements in a leaf node when deleting it");
162 | } else if(!beingDeleted && currentCapacity < conf.getMinLeafNodeCapacity()) {
163 | throw new InvalidBTreeStateException("Cannot have less than " +
164 | conf.getMinLeafNodeCapacity() + " elements in a leaf node");
165 | }
166 | else if(currentCapacity > conf.getMaxLeafNodeCapacity()) {
167 | throw new InvalidBTreeStateException("Exceeded leaf node " +
168 | "allowed capacity (node)");
169 | }
170 | } else if(isInternalNode()) {
171 | if(beingDeleted && currentCapacity < 0) {
172 | throw new InvalidBTreeStateException("Cannot have less than " +
173 | 0 + " elements in an internal node");
174 | }
175 | else if(!beingDeleted && currentCapacity < conf.getMinInternalNodeCapacity()) {
176 | throw new InvalidBTreeStateException("Cannot have less than " +
177 | conf.getMinInternalNodeCapacity() +
178 | " elements in an internal node");
179 | }
180 | else if(currentCapacity > conf.getMaxInternalNodeCapacity()) {
181 | throw new InvalidBTreeStateException("Exceeded internal node " +
182 | "allowed capacity (node)");
183 | }
184 | }
185 | }
186 | }
187 |
188 | /**
189 | * Being deleted flag
190 | *
191 | * @return true if the node is marked to be deleted, false otherwise.
192 | */
193 | public boolean getBeingDeleted() {
194 | return beingDeleted;
195 | }
196 |
197 | /**
198 | * Set being deleted flag
199 | *
200 | * @param beingDeleted value to set the flag.
201 | */
202 | void setBeingDeleted(boolean beingDeleted) {
203 | this.beingDeleted = beingDeleted;
204 | }
205 |
206 | /**
207 | * Check if the node is empty (and *definitely* needs merging)
208 | *
209 | * @return true if it is empty false if it's not.
210 | */
211 | boolean isEmpty()
212 | {return(currentCapacity == 0);}
213 |
214 | /**
215 | * Check if the node in question is an overflow page
216 | *
217 | * @return true if the node is an overflow page, false if it's not
218 | */
219 | boolean isOverflow() {
220 | return (nodeType == TreeNodeType.TREE_LEAF_OVERFLOW);
221 | }
222 |
223 | /**
224 | * Check if the node in question is a leaf (including root)
225 | *
226 | * @return true if the node is a leaf, false if it's not.
227 | */
228 | boolean isLeaf() {
229 | return(nodeType == TreeNodeType.TREE_LEAF ||
230 | nodeType == TreeNodeType.TREE_LEAF_OVERFLOW ||
231 | nodeType == TreeNodeType.TREE_ROOT_LEAF);
232 | }
233 |
234 | /**
235 | * Check if the node in question is a tree root.
236 | *
237 | * @return true if it is a tree root, false if it's not.
238 | */
239 | boolean isRoot() {
240 | return(nodeType == TreeNodeType.TREE_ROOT_INTERNAL ||
241 | nodeType == TreeNodeType.TREE_ROOT_LEAF);
242 | }
243 |
244 | /**
245 | * Check if the node in question is an internal node (including root)
246 | *
247 | * @return true if the node is an internal node, false if it's not.
248 | */
249 | boolean isInternalNode() {
250 | return(nodeType == TreeNodeType.TREE_INTERNAL_NODE ||
251 | nodeType == TreeNodeType.TREE_ROOT_INTERNAL);
252 | }
253 |
254 | /**
255 | * Check if the node in question is a lookup page overflow node
256 | *
257 | * @return true if the node is a lookup page overflow node, false otherwise
258 | */
259 | boolean isLookupPageOverflowNode() {
260 | return (nodeType == TreeNodeType.TREE_LOOKUP_OVERFLOW);
261 | }
262 |
263 | /**
264 | * Return the node type
265 | *
266 | * @return the current node type
267 | */
268 | TreeNodeType getNodeType() {
269 | return (nodeType);
270 | }
271 |
272 | /**
273 | * Explicitly set the node type
274 | *
275 | * @param nodeType set the node type
276 | */
277 | void setNodeType(TreeNodeType nodeType) {
278 | // check if we presently are a leaf
279 | if (isLeaf()) {
280 | this.nodeType = nodeType;
281 | if (isInternalNode()) {
282 | throw new IllegalArgumentException("Cannot convert Leaf to Internal Node");
283 | }
284 | }
285 | // it must be an internal node
286 | else {
287 | this.nodeType = nodeType;
288 | if (isLeaf()) {
289 | throw new IllegalArgumentException("Cannot convert Internal Node to Leaf");
290 | }
291 | }
292 | }
293 |
294 | /**
295 | * Get the specific key at position indicated by index
296 | * @param index the position to get the key
297 | * @return the key at position
298 | */
299 | long getKeyAt(int index)
300 | {return(keyArray.get(index));}
301 |
302 | /**
303 | * Return the page index
304 | *
305 | * @return current page index
306 | */
307 | long getPageIndex()
308 | {return pageIndex;}
309 |
310 | /**
311 | * Update the page index
312 | *
313 | * @param pageIndex new page index
314 | */
315 | void setPageIndex(long pageIndex)
316 | {this.pageIndex = pageIndex;}
317 |
318 | /**
319 | * Set the key in the array at specific position
320 | *
321 | * @param index index to set the key
322 | * @param key key to set in position
323 | */
324 | void setKeyArrayAt(int index, long key)
325 | {keyArray.set(index, key);}
326 |
327 | /**
328 | * Add key at index while shifting entries
329 | * pointed by index and after by one.
330 | *
331 | * @param index index to shift keys and add
332 | * @param key key to add in position
333 | */
334 | void addToKeyArrayAt(int index, long key)
335 | {keyArray.add(index, key);}
336 |
337 | /**
338 | * Push a key to head of the array
339 | *
340 | * @param key key to push
341 | */
342 | void pushToKeyArray(long key)
343 | {keyArray.push(key);}
344 |
345 | /**
346 | * Add a key to the last place of the array
347 | *
348 | * @param key key to add
349 | */
350 | void addLastToKeyArray(long key)
351 | {keyArray.addLast(key);}
352 |
353 | /**
354 | * Get last element
355 | *
356 | * @return return the last key
357 | */
358 | long getLastKey()
359 | {return keyArray.getLast();}
360 |
361 | /**
362 | * Get first key
363 | *
364 | * @return return the first key value
365 | */
366 | long getFirstKey()
367 | {return keyArray.getFirst();}
368 |
369 | /**
370 | * Pop the key at the head of the array
371 | *
372 | * @return key that is in the head of the array
373 | */
374 | long popKey()
375 | {return keyArray.pop();}
376 |
377 | /**
378 | * Remove and pop the last key of the array
379 | *
380 | * @return key that is in the last place of the array
381 | */
382 | long removeLastKey()
383 | {return keyArray.removeLast();}
384 |
385 | /**
386 | * Remove and pop the key at specific position
387 | *
388 | * @param index index that points where to remvoe the key
389 | * @return removed key
390 | */
391 | long removeKeyAt(int index)
392 | {return(keyArray.remove(index));}
393 |
394 | /**
395 | * Get the page type that maps the enumeration to numbers that are
396 | * easily stored in our file.
397 | *
398 | * @return the number representation of the node type
399 | * @throws InvalidPropertiesFormatException is thrown when the page type is not matched.
400 | */
401 | short getPageType()
402 | throws InvalidPropertiesFormatException {
403 | switch(getNodeType()) {
404 | case TREE_LEAF: // LEAF
405 | {return(1);}
406 |
407 | case TREE_INTERNAL_NODE: // INTERNAL NODE
408 | {return(2);}
409 |
410 | case TREE_ROOT_INTERNAL: // INTERNAL NODE /w ROOT
411 | {return(3);}
412 |
413 | case TREE_ROOT_LEAF: // LEAF NODE /w ROOT
414 | {return(4);}
415 |
416 | case TREE_LEAF_OVERFLOW: // LEAF OVERFLOW NODE
417 | {return(5);}
418 |
419 | case TREE_LOOKUP_OVERFLOW: // TREE LOOKUP OVERFLOW
420 | {
421 | return (6);
422 | }
423 |
424 | default: {
425 | throw new InvalidPropertiesFormatException("Unknown " +
426 | "node value read; file possibly corrupt?");
427 | }
428 | }
429 | }
430 |
431 | /**
432 | * Abstract method that all classes must implement that writes
433 | * each node type to a page slot.
434 | *
435 | * More details in each implementation.
436 | *
437 | * @param r an *already* open pointer which points to our B+ Tree file
438 | * @param conf B+ Tree configuration
439 | * @throws IOException is thrown when an I/O operation fails.
440 | */
441 | public abstract void writeNode(RandomAccessFile r, BPlusConfiguration conf,
442 | BPlusTreePerformanceCounter bPerf)
443 | throws IOException;
444 |
445 | /**
446 | *
447 | * Each class must implement it's own printing method.
448 | *
449 | */
450 | public abstract void printNode();
451 |
452 | }
453 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/bptree/TreeNodeType.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.bptree;
2 |
3 | enum TreeNodeType {
4 | TREE_LEAF,
5 | TREE_INTERNAL_NODE,
6 | TREE_ROOT_INTERNAL,
7 | TREE_ROOT_LEAF,
8 | TREE_LEAF_OVERFLOW,
9 | TREE_LOOKUP_OVERFLOW
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/bptree/TreeOverflow.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.bptree;
2 |
3 | import java.io.IOException;
4 | import java.io.RandomAccessFile;
5 | import java.nio.charset.StandardCharsets;
6 | import java.util.LinkedList;
7 |
8 | /**
9 | * Class that is responsible for handling the overflow blocks.
10 | *
11 | * Although it is derived from the TreeNode class we *don't* use
12 | * the key array at all (this could be improved but... well...)
13 | */
14 | @SuppressWarnings("unused")
15 | class TreeOverflow extends TreeNode {
16 |
17 |
18 | private final LinkedList valueList;
19 | private long nextPagePointer;
20 | private long prevPagePointer;
21 |
22 | /**
23 | * Constructor which takes into the node type as well as the
24 | * page index
25 | *
26 | * @param nextPagePointer the next overflow pointer
27 | * @param prevPagePointer the previous leaf or overflow pointer
28 | * @param pageIndex the page index in the file
29 | */
30 | TreeOverflow(long nextPagePointer, long prevPagePointer,
31 | long pageIndex) {
32 | super(TreeNodeType.TREE_LEAF_OVERFLOW, pageIndex);
33 | valueList = new LinkedList<>();
34 | this.nextPagePointer = nextPagePointer;
35 | this.prevPagePointer = prevPagePointer;
36 | }
37 |
38 | void pushToValueList(String value)
39 | {valueList.push(value);}
40 |
41 | String removeLastValue()
42 | {return(valueList.removeLast());}
43 |
44 | void addToValueList(int index, String value)
45 | {valueList.add(index, value);}
46 |
47 | String getValueAt(int index)
48 | {return valueList.get(index);}
49 |
50 | long getNextPagePointer()
51 | {return(nextPagePointer);}
52 |
53 | void setNextPagePointer(long next)
54 | {nextPagePointer = next;}
55 |
56 | private long getPrevPagePointer()
57 | {return prevPagePointer;}
58 |
59 | void setPrevPagePointer(long prevPagePointer)
60 | {this.prevPagePointer = prevPagePointer;}
61 |
62 |
63 | /**
64 | *
65 | * Overflow node write structure is as follows:
66 | *
67 | * -- node type -- (2 bytes)
68 | * -- next pointer -- (8 bytes)
69 | * -- prev pointer -- (8 bytes)
70 | * -- values -- (max size * satellite size)
71 | *
72 | * @param r pointer to *opened* B+ tree file
73 | * @throws IOException is thrown when an I/O operation fails
74 | */
75 | @Override
76 | public void writeNode(RandomAccessFile r, BPlusConfiguration conf,
77 | BPlusTreePerformanceCounter bPerf)
78 | throws IOException {
79 | // account for the header page as well.
80 | r.seek(getPageIndex());
81 |
82 | // now write the node type
83 | r.writeShort(getPageType());
84 |
85 | // write the next pointer
86 | r.writeLong(nextPagePointer);
87 |
88 | // write the prev pointer
89 | r.writeLong(prevPagePointer);
90 |
91 | // then write the current capacity
92 | r.writeInt(getCurrentCapacity());
93 |
94 | // now write the values
95 | for(int i = 0; i < getCurrentCapacity(); i++)
96 | {r.write(valueList.get(i).getBytes(StandardCharsets.UTF_8));}
97 |
98 | // annoying correction
99 | if(r.length() < getPageIndex()+conf.getPageSize())
100 | {r.setLength(getPageIndex()+conf.getPageSize());}
101 |
102 | bPerf.incrementTotalOverflowNodeWrites();
103 | }
104 |
105 | @Override
106 | public void printNode() {
107 | System.out.println("\nPrinting node of type: " + getNodeType().toString() +
108 | " with index: " + getPageIndex());
109 | System.out.println("Current node capacity is: " + getCurrentCapacity());
110 |
111 | System.out.println("Next pointer (index): " + getNextPagePointer());
112 | System.out.println("Prev pointer (index): " + getPrevPagePointer());
113 |
114 | System.out.println("\nPrinting stored values:");
115 | for(int i = 0; i < keyArray.size(); i++) {
116 | System.out.print(" " + valueList.get(i) + " ");
117 | }
118 | System.out.println("\n");
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/fudger/Main.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.fudger;
2 |
3 | import ds.bplus.bptree.BPlusConfiguration;
4 | import ds.bplus.bptree.BPlusTree;
5 | import ds.bplus.bptree.BPlusTreePerformanceCounter;
6 | import ds.bplus.util.InvalidBTreeStateException;
7 | import ds.bplus.util.TestRunner;
8 |
9 | import java.io.IOException;
10 |
11 | public class Main {
12 |
13 | public static void main(String[] args)
14 | throws IOException, InvalidBTreeStateException {
15 | boolean fastTrials = true;
16 | boolean recreateTree = true;
17 | BPlusConfiguration btconf = new BPlusConfiguration();
18 | BPlusTreePerformanceCounter bPerf = new BPlusTreePerformanceCounter(true);
19 | BPlusTree bt = new BPlusTree(btconf, recreateTree ? "rw+" : "rw", bPerf);
20 |
21 | //int tlen = 20000;
22 | //long skey = 0;
23 | //long eKey = tlen;
24 | //String val = "1234567890";
25 | //boolean unique = true;
26 | bt.printCurrentConfiguration();
27 | // if(recreateTree) {
28 | // Utilities.sequentialAddToTree(skey, eKey,
29 | // val, unique, bt);
30 | // bPerf.printTotalStatistics();
31 | // }
32 |
33 | if(fastTrials)
34 | {TestRunner.runDefaultTrialsFast(bPerf);}
35 | else
36 | {TestRunner.runBench(bPerf);}
37 |
38 | System.out.println("\n -- Total pages in the end: " + bt.getTotalTreePages());
39 | // finally close it.
40 | bt.commitTree();
41 |
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/util/InvalidBTreeStateException.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.util;
2 |
3 | /**
4 | * Just a wrapper to state exception.
5 | */
6 |
7 | public class InvalidBTreeStateException extends Exception {
8 | private static final long serialVersionUID = 7295144377433447079L;
9 | public InvalidBTreeStateException(String m)
10 | {super(m);}
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/util/StandardInputRead.java:
--------------------------------------------------------------------------------
1 | /* Courtesy of @garanest with edits by me. */
2 | package ds.bplus.util;
3 |
4 |
5 | import java.io.BufferedReader;
6 | import java.io.IOException;
7 | import java.io.InputStreamReader;
8 | import java.text.DateFormat;
9 | import java.text.ParseException;
10 | import java.util.Date;
11 | import java.util.Locale;
12 |
13 | /**
14 | * It reads input from the standard input hiding from the user the usage
15 | * of java.io package classes
16 | */
17 | @SuppressWarnings({"WeakerAccess", "unused"})
18 | class StandardInputRead {
19 | /** The error that is return when reading positive integers from stdin*/
20 | private final static int POS_ERROR = -1;
21 |
22 | /** The error that is return when reading negative integers from stdin*/
23 | private final static int NEG_ERROR = 1;
24 |
25 | /** The basic reader*/
26 | private final BufferedReader in;
27 |
28 | /**
29 | * Class constructor
30 | */
31 | public StandardInputRead() {
32 | super();
33 | in = new BufferedReader(new InputStreamReader(System.in));
34 | }
35 |
36 | /**
37 | * It reads a string from standard inputand returns it as value.
38 | * In case of an error it returns null
39 | *
40 | * @param message The message that is apperad to the user asking for input
41 | */
42 | public String readString(String message) {
43 |
44 | System.out.print(message);
45 | try {
46 | return in.readLine();
47 | }
48 | catch (IOException e) {
49 | return null;
50 | }
51 | }
52 |
53 | /**
54 | * It reads an positive integer, zero included, from standard input
55 | * and returns it as value. In case of an error it returns -1
56 | *
57 | * @param message The message that is apperad to the user asking for input
58 | */
59 | public int readPositiveInt(String message) {
60 |
61 | String str;
62 | int num;
63 |
64 | System.out.print(message);
65 | try {
66 | str = in.readLine();
67 | num = Integer.parseInt(str);
68 | if (num < 0 ){
69 | return POS_ERROR;
70 | }
71 | else {
72 | return num;
73 | }
74 | }
75 | catch (IOException e) {
76 | return POS_ERROR;
77 | }
78 | catch (NumberFormatException e1) {
79 | return POS_ERROR;
80 | }
81 | }
82 |
83 | /**
84 | * It reads an negative integer from standard input and returns it as value.
85 | * In case of an error it returns 1
86 | *
87 | * @param message The message that is apperad to the user asking for input
88 | */
89 | public int readNegativeInt(String message) {
90 |
91 | String str;
92 | int num;
93 |
94 | System.out.print(message);
95 | try {
96 | str = in.readLine();
97 | num = Integer.parseInt(str);
98 | if (num >= 0 ){
99 | return NEG_ERROR;
100 | }
101 | else {
102 | return num;
103 | }
104 | }
105 | catch (IOException e) {
106 | return NEG_ERROR;
107 | }
108 | catch (NumberFormatException e1) {
109 | return NEG_ERROR;
110 | }
111 | }
112 |
113 | /**
114 | * It reads an positive float, zero included, from standard input
115 | * and returns it as value. In case of an error it returns -1.0
116 | *
117 | * @param message The message that is appeared to the user asking for input
118 | */
119 | public float readPositiveFloat(String message) {
120 |
121 | String str;
122 | float num;
123 |
124 | System.out.print(message);
125 | try {
126 | str = in.readLine();
127 | num = Float.parseFloat(str);
128 | if (num < 0 ){
129 | return POS_ERROR;
130 | }
131 | else {
132 | return num;
133 | }
134 | }
135 | catch (IOException e) {
136 | return POS_ERROR;
137 | }
138 | catch (NumberFormatException e1) {
139 | return POS_ERROR;
140 | }
141 | }
142 |
143 | /**
144 | * It reads an negative float from standard input and returns it as value.
145 | * In case of an error it returns 1
146 | *
147 | * @param message The message that is appeared to the user asking for input
148 | */
149 | public float readNegativeFloat(String message) {
150 |
151 | String str;
152 | float num;
153 |
154 | System.out.print(message);
155 | try {
156 | str = in.readLine();
157 | num = Float.parseFloat(str);
158 | if (num >= 0 ){
159 | return NEG_ERROR;
160 | }
161 | else {
162 | return num;
163 | }
164 | }
165 | catch (IOException e) {
166 | return NEG_ERROR;
167 | }
168 | catch (NumberFormatException e1) {
169 | return NEG_ERROR;
170 | }
171 | }
172 |
173 | /**
174 | * It reads an date in the form dd/mm/yyyy from standard input and
175 | * returns it as value.In case of an error it returns null
176 | *
177 | * @param message The message that is appeared to the user asking for input
178 | */
179 | public Date readDate(String message) {
180 |
181 | String str;
182 |
183 | System.out.print(message);
184 |
185 | try {
186 | str = in.readLine();
187 | Locale l = new Locale("el", "GR");
188 | DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, l);
189 | return df.parse(str);
190 | }
191 | catch (IOException e) {
192 | return null;
193 | }
194 | catch (ParseException e1) {
195 | return null;
196 | }
197 | }
198 |
199 | /**
200 | * It reads an time in the form h:mm AM or PM from standard input and
201 | * returns it as value.In case of an error it returns null
202 | * Example of valid times: 8:30 AM, 2:00 PM, etc
203 | *
204 | * @param message The message that is apperad to the user asking for input
205 | */
206 | public Date readTime(String message) {
207 |
208 | String str;
209 |
210 | System.out.print(message);
211 |
212 | try {
213 | str = in.readLine();
214 | DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
215 |
216 | return df.parse(str);
217 | }
218 | catch (IOException e) {
219 | return null;
220 | }
221 | catch (ParseException e1) {
222 | return null;
223 | }
224 | }
225 |
226 | }
227 |
228 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/util/TestRunner.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.util;
2 |
3 | import ds.bplus.bptree.BPlusTreePerformanceCounter;
4 |
5 | import java.io.IOException;
6 |
7 | /**
8 | *
9 | * Another wrapper class that makes running tests a bit easier.
10 | *
11 | */
12 | @SuppressWarnings("unused")
13 | public class TestRunner {
14 |
15 | /**
16 | * Run the test interface
17 | *
18 | * @param bPerf performance class tied to a B+ Tree instance
19 | * @throws IOException is thrown when an I/O operation fails
20 | */
21 | @SuppressWarnings("unused")
22 | public static void runBench(BPlusTreePerformanceCounter bPerf)
23 | throws IOException, InvalidBTreeStateException {
24 | StandardInputRead sin = new StandardInputRead();
25 | int choice;
26 |
27 | while((choice = menuChoice(sin)) != 6)
28 | {handleChoices(choice, sin, bPerf);}
29 | }
30 |
31 | /**
32 | * Display menu choices and grab the user selection
33 | *
34 | * @param sin input class
35 | * @return a valid user option selection
36 | */
37 | private static int menuChoice(StandardInputRead sin) {
38 | System.out.println("\nSelect from menu\n");
39 | System.out.println("\t1) Run default trials");
40 | System.out.println("\t2) Run insertion run");
41 | System.out.println("\t3) Run deletion run");
42 | System.out.println("\t4) Run search run");
43 | System.out.println("\t5) Run range query run");
44 | System.out.println("\t6) Exit\n");
45 | int choice = sin.readPositiveInt("Enter your choice: ");
46 | while(!(choice > 0 && choice < 7))
47 | {choice = sin.readPositiveInt("Wrong range, try again: ");}
48 | return(choice);
49 | }
50 |
51 | /**
52 | * Silently just run the default trials using the default values
53 | *
54 | * @param bPerf performance class tied to a B+ Tree instance
55 | * @throws IOException is thrown when an I/O operation fails
56 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
57 | */
58 | @SuppressWarnings("unused")
59 | public static void runDefaultTrialsFast(BPlusTreePerformanceCounter bPerf)
60 | throws IOException, InvalidBTreeStateException {
61 | int trials = 4000;
62 | int vmin = 1;
63 | int vmax = 99999;
64 | //boolean verbose = false;
65 | //boolean unique = false;
66 | //String val = "asdfasdfas";
67 | int qrange = 150;
68 | runDefaultTrials(trials, vmin, vmax, qrange, null,
69 | false, false, bPerf);
70 | }
71 |
72 | /**
73 | * Handle the selected user option
74 | *
75 | * @param choice the user choice
76 | * @param sin the input class
77 | * @param bPerf performance class ties to a B+ Tree instance
78 | * @throws IOException is thrown when an I/O operation fails
79 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
80 | */
81 | private static void handleChoices(int choice, StandardInputRead sin,
82 | BPlusTreePerformanceCounter bPerf)
83 | throws IOException, InvalidBTreeStateException {
84 | //boolean unique = true;
85 | switch(choice) {
86 | case 1: {
87 | int trials = 2000;
88 | int vmin = 1;
89 | int vmax = 99999;
90 | //boolean verbose = false;
91 | //String val = "asdfasdfas";
92 | int qrange = 150;
93 | runDefaultTrials(trials, vmin, vmax, qrange, null,
94 | false, false, bPerf);
95 | break;
96 | }
97 | case 2: {
98 | runInsertion(sin, bPerf);
99 | break;
100 | }
101 | case 3: {
102 | runDeletion(sin, bPerf);
103 | break;
104 | }
105 | case 4: {
106 | runSearch(sin, bPerf);
107 | break;
108 | }
109 | case 5: {
110 | runRangeQuery(sin, bPerf);
111 | break;
112 | }
113 | default: {
114 | System.out.println("Closing program.");
115 | break;
116 | }
117 | }
118 | }
119 |
120 | /**
121 | * Grab from user the unique flag.
122 | *
123 | * @param sin console input library.
124 | * @return the boolean user choice.
125 | */
126 | private static boolean isUnique(StandardInputRead sin) {
127 | System.out.println("Want unique results?");
128 | System.out.println("\t1) Yes");
129 | System.out.println("\t2) No");
130 | int choice = sin.readPositiveInt("Enter your choice: ");
131 | if(choice == 2) {
132 | return(false);
133 | } else if(choice == 1) {
134 | return(true);
135 | } else {
136 | System.out.println("Wrong choice, using default (Yes)");
137 | return(true);
138 | }
139 | }
140 |
141 | /**
142 | * Run insertion
143 | *
144 | * @param sin input class
145 | * @param bPerf performance class tied to a B+ Tree instance
146 | * @throws IOException is thrown when an I/O operation fails
147 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
148 | */
149 | private static void runInsertion(StandardInputRead sin,
150 | BPlusTreePerformanceCounter bPerf)
151 | throws IOException, InvalidBTreeStateException {
152 | boolean unique = isUnique(sin);
153 | String val = "1234567890"; // default value
154 | int key;
155 | // get a key to insert
156 | while((key = sin.readPositiveInt("Enter a valid key: ")) == -1)
157 | {System.out.println("Wrong key... try again");}
158 | // all are verbose
159 | bPerf.insertIO(key, val, unique, true);
160 | }
161 |
162 |
163 | /**
164 | * Run deletion
165 | *
166 | * @param sin input class
167 | * @param bPerf performance class tied to a B+ Tree instance
168 | * @throws IOException is thrown when an I/O operation fails
169 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
170 | */
171 | private static void runDeletion(StandardInputRead sin,
172 | BPlusTreePerformanceCounter bPerf)
173 | throws IOException, InvalidBTreeStateException {
174 | boolean unique = isUnique(sin);
175 | int key;
176 | // get a key to insert
177 | while((key = sin.readPositiveInt("Enter a valid key: ")) == -1)
178 | {System.out.println("Wrong key... try again");}
179 | // all are verbose
180 | bPerf.deleteIO(key, unique, true);
181 | //bPerf.insertIO(key, val, unique, true);
182 | }
183 |
184 |
185 | /**
186 | * Run a search instance
187 | *
188 | * @param sin input class
189 | * @param bPerf performance class tied to a B+ Tree instance
190 | * @throws IOException is thrown when an I/O operation fails
191 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
192 | */
193 | private static void runSearch(StandardInputRead sin,
194 | BPlusTreePerformanceCounter bPerf)
195 | throws IOException, InvalidBTreeStateException {
196 | boolean unique = isUnique(sin);
197 | int key;
198 | // get a key to insert
199 | while((key = sin.readPositiveInt("Enter a valid key: ")) == -1)
200 | {System.out.println("Wrong key... try again");}
201 | // all are verbose
202 | bPerf.searchIO(key, unique, true);
203 | }
204 |
205 | /**
206 | * Run a range query instance
207 | *
208 | * @param sin input class
209 | * @param bPerf performance class tied to a B+ Tree instance
210 | * @throws IOException is thrown when an I/O operation fails
211 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
212 | */
213 | private static void runRangeQuery(StandardInputRead sin,
214 | BPlusTreePerformanceCounter bPerf)
215 | throws IOException, InvalidBTreeStateException {
216 | boolean unique = isUnique(sin);
217 | int minKey;
218 | int maxKey;
219 | // get a key to insert
220 | while((minKey = sin.readPositiveInt("Enter a valid min key: ")) == -1)
221 | {System.out.println("Wrong key... try again");}
222 | while((maxKey = sin.readPositiveInt("Enter a valid max key: ")) == -1)
223 | {System.out.println("Wrong key... try again");}
224 |
225 | if(maxKey < minKey)
226 | {System.out.println("Can't proceed maxKey < minKey"); return;}
227 |
228 | // all are verbose
229 | bPerf.rangeIO(minKey, maxKey, unique, true);
230 | }
231 |
232 | /**
233 | * Run default trial set
234 | *
235 | * @param trials number of trials to run
236 | * @param vmin min key value
237 | * @param vmax max key value
238 | * @param qrange range of range queries
239 | * @param val value of tied to the key (the same is used)
240 | * @param unique allow duplicates?
241 | * @param verbose verbose results?
242 | * @param bPerf performance class tied to a B+ Tree instance
243 | * @throws IOException is thrown when an I/O operation fails
244 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
245 | */
246 | private static void runDefaultTrials(int trials, int vmin, int vmax, int qrange,
247 | String val, boolean unique, boolean verbose,
248 | BPlusTreePerformanceCounter bPerf)
249 | throws IOException, InvalidBTreeStateException {
250 | TrialsClass.runInsertTrial(trials, vmin, vmax, val, unique, bPerf, verbose);
251 | TrialsClass.runSearchTrial(trials, vmin, vmax, unique, bPerf, verbose);
252 | TrialsClass.runDeletionTrials(trials, vmin, vmax, unique, bPerf, verbose);
253 | TrialsClass.runRangeQueryTrial(trials, vmin, vmax, qrange, unique, bPerf, verbose);
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/util/TrialsClass.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.util;
2 |
3 | import ds.bplus.bptree.BPlusTreePerformanceCounter;
4 |
5 | import java.io.IOException;
6 |
7 | /**
8 | *
9 | * Wrapper class to run trials for a specific functionality
10 | *
11 | */
12 | @SuppressWarnings("unused")
13 | class TrialsClass {
14 |
15 | /**
16 | * Run a search trial
17 | *
18 | * @param trials the number of trials to run
19 | * @param rmin the min key value
20 | * @param rmax the max key value
21 | * @param unique want unique results?
22 | * @param bPerf performance class tied to a B+ Tree instance
23 | * @param verbose verbose results?
24 | * @throws IOException is thrown when an I/O operation fails
25 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
26 | */
27 | static void runSearchTrial(int trials, int rmin, int rmax, boolean unique,
28 | BPlusTreePerformanceCounter bPerf,
29 | boolean verbose)
30 | throws IOException, InvalidBTreeStateException {
31 |
32 | int pageReads = 0;
33 | int pageWrites = 0;
34 | //int found = 0;
35 | int stats[];
36 |
37 | // trial loop
38 | for(int i = 0; i < trials; i++) {
39 | stats = bPerf.searchIO(Utilities.randInt(rmin, rmax), unique, verbose);
40 | pageReads += stats[0];
41 | pageWrites += stats[1];
42 | }
43 |
44 | System.out.println("\nPerformed " + trials + " search trials");
45 | System.out.println("\n\tTotal page reads: " + pageReads);
46 | System.out.println("\tTotal page writes: " + pageWrites);
47 | System.out.println("\tAverage (reads, writes): " + (pageReads/(1.0*trials))
48 | + ", " + (pageWrites/(1.0*trials)));
49 | }
50 |
51 | /**
52 | * Run a insertion trial
53 | *
54 | * @param trials the number of trials to run
55 | * @param rmin the min key value
56 | * @param rmax the max key value
57 | * @param value value to tie with the inserted key
58 | * @param unique allow duplicate insertions?
59 | * @param bPerf performance class tied to a B+ Tree instance
60 | * @param verbose verbose results?
61 | * @throws IOException is thrown when an I/O operation fails
62 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
63 | */
64 | static void runInsertTrial(int trials, int rmin, int rmax,
65 | String value, boolean unique,
66 | BPlusTreePerformanceCounter bPerf, boolean verbose)
67 | throws IOException, InvalidBTreeStateException {
68 | int pageReads = 0;
69 | int pageWrites = 0;
70 | Long key;
71 | int stats[];
72 |
73 | // trial loop
74 | for(int i = 0; i < trials; i++) {
75 | key = (long) Utilities.randInt(rmin, rmax);
76 | stats = bPerf.insertIO(key,
77 | value == null ? key.toString() : value, unique, verbose);
78 | pageReads += stats[0];
79 | pageWrites += stats[1];
80 | }
81 |
82 | System.out.println("\nPerformed " + trials + " insertion trials");
83 | System.out.println("\n\tTotal page reads: " + pageReads);
84 | System.out.println("\tTotal page writes: " + pageWrites);
85 | System.out.println("\tAverage (reads, writes): " + (pageReads/(1.0*trials))
86 | + ", " + (pageWrites/(1.0*trials)));
87 | }
88 |
89 | /**
90 | * Run a range query trial
91 | *
92 | * @param trials the number of trials to run
93 | * @param rmin the min key value
94 | * @param rmax the max key value
95 | * @param range value to tie with the inserted key
96 | * @param unique allow duplicate insertions?
97 | * @param bPerf performance class tied to a B+ Tree instance
98 | * @param verbose verbose results?
99 | * @throws IOException is thrown when an I/O operation fails
100 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
101 | */
102 | static void runRangeQueryTrial(int trials, int rmin, int rmax, int range,
103 | boolean unique, BPlusTreePerformanceCounter bPerf,
104 | boolean verbose) throws IOException, InvalidBTreeStateException {
105 | int pageReads = 0;
106 | int pageWrites = 0;
107 | int stats[];
108 | int rtmax = rmax - range;
109 | int arange = Utilities.randInt(rmin, rtmax);
110 |
111 | // trial loop
112 | for(int i = 0; i < trials; i++) {
113 | stats = bPerf.rangeIO(arange, arange+range, unique, verbose);
114 | pageReads += stats[0];
115 | pageWrites += stats[1];
116 | }
117 |
118 | System.out.println("\nPerformed " + trials + " Range Query trials");
119 | System.out.println("\n\tTotal page reads: " + pageReads);
120 | System.out.println("\tTotal page writes: " + pageWrites);
121 | System.out.println("\tAverage (reads, writes): " + (pageReads/(1.0*trials))
122 | + ", " + (pageWrites/(1.0*trials)));
123 | }
124 |
125 | /**
126 | * Run a deletion trial
127 | *
128 | * @param trials number of trials to run
129 | * @param rmin the min key value
130 | * @param rmax the max key value
131 | * @param unique delete the *first* found or *all* found?
132 | * @param bPerf performance class tied to a B+ Tree instance
133 | * @param verbose verbose results?
134 | * @throws IOException is thrown when an I/O operation fails
135 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
136 | */
137 | static void runDeletionTrials(int trials, int rmin, int rmax, boolean unique,
138 | BPlusTreePerformanceCounter bPerf,
139 | boolean verbose)
140 | throws IOException, InvalidBTreeStateException {
141 | int pageReads = 0;
142 | int pageWrites = 0;
143 | int stats[];
144 |
145 | // trial loop
146 | for(int i = 0; i < trials; i++) {
147 | stats = bPerf.deleteIO(Utilities.randInt(rmin, rmax), unique, verbose);
148 | pageReads += stats[0];
149 | pageWrites += stats[1];
150 | }
151 |
152 | System.out.println("\nPerformed " + trials + " deletion trials");
153 | System.out.println("\n\tTotal page reads: " + pageReads);
154 | System.out.println("\tTotal page writes: " + pageWrites);
155 | System.out.println("\tAverage (reads, writes): " + (pageReads/(1.0*trials))
156 | + ", " + (pageWrites/(1.0*trials)));
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/java/ds/bplus/util/Utilities.java:
--------------------------------------------------------------------------------
1 | package ds.bplus.util;
2 |
3 | import ds.bplus.bptree.BPlusTree;
4 |
5 | import java.io.*;
6 | import java.util.Collections;
7 | import java.util.LinkedList;
8 | import java.util.Random;
9 |
10 | public class Utilities {
11 |
12 | private static final Random rand = new Random();
13 |
14 | /**
15 | * Returns a pseudo-random number between min and max, inclusive.
16 | * The difference between min and max can be at most
17 | * Integer.MAX_VALUE - 1
.
18 | *
19 | * @param min Minimum value
20 | * @param max Maximum value. Must be greater than min.
21 | * @return Integer between min and max, inclusive.
22 | * @see Random#nextInt(int)
23 | */
24 |
25 | static int randInt(int min, int max) {
26 | // nextInt is normally exclusive of the top value,
27 | // so add 1 to make it inclusive
28 | return rand.nextInt((max - min) + 1) + min;
29 | }
30 |
31 | /**
32 | * Helper to add stuff to the tree
33 | * @param from key to start
34 | * @param to key to end
35 | * @param val value to tie with the keys
36 | * @param unique allow duplicates?
37 | * @param bt B+ Tree instance
38 | * @throws IOException is thrown when an I/O operation fails
39 | */
40 | public static void sequentialAddToTree(long from, long to, String val,
41 | boolean unique, BPlusTree bt)
42 | throws IOException, InvalidBTreeStateException {
43 | long div = (to - from) / 10;
44 | for(long i = from; i < to; i++) {
45 | if (i % div == 0) {
46 | System.out.println("Currently at: " + ((double) i / to) * 100 + " %");
47 | }
48 | bt.insertKey(i, val, unique);
49 | }
50 | System.out.println("Done!\n");
51 | }
52 |
53 | /**
54 | * Add a random sequence of numbers in the tree using unique
55 | * or discrete values for the key.
56 | *
57 | * @param from starting range (>= 0)
58 | * @param to ending range
59 | * @param unique use unique values flag
60 | * @param bt tree instance to add the values
61 | * @return the list of the values in reverse order of insertion
62 | * @throws IOException is thrown when an I/O operation fails
63 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
64 | */
65 | public static LinkedList fuzzyAddToTree(int from, int to,
66 | boolean unique, BPlusTree bt)
67 | throws IOException, InvalidBTreeStateException {
68 |
69 | if(from < 0 || to < from)
70 | {throw new IllegalArgumentException("range must > 0 and from > to");}
71 |
72 | LinkedList l = new LinkedList<>();
73 | if(!unique) {
74 | for(long i = from; i < to; i++) {
75 | l.push((long) randInt(from, to));
76 | bt.insertKey(l.peekFirst(), l.peekFirst().toString(), false);
77 | }
78 | //writeObjectToFile(l, "lfileex.ser");
79 | } else {
80 | //throw new InvalidBTreeStateException("Not yet implemented");
81 | for(long i = from; i < to; i++)
82 | {l.add(i);}
83 |
84 | // randomize
85 | Collections.shuffle(l);
86 |
87 | // add them
88 | for (Long key : l)
89 | {bt.insertKey(key, key.toString(), true);}
90 |
91 | }
92 |
93 | return(l);
94 | }
95 |
96 | /**
97 | * Add values to a B+ Tree from a file
98 | *
99 | * @param filename file to load
100 | * @param unique unique values?
101 | * @param bt tree to add the values
102 | * @return the list of the values in order of insertion
103 | * @throws IOException is thrown when an I/O operation fails
104 | * @throws InvalidBTreeStateException is thrown when there are inconsistencies in the blocks.
105 | * @throws ClassNotFoundException is thrown when the reflection is not able to find the correct class.
106 | */
107 | @SuppressWarnings("unused")
108 | public static LinkedList addToTreeFromList(String filename, boolean unique,
109 | BPlusTree bt)
110 | throws IOException, InvalidBTreeStateException, ClassNotFoundException {
111 |
112 | LinkedList l = loadListFromFile(filename);
113 | for (Long key : l)
114 | {bt.insertKey(key, key.toString(), unique);}
115 | return(l);
116 | }
117 |
118 | /**
119 | * Write object to file (used for testing certain key-sequences)
120 | *
121 | * @param obj Linked list to write
122 | * @param filename filename to dump the object
123 | * @throws IOException is thrown when an I/O operation fails
124 | */
125 | @SuppressWarnings("unused")
126 | public static void writeObjectToFile(LinkedList obj,
127 | String filename) throws IOException {
128 | System.out.println("Writing object to: " + filename);
129 | FileOutputStream fout = new FileOutputStream(filename);
130 | ObjectOutputStream foutStream = new ObjectOutputStream(fout);
131 | foutStream.writeObject(obj);
132 | foutStream.close();
133 | System.out.println("Writing complete to file: " + filename);
134 | }
135 |
136 | /**
137 | * Load linked list object from file (used for testing certain key-sequences)
138 | *
139 | * @param filename file to load the object from
140 | * @return the object itself.
141 | * @throws IOException is thrown when an I/O operation fails
142 | * @throws ClassNotFoundException is thrown when the reflection is not able to find the correct class.
143 | */
144 | private static LinkedList loadListFromFile(String filename)
145 | throws IOException, ClassNotFoundException {
146 | System.out.println("Loading LinkedList object from file: " + filename);
147 | FileInputStream fin = new FileInputStream(filename);
148 | ObjectInputStream finStream = new ObjectInputStream(fin);
149 | @SuppressWarnings("unchecked")
150 | LinkedList l = (LinkedList)finStream.readObject();
151 | finStream.close();
152 | return l;
153 | }
154 |
155 |
156 | /**
157 | * This is a pseudo random number generator for unique
158 | * discreet values using quadratic prime residues.
159 | *
160 | * Taken from @preshing
161 | *
162 | */
163 | @SuppressWarnings("unused")
164 | public static class randQPR {
165 | static final long prime = 4294967291L;
166 | private final long inter_index;
167 | private long index;
168 |
169 | public randQPR(long seed, long seedOffset) {
170 | index = permQPR(permQPR(seed) + 0x682f0161L);
171 | inter_index = permQPR(permQPR(seedOffset) + 0x46790905L);
172 | }
173 |
174 | long permQPR(long x) {
175 | if (x >= prime) {
176 | return x;
177 | }
178 | long residue = (x * x) % prime;
179 | return (x <= prime / 2 ? residue : prime - residue);
180 | }
181 |
182 | public long next() {
183 | return (permQPR(permQPR(index++) + inter_index) ^ 0x5bf03635L);
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/test/java/BPlusTreeTest.java:
--------------------------------------------------------------------------------
1 | import ds.bplus.bptree.BPlusConfiguration;
2 | import ds.bplus.bptree.BPlusTree;
3 | import ds.bplus.bptree.BPlusTreePerformanceCounter;
4 | import ds.bplus.util.Utilities;
5 | import org.junit.After;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 |
9 | import java.util.LinkedList;
10 |
11 | /**
12 | * BPlusTree Tester.
13 | *
14 | * @since Jul 28, 2015
15 | * @version 1.0
16 | */
17 | public class BPlusTreeTest {
18 | private String satelliteValue;
19 | private boolean uniqueEntries;
20 | private boolean verboseResults;
21 | private int startKey;
22 | private int endKey;
23 | private int totalKeys;
24 | private boolean recreateTree;
25 |
26 | private BPlusConfiguration btConf256;
27 | private BPlusConfiguration btConf1024;
28 | private BPlusConfiguration btConf2048;
29 |
30 | private BPlusTreePerformanceCounter bPerf256;
31 | private BPlusTreePerformanceCounter bPerf1024;
32 | private BPlusTreePerformanceCounter bPerf2048;
33 |
34 | private BPlusTree bt256;
35 | private BPlusTree bt1024;
36 | private BPlusTree bt2048;
37 |
38 | @Before
39 | public void before() throws Exception {
40 | System.out.println("Before test");
41 | startKey = 0;
42 | endKey = 10000;
43 | totalKeys = endKey - startKey;
44 | satelliteValue = " ";
45 | }
46 |
47 | @After
48 | public void after() throws Exception {
49 | //System.out.println("After test");
50 | bt256.commitTree();
51 | bt1024.commitTree();
52 | bt2048.commitTree();
53 | }
54 |
55 | /**
56 | *
57 | * This test loads up sequentially a massive key list
58 | * (10^5) onto trees of the following degrees:
59 | *
60 | * - Page sizes: 256, 1024 (1Kb), 2048 (2Kb)
61 | *
62 | * with the following (Key, Value) settings:
63 | *
64 | * - Satellite data size: 20 Bytes each entry
65 | * - Key size: 8 bytes
66 | *
67 | * @throws Exception is thrown when an error is catch'ed in any of the operations performed.
68 | */
69 | @Test
70 | public void testMassSequentialInsertions() throws Exception {
71 | uniqueEntries = true;
72 | verboseResults = false;
73 | recreateTree = true;
74 |
75 | // initialize the configuration
76 | btConf256 = new BPlusConfiguration(256);
77 | btConf1024 = new BPlusConfiguration(1024);
78 | btConf2048 = new BPlusConfiguration(2048);
79 |
80 | // set up the the counters for each tree
81 | bPerf256 = new BPlusTreePerformanceCounter(true);
82 | bPerf1024 = new BPlusTreePerformanceCounter(true);
83 | bPerf2048 = new BPlusTreePerformanceCounter(true);
84 |
85 | // finally setup the tree instances
86 | bt256 = new BPlusTree(btConf256, recreateTree ? "rw+" : "rw",
87 | "tree256.bin", bPerf256);
88 | bt1024 = new BPlusTree(btConf1024, recreateTree ? "rw+" : "rw",
89 | "tree1024.bin", bPerf1024);
90 | bt2048 = new BPlusTree(btConf2048, recreateTree ? "rw+" : "rw",
91 | "tree2048.bin", bPerf2048);
92 |
93 | // now set up the insertions
94 | Utilities.sequentialAddToTree(startKey, endKey,
95 | satelliteValue, uniqueEntries, bt256);
96 |
97 | Utilities.sequentialAddToTree(startKey, endKey,
98 | satelliteValue, uniqueEntries, bt1024);
99 |
100 | Utilities.sequentialAddToTree(startKey, endKey,
101 | satelliteValue, uniqueEntries, bt2048);
102 |
103 | // now search
104 | int found_cnt256 = 0;
105 | int found_cnt1024 = 0;
106 | int found_cnt2048 = 0;
107 |
108 | int[] res256, res1024, res2048;
109 | for(int i = startKey; i < endKey; i++) {
110 | res256 = bPerf256.searchIO(i, uniqueEntries, verboseResults);
111 | res1024 = bPerf1024.searchIO(i, uniqueEntries, verboseResults);
112 | res2048 = bPerf2048.searchIO(i, uniqueEntries, verboseResults);
113 |
114 | if(res256[8] == 1) {found_cnt256++;}
115 | if(res1024[8] == 1) {found_cnt1024++;}
116 | if(res2048[8] == 1) {found_cnt2048++;}
117 | }
118 |
119 | // check result numbers
120 | if(found_cnt256 != totalKeys)
121 | {throw new Exception("BTree with page size: 256 failed to find all keys");}
122 |
123 | if(found_cnt1024 != totalKeys)
124 | {throw new Exception("BTree with page size: 1024 failed to find all keys");}
125 |
126 | if(found_cnt2048 != totalKeys)
127 | {throw new Exception("BTree with page size: 2048 failed to find all keys");}
128 | }
129 |
130 | /**
131 | *
132 | * This test loads up sequentially a massive key list
133 | * (10^5) onto trees of the following degrees:
134 | *
135 | * - Page sizes: 256, 1024 (1Kb), 2048 (2Kb)
136 | *
137 | * with the following (Key, Value) settings:
138 | *
139 | * - Satellite data size: 20 Bytes each entry
140 | * - Key size: 8 bytes
141 | *
142 | * In the end they are deleted as well.
143 | * @throws Exception is thrown when an error is catch'ed in any of the operations performed.
144 | */
145 | @Test
146 | public void testMassSequentialInsertionsWithDelete() throws Exception {
147 | uniqueEntries = true;
148 | verboseResults = false;
149 | recreateTree = true;
150 |
151 | // initialize the configuration
152 | btConf256 = new BPlusConfiguration(256);
153 | btConf1024 = new BPlusConfiguration(1024);
154 | btConf2048 = new BPlusConfiguration(2048);
155 |
156 | // set up the the counters for each tree
157 | bPerf256 = new BPlusTreePerformanceCounter(true);
158 | bPerf1024 = new BPlusTreePerformanceCounter(true);
159 | bPerf2048 = new BPlusTreePerformanceCounter(true);
160 |
161 | // finally setup the tree instances
162 | bt256 = new BPlusTree(btConf256, recreateTree ? "rw+" : "rw",
163 | "tree256.bin", bPerf256);
164 | bt1024 = new BPlusTree(btConf1024, recreateTree ? "rw+" : "rw",
165 | "tree1024.bin", bPerf1024);
166 | bt2048 = new BPlusTree(btConf2048, recreateTree ? "rw+" : "rw",
167 | "tree2048.bin", bPerf2048);
168 |
169 | // now set up the insertions
170 | Utilities.sequentialAddToTree(startKey, endKey,
171 | satelliteValue, uniqueEntries, bt256);
172 |
173 | Utilities.sequentialAddToTree(startKey, endKey,
174 | satelliteValue, uniqueEntries, bt1024);
175 |
176 | Utilities.sequentialAddToTree(startKey, endKey,
177 | satelliteValue, uniqueEntries, bt2048);
178 |
179 | // now search
180 | int found_cnt256 = 0;
181 | int found_cnt1024 = 0;
182 | int found_cnt2048 = 0;
183 |
184 | int[] res256, res1024, res2048;
185 | for(int i = startKey; i < endKey; i++) {
186 | res256 = bPerf256.deleteIO(i, uniqueEntries, verboseResults);
187 | res1024 = bPerf1024.deleteIO(i, uniqueEntries, verboseResults);
188 | res2048 = bPerf2048.deleteIO(i, uniqueEntries, verboseResults);
189 |
190 | //bt256.commitLookupPage();
191 | //bt1024.commitLookupPage();
192 | //bt2048.commitLookupPage();
193 | if (res256[8] == 1) {
194 | found_cnt256++;
195 | }
196 | if (res1024[8] == 1) {
197 | found_cnt1024++;
198 | }
199 | if (res2048[8] == 1) {
200 | found_cnt2048++;
201 | }
202 | }
203 |
204 | // check result numbers
205 | if(found_cnt256 != totalKeys)
206 | {throw new Exception("BTree with page size: 256 failed to find all keys");}
207 |
208 | if(found_cnt1024 != totalKeys)
209 | {throw new Exception("BTree with page size: 1024 failed to find all keys");}
210 |
211 | if(found_cnt2048 != totalKeys)
212 | {throw new Exception("BTree with page size: 2048 failed to find all keys");}
213 | }
214 |
215 | /**
216 | * This test loads up a massive unique key list in
217 | * random order (10^5) onto trees of the following degrees:
218 | *
219 | * - Page sizes: 256, 1024 (1Kb), 2048 (2Kb)
220 | *
221 | * with the following (Key, Value) settings:
222 | *
223 | * - Satellite data size: 20
224 | * @throws Exception is thrown when an error is catch'ed in any of the operations performed.
225 | */
226 | @Test
227 | public void testMassRandomUniqueInsertions() throws Exception {
228 | uniqueEntries = true;
229 | verboseResults = false;
230 | recreateTree = true;
231 |
232 | LinkedList bt256val, bt1024val, bt2048val;
233 |
234 |
235 | // initialize the configuration
236 | btConf256 = new BPlusConfiguration(256);
237 | btConf1024 = new BPlusConfiguration(1024);
238 | btConf2048 = new BPlusConfiguration(2048);
239 |
240 | // set up the the counters for each tree
241 | bPerf256 = new BPlusTreePerformanceCounter(true);
242 | bPerf1024 = new BPlusTreePerformanceCounter(true);
243 | bPerf2048 = new BPlusTreePerformanceCounter(true);
244 |
245 | // finally setup the tree instances
246 | bt256 = new BPlusTree(btConf256, recreateTree ? "rw+" : "rw",
247 | "tree256.bin", bPerf256);
248 | bt1024 = new BPlusTree(btConf1024, recreateTree ? "rw+" : "rw",
249 | "tree1024.bin", bPerf1024);
250 | bt2048 = new BPlusTree(btConf1024, recreateTree ? "rw+" : "rw",
251 | "tree2048.bin", bPerf2048);
252 |
253 | // randomly add non-unique insertions
254 | bt256val = Utilities.fuzzyAddToTree(startKey, endKey,
255 | uniqueEntries, bt256);
256 |
257 | bt1024val = Utilities.fuzzyAddToTree(startKey, endKey,
258 | uniqueEntries, bt1024);
259 | bt2048val = Utilities.fuzzyAddToTree(startKey, endKey,
260 | uniqueEntries, bt2048);
261 |
262 | // now search
263 | int found_cnt256 = 0;
264 | int found_cnt1024 = 0;
265 | int found_cnt2048 = 0;
266 |
267 | int[] res256, res1024, res2048;
268 |
269 | System.out.println("\n--> Dataset size: " + endKey + "\n");
270 | for(int i = startKey; i < endKey; i++) {
271 | res256 = bPerf256.searchIO(bt256val.pop(), uniqueEntries,
272 | verboseResults);
273 | res1024 = bPerf1024.searchIO(bt1024val.pop(), uniqueEntries,
274 | verboseResults);
275 | res2048 = bPerf2048.searchIO(bt2048val.pop(), uniqueEntries,
276 | verboseResults);
277 |
278 | if(res256[8] == 1) {found_cnt256++;}
279 | if(res1024[8] == 1) {found_cnt1024++;}
280 | if(res2048[8] == 1) {found_cnt2048++;}
281 | }
282 |
283 | System.out.println("Total pages for bt256 in the end: " +
284 | bt256.getTotalTreePages());
285 | System.out.println("Total pages for bt1024 in the end: " +
286 | bt1024.getTotalTreePages());
287 | System.out.println("Total pages for bt2048 in the end: " +
288 | bt2048.getTotalTreePages());
289 |
290 | // check result numbers
291 | if(found_cnt256 != totalKeys)
292 | {throw new Exception("BTree with page size: 256 failed to find all keys");}
293 |
294 | if(found_cnt1024 != totalKeys)
295 | {throw new Exception("BTree with page size: 1024 failed to find all keys");}
296 |
297 | if(found_cnt2048 != totalKeys)
298 | {throw new Exception("BTree with page size: 2048 failed to find all keys");}
299 | }
300 |
301 | /**
302 | * This test loads up a massive non-unique key list in
303 | * random order (10^5) onto trees of the following degrees:
304 | *
305 | * - Page sizes: 256, 1024 (1Kb), 2048 (2Kb)
306 | *
307 | * with the following (Key, Value) settings:
308 | *
309 | * - Satellite data size: 20 Bytes each entry
310 | * - Key size: 8 bytes
311 | *
312 | * After insertion each of the keys are searched.
313 | * @throws Exception is thrown when an error is catch'ed in any of the operations performed.
314 | */
315 | @Test
316 | public void testMassRandomInsertionsWithSearch() throws Exception {
317 | uniqueEntries = false;
318 | verboseResults = false;
319 | recreateTree = true;
320 |
321 | LinkedList bt256val, bt1024val, bt2048val;
322 |
323 |
324 | // initialize the configuration
325 | btConf256 = new BPlusConfiguration(256);
326 | btConf1024 = new BPlusConfiguration(1024);
327 | btConf2048 = new BPlusConfiguration(2048);
328 |
329 | // set up the the counters for each tree
330 | bPerf256 = new BPlusTreePerformanceCounter(true);
331 | bPerf1024 = new BPlusTreePerformanceCounter(true);
332 | bPerf2048 = new BPlusTreePerformanceCounter(true);
333 |
334 | // finally setup the tree instances
335 | bt256 = new BPlusTree(btConf256, recreateTree ? "rw+" : "rw",
336 | "tree256.bin", bPerf256);
337 | bt1024 = new BPlusTree(btConf1024, recreateTree ? "rw+" : "rw",
338 | "tree1024.bin", bPerf1024);
339 | bt2048 = new BPlusTree(btConf1024, recreateTree ? "rw+" : "rw",
340 | "tree2048.bin", bPerf2048);
341 |
342 | // randomly add non-unique insertions
343 | bt256val = Utilities.fuzzyAddToTree(startKey, endKey,
344 | uniqueEntries, bt256);
345 |
346 |
347 | //bt256val = Utilities.addToTreeFromList("list.ser", satelliteValue, uniqueEntries, bt256);
348 |
349 | //bt1024val = Utilities.addToTreeFromList("del.ser", satelliteValue, uniqueEntries, bt1024);
350 | //bt1024val = Utilities.addToTreeFromList("delsmall.ser", satelliteValue, uniqueEntries, bt1024);
351 | //bt1024val = Utilities.addToTreeFromList("lfile.ser", satelliteValue, uniqueEntries, bt1024);
352 | //bt1024val = Utilities.addToTreeFromList("lfileex.ser", satelliteValue, uniqueEntries, bt1024);
353 |
354 | bt1024val = Utilities.fuzzyAddToTree(startKey, endKey,
355 | uniqueEntries, bt1024);
356 | bt2048val = Utilities.fuzzyAddToTree(startKey, endKey,
357 | uniqueEntries, bt2048);
358 |
359 | // now search
360 | int found_cnt256 = 0;
361 | int found_cnt1024 = 0;
362 | int found_cnt2048 = 0;
363 |
364 | int[] res256, res1024, res2048;
365 | //Utilities.writeObjectToFile(bt1024val, "delsmall.ser");
366 | //bPerf1024.searchIO(2, false, false);
367 | //bPerf1024.searchIO(0, false, false);
368 | //endKey = bt1024val.size();
369 |
370 |
371 | System.out.println("\n--> Dataset size: " + endKey + "\n");
372 | for(int i = startKey; i < endKey; i++) {
373 | res256 = bPerf256.searchIO(bt256val.pop(), uniqueEntries,
374 | verboseResults);
375 | res1024 = bPerf1024.searchIO(bt1024val.pop(), uniqueEntries,
376 | verboseResults);
377 | res2048 = bPerf2048.searchIO(bt2048val.pop(), uniqueEntries,
378 | verboseResults);
379 |
380 | if(res256[8] == 1) {found_cnt256++;}
381 | if(res1024[8] == 1) {found_cnt1024++;}
382 | if(res2048[8] == 1) {found_cnt2048++;}
383 | }
384 |
385 | System.out.println("Total pages for bt256 in the end: " +
386 | bt256.getTotalTreePages());
387 | System.out.println("Total pages for bt1024 in the end: " +
388 | bt1024.getTotalTreePages());
389 | System.out.println("Total pages for bt2048 in the end: " +
390 | bt2048.getTotalTreePages());
391 |
392 | // check result numbers
393 | if(found_cnt256 != totalKeys)
394 | {throw new Exception("BTree with page size: 256 failed to find all keys");}
395 |
396 | if(found_cnt1024 != totalKeys)
397 | {throw new Exception("BTree with page size: 1024 failed to find all keys");}
398 |
399 | if(found_cnt2048 != totalKeys)
400 | {throw new Exception("BTree with page size: 2048 failed to find all keys");}
401 | }
402 |
403 | /**
404 | * This test loads up a massive non-unique key list in
405 | * random order (10^5) onto trees of the following degrees:
406 | *
407 | * - Page sizes: 256, 1024 (1Kb), 2048 (2Kb)
408 | *
409 | * with the following (Key, Value) settings:
410 | *
411 | * - Satellite data size: 20 Bytes each entry
412 | * - Key size: 8 bytes
413 | *
414 | * After insertion they are deleted in the same order as they were
415 | * put in.
416 | *
417 | * @throws Exception is thrown when an error is catch'ed in any of the operations performed.
418 | */
419 | @Test
420 | public void testMassRandomInsertionsWithDelete() throws Exception {
421 | uniqueEntries = false;
422 | verboseResults = false;
423 | recreateTree = true;
424 |
425 | LinkedList bt256val, bt1024val, bt2048val;
426 |
427 |
428 | // initialize the configuration
429 | btConf256 = new BPlusConfiguration(256);
430 | btConf1024 = new BPlusConfiguration(1024);
431 | btConf2048 = new BPlusConfiguration(2048);
432 |
433 | // set up the the counters for each tree
434 | bPerf256 = new BPlusTreePerformanceCounter(true);
435 | bPerf1024 = new BPlusTreePerformanceCounter(true);
436 | bPerf2048 = new BPlusTreePerformanceCounter(true);
437 |
438 | // finally setup the tree instances
439 | bt256 = new BPlusTree(btConf256, recreateTree ? "rw+" : "rw",
440 | "tree256.bin", bPerf256);
441 | bt1024 = new BPlusTree(btConf1024, recreateTree ? "rw+" : "rw",
442 | "tree1024.bin", bPerf1024);
443 | bt2048 = new BPlusTree(btConf2048, recreateTree ? "rw+" : "rw",
444 | "tree2048.bin", bPerf2048);
445 |
446 | // randomly add non-unique insertions
447 | bt256val = Utilities.fuzzyAddToTree(startKey, endKey,
448 | uniqueEntries, bt256);
449 |
450 | bt1024val = Utilities.fuzzyAddToTree(startKey, endKey,
451 | uniqueEntries, bt1024);
452 | bt2048val = Utilities.fuzzyAddToTree(startKey, endKey,
453 | uniqueEntries, bt2048);
454 |
455 | // our counters
456 | int found_cnt256 = 0;
457 | int found_cnt1024 = 0;
458 | int found_cnt2048 = 0;
459 |
460 | int[] res256, res1024, res2048;
461 |
462 | //System.out.println("\n--> Dataset size: " + endKey + "\n");
463 |
464 | for(int i = startKey; i < endKey; i++) {
465 | res256 = bPerf256.deleteIO(bt256val.pop(), true,
466 | verboseResults);
467 | res1024 = bPerf1024.deleteIO(bt1024val.pop(), true,
468 | verboseResults);
469 | res2048 = bPerf2048.deleteIO(bt2048val.pop(), true,
470 | verboseResults);
471 |
472 | if(res256[8] == 1) {found_cnt256++;}
473 | if(res1024[8] == 1) {found_cnt1024++;}
474 | if(res2048[8] == 1) {found_cnt2048++;}
475 | }
476 |
477 | System.out.println("Total pages for bt256 in the end: " + bt256.getTotalTreePages());
478 | System.out.println("Total pages for bt1024 in the end: " + bt1024.getTotalTreePages());
479 | System.out.println("Total pages for bt2048 in the end: " + bt2048.getTotalTreePages());
480 |
481 | // check result numbers
482 | if(found_cnt256 != totalKeys)
483 | {throw new Exception("BTree with page size: 256 failed to delete all keys");}
484 |
485 | if(found_cnt1024 != totalKeys)
486 | {throw new Exception("BTree with page size: 1024 failed to delete all keys");}
487 |
488 | if(found_cnt2048 != totalKeys)
489 | {throw new Exception("BTree with page size: 2048 failed to delete all keys");}
490 |
491 | }
492 |
493 | }
494 |
--------------------------------------------------------------------------------