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