Geotessera library for OCaml
at main 28 kB view raw
1(* See the end of the file for the license *) 2exception Cannot_write 3exception Read_error of string 4 5let read_error fmt = Printf.ksprintf (fun s -> raise (Read_error s)) fmt 6let magic_string = "\147NUMPY" 7let magic_string_len = String.length magic_string 8 9type packed_kind = P : (_, _) Bigarray.kind -> packed_kind 10 11let dtype ~packed_kind = 12 let endianness = 13 match packed_kind with 14 | P Bigarray.Char -> "|" 15 | P _ -> if Sys.big_endian then ">" else "<" 16 in 17 let kind = 18 match packed_kind with 19 | P Bigarray.Int32 -> "i4" 20 | P Bigarray.Int64 -> "i8" 21 | P Bigarray.Float16 -> "f16" 22 | P Bigarray.Float32 -> "f4" 23 | P Bigarray.Float64 -> "f8" 24 | P Bigarray.Int8_unsigned -> "u1" 25 | P Bigarray.Int8_signed -> "i1" 26 | P Bigarray.Int16_unsigned -> "u2" 27 | P Bigarray.Int16_signed -> "i2" 28 | P Bigarray.Char -> "S1" 29 | P Bigarray.Complex32 -> "c8" (* 2 32bits float. *) 30 | P Bigarray.Complex64 -> "c16" (* 2 64bits float. *) 31 | P Bigarray.Int -> failwith "Int is not supported" 32 | P Bigarray.Nativeint -> failwith "Nativeint is not supported." 33 in 34 endianness ^ kind 35 36let map_file file_descr ~pos kind layout shared shape = 37 let is_scalar = Array.length shape = 0 in 38 let array = 39 Unix.map_file file_descr ~pos kind layout shared 40 (if is_scalar then [| 1 |] else shape) 41 in 42 if is_scalar then Bigarray.reshape array [||] else array 43 44let fortran_order (type a) ~(layout : a Bigarray.layout) = 45 match layout with 46 | Bigarray.C_layout -> "False" 47 | Bigarray.Fortran_layout -> "True" 48 49let shape ~dims = 50 match dims with 51 | [| dim1 |] -> Printf.sprintf "%d," dim1 52 | dims -> Array.to_list dims |> List.map string_of_int |> String.concat ", " 53 54let full_header ?header_len ~layout ~packed_kind ~dims () = 55 let header = 56 Printf.sprintf "{'descr': '%s', 'fortran_order': %s, 'shape': (%s), }" 57 (dtype ~packed_kind) (fortran_order ~layout) (shape ~dims) 58 in 59 let padding_len = 60 let total_len = String.length header + magic_string_len + 4 + 1 in 61 match header_len with 62 | None -> if total_len mod 16 = 0 then 0 else 16 - (total_len mod 16) 63 | Some header_len -> 64 if header_len mod 16 <> 0 then 65 failwith "header_len has to be divisible by 16"; 66 if header_len < total_len then 67 failwith "header_len is smaller than total_len"; 68 header_len - total_len 69 in 70 let total_header_len = String.length header + padding_len + 1 in 71 Printf.sprintf "%s\001\000%c%c%s%s\n" magic_string 72 (total_header_len mod 256 |> Char.chr) 73 (total_header_len / 256 |> Char.chr) 74 header 75 (String.make padding_len ' ') 76 77let with_file filename flags mask ~f = 78 let file_descr = Unix.openfile filename flags mask in 79 try 80 let result = f file_descr in 81 Unix.close file_descr; 82 result 83 with exn -> 84 Unix.close file_descr; 85 raise exn 86 87let write ?header_len bigarray filename = 88 with_file filename [ O_CREAT; O_TRUNC; O_RDWR ] 0o640 ~f:(fun file_descr -> 89 let full_header = 90 full_header () ?header_len 91 ~layout:(Bigarray.Genarray.layout bigarray) 92 ~packed_kind:(P (Bigarray.Genarray.kind bigarray)) 93 ~dims:(Bigarray.Genarray.dims bigarray) 94 in 95 let full_header_len = String.length full_header in 96 if 97 Unix.write_substring file_descr full_header 0 full_header_len 98 <> full_header_len 99 then raise Cannot_write; 100 let file_array = 101 map_file 102 ~pos:(Int64.of_int full_header_len) 103 file_descr 104 (Bigarray.Genarray.kind bigarray) 105 (Bigarray.Genarray.layout bigarray) 106 true 107 (Bigarray.Genarray.dims bigarray) 108 in 109 Bigarray.Genarray.blit bigarray file_array) 110 111let write1 array1 filename = write (Bigarray.genarray_of_array1 array1) filename 112let write2 array2 filename = write (Bigarray.genarray_of_array2 array2) filename 113let write3 array3 filename = write (Bigarray.genarray_of_array3 array3) filename 114 115module Batch_writer = struct 116 let header_len = 128 117 118 type t = { 119 file_descr : Unix.file_descr; 120 mutable bytes_written_so_far : int; 121 mutable dims_and_packed_kind : (int array * packed_kind) option; 122 } 123 124 let append t bigarray = 125 let file_array = 126 map_file 127 ~pos:(Int64.of_int t.bytes_written_so_far) 128 t.file_descr 129 (Bigarray.Genarray.kind bigarray) 130 (Bigarray.Genarray.layout bigarray) 131 true 132 (Bigarray.Genarray.dims bigarray) 133 in 134 Bigarray.Genarray.blit bigarray file_array; 135 let size_in_bytes = Bigarray.Genarray.size_in_bytes bigarray in 136 t.bytes_written_so_far <- t.bytes_written_so_far + size_in_bytes; 137 match t.dims_and_packed_kind with 138 | None -> 139 let dims = Bigarray.Genarray.dims bigarray in 140 let kind = Bigarray.Genarray.kind bigarray in 141 t.dims_and_packed_kind <- Some (dims, P kind) 142 | Some (dims, _kind) -> 143 let dims' = Bigarray.Genarray.dims bigarray in 144 let incorrect_dimensions = 145 match (Array.to_list dims, Array.to_list dims') with 146 | [], _ | _, [] -> true 147 | _ :: d, _ :: d' -> d <> d' 148 in 149 if incorrect_dimensions then 150 Printf.sprintf "Incorrect dimensions %s vs %s." (shape ~dims) 151 (shape ~dims:dims') 152 |> failwith; 153 dims.(0) <- dims.(0) + dims'.(0) 154 155 let create filename = 156 let file_descr = 157 Unix.openfile filename [ O_CREAT; O_TRUNC; O_RDWR ] 0o640 158 in 159 { 160 file_descr; 161 bytes_written_so_far = header_len; 162 dims_and_packed_kind = None; 163 } 164 165 let close t = 166 assert (Unix.lseek t.file_descr 0 SEEK_SET = 0); 167 let header = 168 match t.dims_and_packed_kind with 169 | None -> failwith "Nothing to write" 170 | Some (dims, packed_kind) -> 171 full_header ~header_len ~layout:C_layout ~dims ~packed_kind () 172 in 173 if Unix.write_substring t.file_descr header 0 header_len <> header_len then 174 raise Cannot_write; 175 Unix.close t.file_descr 176end 177 178let really_read fd len = 179 let buffer = Bytes.create len in 180 let rec loop offset = 181 let read = Unix.read fd buffer offset (len - offset) in 182 if read + offset < len then loop (read + offset) 183 else if read = 0 then read_error "unexpected eof" 184 in 185 loop 0; 186 Bytes.to_string buffer 187 188module Header = struct 189 type packed_kind = P : (_, _) Bigarray.kind -> packed_kind 190 type t = { kind : packed_kind; fortran_order : bool; shape : int array } 191 192 let split str ~on = 193 let parens = ref 0 in 194 let indexes = ref [] in 195 for i = 0 to String.length str - 1 do 196 match str.[i] with 197 | '(' -> incr parens 198 | ')' -> decr parens 199 | c when !parens = 0 && c = on -> indexes := i :: !indexes 200 | _ -> () 201 done; 202 List.fold_left 203 (fun (prev_p, acc) index -> 204 (index, String.sub str (index + 1) (prev_p - index - 1) :: acc)) 205 (String.length str, []) 206 !indexes 207 |> fun (first_pos, acc) -> String.sub str 0 first_pos :: acc 208 209 let trim str ~on = 210 let rec loopr start len = 211 if len = 0 then (start, len) 212 else if List.mem str.[start + len - 1] on then loopr start (len - 1) 213 else (start, len) 214 in 215 let rec loopl start len = 216 if len = 0 then (start, len) 217 else if List.mem str.[start] on then loopl (start + 1) (len - 1) 218 else loopr start len 219 in 220 let start, len = loopl 0 (String.length str) in 221 String.sub str start len 222 223 let parse header = 224 let header_fields = 225 trim header ~on:[ '{'; ' '; '}'; '\n' ] 226 |> split ~on:',' 227 |> List.map String.trim 228 |> List.filter (fun s -> String.length s > 0) 229 |> List.map (fun header_field -> 230 match split header_field ~on:':' with 231 | [ name; value ] -> 232 ( trim name ~on:[ '\''; ' ' ], 233 trim value ~on:[ '\''; ' '; '('; ')' ] ) 234 | _ -> read_error "unable to parse field %s" header_field) 235 in 236 let find_field field = 237 try List.assoc field header_fields 238 with Not_found -> read_error "cannot find field %s" field 239 in 240 let kind = 241 let kind = find_field "descr" in 242 (match kind.[0] with 243 | '|' | '=' -> () 244 | '>' -> 245 if not Sys.big_endian then 246 read_error "big endian data but arch is little endian" 247 | '<' -> 248 if Sys.big_endian then 249 read_error "little endian data but arch is big endian" 250 | otherwise -> read_error "incorrect endianness %c" otherwise); 251 match String.sub kind 1 (String.length kind - 1) with 252 | "f2" -> P Float16 253 | "f4" -> P Float32 254 | "f8" -> P Float64 255 | "i4" -> P Int32 256 | "i8" -> P Int64 257 | "u1" -> P Int8_unsigned 258 | "i1" -> P Int8_signed 259 | "u2" -> P Int16_unsigned 260 | "i2" -> P Int16_signed 261 | "S1" -> P Char 262 | "c8" -> P Complex32 263 | "c16" -> P Complex64 264 | otherwise -> read_error "incorrect descr %s" otherwise 265 in 266 let fortran_order = 267 match find_field "fortran_order" with 268 | "False" -> false 269 | "True" -> true 270 | otherwise -> read_error "incorrect fortran_order %s" otherwise 271 in 272 let shape = 273 find_field "shape" 274 |> split ~on:',' 275 |> List.map String.trim 276 |> List.filter (fun s -> String.length s > 0) 277 |> List.map int_of_string 278 |> Array.of_list 279 in 280 { kind; fortran_order; shape } 281end 282 283type packed_array = P : (_, _, _) Bigarray.Genarray.t -> packed_array 284type packed_array1 = P1 : (_, _, _) Bigarray.Array1.t -> packed_array1 285type packed_array2 = P2 : (_, _, _) Bigarray.Array2.t -> packed_array2 286type packed_array3 = P3 : (_, _, _) Bigarray.Array3.t -> packed_array3 287 288let read_mmap filename ~shared = 289 let access = if shared then Unix.O_RDWR else O_RDONLY in 290 let file_descr = Unix.openfile filename [ access ] 0 in 291 let pos, header = 292 try 293 let magic_string' = really_read file_descr magic_string_len in 294 if magic_string <> magic_string' then read_error "magic string mismatch"; 295 let version = really_read file_descr 2 |> fun v -> v.[0] |> Char.code in 296 let header_len_len = 297 match version with 298 | 1 -> 2 299 | 2 -> 4 300 | _ -> read_error "unsupported version %d" version 301 in 302 let header, header_len = 303 really_read file_descr header_len_len |> fun str -> 304 let header_len = ref 0 in 305 for i = String.length str - 1 downto 0 do 306 header_len := (256 * !header_len) + Char.code str.[i] 307 done; 308 (really_read file_descr !header_len, !header_len) 309 in 310 let header = Header.parse header in 311 (Int64.of_int (header_len + header_len_len + magic_string_len + 2), header) 312 with exn -> 313 Unix.close file_descr; 314 raise exn 315 in 316 let (Header.P kind) = header.kind in 317 let build layout = 318 let array = map_file file_descr ~pos kind layout shared header.shape in 319 Gc.finalise (fun _ -> Unix.close file_descr) array; 320 P array 321 in 322 if header.fortran_order then build Fortran_layout else build C_layout 323 324let read_mmap1 filename ~shared = 325 let (P array) = read_mmap filename ~shared in 326 P1 (Bigarray.array1_of_genarray array) 327 328let read_mmap2 filename ~shared = 329 let (P array) = read_mmap filename ~shared in 330 P2 (Bigarray.array2_of_genarray array) 331 332let read_mmap3 filename ~shared = 333 let (P array) = read_mmap filename ~shared in 334 P3 (Bigarray.array3_of_genarray array) 335 336let read_copy filename = 337 let (P array) = read_mmap filename ~shared:false in 338 let result = 339 Bigarray.Genarray.create 340 (Bigarray.Genarray.kind array) 341 (Bigarray.Genarray.layout array) 342 (Bigarray.Genarray.dims array) 343 in 344 Bigarray.Genarray.blit array result; 345 P result 346 347let read_copy1 filename = 348 let (P array) = read_copy filename in 349 P1 (Bigarray.array1_of_genarray array) 350 351let read_copy2 filename = 352 let (P array) = read_copy filename in 353 P2 (Bigarray.array2_of_genarray array) 354 355let read_copy3 filename = 356 let (P array) = read_copy filename in 357 P3 (Bigarray.array3_of_genarray array) 358 359module Npz = struct 360 let npy_suffix = ".npy" 361 362 let maybe_add_suffix array_name ~suffix = 363 let suffix = 364 match suffix with None -> npy_suffix | Some suffix -> suffix 365 in 366 array_name ^ suffix 367 368 type in_file = Zip.in_file 369 370 let open_in = Zip.open_in 371 372 let entries t = 373 Zip.entries t 374 |> List.map (fun entry -> 375 let filename = entry.Zip.filename in 376 if String.length filename < String.length npy_suffix then filename 377 else 378 let start_pos = String.length filename - String.length npy_suffix in 379 if 380 String.sub filename start_pos (String.length npy_suffix) 381 = npy_suffix 382 then String.sub filename 0 start_pos 383 else filename) 384 385 let close_in = Zip.close_in 386 387 let read ?suffix t array_name = 388 let array_name = maybe_add_suffix array_name ~suffix in 389 let entry = 390 try Zip.find_entry t array_name 391 with Not_found -> 392 raise (Invalid_argument ("unable to find " ^ array_name)) 393 in 394 let tmp_file = Filename.temp_file "ocaml-npz" ".tmp" in 395 Zip.copy_entry_to_file t entry tmp_file; 396 let data = read_copy tmp_file in 397 Sys.remove tmp_file; 398 data 399 400 type out_file = Zip.out_file 401 402 let open_out filename = Zip.open_out filename 403 let close_out = Zip.close_out 404 405 let write ?suffix t array_name array = 406 let array_name = maybe_add_suffix array_name ~suffix in 407 let tmp_file = Filename.temp_file "ocaml-npz" ".tmp" in 408 write array tmp_file; 409 Zip.copy_file_to_entry tmp_file t array_name; 410 Sys.remove tmp_file 411end 412 413(** Type equalities module, used in conversion function *) 414module Eq = struct 415 (** An equality type to extract type equalities *) 416 type ('a, 'b) t = W : ('a, 'a) t 417 418 open Bigarray 419 420 (** Type equalities for bigarray kinds *) 421 module Kind = struct 422 let ( === ) : type a b c d. 423 (a, b) kind -> (c, d) kind -> ((a, b) kind, (c, d) kind) t option = 424 fun x y -> 425 match (x, y) with 426 | Float32, Float32 -> Some W 427 | Float64, Float64 -> Some W 428 | Int8_signed, Int8_signed -> Some W 429 | Int8_unsigned, Int8_unsigned -> Some W 430 | Int16_signed, Int16_signed -> Some W 431 | Int16_unsigned, Int16_unsigned -> Some W 432 | Int32, Int32 -> Some W 433 | Int64, Int64 -> Some W 434 | Int, Int -> Some W 435 | Nativeint, Nativeint -> Some W 436 | Complex32, Complex32 -> Some W 437 | Complex64, Complex64 -> Some W 438 | Char, Char -> Some W 439 | _ -> None 440 end 441 442 (** Type equalities for layout *) 443 module Layout = struct 444 let ( === ) : type a b. 445 a layout -> b layout -> (a layout, b layout) t option = 446 fun x y -> 447 match (x, y) with 448 | Fortran_layout, Fortran_layout -> Some W 449 | C_layout, C_layout -> Some W 450 | _, _ -> None 451 end 452end 453 454(** Conversion functions from packed arrays to bigarrays *) 455 456let to_bigarray (type a b c) (layout : c Bigarray.layout) 457 (kind : (a, b) Bigarray.kind) (P x) = 458 match Eq.Layout.(Bigarray.Genarray.layout x === layout) with 459 | None -> None 460 | Some Eq.W -> ( 461 match Eq.Kind.(Bigarray.Genarray.kind x === kind) with 462 | None -> None 463 | Some Eq.W -> Some (x : (a, b, c) Bigarray.Genarray.t)) 464 465let to_bigarray1 (type a b c) (layout : c Bigarray.layout) 466 (kind : (a, b) Bigarray.kind) (P1 x) = 467 match Eq.Layout.(Bigarray.Array1.layout x === layout) with 468 | None -> None 469 | Some Eq.W -> ( 470 match Eq.Kind.(Bigarray.Array1.kind x === kind) with 471 | None -> None 472 | Some Eq.W -> Some (x : (a, b, c) Bigarray.Array1.t)) 473 474let to_bigarray2 (type a b c) (layout : c Bigarray.layout) 475 (kind : (a, b) Bigarray.kind) (P2 x) = 476 match Eq.Layout.(Bigarray.Array2.layout x === layout) with 477 | None -> None 478 | Some Eq.W -> ( 479 match Eq.Kind.(Bigarray.Array2.kind x === kind) with 480 | None -> None 481 | Some Eq.W -> Some (x : (a, b, c) Bigarray.Array2.t)) 482 483let to_bigarray3 (type a b c) (layout : c Bigarray.layout) 484 (kind : (a, b) Bigarray.kind) (P3 x) = 485 match Eq.Layout.(Bigarray.Array3.layout x === layout) with 486 | None -> None 487 | Some Eq.W -> ( 488 match Eq.Kind.(Bigarray.Array3.kind x === kind) with 489 | None -> None 490 | Some Eq.W -> Some (x : (a, b, c) Bigarray.Array3.t)) 491 492(* 493 Apache License 494 Version 2.0, January 2004 495 http://www.apache.org/licenses/ 496 497 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 498 499 1. Definitions. 500 501 "License" shall mean the terms and conditions for use, reproduction, 502 and distribution as defined by Sections 1 through 9 of this document. 503 504 "Licensor" shall mean the copyright owner or entity authorized by 505 the copyright owner that is granting the License. 506 507 "Legal Entity" shall mean the union of the acting entity and all 508 other entities that control, are controlled by, or are under common 509 control with that entity. For the purposes of this definition, 510 "control" means (i) the power, direct or indirect, to cause the 511 direction or management of such entity, whether by contract or 512 otherwise, or (ii) ownership of fifty percent (50%) or more of the 513 outstanding shares, or (iii) beneficial ownership of such entity. 514 515 "You" (or "Your") shall mean an individual or Legal Entity 516 exercising permissions granted by this License. 517 518 "Source" form shall mean the preferred form for making modifications, 519 including but not limited to software source code, documentation 520 source, and configuration files. 521 522 "Object" form shall mean any form resulting from mechanical 523 transformation or translation of a Source form, including but 524 not limited to compiled object code, generated documentation, 525 and conversions to other media types. 526 527 "Work" shall mean the work of authorship, whether in Source or 528 Object form, made available under the License, as indicated by a 529 copyright notice that is included in or attached to the work 530 (an example is provided in the Appendix below). 531 532 "Derivative Works" shall mean any work, whether in Source or Object 533 form, that is based on (or derived from) the Work and for which the 534 editorial revisions, annotations, elaborations, or other modifications 535 represent, as a whole, an original work of authorship. For the purposes 536 of this License, Derivative Works shall not include works that remain 537 separable from, or merely link (or bind by name) to the interfaces of, 538 the Work and Derivative Works thereof. 539 540 "Contribution" shall mean any work of authorship, including 541 the original version of the Work and any modifications or additions 542 to that Work or Derivative Works thereof, that is intentionally 543 submitted to Licensor for inclusion in the Work by the copyright owner 544 or by an individual or Legal Entity authorized to submit on behalf of 545 the copyright owner. For the purposes of this definition, "submitted" 546 means any form of electronic, verbal, or written communication sent 547 to the Licensor or its representatives, including but not limited to 548 communication on electronic mailing lists, source code control systems, 549 and issue tracking systems that are managed by, or on behalf of, the 550 Licensor for the purpose of discussing and improving the Work, but 551 excluding communication that is conspicuously marked or otherwise 552 designated in writing by the copyright owner as "Not a Contribution." 553 554 "Contributor" shall mean Licensor and any individual or Legal Entity 555 on behalf of whom a Contribution has been received by Licensor and 556 subsequently incorporated within the Work. 557 558 2. Grant of Copyright License. Subject to the terms and conditions of 559 this License, each Contributor hereby grants to You a perpetual, 560 worldwide, non-exclusive, no-charge, royalty-free, irrevocable 561 copyright license to reproduce, prepare Derivative Works of, 562 publicly display, publicly perform, sublicense, and distribute the 563 Work and such Derivative Works in Source or Object form. 564 565 3. Grant of Patent License. Subject to the terms and conditions of 566 this License, each Contributor hereby grants to You a perpetual, 567 worldwide, non-exclusive, no-charge, royalty-free, irrevocable 568 (except as stated in this section) patent license to make, have made, 569 use, offer to sell, sell, import, and otherwise transfer the Work, 570 where such license applies only to those patent claims licensable 571 by such Contributor that are necessarily infringed by their 572 Contribution(s) alone or by combination of their Contribution(s) 573 with the Work to which such Contribution(s) was submitted. If You 574 institute patent litigation against any entity (including a 575 cross-claim or counterclaim in a lawsuit) alleging that the Work 576 or a Contribution incorporated within the Work constitutes direct 577 or contributory patent infringement, then any patent licenses 578 granted to You under this License for that Work shall terminate 579 as of the date such litigation is filed. 580 581 4. Redistribution. You may reproduce and distribute copies of the 582 Work or Derivative Works thereof in any medium, with or without 583 modifications, and in Source or Object form, provided that You 584 meet the following conditions: 585 586 (a) You must give any other recipients of the Work or 587 Derivative Works a copy of this License; and 588 589 (b) You must cause any modified files to carry prominent notices 590 stating that You changed the files; and 591 592 (c) You must retain, in the Source form of any Derivative Works 593 that You distribute, all copyright, patent, trademark, and 594 attribution notices from the Source form of the Work, 595 excluding those notices that do not pertain to any part of 596 the Derivative Works; and 597 598 (d) If the Work includes a "NOTICE" text file as part of its 599 distribution, then any Derivative Works that You distribute must 600 include a readable copy of the attribution notices contained 601 within such NOTICE file, excluding those notices that do not 602 pertain to any part of the Derivative Works, in at least one 603 of the following places: within a NOTICE text file distributed 604 as part of the Derivative Works; within the Source form or 605 documentation, if provided along with the Derivative Works; or, 606 within a display generated by the Derivative Works, if and 607 wherever such third-party notices normally appear. The contents 608 of the NOTICE file are for informational purposes only and 609 do not modify the License. You may add Your own attribution 610 notices within Derivative Works that You distribute, alongside 611 or as an addendum to the NOTICE text from the Work, provided 612 that such additional attribution notices cannot be construed 613 as modifying the License. 614 615 You may add Your own copyright statement to Your modifications and 616 may provide additional or different license terms and conditions 617 for use, reproduction, or distribution of Your modifications, or 618 for any such Derivative Works as a whole, provided Your use, 619 reproduction, and distribution of the Work otherwise complies with 620 the conditions stated in this License. 621 622 5. Submission of Contributions. Unless You explicitly state otherwise, 623 any Contribution intentionally submitted for inclusion in the Work 624 by You to the Licensor shall be under the terms and conditions of 625 this License, without any additional terms or conditions. 626 Notwithstanding the above, nothing herein shall supersede or modify 627 the terms of any separate license agreement you may have executed 628 with Licensor regarding such Contributions. 629 630 6. Trademarks. This License does not grant permission to use the trade 631 names, trademarks, service marks, or product names of the Licensor, 632 except as required for reasonable and customary use in describing the 633 origin of the Work and reproducing the content of the NOTICE file. 634 635 7. Disclaimer of Warranty. Unless required by applicable law or 636 agreed to in writing, Licensor provides the Work (and each 637 Contributor provides its Contributions) on an "AS IS" BASIS, 638 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 639 implied, including, without limitation, any warranties or conditions 640 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 641 PARTICULAR PURPOSE. You are solely responsible for determining the 642 appropriateness of using or redistributing the Work and assume any 643 risks associated with Your exercise of permissions under this License. 644 645 8. Limitation of Liability. In no event and under no legal theory, 646 whether in tort (including negligence), contract, or otherwise, 647 unless required by applicable law (such as deliberate and grossly 648 negligent acts) or agreed to in writing, shall any Contributor be 649 liable to You for damages, including any direct, indirect, special, 650 incidental, or consequential damages of any character arising as a 651 result of this License or out of the use or inability to use the 652 Work (including but not limited to damages for loss of goodwill, 653 work stoppage, computer failure or malfunction, or any and all 654 other commercial damages or losses), even if such Contributor 655 has been advised of the possibility of such damages. 656 657 9. Accepting Warranty or Additional Liability. While redistributing 658 the Work or Derivative Works thereof, You may choose to offer, 659 and charge a fee for, acceptance of support, warranty, indemnity, 660 or other liability obligations and/or rights consistent with this 661 License. However, in accepting such obligations, You may act only 662 on Your own behalf and on Your sole responsibility, not on behalf 663 of any other Contributor, and only if You agree to indemnify, 664 defend, and hold each Contributor harmless for any liability 665 incurred by, or claims asserted against, such Contributor by reason 666 of your accepting any such warranty or additional liability. 667 668 END OF TERMS AND CONDITIONS 669 670 APPENDIX: How to apply the Apache License to your work. 671 672 To apply the Apache License to your work, attach the following 673 boilerplate notice, with the fields enclosed by brackets "{}" 674 replaced with your own identifying information. (Don't include 675 the brackets!) The text should be enclosed in the appropriate 676 comment syntax for the file format. We also recommend that a 677 file or class name and description of purpose be included on the 678 same "printed page" as the copyright notice for easier 679 identification within third-party archives. 680 681 Copyright {yyyy} {name of copyright owner} 682 683 Licensed under the Apache License, Version 2.0 (the "License"); 684 you may not use this file except in compliance with the License. 685 You may obtain a copy of the License at 686 687 http://www.apache.org/licenses/LICENSE-2.0 688 689 Unless required by applicable law or agreed to in writing, software 690 distributed under the License is distributed on an "AS IS" BASIS, 691 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 692 See the License for the specific language governing permissions and 693 limitations under the License. *)