Somewhat better attempt at a META file.
[ocaml-bitstring.git] / bitmatch.mli
index a62b870..4aa221d 100644 (file)
@@ -15,7 +15,7 @@
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  *
- * $Id: bitmatch.mli,v 1.10 2008-04-02 12:29:03 rjones Exp $
+ * $Id$
  *)
 
 (**
 
    Bitmatch adds Erlang-style bitstrings and matching over bitstrings
    as a syntax extension and library for OCaml.  You can use
-   this module to both parse and generate binary formats.
+   this module to both parse and generate binary formats, for
+   example, communications protocols, disk formats and binary files.
 
-   {{:http://et.redhat.com/~rjones/bitmatch/}OCaml bitmatch website}
+   {{:http://code.google.com/p/bitmatch/}OCaml bitmatch website}
 
    {2 Examples}
 
@@ -54,13 +55,13 @@ let display pkt =
    |                    Options                    |    Padding    |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *)
-  | 4 : 4; hdrlen : 4; tos : 8;   length : 16;
-    identification : 16;          flags : 3; fragoffset : 13;
-    ttl : 8; protocol : 8;        checksum : 16;
-    source : 32;
-    dest : 32;
-    options : (hdrlen-5)*32 : bitstring;
-    payload : -1 : bitstring ->
+  | 4 : 4; hdrlen : 4; tos : 8;   length : 16;
+      identification : 16;          flags : 3; fragoffset : 13;
+      ttl : 8; protocol : 8;        checksum : 16;
+      source : 32;
+      dest : 32;
+      options : (hdrlen-5)*32 : bitstring;
+      payload : -1 : bitstring } ->
 
     printf "IPv4:\n";
     printf "  header length: %d * 32 bit words\n" hdrlen;
@@ -78,11 +79,11 @@ let display pkt =
     printf "  packet payload:\n";
     Bitmatch.hexdump_bitstring stdout payload
 
-  | version : 4 ->
+  | { version : 4 } ->
     eprintf "unknown IP version %d\n" version;
     exit 1
 
-  | _ as pkt ->
+  | { _ } as pkt ->
     eprintf "data is smaller than one nibble:\n";
     Bitmatch.hexdump_bitstring stderr pkt;
     exit 1
@@ -96,22 +97,22 @@ let bits = Bitmatch.bitstring_of_file "tests/ext3_sb"
 
 let () =
   bitmatch bits with
-  | s_inodes_count : 32 : littleendian;       (* Inodes count *)
-    s_blocks_count : 32 : littleendian;       (* Blocks count *)
-    s_r_blocks_count : 32 : littleendian;     (* Reserved blocks count *)
-    s_free_blocks_count : 32 : littleendian;  (* Free blocks count *)
-    s_free_inodes_count : 32 : littleendian;  (* Free inodes count *)
-    s_first_data_block : 32 : littleendian;   (* First Data Block *)
-    s_log_block_size : 32 : littleendian;     (* Block size *)
-    s_log_frag_size : 32 : littleendian;      (* Fragment size *)
-    s_blocks_per_group : 32 : littleendian;   (* # Blocks per group *)
-    s_frags_per_group : 32 : littleendian;    (* # Fragments per group *)
-    s_inodes_per_group : 32 : littleendian;   (* # Inodes per group *)
-    s_mtime : 32 : littleendian;              (* Mount time *)
-    s_wtime : 32 : littleendian;              (* Write time *)
-    s_mnt_count : 16 : littleendian;          (* Mount count *)
-    s_max_mnt_count : 16 : littleendian;      (* Maximal mount count *)
-    0xef53 : 16 : littleendian ->             (* Magic signature *)
+  | s_inodes_count : 32 : littleendian;       (* Inodes count *)
+      s_blocks_count : 32 : littleendian;       (* Blocks count *)
+      s_r_blocks_count : 32 : littleendian;     (* Reserved blocks count *)
+      s_free_blocks_count : 32 : littleendian;  (* Free blocks count *)
+      s_free_inodes_count : 32 : littleendian;  (* Free inodes count *)
+      s_first_data_block : 32 : littleendian;   (* First Data Block *)
+      s_log_block_size : 32 : littleendian;     (* Block size *)
+      s_log_frag_size : 32 : littleendian;      (* Fragment size *)
+      s_blocks_per_group : 32 : littleendian;   (* # Blocks per group *)
+      s_frags_per_group : 32 : littleendian;    (* # Fragments per group *)
+      s_inodes_per_group : 32 : littleendian;   (* # Inodes per group *)
+      s_mtime : 32 : littleendian;              (* Mount time *)
+      s_wtime : 32 : littleendian;              (* Write time *)
+      s_mnt_count : 16 : littleendian;          (* Mount count *)
+      s_max_mnt_count : 16 : littleendian;      (* Maximal mount count *)
+      0xef53 : 16 : littleendian } ->           (* Magic signature *)
 
     printf "ext3 superblock:\n";
     printf "  s_inodes_count = %ld\n" s_inodes_count;
@@ -119,7 +120,7 @@ let () =
     printf "  s_free_inodes_count = %ld\n" s_free_inodes_count;
     printf "  s_free_blocks_count = %ld\n" s_free_blocks_count
 
-  | _ ->
+  | { _ } ->
     eprintf "not an ext3 superblock!\n%!";
     exit 2
 ]}
@@ -138,10 +139,11 @@ let () =
 *)
 
 let make_message typ subtype param =
-  (BITSTRING
+  (BITSTRING {
      typ : 16;
      subtype : 16;
-     param : 32) ;;
+     param : 32
+   }) ;;
 ]}
 
    {2 Loading, creating bitstrings}
@@ -173,9 +175,9 @@ let make_message typ subtype param =
 
    [bitmatch] {i bitstring-expression} [with]
 
-   [|] {i pattern} [->] {i code}
+   [| {] {i pattern} [} ->] {i code}
 
-   [|] {i pattern} [->] {i code}
+   [| {] {i pattern} [} ->] {i code}
 
    [|] ...
 
@@ -184,7 +186,7 @@ let make_message typ subtype param =
    match then the standard library [Match_failure] exception is
    thrown.
 
-   Patterns look a bit different from normal match patterns.  The
+   Patterns look a bit different from normal match patterns.  They
    consist of a list of bitfields separated by [;] where each bitfield
    contains a bind variable, the width (in bits) of the field, and
    other information.  Some example patterns:
@@ -192,25 +194,25 @@ let make_message typ subtype param =
 {[
 bitmatch bits with
 
-| version : 8; name : 8; param : 8 -> ...
+| { version : 8; name : 8; param : 8 } -> ...
 
    (* Bitstring of at least 3 bytes.  First byte is the version
       number, second byte is a field called name, third byte is
       a field called parameter. *)
 
-| flag : 1 ->
+| { flag : 1 } ->
    printf "flag is %b\n" flag
 
    (* A single flag bit (mapped into an OCaml boolean). *)
 
-| len : 4; data : 1+len ->
+| { len : 4; data : 1+len } ->
    printf "len = %d, data = 0x%Lx\n" len data
 
    (* A 4-bit length, followed by 1-16 bits of data, where the
       length of the data is computed from len. *)
 
-| ipv6_source : 128 : bitstring;
-  ipv6_dest : 128 : bitstring -> ...
+| ipv6_source : 128 : bitstring;
+    ipv6_dest : 128 : bitstring } -> ...
 
    (* IPv6 source and destination addresses.  Each is 128 bits
       and is mapped into a bitstring type which will be a substring
@@ -220,7 +222,7 @@ bitmatch bits with
    You can also add conditional when-clauses:
 
 {[
-| version : 4
+| { version : 4 }
     when version = 4 || version = 6 -> ...
 
    (* Only match and run the code when version is 4 or 6.  If
@@ -236,8 +238,8 @@ bitmatch bits with
    length is zero in the when-clause:
 
 {[
-| n : 4;
-  rest : -1 : bitstring
+| n : 4;
+    rest : -1 : bitstring }
     when Bitmatch.bitstring_length rest = 0 -> ...
 
    (* Only matches exactly 4 bits. *)
@@ -247,9 +249,19 @@ bitmatch bits with
    but you can also match a constant, as in:
 
 {[
-| 6 : 4 -> ...
+| { (4|6) : 4 } -> ...
 
-   (* Only matches if the first 4 bits contain the integer 6. *)
+   (* Only matches if the first 4 bits contain either
+      the integer 4 or the integer 6. *)
+]}
+
+   One may also match on strings:
+
+{[
+| { "MAGIC" : 5*8 : string } -> ...
+
+   (* Only matches if the string "MAGIC" appears at the start
+      of the input. *)
 ]}
 
    {3:patternfieldreference Pattern field reference}
@@ -278,18 +290,24 @@ bitmatch bits with
    A bitstring field of length 0 matches an empty bitstring
    (occasionally useful when matching optional subfields).
 
-   Qualifiers are a list of identifiers which control the type,
+   Qualifiers are a list of identifiers/expressions which control the type,
    signedness and endianness of the field.  Permissible qualifiers are:
 
-   - [int] (field has an integer type)
-   - [bitstring] (field is a bitstring type)
-   - [signed] (field is signed)
-   - [unsigned] (field is unsigned)
-   - [bigendian] (field is big endian - a.k.a network byte order)
-   - [littleendian] (field is little endian - a.k.a Intel byte order)
-   - [nativeendian] (field is same endianness as the machine)
-
-   The default settings are [int], [unsigned], [bigendian].
+   - [int]: field has an integer type
+   - [string]: field is a string type
+   - [bitstring]: field is a bitstring type
+   - [signed]: field is signed
+   - [unsigned]: field is unsigned
+   - [bigendian]: field is big endian - a.k.a network byte order
+   - [littleendian]: field is little endian - a.k.a Intel byte order
+   - [nativeendian]: field is same endianness as the machine
+   - [endian (expr)]: [expr] should be an expression which evaluates to
+       a {!endian} type, ie. [LittleEndian], [BigEndian] or [NativeEndian].
+       The expression is an arbitrary OCaml expression and can use the
+       value of earlier fields in the bitmatch.
+   - [offset (expr)]: see {{:#computedoffsets}computed offsets} below.
+
+   The default settings are [int], [unsigned], [bigendian], no offset.
 
    Note that many of these qualifiers cannot be used together,
    eg. bitstrings do not have endianness.  The syntax extension should
@@ -301,11 +319,11 @@ bitmatch bits with
    bitstring and/or have a default match case:
 
 {[
-| _ -> ...
+| { _ } -> ...
 
    (* Default match case. *)
 
-| _ as pkt -> ...
+| { _ } as pkt -> ...
 
    (* Default match case, with 'pkt' bound to the whole bitstring. *)
 ]}
@@ -320,9 +338,10 @@ bitmatch bits with
 let version = 1 ;;
 let data = 10 ;;
 let bits =
-  BITSTRING
+  BITSTRING {
     version : 4;
-    data : 12 ;;
+    data : 12
+  } ;;
 
    (* Constructs a 16-bit bitstring with the first four bits containing
       the integer 1, and the following 12 bits containing the integer 10,
@@ -366,10 +385,9 @@ Bitmatch.hexdump_bitstring stdout bits ;;
    length expression in the field is a compile-time constant or a
    computed expression.
 
-   Detection of compile-time constants is quite simplistic so only an
-   immediate, simple integer is recognised as a constant and anything
-   else is considered a computed expression, even expressions such as
-   [5-2] which are obviously (to our eyes) constant.
+   Detection of compile-time constants is quite simplistic so only
+   simple integer literals and simple expressions (eg. [5*8]) are
+   recognized as constants.
 
    In any case the bit size of an integer is limited to the range
    \[1..64\].  This is detected as a compile-time error if that is
@@ -400,18 +418,52 @@ Bitmatch.hexdump_bitstring stdout bits ;;
    still need to be a runtime check to enforce the
    size).
 
+   {2:computedoffsets Computed offsets}
+
+   You can add an [offset(..)] qualifier to bitmatch patterns in order
+   to move the current offset within the bitstring forwards.
+
+   For example:
+
+{[
+bitmatch bits with
+| { field1 : 8;
+    field2 : 8 : offset(160) } -> ...
+]}
+
+   matches [field1] at the start of the bitstring and [field2]
+   at 160 bits into the bitstring.  The middle 152 bits go
+   unmatched (ie. can be anything).
+
+   The generated code is efficient.  If field lengths and offsets
+   are known to be constant at compile time, then almost all
+   runtime checks are avoided.  Non-constant field lengths and/or
+   non-constant offsets can result in more runtime checks being added.
+
+   Note that moving the offset backwards, and moving the offset in
+   [BITSTRING] constructors, are both not supported at present.
+
+   {2 Named patterns and persistent patterns}
+
+   Please see {!Bitmatch_persistent} for documentation on this subject.
+
    {2 Compiling}
 
    Using the compiler directly you can do:
 
    {v
-   ocamlc -I bitmatch -pp "camlp4o -I bitmatch pa_bitmatch.cmo" foo.ml -o foo
+   ocamlc -I +bitmatch \
+     -pp "camlp4o bitmatch.cma bitmatch_persistent.cma \
+            `ocamlc -where`/bitmatch/pa_bitmatch.cmo" \
+     unix.cma bitmatch.cma test.ml -o test
    v}
 
-   Using findlib:
+   Simpler method using findlib:
 
    {v
-   ocamlfind ocamlc -package bitmatch.syntax -linkpkg foo.ml -o foo
+   ocamlfind ocamlc \
+     -package bitmatch,bitmatch.syntax -syntax bitmatch.syntax \
+     -linkpkg test.ml -o test
    v}
 
    {2 Security and type safety}
@@ -425,16 +477,16 @@ Bitmatch.hexdump_bitstring stdout bits ;;
    overflows.  In addition to OCaml's normal bounds checks, we check
    that field lengths are >= 0, and many additional checks.
 
-   Denial of service attacks are more problematic although we still
-   believe that the library is robust.  We only work forwards through
-   the bitstring, thus computation will eventually terminate.  As for
-   computed lengths, code such as this is thought to be secure:
+   Denial of service attacks are more problematic.  We only work
+   forwards through the bitstring, thus computation will eventually
+   terminate.  As for computed lengths, code such as this is thought
+   to be secure:
 
-{[
-bitmatch bits with
-| len : 64;
-  buffer : Int64.to_int len : bitstring ->
-]}
+   {[
+   bitmatch bits with
+   | { len : 64;
+       buffer : Int64.to_int len : bitstring } ->
+   ]}
 
    The [len] field can be set arbitrarily large by an attacker, but
    when pattern-matching against the [buffer] field this merely causes
@@ -443,21 +495,28 @@ bitmatch bits with
    allocation of sub-bitstrings is efficient and doesn't involve an
    arbitary-sized allocation or any copying.
 
-   The main protection against attackers should therefore be to ensure
-   that the main program will only read input bitstrings up to a
-   certain length, which is outside the scope of this library.
+   However the above does not necessarily apply to strings used in
+   matching, since they may cause the library to use the
+   {!Bitmatch.string_of_bitstring} function, which allocates a string.
+   So you should take care if you use the [string] type particularly
+   with a computed length that is derived from external input.
+
+   The main protection against attackers should be to ensure that the
+   main program will only read input bitstrings up to a certain
+   length, which is outside the scope of this library.
 
    {3 Security on output}
 
    As with the input side, computed lengths are believed to be
    safe.  For example:
 
-{[
-let len = read_untrusted_source () in
-let buffer = allocate_bitstring () in
-BITSTRING
-  buffer : len : bitstring
-]}
+   {[
+   let len = read_untrusted_source () in
+   let buffer = allocate_bitstring () in
+   BITSTRING {
+     buffer : len : bitstring
+   }
+   ]}
 
    This code merely causes a check that buffer's length is the same as
    [len].  However the program function [allocate_bitstring] must
@@ -502,19 +561,26 @@ BITSTRING
    {3 Types}
 *)
 
+type endian = BigEndian | LittleEndian | NativeEndian
+
+val string_of_endian : endian -> string
+(** Endianness. *)
+
 type bitstring = string * int * int
 (** [bitstring] is the basic type used to store bitstrings.
 
     The type contains the underlying data (a string),
     the current bit offset within the string and the
     current bit length of the string (counting from the
-    bit offset).  Note that the offsets are bits, not bytes.
+    bit offset).  Note that the offset and length are
+    in {b bits}, not bytes.
 
     Normally you don't need to use the bitstring type
     directly, since there are functions and syntax
     extensions which hide the details.
-    See {!bitstring_of_file}, {!hexdump_bitstring},
-    {!bitstring_length}.
+
+    See also {!bitstring_of_string}, {!bitstring_of_file},
+    {!hexdump_bitstring}, {!bitstring_length}.
 *)
 
 (** {3 Exceptions} *)
@@ -549,7 +615,29 @@ val make_bitstring : int -> char -> bitstring
     For example, [make_bitstring 16 '\x5a'] will create
     the bitstring [0x5a5a] or in binary [0101 1010 0101 1010].
 
-    Note that the length is in bits, not bytes. *)
+    Note that the length is in bits, not bytes.  The length does NOT
+    need to be a multiple of 8. *)
+
+val zeroes_bitstring : int -> bitstring
+(** [zeroes_bitstring] creates an [n] bit bitstring of all 0's.
+
+    Actually this is the same as {!create_bitstring}. *)
+
+val ones_bitstring : int -> bitstring
+(** [ones_bitstring] creates an [n] bit bitstring of all 1's. *)
+
+val bitstring_of_string : string -> bitstring
+(** [bitstring_of_string str] creates a bitstring
+    of length [String.length str * 8] (bits) containing the
+    bits in [str].
+
+    Note that the bitstring uses [str] as the underlying
+    string (see the representation of {!bitstring}) so you
+    should not change [str] after calling this. *)
+
+val bitstring_of_file : string -> bitstring
+(** [bitstring_of_file filename] loads the named file
+    into a bitstring. *)
 
 val bitstring_of_chan : in_channel -> bitstring
 (** [bitstring_of_chan chan] loads the contents of
@@ -557,21 +645,76 @@ val bitstring_of_chan : in_channel -> bitstring
 
     The length of the final bitstring is determined
     by the remaining input in [chan], but will always
-    be a multiple of 8 bits. *)
+    be a multiple of 8 bits.
 
-val bitstring_of_file : string -> bitstring
-(** [bitstring_of_file filename] loads the named file
-    into a bitstring. *)
+    See also {!bitstring_of_chan_max}. *)
 
-val hexdump_bitstring : out_channel -> bitstring -> unit
-(** [hexdump_bitstring chan bitstring] prints the bitstring
-    to the output channel in a format similar to the
-    Unix command [hexdump -C]. *)
+val bitstring_of_chan_max : in_channel -> int -> bitstring
+(** [bitstring_of_chan_max chan max] works like
+    {!bitstring_of_chan} but will only read up to
+    [max] bytes from the channel (or fewer if the end of input
+    occurs before that). *)
+
+val bitstring_of_file_descr : Unix.file_descr -> bitstring
+(** [bitstring_of_file_descr fd] loads the contents of
+    the file descriptor [fd] as a bitstring.
+
+    See also {!bitstring_of_chan}, {!bitstring_of_file_descr_max}. *)
+
+val bitstring_of_file_descr_max : Unix.file_descr -> int -> bitstring
+(** [bitstring_of_file_descr_max fd max] works like
+    {!bitstring_of_file_descr} but will only read up to
+    [max] bytes from the channel (or fewer if the end of input
+    occurs before that). *)
 
 val bitstring_length : bitstring -> int
 (** [bitstring_length bitstring] returns the length of
     the bitstring in bits. *)
 
+val string_of_bitstring : bitstring -> string
+(** [string_of_bitstring bitstring] converts a bitstring to a string
+    (eg. to allow comparison).
+
+    This function is inefficient.  In the best case when the bitstring
+    is nicely byte-aligned we do a [String.sub] operation.  If the
+    bitstring isn't aligned then this involves a lot of bit twiddling
+    and is particularly inefficient.
+
+    If the bitstring is not a multiple of 8 bits wide then the
+    final byte of the string contains the high bits set to the
+    remaining bits and the low bits set to 0. *)
+
+val bitstring_to_file : bitstring -> string -> unit
+(** [bitstring_to_file bits filename] writes the bitstring [bits]
+    to the file [filename].  It overwrites the output file.
+
+    Some restrictions apply, see {!bitstring_to_chan}. *)
+
+val bitstring_to_chan : bitstring -> out_channel -> unit
+(** [bitstring_to_file bits filename] writes the bitstring [bits]
+    to the channel [chan].
+
+    Channels are made up of bytes, bitstrings can be any bit length
+    including fractions of bytes.  So this function only works
+    if the length of the bitstring is an exact multiple of 8 bits
+    (otherwise it raises [Invalid_argument "bitstring_to_chan"]).
+
+    Furthermore the function is efficient only in the case where
+    the bitstring is stored fully aligned, otherwise it has to
+    do inefficient bit twiddling like {!string_of_bitstring}.
+
+    In the common case where the bitstring was generated by the
+    [BITSTRING] operator and is an exact multiple of 8 bits wide,
+    then this function will always work efficiently.
+*)
+
+(** {3 Printing bitstrings} *)
+
+val hexdump_bitstring : out_channel -> bitstring -> unit
+(** [hexdump_bitstring chan bitstring] prints the bitstring
+    to the output channel in a format similar to the
+    Unix command [hexdump -C]. *)
+
 (** {3 Bitstring buffer} *)
 
 module Buffer : sig
@@ -588,6 +731,12 @@ end
 
 (** {3 Miscellaneous} *)
 
+val package : string
+(** The package name, always ["ocaml-bitmatch"] *)
+
+val version : string
+(** The package version as a string. *)
+
 val debug : bool ref
 (** Set this variable to true to enable extended debugging.
     This only works if debugging was also enabled in the
@@ -612,16 +761,48 @@ val extract_int_be_unsigned : string -> int -> int -> int -> int * int * int
 
 val extract_int_le_unsigned : string -> int -> int -> int -> int * int * int
 
+val extract_int_ne_unsigned : string -> int -> int -> int -> int * int * int
+
+val extract_int_ee_unsigned : endian -> string -> int -> int -> int -> int * int * int
+
 val extract_int32_be_unsigned : string -> int -> int -> int -> int32 * int * int
 
 val extract_int32_le_unsigned : string -> int -> int -> int -> int32 * int * int
 
+val extract_int32_ne_unsigned : string -> int -> int -> int -> int32 * int * int
+
+val extract_int32_ee_unsigned : endian -> string -> int -> int -> int -> int32 * int * int
+
 val extract_int64_be_unsigned : string -> int -> int -> int -> int64 * int * int
 
-val construct_bit : Buffer.t -> bool -> int -> unit
+val extract_int64_le_unsigned : string -> int -> int -> int -> int64 * int * int
+
+val extract_int64_ne_unsigned : string -> int -> int -> int -> int64 * int * int
+
+val extract_int64_ee_unsigned : endian -> string -> int -> int -> int -> int64 * int * int
+
+val construct_bit : Buffer.t -> bool -> int -> exn -> unit
 
 val construct_char_unsigned : Buffer.t -> int -> int -> exn -> unit
 
 val construct_int_be_unsigned : Buffer.t -> int -> int -> exn -> unit
 
+val construct_int_ne_unsigned : Buffer.t -> int -> int -> exn -> unit
+
+val construct_int_ee_unsigned : endian -> Buffer.t -> int -> int -> exn -> unit
+
+val construct_int32_be_unsigned : Buffer.t -> int32 -> int -> exn -> unit
+
+val construct_int32_ne_unsigned : Buffer.t -> int32 -> int -> exn -> unit
+
+val construct_int32_ee_unsigned : endian -> Buffer.t -> int32 -> int -> exn -> unit
+
 val construct_int64_be_unsigned : Buffer.t -> int64 -> int -> exn -> unit
+
+val construct_int64_ne_unsigned : Buffer.t -> int64 -> int -> exn -> unit
+
+val construct_int64_ee_unsigned : endian -> Buffer.t -> int64 -> int -> exn -> unit
+
+val construct_string : Buffer.t -> string -> unit
+
+val construct_bitstring : Buffer.t -> bitstring -> unit