2 * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version,
8 * with the OCaml linking exception described in COPYING.LIB.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 include Bitstring_types
25 include Bitstring_config
27 (* Enable runtime debug messages. Must also have been enabled
33 exception Construct_failure of string * string * int * int
35 (* A bitstring is simply the data itself (as a string), and the
36 * bitoffset and the bitlength within the string. Note offset/length
37 * are counted in bits, not bytes.
39 type bitstring = string * int * int
43 (* Functions to create and load bitstrings. *)
44 let empty_bitstring = "", 0, 0
46 let make_bitstring len c =
47 if len >= 0 then String.make ((len+7) lsr 3) c, 0, len
50 sprintf "make_bitstring/create_bitstring: len %d < 0" len
53 let create_bitstring len = make_bitstring len '\000'
55 let zeroes_bitstring = create_bitstring
57 let ones_bitstring len = make_bitstring len '\xff'
59 let bitstring_of_string str = str, 0, String.length str lsl 3
61 let bitstring_of_chan chan =
62 let tmpsize = 16384 in
63 let buf = Buffer.create tmpsize in
64 let tmp = String.create tmpsize in
66 while n := input chan tmp 0 tmpsize; !n > 0 do
67 Buffer.add_substring buf tmp 0 !n;
69 Buffer.contents buf, 0, Buffer.length buf lsl 3
71 let bitstring_of_chan_max chan max =
72 let tmpsize = 16384 in
73 let buf = Buffer.create tmpsize in
74 let tmp = String.create tmpsize in
78 let r = min tmpsize (max - !len) in
79 let n = input chan tmp 0 r in
81 Buffer.add_substring buf tmp 0 n;
88 Buffer.contents buf, 0, !len lsl 3
90 let bitstring_of_file_descr fd =
91 let tmpsize = 16384 in
92 let buf = Buffer.create tmpsize in
93 let tmp = String.create tmpsize in
95 while n := Unix.read fd tmp 0 tmpsize; !n > 0 do
96 Buffer.add_substring buf tmp 0 !n;
98 Buffer.contents buf, 0, Buffer.length buf lsl 3
100 let bitstring_of_file_descr_max fd max =
101 let tmpsize = 16384 in
102 let buf = Buffer.create tmpsize in
103 let tmp = String.create tmpsize in
107 let r = min tmpsize (max - !len) in
108 let n = Unix.read fd tmp 0 r in
110 Buffer.add_substring buf tmp 0 n;
117 Buffer.contents buf, 0, !len lsl 3
119 let bitstring_of_file fname =
120 let chan = open_in_bin fname in
122 let bs = bitstring_of_chan chan in
129 let bitstring_length (_, _, len) = len
131 let subbitstring (data, off, len) off' len' =
132 let off = off + off' in
133 if off' < 0 || len' < 0 || off' > len - len' then invalid_arg "subbitstring";
136 let dropbits n (data, off, len) =
139 if len < 0 || n < 0 then invalid_arg "dropbits";
142 let takebits n (data, off, len) =
143 if len < n || n < 0 then invalid_arg "takebits";
146 (*----------------------------------------------------------------------*)
147 (* Bitwise functions.
149 * We try to isolate all bitwise functions within these modules.
153 (* Bitwise operations on ints. Note that we assume int <= 31 bits. *)
154 external (<<<) : int -> int -> int = "%lslint"
155 external (>>>) : int -> int -> int = "%lsrint"
156 external to_int : int -> int = "%identity"
162 (* Create a mask 0-31 bits wide. *)
165 (bits < 32 && Sys.word_size = 64) then
167 else if bits = 30 then
169 else if bits = 31 then
172 invalid_arg "Bitstring.I.mask"
174 (* Byte swap an int of a given size. *)
175 let byteswap v bits =
177 else if bits <= 16 then (
178 let shift = bits-8 in
179 let v1 = v >>> shift in
180 let v2 = ((v land (mask shift)) <<< 8) in
182 ) else if bits <= 24 then (
183 let shift = bits - 16 in
184 let v1 = v >>> (8+shift) in
185 let v2 = ((v >>> shift) land ff) <<< 8 in
186 let v3 = (v land (mask shift)) <<< 16 in
189 let shift = bits - 24 in
190 let v1 = v >>> (16+shift) in
191 let v2 = ((v >>> (8+shift)) land ff) <<< 8 in
192 let v3 = ((v >>> shift) land ff) <<< 16 in
193 let v4 = (v land (mask shift)) <<< 24 in
194 v4 lor v3 lor v2 lor v1
197 (* Check a value is in range 0 .. 2^bits-1. *)
198 let range_unsigned v bits =
199 let mask = lnot (mask bits) in
202 let range_signed v bits =
206 range_unsigned v bits
209 bits = 31 && Sys.word_size = 32
213 pred (minus_one <<< pred bits) < v
215 (* Call function g on the top bits, then f on each full byte
216 * (big endian - so start at top).
218 let rec map_bytes_be g f v bits =
220 map_bytes_be g f (v >>> 8) (bits-8);
221 let lsb = v land ff in
223 ) else if bits > 0 then (
224 let lsb = v land (mask bits) in
228 (* Call function g on the top bits, then f on each full byte
229 * (little endian - so start at root).
231 let rec map_bytes_le g f v bits =
233 let lsb = v land ff in
235 map_bytes_le g f (v >>> 8) (bits-8)
236 ) else if bits > 0 then (
237 let lsb = v land (mask bits) in
243 (* Bitwise operations on int32s. Note we try to keep it as similar
244 * as possible to the I module above, to make it easier to track
247 let (<<<) = Int32.shift_left
248 let (>>>) = Int32.shift_right_logical
249 let (land) = Int32.logand
250 let (lor) = Int32.logor
251 let lnot = Int32.lognot
252 let pred = Int32.pred
253 let max_int = Int32.max_int
254 let to_int = Int32.to_int
255 let zero = Int32.zero
257 let minus_one = Int32.minus_one
260 (* Create a mask so many bits wide. *)
264 else if bits = 31 then
266 else if bits = 32 then
269 invalid_arg "Bitstring.I32.mask"
271 (* Byte swap an int of a given size. *)
272 let byteswap v bits =
274 else if bits <= 16 then (
275 let shift = bits-8 in
276 let v1 = v >>> shift in
277 let v2 = (v land (mask shift)) <<< 8 in
279 ) else if bits <= 24 then (
280 let shift = bits - 16 in
281 let v1 = v >>> (8+shift) in
282 let v2 = ((v >>> shift) land ff) <<< 8 in
283 let v3 = (v land (mask shift)) <<< 16 in
286 let shift = bits - 24 in
287 let v1 = v >>> (16+shift) in
288 let v2 = ((v >>> (8+shift)) land ff) <<< 8 in
289 let v3 = ((v >>> shift) land ff) <<< 16 in
290 let v4 = (v land (mask shift)) <<< 24 in
291 v4 lor v3 lor v2 lor v1
294 (* Check a value is in range 0 .. 2^bits-1. *)
295 let range_unsigned v bits =
296 let mask = lnot (mask bits) in
299 (* Call function g on the top bits, then f on each full byte
300 * (big endian - so start at top).
302 let rec map_bytes_be g f v bits =
304 map_bytes_be g f (v >>> 8) (bits-8);
305 let lsb = v land ff in
307 ) else if bits > 0 then (
308 let lsb = v land (mask bits) in
312 (* Call function g on the top bits, then f on each full byte
313 * (little endian - so start at root).
315 let rec map_bytes_le g f v bits =
317 let lsb = v land ff in
319 map_bytes_le g f (v >>> 8) (bits-8)
320 ) else if bits > 0 then (
321 let lsb = v land (mask bits) in
327 (* Bitwise operations on int64s. Note we try to keep it as similar
328 * as possible to the I/I32 modules above, to make it easier to track
331 let (<<<) = Int64.shift_left
332 let (>>>) = Int64.shift_right_logical
333 let (land) = Int64.logand
334 let (lor) = Int64.logor
335 let lnot = Int64.lognot
336 let pred = Int64.pred
337 let max_int = Int64.max_int
338 let to_int = Int64.to_int
339 let zero = Int64.zero
341 let minus_one = Int64.minus_one
344 (* Create a mask so many bits wide. *)
348 else if bits = 63 then
350 else if bits = 64 then
353 invalid_arg "Bitstring.I64.mask"
355 (* Byte swap an int of a given size. *)
356 (* let byteswap v bits = *)
358 (* Check a value is in range 0 .. 2^bits-1. *)
359 let range_unsigned v bits =
360 let mask = lnot (mask bits) in
363 (* Call function g on the top bits, then f on each full byte
364 * (big endian - so start at top).
366 let rec map_bytes_be g f v bits =
368 map_bytes_be g f (v >>> 8) (bits-8);
369 let lsb = v land ff in
371 ) else if bits > 0 then (
372 let lsb = v land (mask bits) in
376 (* Call function g on the top bits, then f on each full byte
377 * (little endian - so start at root).
379 let rec map_bytes_le g f v bits =
381 let lsb = v land ff in
383 map_bytes_le g f (v >>> 8) (bits-8)
384 ) else if bits > 0 then (
385 let lsb = v land (mask bits) in
390 (*----------------------------------------------------------------------*)
391 (* Extraction functions.
393 * NB: internal functions, called from the generated macros, and
394 * the parameters should have been checked for sanity already).
397 (* Extract and convert to numeric. A single bit is returned as
398 * a boolean. There are no endianness or signedness considerations.
400 let extract_bit data off len _ = (* final param is always 1 *)
401 let byteoff = off lsr 3 in
402 let bitmask = 1 lsl (7 - (off land 7)) in
403 let b = Char.code data.[byteoff] land bitmask <> 0 in
406 (* Returns 8 bit unsigned aligned bytes from the string.
407 * If the string ends then this returns 0's.
409 let _get_byte data byteoff strlen =
410 if strlen > byteoff then Char.code data.[byteoff] else 0
411 let _get_byte32 data byteoff strlen =
412 if strlen > byteoff then Int32.of_int (Char.code data.[byteoff]) else 0l
413 let _get_byte64 data byteoff strlen =
414 if strlen > byteoff then Int64.of_int (Char.code data.[byteoff]) else 0L
416 (* Extend signed [2..31] bits int to 31 bits int or 63 bits int for 64
418 let extend_sign len v =
419 let b = pred Sys.word_size - len in
422 let extract_and_extend_sign f data off len flen =
423 let w = f data off len flen in
426 (* Extract [2..8] bits. Because the result fits into a single
427 * byte we don't have to worry about endianness, only signedness.
429 let extract_char_unsigned data off len flen =
430 let byteoff = off lsr 3 in
432 (* Optimize the common (byte-aligned) case. *)
433 if off land 7 = 0 then (
434 let byte = Char.code data.[byteoff] in
435 byte lsr (8 - flen) (*, off+flen, len-flen*)
437 (* Extract the 16 bits at byteoff and byteoff+1 (note that the
438 * second byte might not exist in the original string).
440 let strlen = String.length data in
443 (_get_byte data byteoff strlen lsl 8) +
444 _get_byte data (byteoff+1) strlen in
446 (* Mask off the top bits. *)
447 let bitmask = (1 lsl (16 - (off land 7))) - 1 in
448 let word = word land bitmask in
449 (* Shift right to get rid of the bottom bits. *)
450 let shift = 16 - ((off land 7) + flen) in
451 let word = word lsr shift in
453 word (*, off+flen, len-flen*)
456 let extract_char_signed =
457 extract_and_extend_sign extract_char_unsigned
459 (* Extract [9..31] bits. We have to consider endianness and signedness. *)
460 let extract_int_be_unsigned data off len flen =
461 let byteoff = off lsr 3 in
463 let strlen = String.length data in
466 (* Optimize the common (byte-aligned) case. *)
467 if off land 7 = 0 then (
469 (_get_byte data byteoff strlen lsl 23) +
470 (_get_byte data (byteoff+1) strlen lsl 15) +
471 (_get_byte data (byteoff+2) strlen lsl 7) +
472 (_get_byte data (byteoff+3) strlen lsr 1) in
474 ) else if flen <= 24 then (
475 (* Extract the 31 bits at byteoff .. byteoff+3. *)
477 (_get_byte data byteoff strlen lsl 23) +
478 (_get_byte data (byteoff+1) strlen lsl 15) +
479 (_get_byte data (byteoff+2) strlen lsl 7) +
480 (_get_byte data (byteoff+3) strlen lsr 1) in
481 (* Mask off the top bits. *)
482 let bitmask = (1 lsl (31 - (off land 7))) - 1 in
483 let word = word land bitmask in
484 (* Shift right to get rid of the bottom bits. *)
485 let shift = 31 - ((off land 7) + flen) in
488 (* Extract the next 31 bits, slow method. *)
490 let c0 = extract_char_unsigned data off len 8
491 and off = off + 8 and len = len - 8 in
492 let c1 = extract_char_unsigned data off len 8
493 and off = off + 8 and len = len - 8 in
494 let c2 = extract_char_unsigned data off len 8
495 and off = off + 8 and len = len - 8 in
496 let c3 = extract_char_unsigned data off len 7 in
497 (c0 lsl 23) + (c1 lsl 15) + (c2 lsl 7) + c3 in
500 word (*, off+flen, len-flen*)
502 let extract_int_be_signed =
503 extract_and_extend_sign extract_int_be_unsigned
505 let extract_int_le_unsigned data off len flen =
506 let v = extract_int_be_unsigned data off len flen in
507 let v = I.byteswap v flen in
510 let extract_int_le_signed =
511 extract_and_extend_sign extract_int_le_unsigned
513 let extract_int_ne_unsigned =
514 if nativeendian = BigEndian
515 then extract_int_be_unsigned
516 else extract_int_le_unsigned
518 let extract_int_ne_signed =
519 extract_and_extend_sign extract_int_ne_unsigned
521 let extract_int_ee_unsigned = function
522 | BigEndian -> extract_int_be_unsigned
523 | LittleEndian -> extract_int_le_unsigned
524 | NativeEndian -> extract_int_ne_unsigned
526 let extract_int_ee_signed e =
527 extract_and_extend_sign (extract_int_ee_unsigned e)
529 let _make_int32_be c0 c1 c2 c3 =
533 (Int32.shift_left c0 24)
534 (Int32.shift_left c1 16))
535 (Int32.shift_left c2 8))
538 let _make_int32_le c0 c1 c2 c3 =
542 (Int32.shift_left c3 24)
543 (Int32.shift_left c2 16))
544 (Int32.shift_left c1 8))
547 (* Extract exactly 32 bits. We have to consider endianness and signedness. *)
548 let extract_int32_be_unsigned data off len flen =
549 let byteoff = off lsr 3 in
551 let strlen = String.length data in
554 (* Optimize the common (byte-aligned) case. *)
555 if off land 7 = 0 then (
557 let c0 = _get_byte32 data byteoff strlen in
558 let c1 = _get_byte32 data (byteoff+1) strlen in
559 let c2 = _get_byte32 data (byteoff+2) strlen in
560 let c3 = _get_byte32 data (byteoff+3) strlen in
561 _make_int32_be c0 c1 c2 c3 in
562 Int32.shift_right_logical word (32 - flen)
564 (* Extract the next 32 bits, slow method. *)
566 let c0 = extract_char_unsigned data off len 8
567 and off = off + 8 and len = len - 8 in
568 let c1 = extract_char_unsigned data off len 8
569 and off = off + 8 and len = len - 8 in
570 let c2 = extract_char_unsigned data off len 8
571 and off = off + 8 and len = len - 8 in
572 let c3 = extract_char_unsigned data off len 8 in
573 let c0 = Int32.of_int c0 in
574 let c1 = Int32.of_int c1 in
575 let c2 = Int32.of_int c2 in
576 let c3 = Int32.of_int c3 in
577 _make_int32_be c0 c1 c2 c3 in
578 Int32.shift_right_logical word (32 - flen)
580 word (*, off+flen, len-flen*)
582 let extract_int32_le_unsigned data off len flen =
583 let v = extract_int32_be_unsigned data off len flen in
584 let v = I32.byteswap v flen in
587 let extract_int32_ne_unsigned =
588 if nativeendian = BigEndian
589 then extract_int32_be_unsigned
590 else extract_int32_le_unsigned
592 let extract_int32_ee_unsigned = function
593 | BigEndian -> extract_int32_be_unsigned
594 | LittleEndian -> extract_int32_le_unsigned
595 | NativeEndian -> extract_int32_ne_unsigned
597 let _make_int64_be c0 c1 c2 c3 c4 c5 c6 c7 =
605 (Int64.shift_left c0 56)
606 (Int64.shift_left c1 48))
607 (Int64.shift_left c2 40))
608 (Int64.shift_left c3 32))
609 (Int64.shift_left c4 24))
610 (Int64.shift_left c5 16))
611 (Int64.shift_left c6 8))
614 let _make_int64_le c0 c1 c2 c3 c4 c5 c6 c7 =
615 _make_int64_be c7 c6 c5 c4 c3 c2 c1 c0
617 (* Extract [1..64] bits. We have to consider endianness and signedness. *)
618 let extract_int64_be_unsigned data off len flen =
619 let byteoff = off lsr 3 in
621 let strlen = String.length data in
624 (* Optimize the common (byte-aligned) case. *)
625 if off land 7 = 0 then (
627 let c0 = _get_byte64 data byteoff strlen in
628 let c1 = _get_byte64 data (byteoff+1) strlen in
629 let c2 = _get_byte64 data (byteoff+2) strlen in
630 let c3 = _get_byte64 data (byteoff+3) strlen in
631 let c4 = _get_byte64 data (byteoff+4) strlen in
632 let c5 = _get_byte64 data (byteoff+5) strlen in
633 let c6 = _get_byte64 data (byteoff+6) strlen in
634 let c7 = _get_byte64 data (byteoff+7) strlen in
635 _make_int64_be c0 c1 c2 c3 c4 c5 c6 c7 in
636 Int64.shift_right_logical word (64 - flen)
638 (* Extract the next 64 bits, slow method. *)
640 let c0 = extract_char_unsigned data off len 8
641 and off = off + 8 and len = len - 8 in
642 let c1 = extract_char_unsigned data off len 8
643 and off = off + 8 and len = len - 8 in
644 let c2 = extract_char_unsigned data off len 8
645 and off = off + 8 and len = len - 8 in
646 let c3 = extract_char_unsigned data off len 8
647 and off = off + 8 and len = len - 8 in
648 let c4 = extract_char_unsigned data off len 8
649 and off = off + 8 and len = len - 8 in
650 let c5 = extract_char_unsigned data off len 8
651 and off = off + 8 and len = len - 8 in
652 let c6 = extract_char_unsigned data off len 8
653 and off = off + 8 and len = len - 8 in
654 let c7 = extract_char_unsigned data off len 8 in
655 let c0 = Int64.of_int c0 in
656 let c1 = Int64.of_int c1 in
657 let c2 = Int64.of_int c2 in
658 let c3 = Int64.of_int c3 in
659 let c4 = Int64.of_int c4 in
660 let c5 = Int64.of_int c5 in
661 let c6 = Int64.of_int c6 in
662 let c7 = Int64.of_int c7 in
663 _make_int64_be c0 c1 c2 c3 c4 c5 c6 c7 in
664 Int64.shift_right_logical word (64 - flen)
666 word (*, off+flen, len-flen*)
668 let extract_int64_le_unsigned data off len flen =
669 let byteoff = off lsr 3 in
671 let strlen = String.length data in
674 (* Optimize the common (byte-aligned) case. *)
675 if off land 7 = 0 then (
677 let c0 = _get_byte64 data byteoff strlen in
678 let c1 = _get_byte64 data (byteoff+1) strlen in
679 let c2 = _get_byte64 data (byteoff+2) strlen in
680 let c3 = _get_byte64 data (byteoff+3) strlen in
681 let c4 = _get_byte64 data (byteoff+4) strlen in
682 let c5 = _get_byte64 data (byteoff+5) strlen in
683 let c6 = _get_byte64 data (byteoff+6) strlen in
684 let c7 = _get_byte64 data (byteoff+7) strlen in
685 _make_int64_le c0 c1 c2 c3 c4 c5 c6 c7 in
686 Int64.logand word (I64.mask flen)
688 (* Extract the next 64 bits, slow method. *)
690 let c0 = extract_char_unsigned data off len 8
691 and off = off + 8 and len = len - 8 in
692 let c1 = extract_char_unsigned data off len 8
693 and off = off + 8 and len = len - 8 in
694 let c2 = extract_char_unsigned data off len 8
695 and off = off + 8 and len = len - 8 in
696 let c3 = extract_char_unsigned data off len 8
697 and off = off + 8 and len = len - 8 in
698 let c4 = extract_char_unsigned data off len 8
699 and off = off + 8 and len = len - 8 in
700 let c5 = extract_char_unsigned data off len 8
701 and off = off + 8 and len = len - 8 in
702 let c6 = extract_char_unsigned data off len 8
703 and off = off + 8 and len = len - 8 in
704 let c7 = extract_char_unsigned data off len 8 in
705 let c0 = Int64.of_int c0 in
706 let c1 = Int64.of_int c1 in
707 let c2 = Int64.of_int c2 in
708 let c3 = Int64.of_int c3 in
709 let c4 = Int64.of_int c4 in
710 let c5 = Int64.of_int c5 in
711 let c6 = Int64.of_int c6 in
712 let c7 = Int64.of_int c7 in
713 _make_int64_le c0 c1 c2 c3 c4 c5 c6 c7 in
714 Int64.logand word (I64.mask flen)
716 word (*, off+flen, len-flen*)
718 let extract_int64_ne_unsigned =
719 if nativeendian = BigEndian
720 then extract_int64_be_unsigned
721 else extract_int64_le_unsigned
723 let extract_int64_ee_unsigned = function
724 | BigEndian -> extract_int64_be_unsigned
725 | LittleEndian -> extract_int64_le_unsigned
726 | NativeEndian -> extract_int64_ne_unsigned
728 external extract_fastpath_int16_be_unsigned : string -> int -> int = "ocaml_bitstring_extract_fastpath_int16_be_unsigned" "noalloc"
730 external extract_fastpath_int16_le_unsigned : string -> int -> int = "ocaml_bitstring_extract_fastpath_int16_le_unsigned" "noalloc"
732 external extract_fastpath_int16_ne_unsigned : string -> int -> int = "ocaml_bitstring_extract_fastpath_int16_ne_unsigned" "noalloc"
734 external extract_fastpath_int16_be_signed : string -> int -> int = "ocaml_bitstring_extract_fastpath_int16_be_signed" "noalloc"
736 external extract_fastpath_int16_le_signed : string -> int -> int = "ocaml_bitstring_extract_fastpath_int16_le_signed" "noalloc"
738 external extract_fastpath_int16_ne_signed : string -> int -> int = "ocaml_bitstring_extract_fastpath_int16_ne_signed" "noalloc"
741 external extract_fastpath_int24_be_unsigned : string -> int -> int = "ocaml_bitstring_extract_fastpath_int24_be_unsigned" "noalloc"
743 external extract_fastpath_int24_le_unsigned : string -> int -> int = "ocaml_bitstring_extract_fastpath_int24_le_unsigned" "noalloc"
745 external extract_fastpath_int24_ne_unsigned : string -> int -> int = "ocaml_bitstring_extract_fastpath_int24_ne_unsigned" "noalloc"
747 external extract_fastpath_int24_be_signed : string -> int -> int = "ocaml_bitstring_extract_fastpath_int24_be_signed" "noalloc"
749 external extract_fastpath_int24_le_signed : string -> int -> int = "ocaml_bitstring_extract_fastpath_int24_le_signed" "noalloc"
751 external extract_fastpath_int24_ne_signed : string -> int -> int = "ocaml_bitstring_extract_fastpath_int24_ne_signed" "noalloc"
754 external extract_fastpath_int32_be_unsigned : string -> int -> int32 = "ocaml_bitstring_extract_fastpath_int32_be_unsigned"
756 external extract_fastpath_int32_le_unsigned : string -> int -> int32 = "ocaml_bitstring_extract_fastpath_int32_le_unsigned"
758 external extract_fastpath_int32_ne_unsigned : string -> int -> int32 = "ocaml_bitstring_extract_fastpath_int32_ne_unsigned"
760 external extract_fastpath_int32_be_signed : string -> int -> int32 = "ocaml_bitstring_extract_fastpath_int32_be_signed"
762 external extract_fastpath_int32_le_signed : string -> int -> int32 = "ocaml_bitstring_extract_fastpath_int32_le_signed"
764 external extract_fastpath_int32_ne_signed : string -> int -> int32 = "ocaml_bitstring_extract_fastpath_int32_ne_signed"
767 external extract_fastpath_int40_be_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int40_be_unsigned"
769 external extract_fastpath_int40_le_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int40_le_unsigned"
771 external extract_fastpath_int40_ne_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int40_ne_unsigned"
773 external extract_fastpath_int40_be_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int40_be_signed"
775 external extract_fastpath_int40_le_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int40_le_signed"
777 external extract_fastpath_int40_ne_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int40_ne_signed"
779 external extract_fastpath_int48_be_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int48_be_unsigned"
781 external extract_fastpath_int48_le_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int48_le_unsigned"
783 external extract_fastpath_int48_ne_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int48_ne_unsigned"
785 external extract_fastpath_int48_be_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int48_be_signed"
787 external extract_fastpath_int48_le_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int48_le_signed"
789 external extract_fastpath_int48_ne_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int48_ne_signed"
791 external extract_fastpath_int56_be_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int56_be_unsigned"
793 external extract_fastpath_int56_le_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int56_le_unsigned"
795 external extract_fastpath_int56_ne_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int56_ne_unsigned"
797 external extract_fastpath_int56_be_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int56_be_signed"
799 external extract_fastpath_int56_le_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int56_le_signed"
801 external extract_fastpath_int56_ne_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int56_ne_signed"
804 external extract_fastpath_int64_be_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int64_be_unsigned"
806 external extract_fastpath_int64_le_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int64_le_unsigned"
808 external extract_fastpath_int64_ne_unsigned : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int64_ne_unsigned"
810 external extract_fastpath_int64_be_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int64_be_signed"
812 external extract_fastpath_int64_le_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int64_le_signed"
814 external extract_fastpath_int64_ne_signed : string -> int -> int64 = "ocaml_bitstring_extract_fastpath_int64_ne_signed"
816 (*----------------------------------------------------------------------*)
817 (* Constructor functions. *)
819 module Buffer = struct
822 mutable len : int; (* Length in bits. *)
823 (* Last byte in the buffer (if len is not aligned). We store
824 * it outside the buffer because buffers aren't mutable.
830 (* XXX We have almost enough information in the generator to
831 * choose a good initial size.
833 { buf = Buffer.create 128; len = 0; last = 0 }
835 let contents { buf = buf; len = len; last = last } =
837 if len land 7 = 0 then
840 Buffer.contents buf ^ (String.make 1 (Char.chr last)) in
843 (* Add exactly 8 bits. *)
844 let add_byte ({ buf = buf; len = len; last = last } as t) byte =
845 if byte < 0 || byte > 255 then invalid_arg "Bitstring.Buffer.add_byte";
846 let shift = len land 7 in
848 (* Target buffer is byte-aligned. *)
849 Buffer.add_char buf (Char.chr byte)
851 (* Target buffer is unaligned. 'last' is meaningful. *)
852 let first = byte lsr shift in
853 let second = (byte lsl (8 - shift)) land 0xff in
854 Buffer.add_char buf (Char.chr (last lor first));
859 (* Add exactly 1 bit. *)
860 let add_bit ({ buf = buf; len = len; last = last } as t) bit =
861 let shift = 7 - (len land 7) in
863 (* Somewhere in the middle of 'last'. *)
864 t.last <- last lor ((if bit then 1 else 0) lsl shift)
866 (* Just a single spare bit in 'last'. *)
867 let last = last lor if bit then 1 else 0 in
868 Buffer.add_char buf (Char.chr last);
873 (* Add a small number of bits (definitely < 8). This uses a loop
874 * to call add_bit so it's slow.
876 let _add_bits t c slen =
877 if slen < 1 || slen >= 8 then invalid_arg "Bitstring.Buffer._add_bits";
878 for i = slen-1 downto 0 do
879 let bit = c land (1 lsl i) <> 0 in
883 let add_bits ({ buf = buf; len = len } as t) str slen =
885 if len land 7 = 0 then (
886 if slen land 7 = 0 then
887 (* Common case - everything is byte-aligned. *)
888 Buffer.add_substring buf str 0 (slen lsr 3)
890 (* Target buffer is aligned. Copy whole bytes then leave the
891 * remaining bits in last.
893 let slenbytes = slen lsr 3 in
894 if slenbytes > 0 then Buffer.add_substring buf str 0 slenbytes;
895 let lastidx = min slenbytes (String.length str - 1) in
896 let last = Char.code str.[lastidx] in (* last char *)
897 let mask = 0xff lsl (8 - (slen land 7)) in
898 t.last <- last land mask
902 (* Target buffer is unaligned. Copy whole bytes using
903 * add_byte which knows how to deal with an unaligned
904 * target buffer, then call add_bit for the remaining < 8 bits.
906 * XXX This is going to be dog-slow.
908 let slenbytes = slen lsr 3 in
909 for i = 0 to slenbytes-1 do
910 let byte = Char.code str.[i] in
913 let bitsleft = slen - (slenbytes lsl 3) in
914 if bitsleft > 0 then (
915 let c = Char.code str.[slenbytes] in
916 for i = 0 to bitsleft - 1 do
917 let bit = c land (0x80 lsr i) <> 0 in
925 (* Construct a single bit. *)
926 let construct_bit buf b _ _ =
929 (* Construct a field, flen = [2..8]. *)
930 let construct_char_unsigned buf v flen exn =
931 let max_val = 1 lsl flen in
932 if v < 0 || v >= max_val then raise exn;
934 Buffer.add_byte buf v
936 Buffer._add_bits buf v flen
938 let construct_char_signed buf v flen exn =
939 let max_val = 1 lsl flen
940 and min_val = - (1 lsl pred flen) in
941 if v < min_val || v >= max_val then
944 Buffer.add_byte buf (if v >= 0 then v else 256 + v)
946 Buffer._add_bits buf v flen
948 (* Construct a field of up to 31 bits. *)
949 let construct_int check_func map_func buf v flen exn =
950 if not (check_func v flen) then raise exn;
951 map_func (Buffer._add_bits buf) (Buffer.add_byte buf) v flen
953 let construct_int_be_unsigned =
954 construct_int I.range_unsigned I.map_bytes_be
956 let construct_int_be_signed =
957 construct_int I.range_signed I.map_bytes_be
959 let construct_int_le_unsigned =
960 construct_int I.range_unsigned I.map_bytes_le
962 let construct_int_le_signed =
963 construct_int I.range_signed I.map_bytes_le
965 let construct_int_ne_unsigned =
966 if nativeendian = BigEndian
967 then construct_int_be_unsigned
968 else construct_int_le_unsigned
970 let construct_int_ne_signed =
971 if nativeendian = BigEndian
972 then construct_int_be_signed
973 else construct_int_le_signed
975 let construct_int_ee_unsigned = function
976 | BigEndian -> construct_int_be_unsigned
977 | LittleEndian -> construct_int_le_unsigned
978 | NativeEndian -> construct_int_ne_unsigned
980 let construct_int_ee_signed = function
981 | BigEndian -> construct_int_be_signed
982 | LittleEndian -> construct_int_le_signed
983 | NativeEndian -> construct_int_ne_signed
985 (* Construct a field of exactly 32 bits. *)
986 let construct_int32_be_unsigned buf v flen _ =
988 (Int32.to_int (Int32.shift_right_logical v 24));
990 (Int32.to_int ((Int32.logand (Int32.shift_right_logical v 16) 0xff_l)));
992 (Int32.to_int ((Int32.logand (Int32.shift_right_logical v 8) 0xff_l)));
994 (Int32.to_int (Int32.logand v 0xff_l))
996 let construct_int32_le_unsigned buf v flen _ =
998 (Int32.to_int (Int32.logand v 0xff_l));
1000 (Int32.to_int ((Int32.logand (Int32.shift_right_logical v 8) 0xff_l)));
1002 (Int32.to_int ((Int32.logand (Int32.shift_right_logical v 16) 0xff_l)));
1004 (Int32.to_int (Int32.shift_right_logical v 24))
1006 let construct_int32_ne_unsigned =
1007 if nativeendian = BigEndian
1008 then construct_int32_be_unsigned
1009 else construct_int32_le_unsigned
1011 let construct_int32_ee_unsigned = function
1012 | BigEndian -> construct_int32_be_unsigned
1013 | LittleEndian -> construct_int32_le_unsigned
1014 | NativeEndian -> construct_int32_ne_unsigned
1016 (* Construct a field of up to 64 bits. *)
1017 let construct_int64_be_unsigned buf v flen exn =
1018 (* Check value is within range. *)
1019 if not (I64.range_unsigned v flen) then raise exn;
1020 (* Add the bytes. *)
1021 I64.map_bytes_be (Buffer._add_bits buf) (Buffer.add_byte buf) v flen
1023 (* Construct a field of up to 64 bits. *)
1024 let construct_int64_le_unsigned buf v flen exn =
1025 (* Check value is within range. *)
1026 if not (I64.range_unsigned v flen) then raise exn;
1027 (* Add the bytes. *)
1028 I64.map_bytes_le (Buffer._add_bits buf) (Buffer.add_byte buf) v flen
1030 let construct_int64_ne_unsigned =
1031 if nativeendian = BigEndian
1032 then construct_int64_be_unsigned
1033 else construct_int64_le_unsigned
1035 let construct_int64_ee_unsigned = function
1036 | BigEndian -> construct_int64_be_unsigned
1037 | LittleEndian -> construct_int64_le_unsigned
1038 | NativeEndian -> construct_int64_ne_unsigned
1040 (* Construct from a string of bytes, exact multiple of 8 bits
1041 * in length of course.
1043 let construct_string buf str =
1044 let len = String.length str in
1045 Buffer.add_bits buf str (len lsl 3)
1047 (* Construct from a bitstring. *)
1048 let construct_bitstring buf (data, off, len) =
1049 (* Add individual bits until we get to the next byte boundary of
1050 * the underlying string.
1052 let blen = 7 - ((off + 7) land 7) in
1053 let blen = min blen len in
1054 let rec loop off len blen =
1055 if blen = 0 then (off, len)
1057 let b = extract_bit data off len 1
1058 and off = off + 1 and len = len - 1 in
1059 Buffer.add_bit buf b;
1060 loop off len (blen-1)
1063 let off, len = loop off len blen in
1064 assert (len = 0 || (off land 7) = 0);
1066 (* Add the remaining 'len' bits. *)
1068 let off = off lsr 3 in
1069 (* XXX dangerous allocation *)
1070 if off = 0 then data
1071 else String.sub data off (String.length data - off) in
1073 Buffer.add_bits buf data len
1075 (* Concatenate bitstrings. *)
1077 let buf = Buffer.create () in
1078 List.iter (construct_bitstring buf) bs;
1081 (*----------------------------------------------------------------------*)
1082 (* Extract a string from a bitstring. *)
1083 let string_of_bitstring (data, off, len) =
1084 if off land 7 = 0 && len land 7 = 0 then
1085 (* Easy case: everything is byte-aligned. *)
1086 String.sub data (off lsr 3) (len lsr 3)
1088 (* Bit-twiddling case. *)
1089 let strlen = (len + 7) lsr 3 in
1090 let str = String.make strlen '\000' in
1091 let rec loop data off len i =
1093 let c = extract_char_unsigned data off len 8
1094 and off = off + 8 and len = len - 8 in
1095 str.[i] <- Char.chr c;
1096 loop data off len (i+1)
1097 ) else if len > 0 then (
1098 let c = extract_char_unsigned data off len len in
1099 str.[i] <- Char.chr (c lsl (8-len))
1102 loop data off len 0;
1108 let bitstring_to_chan ((data, off, len) as bits) chan =
1109 (* Fail if the bitstring length isn't a multiple of 8. *)
1110 if len land 7 <> 0 then invalid_arg "bitstring_to_chan";
1112 if off land 7 = 0 then
1113 (* Easy case: string is byte-aligned. *)
1114 output chan data (off lsr 3) (len lsr 3)
1116 (* Bit-twiddling case: reuse string_of_bitstring *)
1117 let str = string_of_bitstring bits in
1118 output_string chan str
1121 let bitstring_to_file bits filename =
1122 let chan = open_out_bin filename in
1124 bitstring_to_chan bits chan;
1130 (*----------------------------------------------------------------------*)
1132 let compare ((data1, off1, len1) as bs1) ((data2, off2, len2) as bs2) =
1133 (* In the fully-aligned case, this is reduced to string comparison ... *)
1134 if off1 land 7 = 0 && len1 land 7 = 0 && off2 land 7 = 0 && len2 land 7 = 0
1136 (* ... but we have to do that by hand because the bits may
1137 * not extend to the full length of the underlying string.
1139 let off1 = off1 lsr 3 and off2 = off2 lsr 3
1140 and len1 = len1 lsr 3 and len2 = len2 lsr 3 in
1142 if i < len1 && i < len2 then (
1143 let c1 = String.unsafe_get data1 (off1 + i)
1144 and c2 = String.unsafe_get data2 (off2 + i) in
1145 let r = compare c1 c2 in
1154 (* Slow/unaligned. *)
1155 let str1 = string_of_bitstring bs1
1156 and str2 = string_of_bitstring bs2 in
1157 let r = String.compare str1 str2 in
1158 if r <> 0 then r else len1 - len2
1161 let equals ((_, _, len1) as bs1) ((_, _, len2) as bs2) =
1162 if len1 <> len2 then false
1163 else if bs1 = bs2 then true
1164 else 0 = compare bs1 bs2
1166 let is_zeroes_bitstring ((data, off, len) as bits) =
1167 if off land 7 = 0 && len land 7 = 0 then (
1168 let off = off lsr 3 and len = len lsr 3 in
1171 if String.unsafe_get data (off + i) <> '\000' then false
1178 (* Slow/unaligned case. *)
1179 let len = bitstring_length bits in
1180 let zeroes = zeroes_bitstring len in
1181 0 = compare bits zeroes
1184 let is_ones_bitstring ((data, off, len) as bits) =
1185 if off land 7 = 0 && len land 7 = 0 then (
1186 let off = off lsr 3 and len = len lsr 3 in
1189 if String.unsafe_get data (off + i) <> '\xff' then false
1196 (* Slow/unaligned case. *)
1197 let len = bitstring_length bits in
1198 let ones = ones_bitstring len in
1199 0 = compare bits ones
1202 (*----------------------------------------------------------------------*)
1203 (* Bit get/set functions. *)
1205 let index_out_of_bounds () = invalid_arg "index out of bounds"
1207 let put (data, off, len) n v =
1208 if n < 0 || n >= len then index_out_of_bounds ()
1211 let si = i lsr 3 and mask = 0x80 lsr (i land 7) in
1212 let c = Char.code data.[si] in
1213 let c = if v <> 0 then c lor mask else c land (lnot mask) in
1214 data.[si] <- Char.unsafe_chr c
1217 let set bits n = put bits n 1
1219 let clear bits n = put bits n 0
1221 let get (data, off, len) n =
1222 if n < 0 || n >= len then index_out_of_bounds ()
1225 let si = i lsr 3 and mask = 0x80 lsr (i land 7) in
1226 let c = Char.code data.[si] in
1230 let is_set bits n = get bits n <> 0
1232 let is_clear bits n = get bits n = 0
1234 (*----------------------------------------------------------------------*)
1235 (* Display functions. *)
1238 let c = Char.code c in
1241 let hexdump_bitstring chan (data, off, len) =
1242 let count = ref 0 in
1243 let off = ref off in
1244 let len = ref len in
1245 let linelen = ref 0 in
1246 let linechars = String.make 16 ' ' in
1248 fprintf chan "00000000 ";
1251 let bits = min !len 8 in
1252 let byte = extract_char_unsigned data !off !len bits in
1253 off := !off + bits; len := !len - bits;
1255 let byte = byte lsl (8-bits) in
1256 fprintf chan "%02x " byte;
1259 linechars.[!linelen] <-
1260 (let c = Char.chr byte in
1261 if isprint c then c else '.');
1263 if !linelen = 8 then fprintf chan " ";
1264 if !linelen = 16 then (
1265 fprintf chan " |%s|\n%08x " linechars !count;
1267 for i = 0 to 15 do linechars.[i] <- ' ' done
1271 if !linelen > 0 then (
1272 let skip = (16 - !linelen) * 3 + if !linelen < 8 then 1 else 0 in
1273 for i = 0 to skip-1 do fprintf chan " " done;
1274 fprintf chan " |%s|\n%!" linechars
1278 (*----------------------------------------------------------------------*)
1279 (* Alias of functions shadowed by Core. *)
1281 let char_code = Char.code
1282 let int32_of_int = Int32.of_int