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 static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
20 import java
.nio
.BufferUnderflowException
;
21 import java
.nio
.ByteBuffer
;
22 import java
.nio
.ByteOrder
;
24 import org
.eclipse
.jdt
.annotation
.NonNull
;
25 import org
.eclipse
.tracecompass
.ctf
.core
.CTFReaderException
;
28 * <b><u>BitBuffer</u></b>
30 * 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
67 this(checkNotNull(ByteBuffer
.allocateDirect(0)), ByteOrder
.BIG_ENDIAN
);
71 * Constructor, makes a big-endian buffer
74 * the bytebuffer to read
76 public BitBuffer(@NonNull ByteBuffer buf
) {
77 this(buf
, ByteOrder
.BIG_ENDIAN
);
81 * Constructor that is fully parameterizable
86 * the byte order (big-endian, little-endian, network?)
88 public BitBuffer(@NonNull ByteBuffer buf
, ByteOrder order
) {
92 fBitCapacity
= (long) fBuffer
.capacity() * BIT_CHAR
;
95 private void resetPosition() {
99 // ------------------------------------------------------------------------
100 // 'Get' operations on buffer
101 // ------------------------------------------------------------------------
104 * Relative <i>get</i> method for reading 32-bit integer.
106 * Reads next four bytes from the current bit position according to current
109 * @return The int value (signed) read from the buffer
110 * @throws CTFReaderException
111 * An error occurred reading the long. This exception can be
112 * raised if the buffer tries to read out of bounds
114 public int getInt() throws CTFReaderException
{
115 return getInt(BIT_INT
, true);
119 * Relative <i>get</i> method for reading 64-bit integer.
121 * Reads next eight bytes from the current bit position according to current
124 * @return The long value (signed) read from the buffer
125 * @throws CTFReaderException
126 * An error occurred reading the long. This exception can be
127 * raised if the buffer tries to read out of bounds
129 public long getLong() throws CTFReaderException
{
130 return get(BIT_LONG
, true);
134 * Relative <i>get</i> method for reading long of <i>length</i> bits.
136 * Reads <i>length</i> bits starting at the current position. The result is
137 * signed extended if <i>signed</i> is true. The current position is
138 * increased of <i>length</i> bits.
141 * The length in bits of this integer
143 * The sign extended flag
144 * @return The long value read from the buffer
145 * @throws CTFReaderException
146 * An error occurred reading the data. If more than 64 bits at a
147 * time are read, or the buffer is read beyond its end, this
148 * exception will be raised.
150 public long get(int length
, boolean signed
) throws CTFReaderException
{
151 if (length
> BIT_LONG
) {
152 throw new CTFReaderException("Cannot read a long longer than 64 bits. Rquested: " + length
); //$NON-NLS-1$
154 if (length
> BIT_INT
) {
155 final int highShift
= length
- BIT_INT
;
157 long b
= getInt(highShift
, false);
159 /* Cast the signed-extended int into a unsigned int. */
161 b
&= (1L << highShift
) - 1L;
163 retVal
= (fByteOrder
== ByteOrder
.BIG_ENDIAN
) ?
((a
<< highShift
) | b
) : ((b
<< BIT_INT
) | a
);
166 int signExtendBits
= BIT_LONG
- length
;
167 retVal
= (retVal
<< signExtendBits
) >> signExtendBits
;
171 long retVal
= getInt(length
, signed
);
172 return (signed ? retVal
: (retVal
& INT_MASK
));
176 * Relative bulk <i>get</i> method.
179 * This method transfers <strong>bytes</strong> from this buffer into the
180 * given destination array. This method currently only supports reads
181 * aligned to 8 bytes. It is up to the developer to shift the bits in
182 * post-processing to do unaligned reads.
185 * the bytes to write to
186 * @throws BufferUnderflowException
187 * - If there are fewer than length bytes remaining in this
190 public void get(@NonNull byte[] dst
) {
191 fBuffer
.position((int) (fPosition
/ BIT_CHAR
));
193 fPosition
+= dst
.length
* BIT_CHAR
;
197 * Relative <i>get</i> method for reading integer of <i>length</i> bits.
199 * Reads <i>length</i> bits starting at the current position. The result is
200 * signed extended if <i>signed</i> is true. The current position is
201 * increased of <i>length</i> bits.
204 * The length in bits of this integer
206 * The sign extended flag
207 * @return The int value read from the buffer
208 * @throws CTFReaderException
209 * An error occurred reading the data. When the buffer is read
210 * beyond its end, this exception will be raised.
212 private int getInt(int length
, boolean signed
) throws CTFReaderException
{
214 /* Nothing to read. */
219 /* Validate that the buffer has enough bits. */
220 if (!canRead(length
)) {
221 throw new CTFReaderException("Cannot read the integer, " + //$NON-NLS-1$
222 "the buffer does not have enough remaining space. " + //$NON-NLS-1$
223 "Requested:" + length
); //$NON-NLS-1$
226 /* Get the value from the byte buffer. */
228 boolean gotIt
= false;
231 * Try a fast read when the position is byte-aligned by using
232 * java.nio.ByteBuffer's native methods
235 * A faster alignment detection as the compiler cannot guaranty that pos
236 * is always positive.
238 if ((fPosition
& (BitBuffer
.BIT_CHAR
- 1)) == 0) {
240 case BitBuffer
.BIT_CHAR
:
242 val
= fBuffer
.get((int) (fPosition
/ BIT_CHAR
));
244 val
= val
& BYTE_MASK
;
249 case BitBuffer
.BIT_SHORT
:
251 val
= fBuffer
.getShort((int) (fPosition
/ BIT_CHAR
));
253 val
= val
& SHORT_MASK
;
258 case BitBuffer
.BIT_INT
:
260 val
= fBuffer
.getInt((int) (fPosition
/ BIT_CHAR
));
269 /* When not byte-aligned, fall-back to a general decoder. */
271 // Nothing read yet: use longer methods
272 if (fByteOrder
== ByteOrder
.LITTLE_ENDIAN
) {
273 val
= getIntLE(fPosition
, length
, signed
);
275 val
= getIntBE(fPosition
, length
, signed
);
283 private int getIntBE(long index
, int length
, boolean signed
) {
284 if ((length
<= 0) || (length
> BIT_INT
)) {
285 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
287 long end
= index
+ length
;
288 int startByte
= (int) (index
/ BIT_CHAR
);
289 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
290 int currByte
, lshift
, cshift
, mask
, cmask
, cache
;
293 currByte
= startByte
;
294 cache
= fBuffer
.get(currByte
) & BYTE_MASK
;
295 boolean isNeg
= (cache
& (1 << (BIT_CHAR
- (index
% BIT_CHAR
) - 1))) != 0;
296 if (signed
&& isNeg
) {
299 if (startByte
== (endByte
- 1)) {
300 cmask
= cache
>>> ((BIT_CHAR
- (end
% BIT_CHAR
)) % BIT_CHAR
);
301 if (((length
) % BIT_CHAR
) > 0) {
302 mask
= ~
((~
0) << length
);
309 cshift
= (int) (index
% BIT_CHAR
);
311 mask
= ~
((~
0) << (BIT_CHAR
- cshift
));
312 cmask
= cache
& mask
;
313 lshift
= BIT_CHAR
- cshift
;
318 for (; currByte
< (endByte
- 1); currByte
++) {
320 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
322 lshift
= (int) (end
% BIT_CHAR
);
324 mask
= ~
((~
0) << lshift
);
325 cmask
= fBuffer
.get(currByte
) & BYTE_MASK
;
326 cmask
>>>= BIT_CHAR
- lshift
;
332 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
337 private int getIntLE(long index
, int length
, boolean signed
) {
338 if ((length
<= 0) || (length
> BIT_INT
)) {
339 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
341 long end
= index
+ length
;
342 int startByte
= (int) (index
/ BIT_CHAR
);
343 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
344 int currByte
, lshift
, cshift
, mask
, cmask
, cache
, mod
;
347 currByte
= endByte
- 1;
348 cache
= fBuffer
.get(currByte
) & BYTE_MASK
;
349 mod
= (int) (end
% BIT_CHAR
);
350 lshift
= (mod
> 0) ? mod
: BIT_CHAR
;
351 boolean isNeg
= (cache
& (1 << (lshift
- 1))) != 0;
352 if (signed
&& isNeg
) {
355 if (startByte
== (endByte
- 1)) {
356 cmask
= cache
>>> (index
% BIT_CHAR
);
357 if (((length
) % BIT_CHAR
) > 0) {
358 mask
= ~
((~
0) << length
);
365 cshift
= (int) (end
% BIT_CHAR
);
367 mask
= ~
((~
0) << cshift
);
368 cmask
= cache
& mask
;
373 for (; currByte
>= (startByte
+ 1); currByte
--) {
375 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
377 lshift
= (int) (index
% BIT_CHAR
);
379 mask
= ~
((~
0) << (BIT_CHAR
- lshift
));
380 cmask
= fBuffer
.get(currByte
) & BYTE_MASK
;
383 value
<<= (BIT_CHAR
- lshift
);
387 value
|= fBuffer
.get(currByte
) & BYTE_MASK
;
392 // ------------------------------------------------------------------------
393 // 'Put' operations on buffer
394 // ------------------------------------------------------------------------
397 * Relative <i>put</i> method to write signed 32-bit integer.
399 * Write four bytes starting from current bit position in the buffer
400 * according to the current byte order. The current position is increased of
401 * <i>length</i> bits.
404 * The int value to write
405 * @throws CTFReaderException
406 * An error occurred writing the data. If the buffer is written
407 * beyond its end, this exception will be raised.
409 public void putInt(int value
) throws CTFReaderException
{
410 putInt(BIT_INT
, value
);
414 * Relative <i>put</i> method to write <i>length</i> bits integer.
416 * Writes <i>length</i> lower-order bits from the provided <i>value</i>,
417 * starting from current bit position in the buffer. Sequential bytes are
418 * written according to the current byte order. The sign bit is carried to
419 * the MSB if signed is true. The sign bit is included in <i>length</i>. The
420 * current position is increased of <i>length</i>.
423 * The number of bits to write
426 * @throws CTFReaderException
427 * An error occurred writing the data. If the buffer is written
428 * beyond its end, this exception will be raised.
430 public void putInt(int length
, int value
) throws CTFReaderException
{
431 final long curPos
= fPosition
;
433 if (!canRead(length
)) {
434 throw new CTFReaderException("Cannot write to bitbuffer, " //$NON-NLS-1$
435 + "insufficient space. Requested: " + length
); //$NON-NLS-1$
440 if (fByteOrder
== ByteOrder
.LITTLE_ENDIAN
) {
441 putIntLE(curPos
, length
, value
);
443 putIntBE(curPos
, length
, value
);
448 private void putIntBE(long index
, int length
, int value
) {
449 if ((length
<= 0) || (length
> BIT_INT
)) {
450 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
452 long end
= index
+ length
;
453 int startByte
= (int) (index
/ BIT_CHAR
);
454 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
455 int currByte
, lshift
, cshift
, mask
, cmask
;
456 int correctedValue
= value
;
459 * mask v high bits. Works for unsigned and two complement signed
460 * numbers which value do not overflow on length bits.
463 if (length
< BIT_INT
) {
464 correctedValue
&= ~
(~
0 << length
);
468 if (startByte
== (endByte
- 1)) {
469 lshift
= (int) ((BIT_CHAR
- (end
% BIT_CHAR
)) % BIT_CHAR
);
470 mask
= ~
((~
0) << lshift
);
471 if ((index
% BIT_CHAR
) > 0) {
472 mask
|= (~
(0)) << (BIT_CHAR
- (index
% BIT_CHAR
));
474 cmask
= correctedValue
<< lshift
;
476 * low bits are cleared because of left-shift and high bits are
480 int b
= fBuffer
.get(startByte
) & BYTE_MASK
;
481 fBuffer
.put(startByte
, (byte) ((b
& mask
) | cmask
));
485 /* head byte contains MSB */
486 currByte
= endByte
- 1;
487 cshift
= (int) (end
% BIT_CHAR
);
489 lshift
= BIT_CHAR
- cshift
;
490 mask
= ~
((~
0) << lshift
);
491 cmask
= correctedValue
<< lshift
;
493 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
494 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
495 correctedValue
>>>= cshift
;
500 for (; currByte
>= (startByte
+ 1); currByte
--) {
501 fBuffer
.put(currByte
, (byte) correctedValue
);
502 correctedValue
>>>= BIT_CHAR
;
504 /* end byte contains LSB */
505 if ((index
% BIT_CHAR
) > 0) {
506 mask
= (~
0) << (BIT_CHAR
- (index
% BIT_CHAR
));
507 cmask
= correctedValue
& ~mask
;
508 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
509 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
511 fBuffer
.put(currByte
, (byte) correctedValue
);
515 private void putIntLE(long index
, int length
, int value
) {
516 if ((length
<= 0) || (length
> BIT_INT
)) {
517 throw new IllegalArgumentException("Length must be between 1-32 bits"); //$NON-NLS-1$
519 long end
= index
+ length
;
520 int startByte
= (int) (index
/ BIT_CHAR
);
521 int endByte
= (int) ((end
+ (BIT_CHAR
- 1)) / BIT_CHAR
);
522 int currByte
, lshift
, cshift
, mask
, cmask
;
523 int correctedValue
= value
;
526 * mask v high bits. Works for unsigned and two complement signed
527 * numbers which value do not overflow on length bits.
530 if (length
< BIT_INT
) {
531 correctedValue
&= ~
(~
0 << length
);
535 if (startByte
== (endByte
- 1)) {
536 lshift
= (int) (index
% BIT_CHAR
);
537 mask
= ~
((~
0) << lshift
);
538 if ((end
% BIT_CHAR
) > 0) {
539 mask
|= (~
(0)) << (end
% BIT_CHAR
);
541 cmask
= correctedValue
<< lshift
;
543 * low bits are cleared because of left-shift and high bits are
547 int b
= fBuffer
.get(startByte
) & BYTE_MASK
;
548 fBuffer
.put(startByte
, (byte) ((b
& mask
) | cmask
));
553 currByte
= startByte
;
554 cshift
= (int) (index
% BIT_CHAR
);
556 mask
= ~
((~
0) << cshift
);
557 cmask
= correctedValue
<< cshift
;
559 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
560 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
561 correctedValue
>>>= BIT_CHAR
- cshift
;
566 for (; currByte
< (endByte
- 1); currByte
++) {
567 fBuffer
.put(currByte
, (byte) correctedValue
);
568 correctedValue
>>>= BIT_CHAR
;
571 if ((end
% BIT_CHAR
) > 0) {
572 mask
= (~
0) << (end
% BIT_CHAR
);
573 cmask
= correctedValue
& ~mask
;
574 int b
= fBuffer
.get(currByte
) & BYTE_MASK
;
575 fBuffer
.put(currByte
, (byte) ((b
& mask
) | cmask
));
577 fBuffer
.put(currByte
, (byte) correctedValue
);
581 // ------------------------------------------------------------------------
582 // Buffer attributes handling
583 // ------------------------------------------------------------------------
586 * Can this buffer be read for thus amount of bits?
589 * the length in bits to read
590 * @return does the buffer have enough room to read the next "length"
592 public boolean canRead(int length
) {
593 return ((fPosition
+ length
) <= fBitCapacity
);
597 * Sets the order of the buffer.
600 * The order of the buffer.
602 public void setByteOrder(ByteOrder order
) {
604 fBuffer
.order(order
);
608 * Sets the order of the buffer.
610 * @return The order of the buffer.
612 public ByteOrder
getByteOrder() {
617 * Sets the position in the buffer.
620 * The new position of the buffer.
621 * @throws CTFReaderException
622 * Thrown on out of bounds exceptions
624 public void position(long newPosition
) throws CTFReaderException
{
626 if (newPosition
> fBitCapacity
) {
627 throw new CTFReaderException("Out of bounds exception on a position move, attempting to access position: " + newPosition
); //$NON-NLS-1$
629 fPosition
= newPosition
;
634 * Sets the position in the buffer.
636 * @return order The position of the buffer.
638 public long position() {
643 * Sets the byte buffer
649 public void setByteBuffer(ByteBuffer buf
) {
651 * to avoid "The method setByteBuffer(ByteBuffer) from the type
652 * BitBuffer can be declared as static"
654 long data
= fPosition
;
656 throw new UnsupportedOperationException("Bytebuffers are now final"); //$NON-NLS-1$
661 * Gets the byte buffer
663 * @return The byte buffer
665 public ByteBuffer
getByteBuffer() {
670 * Resets the bitbuffer.
672 public void clear() {
This page took 0.047427 seconds and 6 git commands to generate.