ctf: isolate the abstraction of memory map byte-buffer.
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / internal / tmf / core / statesystem / backends / historytree / HTNode.java
CommitLineData
a52fde77 1/*******************************************************************************
61759503 2 * Copyright (c) 2012, 2013 Ericsson
a52fde77
AM
3 * Copyright (c) 2010, 2011 École Polytechnique de Montréal
4 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
6f4e8ec0 5 *
a52fde77
AM
6 * All rights reserved. This program and the accompanying materials are
7 * made available under the terms of the Eclipse Public License v1.0 which
8 * accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
6f4e8ec0 10 *
a52fde77
AM
11 *******************************************************************************/
12
f9a76cac 13package org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree;
a52fde77
AM
14
15import java.io.IOException;
16import java.io.PrintWriter;
17import java.nio.ByteBuffer;
18import java.nio.ByteOrder;
19import java.nio.channels.FileChannel;
20import java.util.ArrayList;
21import java.util.Collections;
22import java.util.List;
23
6d08acca 24import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
a52fde77 25import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
a52fde77
AM
26import org.eclipse.linuxtools.tmf.core.statevalue.TmfStateValue;
27
28/**
29 * The base class for all the types of nodes that go in the History Tree.
6f4e8ec0 30 *
a52fde77 31 * @author alexmont
a52fde77
AM
32 */
33abstract class HTNode {
34
cb42195c
AM
35 /**
36 * Size of an entry in the data section.
37 *
38 * 16 2 x Timevalue/long (interval start + end)
39 * + 4 int (key)
40 * + 1 byte (type)
41 * + 4 int (valueOffset)
42 */
43 protected static final int DATA_ENTRY_SIZE = 25;
44
a52fde77 45 /* Reference to the History Tree to whom this node belongs */
cb42195c 46 private final HistoryTree ownerTree;
a52fde77
AM
47
48 /* Time range of this node */
49 private final long nodeStart;
50 private long nodeEnd;
51
52 /* Sequence number = position in the node section of the file */
53 private final int sequenceNumber;
54 private int parentSequenceNumber; /* = -1 if this node is the root node */
55
56 /* Where the Strings section begins (from the start of the node */
57 private int stringSectionOffset;
58
59 /* True if this node is closed (and to be committed to disk) */
60 private boolean isDone;
61
62 /* Vector containing all the intervals contained in this node */
cb42195c 63 private final List<HTInterval> intervals;
a52fde77
AM
64
65 HTNode(HistoryTree tree, int seqNumber, int parentSeqNumber, long start) {
66 this.ownerTree = tree;
67 this.nodeStart = start;
68 this.sequenceNumber = seqNumber;
69 this.parentSequenceNumber = parentSeqNumber;
70
cb42195c 71 this.stringSectionOffset = ownerTree.getConfig().getBlockSize();
a52fde77
AM
72 this.isDone = false;
73 this.intervals = new ArrayList<HTInterval>();
74 }
75
76 /**
77 * Reader factory constructor. Build a Node object (of the right type) by
78 * reading a block in the file.
6f4e8ec0 79 *
a52fde77
AM
80 * @param tree
81 * Reference to the HT which will own this node
82 * @param fc
83 * FileChannel to the history file, ALREADY SEEKED at the start
84 * of the node.
85 * @throws IOException
86 */
cb42195c 87 static final HTNode readNode(HistoryTree tree, FileChannel fc)
a52fde77
AM
88 throws IOException {
89 HTNode newNode = null;
90 int res, i;
91
cb42195c 92 ByteBuffer buffer = ByteBuffer.allocate(tree.getConfig().getBlockSize());
a52fde77
AM
93 buffer.order(ByteOrder.LITTLE_ENDIAN);
94 buffer.clear();
95 res = fc.read(buffer);
cb42195c 96 assert (res == tree.getConfig().getBlockSize());
a52fde77
AM
97 buffer.flip();
98
99 /* Read the common header part */
100 byte type = buffer.get();
101 long start = buffer.getLong();
102 long end = buffer.getLong();
103 int seqNb = buffer.getInt();
104 int parentSeqNb = buffer.getInt();
105 int intervalCount = buffer.getInt();
106 int stringSectionOffset = buffer.getInt();
107 boolean done = byteToBool(buffer.get());
108
109 /* Now the rest of the header depends on the node type */
110 switch (type) {
111 case 1:
112 /* Core nodes */
113 newNode = new CoreNode(tree, seqNb, parentSeqNb, start);
114 newNode.readSpecificHeader(buffer);
115 break;
116
117 // TODO implement other node types
118 // case 2:
119 // /* Leaf nodes */
120 //
a52fde77
AM
121 //
122 //
123 // case 3:
124 // /* "Claudette" (extended) nodes */
125 //
a52fde77
AM
126
127 default:
128 /* Unrecognized node type */
129 throw new IOException();
130 }
131
132 /*
133 * At this point, we should be done reading the header and 'buffer'
134 * should only have the intervals left
135 */
136 for (i = 0; i < intervalCount; i++) {
137 newNode.intervals.add(HTInterval.readFrom(buffer));
138 }
139
140 /* Assign the node's other information we have read previously */
141 newNode.nodeEnd = end;
142 newNode.stringSectionOffset = stringSectionOffset;
143 newNode.isDone = done;
144
145 return newNode;
146 }
147
148 final void writeSelf(FileChannel fc) throws IOException {
cb42195c
AM
149 final int blockSize = ownerTree.getConfig().getBlockSize();
150 int curStringsEntryEndPos = blockSize;
a52fde77 151
cb42195c 152 ByteBuffer buffer = ByteBuffer.allocate(blockSize);
a52fde77
AM
153 buffer.order(ByteOrder.LITTLE_ENDIAN);
154 buffer.clear();
155
156 /* Write the common header part */
157 buffer.put(this.getNodeType());
158 buffer.putLong(nodeStart);
159 buffer.putLong(nodeEnd);
160 buffer.putInt(sequenceNumber);
161 buffer.putInt(parentSequenceNumber);
162 buffer.putInt(intervals.size());
163 buffer.putInt(stringSectionOffset);
164 buffer.put(boolToByte(isDone));
165
166 /* Now call the inner method to write the specific header part */
167 this.writeSpecificHeader(buffer);
168
169 /* Back to us, we write the intervals */
170 for (HTInterval interval : intervals) {
cb42195c 171 int size = interval.writeInterval(buffer, curStringsEntryEndPos);
a52fde77
AM
172 curStringsEntryEndPos -= size;
173 }
174
175 /*
176 * Write padding between the end of the Data section and the start of
177 * the Strings section (needed to fill the node in case there is no
178 * Strings section)
179 */
180 while (buffer.position() < stringSectionOffset) {
181 buffer.put((byte) 0);
182 }
183
184 /*
185 * If the offsets were right, the size of the Strings section should be
186 * == to the expected size
187 */
188 assert (curStringsEntryEndPos == stringSectionOffset);
189
190 /* Finally, write everything in the Buffer to disk */
191
192 // if we don't do this, flip() will lose what's after.
cb42195c 193 buffer.position(blockSize);
a52fde77
AM
194
195 buffer.flip();
cb42195c
AM
196 int res = fc.write(buffer);
197 assert (res == blockSize);
198 }
199
200 // ------------------------------------------------------------------------
201 // Accessors
202 // ------------------------------------------------------------------------
203
204 protected HistoryTree getTree() {
205 return ownerTree;
a52fde77
AM
206 }
207
a52fde77
AM
208 long getNodeStart() {
209 return nodeStart;
210 }
211
212 long getNodeEnd() {
213 if (this.isDone) {
214 return nodeEnd;
215 }
216 return 0;
217 }
218
219 int getSequenceNumber() {
220 return sequenceNumber;
221 }
222
223 int getParentSequenceNumber() {
224 return parentSequenceNumber;
225 }
226
227 /**
228 * Change this node's parent. Used when we create a new root node for
229 * example.
230 */
231 void setParentSequenceNumber(int newParent) {
232 parentSequenceNumber = newParent;
233 }
234
235 boolean isDone() {
236 return isDone;
237 }
238
239 /**
240 * Add an interval to this node
6f4e8ec0 241 *
a52fde77
AM
242 * @param newInterval
243 */
244 void addInterval(HTInterval newInterval) {
245 /* Just in case, but should be checked before even calling this function */
246 assert (newInterval.getIntervalSize() <= this.getNodeFreeSpace());
247
248 intervals.add(newInterval);
249
250 /* Update the in-node offset "pointer" */
251 stringSectionOffset -= (newInterval.getStringsEntrySize());
252 }
253
254 /**
255 * We've received word from the containerTree that newest nodes now exist to
256 * our right. (Puts isDone = true and sets the endtime)
6f4e8ec0 257 *
a52fde77
AM
258 * @param endtime
259 * The nodeEnd time that the node will have
260 * @throws TimeRangeException
261 */
262 void closeThisNode(long endtime) {
263 assert (endtime >= this.nodeStart);
a52fde77
AM
264
265 if (intervals.size() > 0) {
266 /*
267 * Sort the intervals by ascending order of their end time. This
268 * speeds up lookups a bit
269 */
270 Collections.sort(intervals);
271
272 /*
273 * Make sure there are no intervals in this node with their EndTime
274 * > the one requested. Only need to check the last one since they
275 * are now sorted
276 */
277 assert (endtime >= intervals.get(intervals.size() - 1).getEndTime());
278 }
279
280 this.isDone = true;
281 this.nodeEnd = endtime;
282 return;
283 }
284
285 /**
286 * The method to fill up the stateInfo (passed on from the Current State
287 * Tree when it does a query on the SHT). We'll replace the data in that
288 * vector with whatever relevant we can find from this node
6f4e8ec0 289 *
a52fde77
AM
290 * @param stateInfo
291 * The same stateInfo that comes from SHT's doQuery()
292 * @param t
293 * The timestamp for which the query is for. Only return
294 * intervals that intersect t.
295 * @throws TimeRangeException
296 */
297 void writeInfoFromNode(List<ITmfStateInterval> stateInfo, long t)
298 throws TimeRangeException {
299 assert (this.isDone); // not sure this will always be the case...
300 int startIndex;
301
302 if (intervals.size() == 0) {
303 return;
304 }
305 startIndex = getStartIndexFor(t);
306
307 for (int i = startIndex; i < intervals.size(); i++) {
308 /*
309 * Now we only have to compare the Start times, since we now the End
310 * times necessarily fit
311 */
312 if (intervals.get(i).getStartTime() <= t) {
313 stateInfo.set(intervals.get(i).getAttribute(), intervals.get(i));
314 }
315 }
316 return;
317 }
318
319 /**
320 * Get a single Interval from the information in this node If the
321 * key/timestamp pair cannot be found, we return null.
6f4e8ec0 322 *
a52fde77
AM
323 * @param key
324 * @param t
325 * @return The Interval containing the information we want, or null if it
326 * wasn't found
327 * @throws TimeRangeException
328 */
329 HTInterval getRelevantInterval(int key, long t) throws TimeRangeException {
330 assert (this.isDone);
331 int startIndex;
1d3d1293 332 HTInterval curInterval;
a52fde77
AM
333
334 if (intervals.size() == 0) {
335 return null;
336 }
337
338 startIndex = getStartIndexFor(t);
339
340 for (int i = startIndex; i < intervals.size(); i++) {
1d3d1293
AM
341 curInterval = intervals.get(i);
342 if (curInterval.getAttribute() == key
343 && curInterval.getStartTime() <= t
344 && curInterval.getEndTime() >= t) {
345 return curInterval;
a52fde77
AM
346 }
347 }
348 /* We didn't find the relevant information in this node */
349 return null;
350 }
351
352 private int getStartIndexFor(long t) throws TimeRangeException {
353 HTInterval dummy;
354 int index;
355
356 /*
357 * Since the intervals are sorted by end time, we can skip all the ones
358 * at the beginning whose end times are smaller than 't'. Java does
359 * provides a .binarySearch method, but its API is quite weird...
360 */
361 dummy = new HTInterval(0, t, 0, TmfStateValue.nullValue());
362 index = Collections.binarySearch(intervals, dummy);
363
364 if (index < 0) {
365 /*
366 * .binarySearch returns a negative number if the exact value was
367 * not found. Here we just want to know where to start searching, we
368 * don't care if the value is exact or not.
369 */
370 index = -index - 1;
371
372 }
373
374 /* Sometimes binarySearch yields weird stuff... */
375 if (index < 0) {
376 index = 0;
377 }
378 if (index >= intervals.size()) {
379 index = intervals.size() - 1;
380 }
381
382 /*
383 * Another API quirkiness, the returned index is the one of the *last*
384 * element of a series of equal endtimes, which happens sometimes. We
385 * want the *first* element of such a series, to read through them
386 * again.
387 */
388 while (index > 0
389 && intervals.get(index - 1).compareTo(intervals.get(index)) == 0) {
390 index--;
391 }
a52fde77
AM
392
393 return index;
394 }
395
396 /**
397 * @return The offset, within the node, where the Data section ends
398 */
399 private int getDataSectionEndOffset() {
cb42195c 400 return this.getTotalHeaderSize() + HTNode.DATA_ENTRY_SIZE * intervals.size();
a52fde77
AM
401 }
402
403 /**
404 * Returns the free space in the node, which is simply put, the
405 * stringSectionOffset - dataSectionOffset
406 */
407 int getNodeFreeSpace() {
408 return stringSectionOffset - this.getDataSectionEndOffset();
409 }
410
411 /**
412 * Returns the current space utilisation of this node, as a percentage.
413 * (used space / total usable space, which excludes the header)
414 */
415 long getNodeUsagePRC() {
cb42195c 416 final int blockSize = ownerTree.getConfig().getBlockSize();
a52fde77 417 float freePercent = (float) this.getNodeFreeSpace()
cb42195c
AM
418 / (float) (blockSize - this.getTotalHeaderSize())
419 * 100F;
a52fde77
AM
420 return (long) (100L - freePercent);
421 }
422
cb42195c 423 protected static final byte boolToByte(boolean thebool) {
a52fde77
AM
424 if (thebool) {
425 return (byte) 1;
426 }
427 return (byte) 0;
428 }
429
cb42195c 430 static final boolean byteToBool(byte thebyte) {
a52fde77
AM
431 return (thebyte == (byte) 1);
432 }
433
434 /**
435 * @name Debugging functions
436 */
437
438 @SuppressWarnings("nls")
439 @Override
440 public String toString() {
441 /* Only used for debugging, shouldn't be externalized */
442 StringBuffer buf = new StringBuffer("Node #" + sequenceNumber + ", ");
443 buf.append(this.toStringSpecific());
444 buf.append(intervals.size() + " intervals (" + this.getNodeUsagePRC()
445 + "% used), ");
446
447 buf.append("[" + this.nodeStart + " - ");
448 if (this.isDone) {
449 buf = buf.append("" + this.nodeEnd + "]");
450 } else {
451 buf = buf.append("...]");
452 }
453 return buf.toString();
454 }
455
456 /**
457 * Debugging function that prints out the contents of this node
6f4e8ec0 458 *
a52fde77
AM
459 * @param writer
460 * PrintWriter in which we will print the debug output
461 */
462 @SuppressWarnings("nls")
463 void debugPrintIntervals(PrintWriter writer) {
464 /* Only used for debugging, shouldn't be externalized */
465 writer.println("Node #" + sequenceNumber + ":");
466
467 /* Array of children */
468 if (this.getNodeType() == 1) { /* Only Core Nodes can have children */
469 CoreNode thisNode = (CoreNode) this;
470 writer.print(" " + thisNode.getNbChildren() + " children");
471 if (thisNode.getNbChildren() >= 1) {
472 writer.print(": [ " + thisNode.getChild(0));
473 for (int i = 1; i < thisNode.getNbChildren(); i++) {
474 writer.print(", " + thisNode.getChild(i));
475 }
476 writer.print(']');
477 }
478 writer.print('\n');
479 }
480
481 /* List of intervals in the node */
482 writer.println(" Intervals contained:");
483 for (int i = 0; i < intervals.size(); i++) {
484 writer.println(intervals.get(i).toString());
485 }
486 writer.println('\n');
487 }
488
cb42195c
AM
489 /**
490 * 1 - byte (type)
491 *
492 * 16 - 2x long (start time, end time)
493 *
494 * 16 - 4x int (seq number, parent seq number, intervalcount, strings
495 * section pos.)
496 *
497 * 1 - byte (done or not)
498 */
499 protected static final int COMMON_HEADER_SIZE = 34;
a52fde77 500
6f4e8ec0
AM
501 // ------------------------------------------------------------------------
502 // Abstract methods
503 // ------------------------------------------------------------------------
a52fde77
AM
504
505 protected abstract byte getNodeType();
506
507 protected abstract int getTotalHeaderSize();
508
509 protected abstract void readSpecificHeader(ByteBuffer buffer);
510
511 protected abstract void writeSpecificHeader(ByteBuffer buffer);
512
513 protected abstract String toStringSpecific();
514}
This page took 0.087884 seconds and 5 git commands to generate.