[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Mldonkey-commits] mldonkey config/Makefile.in config/configure.in...
From: |
mldonkey-commits |
Subject: |
[Mldonkey-commits] mldonkey config/Makefile.in config/configure.in... |
Date: |
Sun, 29 Aug 2010 20:17:57 +0000 |
CVSROOT: /sources/mldonkey
Module name: mldonkey
Changes by: spiralvoice <spiralvoice> 10/08/29 20:17:57
Modified files:
config : Makefile.in configure.in
distrib : ChangeLog
src/networks/bittorrent: bTClients.ml bTComplexOptions.ml
bTGlobals.ml bTInteractive.ml
bTTorrent.ml bTTypes.ml
src/utils/lib : options.ml4
src/utils/net : basicSocket.ml ip.ml udpSocket.ml
Added files:
src/networks/bittorrent: bTUdpTracker.mlp
src/utils/bitstring: bitstring.ml.in bitstring.mli bitstring_c.c
bitstring_persistent.mlc4
bitstring_persistent.mli byteswap.h
pa_bitstring.mlt
Log message:
patch 7144
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/mldonkey/config/Makefile.in?cvsroot=mldonkey&r1=1.189&r2=1.190
http://cvs.savannah.gnu.org/viewcvs/mldonkey/config/configure.in?cvsroot=mldonkey&r1=1.337&r2=1.338
http://cvs.savannah.gnu.org/viewcvs/mldonkey/distrib/ChangeLog?cvsroot=mldonkey&r1=1.1466&r2=1.1467
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/networks/bittorrent/bTClients.ml?cvsroot=mldonkey&r1=1.105&r2=1.106
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/networks/bittorrent/bTComplexOptions.ml?cvsroot=mldonkey&r1=1.43&r2=1.44
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/networks/bittorrent/bTGlobals.ml?cvsroot=mldonkey&r1=1.86&r2=1.87
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/networks/bittorrent/bTInteractive.ml?cvsroot=mldonkey&r1=1.160&r2=1.161
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/networks/bittorrent/bTTorrent.ml?cvsroot=mldonkey&r1=1.22&r2=1.23
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/networks/bittorrent/bTTypes.ml?cvsroot=mldonkey&r1=1.47&r2=1.48
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/networks/bittorrent/bTUdpTracker.mlp?cvsroot=mldonkey&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/bitstring/bitstring.ml.in?cvsroot=mldonkey&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/bitstring/bitstring.mli?cvsroot=mldonkey&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/bitstring/bitstring_c.c?cvsroot=mldonkey&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/bitstring/bitstring_persistent.mlc4?cvsroot=mldonkey&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/bitstring/bitstring_persistent.mli?cvsroot=mldonkey&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/bitstring/byteswap.h?cvsroot=mldonkey&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/bitstring/pa_bitstring.mlt?cvsroot=mldonkey&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/lib/options.ml4?cvsroot=mldonkey&r1=1.30&r2=1.31
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/net/basicSocket.ml?cvsroot=mldonkey&r1=1.34&r2=1.35
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/net/ip.ml?cvsroot=mldonkey&r1=1.29&r2=1.30
http://cvs.savannah.gnu.org/viewcvs/mldonkey/src/utils/net/udpSocket.ml?cvsroot=mldonkey&r1=1.23&r2=1.24
Patches:
Index: config/Makefile.in
===================================================================
RCS file: /sources/mldonkey/mldonkey/config/Makefile.in,v
retrieving revision 1.189
retrieving revision 1.190
diff -u -b -r1.189 -r1.190
--- config/Makefile.in 29 Aug 2010 20:11:20 -0000 1.189
+++ config/Makefile.in 29 Aug 2010 20:17:56 -0000 1.190
@@ -68,6 +68,7 @@
CDK=src/utils/cdk
+BITSTRING=src/utils/bitstring
LIB=src/utils/lib
NET=src/utils/net
RSS=src/utils/ocamlrss
@@ -88,7 +89,7 @@
SRC_DIRECTCONNECT=src/networks/direct_connect
SRC_FILETP=src/networks/fileTP
-SUBDIRS=$(CDK) $(LIB) $(RSS) $(XML) $(NET) tools \
+SUBDIRS=$(CDK) $(BITSTRING) $(LIB) $(RSS) $(XML) $(NET) tools \
$(COMMON) $(DRIVER) $(MP3) src/config/$(OS_FILES)
INCLUDES += $(foreach file, $(SUBDIRS), -I $(file)) -I +camlp4
@@ -132,6 +133,16 @@
LIBS_flags += -cclib "resfile.o"
endif
+BITSTRING_SRCS = \
+ $(BITSTRING)/bitstring.ml \
+ $(BITSTRING)/bitstring_persistent.mlc4 \
+ $(BITSTRING)/bitstring_c.c
+
+BITSTRING_LIBS_flags = -I +camlp4
+BITSTRING_LIBS_opt = $(CAMLP4LIB_OPT)
+BITSTRING_STATIC_LIBS_opt = $(BITSTRING_LIBS_opt)
+BITSTRING_LIBS_byte = $(CAMLP4LIB_BYTE)
+
ifeq ("$(BZIP2)", "yes")
LIBS_flags += -cclib -lbz2
CDK_SRCS += $(CDK)/bzlib.ml $(CDK)/bzip2.ml
@@ -425,11 +436,19 @@
$(SRC_FASTTRACK)/fasttrackInteractive.mlt \
$(SRC_FASTTRACK)/fasttrackMain.mlt
+$(BITSTRING)/pa_bitstring.cmo: $(BITSTRING)/pa_bitstring.mlt
build/bitstring.cma
+ $(OCAMLC) build/bitstring.cma -I $(BITSTRING) -I +camlp4 camlp4lib.cma
-pp '$(CAMLP4OF) -impl' -c $<
+
+$(SRC_BITTORRENT)/bTUdpTracker.ml: $(SRC_BITTORRENT)/bTUdpTracker.mlp
$(BITSTRING)/pa_bitstring.cmo
+ $(CAMLP4OF) build/bitstring.cma $(BITSTRING)/pa_bitstring.cmo -impl $<
-o $@
+
BITTORRENT_SRCS= \
+ $(BITSTRING_SRCS) \
$(SRC_BITTORRENT)/bencode.ml \
$(SRC_BITTORRENT)/bTRate.ml \
$(SRC_BITTORRENT)/bTTypes.ml \
$(SRC_BITTORRENT)/bTOptions.ml \
+ $(SRC_BITTORRENT)/bTUdpTracker.ml \
$(SRC_BITTORRENT)/bTProtocol.ml \
$(SRC_BITTORRENT)/bTTorrent.ml \
$(SRC_BITTORRENT)/bTGlobals.ml \
@@ -1101,16 +1120,17 @@
$1_MLL := $(filter %.mll, $($1_SRCS))
$1_MLY := $(filter %.mly, $($1_SRCS))
$1_ML4 := $(filter %.ml4, $($1_SRCS))
+$1_MLC4 := $(filter %.mlc4, $($1_SRCS))
$1_MLT := $(filter %.mlt, $($1_SRCS))
$1_MLP := $(filter %.mlcpp, $($1_SRCS))
-$1_ML := $(filter %.ml %.mll %.zog %.mly %.ml4 %.mlt %.mlcpp, $($1_SRCS))
-$1_DOC := $(filter %.ml %.mll %.zog %.mly %.ml4 %.mlcpp, $($1_SRCS))
+$1_ML := $(filter %.ml %.mll %.zog %.mly %.ml4 %.mlc4 %.mlt %.mlcpp,
$($1_SRCS))
+$1_DOC := $(filter %.ml %.mll %.zog %.mly %.ml4 %.mlc4 %.mlcpp, $($1_SRCS))
$1_C := $(filter %.c %.cc, $($1_SRCS))
$1_CMOS=$(foreach file, $($1_ML), $(basename $(file)).cmo)
$1_CMXS=$(foreach file, $($1_ML), $(basename $(file)).cmx)
$1_OBJS=$(foreach file, $($1_C), $(basename $(file)).o)
-TMPSOURCES += $($1_ML4:.ml4=.ml) $($1_MLT:.mlt=.ml) $($1_MLP:.mlcpp=.ml)
$($1_MLL:.mll=.ml) $($1_MLY:.mly=.ml) $($1_MLY:.mly=.mli) $($1_ZOG:.zog=.ml)
+TMPSOURCES += $($1_ML4:.ml4=.ml) $($1_MLC4:.mlc4=.ml) $($1_MLT:.mlt=.ml)
$($1_MLP:.mlcpp=.ml) $($1_MLL:.mll=.ml) $($1_MLY:.mly=.ml) $($1_MLY:.mly=.mli)
$($1_ZOG:.zog=.ml)
ZOGSOURCES += $($1_ZOG:.zog=.ml)
MLTSOURCES += $($1_MLT:.mlt=.ml)
@@ -1170,6 +1190,7 @@
libcdk_SRCS= $(CDK_SRCS) $(LIB_SRCS) $(NET_SRCS) $(MP3TAG_SRCS)
libmagic_SRCS= $(MAGIC_SRCS)
+libbitstring_SRCS= $(BITSTRING_SRCS)
libcommon_SRCS= $(COMMON_SRCS)
libclient_SRCS= $(COMMON_CLIENT_SRCS)
ifeq ("$(GUI)", "newgui2")
@@ -1185,6 +1206,7 @@
EXPAND_LIB(libicons,icons)
EXPAND_LIB(libcdk,cdk)
EXPAND_LIB(libmagic,magic)
+EXPAND_LIB(libbitstring,bitstring)
EXPAND_LIB(libcommon,common)
EXPAND_LIB(libclient,client)
EXPAND_LIB(DRIVER,driver)
@@ -1209,9 +1231,10 @@
$1_MLL := $(filter %.mll, $($1_SRCS))
$1_MLY := $(filter %.mly, $($1_SRCS))
$1_ML4 := $(filter %.ml4, $($1_SRCS))
+$1_MLC4 := $(filter %.mlc4, $($1_SRCS))
$1_MLT := $(filter %.mlt, $($1_SRCS))
$1_MLP := $(filter %.mlcpp, $($1_SRCS))
-$1_ML := $(filter %.ml %.mll %.zog %.mly %.ml4 %.mlt %.mlcpp, $($1_SRCS))
+$1_ML := $(filter %.ml %.mll %.zog %.mly %.ml4 %.mlc4 %.mlt %.mlcpp,
$($1_SRCS))
$1_C := $(filter %.c %.cc, $($1_SRCS))
$1_CMOS=$(foreach file, $($1_ML), $(basename $(file)).cmo)
$1_CMXS=$(foreach file, $($1_ML), $(basename $(file)).cmx)
@@ -1220,7 +1243,7 @@
$1_CMXAS := $(foreach file, $($1_CMXA), build/$(basename $(file)).cmxa)
$1_CMAS=$(foreach file, $($1_CMXA), build/$(basename $(file)).cma)
-TMPSOURCES += $($1_ML4:.ml4=.ml) $($1_MLT:.mlt=.ml) $($1_MLP:.mlcpp=.ml)
$($1_MLL:.mll=.ml) $($1_MLY:.mly=.ml) $($1_MLY:.mly=.mli) $($1_ZOG:.zog=.ml)
+TMPSOURCES += $($1_ML4:.ml4=.ml) $($1_MLC4:.mlc4=.ml) $($1_MLT:.mlt=.ml)
$($1_MLP:.mlcpp=.ml) $($1_MLL:.mll=.ml) $($1_MLY:.mly=.ml) $($1_MLY:.mly=.mli)
$($1_ZOG:.zog=.ml)
$2: $($1_OBJS) $($1_CMXS) $($1_CMXAS)
$(OCAMLOPT) -linkall -o address@hidden \
@@ -1229,6 +1252,7 @@
$($5_LIBS_opt) $($5_LIBS_flags) \
$($6_LIBS_opt) $($6_LIBS_flags) \
$($7_LIBS_opt) $($7_LIBS_flags) \
+ $($8_LIBS_opt) $($8_LIBS_flags) \
-I build $($1_CMXAS) $($1_CMXS)
$2.byte: $($1_OBJS) $($1_CMOS) $($1_CMAS)
@@ -1238,6 +1262,7 @@
$($5_LIBS_byte) $($5_LIBS_flags) \
$($6_LIBS_byte) $($6_LIBS_flags) \
$($7_LIBS_byte) $($7_LIBS_flags) \
+ $($8_LIBS_byte) $($8_LIBS_flags) \
-I build $($1_CMAS) $($1_CMOS)
$2.static: $($1_OBJS) $($1_CMXS) $($1_CMXAS)
@@ -1247,6 +1272,7 @@
$($5_LIBS_flags) $($5_STATIC_LIBS_opt) \
$($6_LIBS_flags) $($6_STATIC_LIBS_opt) \
$($7_LIBS_flags) $($7_STATIC_LIBS_opt) \
+ $($8_LIBS_flags) $($8_STATIC_LIBS_opt) \
-I build $($1_CMXAS) $($1_CMXS)
$2.byte.static: $($1_OBJS) $($1_CMOS) $($1_CMAS)
@@ -1256,6 +1282,7 @@
$($5_LIBS_flags) $($5_STATIC_LIBS_opt) \
$($6_LIBS_flags) $($6_STATIC_LIBS_opt) \
$($7_LIBS_flags) $($7_STATIC_LIBS_opt) \
+ $($8_LIBS_flags) $($8_STATIC_LIBS_opt) \
-I build $($1_CMAS) $($1_CMOS)
]])
@@ -1266,6 +1293,7 @@
# $5 = if set link GD code
# $6 = if set link CryptoPP code (only for targets mlnet, mldonkey)
# $7 = if set link libmagic code (only for p2p core, not for GUIs, tools etc.)
+# $8 = if set link libbitstring code (only for Bittorrent p2p core)
EXPAND(mldonkey,mldonkey,NO,mldonkey,GD,CRYPTOPP,MAGIC)
EXPAND(mldonkey+gui,mldonkey+gui,GTK,mldonkey+gui,GD,CRYPTOPP,MAGIC)
@@ -1280,14 +1308,14 @@
EXPAND(mldc+gui,mldc+gui,GTK,mldc+gui,GD,NO,MAGIC)
EXPAND(mlnap,mlnap,NO,mlnap,GD,NO,MAGIC)
EXPAND(mlnap+gui,mlnap+gui,GTK,mlnap+gui,GD,NO,MAGIC)
-EXPAND(MLNET,mlnet,NO,MLNET,GD,CRYPTOPP,MAGIC)
-EXPAND(mlnet+gui,mlnet+gui,GTK,mlnet+gui,GD,CRYPTOPP,MAGIC)
+EXPAND(MLNET,mlnet,NO,MLNET,GD,CRYPTOPP,MAGIC,BITSTRING)
+EXPAND(mlnet+gui,mlnet+gui,GTK,mlnet+gui,GD,CRYPTOPP,MAGIC,BITSTRING)
EXPAND(mlgnut,mlgnut,NO,mlgnut,GD,NO,MAGIC)
EXPAND(mlgnut+gui,mlgnut+gui,GTK,mlgnut+gui,GD,NO,MAGIC)
EXPAND(mlg2,mlg2,NO,mlg2,GD,NO,MAGIC)
EXPAND(mlg2+gui,mlg2+gui,GTK,mlg2+gui,GD,NO,MAGIC)
-EXPAND(mlbt,mlbt,NO,mlbt,GD,NO,MAGIC)
-EXPAND(mlbt+gui,mlbt+gui,GTK,mlbt+gui,GD,NO,MAGIC)
+EXPAND(mlbt,mlbt,NO,mlbt,GD,NO,MAGIC,BITSTRING)
+EXPAND(mlbt+gui,mlbt+gui,GTK,mlbt+gui,GD,NO,MAGIC,BITSTRING)
EXPAND(mlfasttrack,mlfasttrack,NO,mlfasttrack,GD,NO,MAGIC)
EXPAND(mlfasttrack+gui,mlfasttrack+gui,GTK,mlfasttrack+gui,GD,NO,MAGIC)
EXPAND(mlfileTP,mlfiletp,NO,mlfileTP,GD,NO,MAGIC)
@@ -1298,7 +1326,7 @@
EXPAND(OBSERVER,observer)
EXPAND(MLD_HASH,mld_hash)
EXPAND(OCAMLPP,ocamlpp)
-EXPAND(MAKE_TORRENT,make_torrent,NO,NO,NO,NO,MAGIC)
+EXPAND(MAKE_TORRENT,make_torrent,NO,NO,NO,NO,MAGIC,BITSTRING)
EXPAND(SUBCONV,subconv)
EXPAND(MLSPLIT,mlsplit)
EXPAND(CONTESTER,contester,CRYPT)
@@ -1397,6 +1425,7 @@
rm -f build/*.a build/*.cma build/*.cmxa
rm -f *_plugin
rm -f mldonkey mlgui mlnet.exe mlgui.exe mldonkeytop mldonkeytop.exe
+ rm -f mlbt mlbt.exe mlfiletp mlfiletp.exe
rm -f svg_converter svg_converter.byte mld_hash make_torrent
copysources get_range subconv testrss
rm -f svg_converter.exe mld_hash.exe make_torrent.exe copysources.exe
get_range.exe subconv.exe testrss.exe
(for i in $(SUBDIRS); do \
@@ -1422,7 +1451,9 @@
rm -f packages/slackware/mldonkey.options
rm -f packages/windows/mlnet.nsi
rm -f src/daemon/driver/driverGraphics.ml
+ rm -f src/networks/bittorrent/bTUdpTracker.ml
rm -f src/networks/donkey/donkeySui.ml
+ rm -f src/utils/bitstring/bitstring.ml
rm -f src/utils/lib/autoconf.ml
rm -f src/utils/lib/autoconf.ml.new
rm -f src/utils/lib/gAutoconf.ml
@@ -1483,12 +1514,12 @@
depend: $(RESFILE) \
$(PA_ZOG) $(LIB)/http_lexer.ml $(TMPSOURCES)
- $(OCAMLDEP) $(OCAMLDEP_OPTIONS) $(patsubst -I +labl$(GTK),,$(INCLUDES))
*.ml *.mli > .depend
- (for i in $(SUBDIRS); do \
+ @$(OCAMLDEP) $(OCAMLDEP_OPTIONS) $(patsubst -I
+labl$(GTK),,$(INCLUDES)) *.ml *.mli > .depend
+ @(for i in $(SUBDIRS); do \
$(OCAMLDEP) $(OCAMLDEP_OPTIONS) $(patsubst -I
+labl$(GTK),,$(INCLUDES)) $$i/*.ml $$i/*.mli >> .depend; \
$(OCAMLPP) $$i/*.mlt >> .depend; \
done)
- if test "$(GUI)" = "newgui2"; then \
+ @if test "$(GUI)" = "newgui2"; then \
$(MAKE) svg_converter.byte; \
fi
@@ -1728,7 +1759,7 @@
-include .depend
-.SUFFIXES: .mli .ml .cmx .cmo .o .c .cmi .mll .mly .zog .plugindep .xpm .ml
.cc .ml_icons .ml4 .mlt .mlii .mlcpp .svg
+.SUFFIXES: .mli .ml .cmx .cmo .o .c .cmi .mll .mly .zog .plugindep .xpm .ml
.cc .ml_icons .ml4 .mlc4 .mlt .mlii .mlcpp .svg
.mli.cmi :
$(OCAMLC) $(INCLUDES) -c $<
@@ -1761,26 +1792,29 @@
$(OCAMLC) $(DEVFLAGS) $(INCLUDES) -c $<
.mlcpp.ml:
- cpp -P $< $(FIX_BROKEN_CPP) > $@
+ @cpp -P $< $(FIX_BROKEN_CPP) > $@
.mll.ml :
- $(OCAMLLEX) $<
+ @$(OCAMLLEX) -q $<
.mly.ml :
- $(OCAMLYACC) $<
+ @$(OCAMLYACC) $<
.mly.mli:
- $(OCAMLYACC) $<
+ @$(OCAMLYACC) $<
.zog.ml:
- $(CAMLP4) pa_o.cmo ./pa_zog.cma pr_o.cmo -impl $< > $@
+ @$(CAMLP4) pa_o.cmo ./pa_zog.cma pr_o.cmo -impl $< > $@
.ml4.ml:
- echo '# 1 "$<"' > $@
- $(CAMLP4) pa_o.cmo pa_op.cmo pr_o.cmo -impl $< >> $@
+ @echo '# 1 "$<"' > $@
+ @$(CAMLP4) pa_o.cmo pa_op.cmo pr_o.cmo -impl $< >> $@
+
+.mlc4.ml:
+ @$(CAMLP4OF) -I +camlp4 -impl $< -o $@
.mlt.ml:
- $(OCAMLPP) -pp $< > $@
+ @$(OCAMLPP) -pp $< > $@
.c.o :
$(OCAMLC) -verbose -ccopt "-I $(OCAML_SRC)/byterun -o $*.o" -ccopt
"$(CFLAGS)" $(LIBS_flags) -c $<
Index: config/configure.in
===================================================================
RCS file: /sources/mldonkey/mldonkey/config/configure.in,v
retrieving revision 1.337
retrieving revision 1.338
diff -u -b -r1.337 -r1.338
--- config/configure.in 29 Aug 2010 20:11:20 -0000 1.337
+++ config/configure.in 29 Aug 2010 20:17:56 -0000 1.338
@@ -718,6 +718,17 @@
])
+dnl Check for native endianness.
+AC_C_BIGENDIAN(,,
+ [AC_MSG_ERROR([Machine endianness could not be determined])]
+)
+if test "x$WORDS_BIGENDIAN" = "x"; then
+ NATIVEENDIAN=LittleEndian
+else
+ NATIVEENDIAN=BigEndian
+fi
+AC_SUBST(NATIVEENDIAN)
+
# various header files
AC_CHECK_FUNCS(setrlimit getrlimit strerror_r strerror posix_fallocate)
AC_CHECK_HEADERS(byteswap.h,,)
@@ -1446,6 +1457,7 @@
Makefile.config \
mldonkey.rc \
$AUTOCONF.new $GTK_AUTOCONF.new \
+ ../src/utils/bitstring/bitstring.ml \
../src/utils/lib/magic.ml \
../src/networks/donkey/donkeySui.ml \
../src/daemon/driver/driverGraphics.ml \
Index: distrib/ChangeLog
===================================================================
RCS file: /sources/mldonkey/mldonkey/distrib/ChangeLog,v
retrieving revision 1.1466
retrieving revision 1.1467
diff -u -b -r1.1466 -r1.1467
--- distrib/ChangeLog 29 Aug 2010 20:13:31 -0000 1.1466
+++ distrib/ChangeLog 29 Aug 2010 20:17:56 -0000 1.1467
@@ -15,6 +15,7 @@
=========
2010/08/29
+7144: BT: Support for UDP trackers (ygrek)
7288: HTML: Prepare localization (balamutick)
7287: Fix GTK1 oldgui compile with Ocaml 3.1* (ygrek)
7289: Raise minimum required Ocaml version to 3.10.1
Index: src/networks/bittorrent/bTClients.ml
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTClients.ml,v
retrieving revision 1.105
retrieving revision 1.106
diff -u -b -r1.105 -r1.106
--- src/networks/bittorrent/bTClients.ml 23 May 2010 09:12:14 -0000
1.105
+++ src/networks/bittorrent/bTClients.ml 29 Aug 2010 20:17:56 -0000
1.106
@@ -19,6 +19,7 @@
(** Functions used in client<->client communication
+ and also client<->tracker
*)
(** A peer (or client) is always a remote peer in this file.
@@ -73,6 +74,142 @@
let next_uploaders = ref ([] : BTTypes.client list)
let current_uploaders = ref ([] : BTTypes.client list)
+(** Check that client is valid and record it *)
+let maybe_new_client file id ip port =
+ let cc = Geoip.get_country_code_option ip in
+ if id <> !!client_uid
+ && ip != Ip.null
+ && port <> 0
+ && (match !Ip.banned (ip, cc) with
+ | None -> true
+ | Some reason ->
+ if !verbose_connect then
+ lprintf_file_nl (as_file file) "%s:%d blocked: %s" (Ip.to_string
ip) port reason;
+ false)
+ then
+ ignore (new_client file id (ip,port) cc);
+ if !verbose_sources > 1 then
+ lprintf_file_nl (as_file file) "Received %s:%d" (Ip.to_string ip) port
+
+
+let resume_clients_hook = ref (fun _ -> assert false)
+
+include struct
+
+(* open modules locally *)
+open BTUdpTracker
+open UdpSocket
+
+let string_of_event = function
+ | READ_DONE -> "READ_DONE"
+ | WRITE_DONE -> "WRITE_DONE"
+ | CAN_REFILL -> "CAN_REFILL"
+ | BASIC_EVENT e -> match e with
+ | CLOSED reason -> "CLOSED " ^ (string_of_reason reason)
+ | RTIMEOUT -> "RTIMEOUT"
+ | WTIMEOUT -> "WTIMEOUT"
+ | LTIMEOUT -> "LTIMEOUT"
+ | CAN_READ -> "CAN_READ"
+ | CAN_WRITE -> "CAN_WRITE"
+
+(** talk to udp tracker and parse response
+ except of parsing should perform everything that
+ talk_to_tracker's inner function does FIXME refactor both
+
+ Better create single global udp socket and use it for all
+ tracker requests and distinguish trackers by txn? FIXME?
+ *)
+let talk_to_udp_tracker host port args file t need_sources =
+ let interact ip =
+ let socket = create (Ip.to_inet_addr !!client_bind_addr) 0 (fun sock event
->
+(* lprintf_nl "udpt got event %s for %s" (string_of_event event) host; *)
+ match event with
+ | WRITE_DONE | CAN_REFILL -> ()
+ | READ_DONE -> assert false (* set_reader prevents this *)
+ | BASIC_EVENT x -> match x with
+ | CLOSED _ -> ()
+ | CAN_READ | CAN_WRITE -> assert false (* udpSocket implementation
prevents this *)
+ | LTIMEOUT | WTIMEOUT | RTIMEOUT -> close sock (Closed_for_error "udpt
timeout"))
+ in
+ let set_reader f =
+ set_reader socket begin fun _ ->
+ try f () with exn ->
+ lprintf_nl "udpt interact exn %s" (Printexc2.to_string exn);
+ close socket (Closed_for_exception exn)
+ end
+ in
+ BasicSocket.set_wtimeout (sock socket) 60.;
+ BasicSocket.set_rtimeout (sock socket) 60.;
+ let txn = Random.int32 Int32.max_int in
+(* lprintf_nl "udpt txn %ld for %s" txn host; *)
+ write socket false (connect_request txn) ip port;
+ set_reader begin fun () ->
+ let p = read socket in
+ let conn = connect_response p.udp_content txn in
+(* lprintf_nl "udpt connection_id %Ld for %s" conn host; *)
+ let txn = Random.int32 Int32.max_int in
+(* lprintf_nl "udpt txn' %ld for host %s" txn host; *)
+ let int s = Int64.of_string (List.assoc s args) in
+ let req = announce_request conn txn
+ ~info_hash:(List.assoc "info_hash" args)
+ ~peer_id:(List.assoc "peer_id" args)
+ (int "downloaded",int "left",int "uploaded")
+ (match try List.assoc "event" args with Not_found -> "" with
+ | "completed" -> 1l
+ | "started" -> 2l
+ | "stopped" -> 3l
+ | "" -> 0l
+ | s -> lprintf_nl "udpt event %s? for %s" s host; 0l)
+ ~ip:(if !!force_client_ip then (Int64.to_int32 (Ip.to_int64
!!set_client_ip)) else 0l)
+ ~numwant:(if need_sources then try Int32.of_string (List.assoc
"numwant" args) with _ -> -1l else 0l)
+ (int_of_string (List.assoc "port" args))
+ in
+ write socket false req ip port;
+ set_reader (fun () ->
+ let p = read socket in
+
+ t.tracker_last_conn <- last_time ();
+ file.file_tracker_connected <- true;
+ t.tracker_interval <- 600;
+ t.tracker_min_interval <- 600;
+ if need_sources then t.tracker_last_clients_num <- 0;
+
+ let (interval,clients) = announce_response p.udp_content txn in
+ if !verbose_msg_servers then
+ lprintf_nl "udpt got interval %ld clients %d for host %s" interval
(List.length clients) host;
+ if interval > 0l then
+ begin
+ t.tracker_interval <- Int32.to_int interval;
+ if t.tracker_min_interval > t.tracker_interval then
+ t.tracker_min_interval <- t.tracker_interval
+ end;
+ if need_sources then
+ List.iter (fun (ip',port) ->
+ let ip = Ip.of_int64 (Int64.logand 0xFFFFFFFFL (Int64.of_int32 ip'))
in
+(* lprintf_nl "udpt got %s:%d" (Ip.to_string ip) port; *)
+ t.tracker_last_clients_num <- t.tracker_last_clients_num + 1;
+ maybe_new_client file Sha1.null ip port
+ ) clients;
+ close socket Closed_by_user;
+ if !verbose_msg_servers then
+ lprintf_nl "udpt interact done for %s" host;
+ if need_sources then !resume_clients_hook file
+ ) end
+ in
+ try
+ if !verbose_msg_servers then
+ lprintf_nl "udpt start with %s:%d" host port;
+ Ip.async_ip host (fun ip ->
+(* lprintf_nl "udpt resolved %s to ip %s" host (Ip.to_string ip); *)
+ try interact ip with exn -> lprintf_nl "udpt interact exn %s"
(Printexc2.to_string exn))
+ (fun n ->
+ if !verbose_msg_servers then
+ lprintf_nl "udpt failed to resolve %s (%d)" host n)
+ with
+ exn ->
+ lprintf_nl "udpt start exn %s" (Printexc2.to_string exn)
+
+end (* include *)
(**
In this function we connect to a tracker.
@@ -182,14 +319,15 @@
then
begin
(* if we already tried to connect but failed, disable tracker, but
allow re-enabling *)
+ (* FIXME t.tracker_last_conn < 1 only at first connect, so later
failures will stay undetected! *)
if file.file_tracker_connected && t.tracker_last_clients_num = 0 &&
t.tracker_last_conn < 1 then
begin
if !verbose_msg_servers then
- lprintf_nl "Request error from tracker: disabling %s"
t.tracker_url;
+ lprintf_nl "Request error from tracker: disabling %s"
(show_tracker_url t.tracker_url);
t.tracker_status <- Disabled (intern "MLDonkey: Request error from
tracker")
end
(* Send request to tracker *)
- else begin
+ else
let args = if String.length t.tracker_id > 0 then
("trackerid", t.tracker_id) :: args else args
in
@@ -197,13 +335,14 @@
("key", t.tracker_key) :: args else args
in
if !verbose_msg_servers then
- lprintf_nl "connect_trackers: tracker_connected:%s id:%s
key:%s last_clients:%i last_conn-last_time:%i file: %s"
+ lprintf_nl "connect_trackers: connected:%s id:%s key:%s
last_clients:%i last_conn-last_time:%i numwant:%s file: %s"
(string_of_bool file.file_tracker_connected)
t.tracker_id t.tracker_key t.tracker_last_clients_num
- (t.tracker_last_conn - last_time()) file.file_name;
+ (t.tracker_last_conn - last_time()) (try List.assoc "numwant"
args with _ -> "_") file.file_name;
+ match t.tracker_url with
+ | `Http url ->
let module H = Http_client in
- let url = t.tracker_url in
let r = {
H.basic_request with
H.req_url = Url.of_string ~args: args url;
@@ -215,19 +354,20 @@
if !verbose_msg_servers then
lprintf_nl "Request sent to tracker %s for file: %s"
- t.tracker_url file.file_name;
+ url file.file_name;
H.wget r
(fun fileres ->
t.tracker_last_conn <- last_time ();
file.file_tracker_connected <- true;
f t fileres)
- end
+ | `Other url -> assert false (* should have been disabled *)
+ | `Udp (host,port) -> talk_to_udp_tracker host port args file t
need_sources
end
else
if !verbose_msg_servers then
lprintf_nl "Request NOT sent to tracker %s - next request in %ds for
file: %s"
- t.tracker_url (t.tracker_interval - (last_time () -
t.tracker_last_conn)) file.file_name
+ (show_tracker_url t.tracker_url) (t.tracker_interval - (last_time
() - t.tracker_last_conn)) file.file_name
) enabled_trackers
let start_upload c =
@@ -363,7 +503,9 @@
let download_finished file =
if List.memq file !current_files then
begin
- connect_trackers file "completed" false (fun _ _ -> ()); (*must be
called before swarmer gets removed from file*)
+ connect_trackers file "completed" false (fun _ _ ->
+ lprintf_file_nl (as_file file) "Tracker return: completed %s"
file.file_name;
+ ()); (*must be called before swarmer gets removed from file*)
(*CommonComplexOptions.file_completed*)
file_completed (as_file file);
(* Remove the swarmer for this file as it is not useful anymore... *)
@@ -1313,6 +1455,9 @@
lprintf_file_nl (as_file file) "Exception %s in resume_clients"
(Printexc2.to_string e)
) file.file_clients
+let () =
+ resume_clients_hook := resume_clients
+
(** Check if the value replied by the tracker is correct.
@param key the name of the key
@param n the value to check
@@ -1322,32 +1467,14 @@
let chk_keyval key n url name =
let int_n = (Int64.to_int n) in
if !verbose_msg_clients then
- lprintf_nl "Reply from %s in file: %s has %s: %d" url name key int_n;
+ lprintf_nl "Reply from %s in file: %s has %s: %d" (show_tracker_url url)
name key int_n;
if int_n > -1 then
int_n
else begin
- lprintf_nl "Reply from %s in file: %s has an invalid %s value: %d" url
name key int_n;
+ lprintf_nl "Reply from %s in file: %s has an invalid %s value: %d"
(show_tracker_url url) name key int_n;
0
end
-(** Check that client is valid and record it *)
-let maybe_new_client file id ip port =
- let cc = Geoip.get_country_code_option ip in
- if id <> !!client_uid
- && ip != Ip.null
- && port <> 0
- && (match !Ip.banned (ip, cc) with
- | None -> true
- | Some reason ->
- if !verbose_connect then
- lprintf_file_nl (as_file file) "%s:%d blocked: %s" (Ip.to_string
ip) port reason;
- false)
- then
- ignore (new_client file id (ip,port) cc);
- if !verbose_sources > 1 then
- lprintf_file_nl (as_file file) "Received %s:%d" (Ip.to_string ip) port;
- ()
-
let exn_catch f x = try `Ok (f x) with exn -> `Exn exn
(** In this function we interact with the tracker
@@ -1357,6 +1484,7 @@
let talk_to_tracker file need_sources =
(* This is the function which will be called by the http client for parsing
the response *)
let f t filename =
+ let tracker_url = show_tracker_url t.tracker_url in
let tracker_failed reason =
(* On failure, disable the tracker and count attempts (@see
is_tracker_enabled) *)
let num = match t.tracker_status with | Disabled_failure (i,_) -> i + 1
| _ -> 1 in
@@ -1364,7 +1492,7 @@
lprintf_file_nl (as_file file) "Failure no. %d%s from Tracker %s for
file: %s Reason: %s"
num
(if !!tracker_retries = 0 then "" else Printf.sprintf "/%d"
!!tracker_retries)
- t.tracker_url file.file_name (Charset.Locale.to_utf8 reason)
+ tracker_url file.file_name (Charset.Locale.to_utf8 reason)
in
match exn_catch File.to_string filename with
| `Exn _ | `Ok "" -> tracker_failed "empty reply"
@@ -1381,7 +1509,7 @@
begin match t.tracker_status with
| Disabled_failure (i, _) ->
lprintf_file_nl (as_file file) "Received good message from
Tracker %s after %d bad attempts"
- t.tracker_url i
+ tracker_url i
| _ -> () end;
(* Received good message from tracker after failures, re-enable
tracker *)
t.tracker_status <- Enabled;
@@ -1391,7 +1519,7 @@
| "failure reason", String failure -> tracker_failed failure
| "warning message", String warning ->
lprintf_file_nl (as_file file) "Warning from Tracker %s in
file: %s Reason: %s"
- t.tracker_url file.file_name warning
+ tracker_url file.file_name warning
| "interval", Int n ->
t.tracker_interval <- chk_keyval key n;
(* in case we don't receive "min interval" *)
@@ -1422,11 +1550,11 @@
| "key", String n ->
t.tracker_key <- n;
if !verbose_msg_clients then
- lprintf_file_nl (as_file file) "%s in file: %s has key: %s"
t.tracker_url file.file_name n
+ lprintf_file_nl (as_file file) "%s in file: %s has key: %s"
tracker_url file.file_name n
| "tracker id", String n ->
t.tracker_id <- n;
if !verbose_msg_clients then
- lprintf_file_nl (as_file file) "%s in file: %s has tracker
id %s" t.tracker_url file.file_name n
+ lprintf_file_nl (as_file file) "%s in file: %s has tracker
id %s" tracker_url file.file_name n
| "peers", List list ->
if need_sources then
Index: src/networks/bittorrent/bTComplexOptions.ml
===================================================================
RCS file:
/sources/mldonkey/mldonkey/src/networks/bittorrent/bTComplexOptions.ml,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -b -r1.43 -r1.44
--- src/networks/bittorrent/bTComplexOptions.ml 4 Apr 2010 09:16:28 -0000
1.43
+++ src/networks/bittorrent/bTComplexOptions.ml 29 Aug 2010 20:17:56 -0000
1.44
@@ -219,7 +219,7 @@
"file_uploaded", int64_to_value (file.file_uploaded);
"file_id", string_to_value (Sha1.to_string file.file_id);
"file_trackers", (list_to_value string_to_value)
- (List.map (fun t -> t.tracker_url) file.file_trackers);
+ (List.map (fun t -> show_tracker_url t.tracker_url)
file.file_trackers);
(* OK, but I still don't like the idea of forgetting all the clients.
We should have a better strategy, ie rating the clients and connecting
to them depending on the results of our last connections. And then,
Index: src/networks/bittorrent/bTGlobals.ml
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTGlobals.ml,v
retrieving revision 1.86
retrieving revision 1.87
diff -u -b -r1.86 -r1.87
--- src/networks/bittorrent/bTGlobals.ml 4 Apr 2010 09:16:28 -0000
1.86
+++ src/networks/bittorrent/bTGlobals.ml 29 Aug 2010 20:17:56 -0000
1.87
@@ -230,16 +230,27 @@
file_temp);
file_fd
-let can_handle_tracker t =
- String2.check_prefix (String.lowercase t.tracker_url) "http://"
+let make_tracker_url url =
+ if String2.check_prefix (String.lowercase url) "http://" then
+ `Http url (* do not change the case of the url *)
+ else
+ try Scanf.sscanf (String.lowercase url) "udp://%s@:%d" (fun host port ->
`Udp (host,port))
+ with _ -> `Other url
-let rec set_trackers file file_trackers =
- match file_trackers with
- | [] -> ()
- | url :: q ->
- if not (List.exists (fun tracker ->
- tracker.tracker_url = url
- ) file.file_trackers) then
+(** invariant: [make_tracker_url (show_tracker_url url) = url] *)
+let show_tracker_url : tracker_url -> string = function
+ | `Http url | `Other url -> url
+ | `Udp (host,port) -> Printf.sprintf "udp://%s:%d" host port
+
+let can_handle_tracker = function
+ | `Http _
+ | `Udp _ -> true
+ | `Other _ -> false
+
+let set_trackers file file_trackers =
+ List.iter (fun url ->
+ let url = make_tracker_url url in
+ if not (List.exists (fun tracker -> tracker.tracker_url = url)
file.file_trackers) then
let t = {
tracker_url = url;
tracker_interval = 600;
@@ -253,12 +264,11 @@
tracker_torrent_last_dl_req = 0;
tracker_id = "";
tracker_key = "";
- tracker_status = Enabled
+ tracker_status = if can_handle_tracker url then Enabled
+ else Disabled_mld (intern "Tracker type not
supported")
} in
- if not (can_handle_tracker t) then
- t.tracker_status <- Disabled_mld (intern "Tracker type not
supported");
- file.file_trackers <- t :: file.file_trackers;
- set_trackers file q
+ file.file_trackers <- t :: file.file_trackers)
+ file_trackers
let new_file file_id t torrent_diskname file_temp file_state user group =
try
@@ -878,7 +888,7 @@
let remove_tracker url file =
if !verbose_msg_servers then
List.iter (fun tracker ->
- lprintf_nl "Old tracker list :%s" tracker.tracker_url
+ lprintf_nl "Old tracker list: %s" (show_tracker_url tracker.tracker_url)
) file.file_trackers;
List.iter (fun bad_tracker ->
if bad_tracker.tracker_url = url then
@@ -886,7 +896,7 @@
) file.file_trackers;
if !verbose_msg_servers then
List.iter (fun tracker ->
- lprintf_nl "New tracker list :%s" tracker.tracker_url
+ lprintf_nl "New tracker list: %s" (show_tracker_url tracker.tracker_url)
) file.file_trackers
let tracker_is_enabled t =
Index: src/networks/bittorrent/bTInteractive.ml
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTInteractive.ml,v
retrieving revision 1.160
retrieving revision 1.161
diff -u -b -r1.160 -r1.161
--- src/networks/bittorrent/bTInteractive.ml 7 Aug 2010 14:51:16 -0000
1.160
+++ src/networks/bittorrent/bTInteractive.ml 29 Aug 2010 20:17:56 -0000
1.161
@@ -234,14 +234,15 @@
Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr
());
let tracker_header_printed = ref false in
List.iter (fun tracker ->
+ let tracker_url = show_tracker_url tracker.tracker_url in
let tracker_text =
match tracker.tracker_status with
| Disabled s | Disabled_mld s ->
- Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled:
%s\\<br\\>\\--error: %s\\</font\\>" tracker.tracker_url s
+ Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled:
%s\\<br\\>\\--error: %s\\</font\\>" tracker_url s
| Disabled_failure (i,s) ->
- Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled:
%s\\<br\\>\\--error: %s (try %d)\\</font\\>" tracker.tracker_url s i
+ Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled:
%s\\<br\\>\\--error: %s (try %d)\\</font\\>" tracker_url s i
| _ ->
- Printf.sprintf "enabled: %s" tracker.tracker_url
+ Printf.sprintf "enabled: %s" tracker_url
in
html_mods_td buf [
@@ -250,7 +251,7 @@
else
("", "sr br", "")
);
- (tracker.tracker_url, "sr", tracker_text)];
+ (tracker_url, "sr", tracker_text)];
Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr
());
tracker_header_printed := true;
) file.file_trackers;
@@ -421,12 +422,13 @@
Printf.bprintf buf "Trackers:\n";
List.iter (fun tracker ->
+ let tracker_url = show_tracker_url tracker.tracker_url in
match tracker.tracker_status with
| Disabled s | Disabled_mld s ->
- Printf.bprintf buf "%s, disabled: %s\n" tracker.tracker_url s
+ Printf.bprintf buf "%s, disabled: %s\n" tracker_url s
| Disabled_failure (i,s) ->
- Printf.bprintf buf "%s, disabled (try %d): %s\n" tracker.tracker_url i
s
- | _ -> Printf.bprintf buf "%s\n" tracker.tracker_url
+ Printf.bprintf buf "%s, disabled (try %d): %s\n" tracker_url i s
+ | _ -> Printf.bprintf buf "%s\n" tracker_url
) file.file_trackers;
if file.file_torrent_diskname <> "" then
Printf.bprintf buf "Torrent diskname: %s\n" file.file_torrent_diskname;
@@ -697,9 +699,7 @@
(* Save the torrent, because we later want to put
it in the seeded directory. *)
let torrent_is_usable = ref false in
- let can_handle_tracker url =
- String2.check_prefix url "http://" in
- List.iter (fun url -> if can_handle_tracker url then torrent_is_usable :=
true)
+ List.iter (fun url -> if can_handle_tracker (make_tracker_url url) then
torrent_is_usable := true)
(if torrent.torrent_announce_list <> [] then torrent.torrent_announce_list
else [torrent.torrent_announce]);
if not !torrent_is_usable then raise (Torrent_can_not_be_used
torrent.torrent_name);
Index: src/networks/bittorrent/bTTorrent.ml
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTTorrent.ml,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -b -r1.22 -r1.23
--- src/networks/bittorrent/bTTorrent.ml 15 Aug 2010 15:05:18 -0000
1.22
+++ src/networks/bittorrent/bTTorrent.ml 29 Aug 2010 20:17:56 -0000
1.23
@@ -145,7 +145,7 @@
match key, value with
| "announce", String tracker_url ->
if !verbose_msg_servers then
- lprintf_nl "[BT] New tracker added :%s" tracker_url;
+ lprintf_nl "[BT] New tracker added: %s" tracker_url;
announce := tracker_url
| "announce-list", List list ->
List.iter (fun url_list ->
Index: src/networks/bittorrent/bTTypes.ml
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTTypes.ml,v
retrieving revision 1.47
retrieving revision 1.48
diff -u -b -r1.47 -r1.48
--- src/networks/bittorrent/bTTypes.ml 27 Mar 2010 13:22:35 -0000 1.47
+++ src/networks/bittorrent/bTTypes.ml 29 Aug 2010 20:17:56 -0000 1.48
@@ -232,6 +232,11 @@
| Disabled_mld of string
| Disabled_failure of (int * string)
+type tracker_url =
+[ `Http of string (* url *)
+| `Udp of string * int (* host and port *)
+| `Other of string ]
+
type client = {
client_client : client CommonClient.client_impl;
mutable client_file : file;
@@ -287,7 +292,7 @@
}
and tracker_info = {
- tracker_url : string;
+ tracker_url : tracker_url;
mutable tracker_interval : int;
mutable tracker_min_interval : int;
mutable tracker_last_conn : int;
Index: src/utils/lib/options.ml4
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/utils/lib/options.ml4,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -b -r1.30 -r1.31
--- src/utils/lib/options.ml4 16 Jul 2010 13:56:35 -0000 1.30
+++ src/utils/lib/options.ml4 29 Aug 2010 20:17:57 -0000 1.31
@@ -256,10 +256,10 @@
let temp_file = filename ^ ".tmp" in
if Sys.file_exists temp_file then
begin
- Printf.eprintf "File %s exists\n" temp_file;
- Printf.eprintf "An error may have occurred during previous configuration
save.\n";
- Printf.eprintf "Please, check your configurations files, and
rename/remove this file\n";
- Printf.eprintf "before restarting\n";
+ lprintf "File %s exists\n" temp_file;
+ lprintf "An error may have occurred during previous configuration
save.\n";
+ lprintf "Please, check your configurations files, and rename/remove this
file\n";
+ lprintf "before restarting\n";
exit 70
end;
Unix2.tryopen_read filename (fun ic ->
@@ -271,10 +271,10 @@
try
parse_gwmlrc stream
with e ->
- Printf.eprintf "Syntax error while parsing file %s at pos %d:(%s)\n"
+ lprintf "Syntax error while parsing file %s at pos %d:(%s)\n"
filename (Stream.count s) (Printexc2.to_string e);
- Printf.eprintf "it seems that %s is corrupt,\n" filename;
- Printf.eprintf "try to use a backup from %s\n"
+ lprintf "it seems that %s is corrupt,\n" filename;
+ lprintf "try to use a backup from %s\n"
(Filename.concat (Sys.getcwd ()) "old_config");
exit 70 in
Hashtbl.clear once_values;
Index: src/utils/net/basicSocket.ml
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/utils/net/basicSocket.ml,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -b -r1.34 -r1.35
--- src/utils/net/basicSocket.ml 28 Jul 2010 16:25:44 -0000 1.34
+++ src/utils/net/basicSocket.ml 29 Aug 2010 20:17:57 -0000 1.35
@@ -260,13 +260,17 @@
(* *)
(*************************************************************************)
+let exn_log name f x =
+ try
+ f x
+ with e ->
+ lprintf_nl "[bS] %s : unexpected exn %s" name (Printexc2.to_string e)
+
let close t msg =
if t.fd <> dummy_fd then begin
if !debug then
lprintf_nl "[bS] CLOSING: %s (%s)" (sprint_socket t)
(string_of_reason msg);
- (try
- Unix.close t.fd;
- with _ -> ());
+ exn_log "close" Unix.close t.fd;
t.fd <- dummy_fd;
closed_tasks := t :: !closed_tasks;
t.closed <- true;
@@ -454,7 +458,7 @@
match list with
[] -> ()
| f :: tail ->
- (try f () with _ -> ());
+ exn_log "hook" f ();
exec_hooks tail
(*************************************************************************)
@@ -469,19 +473,19 @@
(
let time = !current_time in
if not t.closed && t.next_rtimeout < time then
- (try t.event_handler t RTIMEOUT with _ -> ());
+ exn_log "exec rtimeout" (t.event_handler t) RTIMEOUT;
if not t.closed && t.next_wtimeout < time then
- (try t.event_handler t WTIMEOUT with _ -> ());
+ exn_log "exec wtimeout" (t.event_handler t) WTIMEOUT;
if not t.closed && t.lifetime < time then
- (try t.event_handler t LTIMEOUT with _ -> ());
+ exn_log "exec ltimeout" (t.event_handler t) LTIMEOUT;
if not t.closed && t.flags land can_read <> 0 then
- (try
+ exn_log "exec can_read" (fun () ->
t.next_rtimeout <- time +. t.rtimeout;
- t.event_handler t CAN_READ with _ -> ());
+ t.event_handler t CAN_READ) ();
if not t.closed && t.flags land can_write <> 0 then
- (try
+ exn_log "exec can_write" (fun () ->
t.next_wtimeout <- time +. t.wtimeout;
- t.event_handler t CAN_WRITE with _ -> ());
+ t.event_handler t CAN_WRITE) ();
);
exec_tasks tail
@@ -497,7 +501,7 @@
(
if (not t.applied) && t.next_time <= !current_time then begin
t.applied <- true;
- try t.time_handler t with _ -> ()
+ begin try t.time_handler t with _ -> () end (* exn_log -> many
Fifo.empty *)
end
);
exec_timers tail
@@ -512,7 +516,7 @@
add_infinite_timer 1.0 (fun _ ->
if !verbose_bandwidth > 0 then
lprintf_nl "[BW1] Resetting bandwidth counters";
- List.iter (fun f -> try f () with _ -> ()) !bandwidth_second_timers
+ List.iter (fun f -> exn_log "reset bw ctr" f ()) !bandwidth_second_timers
);
while true do
try
@@ -527,7 +531,7 @@
[] -> ()
| t :: tail ->
closed_tasks := tail;
- (try t.event_handler t (CLOSED t.error) with _ -> ());
+ exn_log "exec closed" (t.event_handler t) (CLOSED t.error)
done;
(* lprintf "before iter_timer\n"; *)
Index: src/utils/net/ip.ml
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/utils/net/ip.ml,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -b -r1.29 -r1.30
--- src/utils/net/ip.ml 24 Feb 2009 18:35:47 -0000 1.29
+++ src/utils/net/ip.ml 29 Aug 2010 20:17:57 -0000 1.30
@@ -291,6 +291,11 @@
err_handler : (int -> unit);
}
+let exn_log name f x =
+ try
+ f x
+ with e ->
+ lprintf_nl "[Ip] %s : unexpected exn %s" name (Printexc2.to_string e)
external job_done : job -> bool = "ml_ip_job_done"
external job_start : job -> unit = "ml_ip_job_start"
@@ -302,7 +307,7 @@
try
(* lprintf "async_ip [%s]\n" name; *)
let ip = resolve_name_immediate name in
- (try f ip with _ -> ())
+ exn_log "async_ip" f ip
with Not_found ->
Fifo.put ip_fifo (name, f, ferr)
@@ -322,14 +327,14 @@
let (name, f, ferr) = Fifo.take ip_fifo in
(try
let ip = resolve_name_immediate name in
- (try f ip with _ -> ())
+ exn_log "ip_fifo immediate" f ip
with Not_found ->
(* lprintf "resolving name...\n"; *)
if !BasicSocket.use_threads &&
BasicSocket.has_threads () then
let job = {
- handler = f;
- err_handler = ferr;
+ handler = exn_log "ip_fifo handler" f;
+ err_handler = exn_log "ip_fifo err_handler" ferr;
name = name;
entries = [||];
error = false;
@@ -338,7 +343,7 @@
job_start job
else begin
(* lprintf "from_name ...\n"; *)
- f (from_name name)
+ exn_log "ip_fifo no threads" f (from_name name)
end
)
Index: src/utils/net/udpSocket.ml
===================================================================
RCS file: /sources/mldonkey/mldonkey/src/utils/net/udpSocket.ml,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -b -r1.23 -r1.24
--- src/utils/net/udpSocket.ml 11 Apr 2010 10:42:06 -0000 1.23
+++ src/utils/net/udpSocket.ml 29 Aug 2010 20:17:57 -0000 1.24
@@ -31,6 +31,12 @@
let lprintf_n fmt =
lprintf2 log_prefix fmt
+let exn_log name f x =
+ try
+ f x
+ with e ->
+ lprintf_nl "%s : unexpected exn %s" name (Printexc2.to_string e)
+
type event =
WRITE_DONE
| CAN_REFILL
@@ -65,7 +71,6 @@
Fifo.put pings_fifo (ping.ping_die_time, ping)
let declare_pong ip =
- try
let ping = Hashtbl.find pings_hashtbl ip in
if ping.ping_obsolete_time > last_time () then begin
ping.ping_die_time <- 0;
@@ -82,7 +87,8 @@
with _ ->
Hashtbl.add latencies ip (ref delay, ref 1)
end
- with _ -> ()
+
+let declare_pong = exn_log "declare_pong" declare_pong
type udp_packet = {
udp_ping : bool;
@@ -171,13 +177,13 @@
let set_refill t f =
set_handler t CAN_REFILL f;
if PacketSet.is_empty t.wlist then
- (try f t with _ -> ())
+ exn_log "immediate refill" f t
let set_reader t f =
set_handler t READ_DONE f;
match t.rlist with
[] -> ()
- | _ -> (try f t with _ -> ())
+ | _ -> exn_log "immediate read" f t
let sock t = t.sock
let closed t = closed t.sock
@@ -425,9 +431,7 @@
PacketSet.is_empty t.wlist
let read_packets t f =
- List.iter (fun p ->
- try f p with _ -> ()
- ) t.rlist;
+ List.iter (exn_log "read_packets" f) t.rlist;
t.rlist <- []
let set_write_controler s c =
Index: src/networks/bittorrent/bTUdpTracker.mlp
===================================================================
RCS file: src/networks/bittorrent/bTUdpTracker.mlp
diff -N src/networks/bittorrent/bTUdpTracker.mlp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/networks/bittorrent/bTUdpTracker.mlp 29 Aug 2010 20:17:56 -0000
1.1
@@ -0,0 +1,60 @@
+(** UDP trackers
+ http://www.bittorrent.org/beps/bep_0015.html *)
+
+open Bitstring
+
+let of_bits = string_of_bitstring
+let bits = bitstring_of_string
+
+exception Error of string
+
+let fail fmt = Printf.ksprintf (fun s -> raise (Error s)) fmt
+
+let bitmatch error_response = { 3l : 32 ; txn : 32 ; msg : -1 : string }
+
+(** connect - obtain connection_id *)
+let connect_request txn =
+ of_bits ( BITSTRING { 0x41727101980L : 64 ; 0l : 32 ; txn : 32 } )
+
+(** connect response with connection_id for future use *)
+let connect_response s exp_txn =
+ bitmatch bits s with
+ | { 0l : 32 ; txn : 32 ; conn_id : 64 } ->
+ if txn = exp_txn then conn_id else fail "error connect_response txn %ld
expected %ld" txn exp_txn
+ | { :error_response } -> fail "error connect_response txn %ld : %s" txn msg
+ | { } -> fail "error connect_response"
+
+(** announce *)
+let announce_request conn txn ~info_hash ~peer_id (downloaded,left,uploaded)
event ?(ip=0l) ?(key=0l) ~numwant port =
+ of_bits (BITSTRING {
+ conn : 64 ;
+ 1l : 32 ;
+ txn : 32 ;
+ info_hash : 20 * 8 : string;
+ peer_id : 20 * 8 : string;
+ downloaded : 64 ;
+ left : 64 ;
+ uploaded : 64 ;
+ event : 32 ;
+ 0l : 32 ; (* ip *)
+ key : 32 ; (* key *)
+ numwant : 32 ; (* numwant *)
+ port : 16 })
+
+(** announce response *)
+let announce_response s exp_txn =
+ let rec clients rest l =
+ bitmatch rest with
+ | { ip : 32 ; port : 16 ; rest : -1 : bitstring } -> clients rest
((ip,port)::l)
+ | { } -> l
+ in
+ bitmatch bits s with
+ | { 1l : 32 ; txn : 32 ; interval : 32 ; leechers : 32 ; seeders : 32 ;
+ rest : -1 : bitstring } ->
+ if txn = exp_txn then
+ (interval,clients rest [])
+ else
+ fail "error announce_response txn %ld expected %ld" txn exp_txn
+ | { :error_response } -> fail "error announce_response txn %ld : %s" txn msg
+ | { } -> fail "error announce_response"
+
Index: src/utils/bitstring/bitstring.ml.in
===================================================================
RCS file: src/utils/bitstring/bitstring.ml.in
diff -N src/utils/bitstring/bitstring.ml.in
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/utils/bitstring/bitstring.ml.in 29 Aug 2010 20:17:57 -0000 1.1
@@ -0,0 +1,1185 @@
+(* Bitstring library.
+ * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version,
+ * with the OCaml linking exception described in COPYING.LIB.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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: bitstring.ml.in,v 1.1 2010/08/29 20:17:57 spiralvoice Exp $
+ *)
+
+open Printf
+
+(* Enable runtime debug messages. Must also have been enabled
+ * in pa_bitstring.ml.
+ *)
+let debug = ref false
+let version = "2.0.2"
+let package = "ocaml-bitstring"
+
+type endian = BigEndian | LittleEndian | NativeEndian
+
+let string_of_endian = function
+ | BigEndian -> "bigendian"
+ | LittleEndian -> "littleendian"
+ | NativeEndian -> "nativeendian"
+
+let nativeendian = @NATIVEENDIAN@
+
+(* Exceptions. *)
+exception Construct_failure of string * string * int * int
+
+(* A bitstring is simply the data itself (as a string), and the
+ * bitoffset and the bitlength within the string. Note offset/length
+ * are counted in bits, not bytes.
+ *)
+type bitstring = string * int * int
+
+type t = bitstring
+
+(* Functions to create and load bitstrings. *)
+let empty_bitstring = "", 0, 0
+
+let make_bitstring len c =
+ if len >= 0 then String.make ((len+7) lsr 3) c, 0, len
+ else
+ invalid_arg (
+ sprintf "make_bitstring/create_bitstring: len %d < 0" len
+ )
+
+let create_bitstring len = make_bitstring len '\000'
+
+let zeroes_bitstring = create_bitstring
+
+let ones_bitstring len = make_bitstring len '\xff'
+
+let bitstring_of_string str = str, 0, String.length str lsl 3
+
+let bitstring_of_chan chan =
+ let tmpsize = 16384 in
+ let buf = Buffer.create tmpsize in
+ let tmp = String.create tmpsize in
+ let n = ref 0 in
+ while n := input chan tmp 0 tmpsize; !n > 0 do
+ Buffer.add_substring buf tmp 0 !n;
+ done;
+ Buffer.contents buf, 0, Buffer.length buf lsl 3
+
+let bitstring_of_chan_max chan max =
+ let tmpsize = 16384 in
+ let buf = Buffer.create tmpsize in
+ let tmp = String.create tmpsize in
+ let len = ref 0 in
+ let rec loop () =
+ if !len < max then (
+ let r = min tmpsize (max - !len) in
+ let n = input chan tmp 0 r in
+ if n > 0 then (
+ Buffer.add_substring buf tmp 0 n;
+ len := !len + n;
+ loop ()
+ )
+ )
+ in
+ loop ();
+ Buffer.contents buf, 0, !len lsl 3
+
+let bitstring_of_file_descr fd =
+ let tmpsize = 16384 in
+ let buf = Buffer.create tmpsize in
+ let tmp = String.create tmpsize in
+ let n = ref 0 in
+ while n := Unix.read fd tmp 0 tmpsize; !n > 0 do
+ Buffer.add_substring buf tmp 0 !n;
+ done;
+ Buffer.contents buf, 0, Buffer.length buf lsl 3
+
+let bitstring_of_file_descr_max fd max =
+ let tmpsize = 16384 in
+ let buf = Buffer.create tmpsize in
+ let tmp = String.create tmpsize in
+ let len = ref 0 in
+ let rec loop () =
+ if !len < max then (
+ let r = min tmpsize (max - !len) in
+ let n = Unix.read fd tmp 0 r in
+ if n > 0 then (
+ Buffer.add_substring buf tmp 0 n;
+ len := !len + n;
+ loop ()
+ )
+ )
+ in
+ loop ();
+ Buffer.contents buf, 0, !len lsl 3
+
+let bitstring_of_file fname =
+ let chan = open_in_bin fname in
+ try
+ let bs = bitstring_of_chan chan in
+ close_in chan;
+ bs
+ with exn ->
+ close_in chan;
+ raise exn
+
+let bitstring_length (_, _, len) = len
+
+let subbitstring (data, off, len) off' len' =
+ let off = off + off' in
+ if len < off' + len' then invalid_arg "subbitstring";
+ (data, off, len')
+
+let dropbits n (data, off, len) =
+ let off = off + n in
+ let len = len - n in
+ if len < 0 then invalid_arg "dropbits";
+ (data, off, len)
+
+let takebits n (data, off, len) =
+ if len < n then invalid_arg "takebits";
+ (data, off, n)
+
+(*----------------------------------------------------------------------*)
+(* Bitwise functions.
+ *
+ * We try to isolate all bitwise functions within these modules.
+ *)
+
+module I = struct
+ (* Bitwise operations on ints. Note that we assume int <= 31 bits. *)
+ external (<<<) : int -> int -> int = "%lslint"
+ external (>>>) : int -> int -> int = "%lsrint"
+ external to_int : int -> int = "%identity"
+ let zero = 0
+ let one = 1
+ let minus_one = -1
+ let ff = 0xff
+
+ (* Create a mask 0-31 bits wide. *)
+ let mask bits =
+ if bits < 30 then
+ (one <<< bits) - 1
+ else if bits = 30 then
+ max_int
+ else if bits = 31 then
+ minus_one
+ else
+ invalid_arg "Bitstring.I.mask"
+
+ (* Byte swap an int of a given size. *)
+ let byteswap v bits =
+ if bits <= 8 then v
+ else if bits <= 16 then (
+ let shift = bits-8 in
+ let v1 = v >>> shift in
+ let v2 = ((v land (mask shift)) <<< 8) in
+ v2 lor v1
+ ) else if bits <= 24 then (
+ let shift = bits - 16 in
+ let v1 = v >>> (8+shift) in
+ let v2 = ((v >>> shift) land ff) <<< 8 in
+ let v3 = (v land (mask shift)) <<< 16 in
+ v3 lor v2 lor v1
+ ) else (
+ let shift = bits - 24 in
+ let v1 = v >>> (16+shift) in
+ let v2 = ((v >>> (8+shift)) land ff) <<< 8 in
+ let v3 = ((v >>> shift) land ff) <<< 16 in
+ let v4 = (v land (mask shift)) <<< 24 in
+ v4 lor v3 lor v2 lor v1
+ )
+
+ (* Check a value is in range 0 .. 2^bits-1. *)
+ let range_unsigned v bits =
+ let mask = lnot (mask bits) in
+ (v land mask) = zero
+
+ (* Call function g on the top bits, then f on each full byte
+ * (big endian - so start at top).
+ *)
+ let rec map_bytes_be g f v bits =
+ if bits >= 8 then (
+ map_bytes_be g f (v >>> 8) (bits-8);
+ let lsb = v land ff in
+ f (to_int lsb)
+ ) else if bits > 0 then (
+ let lsb = v land (mask bits) in
+ g (to_int lsb) bits
+ )
+
+ (* Call function g on the top bits, then f on each full byte
+ * (little endian - so start at root).
+ *)
+ let rec map_bytes_le g f v bits =
+ if bits >= 8 then (
+ let lsb = v land ff in
+ f (to_int lsb);
+ map_bytes_le g f (v >>> 8) (bits-8)
+ ) else if bits > 0 then (
+ let lsb = v land (mask bits) in
+ g (to_int lsb) bits
+ )
+end
+
+module I32 = struct
+ (* Bitwise operations on int32s. Note we try to keep it as similar
+ * as possible to the I module above, to make it easier to track
+ * down bugs.
+ *)
+ let (<<<) = Int32.shift_left
+ let (>>>) = Int32.shift_right_logical
+ let (land) = Int32.logand
+ let (lor) = Int32.logor
+ let lnot = Int32.lognot
+ let pred = Int32.pred
+ let max_int = Int32.max_int
+ let to_int = Int32.to_int
+ let zero = Int32.zero
+ let one = Int32.one
+ let minus_one = Int32.minus_one
+ let ff = 0xff_l
+
+ (* Create a mask so many bits wide. *)
+ let mask bits =
+ if bits < 31 then
+ pred (one <<< bits)
+ else if bits = 31 then
+ max_int
+ else if bits = 32 then
+ minus_one
+ else
+ invalid_arg "Bitstring.I32.mask"
+
+ (* Byte swap an int of a given size. *)
+ let byteswap v bits =
+ if bits <= 8 then v
+ else if bits <= 16 then (
+ let shift = bits-8 in
+ let v1 = v >>> shift in
+ let v2 = (v land (mask shift)) <<< 8 in
+ v2 lor v1
+ ) else if bits <= 24 then (
+ let shift = bits - 16 in
+ let v1 = v >>> (8+shift) in
+ let v2 = ((v >>> shift) land ff) <<< 8 in
+ let v3 = (v land (mask shift)) <<< 16 in
+ v3 lor v2 lor v1
+ ) else (
+ let shift = bits - 24 in
+ let v1 = v >>> (16+shift) in
+ let v2 = ((v >>> (8+shift)) land ff) <<< 8 in
+ let v3 = ((v >>> shift) land ff) <<< 16 in
+ let v4 = (v land (mask shift)) <<< 24 in
+ v4 lor v3 lor v2 lor v1
+ )
+
+ (* Check a value is in range 0 .. 2^bits-1. *)
+ let range_unsigned v bits =
+ let mask = lnot (mask bits) in
+ (v land mask) = zero
+
+ (* Call function g on the top bits, then f on each full byte
+ * (big endian - so start at top).
+ *)
+ let rec map_bytes_be g f v bits =
+ if bits >= 8 then (
+ map_bytes_be g f (v >>> 8) (bits-8);
+ let lsb = v land ff in
+ f (to_int lsb)
+ ) else if bits > 0 then (
+ let lsb = v land (mask bits) in
+ g (to_int lsb) bits
+ )
+
+ (* Call function g on the top bits, then f on each full byte
+ * (little endian - so start at root).
+ *)
+ let rec map_bytes_le g f v bits =
+ if bits >= 8 then (
+ let lsb = v land ff in
+ f (to_int lsb);
+ map_bytes_le g f (v >>> 8) (bits-8)
+ ) else if bits > 0 then (
+ let lsb = v land (mask bits) in
+ g (to_int lsb) bits
+ )
+end
+
+module I64 = struct
+ (* Bitwise operations on int64s. Note we try to keep it as similar
+ * as possible to the I/I32 modules above, to make it easier to track
+ * down bugs.
+ *)
+ let (<<<) = Int64.shift_left
+ let (>>>) = Int64.shift_right_logical
+ let (land) = Int64.logand
+ let (lor) = Int64.logor
+ let lnot = Int64.lognot
+ let pred = Int64.pred
+ let max_int = Int64.max_int
+ let to_int = Int64.to_int
+ let zero = Int64.zero
+ let one = Int64.one
+ let minus_one = Int64.minus_one
+ let ff = 0xff_L
+
+ (* Create a mask so many bits wide. *)
+ let mask bits =
+ if bits < 63 then
+ pred (one <<< bits)
+ else if bits = 63 then
+ max_int
+ else if bits = 64 then
+ minus_one
+ else
+ invalid_arg "Bitstring.I64.mask"
+
+ (* Byte swap an int of a given size. *)
+ (* let byteswap v bits = *)
+
+ (* Check a value is in range 0 .. 2^bits-1. *)
+ let range_unsigned v bits =
+ let mask = lnot (mask bits) in
+ (v land mask) = zero
+
+ (* Call function g on the top bits, then f on each full byte
+ * (big endian - so start at top).
+ *)
+ let rec map_bytes_be g f v bits =
+ if bits >= 8 then (
+ map_bytes_be g f (v >>> 8) (bits-8);
+ let lsb = v land ff in
+ f (to_int lsb)
+ ) else if bits > 0 then (
+ let lsb = v land (mask bits) in
+ g (to_int lsb) bits
+ )
+
+ (* Call function g on the top bits, then f on each full byte
+ * (little endian - so start at root).
+ *)
+ let rec map_bytes_le g f v bits =
+ if bits >= 8 then (
+ let lsb = v land ff in
+ f (to_int lsb);
+ map_bytes_le g f (v >>> 8) (bits-8)
+ ) else if bits > 0 then (
+ let lsb = v land (mask bits) in
+ g (to_int lsb) bits
+ )
+end
+
+(*----------------------------------------------------------------------*)
+(* Extraction functions.
+ *
+ * NB: internal functions, called from the generated macros, and
+ * the parameters should have been checked for sanity already).
+ *)
+
+(* Extract and convert to numeric. A single bit is returned as
+ * a boolean. There are no endianness or signedness considerations.
+ *)
+let extract_bit data off len _ = (* final param is always 1 *)
+ let byteoff = off lsr 3 in
+ let bitmask = 1 lsl (7 - (off land 7)) in
+ let b = Char.code data.[byteoff] land bitmask <> 0 in
+ b (*, off+1, len-1*)
+
+(* Returns 8 bit unsigned aligned bytes from the string.
+ * If the string ends then this returns 0's.
+ *)
+let _get_byte data byteoff strlen =
+ if strlen > byteoff then Char.code data.[byteoff] else 0
+let _get_byte32 data byteoff strlen =
+ if strlen > byteoff then Int32.of_int (Char.code data.[byteoff]) else 0l
+let _get_byte64 data byteoff strlen =
+ if strlen > byteoff then Int64.of_int (Char.code data.[byteoff]) else 0L
+
+(* Extract [2..8] bits. Because the result fits into a single
+ * byte we don't have to worry about endianness, only signedness.
+ *)
+let extract_char_unsigned data off len flen =
+ let byteoff = off lsr 3 in
+
+ (* Optimize the common (byte-aligned) case. *)
+ if off land 7 = 0 then (
+ let byte = Char.code data.[byteoff] in
+ byte lsr (8 - flen) (*, off+flen, len-flen*)
+ ) else (
+ (* Extract the 16 bits at byteoff and byteoff+1 (note that the
+ * second byte might not exist in the original string).
+ *)
+ let strlen = String.length data in
+
+ let word =
+ (_get_byte data byteoff strlen lsl 8) +
+ _get_byte data (byteoff+1) strlen in
+
+ (* Mask off the top bits. *)
+ let bitmask = (1 lsl (16 - (off land 7))) - 1 in
+ let word = word land bitmask in
+ (* Shift right to get rid of the bottom bits. *)
+ let shift = 16 - ((off land 7) + flen) in
+ let word = word lsr shift in
+
+ word (*, off+flen, len-flen*)
+ )
+
+(* Extract [9..31] bits. We have to consider endianness and signedness. *)
+let extract_int_be_unsigned data off len flen =
+ let byteoff = off lsr 3 in
+
+ let strlen = String.length data in
+
+ let word =
+ (* Optimize the common (byte-aligned) case. *)
+ if off land 7 = 0 then (
+ let word =
+ (_get_byte data byteoff strlen lsl 23) +
+ (_get_byte data (byteoff+1) strlen lsl 15) +
+ (_get_byte data (byteoff+2) strlen lsl 7) +
+ (_get_byte data (byteoff+3) strlen lsr 1) in
+ word lsr (31 - flen)
+ ) else if flen <= 24 then (
+ (* Extract the 31 bits at byteoff .. byteoff+3. *)
+ let word =
+ (_get_byte data byteoff strlen lsl 23) +
+ (_get_byte data (byteoff+1) strlen lsl 15) +
+ (_get_byte data (byteoff+2) strlen lsl 7) +
+ (_get_byte data (byteoff+3) strlen lsr 1) in
+ (* Mask off the top bits. *)
+ let bitmask = (1 lsl (31 - (off land 7))) - 1 in
+ let word = word land bitmask in
+ (* Shift right to get rid of the bottom bits. *)
+ let shift = 31 - ((off land 7) + flen) in
+ word lsr shift
+ ) else (
+ (* Extract the next 31 bits, slow method. *)
+ let word =
+ let c0 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c1 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c2 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c3 = extract_char_unsigned data off len 7 in
+ (c0 lsl 23) + (c1 lsl 15) + (c2 lsl 7) + c3 in
+ word lsr (31 - flen)
+ ) in
+ word (*, off+flen, len-flen*)
+
+let extract_int_le_unsigned data off len flen =
+ let v = extract_int_be_unsigned data off len flen in
+ let v = I.byteswap v flen in
+ v
+
+let extract_int_ne_unsigned =
+ if nativeendian = BigEndian
+ then extract_int_be_unsigned
+ else extract_int_le_unsigned
+
+let extract_int_ee_unsigned = function
+ | BigEndian -> extract_int_be_unsigned
+ | LittleEndian -> extract_int_le_unsigned
+ | NativeEndian -> extract_int_ne_unsigned
+
+let _make_int32_be c0 c1 c2 c3 =
+ Int32.logor
+ (Int32.logor
+ (Int32.logor
+ (Int32.shift_left c0 24)
+ (Int32.shift_left c1 16))
+ (Int32.shift_left c2 8))
+ c3
+
+let _make_int32_le c0 c1 c2 c3 =
+ Int32.logor
+ (Int32.logor
+ (Int32.logor
+ (Int32.shift_left c3 24)
+ (Int32.shift_left c2 16))
+ (Int32.shift_left c1 8))
+ c0
+
+(* Extract exactly 32 bits. We have to consider endianness and signedness. *)
+let extract_int32_be_unsigned data off len flen =
+ let byteoff = off lsr 3 in
+
+ let strlen = String.length data in
+
+ let word =
+ (* Optimize the common (byte-aligned) case. *)
+ if off land 7 = 0 then (
+ let word =
+ let c0 = _get_byte32 data byteoff strlen in
+ let c1 = _get_byte32 data (byteoff+1) strlen in
+ let c2 = _get_byte32 data (byteoff+2) strlen in
+ let c3 = _get_byte32 data (byteoff+3) strlen in
+ _make_int32_be c0 c1 c2 c3 in
+ Int32.shift_right_logical word (32 - flen)
+ ) else (
+ (* Extract the next 32 bits, slow method. *)
+ let word =
+ let c0 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c1 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c2 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c3 = extract_char_unsigned data off len 8 in
+ let c0 = Int32.of_int c0 in
+ let c1 = Int32.of_int c1 in
+ let c2 = Int32.of_int c2 in
+ let c3 = Int32.of_int c3 in
+ _make_int32_be c0 c1 c2 c3 in
+ Int32.shift_right_logical word (32 - flen)
+ ) in
+ word (*, off+flen, len-flen*)
+
+let extract_int32_le_unsigned data off len flen =
+ let v = extract_int32_be_unsigned data off len flen in
+ let v = I32.byteswap v flen in
+ v
+
+let extract_int32_ne_unsigned =
+ if nativeendian = BigEndian
+ then extract_int32_be_unsigned
+ else extract_int32_le_unsigned
+
+let extract_int32_ee_unsigned = function
+ | BigEndian -> extract_int32_be_unsigned
+ | LittleEndian -> extract_int32_le_unsigned
+ | NativeEndian -> extract_int32_ne_unsigned
+
+let _make_int64_be c0 c1 c2 c3 c4 c5 c6 c7 =
+ Int64.logor
+ (Int64.logor
+ (Int64.logor
+ (Int64.logor
+ (Int64.logor
+ (Int64.logor
+ (Int64.logor
+ (Int64.shift_left c0 56)
+ (Int64.shift_left c1 48))
+ (Int64.shift_left c2 40))
+ (Int64.shift_left c3 32))
+ (Int64.shift_left c4 24))
+ (Int64.shift_left c5 16))
+ (Int64.shift_left c6 8))
+ c7
+
+let _make_int64_le c0 c1 c2 c3 c4 c5 c6 c7 =
+ _make_int64_be c7 c6 c5 c4 c3 c2 c1 c0
+
+(* Extract [1..64] bits. We have to consider endianness and signedness. *)
+let extract_int64_be_unsigned data off len flen =
+ let byteoff = off lsr 3 in
+
+ let strlen = String.length data in
+
+ let word =
+ (* Optimize the common (byte-aligned) case. *)
+ if off land 7 = 0 then (
+ let word =
+ let c0 = _get_byte64 data byteoff strlen in
+ let c1 = _get_byte64 data (byteoff+1) strlen in
+ let c2 = _get_byte64 data (byteoff+2) strlen in
+ let c3 = _get_byte64 data (byteoff+3) strlen in
+ let c4 = _get_byte64 data (byteoff+4) strlen in
+ let c5 = _get_byte64 data (byteoff+5) strlen in
+ let c6 = _get_byte64 data (byteoff+6) strlen in
+ let c7 = _get_byte64 data (byteoff+7) strlen in
+ _make_int64_be c0 c1 c2 c3 c4 c5 c6 c7 in
+ Int64.shift_right_logical word (64 - flen)
+ ) else (
+ (* Extract the next 64 bits, slow method. *)
+ let word =
+ let c0 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c1 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c2 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c3 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c4 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c5 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c6 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c7 = extract_char_unsigned data off len 8 in
+ let c0 = Int64.of_int c0 in
+ let c1 = Int64.of_int c1 in
+ let c2 = Int64.of_int c2 in
+ let c3 = Int64.of_int c3 in
+ let c4 = Int64.of_int c4 in
+ let c5 = Int64.of_int c5 in
+ let c6 = Int64.of_int c6 in
+ let c7 = Int64.of_int c7 in
+ _make_int64_be c0 c1 c2 c3 c4 c5 c6 c7 in
+ Int64.shift_right_logical word (64 - flen)
+ ) in
+ word (*, off+flen, len-flen*)
+
+let extract_int64_le_unsigned data off len flen =
+ let byteoff = off lsr 3 in
+
+ let strlen = String.length data in
+
+ let word =
+ (* Optimize the common (byte-aligned) case. *)
+ if off land 7 = 0 then (
+ let word =
+ let c0 = _get_byte64 data byteoff strlen in
+ let c1 = _get_byte64 data (byteoff+1) strlen in
+ let c2 = _get_byte64 data (byteoff+2) strlen in
+ let c3 = _get_byte64 data (byteoff+3) strlen in
+ let c4 = _get_byte64 data (byteoff+4) strlen in
+ let c5 = _get_byte64 data (byteoff+5) strlen in
+ let c6 = _get_byte64 data (byteoff+6) strlen in
+ let c7 = _get_byte64 data (byteoff+7) strlen in
+ _make_int64_le c0 c1 c2 c3 c4 c5 c6 c7 in
+ Int64.logand word (I64.mask flen)
+ ) else (
+ (* Extract the next 64 bits, slow method. *)
+ let word =
+ let c0 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c1 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c2 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c3 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c4 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c5 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c6 = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ let c7 = extract_char_unsigned data off len 8 in
+ let c0 = Int64.of_int c0 in
+ let c1 = Int64.of_int c1 in
+ let c2 = Int64.of_int c2 in
+ let c3 = Int64.of_int c3 in
+ let c4 = Int64.of_int c4 in
+ let c5 = Int64.of_int c5 in
+ let c6 = Int64.of_int c6 in
+ let c7 = Int64.of_int c7 in
+ _make_int64_le c0 c1 c2 c3 c4 c5 c6 c7 in
+ Int64.logand word (I64.mask flen)
+ ) in
+ word (*, off+flen, len-flen*)
+
+let extract_int64_ne_unsigned =
+ if nativeendian = BigEndian
+ then extract_int64_be_unsigned
+ else extract_int64_le_unsigned
+
+let extract_int64_ee_unsigned = function
+ | BigEndian -> extract_int64_be_unsigned
+ | LittleEndian -> extract_int64_le_unsigned
+ | NativeEndian -> extract_int64_ne_unsigned
+
+external extract_fastpath_int16_be_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_be_unsigned" "noalloc"
+
+external extract_fastpath_int16_le_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_le_unsigned" "noalloc"
+
+external extract_fastpath_int16_ne_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_ne_unsigned" "noalloc"
+
+external extract_fastpath_int16_be_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_be_signed" "noalloc"
+
+external extract_fastpath_int16_le_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_le_signed" "noalloc"
+
+external extract_fastpath_int16_ne_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_ne_signed" "noalloc"
+
+(*
+external extract_fastpath_int24_be_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_be_unsigned" "noalloc"
+
+external extract_fastpath_int24_le_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_le_unsigned" "noalloc"
+
+external extract_fastpath_int24_ne_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_ne_unsigned" "noalloc"
+
+external extract_fastpath_int24_be_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_be_signed" "noalloc"
+
+external extract_fastpath_int24_le_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_le_signed" "noalloc"
+
+external extract_fastpath_int24_ne_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_ne_signed" "noalloc"
+*)
+
+external extract_fastpath_int32_be_unsigned : string -> int -> int32 -> int32
= "ocaml_bitstring_extract_fastpath_int32_be_unsigned" "noalloc"
+
+external extract_fastpath_int32_le_unsigned : string -> int -> int32 -> int32
= "ocaml_bitstring_extract_fastpath_int32_le_unsigned" "noalloc"
+
+external extract_fastpath_int32_ne_unsigned : string -> int -> int32 -> int32
= "ocaml_bitstring_extract_fastpath_int32_ne_unsigned" "noalloc"
+
+external extract_fastpath_int32_be_signed : string -> int -> int32 -> int32 =
"ocaml_bitstring_extract_fastpath_int32_be_signed" "noalloc"
+
+external extract_fastpath_int32_le_signed : string -> int -> int32 -> int32 =
"ocaml_bitstring_extract_fastpath_int32_le_signed" "noalloc"
+
+external extract_fastpath_int32_ne_signed : string -> int -> int32 -> int32 =
"ocaml_bitstring_extract_fastpath_int32_ne_signed" "noalloc"
+
+(*
+external extract_fastpath_int40_be_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int40_be_unsigned" "noalloc"
+
+external extract_fastpath_int40_le_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int40_le_unsigned" "noalloc"
+
+external extract_fastpath_int40_ne_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int40_ne_unsigned" "noalloc"
+
+external extract_fastpath_int40_be_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int40_be_signed" "noalloc"
+
+external extract_fastpath_int40_le_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int40_le_signed" "noalloc"
+
+external extract_fastpath_int40_ne_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int40_ne_signed" "noalloc"
+
+external extract_fastpath_int48_be_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int48_be_unsigned" "noalloc"
+
+external extract_fastpath_int48_le_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int48_le_unsigned" "noalloc"
+
+external extract_fastpath_int48_ne_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int48_ne_unsigned" "noalloc"
+
+external extract_fastpath_int48_be_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int48_be_signed" "noalloc"
+
+external extract_fastpath_int48_le_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int48_le_signed" "noalloc"
+
+external extract_fastpath_int48_ne_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int48_ne_signed" "noalloc"
+
+external extract_fastpath_int56_be_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int56_be_unsigned" "noalloc"
+
+external extract_fastpath_int56_le_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int56_le_unsigned" "noalloc"
+
+external extract_fastpath_int56_ne_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int56_ne_unsigned" "noalloc"
+
+external extract_fastpath_int56_be_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int56_be_signed" "noalloc"
+
+external extract_fastpath_int56_le_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int56_le_signed" "noalloc"
+
+external extract_fastpath_int56_ne_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int56_ne_signed" "noalloc"
+*)
+
+external extract_fastpath_int64_be_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int64_be_unsigned" "noalloc"
+
+external extract_fastpath_int64_le_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int64_le_unsigned" "noalloc"
+
+external extract_fastpath_int64_ne_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int64_ne_unsigned" "noalloc"
+
+external extract_fastpath_int64_be_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int64_be_signed" "noalloc"
+
+external extract_fastpath_int64_le_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int64_le_signed" "noalloc"
+
+external extract_fastpath_int64_ne_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int64_ne_signed" "noalloc"
+
+(*----------------------------------------------------------------------*)
+(* Constructor functions. *)
+
+module Buffer = struct
+ type t = {
+ buf : Buffer.t;
+ mutable len : int; (* Length in bits. *)
+ (* Last byte in the buffer (if len is not aligned). We store
+ * it outside the buffer because buffers aren't mutable.
+ *)
+ mutable last : int;
+ }
+
+ let create () =
+ (* XXX We have almost enough information in the generator to
+ * choose a good initial size.
+ *)
+ { buf = Buffer.create 128; len = 0; last = 0 }
+
+ let contents { buf = buf; len = len; last = last } =
+ let data =
+ if len land 7 = 0 then
+ Buffer.contents buf
+ else
+ Buffer.contents buf ^ (String.make 1 (Char.chr last)) in
+ data, 0, len
+
+ (* Add exactly 8 bits. *)
+ let add_byte ({ buf = buf; len = len; last = last } as t) byte =
+ if byte < 0 || byte > 255 then invalid_arg "Bitstring.Buffer.add_byte";
+ let shift = len land 7 in
+ if shift = 0 then
+ (* Target buffer is byte-aligned. *)
+ Buffer.add_char buf (Char.chr byte)
+ else (
+ (* Target buffer is unaligned. 'last' is meaningful. *)
+ let first = byte lsr shift in
+ let second = (byte lsl (8 - shift)) land 0xff in
+ Buffer.add_char buf (Char.chr (last lor first));
+ t.last <- second
+ );
+ t.len <- t.len + 8
+
+ (* Add exactly 1 bit. *)
+ let add_bit ({ buf = buf; len = len; last = last } as t) bit =
+ let shift = 7 - (len land 7) in
+ if shift > 0 then
+ (* Somewhere in the middle of 'last'. *)
+ t.last <- last lor ((if bit then 1 else 0) lsl shift)
+ else (
+ (* Just a single spare bit in 'last'. *)
+ let last = last lor if bit then 1 else 0 in
+ Buffer.add_char buf (Char.chr last);
+ t.last <- 0
+ );
+ t.len <- len + 1
+
+ (* Add a small number of bits (definitely < 8). This uses a loop
+ * to call add_bit so it's slow.
+ *)
+ let _add_bits t c slen =
+ if slen < 1 || slen >= 8 then invalid_arg "Bitstring.Buffer._add_bits";
+ for i = slen-1 downto 0 do
+ let bit = c land (1 lsl i) <> 0 in
+ add_bit t bit
+ done
+
+ let add_bits ({ buf = buf; len = len } as t) str slen =
+ if slen > 0 then (
+ if len land 7 = 0 then (
+ if slen land 7 = 0 then
+ (* Common case - everything is byte-aligned. *)
+ Buffer.add_substring buf str 0 (slen lsr 3)
+ else (
+ (* Target buffer is aligned. Copy whole bytes then leave the
+ * remaining bits in last.
+ *)
+ let slenbytes = slen lsr 3 in
+ if slenbytes > 0 then Buffer.add_substring buf str 0 slenbytes;
+ let last = Char.code str.[slenbytes] in (* last char *)
+ let mask = 0xff lsl (8 - (slen land 7)) in
+ t.last <- last land mask
+ );
+ t.len <- len + slen
+ ) else (
+ (* Target buffer is unaligned. Copy whole bytes using
+ * add_byte which knows how to deal with an unaligned
+ * target buffer, then call add_bit for the remaining < 8 bits.
+ *
+ * XXX This is going to be dog-slow.
+ *)
+ let slenbytes = slen lsr 3 in
+ for i = 0 to slenbytes-1 do
+ let byte = Char.code str.[i] in
+ add_byte t byte
+ done;
+ let bitsleft = slen - (slenbytes lsl 3) in
+ if bitsleft > 0 then (
+ let c = Char.code str.[slenbytes] in
+ for i = 0 to bitsleft - 1 do
+ let bit = c land (0x80 lsr i) <> 0 in
+ add_bit t bit
+ done
+ )
+ );
+ )
+end
+
+(* Construct a single bit. *)
+let construct_bit buf b _ _ =
+ Buffer.add_bit buf b
+
+(* Construct a field, flen = [2..8]. *)
+let construct_char_unsigned buf v flen exn =
+ let max_val = 1 lsl flen in
+ if v < 0 || v >= max_val then raise exn;
+ if flen = 8 then
+ Buffer.add_byte buf v
+ else
+ Buffer._add_bits buf v flen
+
+(* Construct a field of up to 31 bits. *)
+let construct_int_be_unsigned buf v flen exn =
+ (* Check value is within range. *)
+ if not (I.range_unsigned v flen) then raise exn;
+ (* Add the bytes. *)
+ I.map_bytes_be (Buffer._add_bits buf) (Buffer.add_byte buf) v flen
+
+(* Construct a field of up to 31 bits. *)
+let construct_int_le_unsigned buf v flen exn =
+ (* Check value is within range. *)
+ if not (I.range_unsigned v flen) then raise exn;
+ (* Add the bytes. *)
+ I.map_bytes_le (Buffer._add_bits buf) (Buffer.add_byte buf) v flen
+
+let construct_int_ne_unsigned =
+ if nativeendian = BigEndian
+ then construct_int_be_unsigned
+ else construct_int_le_unsigned
+
+let construct_int_ee_unsigned = function
+ | BigEndian -> construct_int_be_unsigned
+ | LittleEndian -> construct_int_le_unsigned
+ | NativeEndian -> construct_int_ne_unsigned
+
+(* Construct a field of exactly 32 bits. *)
+let construct_int32_be_unsigned buf v flen _ =
+ Buffer.add_byte buf
+ (Int32.to_int (Int32.shift_right_logical v 24));
+ Buffer.add_byte buf
+ (Int32.to_int ((Int32.logand (Int32.shift_right_logical v 16) 0xff_l)));
+ Buffer.add_byte buf
+ (Int32.to_int ((Int32.logand (Int32.shift_right_logical v 8) 0xff_l)));
+ Buffer.add_byte buf
+ (Int32.to_int (Int32.logand v 0xff_l))
+
+let construct_int32_le_unsigned buf v flen _ =
+ Buffer.add_byte buf
+ (Int32.to_int (Int32.logand v 0xff_l));
+ Buffer.add_byte buf
+ (Int32.to_int ((Int32.logand (Int32.shift_right_logical v 8) 0xff_l)));
+ Buffer.add_byte buf
+ (Int32.to_int ((Int32.logand (Int32.shift_right_logical v 16) 0xff_l)));
+ Buffer.add_byte buf
+ (Int32.to_int (Int32.shift_right_logical v 24))
+
+let construct_int32_ne_unsigned =
+ if nativeendian = BigEndian
+ then construct_int32_be_unsigned
+ else construct_int32_le_unsigned
+
+let construct_int32_ee_unsigned = function
+ | BigEndian -> construct_int32_be_unsigned
+ | LittleEndian -> construct_int32_le_unsigned
+ | NativeEndian -> construct_int32_ne_unsigned
+
+(* Construct a field of up to 64 bits. *)
+let construct_int64_be_unsigned buf v flen exn =
+ (* Check value is within range. *)
+ if not (I64.range_unsigned v flen) then raise exn;
+ (* Add the bytes. *)
+ I64.map_bytes_be (Buffer._add_bits buf) (Buffer.add_byte buf) v flen
+
+(* Construct a field of up to 64 bits. *)
+let construct_int64_le_unsigned buf v flen exn =
+ (* Check value is within range. *)
+ if not (I64.range_unsigned v flen) then raise exn;
+ (* Add the bytes. *)
+ I64.map_bytes_le (Buffer._add_bits buf) (Buffer.add_byte buf) v flen
+
+let construct_int64_ne_unsigned =
+ if nativeendian = BigEndian
+ then construct_int64_be_unsigned
+ else construct_int64_le_unsigned
+
+let construct_int64_ee_unsigned = function
+ | BigEndian -> construct_int64_be_unsigned
+ | LittleEndian -> construct_int64_le_unsigned
+ | NativeEndian -> construct_int64_ne_unsigned
+
+(* Construct from a string of bytes, exact multiple of 8 bits
+ * in length of course.
+ *)
+let construct_string buf str =
+ let len = String.length str in
+ Buffer.add_bits buf str (len lsl 3)
+
+(* Construct from a bitstring. *)
+let construct_bitstring buf (data, off, len) =
+ (* Add individual bits until we get to the next byte boundary of
+ * the underlying string.
+ *)
+ let blen = 7 - ((off + 7) land 7) in
+ let blen = min blen len in
+ let rec loop off len blen =
+ if blen = 0 then (off, len)
+ else (
+ let b = extract_bit data off len 1
+ and off = off + 1 and len = len + 1 in
+ Buffer.add_bit buf b;
+ loop off len (blen-1)
+ )
+ in
+ let off, len = loop off len blen in
+ assert (len = 0 || (off land 7) = 0);
+
+ (* Add the remaining 'len' bits. *)
+ let data =
+ let off = off lsr 3 in
+ (* XXX dangerous allocation *)
+ if off = 0 then data
+ else String.sub data off (String.length data - off) in
+
+ Buffer.add_bits buf data len
+
+(* Concatenate bitstrings. *)
+let concat bs =
+ let buf = Buffer.create () in
+ List.iter (construct_bitstring buf) bs;
+ Buffer.contents buf
+
+(*----------------------------------------------------------------------*)
+(* Extract a string from a bitstring. *)
+let string_of_bitstring (data, off, len) =
+ if off land 7 = 0 && len land 7 = 0 then
+ (* Easy case: everything is byte-aligned. *)
+ String.sub data (off lsr 3) (len lsr 3)
+ else (
+ (* Bit-twiddling case. *)
+ let strlen = (len + 7) lsr 3 in
+ let str = String.make strlen '\000' in
+ let rec loop data off len i =
+ if len >= 8 then (
+ let c = extract_char_unsigned data off len 8
+ and off = off + 8 and len = len - 8 in
+ str.[i] <- Char.chr c;
+ loop data off len (i+1)
+ ) else if len > 0 then (
+ let c = extract_char_unsigned data off len len in
+ str.[i] <- Char.chr (c lsl (8-len))
+ )
+ in
+ loop data off len 0;
+ str
+ )
+
+(* To channel. *)
+
+let bitstring_to_chan ((data, off, len) as bits) chan =
+ (* Fail if the bitstring length isn't a multiple of 8. *)
+ if len land 7 <> 0 then invalid_arg "bitstring_to_chan";
+
+ if off land 7 = 0 then
+ (* Easy case: string is byte-aligned. *)
+ output chan data (off lsr 3) (len lsr 3)
+ else (
+ (* Bit-twiddling case: reuse string_of_bitstring *)
+ let str = string_of_bitstring bits in
+ output_string chan str
+ )
+
+let bitstring_to_file bits filename =
+ let chan = open_out_bin filename in
+ try
+ bitstring_to_chan bits chan;
+ close_out chan
+ with exn ->
+ close_out chan;
+ raise exn
+
+(*----------------------------------------------------------------------*)
+(* Comparison. *)
+let compare ((data1, off1, len1) as bs1) ((data2, off2, len2) as bs2) =
+ (* In the fully-aligned case, this is reduced to string comparison ... *)
+ if off1 land 7 = 0 && len1 land 7 = 0 && off2 land 7 = 0 && len2 land 7 = 0
+ then (
+ (* ... but we have to do that by hand because the bits may
+ * not extend to the full length of the underlying string.
+ *)
+ let off1 = off1 lsr 3 and off2 = off2 lsr 3
+ and len1 = len1 lsr 3 and len2 = len2 lsr 3 in
+ let rec loop i =
+ if i < len1 && i < len2 then (
+ let c1 = String.unsafe_get data1 (off1 + i)
+ and c2 = String.unsafe_get data2 (off2 + i) in
+ let r = compare c1 c2 in
+ if r <> 0 then r
+ else loop (i+1)
+ )
+ else len1 - len2
+ in
+ loop 0
+ )
+ else (
+ (* Slow/unaligned. *)
+ let str1 = string_of_bitstring bs1
+ and str2 = string_of_bitstring bs2 in
+ let r = String.compare str1 str2 in
+ if r <> 0 then r else len1 - len2
+ )
+
+let equals ((_, _, len1) as bs1) ((_, _, len2) as bs2) =
+ if len1 <> len2 then false
+ else if bs1 = bs2 then true
+ else 0 = compare bs1 bs2
+
+(*----------------------------------------------------------------------*)
+(* Bit get/set functions. *)
+
+let index_out_of_bounds () = invalid_arg "index out of bounds"
+
+let put (data, off, len) n v =
+ if n < 0 || n >= len then index_out_of_bounds ()
+ else (
+ let i = off+n in
+ let si = i lsr 3 and mask = 0x80 lsr (i land 7) in
+ let c = Char.code data.[si] in
+ let c = if v <> 0 then c lor mask else c land (lnot mask) in
+ data.[si] <- Char.unsafe_chr c
+ )
+
+let set bits n = put bits n 1
+
+let clear bits n = put bits n 0
+
+let get (data, off, len) n =
+ if n < 0 || n >= len then index_out_of_bounds ()
+ else (
+ let i = off+n in
+ let si = i lsr 3 and mask = 0x80 lsr (i land 7) in
+ let c = Char.code data.[si] in
+ c land mask
+ )
+
+let is_set bits n = get bits n <> 0
+
+let is_clear bits n = get bits n = 0
+
+(*----------------------------------------------------------------------*)
+(* Display functions. *)
+
+let isprint c =
+ let c = Char.code c in
+ c >= 32 && c < 127
+
+let hexdump_bitstring chan (data, off, len) =
+ let count = ref 0 in
+ let off = ref off in
+ let len = ref len in
+ let linelen = ref 0 in
+ let linechars = String.make 16 ' ' in
+
+ fprintf chan "00000000 ";
+
+ while !len > 0 do
+ let bits = min !len 8 in
+ let byte = extract_char_unsigned data !off !len bits in
+ off := !off + bits; len := !len - bits;
+
+ let byte = byte lsl (8-bits) in
+ fprintf chan "%02x " byte;
+
+ incr count;
+ linechars.[!linelen] <-
+ (let c = Char.chr byte in
+ if isprint c then c else '.');
+ incr linelen;
+ if !linelen = 8 then fprintf chan " ";
+ if !linelen = 16 then (
+ fprintf chan " |%s|\n%08x " linechars !count;
+ linelen := 0;
+ for i = 0 to 15 do linechars.[i] <- ' ' done
+ )
+ done;
+
+ if !linelen > 0 then (
+ let skip = (16 - !linelen) * 3 + if !linelen < 8 then 1 else 0 in
+ for i = 0 to skip-1 do fprintf chan " " done;
+ fprintf chan " |%s|\n%!" linechars
+ ) else
+ fprintf chan "\n%!"
Index: src/utils/bitstring/bitstring.mli
===================================================================
RCS file: src/utils/bitstring/bitstring.mli
diff -N src/utils/bitstring/bitstring.mli
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/utils/bitstring/bitstring.mli 29 Aug 2010 20:17:57 -0000 1.1
@@ -0,0 +1,1081 @@
+(** Bitstring library. *)
+(* Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version,
+ * with the OCaml linking exception described in COPYING.LIB.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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: bitstring.mli,v 1.1 2010/08/29 20:17:57 spiralvoice Exp $
+ *)
+
+(**
+ {{:#reference}Jump straight to the reference section for
+ documentation on types and functions}.
+
+ {2 Introduction}
+
+ Bitstring 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, for
+ example, communications protocols, disk formats and binary files.
+
+ {{:http://code.google.com/p/bitstring/}OCaml bitstring website}
+
+ This library used to be called "bitmatch".
+
+ {2 Examples}
+
+ A function which can parse IPv4 packets:
+
+{[
+let display pkt =
+ bitmatch pkt with
+ (* IPv4 packet header
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 4 | IHL |Type of Service| Total Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Identification |Flags| Fragment Offset |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Time to Live | Protocol | Header Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Destination Address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 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 } ->
+
+ printf "IPv4:\n";
+ printf " header length: %d * 32 bit words\n" hdrlen;
+ printf " type of service: %d\n" tos;
+ printf " packet length: %d bytes\n" length;
+ printf " identification: %d\n" identification;
+ printf " flags: %d\n" flags;
+ printf " fragment offset: %d\n" fragoffset;
+ printf " ttl: %d\n" ttl;
+ printf " protocol: %d\n" protocol;
+ printf " checksum: %d\n" checksum;
+ printf " source: %lx dest: %lx\n" source dest;
+ printf " header options + padding:\n";
+ Bitstring.hexdump_bitstring stdout options;
+ printf " packet payload:\n";
+ Bitstring.hexdump_bitstring stdout payload
+
+ | { version : 4 } ->
+ eprintf "unknown IP version %d\n" version;
+ exit 1
+
+ | { _ } as pkt ->
+ eprintf "data is smaller than one nibble:\n";
+ Bitstring.hexdump_bitstring stderr pkt;
+ exit 1
+]}
+
+ A program which can parse
+ {{:http://lxr.linux.no/linux/include/linux/ext3_fs.h}Linux EXT3 filesystem
superblocks}:
+
+{[
+let bits = Bitstring.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 *)
+
+ printf "ext3 superblock:\n";
+ printf " s_inodes_count = %ld\n" s_inodes_count;
+ printf " s_blocks_count = %ld\n" s_blocks_count;
+ 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
+]}
+
+ Constructing packets for a simple binary message
+ protocol:
+
+{[
+(*
+ +---------------+---------------+--------------------------+
+ | type | subtype | parameter |
+ +---------------+---------------+--------------------------+
+ <-- 16 bits --> <-- 16 bits --> <------- 32 bits -------->
+
+ All fields are in network byte order.
+*)
+
+let make_message typ subtype param =
+ (BITSTRING {
+ typ : 16;
+ subtype : 16;
+ param : 32
+ }) ;;
+]}
+
+ {2 Loading, creating bitstrings}
+
+ The basic data type is the {!bitstring}, a string of bits of
+ arbitrary length. Bitstrings can be any length in bits and
+ operations do not need to be byte-aligned (although they will
+ generally be more efficient if they are byte-aligned).
+
+ Internally a bitstring is stored as a normal OCaml [string]
+ together with an offset and length, where the offset and length are
+ measured in bits. Thus one can efficiently form substrings of
+ bitstrings, overlay a bitstring on existing data, and load and save
+ bitstrings from files or other external sources.
+
+ To load a bitstring from a file use {!bitstring_of_file} or
+ {!bitstring_of_chan}.
+
+ There are also functions to create bitstrings from arbitrary data.
+ See the {{:#reference}reference} below.
+
+ {2 Matching bitstrings with patterns}
+
+ Use the [bitmatch] operator (part of the syntax extension) to break
+ apart a bitstring into its fields. [bitmatch] works a lot like the
+ OCaml [match] operator.
+
+ The general form of [bitmatch] is:
+
+ [bitmatch] {i bitstring-expression} [with]
+
+ [| {] {i pattern} [} ->] {i code}
+
+ [| {] {i pattern} [} ->] {i code}
+
+ [|] ...
+
+ As with normal match, the statement attempts to match the
+ bitstring against each pattern in turn. If none of the patterns
+ match then the standard library [Match_failure] exception is
+ thrown.
+
+ 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:
+
+{[
+bitmatch bits with
+
+| { 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 } ->
+ printf "flag is %b\n" flag
+
+ (* A single flag bit (mapped into an OCaml boolean). *)
+
+| { 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 and destination addresses. Each is 128 bits
+ and is mapped into a bitstring type which will be a substring
+ of the main bitstring expression. *)
+]}
+
+ You can also add conditional when-clauses:
+
+{[
+| { version : 4 }
+ when version = 4 || version = 6 -> ...
+
+ (* Only match and run the code when version is 4 or 6. If
+ it isn't we will drop through to the next case. *)
+]}
+
+ Note that the pattern is only compared against the first part of
+ the bitstring (there may be more data in the bitstring following
+ the pattern, which is not matched). In terms of regular
+ expressions you might say that the pattern matches [^pattern], not
+ [^pattern$]. To ensure that the bitstring contains only the
+ pattern, add a length -1 bitstring to the end and test that its
+ length is zero in the when-clause:
+
+{[
+| { n : 4;
+ rest : -1 : bitstring }
+ when Bitstring.bitstring_length rest = 0 -> ...
+
+ (* Only matches exactly 4 bits. *)
+]}
+
+ Normally the first part of each field is a binding variable,
+ but you can also match a constant, as in:
+
+{[
+| { (4|6) : 4 } -> ...
+
+ (* 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}
+
+ The exact format of each pattern field is:
+
+ [pattern : length [: qualifier [,qualifier ...]]]
+
+ [pattern] is the pattern, binding variable name, or constant to
+ match. [length] is the length in bits which may be either a
+ constant or an expression. The length expression is just an OCaml
+ expression and can use any values defined in the program, and refer
+ back to earlier fields (but not to later fields).
+
+ Integers can only have lengths in the range \[1..64\] bits. See the
+ {{:#integertypes}integer types} section below for how these are
+ mapped to the OCaml int/int32/int64 types. This is checked
+ at compile time if the length expression is constant, otherwise it is
+ checked at runtime and you will get a runtime exception eg. in
+ the case of a computed length expression.
+
+ A bitstring field of length -1 matches all the rest of the
+ bitstring (thus this is only useful as the last field in a
+ pattern).
+
+ A bitstring field of length 0 matches an empty bitstring
+ (occasionally useful when matching optional subfields).
+
+ 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
+ - [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
+ give you a compile-time error if you use incompatible qualifiers.
+
+ {3 Other cases in bitmatch}
+
+ As well as a list of fields, it is possible to name the
+ bitstring and/or have a default match case:
+
+{[
+| { _ } -> ...
+
+ (* Default match case. *)
+
+| { _ } as pkt -> ...
+
+ (* Default match case, with 'pkt' bound to the whole bitstring. *)
+]}
+
+ {2 Constructing bitstrings}
+
+ Bitstrings may be constructed using the [BITSTRING] operator (as an
+ expression). The [BITSTRING] operator takes a list of fields,
+ similar to the list of fields for matching:
+
+{[
+let version = 1 ;;
+let data = 10 ;;
+let bits =
+ BITSTRING {
+ version : 4;
+ 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,
+ arranged in network byte order. *)
+
+Bitstring.hexdump_bitstring stdout bits ;;
+
+ (* Prints:
+
+ 00000000 10 0a |.. |
+ *)
+]}
+
+ The format of each field is the same as for pattern fields (see
+ {{:#patternfieldreference}Pattern field reference section}), and
+ things like computed length fields, fixed value fields, insertion
+ of bitstrings within bitstrings, etc. are all supported.
+
+ {3 Construction exception}
+
+ The [BITSTRING] operator may throw a {!Construct_failure}
+ exception at runtime.
+
+ Runtime errors include:
+
+ - int field length not in the range \[1..64\]
+ - a bitstring with a length declared which doesn't have the
+ same length at runtime
+ - trying to insert an out of range value into an int field
+ (eg. an unsigned int field which is 2 bits wide can only
+ take values in the range \[0..3\]).
+
+ {2:integertypes Integer types}
+
+ Integer types are mapped to OCaml types [bool], [int], [int32] or
+ [int64] using a system which tries to ensure that (a) the types are
+ reasonably predictable and (b) the most efficient type is
+ preferred.
+
+ The rules are slightly different depending on whether the bit
+ length expression in the field is a compile-time constant or a
+ computed expression.
+
+ 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
+ possible, otherwise a runtime check is added which can throw an
+ [Invalid_argument] exception.
+
+ The mapping is thus:
+
+ {v
+ Bit size ---- OCaml type ----
+ Constant Computed expression
+
+ 1 bool int64
+ 2..31 int int64
+ 32 int32 int64
+ 33..64 int64 int64
+ v}
+
+ A possible future extension may allow people with 64 bit computers
+ to specify a more optimal [int] type for bit sizes in the range
+ [32..63]. If this was implemented then such code {i could not even
+ be compiled} on 32 bit platforms, so it would limit portability.
+
+ Another future extension may be to allow computed
+ expressions to assert min/max range for the bit size,
+ allowing a more efficient data type than int64 to be
+ used. (Of course under such circumstances there would
+ still need to be a runtime check to enforce the
+ size).
+
+ {2 Advanced pattern-matching features}
+
+ {3: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.
+
+ {3 Check expressions}
+
+ You can add a [check(expr)] qualifier to bitmatch patterns.
+ If the expression evaluates to false then the current match case
+ fails to match (in other words, we fall through to the next
+ match case - there is no error).
+
+ For example:
+{[
+bitmatch bits with
+| { field : 16 : check (field > 100) } -> ...
+]}
+
+ Note the difference between a check expression and a when-clause
+ is that the when-clause is evaluated after all the fields have
+ been matched. On the other hand a check expression is evaluated
+ after the individual field has been matched, which means it is
+ potentially more efficient (if the check expression fails then
+ we don't waste any time matching later fields).
+
+ We wanted to use the notation [when(expr)] here, but because
+ [when] is a reserved word we could not do this.
+
+ {3 Bind expressions}
+
+ A bind expression is used to change the value of a matched
+ field. For example:
+{[
+bitmatch bits with
+| { len : 16 : bind (len * 8);
+ field : len : bitstring } -> ...
+]}
+
+ In the example, after 'len' has been matched, its value would
+ be multiplied by 8, so the width of 'field' is the matched
+ value multiplied by 8.
+
+ In the general case:
+{[
+| { field : ... : bind (expr) } -> ...
+]}
+ evaluates the following after the field has been matched:
+{[
+ let field = expr in
+ (* remaining fields *)
+]}
+
+ {3 Order of evaluation of check() and bind()}
+
+ The choice is arbitrary, but we have chosen that check expressions
+ are evaluated first, and bind expressions are evaluated after.
+
+ This means that the result of bind() is {i not} available in
+ the check expression.
+
+ Note that this rule applies regardless of the order of check()
+ and bind() in the source code.
+
+ {3 save_offset_to}
+
+ Use [save_offset_to(variable)] to save the current bit offset
+ within the match to a variable (strictly speaking, to a pattern).
+ This variable is then made available in any [check()] and [bind()]
+ clauses in the current field, {i and} to any later fields, and
+ to the code after the [->].
+
+ For example:
+{[
+bitmatch bits with
+| { len : 16;
+ _ : len : bitstring;
+ field : 16 : save_offset_to (field_offset) } ->
+ printf "field is at bit offset %d in the match\n" field_offset
+]}
+
+ (In that example, [field_offset] should always have the value
+ [len+16]).
+
+ {2 Named patterns and persistent patterns}
+
+ Please see {!Bitstring_persistent} for documentation on this subject.
+
+ {2 Compiling}
+
+ Using the compiler directly you can do:
+
+ {v
+ ocamlc -I +bitstring \
+ -pp "camlp4of bitstring.cma bitstring_persistent.cma \
+ `ocamlc -where`/bitstring/pa_bitstring.cmo" \
+ unix.cma bitstring.cma test.ml -o test
+ v}
+
+ Simpler method using findlib:
+
+ {v
+ ocamlfind ocamlc \
+ -package bitstring,bitstring.syntax -syntax bitstring.syntax \
+ -linkpkg test.ml -o test
+ v}
+
+ {2 Security and type safety}
+
+ {3 Security on input}
+
+ The main concerns for input are buffer overflows and denial
+ of service.
+
+ It is believed that this library is robust against attempted buffer
+ 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. 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 } ->
+ ]}
+
+ The [len] field can be set arbitrarily large by an attacker, but
+ when pattern-matching against the [buffer] field this merely causes
+ a test such as [if len <= remaining_size] to fail. Even if the
+ length is chosen so that [buffer] bitstring is allocated, the
+ allocation of sub-bitstrings is efficient and doesn't involve an
+ arbitary-sized allocation or any copying.
+
+ However the above does not necessarily apply to strings used in
+ matching, since they may cause the library to use the
+ {!Bitstring.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
+ }
+ ]}
+
+ This code merely causes a check that buffer's length is the same as
+ [len]. However the program function [allocate_bitstring] must
+ refuse to allocate an oversized buffer (but that is outside the
+ scope of this library).
+
+ {3 Order of evaluation}
+
+ In [bitmatch] statements, fields are evaluated left to right.
+
+ Note that the when-clause is evaluated {i last}, so if you are
+ relying on the when-clause to filter cases then your code may do a
+ lot of extra and unncessary pattern-matching work on fields which
+ may never be needed just to evaluate the when-clause. Either
+ rearrange the code to do only the first part of the match,
+ followed by the when-clause, followed by a second inner bitmatch,
+ or use a [check()] qualifier within fields.
+
+ {3 Safety}
+
+ The current implementation is believed to be fully type-safe,
+ and makes compile and run-time checks where appropriate. If
+ you find a case where a check is missing please submit a
+ bug report or a patch.
+
+ {2 Limits}
+
+ These are thought to be the current limits:
+
+ Integers: \[1..64\] bits.
+
+ Bitstrings (32 bit platforms): maximum length is limited
+ by the string size, ie. 16 MBytes.
+
+ Bitstrings (64 bit platforms): maximum length is thought to be
+ limited by the string size, ie. effectively unlimited.
+
+ Bitstrings must be loaded into memory before we can match against
+ them. Thus available memory may be considered a limit for some
+ applications.
+
+ {2:reference Reference}
+ {3 Types}
+*)
+
+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 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 also {!bitstring_of_string}, {!bitstring_of_file},
+ {!hexdump_bitstring}, {!bitstring_length}.
+*)
+
+type t = bitstring
+(** [t] is a synonym for the {!bitstring} type.
+
+ This allows you to use this module with functors like
+ [Set] and [Map] from the stdlib. *)
+
+(** {3 Exceptions} *)
+
+exception Construct_failure of string * string * int * int
+(** [Construct_failure (message, file, line, char)] may be
+ raised by the [BITSTRING] constructor.
+
+ Common reasons are that values are out of range of
+ the fields that contain them, or that computed lengths
+ are impossible (eg. negative length bitfields).
+
+ [message] is the error message.
+
+ [file], [line] and [char] point to the original source
+ location of the [BITSTRING] constructor that failed.
+*)
+
+(** {3 Bitstring comparison} *)
+
+val compare : bitstring -> bitstring -> int
+(** [compare bs1 bs2] compares two bitstrings and returns zero
+ if they are equal, a negative number if [bs1 < bs2], or a
+ positive number if [bs1 > bs2].
+
+ This tests "semantic equality" which is not affected by
+ the offset or alignment of the underlying representation
+ (see {!bitstring}).
+
+ The ordering is total and lexicographic. *)
+
+val equals : bitstring -> bitstring -> bool
+(** [equals] returns true if and only if the two bitstrings are
+ semantically equal. It is the same as calling [compare] and
+ testing if the result is [0], but usually more efficient. *)
+
+(** {3 Bitstring manipulation} *)
+
+val bitstring_length : bitstring -> int
+(** [bitstring_length bitstring] returns the length of
+ the bitstring in bits.
+
+ Note this just returns the third field in the {!bitstring} tuple. *)
+
+val subbitstring : bitstring -> int -> int -> bitstring
+(** [subbitstring bits off len] returns a sub-bitstring
+ of the bitstring, starting at offset [off] bits and
+ with length [len] bits.
+
+ If the original bitstring is not long enough to do this
+ then the function raises [Invalid_argument "subbitstring"].
+
+ Note that this function just changes the offset and length
+ fields of the {!bitstring} tuple, so is very efficient. *)
+
+val dropbits : int -> bitstring -> bitstring
+(** Drop the first n bits of the bitstring and return a new
+ bitstring which is shorter by n bits.
+
+ If the length of the original bitstring is less than n bits,
+ this raises [Invalid_argument "dropbits"].
+
+ Note that this function just changes the offset and length
+ fields of the {!bitstring} tuple, so is very efficient. *)
+
+val takebits : int -> bitstring -> bitstring
+(** Take the first n bits of the bitstring and return a new
+ bitstring which is exactly n bits long.
+
+ If the length of the original bitstring is less than n bits,
+ this raises [Invalid_argument "takebits"].
+
+ Note that this function just changes the offset and length
+ fields of the {!bitstring} tuple, so is very efficient. *)
+
+val concat : bitstring list -> bitstring
+(** Concatenate a list of bitstrings together into a single
+ bitstring. *)
+
+(** {3 Constructing bitstrings} *)
+
+val empty_bitstring : bitstring
+(** [empty_bitstring] is the empty, zero-length bitstring. *)
+
+val create_bitstring : int -> bitstring
+(** [create_bitstring n] creates an [n] bit bitstring
+ containing all zeroes. *)
+
+val make_bitstring : int -> char -> bitstring
+(** [make_bitstring n c] creates an [n] bit bitstring
+ containing the repeated 8 bit pattern in [c].
+
+ 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. 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
+ the input channel [chan] as a bitstring.
+
+ The length of the final bitstring is determined
+ by the remaining input in [chan], but will always
+ be a multiple of 8 bits.
+
+ See also {!bitstring_of_chan_max}. *)
+
+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). *)
+
+(** {3 Converting bitstrings} *)
+
+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
+ type t
+ val create : unit -> t
+ val contents : t -> bitstring
+ val add_bits : t -> string -> int -> unit
+ val add_bit : t -> bool -> unit
+ val add_byte : t -> int -> unit
+end
+(** Buffers are mainly used by the [BITSTRING] constructor, but
+ may also be useful for end users. They work much like the
+ standard library [Buffer] module. *)
+
+(** {3 Get/set bits}
+
+ These functions let you manipulate individual bits in the
+ bitstring. However they are not particularly efficient and you
+ should generally use the [bitmatch] and [BITSTRING] operators when
+ building and parsing bitstrings.
+
+ These functions all raise [Invalid_argument "index out of bounds"]
+ if the index is out of range of the bitstring.
+*)
+
+val set : bitstring -> int -> unit
+ (** [set bits n] sets the [n]th bit in the bitstring to 1. *)
+
+val clear : bitstring -> int -> unit
+ (** [clear bits n] sets the [n]th bit in the bitstring to 0. *)
+
+val is_set : bitstring -> int -> bool
+ (** [is_set bits n] is true if the [n]th bit is set to 1. *)
+
+val is_clear : bitstring -> int -> bool
+ (** [is_clear bits n] is true if the [n]th bit is set to 0. *)
+
+val put : bitstring -> int -> int -> unit
+ (** [put bits n v] sets the [n]th bit in the bitstring to 1
+ if [v] is not zero, or to 0 if [v] is zero. *)
+
+val get : bitstring -> int -> int
+ (** [get bits n] returns the [n]th bit (returns non-zero or 0). *)
+
+(** {3 Miscellaneous} *)
+
+val package : string
+(** The package name, always ["ocaml-bitstring"] *)
+
+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
+ [pa_bitstring.ml] file at compile time, otherwise it
+ does nothing. *)
+
+type endian = BigEndian | LittleEndian | NativeEndian
+
+val string_of_endian : endian -> string
+
+val nativeendian : endian
+
+(**/**)
+
+(* Private functions, called from generated code. Do not use
+ * these directly - they are not safe.
+ *)
+
+(* 'extract' functions are used in bitmatch statements. *)
+
+val extract_bit : string -> int -> int -> int -> bool
+
+val extract_char_unsigned : string -> int -> int -> int -> int
+
+val extract_int_be_unsigned : string -> int -> int -> int -> int
+
+val extract_int_le_unsigned : string -> int -> int -> int -> int
+
+val extract_int_ne_unsigned : string -> int -> int -> int -> int
+
+val extract_int_ee_unsigned : endian -> string -> int -> int -> int -> int
+
+val extract_int32_be_unsigned : string -> int -> int -> int -> int32
+
+val extract_int32_le_unsigned : string -> int -> int -> int -> int32
+
+val extract_int32_ne_unsigned : string -> int -> int -> int -> int32
+
+val extract_int32_ee_unsigned : endian -> string -> int -> int -> int -> int32
+
+val extract_int64_be_unsigned : string -> int -> int -> int -> int64
+
+val extract_int64_le_unsigned : string -> int -> int -> int -> int64
+
+val extract_int64_ne_unsigned : string -> int -> int -> int -> int64
+
+val extract_int64_ee_unsigned : endian -> string -> int -> int -> int -> int64
+
+external extract_fastpath_int16_be_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_be_unsigned" "noalloc"
+
+external extract_fastpath_int16_le_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_le_unsigned" "noalloc"
+
+external extract_fastpath_int16_ne_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_ne_unsigned" "noalloc"
+
+external extract_fastpath_int16_be_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_be_signed" "noalloc"
+
+external extract_fastpath_int16_le_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_le_signed" "noalloc"
+
+external extract_fastpath_int16_ne_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int16_ne_signed" "noalloc"
+
+(*
+external extract_fastpath_int24_be_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_be_unsigned" "noalloc"
+
+external extract_fastpath_int24_le_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_le_unsigned" "noalloc"
+
+external extract_fastpath_int24_ne_unsigned : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_ne_unsigned" "noalloc"
+
+external extract_fastpath_int24_be_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_be_signed" "noalloc"
+
+external extract_fastpath_int24_le_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_le_signed" "noalloc"
+
+external extract_fastpath_int24_ne_signed : string -> int -> int =
"ocaml_bitstring_extract_fastpath_int24_ne_signed" "noalloc"
+*)
+
+external extract_fastpath_int32_be_unsigned : string -> int -> int32 -> int32
= "ocaml_bitstring_extract_fastpath_int32_be_unsigned" "noalloc"
+
+external extract_fastpath_int32_le_unsigned : string -> int -> int32 -> int32
= "ocaml_bitstring_extract_fastpath_int32_le_unsigned" "noalloc"
+
+external extract_fastpath_int32_ne_unsigned : string -> int -> int32 -> int32
= "ocaml_bitstring_extract_fastpath_int32_ne_unsigned" "noalloc"
+
+external extract_fastpath_int32_be_signed : string -> int -> int32 -> int32 =
"ocaml_bitstring_extract_fastpath_int32_be_signed" "noalloc"
+
+external extract_fastpath_int32_le_signed : string -> int -> int32 -> int32 =
"ocaml_bitstring_extract_fastpath_int32_le_signed" "noalloc"
+
+external extract_fastpath_int32_ne_signed : string -> int -> int32 -> int32 =
"ocaml_bitstring_extract_fastpath_int32_ne_signed" "noalloc"
+
+(*
+external extract_fastpath_int40_be_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int40_be_unsigned" "noalloc"
+
+external extract_fastpath_int40_le_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int40_le_unsigned" "noalloc"
+
+external extract_fastpath_int40_ne_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int40_ne_unsigned" "noalloc"
+
+external extract_fastpath_int40_be_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int40_be_signed" "noalloc"
+
+external extract_fastpath_int40_le_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int40_le_signed" "noalloc"
+
+external extract_fastpath_int40_ne_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int40_ne_signed" "noalloc"
+
+external extract_fastpath_int48_be_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int48_be_unsigned" "noalloc"
+
+external extract_fastpath_int48_le_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int48_le_unsigned" "noalloc"
+
+external extract_fastpath_int48_ne_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int48_ne_unsigned" "noalloc"
+
+external extract_fastpath_int48_be_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int48_be_signed" "noalloc"
+
+external extract_fastpath_int48_le_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int48_le_signed" "noalloc"
+
+external extract_fastpath_int48_ne_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int48_ne_signed" "noalloc"
+
+external extract_fastpath_int56_be_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int56_be_unsigned" "noalloc"
+
+external extract_fastpath_int56_le_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int56_le_unsigned" "noalloc"
+
+external extract_fastpath_int56_ne_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int56_ne_unsigned" "noalloc"
+
+external extract_fastpath_int56_be_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int56_be_signed" "noalloc"
+
+external extract_fastpath_int56_le_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int56_le_signed" "noalloc"
+
+external extract_fastpath_int56_ne_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int56_ne_signed" "noalloc"
+*)
+
+external extract_fastpath_int64_be_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int64_be_unsigned" "noalloc"
+
+external extract_fastpath_int64_le_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int64_le_unsigned" "noalloc"
+
+external extract_fastpath_int64_ne_unsigned : string -> int -> int64 -> int64
= "ocaml_bitstring_extract_fastpath_int64_ne_unsigned" "noalloc"
+
+external extract_fastpath_int64_be_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int64_be_signed" "noalloc"
+
+external extract_fastpath_int64_le_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int64_le_signed" "noalloc"
+
+external extract_fastpath_int64_ne_signed : string -> int -> int64 -> int64 =
"ocaml_bitstring_extract_fastpath_int64_ne_signed" "noalloc"
+
+(* 'construct' functions are used in BITSTRING constructors. *)
+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_le_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_le_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_le_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
Index: src/utils/bitstring/bitstring_c.c
===================================================================
RCS file: src/utils/bitstring/bitstring_c.c
diff -N src/utils/bitstring/bitstring_c.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/utils/bitstring/bitstring_c.c 29 Aug 2010 20:17:57 -0000 1.1
@@ -0,0 +1,141 @@
+/* Bitstring library.
+ * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version,
+ * with the OCaml linking exception described in COPYING.LIB.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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: bitstring_c.c,v 1.1 2010/08/29 20:17:57 spiralvoice Exp $
+ */
+
+/* This file contains hand-coded, optimized C implementations of
+ * certain very frequently used functions.
+ */
+
+#include "../../../config/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#if defined(HAVE_BYTESWAP_H)
+#include <byteswap.h>
+#else
+#include "byteswap.h"
+#endif
+
+#include <caml/mlvalues.h>
+#include <caml/fail.h>
+
+/* Fastpath functions. These are used in the common case for reading
+ * ints where the following conditions are known to be true:
+ * (a) the int size is a whole number of bytes (eg. 16, 24, 32, etc bits)
+ * (b) the access in the match is byte-aligned
+ * (c) the access in the underlying bitstring is byte-aligned
+ *
+ * These functions are all "noalloc" meaning they must not perform
+ * any OCaml allocations. For this reason, when the function returns
+ * an int32 or int64, the OCaml code passes in the pre-allocated pointer
+ * to the return value.
+ *
+ * The final offset in the string is calculated by the OCaml (caller)
+ * code. All we need to do is to read the string+offset and byteswap,
+ * sign-extend as necessary.
+ *
+ * There is one function for every combination of:
+ * (i) int size: 16, 32, 64 bits
+ * (ii) endian: bigendian, littleendian, nativeendian
+ * (iii) signed and unsigned
+ *
+ * XXX Future work: Expand this to 24, 40, 48, 56 bits. This
+ * requires some extra work because sign-extension won't "just happen".
+ */
+
+#ifdef ARCH_BIG_ENDIAN
+#define swap_be(size,v)
+#define swap_le(size,v) v = bswap_##size (v)
+#define swap_ne(size,v)
+#else
+#define swap_be(size,v) v = bswap_##size (v)
+#define swap_le(size,v)
+#define swap_ne(size,v)
+#endif
+
+#define fastpath1(size,endian,signed,type) \
+ CAMLprim value \
+ ocaml_bitstring_extract_fastpath_int##size##_##endian##_##signed \
+ (value strv, value offv) \
+ { \
+ type *ptr = (type *) ((void *) String_val (strv) + Int_val (offv));
\
+ type r; \
+ r = *ptr; \
+ swap_##endian(size,r); \
+ return Val_int (r);
\
+ }
+
+fastpath1(16,be,unsigned,uint16_t)
+fastpath1(16,le,unsigned,uint16_t)
+fastpath1(16,ne,unsigned,uint16_t)
+fastpath1(16,be,signed,int16_t)
+fastpath1(16,le,signed,int16_t)
+fastpath1(16,ne,signed,int16_t)
+
+#define fastpath2(size,endian,signed,type,rval)
\
+ CAMLprim value \
+ ocaml_bitstring_extract_fastpath_int##size##_##endian##_##signed \
+ (value strv, value offv, value rv) \
+ { \
+ type *ptr = (type *) ((void *) String_val (strv) + Int_val (offv));
\
+ type r; \
+ r = *ptr; \
+ swap_##endian(size,r); \
+ rval(rv) = r; \
+ return rv; \
+ }
+
+fastpath2(32,be,unsigned,uint32_t,Int32_val)
+fastpath2(32,le,unsigned,uint32_t,Int32_val)
+fastpath2(32,ne,unsigned,uint32_t,Int32_val)
+fastpath2(32,be,signed,int32_t,Int32_val)
+fastpath2(32,le,signed,int32_t,Int32_val)
+fastpath2(32,ne,signed,int32_t,Int32_val)
+
+/* Special care needs to be taken on ARCH_ALIGN_INT64 platforms
+ (hppa and sparc in Debian). */
+
+#ifdef ARCH_ALIGN_INT64
+#include <caml/memory.h>
+#include <caml/alloc.h>
+#define fastpath3(size,endian,signed,type,rval)
\
+ CAMLprim value \
+ ocaml_bitstring_extract_fastpath_int##size##_##endian##_##signed \
+ (value strv, value offv, value rv) \
+ { \
+ CAMLparam3(strv, offv, rv);
\
+ type *ptr = (type *) ((void *) String_val (strv) + Int_val (offv));
\
+ type r; \
+ r = *ptr; \
+ swap_##endian(size,r); \
+ CAMLreturn(caml_copy_int64(r)); \
+ }
+
+#else
+#define fastpath3 fastpath2
+#endif
+
+fastpath3(64,be,unsigned,uint64_t,Int64_val)
+fastpath3(64,le,unsigned,uint64_t,Int64_val)
+fastpath3(64,ne,unsigned,uint64_t,Int64_val)
+fastpath3(64,be,signed,int64_t,Int64_val)
+fastpath3(64,le,signed,int64_t,Int64_val)
+fastpath3(64,ne,signed,int64_t,Int64_val)
Index: src/utils/bitstring/bitstring_persistent.mlc4
===================================================================
RCS file: src/utils/bitstring/bitstring_persistent.mlc4
diff -N src/utils/bitstring/bitstring_persistent.mlc4
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/utils/bitstring/bitstring_persistent.mlc4 29 Aug 2010 20:17:57
-0000 1.1
@@ -0,0 +1,274 @@
+(* Bitstring persistent patterns.
+ * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version,
+ * with the OCaml linking exception described in COPYING.LIB.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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: bitstring_persistent.mlc4,v 1.1 2010/08/29 20:17:57 spiralvoice Exp $
+ *)
+
+open Printf
+
+open Camlp4.PreCast
+open Syntax
+open Ast
+
+type patt = Camlp4.PreCast.Syntax.Ast.patt
+type expr = Camlp4.PreCast.Syntax.Ast.expr
+type loc_t = Camlp4.PreCast.Syntax.Ast.Loc.t
+
+(* Field. In bitmatch (patterns) the type is [patt field]. In
+ * BITSTRING (constructor) the type is [expr field].
+ *)
+type 'a field = {
+ field : 'a; (* field ('a is either patt or expr) *)
+ flen : expr; (* length in bits, may be non-const *)
+ endian : endian_expr; (* endianness *)
+ signed : bool; (* true if signed, false if unsigned *)
+ t : field_type; (* type *)
+ _loc : Loc.t; (* location in source code *)
+ offset : expr option; (* offset expression *)
+ check : expr option; (* check expression [patterns only] *)
+ bind : expr option; (* bind expression [patterns only] *)
+ save_offset_to : patt option; (* save_offset_to [patterns
only] *)
+}
+and field_type = Int | String | Bitstring (* field type *)
+and endian_expr =
+ | ConstantEndian of Bitstring.endian (* a constant little/big/nativeendian *)
+ | EndianExpr of expr (* an endian expression *)
+
+type pattern = patt field list
+
+type constructor = expr field list
+
+type named = string * alt
+and alt =
+ | Pattern of pattern
+ | Constructor of constructor
+
+(* Work out if an expression is an integer constant.
+ *
+ * Returns [Some i] if so (where i is the integer value), else [None].
+ *
+ * Fairly simplistic algorithm: we can only detect simple constant
+ * expressions such as [k], [k+c], [k-c] etc.
+ *)
+let rec expr_is_constant = function
+ | <:expr< $int:i$ >> -> (* Literal integer constant. *)
+ Some (int_of_string i)
+ | <:expr< $a$ + $b$ >> -> (* Addition of constants. *)
+ (match expr_is_constant a, expr_is_constant b with
+ | Some a, Some b -> Some (a+b)
+ | _ -> None)
+ | <:expr< $a$ - $b$ >> -> (* Subtraction. *)
+ (match expr_is_constant a, expr_is_constant b with
+ | Some a, Some b -> Some (a-b)
+ | _ -> None)
+ | <:expr< $a$ * $b$ >> -> (* Multiplication. *)
+ (match expr_is_constant a, expr_is_constant b with
+ | Some a, Some b -> Some (a*b)
+ | _ -> None)
+ | <:expr< $a$ / $b$ >> -> (* Division. *)
+ (match expr_is_constant a, expr_is_constant b with
+ | Some a, Some b -> Some (a/b)
+ | _ -> None)
+ | <:expr< $a$ lsl $b$ >> -> (* Shift left. *)
+ (match expr_is_constant a, expr_is_constant b with
+ | Some a, Some b -> Some (a lsl b)
+ | _ -> None)
+ | <:expr< $a$ lsr $b$ >> -> (* Shift right. *)
+ (match expr_is_constant a, expr_is_constant b with
+ | Some a, Some b -> Some (a lsr b)
+ | _ -> None)
+ | _ -> None (* Anything else is not constant. *)
+
+let string_of_field_type = function
+ | Int -> "int"
+ | String -> "string"
+ | Bitstring -> "bitstring"
+
+let patt_printer = function
+ | <:patt< $lid:id$ >> -> id
+ | <:patt< _ >> -> "_"
+ | _ -> "[pattern]"
+
+let rec expr_printer = function
+ | <:expr< $lid:id$ >> -> id
+ | <:expr< $int:i$ >> -> i
+ | <:expr< $lid:op$ $a$ $b$ >> ->
+ sprintf "%s %s %s" op (expr_printer a) (expr_printer b)
+ | _ -> "[expr]"
+
+let _string_of_field { flen = flen;
+ endian = endian; signed = signed; t = t;
+ _loc = _loc;
+ offset = offset; check = check; bind = bind;
+ save_offset_to = save_offset_to } =
+ let flen = expr_printer flen in
+ let endian =
+ match endian with
+ | ConstantEndian endian -> Bitstring.string_of_endian endian
+ | EndianExpr expr -> sprintf "endian(%s)" (expr_printer expr) in
+ let signed = if signed then "signed" else "unsigned" in
+ let t = string_of_field_type t in
+
+ let offset =
+ match offset with
+ | None -> ""
+ | Some expr -> sprintf ", offset(%s)" (expr_printer expr) in
+
+ let check =
+ match check with
+ | None -> ""
+ | Some expr -> sprintf ", check(%s)" (expr_printer expr) in
+
+ let bind =
+ match bind with
+ | None -> ""
+ | Some expr -> sprintf ", bind(%s)" (expr_printer expr) in
+
+ let save_offset_to =
+ match save_offset_to with
+ | None -> ""
+ | Some patt ->
+ match patt with
+ | <:patt< $lid:id$ >> -> sprintf ", save_offset_to(%s)" id
+ | _ -> sprintf ", save_offset_to([patt])" in
+
+ let loc_fname = Loc.file_name _loc in
+ let loc_line = Loc.start_line _loc in
+ let loc_char = Loc.start_off _loc - Loc.start_bol _loc in
+
+ sprintf "%s : %s, %s, %s%s%s%s%s (* %S:%d %d *)"
+ flen t endian signed offset check bind save_offset_to
+ loc_fname loc_line loc_char
+
+let rec string_of_pattern_field ({ field = patt } as field) =
+ sprintf "%s : %s" (patt_printer patt) (_string_of_field field)
+
+and string_of_constructor_field ({ field = expr } as field) =
+ sprintf "%s : %s" (expr_printer expr) (_string_of_field field)
+
+let string_of_pattern pattern =
+ "{ " ^
+ String.concat ";\n " (List.map string_of_pattern_field pattern) ^
+ " }\n"
+
+let string_of_constructor constructor =
+ "{ " ^
+ String.concat ";\n " (List.map string_of_constructor_field constructor) ^
+ " }\n"
+
+let named_to_channel chan n = Marshal.to_channel chan n []
+
+let named_to_string n = Marshal.to_string n []
+
+let named_to_buffer str ofs len n = Marshal.to_buffer str ofs len n []
+
+let named_from_channel = Marshal.from_channel
+
+let named_from_string = Marshal.from_string
+
+let create_pattern_field _loc =
+ {
+ field = <:patt< _ >>;
+ flen = <:expr< 32 >>;
+ endian = ConstantEndian Bitstring.BigEndian;
+ signed = false;
+ t = Int;
+ _loc = _loc;
+ offset = None;
+ check = None;
+ bind = None;
+ save_offset_to = None;
+ }
+
+let set_lident_patt field id =
+ let _loc = field._loc in
+ { field with field = <:patt< $lid:id$ >> }
+let set_int_patt field i =
+ let _loc = field._loc in
+ { field with field = <:patt< $`int:i$ >> }
+let set_string_patt field str =
+ let _loc = field._loc in
+ { field with field = <:patt< $str:str$ >> }
+let set_unbound_patt field =
+ let _loc = field._loc in
+ { field with field = <:patt< _ >> }
+let set_patt field patt = { field with field = patt }
+let set_length_int field flen =
+ let _loc = field._loc in
+ { field with flen = <:expr< $`int:flen$ >> }
+let set_length field flen = { field with flen = flen }
+let set_endian field endian = { field with endian = ConstantEndian endian }
+let set_endian_expr field expr = { field with endian = EndianExpr expr }
+let set_signed field signed = { field with signed = signed }
+let set_type_int field = { field with t = Int }
+let set_type_string field = { field with t = String }
+let set_type_bitstring field = { field with t = Bitstring }
+let set_location field loc = { field with _loc = loc }
+let set_offset_int field i =
+ let _loc = field._loc in
+ { field with offset = Some <:expr< $`int:i$ >> }
+let set_offset field expr = { field with offset = Some expr }
+let set_no_offset field = { field with offset = None }
+let set_check field expr = { field with check = Some expr }
+let set_no_check field = { field with check = None }
+let set_bind field expr = { field with bind = Some expr }
+let set_no_bind field = { field with bind = None }
+let set_save_offset_to field patt = { field with save_offset_to = Some patt }
+let set_save_offset_to_lident field id =
+ let _loc = field._loc in
+ { field with save_offset_to = Some <:patt< $lid:id$ >> }
+let set_no_save_offset_to field = { field with save_offset_to = None }
+
+let create_constructor_field _loc =
+ {
+ field = <:expr< 0 >>;
+ flen = <:expr< 32 >>;
+ endian = ConstantEndian Bitstring.BigEndian;
+ signed = false;
+ t = Int;
+ _loc = _loc;
+ offset = None;
+ check = None;
+ bind = None;
+ save_offset_to = None;
+ }
+
+let set_lident_expr field id =
+ let _loc = field._loc in
+ { field with field = <:expr< $lid:id$ >> }
+let set_int_expr field i =
+ let _loc = field._loc in
+ { field with field = <:expr< $`int:i$ >> }
+let set_string_expr field str =
+ let _loc = field._loc in
+ { field with field = <:expr< $str:str$ >> }
+let set_expr field expr =
+ let _loc = field._loc in
+ { field with field = expr }
+
+let get_patt field = field.field
+let get_expr field = field.field
+let get_length field = field.flen
+let get_endian field = field.endian
+let get_signed field = field.signed
+let get_type field = field.t
+let get_location field = field._loc
+let get_offset field = field.offset
+let get_check field = field.check
+let get_bind field = field.bind
+let get_save_offset_to field = field.save_offset_to
Index: src/utils/bitstring/bitstring_persistent.mli
===================================================================
RCS file: src/utils/bitstring/bitstring_persistent.mli
diff -N src/utils/bitstring/bitstring_persistent.mli
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/utils/bitstring/bitstring_persistent.mli 29 Aug 2010 20:17:57
-0000 1.1
@@ -0,0 +1,541 @@
+(** Bitstring persistent patterns. *)
+(* Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version,
+ * with the OCaml linking exception described in COPYING.LIB.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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: bitstring_persistent.mli,v 1.1 2010/08/29 20:17:57 spiralvoice Exp $
+ *)
+
+(**
+ {b Warning:} This documentation is for ADVANCED USERS ONLY.
+ If you are not an advanced user, you are probably looking
+ for {{:Bitstring.html}the Bitstring documentation}.
+
+ {{:#reference}Jump straight to the reference section for
+ documentation on types and functions}.
+
+ {2 Introduction}
+
+ Bitstring allows you to name sets of fields and reuse them
+ elsewhere. For example if you frequently need to parse
+ Pascal-style strings in the form length byte + string, then you
+ could name the [{ strlen : 8 : int; str : strlen*8 : string }]
+ pattern and reuse it everywhere by name.
+
+ These are called {b persistent patterns}.
+
+ The basic usage is:
+
+{v
+(* Create a persistent pattern called 'pascal_string' which
+ * matches Pascal-style strings (length byte + string).
+ *)
+let bitmatch pascal_string =
+ \{ strlen : 8 : int;
+ str : strlen*8 : string }
+
+let is_pascal_string bits =
+ bitmatch bits with
+ | \{ :pascal_string } ->
+ printf "matches a Pascal string %s, len %d bytes\n"
+ str strlen
+v}
+
+ or:
+
+{v
+(* Load a persistent pattern from a file. *)
+open bitmatch "pascal.bmpp"
+
+let is_pascal_string bits =
+ bitmatch bits with
+ | \{ :pascal_string } ->
+ printf "matches a Pascal string %s, len %d bytes\n"
+ str strlen
+v}
+
+ {3 Important notes}
+
+ There are some important things you should know about
+ persistent patterns before you decide to use them:
+
+ 'Persistent' refers to the fact that they can be saved into binary
+ files. However these binary files use OCaml [Marshal] module and
+ depend (sometimes) on the version of OCaml used to generate them
+ and (sometimes) the version of bitstring used. So your build system
+ should rebuild these files from source when your code is rebuilt.
+
+ Persistent patterns are syntactic. They work in the same way
+ as cutting and pasting (or [#include]-ing) code. For example
+ if a persistent pattern binds a field named [len], then any
+ uses of [len] following in the surrounding pattern could
+ be affected.
+
+ Programs which generate and manipulate persistent patterns have to
+ link to camlp4. Since camlp4 in OCaml >= 3.10 is rather large, we
+ have placed this code into this separate submodule, so that
+ programs which just use bitstring don't need to pull in the whole of
+ camlp4. This restriction does not apply to code which only uses
+ persistent patterns but does not generate them. If the distinction
+ isn't clear, use [ocamlobjinfo] to look at the dependencies of your
+ [*.cmo] files.
+
+ Persistent patterns can be generated in several ways, but they
+ can only be {i used} by the [pa_bitstring] syntax extension.
+ This means they are purely compile-time constructs. You
+ cannot use them to make arbitrary patterns and run those
+ patterns (not unless your program runs [ocamlc] to make a [*.cmo]
+ file then dynamically links to the [*.cmo] file).
+
+ {2 Named patterns}
+
+ A named pattern is a way to name a pattern and use it later
+ in the same source file. To name a pattern, use:
+
+ [let bitmatch name = { fields ... } ;;]
+
+ and you can then use the name later on inside another pattern,
+ by prefixing the name with a colon.
+ For example:
+
+ [bitmatch bits with { :name } -> ...]
+
+ You can nest named patterns within named patterns to any depth.
+
+ Currently the use of named patterns is somewhat limited.
+ The restrictions are:
+
+ Named patterns can only be used within the same source file, and
+ the names occupy a completely separate namespace from anything
+ else in the source file.
+
+ The [let bitmatch] syntax only works at the top level. We may
+ add a [let bitmatch ... in] for inner levels later.
+
+ Because you cannot rename the bound identifiers in named
+ patterns, you can effectively only use them once in a
+ pattern. For example, [{ :name; :name }] is legal, but
+ any bindings in the first name would be overridden by
+ the second name.
+
+ There are no "named constructors" yet, but the machinery
+ is in place to do this, and we may add them later.
+
+ {2 Persistent patterns in files}
+
+ More useful than just naming patterns, you can load
+ persistent patterns from external files. The patterns
+ in these external files can come from a variety of sources:
+ for example, in the [cil-tools] subdirectory are some
+ {{:http://cil.sf.net/}Cil-based} tools for importing C
+ structures from header files. You can also generate
+ your own files or write your own tools, as described below.
+
+ To use the persistent pattern(s) from a file do:
+
+ [open bitmatch "filename.bmpp" ;;]
+
+ A list of zero or more {!named} patterns are read from the file
+ and each is bound to a name (as contained in the file),
+ and then the patterns can be used with the usual [:name]
+ syntax described above.
+
+ {3 Extension}
+
+ The standard extension is [.bmpp]. This is just a convention
+ and you can use any extension you want.
+
+ {3 Directory search order}
+
+ If the filename is an absolute or explicit path, then we try to
+ load it from that path and stop if it fails. See the [Filename]
+ module in the standard OCaml library for the definitions of
+ "absolute path" and "explicit path". Otherwise we use the
+ following directory search order:
+
+ - Relative to the current directory
+ - Relative to the OCaml library directory
+
+ {3 bitstring-objinfo}
+
+ The [bitstring-objinfo] command can be run on a file in order
+ to print out the patterns in the file.
+
+ {3 Constructors}
+
+ We haven't implemented persistent constructors yet, although
+ the machinery is in place to make this happen. Any constructors
+ found in the file are ignored.
+
+ {2 Creating your own persistent patterns}
+
+ If you want to write a tool to import bitstrings from an
+ exotic location or markup language, you will need
+ to use the functions found in the {{:#reference}reference section}.
+
+ I will describe using an example here of how you would
+ programmatically create a persistent pattern which
+ matches Pascal-style "length byte + data" strings.
+ Firstly note that there are two fields, so our pattern
+ will be a list of length 2 and type {!pattern}.
+
+ You will need to create a camlp4 location object ([Loc.t])
+ describing the source file. This source file is used
+ to generate useful error messages for the user, so
+ you may want to set it to be the name and location in
+ the file that your tool reads for input. By convention,
+ locations are bound to name [_loc]:
+
+ {v
+ let _loc = Loc.move_line 42 (Loc.mk "input.xml")
+ v}
+
+ Create a pattern field representing a length field which is 8 bits wide,
+ bound to the identifier [len]:
+
+ {v
+ let len_field = create_pattern_field _loc
+ let len_field = set_length_int len_field 8
+ let len_field = set_lident_patt len_field "len"
+ v}
+
+ Create a pattern field representing a string of [len*8] bits.
+ Note that the use of [<:expr< >>] quotation requires
+ you to preprocess your source with [camlp4of]
+ (see {{:http://brion.inria.fr/gallium/index.php/Reflective_OCaml}this
+ page on Reflective OCaml}).
+
+ {v
+ let str_field = create_pattern_field _loc
+ let str_field = set_length str_field <:expr< len*8 >>
+ let str_field = set_lident_patt str_field "str"
+ let str_field = set_type_string str_field
+ v}
+
+ Join the two fields together and name it:
+
+ {v
+ let pattern = [len_field; str_field]
+ let named_pattern = "pascal_string", Pattern pattern
+ v}
+
+ Save it to a file:
+
+ {v
+ let chan = open_out "output.bmpp" in
+ named_to_channel chan named_pattern;
+ close_out chan
+ v}
+
+ You can now use this pattern in another program like this:
+
+ {v
+ open bitmatch "output.bmpp" ;;
+ let parse_pascal_string bits =
+ bitmatch bits with
+ | \{ :pascal_string } -> str, len
+ | \{ _ } -> invalid_arg "not a Pascal string"
+ v}
+
+ You can write more than one named pattern to the output file, and
+ they will all be loaded at the same time by [open bitmatch ".."]
+ (obviously you should give each pattern a different name). To do
+ this, just call {!named_to_channel} as many times as needed.
+
+ {2:reference Reference}
+
+ {3 Types}
+*)
+
+open Camlp4
+
+type patt = Camlp4.PreCast.Syntax.Ast.patt
+type expr = Camlp4.PreCast.Syntax.Ast.expr
+type loc_t = Camlp4.PreCast.Syntax.Ast.Loc.t
+(** Just short names for the camlp4 types. *)
+
+type 'a field
+(** A field in a persistent pattern or persistent constructor. *)
+
+type pattern = patt field list
+(** A persistent pattern (used in [bitmatch] operator), is just a
+ list of pattern fields. *)
+
+type constructor = expr field list
+(** A persistent constructor (used in [BITSTRING] operator), is just a
+ list of constructor fields. *)
+
+type named = string * alt
+and alt =
+ | Pattern of pattern (** Pattern *)
+ | Constructor of constructor (** Constructor *)
+(** A named pattern or constructor.
+
+ The name is used when binding a pattern from a file, but
+ is otherwise ignored. *)
+
+(** {3 Printers} *)
+
+val string_of_pattern : pattern -> string
+val string_of_constructor : constructor -> string
+val string_of_pattern_field : patt field -> string
+val string_of_constructor_field : expr field -> string
+(** Convert patterns, constructors or individual fields
+ into printable strings for debugging purposes.
+
+ The strings look similar to the syntax used by bitmatch, but
+ some things cannot be printed fully, eg. length expressions. *)
+
+(** {3 Persistence} *)
+
+val named_to_channel : out_channel -> named -> unit
+(** Save a pattern/constructor to an output channel. *)
+
+val named_to_string : named -> string
+(** Serialize a pattern/constructor to a string. *)
+
+val named_to_buffer : string -> int -> int -> named -> int
+(** Serialize a pattern/constructor to part of a string, return the length. *)
+
+val named_from_channel : in_channel -> named
+(** Load a pattern/constructor from an output channel.
+
+ Note: This is not type safe. The pattern/constructor must
+ have been written out under the same version of OCaml and
+ the same version of bitstring. *)
+
+val named_from_string : string -> int -> named
+(** Load a pattern/constructor from a string at offset within the string.
+
+ Note: This is not type safe. The pattern/constructor must
+ have been written out under the same version of OCaml and
+ the same version of bitstring. *)
+
+(** {3 Create pattern fields}
+
+ These fields are used in pattern matches ([bitmatch]). *)
+
+val create_pattern_field : loc_t -> patt field
+(** Create a pattern field.
+
+ The pattern is unbound, the type is set to [int], bit length to [32],
+ endianness to [BigEndian], signedness to unsigned ([false]),
+ source code location to the [_loc] parameter, and no offset expression.
+
+ To create a complete field you need to call the [set_*]
+ functions. For example, to create [{ len : 8 : int }]
+ you would do:
+
+{v
+ let field = create_pattern_field _loc in
+ let field = set_lident_patt field "len" in
+ let field = set_length_int field 8 in
+v}
+*)
+
+val set_lident_patt : patt field -> string -> patt field
+(** Sets the pattern to the pattern binding an identifier
+ given in the string.
+
+ The effect is that the field [{ len : 8 : int }] could
+ be created by calling [set_lident_patt field "len"]. *)
+
+val set_int_patt : patt field -> int -> patt field
+(** Sets the pattern field to the pattern which matches an integer.
+
+ The effect is that the field [{ 2 : 8 : int }] could
+ be created by calling [set_int_patt field 2]. *)
+
+val set_string_patt : patt field -> string -> patt field
+(** Sets the pattern field to the pattern which matches a string.
+
+ The effect is that the field [{ "MAGIC" : 8*5 : string }] could
+ be created by calling [set_int_patt field "MAGIC"]. *)
+
+val set_unbound_patt : patt field -> patt field
+(** Sets the pattern field to the unbound pattern (usually written [_]).
+
+ The effect is that the field [{ _ : 8 : int }] could
+ be created by calling [set_unbound_patt field]. *)
+
+val set_patt : patt field -> patt -> patt field
+(** Sets the pattern field to an arbitrary OCaml pattern match. *)
+
+val set_length_int : 'a field -> int -> 'a field
+(** Sets the length in bits of a field to a constant integer.
+
+ The effect is that the field [{ len : 8 : string }] could
+ be created by calling [set_length field 8]. *)
+
+val set_length : 'a field -> expr -> 'a field
+(** Sets the length in bits of a field to an OCaml expression.
+
+ The effect is that the field [{ len : 2*i : string }] could
+ be created by calling [set_length field <:expr< 2*i >>]. *)
+
+val set_endian : 'a field -> Bitstring.endian -> 'a field
+(** Sets the endianness of a field to the constant endianness.
+
+ The effect is that the field [{ _ : 16 : bigendian }] could
+ be created by calling [set_endian field Bitstring.BigEndian]. *)
+
+val set_endian_expr : 'a field -> expr -> 'a field
+(** Sets the endianness of a field to an endianness expression.
+
+ The effect is that the field [{ _ : 16 : endian(e) }] could
+ be created by calling [set_endian_expr field e]. *)
+
+val set_signed : 'a field -> bool -> 'a field
+(** Sets the signedness of a field to a constant signedness.
+
+ The effect is that the field [{ _ : 16 : signed }] could
+ be created by calling [set_signed field true]. *)
+
+val set_type_int : 'a field -> 'a field
+(** Sets the type of a field to [int].
+
+ The effect is that the field [{ _ : 16 : int }] could
+ be created by calling [set_type_int field]. *)
+
+val set_type_string : 'a field -> 'a field
+(** Sets the type of a field to [string].
+
+ The effect is that the field [{ str : 16 : string }] could
+ be created by calling [set_type_string field]. *)
+
+val set_type_bitstring : 'a field -> 'a field
+(** Sets the type of a field to [bitstring].
+
+ The effect is that the field [{ _ : 768 : bitstring }] could
+ be created by calling [set_type_bitstring field]. *)
+
+val set_location : 'a field -> loc_t -> 'a field
+(** Sets the source code location of a field. This is used when
+ pa_bitstring displays error messages. *)
+
+val set_offset_int : 'a field -> int -> 'a field
+(** Set the offset expression for a field to the given number.
+
+ The effect is that the field [{ _ : 8 : offset(160) }] could
+ be created by calling [set_offset_int field 160]. *)
+
+val set_offset : 'a field -> expr -> 'a field
+(** Set the offset expression for a field to the given expression.
+
+ The effect is that the field [{ _ : 8 : offset(160) }] could
+ be created by calling [set_offset_int field <:expr< 160 >>]. *)
+
+val set_no_offset : 'a field -> 'a field
+(** Remove the offset expression from a field. The field will
+ follow the previous field, or if it is the first field will
+ be at offset zero. *)
+
+val set_check : 'a field -> expr -> 'a field
+(** Set the check expression for a field to the given expression. *)
+
+val set_no_check : 'a field -> 'a field
+(** Remove the check expression from a field. *)
+
+val set_bind : 'a field -> expr -> 'a field
+(** Set the bind-expression for a field to the given expression. *)
+
+val set_no_bind : 'a field -> 'a field
+(** Remove the bind-expression from a field. *)
+
+val set_save_offset_to : 'a field -> patt -> 'a field
+(** Set the save_offset_to pattern for a field to the given pattern. *)
+
+val set_save_offset_to_lident : 'a field -> string -> 'a field
+(** Set the save_offset_to pattern for a field to identifier. *)
+
+val set_no_save_offset_to : 'a field -> 'a field
+(** Remove the save_offset_to from a field. *)
+
+(** {3 Create constructor fields}
+
+ These fields are used in constructors ([BITSTRING]). *)
+
+val create_constructor_field : loc_t -> expr field
+(** Create a constructor field.
+
+ The defaults are the same as for {!create_pattern_field}
+ except that the expression is initialized to [0].
+*)
+
+val set_lident_expr : expr field -> string -> expr field
+(** Sets the expression in a constructor field to an expression
+ which uses the identifier.
+
+ The effect is that the field [{ len : 8 : int }] could
+ be created by calling [set_lident_expr field "len"]. *)
+
+val set_int_expr : expr field -> int -> expr field
+(** Sets the expression to the value of the integer.
+
+ The effect is that the field [{ 2 : 8 : int }] could
+ be created by calling [set_int_expr field 2]. *)
+
+val set_string_expr : expr field -> string -> expr field
+(** Sets the expression to the value of the string.
+
+ The effect is that the field [{ "MAGIC" : 8*5 : string }] could
+ be created by calling [set_int_expr field "MAGIC"]. *)
+
+val set_expr : expr field -> expr -> expr field
+(** Sets the expression field to an arbitrary OCaml expression. *)
+
+(** {3 Accessors} *)
+
+val get_patt : patt field -> patt
+(** Get the pattern from a pattern field. *)
+
+val get_expr : expr field -> expr
+(** Get the expression from an expression field. *)
+
+val get_length : 'a field -> expr
+(** Get the length in bits from a field. Note that what is returned
+ is an OCaml expression, since lengths can be non-constant. *)
+
+type endian_expr =
+ | ConstantEndian of Bitstring.endian
+ | EndianExpr of expr
+
+val get_endian : 'a field -> endian_expr
+(** Get the endianness of a field. This is an {!endian_expr} which
+ could be a constant or an OCaml expression. *)
+
+val get_signed : 'a field -> bool
+(** Get the signedness of a field. *)
+
+type field_type = Int | String | Bitstring
+
+val get_type : 'a field -> field_type
+(** Get the type of a field, [Int], [String] or [Bitstring]. *)
+
+val get_location : 'a field -> loc_t
+(** Get the source code location of a field. *)
+
+val get_offset : 'a field -> expr option
+(** Get the offset expression of a field, or [None] if there is none. *)
+
+val get_check : 'a field -> expr option
+(** Get the check expression of a field, or [None] if there is none. *)
+
+val get_bind : 'a field -> expr option
+(** Get the bind expression of a field, or [None] if there is none. *)
+
+val get_save_offset_to : 'a field -> patt option
+(** Get the save_offset_to pattern of a field, or [None] if there is none. *)
Index: src/utils/bitstring/byteswap.h
===================================================================
RCS file: src/utils/bitstring/byteswap.h
diff -N src/utils/bitstring/byteswap.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/utils/bitstring/byteswap.h 29 Aug 2010 20:17:57 -0000 1.1
@@ -0,0 +1,54 @@
+/* byteswap.h - Byte swapping
+ Copyright (C) 2005, 2007 Free Software Foundation, Inc.
+ Written by Oskar Liljeblad <address@hidden>, 2005.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* NB:
+
+ This file is from Gnulib, and in accordance with the convention
+ there, the real license of this file comes from the module
+ definition. It is really LGPLv2+.
+
+ - RWMJ. 2008/08/23
+*/
+
+#ifndef _GL_BYTESWAP_H
+#define _GL_BYTESWAP_H
+
+/* Given an unsigned 16-bit argument X, return the value corresponding to
+ X with reversed byte order. */
+#define bswap_16(x) ((((x) & 0x00FF) << 8) | \
+ (((x) & 0xFF00) >> 8))
+
+/* Given an unsigned 32-bit argument X, return the value corresponding to
+ X with reversed byte order. */
+#define bswap_32(x) ((((x) & 0x000000FF) << 24) | \
+ (((x) & 0x0000FF00) << 8) | \
+ (((x) & 0x00FF0000) >> 8) | \
+ (((x) & 0xFF000000) >> 24))
+
+/* Given an unsigned 64-bit argument X, return the value corresponding to
+ X with reversed byte order. */
+#define bswap_64(x) ((((x) & 0x00000000000000FFULL) << 56) | \
+ (((x) & 0x000000000000FF00ULL) << 40) | \
+ (((x) & 0x0000000000FF0000ULL) << 24) | \
+ (((x) & 0x00000000FF000000ULL) << 8) | \
+ (((x) & 0x000000FF00000000ULL) >> 8) | \
+ (((x) & 0x0000FF0000000000ULL) >> 24) | \
+ (((x) & 0x00FF000000000000ULL) >> 40) | \
+ (((x) & 0xFF00000000000000ULL) >> 56))
+
+#endif /* _GL_BYTESWAP_H */
Index: src/utils/bitstring/pa_bitstring.mlt
===================================================================
RCS file: src/utils/bitstring/pa_bitstring.mlt
diff -N src/utils/bitstring/pa_bitstring.mlt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/utils/bitstring/pa_bitstring.mlt 29 Aug 2010 20:17:57 -0000
1.1
@@ -0,0 +1,1193 @@
+(* Bitstring syntax extension.
+ * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version,
+ * with the OCaml linking exception described in COPYING.LIB.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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: pa_bitstring.mlt,v 1.1 2010/08/29 20:17:57 spiralvoice Exp $
+ *)
+
+open Printf
+
+open Camlp4.PreCast
+open Syntax
+open Ast
+
+open Bitstring
+module P = Bitstring_persistent
+
+(* If this is true then we emit some debugging code which can
+ * be useful to tell what is happening during matches. You
+ * also need to do 'Bitstring.debug := true' in your main program.
+ *
+ * If this is false then no extra debugging code is emitted.
+ *)
+let debug = false
+
+(* Hashtable storing named persistent patterns. *)
+let pattern_hash : (string, P.pattern) Hashtbl.t = Hashtbl.create 13
+
+let locfail _loc msg = Loc.raise _loc (Failure msg)
+
+(* Work out if an expression is an integer constant.
+ *
+ * Returns [Some i] if so (where i is the integer value), else [None].
+ *
+ * Fairly simplistic algorithm: we can only detect simple constant
+ * expressions such as [k], [k+c], [k-c] etc.
+ *)
+let rec expr_is_constant = function
+ | <:expr< $int:i$ >> -> (* Literal integer constant. *)
+ Some (int_of_string i)
+ | <:expr< $lid:op$ $a$ $b$ >> ->
+ (match expr_is_constant a, expr_is_constant b with
+ | Some a, Some b -> (* Integer binary operations. *)
+ let ops = ["+", (+); "-", (-); "*", ( * ); "/", (/);
+ (* NB: explicit fun .. -> is necessary here to work
+ * around a camlp4 bug in OCaml 3.10.0.
+ *)
+ "land", (fun a b -> a land b);
+ "lor", (fun a b -> a lor b);
+ "lxor", (fun a b -> a lxor b);
+ "lsl", (fun a b -> a lsl b);
+ "lsr", (fun a b -> a lsr b);
+ "asr", (fun a b -> a asr b);
+ "mod", (fun a b -> a mod b)] in
+ (try Some ((List.assoc op ops) a b) with Not_found -> None)
+ | _ -> None)
+ | _ -> None
+
+(* Generate a fresh, unique symbol each time called. *)
+let gensym =
+ let i = ref 1000 in
+ fun name ->
+ incr i; let i = !i in
+ sprintf "__pabitstring_%s_%d" name i
+
+(* Used to keep track of which qualifiers we've seen in parse_field. *)
+type whatset_t = {
+ endian_set : bool; signed_set : bool; type_set : bool;
+ offset_set : bool; check_set : bool; bind_set : bool;
+ save_offset_to_set : bool;
+}
+let noneset = {
+ endian_set = false; signed_set = false; type_set = false;
+ offset_set = false; check_set = false; bind_set = false;
+ save_offset_to_set = false
+}
+
+(* Deal with the qualifiers which appear for a field of both types. *)
+let parse_field _loc field qs =
+ let fail = locfail _loc in
+
+ let whatset, field =
+ match qs with
+ | None -> noneset, field
+ | Some qs ->
+ let check already_set msg = if already_set then fail msg in
+ let apply_qualifier (whatset, field) =
+ function
+ | "endian", Some expr ->
+ check whatset.endian_set "an endian flag has been set already";
+ let field = P.set_endian_expr field expr in
+ { whatset with endian_set = true }, field
+ | "endian", None ->
+ fail "qualifier 'endian' should be followed by an expression"
+ | "offset", Some expr ->
+ check whatset.offset_set "an offset has been set already";
+ let field = P.set_offset field expr in
+ { whatset with offset_set = true }, field
+ | "offset", None ->
+ fail "qualifier 'offset' should be followed by an expression"
+ | "check", Some expr ->
+ check whatset.check_set "a check-qualifier has been set already";
+ let field = P.set_check field expr in
+ { whatset with check_set = true }, field
+ | "check", None ->
+ fail "qualifier 'check' should be followed by an expression"
+ | "bind", Some expr ->
+ check whatset.bind_set "a bind expression has been set already";
+ let field = P.set_bind field expr in
+ { whatset with bind_set = true }, field
+ | "bind", None ->
+ fail "qualifier 'bind' should be followed by an expression"
+ | "save_offset_to", Some expr (* XXX should be a pattern *) ->
+ check whatset.save_offset_to_set
+ "a save_offset_to-qualifier has been set already";
+ let id =
+ match expr with
+ | <:expr< $lid:id$ >> -> id
+ | _ ->
+ failwith "pa_bitstring: internal error: save_offset_to only
supports simple identifiers at the moment. In future we should support full
patterns." in
+ let field = P.set_save_offset_to_lident field id in
+ { whatset with save_offset_to_set = true }, field
+ | "save_offset_to", None ->
+ fail "qualifier 'save_offset_to' should be followed by a binding
expression"
+ | s, Some _ ->
+ fail (s ^ ": unknown qualifier, or qualifier should not be
followed by an expression")
+ | qual, None ->
+ let endian_quals = ["bigendian", BigEndian;
+ "littleendian", LittleEndian;
+ "nativeendian", NativeEndian] in
+ let sign_quals = ["signed", true; "unsigned", false] in
+ let type_quals = ["int", P.set_type_int;
+ "string", P.set_type_string;
+ "bitstring", P.set_type_bitstring] in
+ if List.mem_assoc qual endian_quals then (
+ check whatset.endian_set "an endian flag has been set already";
+ let field = P.set_endian field (List.assoc qual endian_quals) in
+ { whatset with endian_set = true }, field
+ ) else if List.mem_assoc qual sign_quals then (
+ check whatset.signed_set "a signed flag has been set already";
+ let field = P.set_signed field (List.assoc qual sign_quals) in
+ { whatset with signed_set = true }, field
+ ) else if List.mem_assoc qual type_quals then (
+ check whatset.type_set "a type flag has been set already";
+ let field = (List.assoc qual type_quals) field in
+ { whatset with type_set = true }, field
+ ) else
+ fail (qual ^ ": unknown qualifier, or qualifier should be
followed by an expression") in
+ List.fold_left apply_qualifier (noneset, field) qs in
+
+ (* If type is set to string or bitstring then endianness and
+ * signedness qualifiers are meaningless and must not be set.
+ *)
+ let () =
+ let t = P.get_type field in
+ if (t = P.Bitstring || t = P.String) &&
+ (whatset.endian_set || whatset.signed_set) then
+ fail "string types and endian or signed qualifiers cannot be mixed" in
+
+ (* Default endianness, signedness, type if not set already. *)
+ let field =
+ if whatset.endian_set then field else P.set_endian field BigEndian in
+ let field =
+ if whatset.signed_set then field else P.set_signed field false in
+ let field =
+ if whatset.type_set then field else P.set_type_int field in
+
+ field
+
+type functype = ExtractFunc | ConstructFunc
+
+(* Choose the right constructor function. *)
+let build_bitstring_call _loc functype length endian signed =
+ match functype, length, endian, signed with
+ (* XXX The meaning of signed/unsigned breaks down at
+ * 31, 32, 63 and 64 bits.
+ *)
+ | (ExtractFunc, Some 1, _, _) -> <:expr< Bitstring.extract_bit >>
+ | (ConstructFunc, Some 1, _, _) -> <:expr< Bitstring.construct_bit >>
+ | (functype, Some (2|3|4|5|6|7|8), _, signed) ->
+ let funcname = match functype with
+ | ExtractFunc -> "extract"
+ | ConstructFunc -> "construct" in
+ let sign = if signed then "signed" else "unsigned" in
+ let call = sprintf "%s_char_%s" funcname sign in
+ <:expr< Bitstring.$lid:call$ >>
+ | (functype, len, endian, signed) ->
+ let funcname = match functype with
+ | ExtractFunc -> "extract"
+ | ConstructFunc -> "construct" in
+ let t = match len with
+ | Some i when i <= 31 -> "int"
+ | Some 32 -> "int32"
+ | _ -> "int64" in
+ let sign = if signed then "signed" else "unsigned" in
+ match endian with
+ | P.ConstantEndian constant ->
+ let endianness = match constant with
+ | BigEndian -> "be"
+ | LittleEndian -> "le"
+ | NativeEndian -> "ne" in
+ let call = sprintf "%s_%s_%s_%s" funcname t endianness sign in
+ <:expr< Bitstring.$lid:call$ >>
+ | P.EndianExpr expr ->
+ let call = sprintf "%s_%s_%s_%s" funcname t "ee" sign in
+ <:expr< Bitstring.$lid:call$ $expr$ >>
+
+(* Generate the code for a constructor, ie. 'BITSTRING ...'. *)
+let output_constructor _loc fields =
+ (* This function makes code to raise a Bitstring.Construct_failure exception
+ * containing a message and the current _loc context.
+ * (Thanks to Bluestorm for suggesting this).
+ *)
+ let construct_failure _loc msg =
+ <:expr<
+ Bitstring.Construct_failure
+ ($`str:msg$,
+ $`str:Loc.file_name _loc$,
+ $`int:Loc.start_line _loc$,
+ $`int:Loc.start_off _loc - Loc.start_bol _loc$)
+ >>
+ in
+ let raise_construct_failure _loc msg =
+ <:expr< raise $construct_failure _loc msg$ >>
+ in
+
+ (* Bitstrings are created like the 'Buffer' module (in fact, using
+ * the Buffer module), by appending snippets to a growing buffer.
+ * This is reasonably efficient and avoids a lot of garbage.
+ *)
+ let buffer = gensym "buffer" in
+
+ (* General exception which is raised inside the constructor functions
+ * when an int expression is out of range at runtime.
+ *)
+ let exn = gensym "exn" in
+ let exn_used = ref false in
+
+ (* Convert each field to a simple bitstring-generating expression. *)
+ let fields = List.map (
+ fun field ->
+ let fexpr = P.get_expr field in
+ let flen = P.get_length field in
+ let endian = P.get_endian field in
+ let signed = P.get_signed field in
+ let t = P.get_type field in
+ let _loc = P.get_location field in
+
+ let fail = locfail _loc in
+
+ (* offset(), check(), bind(), save_offset_to() not supported in
+ * constructors.
+ *
+ * Implementation of forward-only offsets is fairly
+ * straightforward: we would need to just calculate the length of
+ * padding here and add it to what has been constructed. For
+ * general offsets, including going backwards, that would require
+ * a rethink in how we construct bitstrings.
+ *)
+ if P.get_offset field <> None then
+ fail "offset expressions are not supported in BITSTRING constructors";
+ if P.get_check field <> None then
+ fail "check expressions are not supported in BITSTRING constructors";
+ if P.get_bind field <> None then
+ fail "bind expressions are not supported in BITSTRING constructors";
+ if P.get_save_offset_to field <> None then
+ fail "save_offset_to is not supported in BITSTRING constructors";
+
+ (* Is flen an integer constant? If so, what is it? This
+ * is very simple-minded and only detects simple constants.
+ *)
+ let flen_is_const = expr_is_constant flen in
+
+ let int_construct_const (i, endian, signed) =
+ build_bitstring_call _loc ConstructFunc (Some i) endian signed in
+ let int_construct (endian, signed) =
+ build_bitstring_call _loc ConstructFunc None endian signed in
+
+ let expr =
+ match t, flen_is_const with
+ (* Common case: int field, constant flen.
+ *
+ * Range checks are done inside the construction function
+ * because that's a lot simpler w.r.t. types. It might
+ * be better to move them here. XXX
+ *)
+ | P.Int, Some i when i > 0 && i <= 64 ->
+ let construct_fn = int_construct_const (i,endian,signed) in
+ exn_used := true;
+
+ <:expr<
+ $construct_fn$ $lid:buffer$ $fexpr$ $`int:i$ $lid:exn$
+ >>
+
+ | P.Int, Some _ ->
+ fail "length of int field must be [1..64]"
+
+ (* Int field, non-constant length. We need to perform a runtime
+ * test to ensure the length is [1..64].
+ *
+ * Range checks are done inside the construction function
+ * because that's a lot simpler w.r.t. types. It might
+ * be better to move them here. XXX
+ *)
+ | P.Int, None ->
+ let construct_fn = int_construct (endian,signed) in
+ exn_used := true;
+
+ <:expr<
+ if $flen$ >= 1 && $flen$ <= 64 then
+ $construct_fn$ $lid:buffer$ $fexpr$ $flen$ $lid:exn$
+ else
+ $raise_construct_failure _loc "length of int field must be
[1..64]"$
+ >>
+
+ (* String, constant length > 0, must be a multiple of 8. *)
+ | P.String, Some i when i > 0 && i land 7 = 0 ->
+ let bs = gensym "bs" in
+ let j = i lsr 3 in
+ <:expr<
+ let $lid:bs$ = $fexpr$ in
+ if String.length $lid:bs$ = $`int:j$ then
+ Bitstring.construct_string $lid:buffer$ $lid:bs$
+ else
+ $raise_construct_failure _loc "length of string does not match
declaration"$
+ >>
+
+ (* String, constant length -1, means variable length string
+ * with no checks.
+ *)
+ | P.String, Some (-1) ->
+ <:expr< Bitstring.construct_string $lid:buffer$ $fexpr$ >>
+
+ (* String, constant length = 0 is probably an error, and so is
+ * any other value.
+ *)
+ | P.String, Some _ ->
+ fail "length of string must be > 0 and a multiple of 8, or the
special value -1"
+
+ (* String, non-constant length.
+ * We check at runtime that the length is > 0, a multiple of 8,
+ * and matches the declared length.
+ *)
+ | P.String, None ->
+ let bslen = gensym "bslen" in
+ let bs = gensym "bs" in
+ <:expr<
+ let $lid:bslen$ = $flen$ in
+ if $lid:bslen$ > 0 then (
+ if $lid:bslen$ land 7 = 0 then (
+ let $lid:bs$ = $fexpr$ in
+ if String.length $lid:bs$ = ($lid:bslen$ lsr 3) then
+ Bitstring.construct_string $lid:buffer$ $lid:bs$
+ else
+ $raise_construct_failure _loc "length of string does not
match declaration"$
+ ) else
+ $raise_construct_failure _loc "length of string must be a
multiple of 8"$
+ ) else
+ $raise_construct_failure _loc "length of string must be > 0"$
+ >>
+
+ (* Bitstring, constant length >= 0. *)
+ | P.Bitstring, Some i when i >= 0 ->
+ let bs = gensym "bs" in
+ <:expr<
+ let $lid:bs$ = $fexpr$ in
+ if Bitstring.bitstring_length $lid:bs$ = $`int:i$ then
+ Bitstring.construct_bitstring $lid:buffer$ $lid:bs$
+ else
+ $raise_construct_failure _loc "length of bitstring does not
match declaration"$
+ >>
+
+ (* Bitstring, constant length -1, means variable length bitstring
+ * with no checks.
+ *)
+ | P.Bitstring, Some (-1) ->
+ <:expr< Bitstring.construct_bitstring $lid:buffer$ $fexpr$ >>
+
+ (* Bitstring, constant length < -1 is an error. *)
+ | P.Bitstring, Some _ ->
+ fail "length of bitstring must be >= 0 or the special value -1"
+
+ (* Bitstring, non-constant length.
+ * We check at runtime that the length is >= 0 and matches
+ * the declared length.
+ *)
+ | P.Bitstring, None ->
+ let bslen = gensym "bslen" in
+ let bs = gensym "bs" in
+ <:expr<
+ let $lid:bslen$ = $flen$ in
+ if $lid:bslen$ >= 0 then (
+ let $lid:bs$ = $fexpr$ in
+ if Bitstring.bitstring_length $lid:bs$ = $lid:bslen$ then
+ Bitstring.construct_bitstring $lid:buffer$ $lid:bs$
+ else
+ $raise_construct_failure _loc "length of bitstring does not
match declaration"$
+ ) else
+ $raise_construct_failure _loc "length of bitstring must be > 0"$
+ >> in
+ expr
+ ) fields in
+
+ (* Create the final bitstring. Start by creating an empty buffer
+ * and then evaluate each expression above in turn which will
+ * append some more to the bitstring buffer. Finally extract
+ * the bitstring.
+ *
+ * XXX We almost have enough information to be able to guess
+ * a good initial size for the buffer.
+ *)
+ let fields =
+ match fields with
+ | [] -> <:expr< [] >>
+ | h::t -> List.fold_left (fun h t -> <:expr< $h$; $t$ >>) h t in
+
+ let expr =
+ <:expr<
+ let $lid:buffer$ = Bitstring.Buffer.create () in
+ $fields$;
+ Bitstring.Buffer.contents $lid:buffer$
+ >> in
+
+ if !exn_used then
+ <:expr<
+ let $lid:exn$ = $construct_failure _loc "value out of range"$ in
+ $expr$
+ >>
+ else
+ expr
+
+(* Generate the code for a bitmatch statement. '_loc' is the
+ * location, 'bs' is the bitstring parameter, 'cases' are
+ * the list of cases to test against.
+ *)
+let output_bitmatch _loc bs cases =
+ (* These symbols are used through the generated code to record our
+ * current position within the bitstring:
+ *
+ * data - original bitstring data (string, never changes)
+ * off - current offset within data (int, increments as we move through
+ * the bitstring)
+ * len - current remaining length within data (int, decrements as
+ * we move through the bitstring)
+ *
+ * Also:
+ *
+ * original_off - saved offset at the start of the match (never changes)
+ * original_len - saved length at the start of the match (never changes)
+ * off_aligned - true if the original offset is byte-aligned (allows
+ * us to make some common optimizations)
+ *)
+ let data = gensym "data"
+ and off = gensym "off"
+ and len = gensym "len"
+ and original_off = gensym "original_off"
+ and original_len = gensym "original_len"
+ and off_aligned = gensym "off_aligned"
+
+ (* This is where the result will be stored (a reference). *)
+ and result = gensym "result" in
+
+ (* This generates the field extraction code for each
+ * field in a single case. There must be enough remaining data
+ * in the bitstring to satisfy the field.
+ *
+ * As we go through the fields, symbols 'data', 'off' and 'len'
+ * track our position and remaining length in the bitstring.
+ *
+ * The whole thing is a lot of nested 'if'/'match' statements.
+ * Code is generated from the inner-most (last) field outwards.
+ *)
+ let rec output_field_extraction inner = function
+ | [] -> inner
+ | field :: fields ->
+ let fpatt = P.get_patt field in
+ let flen = P.get_length field in
+ let endian = P.get_endian field in
+ let signed = P.get_signed field in
+ let t = P.get_type field in
+ let _loc = P.get_location field in
+
+ let fail = locfail _loc in
+
+ (* Is flen (field len) an integer constant? If so, what is it?
+ * This will be [Some i] if it's a constant or [None] if it's
+ * non-constant or we couldn't determine.
+ *)
+ let flen_is_const = expr_is_constant flen in
+
+ (* Surround the inner expression by check and bind clauses, so:
+ * if $check$ then
+ * let $bind...$ in
+ * $inner$
+ * where the check and bind are switched on only if they are
+ * present in the field. (In the common case when neither
+ * clause is present, expr = inner). Note the order of the
+ * check & bind is visible to the user and defined in the
+ * documentation, so it must not change.
+ *)
+ let expr = inner in
+ let expr =
+ match P.get_bind field with
+ | None -> expr
+ | Some bind_expr ->
+ <:expr< let $fpatt$ = $bind_expr$ in $expr$ >> in
+ let expr =
+ match P.get_check field with
+ | None -> expr
+ | Some check_expr ->
+ <:expr< if $check_expr$ then $expr$ >> in
+
+ (* Compute the offset of this field within the match, if it
+ * can be known at compile time.
+ *
+ * Actually, we'll compute two things: the 'natural_field_offset'
+ * is the offset assuming this field had no offset() qualifier
+ * (in other words, its position, immediately following the
+ * preceding field). 'field_offset' is the real field offset
+ * taking into account any offset() qualifier.
+ *
+ * This will be [Some i] if our current offset is known
+ * at compile time, or [None] if we can't determine it.
+ *)
+ let natural_field_offset, field_offset =
+ let has_constant_offset field =
+ match P.get_offset field with
+ | None -> false
+ | Some expr ->
+ match expr_is_constant expr with
+ | None -> false
+ | Some i -> true
+ in
+ let get_constant_offset field =
+ match P.get_offset field with
+ | None -> assert false
+ | Some expr ->
+ match expr_is_constant expr with
+ | None -> assert false
+ | Some i -> i
+ in
+
+ let has_constant_len field =
+ match expr_is_constant (P.get_length field) with
+ | None -> false
+ | Some i when i > 0 -> true
+ | Some _ -> false
+ in
+ let get_constant_len field =
+ match expr_is_constant (P.get_length field) with
+ | None -> assert false
+ | Some i when i > 0 -> i
+ | Some _ -> assert false
+ in
+
+ (* NB: We are looping over the PRECEDING fields in reverse order. *)
+ let rec loop = function
+ (* first field has constant offset 0 *)
+ | [] -> Some 0
+ (* preceding field with constant offset & length *)
+ | f :: _
+ when has_constant_offset f && has_constant_len f ->
+ Some (get_constant_offset f + get_constant_len f)
+ (* preceding field with no offset & constant length *)
+ | f :: fs
+ when P.get_offset f = None && has_constant_len f ->
+ (match loop fs with
+ | None -> None
+ | Some offset -> Some (offset + get_constant_len f))
+ (* else, can't work out the offset *)
+ | _ -> None
+ in
+
+ let natural_field_offset = loop fields in
+
+ let field_offset =
+ match P.get_offset field with
+ | None -> natural_field_offset
+ | Some expr -> (* has an offset() clause *)
+ match expr_is_constant expr with
+ | None -> None
+ | i -> i in
+
+ natural_field_offset, field_offset in
+
+ (* Also compute if the field_offset is known to be byte-aligned at
+ * compile time, which is usually both the common and best possible
+ * case for generating optimized code.
+ *
+ * This is None if not aligned / don't know.
+ * Or Some byte_offset if we can work it out.
+ *)
+ let field_offset_aligned =
+ match field_offset with
+ | None -> None (* unknown, assume no *)
+ | Some off when off land 7 = 0 -> Some (off lsr 3)
+ | Some _ -> None in (* definitely no *)
+
+ (* Now build the code which matches a single field. *)
+ let int_extract_const i endian signed =
+ build_bitstring_call _loc ExtractFunc (Some i) endian signed in
+ let int_extract endian signed =
+ build_bitstring_call _loc ExtractFunc None endian signed in
+
+ let expr =
+ match t, flen_is_const, field_offset_aligned, endian, signed with
+ (* Very common cases: int field, constant 8/16/32/64 bit
+ * length, aligned to the match at a known offset. We
+ * still have to check if the bitstring is aligned (can only
+ * be known at runtime) but we may be able to directly access
+ * the bytes in the string.
+ *)
+ | P.Int, Some 8, Some field_byte_offset, _, _ ->
+ let extract_fn = int_extract_const 8 endian signed in
+
+ (* The fast-path code when everything is aligned. *)
+ let fastpath =
+ <:expr<
+ let o =
+ ($lid:original_off$ lsr 3) + $`int:field_byte_offset$ in
+ Char.code (String.unsafe_get $lid:data$ o)
+ >> in
+
+ <:expr<
+ if $lid:len$ >= 8 then (
+ let v =
+ if $lid:off_aligned$ then
+ $fastpath$
+ else
+ $extract_fn$ $lid:data$ $lid:off$ $lid:len$ 8 in
+ let $lid:off$ = $lid:off$ + 8
+ and $lid:len$ = $lid:len$ - 8 in
+ match v with $fpatt$ when true -> $expr$ | _ -> ()
+ )
+ >>
+
+ | P.Int, Some ((16|32|64) as i),
+ Some field_byte_offset, (P.ConstantEndian _ as endian), signed ->
+ let extract_fn = int_extract_const i endian signed in
+
+ (* The fast-path code when everything is aligned. *)
+ let fastpath =
+ let fastpath_call =
+ let endian = match endian with
+ | P.ConstantEndian BigEndian -> "be"
+ | P.ConstantEndian LittleEndian -> "le"
+ | P.ConstantEndian NativeEndian -> "ne"
+ | P.EndianExpr _ -> assert false in
+ let signed = if signed then "signed" else "unsigned" in
+ let name =
+ sprintf "extract_fastpath_int%d_%s_%s" i endian signed in
+ match i with
+ | 16 ->
+ <:expr< Bitstring.$lid:name$ $lid:data$ o >>
+ | 32 ->
+ <:expr<
+ (* must allocate a new zero each time *)
+ let zero = Int32.of_int 0 in
+ Bitstring.$lid:name$ $lid:data$ o zero
+ >>
+ | 64 ->
+ <:expr<
+ (* must allocate a new zero each time *)
+ let zero = Int64.of_int 0 in
+ Bitstring.$lid:name$ $lid:data$ o zero
+ >>
+ | _ -> assert false in
+ <:expr<
+ (* Starting offset within the string. *)
+ let o =
+ ($lid:original_off$ lsr 3) + $`int:field_byte_offset$ in
+ $fastpath_call$
+ >> in
+
+ let slowpath =
+ <:expr<
+ $extract_fn$ $lid:data$ $lid:off$ $lid:len$ $`int:i$
+ >> in
+
+ <:expr<
+ if $lid:len$ >= $`int:i$ then (
+ let v =
+ if $lid:off_aligned$ then $fastpath$ else $slowpath$ in
+ let $lid:off$ = $lid:off$ + $`int:i$
+ and $lid:len$ = $lid:len$ - $`int:i$ in
+ match v with $fpatt$ when true -> $expr$ | _ -> ()
+ )
+ >>
+
+ (* Common case: int field, constant flen *)
+ | P.Int, Some i, _, _, _ when i > 0 && i <= 64 ->
+ let extract_fn = int_extract_const i endian signed in
+ let v = gensym "val" in
+ <:expr<
+ if $lid:len$ >= $`int:i$ then (
+ let $lid:v$ =
+ $extract_fn$ $lid:data$ $lid:off$ $lid:len$ $`int:i$ in
+ let $lid:off$ = $lid:off$ + $`int:i$
+ and $lid:len$ = $lid:len$ - $`int:i$ in
+ match $lid:v$ with $fpatt$ when true -> $expr$ | _ -> ()
+ )
+ >>
+
+ | P.Int, Some _, _, _, _ ->
+ fail "length of int field must be [1..64]"
+
+ (* Int field, non-const flen. We have to test the range of
+ * the field at runtime. If outside the range it's a no-match
+ * (not an error).
+ *)
+ | P.Int, None, _, _, _ ->
+ let extract_fn = int_extract endian signed in
+ let v = gensym "val" in
+ <:expr<
+ if $flen$ >= 1 && $flen$ <= 64 && $flen$ <= $lid:len$ then (
+ let $lid:v$ =
+ $extract_fn$ $lid:data$ $lid:off$ $lid:len$ $flen$ in
+ let $lid:off$ = $lid:off$ + $flen$
+ and $lid:len$ = $lid:len$ - $flen$ in
+ match $lid:v$ with $fpatt$ when true -> $expr$ | _ -> ()
+ )
+ >>
+
+ (* String, constant flen > 0.
+ * The field is at a known byte-aligned offset so we may
+ * be able to optimize the substring extraction.
+ *)
+ | P.String, Some i, Some field_byte_offset, _, _
+ when i > 0 && i land 7 = 0 ->
+ let fastpath =
+ <:expr<
+ (* Starting offset within the string. *)
+ let o =
+ ($lid:original_off$ lsr 3) + $`int:field_byte_offset$ in
+ String.sub $lid:data$ o $`int:(i lsr 3)$
+ >> in
+
+ let slowpath =
+ <:expr<
+ Bitstring.string_of_bitstring
+ ($lid:data$, $lid:off$, $`int:i$)
+ >> in
+
+ let cond =
+ <:expr<
+ if $lid:off_aligned$ then $fastpath$ else $slowpath$
+ >> in
+
+ <:expr<
+ if $lid:len$ >= $`int:i$ then (
+ let str = $cond$ in
+ let $lid:off$ = $lid:off$ + $`int:i$
+ and $lid:len$ = $lid:len$ - $`int:i$ in
+ match str with
+ | $fpatt$ when true -> $expr$
+ | _ -> ()
+ )
+ >>
+
+ (* String, constant flen > 0. *)
+ | P.String, Some i, None, _, _ when i > 0 && i land 7 = 0 ->
+ <:expr<
+ if $lid:len$ >= $`int:i$ then (
+ let str =
+ Bitstring.string_of_bitstring
+ ($lid:data$, $lid:off$, $`int:i$) in
+ let $lid:off$ = $lid:off$ + $`int:i$
+ and $lid:len$ = $lid:len$ - $`int:i$ in
+ match str with
+ | $fpatt$ when true -> $expr$
+ | _ -> ()
+ )
+ >>
+
+ (* String, constant flen = -1, means consume all the
+ * rest of the input.
+ * XXX It should be possible to optimize this for known byte
+ * offset, but the optimization is tricky because the end/length
+ * of the string may not be byte-aligned.
+ *)
+ | P.String, Some i, _, _, _ when i = -1 ->
+ let str = gensym "str" in
+
+ <:expr<
+ let $lid:str$ =
+ Bitstring.string_of_bitstring
+ ($lid:data$, $lid:off$, $lid:len$) in
+ let $lid:off$ = $lid:off$ + $lid:len$ in
+ let $lid:len$ = 0 in
+ match $lid:str$ with
+ | $fpatt$ when true -> $expr$
+ | _ -> ()
+ >>
+
+ | P.String, Some _, _, _, _ ->
+ fail "length of string must be > 0 and a multiple of 8, or the
special value -1"
+
+ (* String field, non-const flen. We check the flen is > 0
+ * and a multiple of 8 (-1 is not allowed here), at runtime.
+ *)
+ | P.String, None, _, _, _ ->
+ let bs = gensym "bs" in
+ <:expr<
+ if $flen$ >= 0 && $flen$ <= $lid:len$
+ && $flen$ land 7 = 0 then (
+ let $lid:bs$ = ($lid:data$, $lid:off$, $flen$) in
+ let $lid:off$ = $lid:off$ + $flen$
+ and $lid:len$ = $lid:len$ - $flen$ in
+ match Bitstring.string_of_bitstring $lid:bs$ with
+ | $fpatt$ when true -> $expr$
+ | _ -> ()
+ )
+ >>
+
+ (* Bitstring, constant flen >= 0.
+ * At the moment all we can do is assign the bitstring to an
+ * identifier.
+ *)
+ | P.Bitstring, Some i, _, _, _ when i >= 0 ->
+ let ident =
+ match fpatt with
+ | <:patt< $lid:ident$ >> -> ident
+ | <:patt< _ >> -> "_"
+ | _ ->
+ fail "cannot compare a bitstring to a constant" in
+ <:expr<
+ if $lid:len$ >= $`int:i$ then (
+ let $lid:ident$ = ($lid:data$, $lid:off$, $`int:i$) in
+ let $lid:off$ = $lid:off$ + $`int:i$
+ and $lid:len$ = $lid:len$ - $`int:i$ in
+ $expr$
+ )
+ >>
+
+ (* Bitstring, constant flen = -1, means consume all the
+ * rest of the input.
+ *)
+ | P.Bitstring, Some i, _, _, _ when i = -1 ->
+ let ident =
+ match fpatt with
+ | <:patt< $lid:ident$ >> -> ident
+ | <:patt< _ >> -> "_"
+ | _ ->
+ fail "cannot compare a bitstring to a constant" in
+ <:expr<
+ let $lid:ident$ = ($lid:data$, $lid:off$, $lid:len$) in
+ let $lid:off$ = $lid:off$ + $lid:len$ in
+ let $lid:len$ = 0 in
+ $expr$
+ >>
+
+ | P.Bitstring, Some _, _, _, _ ->
+ fail "length of bitstring must be >= 0 or the special value -1"
+
+ (* Bitstring field, non-const flen. We check the flen is >= 0
+ * (-1 is not allowed here) at runtime.
+ *)
+ | P.Bitstring, None, _, _, _ ->
+ let ident =
+ match fpatt with
+ | <:patt< $lid:ident$ >> -> ident
+ | <:patt< _ >> -> "_"
+ | _ ->
+ fail "cannot compare a bitstring to a constant" in
+ <:expr<
+ if $flen$ >= 0 && $flen$ <= $lid:len$ then (
+ let $lid:ident$ = ($lid:data$, $lid:off$, $flen$) in
+ let $lid:off$ = $lid:off$ + $flen$
+ and $lid:len$ = $lid:len$ - $flen$ in
+ $expr$
+ )
+ >>
+ in
+
+ (* Computed offset: only offsets forward are supported.
+ *
+ * We try hard to optimize this based on what we know. Are
+ * we at a predictable offset now? (Look at the outer 'fields'
+ * list and see if they all have constant field length starting
+ * at some constant offset). Is this offset constant?
+ *
+ * Based on this we can do a lot of the computation at
+ * compile time, or defer it to runtime only if necessary.
+ *
+ * In all cases, the off and len fields get updated.
+ *)
+ let expr =
+ match P.get_offset field with
+ | None -> expr (* common case: there was no offset expression *)
+ | Some offset_expr ->
+ (* This will be [Some i] if offset is a constant expression
+ * or [None] if it's a non-constant.
+ *)
+ let requested_offset = expr_is_constant offset_expr in
+
+ (* Look at the field offset (if known) and requested offset
+ * cases and determine what code to generate.
+ *)
+ match natural_field_offset, requested_offset with
+ (* This is the good case: both the field offset and
+ * the requested offset are constant, so we can remove
+ * almost all the runtime checks.
+ *)
+ | Some natural_field_offset, Some requested_offset ->
+ let move = requested_offset - natural_field_offset in
+ if move < 0 then
+ fail (sprintf "requested offset is less than the field
offset (%d < %d)" requested_offset natural_field_offset);
+ (* Add some code to move the offset and length by a
+ * constant amount, and a runtime test that len >= 0
+ * (XXX possibly the runtime test is unnecessary?)
+ *)
+ <:expr<
+ let $lid:off$ = $lid:off$ + $`int:move$ in
+ let $lid:len$ = $lid:len$ - $`int:move$ in
+ if $lid:len$ >= 0 then $expr$
+ >>
+ (* In any other case, we need to use runtime checks.
+ *
+ * XXX It's not clear if a backwards move detected at runtime
+ * is merely a match failure, or a runtime error. At the
+ * moment it's just a match failure since bitmatch generally
+ * doesn't raise runtime errors.
+ *)
+ | _ ->
+ let move = gensym "move" in
+ <:expr<
+ let $lid:move$ =
+ $offset_expr$ - ($lid:off$ - $lid:original_off$) in
+ if $lid:move$ >= 0 then (
+ let $lid:off$ = $lid:off$ + $lid:move$ in
+ let $lid:len$ = $lid:len$ - $lid:move$ in
+ if $lid:len$ >= 0 then $expr$
+ )
+ >> in (* end of computed offset code *)
+
+ (* save_offset_to(patt) saves the current offset into a variable. *)
+ let expr =
+ match P.get_save_offset_to field with
+ | None -> expr (* no save_offset_to *)
+ | Some patt ->
+ <:expr<
+ let $patt$ = $lid:off$ - $lid:original_off$ in
+ $expr$
+ >> in
+
+ (* Emit extra debugging code. *)
+ let expr =
+ if not debug then expr else (
+ let field = P.string_of_pattern_field field in
+
+ <:expr<
+ if !Bitstring.debug then (
+ Printf.eprintf "PA_BITSTRING: TEST:\n";
+ Printf.eprintf " %s\n" $str:field$;
+ Printf.eprintf " off %d len %d\n%!" $lid:off$ $lid:len$;
+ (*Bitstring.hexdump_bitstring stderr
+ ($lid:data$,$lid:off$,$lid:len$);*)
+ );
+ $expr$
+ >>
+ ) in
+
+ output_field_extraction expr fields
+ in
+
+ (* Convert each case in the match. *)
+ let cases = List.map (
+ fun (fields, bind, whenclause, code) ->
+ let inner = <:expr< $lid:result$ := Some ($code$); raise Exit >> in
+ let inner =
+ match whenclause with
+ | Some whenclause ->
+ <:expr< if $whenclause$ then $inner$ >>
+ | None -> inner in
+ let inner =
+ match bind with
+ | Some name ->
+ <:expr<
+ let $lid:name$ = ($lid:data$, $lid:off$, $lid:len$) in
+ $inner$
+ >>
+ | None -> inner in
+ output_field_extraction inner (List.rev fields)
+ ) cases in
+
+ (* Join them into a single expression.
+ *
+ * Don't do it with a normal fold_right because that leaves
+ * 'raise Exit; ()' at the end which causes a compiler warning.
+ * Hence a bit of complexity here.
+ *
+ * Note that the number of cases is always >= 1 so List.hd is safe.
+ *)
+ let cases = List.rev cases in
+ let cases =
+ List.fold_left (fun base case -> <:expr< $case$ ; $base$ >>)
+ (List.hd cases) (List.tl cases) in
+
+ (* The final code just wraps the list of cases in a
+ * try/with construct so that each case is tried in
+ * turn until one case matches (that case sets 'result'
+ * and raises 'Exit' to leave the whole statement).
+ * If result isn't set by the end then we will raise
+ * Match_failure with the location of the bitmatch
+ * statement in the original code.
+ *)
+ let loc_fname = Loc.file_name _loc in
+ let loc_line = string_of_int (Loc.start_line _loc) in
+ let loc_char = string_of_int (Loc.start_off _loc - Loc.start_bol _loc) in
+
+ <:expr<
+ (* Note we save the original offset/length at the start of the match
+ * in 'original_off'/'original_len' symbols. 'data' never changes.
+ * This code also ensures that if original_off/original_len/off_aligned
+ * aren't actually used, we don't get a warning.
+ *)
+ let ($lid:data$, $lid:original_off$, $lid:original_len$) = $bs$ in
+ let $lid:off$ = $lid:original_off$ and $lid:len$ = $lid:original_len$ in
+ let $lid:off_aligned$ = $lid:off$ land 7 = 0 in
+ ignore $lid:off_aligned$;
+ let $lid:result$ = ref None in
+ (try
+ $cases$
+ with Exit -> ());
+ match ! $lid:result$ with
+ | Some x -> x
+ | None -> raise (Match_failure ($str:loc_fname$,
+ $int:loc_line$, $int:loc_char$))
+ >>
+
+(* Add a named pattern. *)
+let add_named_pattern _loc name pattern =
+ Hashtbl.add pattern_hash name pattern
+
+(* Expand a named pattern from the pattern_hash. *)
+let expand_named_pattern _loc name =
+ try Hashtbl.find pattern_hash name
+ with Not_found ->
+ locfail _loc (sprintf "named pattern not found: %s" name)
+
+(* Add named patterns from a file. See the documentation on the
+ * directory search path in bitstring_persistent.mli
+let load_patterns_from_file _loc filename =
+ let chan =
+ if Filename.is_relative filename && Filename.is_implicit filename then (
+ (* Try current directory. *)
+ try open_in filename
+ with _ ->
+ (* Try OCaml library directory. *)
+ try open_in (Filename.concat Bitstring_config.ocamllibdir filename)
+ with exn -> Loc.raise _loc exn
+ ) else (
+ try open_in filename
+ with exn -> Loc.raise _loc exn
+ ) in
+ let names = ref [] in
+ (try
+ let rec loop () =
+ let name = P.named_from_channel chan in
+ names := name :: !names
+ in
+ loop ()
+ with End_of_file -> ()
+ );
+ close_in chan;
+ let names = List.rev !names in
+ List.iter (
+ function
+ | name, P.Pattern patt ->
+ if patt = [] then
+ locfail _loc (sprintf "pattern %s: no fields" name);
+ add_named_pattern _loc name patt
+ | _, P.Constructor _ -> () (* just ignore these for now *)
+ ) names
+ *)
+
+EXTEND Gram
+ GLOBAL: expr str_item;
+
+ (* Qualifiers are a list of identifiers ("string", "bigendian", etc.)
+ * followed by an optional expression (used in certain cases). Note
+ * that we are careful not to declare any explicit reserved words.
+ *)
+ qualifiers: [
+ [ LIST0
+ [ q = LIDENT;
+ e = OPT [ "("; e = expr; ")" -> e ] -> (q, e) ]
+ SEP "," ]
+ ];
+
+ (* Field used in the bitmatch operator (a pattern). This can actually
+ * return multiple fields, in the case where the 'field' is a named
+ * persitent pattern.
+ *)
+ patt_field: [
+ [ fpatt = patt; ":"; len = expr LEVEL "top";
+ qs = OPT [ ":"; qs = qualifiers -> qs ] ->
+ let field = P.create_pattern_field _loc in
+ let field = P.set_patt field fpatt in
+ let field = P.set_length field len in
+ [parse_field _loc field qs] (* Normal, single field. *)
+ | ":"; name = LIDENT ->
+ expand_named_pattern _loc name (* Named -> list of fields. *)
+ ]
+ ];
+
+ (* Case inside bitmatch operator. *)
+ patt_fields: [
+ [ "{";
+ fields = LIST0 patt_field SEP ";";
+ "}" ->
+ List.concat fields
+ ]
+ ];
+
+ patt_case: [
+ [ fields = patt_fields;
+ bind = OPT [ "as"; name = LIDENT -> name ];
+ whenclause = OPT [ "when"; e = expr -> e ]; "->";
+ code = expr ->
+ (fields, bind, whenclause, code)
+ ]
+ ];
+
+ (* Field used in the BITSTRING constructor (an expression). *)
+ constr_field: [
+ [ fexpr = expr LEVEL "top"; ":"; len = expr LEVEL "top";
+ qs = OPT [ ":"; qs = qualifiers -> qs ] ->
+ let field = P.create_constructor_field _loc in
+ let field = P.set_expr field fexpr in
+ let field = P.set_length field len in
+ parse_field _loc field qs
+ ]
+ ];
+
+ constr_fields: [
+ [ "{";
+ fields = LIST0 constr_field SEP ";";
+ "}" ->
+ fields
+ ]
+ ];
+
+ (* 'bitmatch' expressions. *)
+ expr: LEVEL ";" [
+ [ "bitmatch";
+ bs = expr; "with"; OPT "|";
+ cases = LIST1 patt_case SEP "|" ->
+ output_bitmatch _loc bs cases
+ ]
+
+ (* Constructor. *)
+ | [ "BITSTRING";
+ fields = constr_fields ->
+ output_constructor _loc fields
+ ]
+ ];
+
+ (* Named persistent patterns.
+ *
+ * NB: Currently only allowed at the top level. We can probably lift
+ * this restriction later if necessary. We only deal with patterns
+ * at the moment, not constructors, but the infrastructure to do
+ * constructors is in place.
+ *)
+ str_item: LEVEL "top" [
+ [ "let"; "bitmatch";
+ name = LIDENT; "="; fields = patt_fields ->
+ add_named_pattern _loc name fields;
+ (* The statement disappears, but we still need a str_item so ... *)
+ <:str_item< >>
+ (*
+ | "open"; "bitmatch"; filename = STRING ->
+ load_patterns_from_file _loc filename;
+ <:str_item< >>
+ *)
+ ]
+ ];
+
+END
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Mldonkey-commits] mldonkey config/Makefile.in config/configure.in...,
mldonkey-commits <=