1 /*******************************************************************************.
2 * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others
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
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 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.ctf
.core
.event
.io
;
18 import java
.nio
.BufferUnderflowException
;
19 import java
.nio
.ByteBuffer
;
20 import java
.nio
.ByteOrder
;
22 import org
.eclipse
.jdt
.annotation
.NonNull
;
23 import org
.eclipse
.tracecompass
.ctf
.core
.trace
.CTFReaderException
;
26 * <b><u>BitBuffer</u></b>
28 * A bitwise buffer capable of accessing fields with bit offsets.
32 public final class BitBuffer
{
34 // ------------------------------------------------------------------------
36 // ------------------------------------------------------------------------
38 /* default bit width */
39 private static final int BIT_CHAR
= Byte
.SIZE
; // yum
40 private static final int BYTE_MASK
= (1 << BIT_CHAR
) - 1;
41 private static final int BIT_SHORT
= Short
.SIZE
;
42 private static final int SHORT_MASK
= (1 << BIT_SHORT
) - 1;
43 private static final int BIT_INT
= Integer
.SIZE
;
44 private static final long INT_MASK
= (1L << BIT_INT
) - 1;
45 private static final int BIT_LONG
= Long
.SIZE
;
47 // ------------------------------------------------------------------------
49 // ------------------------------------------------------------------------
51 private final @NonNull ByteBuffer fBuffer
;
52 private final long fBitCapacity
;
55 * Bit-buffer's position, maximum value = Integer.MAX_VALUE * 8
57 private long fPosition
;
58 private ByteOrder fByteOrder
;
60 // ------------------------------------------------------------------------
62 // ------------------------------------------------------------------------
64 * Default constructor, makes a big-endian buffer
66 @SuppressWarnings("null")
68 this(ByteBuffer
.allocateDirect(0), ByteOrder
.BIG_ENDIAN
);
72 * Constructor, makes a big-endian buffer
75 * the bytebuffer to read
77 public BitBuffer(@NonNull ByteBuffer buf
) {
78 this(buf
, ByteOrder
.BIG_ENDIAN
);
82 * Constructor that is fully parameterizable
87 * the byte order (big-endian, little-endian, network?)
89 public BitBuffer(@NonNull ByteBuffer buf
, ByteOrder order
) {
93 fBitCapacity
= (long) fBuffer
.capacity() * BIT_CHAR
;
96 private void resetPosition() {
100 // ------------------------------------------------------------------------
101 // 'Get' operations on buffer
102 // ------------------------------------------------------------------------
105 * Relative <i>get</i> method for reading 32-bit integer.
107 * Reads next four bytes from the current bit position according to current
110 * @return The int value (signed) read from the buffer
111 * @throws CTFReaderException
112 * An error occurred reading the long. This exception can be
113 * raised if the buffer tries to read out of bounds
115 public int getInt() throws CTFReaderException
{
116 return getInt(BIT_INT
, true);
120 * Relative <i>get</i> method for reading 64-bit integer.
122 * Reads next eight bytes from the current bit position according to current
125 * @return The long value (signed) read from the buffer
126 * @throws CTFReaderException
127 * An error occurred reading the long. This exception can be
128 * raised if the buffer tries to read out of bounds
131 public long getLong() throws CTFReaderException
{
132 return get(BIT_LONG
, true);
136 * Relative <i>get</i> method for reading long of <i>length</i> bits.
138 * Reads <i>length</i> bits starting at the current position. The result is
139 * signed extended if <i>signed</i> is true. The current position is
140 * increased of <i>length</i> bits.
143 * The length in bits of this integer
145 * The sign extended flag
146 * @return The long value read from the buffer
147 * @throws CTFReaderException
148 * An error occurred reading the data. If more than 64 bits at a
149 * time are read, or the buffer is read beyond its end, this
150 * exception will be raised.
153 public long get(int length
, boolean signed
) throws CTFReaderException
{
154 if (length
> BIT_LONG
) {
155 throw new CTFReaderException("Cannot read a long longer than 64 bits. Rquested: " + length
); //$NON-NLS-1$
157 if (length
> BIT_INT
) {
158 final int highShift
= length
- BIT_INT
;
160 long b
= getInt(highShift
, false);
162 /* Cast the signed-extended int into a unsigned int. */
164 b
&= (1L << highShift
) - 1L;
166 retVal
= (fByteOrder
== ByteOrder
.BIG_ENDIAN
) ?
((a
<< highShift
) | b
) : ((b
<< BIT_INT
) | a
);
169 int signExtendBits
= BIT_LONG
- length
;
170 retVal
= (retVal
<< signExtendBits
) >> signExtendBits
;
174 long retVal
= getInt(length
, signed
);
175 return (signed ? retVal
: (retVal
& INT_MASK
));
179 * Relative bulk <i>get</i> method.
182 * This method transfers <strong>bytes</strong> from this buffer into the
183 * given destination array. This method currently only supports reads
184 * aligned to 8 bytes. It is up to the developer to shift the bits in
185 * post-processing to do unaligned reads.
188 * the bytes to write to
189 * @throws BufferUnderflowException
190 * - If there are fewer than length bytes remaining in this
194 public void get(@NonNull byte[] dst
) {
195 fBuffer
.position((int) (fPosition
/ BIT_CHAR
));
197 fPosition
+= dst
.length
* BIT_CHAR
;
201 * Relative <i>get</i> method for reading integer of <i>length</i> bits.
203 * Reads <i>length</i> bits starting at the current position. The result is
204 * signed extended if <i>signed</i> is true. The current position is
205 * increased of <i>length</i> bits.
208 * The length in bits of this integer
210 * The sign extended flag
211 * @return The int value read from the buffer
212 * @throws CTFReaderException
213 * An error occurred reading the data. When the buffer is read
214 * beyond its end, this exception will be raised.
216 private int getInt(int length
, boolean signed
) throws CTFReaderException
{
218 /* Nothing to read. */
223 /* Validate that the buffer has enough bits. */
224 if (!canRead(length
)) {
225 throw new CTFReaderException("Cannot read the integer, " + //$NON-NLS-1$
226 "the buffer does not have enough remaining space. " + //$NON-NLS-1$
227 "Requested:" + length
); //$NON-NLS-1$
230 /* Get the value from the byte buffer. */
232 boolean gotIt
= false;
235 * Try a fast read when the position is byte-aligned by using
236 * java.nio.ByteBuffer's native methods
239 * A faster alignment detection as the compiler cannot guaranty that pos
240 * is always positive.
242 if ((fPosition
& (BitBuffer
.BIT_CHAR
- 1)) == 0) {
244 case BitBuffer
.BIT_CHAR
:
246 val
= fBuffer
.get((int) (fPosition
/ BIT_CHAR
));
248 val
= val
& BYTE_MASK
;
253 case BitBuffer
.BIT_SHORT
:
255 val
= fBuffer
.getShort((int) (fPosition
/ BIT_CHAR
));
257 val
= val
& SHORT_MASK
;
262 case BitBuffer
.BIT_INT
:
264 val
= fBuffer
.getInt((int) (fPosition
/ BIT_CHAR
));
273 /* When not byte-aligned, fall-back to a general decoder. */
275 // Nothing read yet: use longer methods
276 if (fByteOrder
== ByteOrder
.LITTLE_ENDIAN
) {
277 val
= getIntLE(fPosition
, length
, signed
);
279 val
= getIntBE(fPosition
, length
, signed
);
287 private int getIntBE(long index
, int length
, boolean signed
) {
288 if ((length
<= 0) || (length
> BIT_INT
)) {
289 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
291 long end
= index
+ length
;
292 int startByte
= (int) (index
/ BIT_CHAR
);
293 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
294 int currByte
, lshift
, cshift
, mask
, cmask
, cache
;
297 currByte
= startByte
;
298 cache
= fBuffer
.get(currByte
) & BYTE_MASK
;
299 boolean isNeg
= (cache
& (1 << (BIT_CHAR
- (index
% BIT_CHAR
) - 1))) != 0;
300 if (signed
&& isNeg
) {
303 if (startByte
== (endByte
- 1)) {
304 cmask
= cache
>>> ((BIT_CHAR
- (end
% BIT_CHAR
)) % BIT_CHAR
);
305 if (((length
) % BIT_CHAR
) > 0) {
306 mask
= ~
((~
0) << length
);
313 cshift
= (int) (index
% BIT_CHAR
);
315 mask
= ~
((~
0) << (BIT_CHAR
- cshift
));
316 cmask
= cache
& mask
;
317 lshift
= BIT_CHAR
- cshift
;
322 for (; currByte
< (endByte
- 1); currByte
++) {
324 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
326 lshift
= (int) (end
% BIT_CHAR
);
328 mask
= ~
((~
0) << lshift
);
329 cmask
= fBuffer
.get(currByte
) & BYTE_MASK
;
330 cmask
>>>= BIT_CHAR
- lshift
;
336 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
341 private int getIntLE(long index
, int length
, boolean signed
) {
342 if ((length
<= 0) || (length
> BIT_INT
)) {
343 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
345 long end
= index
+ length
;
346 int startByte
= (int) (index
/ BIT_CHAR
);
347 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
348 int currByte
, lshift
, cshift
, mask
, cmask
, cache
, mod
;
351 currByte
= endByte
- 1;
352 cache
= fBuffer
.get(currByte
) & BYTE_MASK
;
353 mod
= (int) (end
% BIT_CHAR
);
354 lshift
= (mod
> 0) ? mod
: BIT_CHAR
;
355 boolean isNeg
= (cache
& (1 << (lshift
- 1))) != 0;
356 if (signed
&& isNeg
) {
359 if (startByte
== (endByte
- 1)) {
360 cmask
= cache
>>> (index
% BIT_CHAR
);
361 if (((length
) % BIT_CHAR
) > 0) {
362 mask
= ~
((~
0) << length
);
369 cshift
= (int) (end
% BIT_CHAR
);
371 mask
= ~
((~
0) << cshift
);
372 cmask
= cache
& mask
;
377 for (; currByte
>= (startByte
+ 1); currByte
--) {
379 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
381 lshift
= (int) (index
% BIT_CHAR
);
383 mask
= ~
((~
0) << (BIT_CHAR
- lshift
));
384 cmask
= fBuffer
.get(currByte
) & BYTE_MASK
;
387 value
<<= (BIT_CHAR
- lshift
);
391 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
396 // ------------------------------------------------------------------------
397 // 'Put' operations on buffer
398 // ------------------------------------------------------------------------
401 * Relative <i>put</i> method to write signed 32-bit integer.
403 * Write four bytes starting from current bit position in the buffer
404 * according to the current byte order. The current position is increased of
405 * <i>length</i> bits.
408 * The int value to write
409 * @throws CTFReaderException
410 * An error occurred writing the data. If the buffer is written
411 * beyond its end, this exception will be raised.
413 public void putInt(int value
) throws CTFReaderException
{
414 putInt(BIT_INT
, value
);
418 * Relative <i>put</i> method to write <i>length</i> bits integer.
420 * Writes <i>length</i> lower-order bits from the provided <i>value</i>,
421 * starting from current bit position in the buffer. Sequential bytes are
422 * written according to the current byte order. The sign bit is carried to
423 * the MSB if signed is true. The sign bit is included in <i>length</i>. The
424 * current position is increased of <i>length</i>.
427 * The number of bits to write
430 * @throws CTFReaderException
431 * An error occurred writing the data. If the buffer is written
432 * beyond its end, this exception will be raised.
434 public void putInt(int length
, int value
) throws CTFReaderException
{
435 final long curPos
= fPosition
;
437 if (!canRead(length
)) {
438 throw new CTFReaderException("Cannot write to bitbuffer, " //$NON-NLS-1$
439 + "insufficient space. Requested: " + length
); //$NON-NLS-1$
444 if (fByteOrder
== ByteOrder
.LITTLE_ENDIAN
) {
445 putIntLE(curPos
, length
, value
);
447 putIntBE(curPos
, length
, value
);
452 private void putIntBE(long index
, int length
, int value
) {
453 if ((length
<= 0) || (length
> BIT_INT
)) {
454 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
456 long end
= index
+ length
;
457 int startByte
= (int) (index
/ BIT_CHAR
);
458 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
459 int currByte
, lshift
, cshift
, mask
, cmask
;
460 int correctedValue
= value
;
463 * mask v high bits. Works for unsigned and two complement signed
464 * numbers which value do not overflow on length bits.
467 if (length
< BIT_INT
) {
468 correctedValue
&= ~
(~
0 << length
);
472 if (startByte
== (endByte
- 1)) {
473 lshift
= (int) ((BIT_CHAR
- (end
% BIT_CHAR
)) % BIT_CHAR
);
474 mask
= ~
((~
0) << lshift
);
475 if ((index
% BIT_CHAR
) > 0) {
476 mask
|= (~
(0)) << (BIT_CHAR
- (index
% BIT_CHAR
));
478 cmask
= correctedValue
<< lshift
;
480 * low bits are cleared because of left-shift and high bits are
484 int b
= fBuffer
.get(startByte
) & BYTE_MASK
;
485 fBuffer
.put(startByte
, (byte) ((b
& mask
) | cmask
));
489 /* head byte contains MSB */
490 currByte
= endByte
- 1;
491 cshift
= (int) (end
% BIT_CHAR
);
493 lshift
= BIT_CHAR
- cshift
;
494 mask
= ~
((~
0) << lshift
);
495 cmask
= correctedValue
<< lshift
;
497 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
498 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
499 correctedValue
>>>= cshift
;
504 for (; currByte
>= (startByte
+ 1); currByte
--) {
505 fBuffer
.put(currByte
, (byte) correctedValue
);
506 correctedValue
>>>= BIT_CHAR
;
508 /* end byte contains LSB */
509 if ((index
% BIT_CHAR
) > 0) {
510 mask
= (~
0) << (BIT_CHAR
- (index
% BIT_CHAR
));
511 cmask
= correctedValue
& ~mask
;
512 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
513 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
515 fBuffer
.put(currByte
, (byte) correctedValue
);
519 private void putIntLE(long index
, int length
, int value
) {
520 if ((length
<= 0) || (length
> BIT_INT
)) {
521 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
523 long end
= index
+ length
;
524 int startByte
= (int) (index
/ BIT_CHAR
);
525 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
526 int currByte
, lshift
, cshift
, mask
, cmask
;
527 int correctedValue
= value
;
530 * mask v high bits. Works for unsigned and two complement signed
531 * numbers which value do not overflow on length bits.
534 if (length
< BIT_INT
) {
535 correctedValue
&= ~
(~
0 << length
);
539 if (startByte
== (endByte
- 1)) {
540 lshift
= (int) (index
% BIT_CHAR
);
541 mask
= ~
((~
0) << lshift
);
542 if ((end
% BIT_CHAR
) > 0) {
543 mask
|= (~
(0)) << (end
% BIT_CHAR
);
545 cmask
= correctedValue
<< lshift
;
547 * low bits are cleared because of left-shift and high bits are
551 int b
= fBuffer
.get(startByte
) & BYTE_MASK
;
552 fBuffer
.put(startByte
, (byte) ((b
& mask
) | cmask
));
557 currByte
= startByte
;
558 cshift
= (int) (index
% BIT_CHAR
);
560 mask
= ~
((~
0) << cshift
);
561 cmask
= correctedValue
<< cshift
;
563 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
564 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
565 correctedValue
>>>= BIT_CHAR
- cshift
;
570 for (; currByte
< (endByte
- 1); currByte
++) {
571 fBuffer
.put(currByte
, (byte) correctedValue
);
572 correctedValue
>>>= BIT_CHAR
;
575 if ((end
% BIT_CHAR
) > 0) {
576 mask
= (~
0) << (end
% BIT_CHAR
);
577 cmask
= correctedValue
& ~mask
;
578 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
579 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
581 fBuffer
.put(currByte
, (byte) correctedValue
);
585 // ------------------------------------------------------------------------
586 // Buffer attributes handling
587 // ------------------------------------------------------------------------
590 * Can this buffer be read for thus amount of bits?
593 * the length in bits to read
594 * @return does the buffer have enough room to read the next "length"
596 public boolean canRead(int length
) {
597 return ((fPosition
+ length
) <= fBitCapacity
);
601 * Sets the order of the buffer.
604 * The order of the buffer.
606 public void setByteOrder(ByteOrder order
) {
608 fBuffer
.order(order
);
612 * Sets the order of the buffer.
614 * @return The order of the buffer.
616 public ByteOrder
getByteOrder() {
621 * Sets the position in the buffer.
624 * The new position of the buffer.
625 * @throws CTFReaderException
626 * Thrown on out of bounds exceptions
629 public void position(long newPosition
) throws CTFReaderException
{
631 if (newPosition
> fBitCapacity
) {
632 throw new CTFReaderException("Out of bounds exception on a position move, attempting to access position: " + newPosition
); //$NON-NLS-1$
634 fPosition
= newPosition
;
639 * Sets the position in the buffer.
641 * @return order The position of the buffer.
644 public long position() {
649 * Sets the byte buffer
655 public void setByteBuffer(ByteBuffer buf
) {
657 * to avoid "The method setByteBuffer(ByteBuffer) from the type
658 * BitBuffer can be declared as static"
660 long data
= fPosition
;
662 throw new UnsupportedOperationException("Bytebuffers are now final"); //$NON-NLS-1$
667 * Gets the byte buffer
669 * @return The byte buffer
671 public ByteBuffer
getByteBuffer() {
676 * Resets the bitbuffer.
678 public void clear() {
This page took 0.058778 seconds and 6 git commands to generate.