btf: Move plugins to the Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.linuxtools.ctf.core / src / org / eclipse / linuxtools / ctf / core / event / io / BitBuffer.java
1 /*******************************************************************************.
2 * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others
3 *
4 * All rights reserved. This program and the accompanying materials are made
5 * available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Matthew Khouzam - Initial Design and implementation + overhaul
11 * Francis Giraldeau - Initial API and implementation
12 * Philippe Proulx - Some refinement and optimization
13 * Etienne Bergeron <Etienne.Bergeron@gmail.com> - fix zero size read + cleanup
14 *******************************************************************************/
15
16 package org.eclipse.linuxtools.ctf.core.event.io;
17
18 import java.nio.BufferUnderflowException;
19 import java.nio.ByteBuffer;
20 import java.nio.ByteOrder;
21
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.eclipse.linuxtools.ctf.core.trace.CTFReaderException;
24
25 /**
26 * <b><u>BitBuffer</u></b>
27 * <p>
28 * A bitwise buffer capable of accessing fields with bit offsets.
29 *
30 * @since 2.0
31 */
32 public final class BitBuffer {
33
34 // ------------------------------------------------------------------------
35 // Constants
36 // ------------------------------------------------------------------------
37
38 /* default bit width */
39 /** 8 bits to a char */
40 public static final int BIT_CHAR = 8;
41 /** 16 bits to a short */
42 public static final int BIT_SHORT = 16;
43 /** 32 bits to an int */
44 public static final int BIT_INT = 32;
45 /** 32 bits to a float */
46 public static final int BIT_FLOAT = 32;
47 /** 64 bits to a long */
48 public static final int BIT_LONG = 64;
49
50 // ------------------------------------------------------------------------
51 // Attributes
52 // ------------------------------------------------------------------------
53
54 private final @NonNull ByteBuffer fBuffer;
55 private final long fBitCapacity;
56
57 /**
58 * Bit-buffer's position, maximum value = Integer.MAX_VALUE * 8
59 */
60 private long fPosition;
61 private ByteOrder fByteOrder;
62
63 // ------------------------------------------------------------------------
64 // Constructors
65 // ------------------------------------------------------------------------
66 /**
67 * Default constructor, makes a big-endian buffer
68 */
69 @SuppressWarnings("null")
70 public BitBuffer() {
71 this(ByteBuffer.allocateDirect(0), ByteOrder.BIG_ENDIAN);
72 }
73
74 /**
75 * Constructor, makes a big-endian buffer
76 *
77 * @param buf
78 * the bytebuffer to read
79 */
80 public BitBuffer(@NonNull ByteBuffer buf) {
81 this(buf, ByteOrder.BIG_ENDIAN);
82 }
83
84 /**
85 * Constructor that is fully parameterizable
86 *
87 * @param buf
88 * the buffer to read
89 * @param order
90 * the byte order (big-endian, little-endian, network?)
91 */
92 public BitBuffer(@NonNull ByteBuffer buf, ByteOrder order) {
93 fBuffer = buf;
94 setByteOrder(order);
95 resetPosition();
96 fBitCapacity = fBuffer.capacity() * BIT_CHAR;
97 }
98
99 private void resetPosition() {
100 fPosition = 0;
101 }
102
103 // ------------------------------------------------------------------------
104 // 'Get' operations on buffer
105 // ------------------------------------------------------------------------
106
107 /**
108 * Relative <i>get</i> method for reading 32-bit integer.
109 *
110 * Reads next four bytes from the current bit position according to current
111 * byte order.
112 *
113 * @return The int value (signed) read from the buffer
114 * @throws CTFReaderException
115 * An error occurred reading the long. This exception can be
116 * raised if the buffer tries to read out of bounds
117 */
118 public int getInt() throws CTFReaderException {
119 return getInt(BIT_INT, true);
120 }
121
122 /**
123 * Relative <i>get</i> method for reading 64-bit integer.
124 *
125 * Reads next eight bytes from the current bit position according to current
126 * byte order.
127 *
128 * @return The long value (signed) read from the buffer
129 * @throws CTFReaderException
130 * An error occurred reading the long. This exception can be
131 * raised if the buffer tries to read out of bounds
132 * @since 3.0
133 */
134 public long getLong() throws CTFReaderException {
135 return get(BIT_LONG, true);
136 }
137
138 /**
139 * Relative <i>get</i> method for reading long of <i>length</i> bits.
140 *
141 * Reads <i>length</i> bits starting at the current position. The result is
142 * signed extended if <i>signed</i> is true. The current position is
143 * increased of <i>length</i> bits.
144 *
145 * @param length
146 * The length in bits of this integer
147 * @param signed
148 * The sign extended flag
149 * @return The long value read from the buffer
150 * @throws CTFReaderException
151 * An error occurred reading the data. If more than 64 bits at a
152 * time are read, or the buffer is read beyond its end, this
153 * exception will be raised.
154 * @since 3.0
155 */
156 public long get(int length, boolean signed) throws CTFReaderException {
157 if (length > BIT_LONG) {
158 throw new CTFReaderException("Cannot read a long longer than 64 bits. Rquested: " + length); //$NON-NLS-1$
159 }
160 if (length > BIT_INT) {
161 final int highShift = length - BIT_INT;
162 long a = getInt();
163 long b = getInt(highShift, false);
164 long retVal;
165 /* Cast the signed-extended int into a unsigned int. */
166 a &= 0xFFFFFFFFL;
167 b &= (1L << highShift) - 1L;
168
169 retVal = (fByteOrder == ByteOrder.BIG_ENDIAN) ? ((a << highShift) | b) : ((b << BIT_INT) | a);
170 /* sign extend */
171 if (signed) {
172 int signExtendBits = BIT_LONG - length;
173 retVal = (retVal << signExtendBits) >> signExtendBits;
174 }
175 return retVal;
176 }
177 long retVal = getInt(length, signed);
178 return (signed ? retVal : (retVal & 0xFFFFFFFFL));
179 }
180
181 /**
182 * Relative bulk <i>get</i> method.
183 *
184 * <p>
185 * This method transfers <strong>bytes</strong> from this buffer into the
186 * given destination array. This method currently only supports reads
187 * aligned to 8 bytes. It is up to the developer to shift the bits in
188 * post-processing to do unaligned reads.
189 *
190 * @param dst
191 * the bytes to write to
192 * @throws BufferUnderflowException
193 * - If there are fewer than length bytes remaining in this
194 * buffer
195 * @since 3.1
196 */
197 public void get(@NonNull byte[] dst) {
198 fBuffer.position((int) (fPosition / 8));
199 fBuffer.get(dst);
200 fPosition += dst.length * 8;
201 }
202
203 /**
204 * Relative <i>get</i> method for reading integer of <i>length</i> bits.
205 *
206 * Reads <i>length</i> bits starting at the current position. The result is
207 * signed extended if <i>signed</i> is true. The current position is
208 * increased of <i>length</i> bits.
209 *
210 * @param length
211 * The length in bits of this integer
212 * @param signed
213 * The sign extended flag
214 * @return The int value read from the buffer
215 * @throws CTFReaderException
216 * An error occurred reading the data. When the buffer is read
217 * beyond its end, this exception will be raised.
218 */
219 private int getInt(int length, boolean signed) throws CTFReaderException {
220
221 /* Nothing to read. */
222 if (length == 0) {
223 return 0;
224 }
225
226 /* Validate that the buffer has enough bits. */
227 if (!canRead(length)) {
228 throw new CTFReaderException("Cannot read the integer, " + //$NON-NLS-1$
229 "the buffer does not have enough remaining space. " + //$NON-NLS-1$
230 "Requested:" + length); //$NON-NLS-1$
231 }
232
233 /* Get the value from the byte buffer. */
234 int val = 0;
235 boolean gotIt = false;
236
237 /*
238 * Try a fast read when the position is byte-aligned by using
239 * java.nio.ByteBuffer's native methods
240 */
241 /*
242 * A faster alignment detection as the compiler cannot guaranty that pos
243 * is always positive.
244 */
245 if ((fPosition & (BitBuffer.BIT_CHAR - 1)) == 0) {
246 switch (length) {
247 case BitBuffer.BIT_CHAR:
248 // Byte
249 val = fBuffer.get((int) (fPosition / 8));
250 if (!signed) {
251 val = val & 0xff;
252 }
253 gotIt = true;
254 break;
255
256 case BitBuffer.BIT_SHORT:
257 // Word
258 val = fBuffer.getShort((int) (fPosition / 8));
259 if (!signed) {
260 val = val & 0xffff;
261 }
262 gotIt = true;
263 break;
264
265 case BitBuffer.BIT_INT:
266 // Double word
267 val = fBuffer.getInt((int) (fPosition / 8));
268 gotIt = true;
269 break;
270
271 default:
272 break;
273 }
274 }
275
276 /* When not byte-aligned, fall-back to a general decoder. */
277 if (!gotIt) {
278 // Nothing read yet: use longer methods
279 if (fByteOrder == ByteOrder.LITTLE_ENDIAN) {
280 val = getIntLE(fPosition, length, signed);
281 } else {
282 val = getIntBE(fPosition, length, signed);
283 }
284 }
285 fPosition += length;
286
287 return val;
288 }
289
290 private int getIntBE(long index, int length, boolean signed) {
291 if ((length <= 0) || (length > BIT_INT)) {
292 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
293 }
294 long end = index + length;
295 int startByte = (int) (index / BIT_CHAR);
296 int endByte = (int) ((end + (BIT_CHAR - 1)) / BIT_CHAR);
297 int currByte, lshift, cshift, mask, cmask, cache;
298 int value = 0;
299
300 currByte = startByte;
301 cache = fBuffer.get(currByte) & 0xFF;
302 boolean isNeg = (cache & (1 << (BIT_CHAR - (index % BIT_CHAR) - 1))) != 0;
303 if (signed && isNeg) {
304 value = ~0;
305 }
306 if (startByte == (endByte - 1)) {
307 cmask = cache >>> ((BIT_CHAR - (end % BIT_CHAR)) % BIT_CHAR);
308 if (((length) % BIT_CHAR) > 0) {
309 mask = ~((~0) << length);
310 cmask &= mask;
311 }
312 value <<= length;
313 value |= cmask;
314 return value;
315 }
316 cshift = (int) (index % BIT_CHAR);
317 if (cshift > 0) {
318 mask = ~((~0) << (BIT_CHAR - cshift));
319 cmask = cache & mask;
320 lshift = BIT_CHAR - cshift;
321 value <<= lshift;
322 value |= cmask;
323 currByte++;
324 }
325 for (; currByte < (endByte - 1); currByte++) {
326 value <<= BIT_CHAR;
327 value |= fBuffer.get(currByte) & 0xFF;
328 }
329 lshift = (int) (end % BIT_CHAR);
330 if (lshift > 0) {
331 mask = ~((~0) << lshift);
332 cmask = fBuffer.get(currByte) & 0xFF;
333 cmask >>>= BIT_CHAR - lshift;
334 cmask &= mask;
335 value <<= lshift;
336 value |= cmask;
337 } else {
338 value <<= BIT_CHAR;
339 value |= fBuffer.get(currByte) & 0xFF;
340 }
341 return value;
342 }
343
344 private int getIntLE(long index, int length, boolean signed) {
345 if ((length <= 0) || (length > BIT_INT)) {
346 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
347 }
348 long end = index + length;
349 int startByte = (int) (index / BIT_CHAR);
350 int endByte = (int) ((end + (BIT_CHAR - 1)) / BIT_CHAR);
351 int currByte, lshift, cshift, mask, cmask, cache, mod;
352 int value = 0;
353
354 currByte = endByte - 1;
355 cache = fBuffer.get(currByte) & 0xFF;
356 mod = (int) (end % BIT_CHAR);
357 lshift = (mod > 0) ? mod : BIT_CHAR;
358 boolean isNeg = (cache & (1 << (lshift - 1))) != 0;
359 if (signed && isNeg) {
360 value = ~0;
361 }
362 if (startByte == (endByte - 1)) {
363 cmask = cache >>> (index % BIT_CHAR);
364 if (((length) % BIT_CHAR) > 0) {
365 mask = ~((~0) << length);
366 cmask &= mask;
367 }
368 value <<= length;
369 value |= cmask;
370 return value;
371 }
372 cshift = (int) (end % BIT_CHAR);
373 if (cshift > 0) {
374 mask = ~((~0) << cshift);
375 cmask = cache & mask;
376 value <<= cshift;
377 value |= cmask;
378 currByte--;
379 }
380 for (; currByte >= (startByte + 1); currByte--) {
381 value <<= BIT_CHAR;
382 value |= fBuffer.get(currByte) & 0xFF;
383 }
384 lshift = (int) (index % BIT_CHAR);
385 if (lshift > 0) {
386 mask = ~((~0) << (BIT_CHAR - lshift));
387 cmask = fBuffer.get(currByte) & 0xFF;
388 cmask >>>= lshift;
389 cmask &= mask;
390 value <<= (BIT_CHAR - lshift);
391 value |= cmask;
392 } else {
393 value <<= BIT_CHAR;
394 value |= fBuffer.get(currByte) & 0xFF;
395 }
396 return value;
397 }
398
399 // ------------------------------------------------------------------------
400 // 'Put' operations on buffer
401 // ------------------------------------------------------------------------
402
403 /**
404 * Relative <i>put</i> method to write signed 32-bit integer.
405 *
406 * Write four bytes starting from current bit position in the buffer
407 * according to the current byte order. The current position is increased of
408 * <i>length</i> bits.
409 *
410 * @param value
411 * The int value to write
412 * @throws CTFReaderException
413 * An error occurred writing the data. If the buffer is written
414 * beyond its end, this exception will be raised.
415 */
416 public void putInt(int value) throws CTFReaderException {
417 putInt(BIT_INT, value);
418 }
419
420 /**
421 * Relative <i>put</i> method to write <i>length</i> bits integer.
422 *
423 * Writes <i>length</i> lower-order bits from the provided <i>value</i>,
424 * starting from current bit position in the buffer. Sequential bytes are
425 * written according to the current byte order. The sign bit is carried to
426 * the MSB if signed is true. The sign bit is included in <i>length</i>. The
427 * current position is increased of <i>length</i>.
428 *
429 * @param length
430 * The number of bits to write
431 * @param value
432 * The value to write
433 * @throws CTFReaderException
434 * An error occurred writing the data. If the buffer is written
435 * beyond its end, this exception will be raised.
436 */
437 public void putInt(int length, int value) throws CTFReaderException {
438 final long curPos = fPosition;
439
440 if (!canRead(length)) {
441 throw new CTFReaderException("Cannot write to bitbuffer, " //$NON-NLS-1$
442 + "insufficient space. Requested: " + length); //$NON-NLS-1$
443 }
444 if (length == 0) {
445 return;
446 }
447 if (fByteOrder == ByteOrder.LITTLE_ENDIAN) {
448 putIntLE(curPos, length, value);
449 } else {
450 putIntBE(curPos, length, value);
451 }
452 fPosition += length;
453 }
454
455 private void putIntBE(long index, int length, int value) {
456 if ((length <= 0) || (length > BIT_INT)) {
457 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
458 }
459 long end = index + length;
460 int startByte = (int) (index / BIT_CHAR);
461 int endByte = (int) ((end + (BIT_CHAR - 1)) / BIT_CHAR);
462 int currByte, lshift, cshift, mask, cmask;
463 int correctedValue = value;
464
465 /*
466 * mask v high bits. Works for unsigned and two complement signed
467 * numbers which value do not overflow on length bits.
468 */
469
470 if (length < BIT_INT) {
471 correctedValue &= ~(~0 << length);
472 }
473
474 /* sub byte */
475 if (startByte == (endByte - 1)) {
476 lshift = (int) ((BIT_CHAR - (end % BIT_CHAR)) % BIT_CHAR);
477 mask = ~((~0) << lshift);
478 if ((index % BIT_CHAR) > 0) {
479 mask |= (~(0)) << (BIT_CHAR - (index % BIT_CHAR));
480 }
481 cmask = correctedValue << lshift;
482 /*
483 * low bits are cleared because of left-shift and high bits are
484 * already cleared
485 */
486 cmask &= ~mask;
487 int b = fBuffer.get(startByte) & 0xFF;
488 fBuffer.put(startByte, (byte) ((b & mask) | cmask));
489 return;
490 }
491
492 /* head byte contains MSB */
493 currByte = endByte - 1;
494 cshift = (int) (end % BIT_CHAR);
495 if (cshift > 0) {
496 lshift = BIT_CHAR - cshift;
497 mask = ~((~0) << lshift);
498 cmask = correctedValue << lshift;
499 cmask &= ~mask;
500 int b = fBuffer.get(currByte) & 0xFF;
501 fBuffer.put(currByte, (byte) ((b & mask) | cmask));
502 correctedValue >>>= cshift;
503 currByte--;
504 }
505
506 /* middle byte(s) */
507 for (; currByte >= (startByte + 1); currByte--) {
508 fBuffer.put(currByte, (byte) correctedValue);
509 correctedValue >>>= BIT_CHAR;
510 }
511 /* end byte contains LSB */
512 if ((index % BIT_CHAR) > 0) {
513 mask = (~0) << (BIT_CHAR - (index % BIT_CHAR));
514 cmask = correctedValue & ~mask;
515 int b = fBuffer.get(currByte) & 0xFF;
516 fBuffer.put(currByte, (byte) ((b & mask) | cmask));
517 } else {
518 fBuffer.put(currByte, (byte) correctedValue);
519 }
520 }
521
522 private void putIntLE(long index, int length, int value) {
523 if ((length <= 0) || (length > BIT_INT)) {
524 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
525 }
526 long end = index + length;
527 int startByte = (int) (index / BIT_CHAR);
528 int endByte = (int) ((end + (BIT_CHAR - 1)) / BIT_CHAR);
529 int currByte, lshift, cshift, mask, cmask;
530 int correctedValue = value;
531
532 /*
533 * mask v high bits. Works for unsigned and two complement signed
534 * numbers which value do not overflow on length bits.
535 */
536
537 if (length < BIT_INT) {
538 correctedValue &= ~(~0 << length);
539 }
540
541 /* sub byte */
542 if (startByte == (endByte - 1)) {
543 lshift = (int) (index % BIT_CHAR);
544 mask = ~((~0) << lshift);
545 if ((end % BIT_CHAR) > 0) {
546 mask |= (~(0)) << (end % BIT_CHAR);
547 }
548 cmask = correctedValue << lshift;
549 /*
550 * low bits are cleared because of left-shift and high bits are
551 * already cleared
552 */
553 cmask &= ~mask;
554 int b = fBuffer.get(startByte) & 0xFF;
555 fBuffer.put(startByte, (byte) ((b & mask) | cmask));
556 return;
557 }
558
559 /* head byte */
560 currByte = startByte;
561 cshift = (int) (index % BIT_CHAR);
562 if (cshift > 0) {
563 mask = ~((~0) << cshift);
564 cmask = correctedValue << cshift;
565 cmask &= ~mask;
566 int b = fBuffer.get(currByte) & 0xFF;
567 fBuffer.put(currByte, (byte) ((b & mask) | cmask));
568 correctedValue >>>= BIT_CHAR - cshift;
569 currByte++;
570 }
571
572 /* middle byte(s) */
573 for (; currByte < (endByte - 1); currByte++) {
574 fBuffer.put(currByte, (byte) correctedValue);
575 correctedValue >>>= BIT_CHAR;
576 }
577 /* end byte */
578 if ((end % BIT_CHAR) > 0) {
579 mask = (~0) << (end % BIT_CHAR);
580 cmask = correctedValue & ~mask;
581 int b = fBuffer.get(currByte) & 0xFF;
582 fBuffer.put(currByte, (byte) ((b & mask) | cmask));
583 } else {
584 fBuffer.put(currByte, (byte) correctedValue);
585 }
586 }
587
588 // ------------------------------------------------------------------------
589 // Buffer attributes handling
590 // ------------------------------------------------------------------------
591
592 /**
593 * Can this buffer be read for thus amount of bits?
594 *
595 * @param length
596 * the length in bits to read
597 * @return does the buffer have enough room to read the next "length"
598 */
599 public boolean canRead(int length) {
600 return ((fPosition + length) <= fBitCapacity);
601 }
602
603 /**
604 * Sets the order of the buffer.
605 *
606 * @param order
607 * The order of the buffer.
608 */
609 public void setByteOrder(ByteOrder order) {
610 fByteOrder = order;
611 fBuffer.order(order);
612 }
613
614 /**
615 * Sets the order of the buffer.
616 *
617 * @return The order of the buffer.
618 */
619 public ByteOrder getByteOrder() {
620 return fByteOrder;
621 }
622
623 /**
624 * Sets the position in the buffer.
625 *
626 * @param newPosition
627 * The new position of the buffer.
628 * @throws CTFReaderException
629 * Thrown on out of bounds exceptions
630 * @since 3.0
631 */
632 public void position(long newPosition) throws CTFReaderException {
633
634
635 if (newPosition > fBitCapacity) {
636 throw new CTFReaderException("Out of bounds exception on a position move, attempting to access position: " + newPosition); //$NON-NLS-1$
637 }
638 fPosition = newPosition;
639 }
640
641 /**
642 *
643 * Sets the position in the buffer.
644 *
645 * @return order The position of the buffer.
646 * @since 3.0
647 */
648 public long position() {
649 return fPosition;
650 }
651
652 /**
653 * Sets the byte buffer
654 *
655 * @param buf
656 * the byte buffer
657 */
658 @Deprecated
659 public void setByteBuffer(ByteBuffer buf) {
660 /*
661 * to avoid "The method setByteBuffer(ByteBuffer) from the type
662 * BitBuffer can be declared as static"
663 */
664 long data = fPosition;
665 fPosition = data;
666 throw new UnsupportedOperationException("Bytebuffers are now final"); //$NON-NLS-1$
667
668 }
669
670 /**
671 * Gets the byte buffer
672 *
673 * @return The byte buffer
674 */
675 public ByteBuffer getByteBuffer() {
676 return fBuffer;
677 }
678
679 /**
680 * Resets the bitbuffer.
681 */
682 public void clear() {
683 resetPosition();
684 fBuffer.clear();
685 }
686
687 }
This page took 0.045968 seconds and 5 git commands to generate.