ctf: Throw CTFReaderException in the BitBuffer API
[deliverable/tracecompass.git] / org.eclipse.linuxtools.ctf.core / src / org / eclipse / linuxtools / ctf / core / event / io / BitBuffer.java
CommitLineData
486efb2e
AM
1/*******************************************************************************.
2 * Copyright (c) 2011-2012 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: Matthew Khouzam - Initial Design and implementation
10 * Contributors: Francis Giraldeau - Initial API and implementation
11 * Contributors: Philippe Proulx - Some refinement and optimization
74e4b6b9 12 * Contributors: Etienne Bergeron <Etienne.Bergeron@gmail.com> - fix zero size read + cleanup
486efb2e
AM
13 *******************************************************************************/
14
15package org.eclipse.linuxtools.ctf.core.event.io;
16
486efb2e
AM
17import java.nio.ByteBuffer;
18import java.nio.ByteOrder;
19
db8e8f7d
AM
20import org.eclipse.linuxtools.ctf.core.trace.CTFReaderException;
21
486efb2e
AM
22/**
23 * <b><u>BitBuffer</u></b>
24 * <p>
25 * A bitwise buffer capable of accessing fields with bit offsets.
26 * @since 2.0
27 */
0594c61c 28public final class BitBuffer {
486efb2e
AM
29
30 // ------------------------------------------------------------------------
31 // Constants
32 // ------------------------------------------------------------------------
33
34 /* default bit width */
35 /** 8 bits to a char */
36 public static final int BIT_CHAR = 8;
37 /** 16 bits to a short */
38 public static final int BIT_SHORT = 16;
39 /** 32 bits to an int */
40 public static final int BIT_INT = 32;
41 /** 32 bits to a float */
42 public static final int BIT_FLOAT = 32;
43 /** 64 bits to a long */
44 public static final int BIT_LONG = 64;
45
46 // ------------------------------------------------------------------------
47 // Attributes
48 // ------------------------------------------------------------------------
49
50 private ByteBuffer buf;
47ca6c05 51 private long pos;
486efb2e
AM
52 private ByteOrder byteOrder;
53
54 // ------------------------------------------------------------------------
55 // Constructors
56 // ------------------------------------------------------------------------
57 /**
74e4b6b9 58 * Default constructor, makes a big-endian buffer
486efb2e
AM
59 */
60 public BitBuffer() {
61 this(null, ByteOrder.BIG_ENDIAN);
62 }
63
64 /**
74e4b6b9 65 * Constructor, makes a big-endian buffer
486efb2e
AM
66 *
67 * @param buf
68 * the bytebuffer to read
69 */
70 public BitBuffer(ByteBuffer buf) {
71 this(buf, ByteOrder.BIG_ENDIAN);
72 }
73
74 /**
74e4b6b9 75 * Constructor that is fully parameterizable
486efb2e
AM
76 *
77 * @param buf
78 * the buffer to read
79 * @param order
74e4b6b9 80 * the byte order (big-endian, little-endian, network?)
486efb2e
AM
81 */
82 public BitBuffer(ByteBuffer buf, ByteOrder order) {
83 setByteBuffer(buf);
84 setByteOrder(order);
85 position(0);
86 }
87
88 // ------------------------------------------------------------------------
89 // 'Get' operations on buffer
90 // ------------------------------------------------------------------------
91
92 /**
93 * Relative <i>get</i> method for reading 32-bit integer.
94 *
95 * Reads next four bytes from the current bit position according to current
96 * byte order.
97 *
98 * @return The int value read from the buffer
db8e8f7d
AM
99 * @throws CTFReaderException
100 * An error occurred reading the data. When the buffer is read
101 * beyond its end, this exception will be raised.
486efb2e 102 */
db8e8f7d 103 public int getInt() throws CTFReaderException {
0594c61c 104 return getInt(BIT_INT, true);
486efb2e
AM
105 }
106
107 /**
108 * Relative <i>get</i> method for reading integer of <i>length</i> bits.
109 *
110 * Reads <i>length</i> bits starting at the current position. The result is
111 * signed extended if <i>signed</i> is true. The current position is
112 * increased of <i>length</i> bits.
113 *
114 * @param length
115 * The length in bits of this integer
116 * @param signed
117 * The sign extended flag
118 * @return The int value read from the buffer
db8e8f7d
AM
119 * @throws CTFReaderException
120 * An error occurred reading the data. When the buffer is read
121 * beyond its end, this exception will be raised.
486efb2e 122 */
db8e8f7d 123 public int getInt(int length, boolean signed) throws CTFReaderException {
74e4b6b9
EB
124
125 /* Nothing to read. */
486efb2e
AM
126 if (length == 0) {
127 return 0;
128 }
74e4b6b9
EB
129
130 /* Validate that the buffer has enough bits. */
131 if (!canRead(length)) {
db8e8f7d
AM
132 throw new CTFReaderException("Cannot read the integer, " + //$NON-NLS-1$
133 "the buffer does not have enough remaining space. " + //$NON-NLS-1$
134 "Requested:" + length); //$NON-NLS-1$
74e4b6b9
EB
135 }
136
137 /* Get the value from the byte buffer. */
138 int val = 0;
486efb2e
AM
139 boolean gotIt = false;
140
74e4b6b9
EB
141 /* Try a fast read when the position is byte-aligned by using the */
142 /* native methods of ByteBuffer. */
486efb2e
AM
143 if (this.pos % BitBuffer.BIT_CHAR == 0) {
144 switch (length) {
145 case BitBuffer.BIT_CHAR:
146 // Byte
74e4b6b9
EB
147 val = this.buf.get((int) (this.pos / 8));
148 if (!signed) {
149 val = val & 0xff;
486efb2e
AM
150 }
151 gotIt = true;
152 break;
153
154 case BitBuffer.BIT_SHORT:
155 // Word
74e4b6b9
EB
156 val = this.buf.getShort((int) (this.pos / 8));
157 if (!signed) {
158 val = val & 0xffff;
486efb2e
AM
159 }
160 gotIt = true;
161 break;
162
163 case BitBuffer.BIT_INT:
164 // Double word
47ca6c05 165 val = this.buf.getInt((int) (this.pos / 8));
486efb2e
AM
166 gotIt = true;
167 break;
168
169 default:
170 break;
171 }
172 }
74e4b6b9
EB
173
174 /* When not byte-aligned, fall-back to a general decoder. */
486efb2e
AM
175 if (!gotIt) {
176 // Nothing read yet: use longer methods
177 if (this.byteOrder == ByteOrder.LITTLE_ENDIAN) {
178 val = getIntLE(this.pos, length, signed);
179 } else {
180 val = getIntBE(this.pos, length, signed);
181 }
182 }
183 this.pos += length;
184
185 return val;
186 }
187
47ca6c05 188 private int getIntBE(long index, int length, boolean signed) {
486efb2e 189 assert ((length > 0) && (length <= BIT_INT));
47ca6c05
SM
190 long end = index + length;
191 int startByte = (int) (index / BIT_CHAR);
192 int endByte = (int) ((end + (BIT_CHAR - 1)) / BIT_CHAR);
486efb2e
AM
193 int currByte, lshift, cshift, mask, cmask, cache;
194 int value = 0;
195
196 currByte = startByte;
197 cache = this.buf.get(currByte) & 0xFF;
198 boolean isNeg = (cache & (1 << (BIT_CHAR - (index % BIT_CHAR) - 1))) != 0;
199 if (signed && isNeg) {
200 value = ~0;
201 }
202 if (startByte == (endByte - 1)) {
203 cmask = cache >>> ((BIT_CHAR - (end % BIT_CHAR)) % BIT_CHAR);
204 if (((length) % BIT_CHAR) > 0) {
205 mask = ~((~0) << length);
206 cmask &= mask;
207 }
208 value <<= length;
209 value |= cmask;
210 return value;
211 }
47ca6c05 212 cshift = (int) (index % BIT_CHAR);
486efb2e
AM
213 if (cshift > 0) {
214 mask = ~((~0) << (BIT_CHAR - cshift));
215 cmask = cache & mask;
216 lshift = BIT_CHAR - cshift;
217 value <<= lshift;
218 value |= cmask;
486efb2e
AM
219 currByte++;
220 }
221 for (; currByte < (endByte - 1); currByte++) {
222 value <<= BIT_CHAR;
223 value |= this.buf.get(currByte) & 0xFF;
224 }
47ca6c05 225 lshift = (int) (end % BIT_CHAR);
486efb2e
AM
226 if (lshift > 0) {
227 mask = ~((~0) << lshift);
228 cmask = this.buf.get(currByte) & 0xFF;
229 cmask >>>= BIT_CHAR - lshift;
230 cmask &= mask;
231 value <<= lshift;
232 value |= cmask;
233 } else {
234 value <<= BIT_CHAR;
235 value |= this.buf.get(currByte) & 0xFF;
236 }
237 return value;
238 }
239
47ca6c05 240 private int getIntLE(long index, int length, boolean signed) {
486efb2e 241 assert ((length > 0) && (length <= BIT_INT));
47ca6c05
SM
242 long end = index + length;
243 int startByte = (int) (index / BIT_CHAR);
244 int endByte = (int) ((end + (BIT_CHAR - 1)) / BIT_CHAR);
486efb2e
AM
245 int currByte, lshift, cshift, mask, cmask, cache, mod;
246 int value = 0;
247
248 currByte = endByte - 1;
249 cache = buf.get(currByte) & 0xFF;
47ca6c05 250 mod = (int) (end % BIT_CHAR);
486efb2e
AM
251 lshift = (mod > 0) ? mod : BIT_CHAR;
252 boolean isNeg = (cache & (1 << (lshift - 1))) != 0;
253 if (signed && isNeg) {
254 value = ~0;
255 }
256 if (startByte == (endByte - 1)) {
257 cmask = cache >>> (index % BIT_CHAR);
258 if (((length) % BIT_CHAR) > 0) {
259 mask = ~((~0) << length);
260 cmask &= mask;
261 }
262 value <<= length;
263 value |= cmask;
264 return value;
265 }
47ca6c05 266 cshift = (int) (end % BIT_CHAR);
486efb2e
AM
267 if (cshift > 0) {
268 mask = ~((~0) << cshift);
269 cmask = cache & mask;
270 value <<= cshift;
271 value |= cmask;
486efb2e
AM
272 currByte--;
273 }
274 for (; currByte >= (startByte + 1); currByte--) {
275 value <<= BIT_CHAR;
276 value |= buf.get(currByte) & 0xFF;
277 }
47ca6c05 278 lshift = (int) (index % BIT_CHAR);
486efb2e
AM
279 if (lshift > 0) {
280 mask = ~((~0) << (BIT_CHAR - lshift));
281 cmask = buf.get(currByte) & 0xFF;
282 cmask >>>= lshift;
283 cmask &= mask;
284 value <<= (BIT_CHAR - lshift);
285 value |= cmask;
286 } else {
287 value <<= BIT_CHAR;
288 value |= buf.get(currByte) & 0xFF;
289 }
290 return value;
291 }
292
293 // ------------------------------------------------------------------------
294 // 'Put' operations on buffer
295 // ------------------------------------------------------------------------
296
297 /**
298 * Relative <i>put</i> method to write signed 32-bit integer.
299 *
300 * Write four bytes starting from current bit position in the buffer
301 * according to the current byte order. The current position is increased of
302 * <i>length</i> bits.
303 *
304 * @param value
305 * The int value to write
db8e8f7d
AM
306 * @throws CTFReaderException
307 * An error occurred writing the data. If the buffer is written
308 * beyond its end, this exception will be raised.
486efb2e 309 */
db8e8f7d 310 public void putInt(int value) throws CTFReaderException {
486efb2e
AM
311 putInt(BIT_INT, value);
312 }
313
314 /**
315 * Relative <i>put</i> method to write <i>length</i> bits integer.
316 *
317 * Writes <i>length</i> lower-order bits from the provided <i>value</i>,
318 * starting from current bit position in the buffer. Sequential bytes are
319 * written according to the current byte order. The sign bit is carried to
320 * the MSB if signed is true. The sign bit is included in <i>length</i>. The
321 * current position is increased of <i>length</i>.
322 *
323 * @param length
324 * The number of bits to write
325 * @param value
326 * The value to write
db8e8f7d
AM
327 * @throws CTFReaderException
328 * An error occurred writing the data. If the buffer is written
329 * beyond its end, this exception will be raised.
486efb2e 330 */
db8e8f7d 331 public void putInt(int length, int value) throws CTFReaderException {
47ca6c05 332 final long curPos = this.pos;
486efb2e
AM
333
334 if (!canRead(length)) {
db8e8f7d
AM
335 throw new CTFReaderException("Cannot write to bitbuffer, " //$NON-NLS-1$
336 + "insufficient space. Requested: " + length); //$NON-NLS-1$
486efb2e
AM
337 }
338 if (length == 0) {
339 return;
340 }
341 if (this.byteOrder == ByteOrder.LITTLE_ENDIAN) {
342 putIntLE(curPos, length, value);
343 } else {
344 putIntBE(curPos, length, value);
345 }
346 this.pos += length;
347 }
348
47ca6c05 349 private void putIntBE(long index, int length, int value) {
486efb2e 350 assert ((length > 0) && (length <= BIT_INT));
47ca6c05
SM
351 long end = index + length;
352 int startByte = (int) (index / BIT_CHAR);
353 int endByte = (int) ((end + (BIT_CHAR - 1)) / BIT_CHAR);
486efb2e
AM
354 int currByte, lshift, cshift, mask, cmask;
355 int correctedValue = value;
356
357 /*
358 * mask v high bits. Works for unsigned and two complement signed
359 * numbers which value do not overflow on length bits.
360 */
361
362 if (length < BIT_INT) {
363 correctedValue &= ~(~0 << length);
364 }
365
366 /* sub byte */
367 if (startByte == (endByte - 1)) {
47ca6c05 368 lshift = (int) ((BIT_CHAR - (end % BIT_CHAR)) % BIT_CHAR);
486efb2e
AM
369 mask = ~((~0) << lshift);
370 if ((index % BIT_CHAR) > 0) {
371 mask |= (~(0)) << (BIT_CHAR - (index % BIT_CHAR));
372 }
373 cmask = correctedValue << lshift;
374 /*
74e4b6b9
EB
375 * low bits are cleared because of left-shift and high bits are
376 * already cleared
486efb2e
AM
377 */
378 cmask &= ~mask;
379 int b = this.buf.get(startByte) & 0xFF;
380 this.buf.put(startByte, (byte) ((b & mask) | cmask));
381 return;
382 }
383
384 /* head byte contains MSB */
385 currByte = endByte - 1;
47ca6c05 386 cshift = (int) (end % BIT_CHAR);
486efb2e
AM
387 if (cshift > 0) {
388 lshift = BIT_CHAR - cshift;
389 mask = ~((~0) << lshift);
390 cmask = correctedValue << lshift;
391 cmask &= ~mask;
392 int b = this.buf.get(currByte) & 0xFF;
393 this.buf.put(currByte, (byte) ((b & mask) | cmask));
394 correctedValue >>>= cshift;
486efb2e
AM
395 currByte--;
396 }
397
398 /* middle byte(s) */
399 for (; currByte >= (startByte + 1); currByte--) {
400 this.buf.put(currByte, (byte) correctedValue);
401 correctedValue >>>= BIT_CHAR;
402 }
403 /* end byte contains LSB */
404 if ((index % BIT_CHAR) > 0) {
405 mask = (~0) << (BIT_CHAR - (index % BIT_CHAR));
406 cmask = correctedValue & ~mask;
407 int b = this.buf.get(currByte) & 0xFF;
408 this.buf.put(currByte, (byte) ((b & mask) | cmask));
409 } else {
410 this.buf.put(currByte, (byte) correctedValue);
411 }
412 }
413
47ca6c05 414 private void putIntLE(long index, int length, int value) {
486efb2e 415 assert ((length > 0) && (length <= BIT_INT));
47ca6c05
SM
416 long end = index + length;
417 int startByte = (int) (index / BIT_CHAR);
418 int endByte = (int) ((end + (BIT_CHAR - 1)) / BIT_CHAR);
486efb2e
AM
419 int currByte, lshift, cshift, mask, cmask;
420 int correctedValue = value;
421
422 /*
423 * mask v high bits. Works for unsigned and two complement signed
424 * numbers which value do not overflow on length bits.
425 */
426
427 if (length < BIT_INT) {
428 correctedValue &= ~(~0 << length);
429 }
430
431 /* sub byte */
432 if (startByte == (endByte - 1)) {
47ca6c05 433 lshift = (int) (index % BIT_CHAR);
486efb2e
AM
434 mask = ~((~0) << lshift);
435 if ((end % BIT_CHAR) > 0) {
436 mask |= (~(0)) << (end % BIT_CHAR);
437 }
438 cmask = correctedValue << lshift;
439 /*
74e4b6b9
EB
440 * low bits are cleared because of left-shift and high bits are
441 * already cleared
486efb2e
AM
442 */
443 cmask &= ~mask;
444 int b = this.buf.get(startByte) & 0xFF;
445 this.buf.put(startByte, (byte) ((b & mask) | cmask));
446 return;
447 }
448
449 /* head byte */
450 currByte = startByte;
47ca6c05 451 cshift = (int) (index % BIT_CHAR);
486efb2e
AM
452 if (cshift > 0) {
453 mask = ~((~0) << cshift);
454 cmask = correctedValue << cshift;
455 cmask &= ~mask;
456 int b = this.buf.get(currByte) & 0xFF;
457 this.buf.put(currByte, (byte) ((b & mask) | cmask));
458 correctedValue >>>= BIT_CHAR - cshift;
486efb2e
AM
459 currByte++;
460 }
461
462 /* middle byte(s) */
463 for (; currByte < (endByte - 1); currByte++) {
464 this.buf.put(currByte, (byte) correctedValue);
465 correctedValue >>>= BIT_CHAR;
466 }
467 /* end byte */
468 if ((end % BIT_CHAR) > 0) {
469 mask = (~0) << (end % BIT_CHAR);
470 cmask = correctedValue & ~mask;
471 int b = this.buf.get(currByte) & 0xFF;
472 this.buf.put(currByte, (byte) ((b & mask) | cmask));
473 } else {
474 this.buf.put(currByte, (byte) correctedValue);
475 }
476 }
477
478 // ------------------------------------------------------------------------
479 // Buffer attributes handling
480 // ------------------------------------------------------------------------
481
482 /**
483 * Can this buffer be read for thus amount of bits?
484 *
485 * @param length
486 * the length in bits to read
487 * @return does the buffer have enough room to read the next "length"
488 */
489 public boolean canRead(int length) {
490 if (this.buf == null) {
491 return false;
492 }
493
47ca6c05 494 if ((this.pos + length) > (((long) this.buf.capacity()) * BIT_CHAR)) {
486efb2e
AM
495 return false;
496 }
497 return true;
498 }
499
500 /**
501 * Sets the order of the buffer.
502 *
503 * @param order
504 * The order of the buffer.
505 */
506 public void setByteOrder(ByteOrder order) {
507 this.byteOrder = order;
508 if (this.buf != null) {
509 this.buf.order(order);
510 }
511 }
512
513 /**
514 * Sets the order of the buffer.
515 *
516 * @return The order of the buffer.
517 */
518 public ByteOrder getByteOrder() {
519 return this.byteOrder;
520 }
521
522 /**
523 * Sets the position in the buffer.
524 *
525 * @param newPosition
526 * The new position of the buffer.
527 */
47ca6c05 528 public void position(long newPosition) {
486efb2e
AM
529 this.pos = newPosition;
530 }
531
532 /**
533 *
534 * Sets the position in the buffer.
535 *
536 * @return order The position of the buffer.
537 */
47ca6c05 538 public long position() {
486efb2e
AM
539 return this.pos;
540 }
541
542 /**
543 * Sets the byte buffer
544 *
545 * @param buf
546 * the byte buffer
547 */
548 public void setByteBuffer(ByteBuffer buf) {
549 this.buf = buf;
550 if (buf != null) {
551 this.buf.order(this.byteOrder);
552 }
553 clear();
554 }
555
556 /**
557 * Gets the byte buffer
558 *
559 * @return The byte buffer
560 */
561 public ByteBuffer getByteBuffer() {
562 return this.buf;
563 }
564
565 /**
74e4b6b9 566 * Resets the bitbuffer.
486efb2e
AM
567 */
568 public void clear() {
569 position(0);
570
571 if (this.buf == null) {
572 return;
573 }
574 this.buf.clear();
575 }
576
577}
This page took 0.11557 seconds and 5 git commands to generate.