2020-03-04 15:31:05 +01:00
|
|
|
|
|
|
|
|
(** {1 Hexadecimal encoding and decoding of a string}
|
|
|
|
|
|
|
|
|
|
Implementation of functions to encode and decode strings into
|
|
|
|
|
hexadecimal.
|
|
|
|
|
|
|
|
|
|
The length of the encoded string is always twice the length of the
|
|
|
|
|
input string.
|
|
|
|
|
*)
|
|
|
|
|
|
2020-02-12 15:17:22 +01:00
|
|
|
use mach.int.Int
|
|
|
|
|
use mach.int.Int63
|
2020-02-20 15:28:46 +01:00
|
|
|
use string.String
|
|
|
|
|
use string.Char
|
2020-02-12 15:17:22 +01:00
|
|
|
use string.OCaml
|
2020-02-20 15:28:46 +01:00
|
|
|
use string.StringBuffer
|
2020-02-12 15:17:22 +01:00
|
|
|
|
2020-03-04 15:31:05 +01:00
|
|
|
(** `valid_hex_char c` is true, if and only if, `c` is a valid
|
|
|
|
|
hexadecimal character *)
|
2020-02-24 16:16:39 +01:00
|
|
|
predicate valid_hex_char (c: char) =
|
|
|
|
|
48 <= code c < 58 || 65 <= code c < 71
|
|
|
|
|
|
2020-03-04 15:31:05 +01:00
|
|
|
(** `hex i` gives the `i`th hexadecimal value, for `0 <= i < 16` or
|
|
|
|
|
`0` otherwise. *)
|
2020-02-12 15:17:22 +01:00
|
|
|
function hex (i: int) : char =
|
|
|
|
|
if 0 <= i < 10 then chr (i + 48)
|
|
|
|
|
else if 10 <= i < 16 then chr (i + 55)
|
|
|
|
|
else "\000"[0]
|
|
|
|
|
|
|
|
|
|
let hex (i: int63) : char
|
|
|
|
|
requires { 0 <= i < 16 }
|
|
|
|
|
ensures { result = hex i }
|
|
|
|
|
= if i < 10 then chr (i + 48) else chr (i + 55)
|
|
|
|
|
|
2020-03-04 15:31:05 +01:00
|
|
|
(** `xeh c` gives the index of the hexadecimal value `c`, if `c` is a
|
|
|
|
|
valid hexadecimal value or `-1` otherwise *)
|
2020-02-24 16:16:39 +01:00
|
|
|
function xeh (c: char) : int =
|
|
|
|
|
if 48 <= code c < 58 then code c - 48
|
|
|
|
|
else if 65 <= code c < 71 then code c - 55
|
2020-02-12 15:17:22 +01:00
|
|
|
else -1
|
|
|
|
|
|
2020-02-24 16:16:39 +01:00
|
|
|
let xeh (c: char) : int63
|
|
|
|
|
requires { valid_hex_char c }
|
|
|
|
|
ensures { result = xeh c }
|
|
|
|
|
= if 48 <= code c < 58 then code c - 48
|
|
|
|
|
else code c - 55
|
2020-02-12 15:17:22 +01:00
|
|
|
|
2020-03-04 15:31:05 +01:00
|
|
|
(** checks whether a string contains only valid hexadecimal characters *)
|
2020-02-24 16:16:39 +01:00
|
|
|
predicate valid_hex (s : string) =
|
|
|
|
|
mod (length s) 2 = 0 &&
|
|
|
|
|
( forall i. 0 <= i < length s -> valid_hex_char s[i] )
|
|
|
|
|
|
2020-03-04 15:31:05 +01:00
|
|
|
(** `encoding s1 s2` is true, if and only if, `s2` is an encoding of
|
|
|
|
|
`s1` *)
|
2020-02-24 16:16:39 +01:00
|
|
|
predicate encoding (s1 s2: string) =
|
|
|
|
|
length s2 = 2 * length s1 &&
|
|
|
|
|
(forall i. 0 <= i < length s1 ->
|
|
|
|
|
s1[i] = chr (xeh s2[2 * i] * 16 + xeh s2[2 * i + 1])) &&
|
|
|
|
|
valid_hex s2
|
|
|
|
|
|
2020-03-04 15:31:05 +01:00
|
|
|
(** the encoding of a string is unique *)
|
2020-02-24 16:16:39 +01:00
|
|
|
lemma decode_unique: forall s1 s2 s3.
|
|
|
|
|
encoding s1 s2 /\ encoding s3 s2 -> s1 = s3
|
|
|
|
|
|
2024-10-31 15:27:09 +01:00
|
|
|
let partial encode (s: string) : string
|
2020-02-24 16:16:39 +01:00
|
|
|
ensures { encoding s result }
|
2020-02-12 15:17:22 +01:00
|
|
|
= let ref i = 0 in
|
2020-02-24 16:16:39 +01:00
|
|
|
let r = StringBuffer.create (length s) in
|
2020-02-12 15:17:22 +01:00
|
|
|
while i < OCaml.length s do
|
|
|
|
|
variant { length s - i }
|
2020-02-20 15:28:46 +01:00
|
|
|
invariant { 0 <= i <= length s }
|
2020-02-12 15:17:22 +01:00
|
|
|
invariant { length r = 2 * i }
|
|
|
|
|
invariant { forall j. 0 <= j < i ->
|
|
|
|
|
r[2 * j] = hex (div (code s[j]) 16) &&
|
|
|
|
|
r[2 * j + 1] = hex (mod (code s[j]) 16)
|
|
|
|
|
}
|
2020-02-24 16:16:39 +01:00
|
|
|
invariant { forall j. 0 <= j < 2*i -> valid_hex_char r[j]}
|
2020-02-12 15:17:22 +01:00
|
|
|
let v = code s[i] in
|
2020-02-20 15:28:46 +01:00
|
|
|
StringBuffer.add_char r (hex (v / 16));
|
|
|
|
|
StringBuffer.add_char r (hex (v % 16));
|
2020-02-12 15:17:22 +01:00
|
|
|
i <- i + 1
|
|
|
|
|
done;
|
2020-02-20 15:28:46 +01:00
|
|
|
StringBuffer.contents r
|
2020-02-12 15:17:22 +01:00
|
|
|
|
2024-10-31 15:27:09 +01:00
|
|
|
let partial decode (s: string) : string
|
2020-02-24 16:16:39 +01:00
|
|
|
requires { valid_hex s }
|
|
|
|
|
ensures { encoding result s }
|
2020-02-12 15:17:22 +01:00
|
|
|
= let ref i = 0 in
|
2020-02-20 15:28:46 +01:00
|
|
|
let r = StringBuffer.create (length s / 2) in
|
2020-02-12 15:17:22 +01:00
|
|
|
while i < length s do
|
|
|
|
|
variant {length s - i}
|
|
|
|
|
invariant { mod i 2 = 0 }
|
|
|
|
|
invariant { 0 <= i <= length s }
|
|
|
|
|
invariant { length r = div i 2 }
|
|
|
|
|
invariant { forall j. 0 <= j < div i 2 ->
|
2020-02-24 16:16:39 +01:00
|
|
|
r[j] = chr (xeh s[2 * j] * 16 + xeh s[2 * j + 1]) }
|
|
|
|
|
let v_i = xeh s[i] in
|
|
|
|
|
let v_ii = xeh s[i + 1] in
|
2020-02-20 15:28:46 +01:00
|
|
|
StringBuffer.add_char r (chr (v_i * 16 + v_ii));
|
2020-02-12 15:17:22 +01:00
|
|
|
i <- i + 2
|
|
|
|
|
done;
|
2020-02-20 15:28:46 +01:00
|
|
|
StringBuffer.contents r
|
2020-02-12 15:17:22 +01:00
|
|
|
|
2024-10-31 15:27:09 +01:00
|
|
|
let partial decode_encode (s: string) : unit
|
2020-02-12 15:17:22 +01:00
|
|
|
= let s1 = encode s in
|
|
|
|
|
let s2 = decode s1 in
|
2024-10-31 15:27:09 +01:00
|
|
|
assert { s = s2 }
|