~cypheon/ocaml-apfs

ref: 61b44ed33d0b50533d003402e5de4e07aa8bafc6 ocaml-apfs/lib/util.ml -rw-r--r-- 1.3 KiB
61b44ed3 — Johann Rudloff Do simple (ASCII-only) case folding when calculating file name hashes. 1 year, 9 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
let ascii_lowercase c =
  if Uchar.is_char c then
    c |> Uchar.to_char |> Char.lowercase_ascii |> Uchar.of_char
  else c
let name_hash str =
  let len = ref 0 in
  let buf = Cstruct.create_unsafe (4 * (1 +String.length str)) in
  let dec = Uutf.(decoder ~encoding:`UTF_8 (`String str)) in
  (* TODO: normalize to NFD *)
  let rec loop d = match Uutf.decode d with
  (* TODO: proper Unicode-aware case folding *)
  | `Uchar uc -> let lowercased = ascii_lowercase uc in
                 (Cstruct.LE.set_uint32 buf (4 * !len) (Int32.of_int (Uchar.to_int lowercased)));
                 (*Printf.printf "%d: %c (0x%x)\n" (!len) (Uchar.to_char uc) (Uchar.to_int uc);*)
                 len := (!len + 1);
                 loop d
  | `End -> !len
  | `Await -> assert false
  | `Malformed _ -> assert false
  in
  let charcount = loop dec in
  let buf = Cstruct.sub buf 0 (4 * charcount) in
  let crc32c = Checkseum.Crc32c.unsafe_digest_bigstring (Cstruct.to_bigarray buf) 0 (Cstruct.len buf) Checkseum.Crc32c.default in
  Int32.lognot (Optint.to_int32 crc32c)

let name_len_and_hash str =
  let hash = Int32.logand 0x3fffffl (name_hash str) in
  let len = 0x3ff land (String.length str) |> Int32.of_int in
  let len = Int32.(add len 1l) in (* len includes the final NUL byte *)
  Int32.(logor (shift_left hash 10) len)