Geotessera library for OCaml
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. *)