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
.CTFException
;
26 * <b><u>BitBuffer</u></b>
28 * A bitwise buffer capable of accessing fields with bit offsets.
30 public final class BitBuffer
{
32 // ------------------------------------------------------------------------
34 // ------------------------------------------------------------------------
36 /* default bit width */
37 private static final int BIT_CHAR
= Byte
.SIZE
; // yum
38 private static final int BYTE_MASK
= (1 << BIT_CHAR
) - 1;
39 private static final int BIT_SHORT
= Short
.SIZE
;
40 private static final int SHORT_MASK
= (1 << BIT_SHORT
) - 1;
41 private static final int BIT_INT
= Integer
.SIZE
;
42 private static final long INT_MASK
= (1L << BIT_INT
) - 1;
43 private static final int BIT_LONG
= Long
.SIZE
;
45 // ------------------------------------------------------------------------
47 // ------------------------------------------------------------------------
49 private final @NonNull ByteBuffer fBuffer
;
50 private final long fBitCapacity
;
53 * Bit-buffer's position, maximum value = Integer.MAX_VALUE * 8
55 private long fPosition
;
56 private ByteOrder fByteOrder
;
58 // ------------------------------------------------------------------------
60 // ------------------------------------------------------------------------
62 * Default constructor, makes a big-endian buffer
65 this(ByteBuffer
.allocateDirect(0), ByteOrder
.BIG_ENDIAN
);
69 * Constructor, makes a big-endian buffer
72 * the bytebuffer to read
74 public BitBuffer(@NonNull ByteBuffer buf
) {
75 this(buf
, ByteOrder
.BIG_ENDIAN
);
79 * Constructor that is fully parameterizable
84 * the byte order (big-endian, little-endian, network?)
86 public BitBuffer(@NonNull ByteBuffer buf
, ByteOrder order
) {
90 fBitCapacity
= (long) fBuffer
.capacity() * BIT_CHAR
;
93 private void resetPosition() {
97 // ------------------------------------------------------------------------
98 // 'Get' operations on buffer
99 // ------------------------------------------------------------------------
102 * Relative <i>get</i> method for reading 32-bit integer.
104 * Reads next four bytes from the current bit position according to current
107 * @return The int value (signed) read from the buffer
108 * @throws CTFException
109 * An error occurred reading the long. This exception can be
110 * raised if the buffer tries to read out of bounds
112 public int getInt() throws CTFException
{
113 return getInt(BIT_INT
, true);
117 * Relative <i>get</i> method for reading 64-bit integer.
119 * Reads next eight bytes from the current bit position according to current
122 * @return The long value (signed) read from the buffer
123 * @throws CTFException
124 * An error occurred reading the long. This exception can be
125 * raised if the buffer tries to read out of bounds
127 public long getLong() throws CTFException
{
128 return get(BIT_LONG
, true);
132 * Relative <i>get</i> method for reading long of <i>length</i> bits.
134 * Reads <i>length</i> bits starting at the current position. The result is
135 * signed extended if <i>signed</i> is true. The current position is
136 * increased of <i>length</i> bits.
139 * The length in bits of this integer
141 * The sign extended flag
142 * @return The long value read from the buffer
143 * @throws CTFException
144 * An error occurred reading the data. If more than 64 bits at a
145 * time are read, or the buffer is read beyond its end, this
146 * exception will be raised.
148 public long get(int length
, boolean signed
) throws CTFException
{
149 if (length
> BIT_LONG
) {
150 throw new CTFException("Cannot read a long longer than 64 bits. Rquested: " + length
); //$NON-NLS-1$
152 if (length
> BIT_INT
) {
153 final int highShift
= length
- BIT_INT
;
155 long b
= getInt(highShift
, false);
157 /* Cast the signed-extended int into a unsigned int. */
159 b
&= (1L << highShift
) - 1L;
161 retVal
= (fByteOrder
== ByteOrder
.BIG_ENDIAN
) ?
((a
<< highShift
) | b
) : ((b
<< BIT_INT
) | a
);
164 int signExtendBits
= BIT_LONG
- length
;
165 retVal
= (retVal
<< signExtendBits
) >> signExtendBits
;
169 long retVal
= getInt(length
, signed
);
170 return (signed ? retVal
: (retVal
& INT_MASK
));
174 * Relative bulk <i>get</i> method.
177 * This method transfers <strong>bytes</strong> from this buffer into the
178 * given destination array. This method currently only supports reads
179 * aligned to 8 bytes. It is up to the developer to shift the bits in
180 * post-processing to do unaligned reads.
183 * the bytes to write to
184 * @throws BufferUnderflowException
185 * - If there are fewer than length bytes remaining in this
188 public void get(byte @NonNull [] dst
) {
189 fBuffer
.position((int) (fPosition
/ BIT_CHAR
));
191 fPosition
+= dst
.length
* BIT_CHAR
;
195 * Relative <i>get</i> method for reading integer of <i>length</i> bits.
197 * Reads <i>length</i> bits starting at the current position. The result is
198 * signed extended if <i>signed</i> is true. The current position is
199 * increased of <i>length</i> bits.
202 * The length in bits of this integer
204 * The sign extended flag
205 * @return The int value read from the buffer
206 * @throws CTFException
207 * An error occurred reading the data. When the buffer is read
208 * beyond its end, this exception will be raised.
210 private int getInt(int length
, boolean signed
) throws CTFException
{
212 /* Nothing to read. */
217 /* Validate that the buffer has enough bits. */
218 if (!canRead(length
)) {
219 throw new CTFException("Cannot read the integer, " + //$NON-NLS-1$
220 "the buffer does not have enough remaining space. " + //$NON-NLS-1$
221 "Requested:" + length
+ " Available:" + (fBitCapacity
- fPosition
)); //$NON-NLS-1$ //$NON-NLS-2$
224 /* Get the value from the byte buffer. */
226 boolean gotIt
= false;
229 * Try a fast read when the position is byte-aligned by using
230 * java.nio.ByteBuffer's native methods
233 * A faster alignment detection as the compiler cannot guaranty that pos
234 * is always positive.
236 if ((fPosition
& (BitBuffer
.BIT_CHAR
- 1)) == 0) {
238 case BitBuffer
.BIT_CHAR
:
240 val
= fBuffer
.get((int) (fPosition
/ BIT_CHAR
));
242 val
= val
& BYTE_MASK
;
247 case BitBuffer
.BIT_SHORT
:
249 val
= fBuffer
.getShort((int) (fPosition
/ BIT_CHAR
));
251 val
= val
& SHORT_MASK
;
256 case BitBuffer
.BIT_INT
:
258 val
= fBuffer
.getInt((int) (fPosition
/ BIT_CHAR
));
267 /* When not byte-aligned, fall-back to a general decoder. */
269 // Nothing read yet: use longer methods
270 if (fByteOrder
== ByteOrder
.LITTLE_ENDIAN
) {
271 val
= getIntLE(fPosition
, length
, signed
);
273 val
= getIntBE(fPosition
, length
, signed
);
281 private int getIntBE(long index
, int length
, boolean signed
) {
282 if ((length
<= 0) || (length
> BIT_INT
)) {
283 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
285 long end
= index
+ length
;
286 int startByte
= (int) (index
/ BIT_CHAR
);
287 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
288 int currByte
, lshift
, cshift
, mask
, cmask
, cache
;
291 currByte
= startByte
;
292 cache
= fBuffer
.get(currByte
) & BYTE_MASK
;
293 boolean isNeg
= (cache
& (1 << (BIT_CHAR
- (index
% BIT_CHAR
) - 1))) != 0;
294 if (signed
&& isNeg
) {
297 if (startByte
== (endByte
- 1)) {
298 cmask
= cache
>>> ((BIT_CHAR
- (end
% BIT_CHAR
)) % BIT_CHAR
);
299 if (((length
) % BIT_CHAR
) > 0) {
300 mask
= ~
((~
0) << length
);
307 cshift
= (int) (index
% BIT_CHAR
);
309 mask
= ~
((~
0) << (BIT_CHAR
- cshift
));
310 cmask
= cache
& mask
;
311 lshift
= BIT_CHAR
- cshift
;
316 for (; currByte
< (endByte
- 1); currByte
++) {
318 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
320 lshift
= (int) (end
% BIT_CHAR
);
322 mask
= ~
((~
0) << lshift
);
323 cmask
= fBuffer
.get(currByte
) & BYTE_MASK
;
324 cmask
>>>= BIT_CHAR
- lshift
;
330 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
335 private int getIntLE(long index
, int length
, boolean signed
) {
336 if ((length
<= 0) || (length
> BIT_INT
)) {
337 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
339 long end
= index
+ length
;
340 int startByte
= (int) (index
/ BIT_CHAR
);
341 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
342 int currByte
, lshift
, cshift
, mask
, cmask
, cache
, mod
;
345 currByte
= endByte
- 1;
346 cache
= fBuffer
.get(currByte
) & BYTE_MASK
;
347 mod
= (int) (end
% BIT_CHAR
);
348 lshift
= (mod
> 0) ? mod
: BIT_CHAR
;
349 boolean isNeg
= (cache
& (1 << (lshift
- 1))) != 0;
350 if (signed
&& isNeg
) {
353 if (startByte
== (endByte
- 1)) {
354 cmask
= cache
>>> (index
% BIT_CHAR
);
355 if (((length
) % BIT_CHAR
) > 0) {
356 mask
= ~
((~
0) << length
);
363 cshift
= (int) (end
% BIT_CHAR
);
365 mask
= ~
((~
0) << cshift
);
366 cmask
= cache
& mask
;
371 for (; currByte
>= (startByte
+ 1); currByte
--) {
373 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
375 lshift
= (int) (index
% BIT_CHAR
);
377 mask
= ~
((~
0) << (BIT_CHAR
- lshift
));
378 cmask
= fBuffer
.get(currByte
) & BYTE_MASK
;
381 value
<<= (BIT_CHAR
- lshift
);
385 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
390 // ------------------------------------------------------------------------
391 // 'Put' operations on buffer
392 // ------------------------------------------------------------------------
395 * Relative <i>put</i> method to write signed 32-bit integer.
397 * Write four bytes starting from current bit position in the buffer
398 * according to the current byte order. The current position is increased of
399 * <i>length</i> bits.
402 * The int value to write
403 * @throws CTFException
404 * An error occurred writing the data. If the buffer is written
405 * beyond its end, this exception will be raised.
407 public void putInt(int value
) throws CTFException
{
408 putInt(BIT_INT
, value
);
412 * Relative <i>put</i> method to write <i>length</i> bits integer.
414 * Writes <i>length</i> lower-order bits from the provided <i>value</i>,
415 * starting from current bit position in the buffer. Sequential bytes are
416 * written according to the current byte order. The sign bit is carried to
417 * the MSB if signed is true. The sign bit is included in <i>length</i>. The
418 * current position is increased of <i>length</i>.
421 * The number of bits to write
424 * @throws CTFException
425 * An error occurred writing the data. If the buffer is written
426 * beyond its end, this exception will be raised.
428 public void putInt(int length
, int value
) throws CTFException
{
429 final long curPos
= fPosition
;
431 if (!canRead(length
)) {
432 throw new CTFException("Cannot write to bitbuffer, " //$NON-NLS-1$
433 + "insufficient space. Requested: " + length
); //$NON-NLS-1$
438 if (fByteOrder
== ByteOrder
.LITTLE_ENDIAN
) {
439 putIntLE(curPos
, length
, value
);
441 putIntBE(curPos
, length
, value
);
446 private void putIntBE(long index
, int length
, int value
) {
447 if ((length
<= 0) || (length
> BIT_INT
)) {
448 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
450 long end
= index
+ length
;
451 int startByte
= (int) (index
/ BIT_CHAR
);
452 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
453 int currByte
, lshift
, cshift
, mask
, cmask
;
454 int correctedValue
= value
;
457 * mask v high bits. Works for unsigned and two complement signed
458 * numbers which value do not overflow on length bits.
461 if (length
< BIT_INT
) {
462 correctedValue
&= ~
(~
0 << length
);
466 if (startByte
== (endByte
- 1)) {
467 lshift
= (int) ((BIT_CHAR
- (end
% BIT_CHAR
)) % BIT_CHAR
);
468 mask
= ~
((~
0) << lshift
);
469 if ((index
% BIT_CHAR
) > 0) {
470 mask
|= (~
(0)) << (BIT_CHAR
- (index
% BIT_CHAR
));
472 cmask
= correctedValue
<< lshift
;
474 * low bits are cleared because of left-shift and high bits are
478 int b
= fBuffer
.get(startByte
) & BYTE_MASK
;
479 fBuffer
.put(startByte
, (byte) ((b
& mask
) | cmask
));
483 /* head byte contains MSB */
484 currByte
= endByte
- 1;
485 cshift
= (int) (end
% BIT_CHAR
);
487 lshift
= BIT_CHAR
- cshift
;
488 mask
= ~
((~
0) << lshift
);
489 cmask
= correctedValue
<< lshift
;
491 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
492 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
493 correctedValue
>>>= cshift
;
498 for (; currByte
>= (startByte
+ 1); currByte
--) {
499 fBuffer
.put(currByte
, (byte) correctedValue
);
500 correctedValue
>>>= BIT_CHAR
;
502 /* end byte contains LSB */
503 if ((index
% BIT_CHAR
) > 0) {
504 mask
= (~
0) << (BIT_CHAR
- (index
% BIT_CHAR
));
505 cmask
= correctedValue
& ~mask
;
506 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
507 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
509 fBuffer
.put(currByte
, (byte) correctedValue
);
513 private void putIntLE(long index
, int length
, int value
) {
514 if ((length
<= 0) || (length
> BIT_INT
)) {
515 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
517 long end
= index
+ length
;
518 int startByte
= (int) (index
/ BIT_CHAR
);
519 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
520 int currByte
, lshift
, cshift
, mask
, cmask
;
521 int correctedValue
= value
;
524 * mask v high bits. Works for unsigned and two complement signed
525 * numbers which value do not overflow on length bits.
528 if (length
< BIT_INT
) {
529 correctedValue
&= ~
(~
0 << length
);
533 if (startByte
== (endByte
- 1)) {
534 lshift
= (int) (index
% BIT_CHAR
);
535 mask
= ~
((~
0) << lshift
);
536 if ((end
% BIT_CHAR
) > 0) {
537 mask
|= (~
(0)) << (end
% BIT_CHAR
);
539 cmask
= correctedValue
<< lshift
;
541 * low bits are cleared because of left-shift and high bits are
545 int b
= fBuffer
.get(startByte
) & BYTE_MASK
;
546 fBuffer
.put(startByte
, (byte) ((b
& mask
) | cmask
));
551 currByte
= startByte
;
552 cshift
= (int) (index
% BIT_CHAR
);
554 mask
= ~
((~
0) << cshift
);
555 cmask
= correctedValue
<< cshift
;
557 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
558 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
559 correctedValue
>>>= BIT_CHAR
- cshift
;
564 for (; currByte
< (endByte
- 1); currByte
++) {
565 fBuffer
.put(currByte
, (byte) correctedValue
);
566 correctedValue
>>>= BIT_CHAR
;
569 if ((end
% BIT_CHAR
) > 0) {
570 mask
= (~
0) << (end
% BIT_CHAR
);
571 cmask
= correctedValue
& ~mask
;
572 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
573 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
575 fBuffer
.put(currByte
, (byte) correctedValue
);
579 // ------------------------------------------------------------------------
580 // Buffer attributes handling
581 // ------------------------------------------------------------------------
584 * Can this buffer be read for thus amount of bits?
587 * the length in bits to read
588 * @return does the buffer have enough room to read the next "length"
590 public boolean canRead(int length
) {
591 return ((fPosition
+ length
) <= fBitCapacity
);
595 * Sets the order of the buffer.
598 * The order of the buffer.
600 public void setByteOrder(ByteOrder order
) {
602 fBuffer
.order(order
);
606 * Sets the order of the buffer.
608 * @return The order of the buffer.
610 public ByteOrder
getByteOrder() {
615 * Sets the position in the buffer.
618 * The new position of the buffer.
619 * @throws CTFException
620 * Thrown on out of bounds exceptions
622 public void position(long newPosition
) throws CTFException
{
624 if (newPosition
> fBitCapacity
) {
625 throw new CTFException("Out of bounds exception on a position move, attempting to access position: " + newPosition
); //$NON-NLS-1$
627 fPosition
= newPosition
;
632 * Sets the position in the buffer.
634 * @return order The position of the buffer.
636 public long position() {
641 * Gets the byte buffer
643 * @return The byte buffer
645 public ByteBuffer
getByteBuffer() {
650 * Resets the bitbuffer.
652 public void clear() {
This page took 0.047706 seconds and 5 git commands to generate.