package fluxt
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Extensions of flux to provide basic streams (compression, hash, format)
Install
dune-project
Dependency
Authors
Maintainers
Sources
flux-0.0.1.beta6.tbz
sha256=a73ac59332c252a7ec2bf1b6cf59f1f03e0a2bbd8fa93a059c31a1263527b759
sha512=c020ad017f3606b430bb717d45615baa4b875c1b3aee5945ac84a65d85b5c01ffad198bf7d3c3c65c864bfc53d702d474b82d5655eb4e926dd03bb10e55f1d3b
doc/src/fluxt.unzip/flux_unzip.ml.html
Source file flux_unzip.ml
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202type entry = { filepath: string ; mtime: Ptime.t ; meth: [ `Stored | `Deflated ] ; crc32: Checkseum.Crc32.t ; csz: int64 ; usz: int64 ; offset: int64 } type t = { filename: string; entries: entry list } let ( let* ) = Result.bind let error_msgf fmt = Fmt.kstr (fun msg -> Error (`Msg msg)) fmt let entries { entries; _ } = entries let really_read ic ~pos len = In_channel.seek ic pos; match In_channel.really_input_string ic len with | Some str -> Ok str | None -> error_msgf "Flux_unzip: truncated archive" let to_ptime ~time ~date = let ss = time land 0x1f * 2 and mm = (time lsr 5) land 0x3f and hh = (time lsr 11) land 0x1f in let d = date land 0x1f and m = (date lsr 5) land 0xf and y = 1980 + (date lsr 9) in match Ptime.of_date_time ((y, m, d), ((hh, mm, ss), 0)) with | Some t -> t | None -> Ptime.epoch let u32_to_i64 value = Int64.logand (Int64.of_int32 value) 0xffffffffL let find_eocd ic = let len = In_channel.length ic in let max_scan = Int64.of_int (22 + 0xffff) in let scan = if Int64.compare len max_scan < 0 then len else max_scan in let pos = Int64.sub len scan in let* tail = really_read ic ~pos (Int64.to_int scan) in let rec go idx = if idx < 0 then error_msgf "Flux_unzip: no end of central directory" else if String.get_int32_le tail idx = 0x06054b50l && idx + 22 + String.get_uint16_le tail (idx + 20) = String.length tail then Ok (Int64.add pos (Int64.of_int idx), String.sub tail idx 22) else go (idx - 1) in go (String.length tail - 22) let find_central_directory ic = let* eocd_pos, eocd = find_eocd ic in let entries = String.get_uint16_le eocd 10 in let cd_size = String.get_int32_le eocd 12 in let cd_off = String.get_int32_le eocd 16 in if entries <> 0xffff && cd_size <> 0xffffffffl && cd_off <> 0xffffffffl then Ok (Int64.of_int entries, u32_to_i64 cd_off) else if Int64.compare eocd_pos 20L < 0 then error_msgf "Flux_unzip: missing zip64 locator" else let* locator = really_read ic ~pos:(Int64.sub eocd_pos 20L) 20 in if String.get_int32_le locator 0 <> 0x07064b50l then error_msgf "Flux_unzip: missing zip64 locator" else let eocd64_pos = String.get_int64_le locator 8 in let* eocd64 = really_read ic ~pos:eocd64_pos 56 in if String.get_int32_le eocd64 0 <> 0x06064b50l then error_msgf "Flux_unzip: malformed zip64 end of central directory" else let entries = String.get_int64_le eocd64 32 in let cd_off = String.get_int64_le eocd64 48 in Ok (entries, cd_off) let parse_extra ~usz ~csz ~offset extra = let rec go idx usz csz offset = if idx + 4 > String.length extra then (usz, csz, offset) else begin let id = String.get_uint16_le extra idx in let sz = String.get_uint16_le extra (idx + 2) in if id = 0x0001 && idx + 4 + sz <= String.length extra then begin let jdx = ref (idx + 4) in let take () = let value = String.get_int64_le extra !jdx in jdx := !jdx + 8; value in let usz = if usz = 0xffffffffL then take () else usz in let csz = if csz = 0xffffffffL then take () else csz in let offset = if offset = 0xffffffffL then take () else offset in go (idx + 4 + sz) usz csz offset end else go (idx + 4 + sz) usz csz offset end in go 0 usz csz offset let parse_entry ic pos = let* cdfh = really_read ic ~pos 46 in if String.get_int32_le cdfh 0 <> 0x02014b50l then error_msgf "Flux_unzip: malformed central directory" else if String.get_uint16_le cdfh 8 land 0x1 <> 0 then error_msgf "Flux_unzip: encrypted members are not supported" else let* meth = match String.get_uint16_le cdfh 10 with | 0 -> Ok `Stored | 8 -> Ok `Deflated | meth -> error_msgf "Flux_unzip: unsupported compression method %d" meth in let time = String.get_uint16_le cdfh 12 and date = String.get_uint16_le cdfh 14 in let crc32 = Optint.of_unsigned_int32 (String.get_int32_le cdfh 16) in let csz = u32_to_i64 (String.get_int32_le cdfh 20) in let usz = u32_to_i64 (String.get_int32_le cdfh 24) in let name_len = String.get_uint16_le cdfh 28 and extra_len = String.get_uint16_le cdfh 30 and comment_len = String.get_uint16_le cdfh 32 in let offset = u32_to_i64 (String.get_int32_le cdfh 42) in let* filepath = really_read ic ~pos:(Int64.add pos 46L) name_len in let* extra = really_read ic ~pos:(Int64.add pos (Int64.of_int (46 + name_len))) extra_len in let usz, csz, offset = parse_extra ~usz ~csz ~offset extra in let mtime = to_ptime ~time ~date in let next = Int64.add pos (Int64.of_int (46 + name_len + extra_len + comment_len)) in Ok ({ filepath; mtime; meth; crc32; csz; usz; offset }, next) let of_filename filename = let process ic = let* entries, cd_off = find_central_directory ic in let rec go acc pos = function | 0L -> Ok (List.rev acc) | n -> let* entry, next = parse_entry ic pos in go (entry :: acc) next (Int64.pred n) in let* entries = go [] cd_off entries in Ok { filename; entries } in match In_channel.open_bin filename with | ic -> Fun.protect ~finally:(fun () -> In_channel.close ic) @@ fun () -> process ic | exception Sys_error err -> error_msgf "Flux_unzip: %s" err let raw ~filename ~pos ~len : string Flux.source = let init () = let ic = In_channel.open_bin filename in match really_read ic ~pos 30 with | Error (`Msg msg) -> In_channel.close ic; failwith msg | Ok lfh -> if String.get_int32_le lfh 0 <> 0x04034b50l then begin In_channel.close ic; failwith "Flux_unzip: malformed local file header" end; let name_len = String.get_uint16_le lfh 26 and extra_len = String.get_uint16_le lfh 28 in let data = Int64.add pos (Int64.of_int (30 + name_len + extra_len)) in In_channel.seek ic data; (ic, len) and pull (ic, rem) = if Int64.compare rem 0L <= 0 then None else let len = if Int64.compare rem 0x7ffL < 0 then Int64.to_int rem else 0x7ff in match In_channel.really_input_string ic len with | Some str -> Some (str, (ic, Int64.sub rem (Int64.of_int len))) | None -> failwith "Flux_unzip: truncated archive" and stop (ic, _) = In_channel.close ic in Flux.Source { init; pull; stop } let stream t entry = let raw = raw ~filename:t.filename ~pos:entry.offset ~len:entry.csz in let via = match entry.meth with | `Stored -> Flux.Flow.identity | `Deflated -> Flux.Flow.(bstr ~len:0x7ff << Flux_de.inflate) in let stream (Flux.Sink k) = let crc32 = ref Checkseum.Crc32.default in let seen = ref 0L in let push acc str = let len = String.length str in crc32 := Checkseum.Crc32.digest_string str 0 len !crc32; seen := Int64.add !seen (Int64.of_int len); k.push acc str and stop acc = if Int64.equal !seen entry.usz && not (Optint.equal !crc32 entry.crc32) then Fmt.failwith "Flux_unzip: CRC-32 mismatch for %s" entry.filepath; k.stop acc in let into = Flux.Sink { init= k.init; push; full= k.full; stop } in let value, leftover = Flux.Stream.run ~from:raw ~via ~into in Option.iter Flux.Source.dispose leftover; value in { Flux.stream }
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>