package yuscii
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file yuscii.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 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267(* XXX(dinosaure): IO_BUFFER_SIZE *) let io_buffer_size = 65536 let invalid_arg fmt = Format.ksprintf (fun s -> invalid_arg s) fmt let invalid_bounds off len = invalid_arg "Invalid bounds (off: %d, len: %d)" off len let pp = Format.fprintf let b64d v = let inv = [| -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; -1; 0x3e; -1; -1; -1; 0x3f; 0x34; 0x35; 0x36; 0x37; 0x38; 0x39; 0x3a; 0x3b; 0x3c; 0x3d; -1; -1; -1; -1; -1; -1; -1; 0x00; 0x01; 0x02; 0x03; 0x04; 0x05; 0x06; 0x07; 0x08; 0x09; 0x0a; 0x0b; 0x0c; 0x0d; 0x0e; 0x0f; 0x10; 0x11; 0x12; 0x13; 0x14; 0x15; 0x16; 0x17; 0x18; 0x19; -1; -1; -1; -1; -1; -1; 0x1a; 0x1b; 0x1c; 0x1d; 0x1e; 0x1f; 0x20; 0x21; 0x22; 0x23; 0x24; 0x25; 0x26; 0x27; 0x28; 0x29; 0x2a; 0x2b; 0x2c; 0x2d; 0x2e; 0x2f; 0x30; 0x31; 0x32; 0x33; -1; -1; -1; -1; -1; |] in if v < 128 then inv.(v) else (-1) let is_high v = v >= 0xd800 && v <= 0xdbff let is_low v = v >= 0xdc00 && v <= 0xdfff let malformed source off pos len = `Malformed (Bytes.sub_string source (off + pos) len) let unsafe_byte source off pos = Char.code (Bytes.unsafe_get source (off + pos)) type src = [ `Channel of in_channel | `String of string | `Manual ] type decode = [ `Await | `End | `Malformed of string | `Uchar of Uchar.t ] let pp_decode ppf = function | `Uchar u -> pp ppf "@[`Uchar U+%04X@]" (Uchar.to_int u) | `End -> pp ppf "`End" | `Await -> pp ppf "`Await" | `Malformed s -> let l = String.length s in pp ppf "@[`Malformed ("; for i = 0 to l - 1 do pp ppf "%02X" (Char.code s.[i]) done; pp ppf ")@]" type decoder = { src : src ; mutable i : Bytes.t ; mutable i_off : int ; mutable i_pos : int ; mutable i_len : int ; mutable acc : int ; mutable bits : int ; mutable high : int ; mutable f_open : bool ; mutable f_used : bool ; mutable byte_count : int ; mutable pp : decoder -> [ `Malformed of string | `Uchar of Uchar.t ] -> decode ; mutable k : decoder -> decode } let i_rem decoder = decoder.i_len - decoder.i_pos + 1 let end_of_input decoder = decoder.i <- Bytes.empty ; decoder.i_off <- 0 ; decoder.i_pos <- 0 ; decoder.i_len <- min_int let src decoder source off len = if (off < 0 || len < 0 || off + len > Bytes.length source) then invalid_bounds off len else if (len = 0) then end_of_input decoder else ( decoder.i <- source ; decoder.i_off <- off ; decoder.i_pos <- 0 ; decoder.i_len <- len - 1) let refill k decoder = match decoder.src with | `Manual -> decoder.k <- k; `Await | `String _ -> end_of_input decoder; k decoder | `Channel ic -> let len = input ic decoder.i 0 (Bytes.length decoder.i) in (src decoder decoder.i 0 len; k decoder) let ret k v byte_count decoder = decoder.k <- k ; decoder.byte_count <- decoder.byte_count + byte_count ; decoder.pp decoder v let consume k byte_count decoder = decoder.k <- k ; decoder.byte_count <- decoder.byte_count + byte_count ; decoder.k decoder let finish decoder = if decoder.f_open || decoder.high <> 0 then `Malformed "" (* XXX(dinosaure): other error message? *) else `End let rec decode_utf_7 decoder = let rem = i_rem decoder in let malformed n = let i_pos = decoder.i_pos in decoder.i_pos <- decoder.i_pos + 1 ; ret decode_utf_7 (malformed decoder.i decoder.i_off i_pos n) n decoder in if rem <= 0 then (if rem < 0 then finish decoder else refill decode_utf_7 decoder) else let byte = unsafe_byte decoder.i decoder.i_off decoder.i_pos in if byte < 0 || byte > 127 then malformed 1 else ( if decoder.f_open then decode_shifted_unicode byte decoder else if byte = 0x2b then (* begin decoding base64. *) ( decoder.f_open <- true ; decoder.f_used <- false ; decoder.bits <- 0 ; decoder.i_pos <- decoder.i_pos + 1 ; consume decode_utf_7 1 decoder ) else ( let uchar byte = `Uchar (Uchar.unsafe_of_int byte) in (* there was unpaired high surrogate. *) if decoder.high <> 0 then malformed 1 else ( decoder.i_pos <- decoder.i_pos + 1 ; ret decode_utf_7 (uchar byte) 1 decoder))) and decode_shift_character decoder = let uchar byte = `Uchar (Uchar.unsafe_of_int byte) in (* Also, as a special case, the sequence "+-" may be used to encoder the character "+". *) decoder.i_pos <- decoder.i_pos + 1 ; decoder.f_open <- false ; ret decode_utf_7 (uchar 0x2b) 1 decoder and decode_shifted_unicode byte decoder = let uchar byte = `Uchar (Uchar.unsafe_of_int byte) in let malformed n = let i_pos = decoder.i_pos in decoder.i_pos <- decoder.i_pos + 1 ; ret decode_utf_7 (malformed decoder.i decoder.i_off i_pos n) n decoder in (* currently shift decoding. *) if not decoder.f_used && byte = 0x2d then decode_shift_character decoder else let value = b64d byte in if value < 0 then if decoder.bits >= 6 then malformed 1 else (* too many bits in accumulation buffer *) let mask = (1 lsl decoder.bits) - 1 in if decoder.acc land mask <> 0 then malformed 1 else (* non-zero trailing base64 bits *) ( decoder.f_open <- false ; if byte <> 0x2d then if decoder.high <> 0 then malformed 1 else (* unpaired high surrogate *) if decoder.f_used then ( decoder.i_pos <- decoder.i_pos + 1 ; ret decode_utf_7 (uchar byte) 1 decoder) else malformed 1 (* shift encoded ended without being used *) else ( decoder.i_pos <- decoder.i_pos + 1 ; consume decode_utf_7 1 decoder)) else (* accumulate more base64 bits. *) ( decoder.f_used <- true ; decoder.acc <- (decoder.acc lsl 6) lor value ; decoder.bits <- decoder.bits + 6 ; if decoder.bits >= 16 then (* extract a code point. *) ( decoder.bits <- decoder.bits - 16 ; let code_point = (decoder.acc lsr decoder.bits) land 0xffff in if decoder.high <> 0 then let low = code_point in let high = decoder.high in (* next code point must be low surrogate. *) ( if not (is_low low) then malformed 1 else ( decoder.high <- 0 ; let code_point = ((high - 0xd800) * 0x400) + ((low - 0xdc00) + 0x10000) in decoder.i_pos <- decoder.i_pos + 1 ; ret decode_utf_7 (uchar code_point) 1 decoder)) else if is_high code_point then (* save and recurse to look for low surrogate. *) ( decoder.high <- code_point ; decoder.i_pos <- decoder.i_pos + 1 ; consume decode_utf_7 1 decoder) else if is_low code_point then malformed 1 (* unpaired low surrogate *) else (* not surrogate. *) ( decoder.i_pos <- decoder.i_pos + 1 ; ret decode_utf_7 (uchar code_point) 1 decoder)) else (* consume safely and look next. *) ( decoder.i_pos <- decoder.i_pos + 1 ; consume decode_utf_7 1 decoder)) let pp_utf_7 _decoder v = (v :> decode) let decoder src = let pp = pp_utf_7 in let k = decode_utf_7 in let i, i_off, i_pos, i_len = match src with | `Manual -> Bytes.empty, 0, 1, 0 | `Channel _ -> Bytes.create io_buffer_size, 0, 1, 0 | `String s -> Bytes.unsafe_of_string s, 0, 0, String.length s - 1 in { src ; i_off ; i_pos ; i_len ; i ; acc = 0 ; bits = 0 ; high = 0 ; f_open = false ; f_used = false ; byte_count = 0 ; pp ; k } let decode decoder = decoder.k decoder let decoder_byte_count decoder = decoder.byte_count let decoder_src decoder = decoder.src module String = struct type 'a folder = 'a -> int -> [ `Malformed of string | `Uchar of Uchar.t ] -> 'a let fold ?off ?len folder acc str = let off, len = match off, len with | Some off, Some len -> off, len | None, Some len -> 0, len | Some off, None -> off, String.length str - off | None, None -> 0, String.length str in let acc = ref acc in let decoder = decoder (`String (String.sub str off len)) in let rec go decoder = match decode decoder with | `Await -> assert false (* can never occur iff [src = `String _] *) | `End -> !acc | (`Uchar _ | `Malformed _) as v -> acc := folder !acc (decoder_byte_count decoder) v ; go decoder in go decoder end