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