package corvus-json-schema

  1. Overview
  2. Docs
A fast JSON Schema evaluator (draft 4 to 2020-12) with no dependencies outside the OCaml distribution

Install

dune-project
 Dependency

Authors

Maintainers

Sources

v0.1.1.tar.gz
sha256=d0a084719770883384e488d42ff1a2c0f84a53e2b2898016183533b8393f2cab
sha512=d5875f234b316cd50a888e1a3acc055133d705eb6622e3856c22f6719fa3eae196f1f08cd08cc073cc0924d27e330ae2ea5388142dd5e91b173bdfd91ad997f0

doc/src/corvus-json-schema.ucd/corvus_json_schema_ucd.ml.html

Source file corvus_json_schema_ucd.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
(* A port of ucd.go of the Go module. See the interface for what differs. *)

type table = Tables.table = { r16 : string; r32 : string }

let version = Tables.version
let max_rune = 0x10FFFF

(* Bound [i] of the ranges that end at or below U+FFFF: two bytes, the low byte first. *)
let get16 s i = Char.code (String.get s (2 * i)) lor (Char.code (String.get s ((2 * i) + 1)) lsl 8)

(* Bound [i] of the ranges that start above U+FFFF: three bytes, the low byte first. *)
let get24 s i =
  Char.code (String.get s (3 * i))
  lor (Char.code (String.get s ((3 * i) + 1)) lsl 8)
  lor (Char.code (String.get s ((3 * i) + 2)) lsl 16)

let rec search16 ranges c lo hi =
  if lo >= hi then false
  else
    let mid = (lo + hi) lsr 1 in
    if c > get16 ranges ((2 * mid) + 1) then search16 ranges c (mid + 1) hi
    else if c < get16 ranges (2 * mid) then search16 ranges c lo mid
    else true

let rec search24 ranges c lo hi =
  if lo >= hi then false
  else
    let mid = (lo + hi) lsr 1 in
    if c > get24 ranges ((2 * mid) + 1) then search24 ranges c (mid + 1) hi
    else if c < get24 ranges (2 * mid) then search24 ranges c lo mid
    else true

let contains t r =
  if r < 0 then false
  else if r <= 0xFFFF then search16 t.r16 r 0 (String.length t.r16 / 4)
  else search24 t.r32 r 0 (String.length t.r32 / 6)

let bounds16 t = Array.init (String.length t.r16 / 2) (get16 t.r16)
let bounds32 t = Array.init (String.length t.r32 / 3) (get24 t.r32)

let ranges t =
  let n16 = String.length t.r16 / 2 and n32 = String.length t.r32 / 3 in
  (* A range that crosses U+FFFF is stored in two parts. *)
  let joined = n16 > 0 && n32 > 0 && get16 t.r16 (n16 - 1) = 0xFFFF && get24 t.r32 0 = 0x10000 in
  let dst = Array.make (if joined then n16 + n32 - 2 else n16 + n32) 0 in
  for i = 0 to n16 - 1 do
    dst.(i) <- get16 t.r16 i
  done;
  if joined then begin
    dst.(n16 - 1) <- get24 t.r32 1;
    for i = 2 to n32 - 1 do
      dst.(n16 + i - 2) <- get24 t.r32 i
    done
  end
  else
    for i = 0 to n32 - 1 do
      dst.(n16 + i) <- get24 t.r32 i
    done;
  dst

(* Builds a table from inclusive pairs in ascending order that neither overlap nor touch. *)
let new_table pairs =
  let r16 = Buffer.create 64 and r32 = Buffer.create 64 in
  let add16 c =
    Buffer.add_char r16 (Char.chr (c land 0xFF));
    Buffer.add_char r16 (Char.chr (c lsr 8))
  and add24 c =
    Buffer.add_char r32 (Char.chr (c land 0xFF));
    Buffer.add_char r32 (Char.chr ((c lsr 8) land 0xFF));
    Buffer.add_char r32 (Char.chr (c lsr 16))
  in
  let i = ref 0 in
  while !i < Array.length pairs do
    let lo = pairs.(!i) and hi = pairs.(!i + 1) in
    if hi <= 0xFFFF then begin
      add16 lo;
      add16 hi
    end
    else if lo > 0xFFFF then begin
      add24 lo;
      add24 hi
    end
    else begin
      add16 lo;
      add16 0xFFFF;
      add24 0x10000;
      add24 hi
    end;
    i := !i + 2
  done;
  { r16 = Buffer.contents r16; r32 = Buffer.contents r32 }

let union tables =
  let all = Array.concat (List.map ranges tables) in
  let n = Array.length all / 2 in
  (* The ranges are sorted by their first code point. A range is one number here, its first code point above its last,
     so that sorting the numbers sorts the ranges. A code point has 21 bits, so the number fits an int of 63 bits. *)
  let order = Array.init n (fun i -> (all.(2 * i) lsl 32) lor all.((2 * i) + 1)) in
  Array.sort compare order;
  let merged = Array.make (Array.length all) 0 in
  let m = ref 0 in
  Array.iter
    (fun pair ->
      let lo = pair lsr 32 and hi = pair land 0xFFFFFFFF in
      if !m > 0 && lo <= merged.(!m - 1) + 1 then
        begin if hi > merged.(!m - 1) then merged.(!m - 1) <- hi
        end
      else begin
        merged.(!m) <- lo;
        merged.(!m + 1) <- hi;
        m := !m + 2
      end)
    order;
  new_table (Array.sub merged 0 !m)

(* Builds the table of the code points that the table does not hold. *)
let complement t =
  let pairs = ranges t in
  let inverse = Array.make (Array.length pairs + 2) 0 in
  let n = ref 0 and next = ref 0 and i = ref 0 in
  while !i < Array.length pairs do
    if pairs.(!i) > !next then begin
      inverse.(!n) <- !next;
      inverse.(!n + 1) <- pairs.(!i) - 1;
      n := !n + 2
    end;
    next := pairs.(!i + 1) + 1;
    i := !i + 2
  done;
  if !next <= max_rune then begin
    inverse.(!n) <- !next;
    inverse.(!n + 1) <- max_rune;
    n := !n + 2
  end;
  new_table (Array.sub inverse 0 !n)

let letter = Tables.(union [ gc_lu; gc_ll; gc_lt; gc_lm; gc_lo ])
let cased_letter = Tables.(union [ gc_lu; gc_ll; gc_lt ])
let mark = Tables.(union [ gc_mn; gc_mc; gc_me ])
let number = Tables.(union [ gc_nd; gc_nl; gc_no ])
let punctuation = Tables.(union [ gc_pc; gc_pd; gc_ps; gc_pe; gc_pi; gc_pf; gc_po ])
let symbol = Tables.(union [ gc_sm; gc_sc; gc_sk; gc_so ])
let separator = Tables.(union [ gc_zs; gc_zl; gc_zp ])
let other = Tables.(union [ gc_cc; gc_cf; gc_cs; gc_co; gc_cn ])
let assigned = complement Tables.gc_cn
let unknown_script = Tables.(union [ gc_cn; gc_co; gc_cs ])
let some_letter = Some letter
let some_cased_letter = Some cased_letter
let some_mark = Some mark
let some_number = Some number
let some_punctuation = Some punctuation
let some_symbol = Some symbol
let some_separator = Some separator
let some_other = Some other
let some_assigned = Some assigned
let some_unknown_script = Some unknown_script

let category = function
  | "L" -> some_letter
  | "LC" -> some_cased_letter
  | "M" -> some_mark
  | "N" -> some_number
  | "P" -> some_punctuation
  | "S" -> some_symbol
  | "Z" -> some_separator
  | "C" -> some_other
  | name -> Tables.category_table name

let binary = function "Assigned" -> some_assigned | name -> Tables.binary_table name

(* The table of the Script value Katakana_Or_Hiragana (Hrkt). PropertyValueAliases.txt lists the value, and no code
   point has it, in Script or in Script_Extensions. Scripts.txt has no line for it, so the generated tables do not
   have it. *)
let some_katakana_or_hiragana = Some (new_table [||])

let script = function
  | "Unknown" | "Zzzz" -> some_unknown_script
  | "Katakana_Or_Hiragana" | "Hrkt" -> some_katakana_or_hiragana
  | name -> Tables.script_table name

let script_extensions = function
  | "Unknown" | "Zzzz" -> some_unknown_script
  | "Katakana_Or_Hiragana" | "Hrkt" -> some_katakana_or_hiragana
  | name -> Tables.script_extensions_table name

let script_names = Tables.script_names
let binary_names = Tables.binary_names

(* A case table holds rows of four elements. A row maps some of the code points first, first+1, ... first+length-1 to
   themselves plus delta. The ones it maps are those whose offset from first has none of the bits of mask, so a mask
   of 1 takes every other code point. *)
let rec map_case table r lo hi =
  if lo >= hi then r
  else
    let mid = (lo + hi) lsr 1 in
    let first = table.(4 * mid) in
    if r < first then map_case table r lo mid
    else
      let offset = r - first in
      if offset >= table.((4 * mid) + 2) then map_case table r (mid + 1) hi
      else if offset land table.((4 * mid) + 3) = 0 then r + table.((4 * mid) + 1)
      else r

let fold_by_table r = map_case Tables.case_folds r 0 (Array.length Tables.case_folds / 4)
let to_upper_by_table r = map_case Tables.upper_cases r 0 (Array.length Tables.upper_cases / 4)
let fold r = if r < 0x80 then if r >= 0x41 && r <= 0x5A then r + 0x20 else r else fold_by_table r
let to_upper r = if r < 0x80 then if r >= 0x61 && r <= 0x7A then r - 0x20 else r else to_upper_by_table r

let folding () =
  let table = Tables.case_folds in
  let count = ref 0 in
  let each f =
    for i = 0 to (Array.length table / 4) - 1 do
      if table.((4 * i) + 1) <> 0 then
        for offset = 0 to table.((4 * i) + 2) - 1 do
          if offset land table.((4 * i) + 3) = 0 then f (table.(4 * i) + offset)
        done
    done
  in
  each (fun _ -> incr count);
  let dst = Array.make !count 0 in
  let n = ref 0 in
  each (fun r ->
      dst.(!n) <- r;
      incr n);
  dst