Skip to content

Commit 4f89474

Browse files
gaschergrinberg
authored andcommitted
cohttp.headers: faster faster comparison
see mirage#778 (comment)
1 parent 8b835ae commit 4f89474

File tree

1 file changed

+37
-9
lines changed

1 file changed

+37
-9
lines changed

http/src/http.ml

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,49 @@ module Transfer = struct
55
end
66

77
module Header = struct
8+
(* [caseless_equal a b] must be equivalent to
9+
[String.equal (String.lowercase_ascii a) (String.lowercase_ascii b)]. *)
810
let caseless_equal a b =
911
if a == b then true
1012
else
1113
let len = String.length a in
1214
len = String.length b
15+
(* Note: at this point we konw that [a] and [b] have the same length. *)
1316
&&
14-
let stop = ref false in
15-
let idx = ref 0 in
16-
while (not !stop) && !idx < len do
17-
let c1 = String.unsafe_get a !idx in
18-
let c2 = String.unsafe_get b !idx in
19-
if Char.lowercase_ascii c1 <> Char.lowercase_ascii c2 then stop := true;
20-
incr idx
21-
done;
22-
not !stop
17+
(* [word_loop a b i len] compares strings [a] and [b] from
18+
offsets [i] (included) to [len] (excluded), one word at a time.
19+
[i] is a world-aligned index into the strings.
20+
*)
21+
let rec word_loop a b i len =
22+
if i = len then true
23+
else
24+
let i' = i + 8 in
25+
(* If [i' > len], what remains to be compared is strictly
26+
less than a word long, use byte-per-byte comparison. *)
27+
if i' > len then byte_loop a b i len
28+
else if String.get_int64_ne a i = String.get_int64_ne b i then
29+
word_loop a b i' len
30+
else
31+
(* If the words at [i] differ, it may due to a case
32+
difference; we check the individual bytes of this
33+
work, and then we continue checking the other
34+
words. *)
35+
byte_loop a b i i' && word_loop a b i' len
36+
(* [byte_loop a b i len] compares the strings [a] and [b] from
37+
offsets [i] (included) to [len] (excluded), one byte at
38+
a time.
39+
40+
This function assumes that [i < len] holds -- its only called
41+
by [word_loop] when this is known to hold. *)
42+
and byte_loop a b i len =
43+
let c1 = String.unsafe_get a i in
44+
let c2 = String.unsafe_get b i in
45+
Char.lowercase_ascii c1 = Char.lowercase_ascii c2
46+
&&
47+
let i' = i + 1 in
48+
i' = len || byte_loop a b i' len
49+
in
50+
word_loop a b 0 len
2351

2452
type t = (string * string) list
2553

0 commit comments

Comments
 (0)