package ucharset

  1. Overview
  2. Docs

Module UcharsetSource

Sourcetype t

Character classes for Unicode-aware lexers and regex engines: a faster, smaller Set.Make (Uchar).

Elements are Unicode scalar values in the sense of Uchar.t: codepoints 0 .. max_codepoint excluding the surrogate block 0xD800 .. 0xDFFF, which exists only as a UTF-16 encoding mechanism and cannot occur in well-formed text. Noncharacters such as U+FFFE are ordinary scalar values and are included.

Every function taking a raw int codepoint to build or update a set validates it, raising Invalid_argument on a surrogate or on a value outside 0 .. max_codepoint. add, remove, add_range and remove_range validate before the membership test, so remove t 0xD800 raises rather than returning t unchanged. The _char and _uchar families cannot raise, their arguments being scalar values already.

Queries go the other way: mem, next_elt_opt, prev_elt_opt, Lookup.mem and Partition.block_of_opt take any int and answer, reading a surrogate or an out-of-range value as one the set does not contain.

Sourceval max_codepoint : int

Largest valid codepoint, 0x10FFFF.

Constructors

Sourceval empty : t

The empty set.

Sourceval all : t

The set of all Unicode scalar values: 0 .. 0xD7FF and 0xE000 .. max_codepoint.

Sourceval singleton : int -> t

singleton cp is the set containing exactly cp.

Sourceval singleton_char : char -> t

singleton_char c is singleton (Char.code c).

Sourceval singleton_uchar : Uchar.t -> t

singleton_uchar u is singleton (Uchar.to_int u). A Uchar.t is a scalar value by construction, so none of the _uchar functions can raise.

Sourceval range : lo:int -> hi:int -> t

range ~lo ~hi is the set of scalar values from lo to hi inclusive; empty if lo > hi. Both bounds are validated (and must not be surrogates) even when the result is empty. A range straddling the surrogate block is split around it, so range ~lo:0xD000 ~hi:0xF000 contains 0xD000 .. 0xD7FF and 0xE000 .. 0xF000. An endpoint landing inside the block raises, so range ~lo:0xD000 ~hi:0xD900 is an error and a caller slicing arbitrary spans has to snap its own bounds clear.

Sourceval range_char : lo:char -> hi:char -> t

range_char ~lo ~hi is range ~lo:(Char.code lo) ~hi:(Char.code hi).

Sourceval of_list : int list -> t

of_list cps is the set of the given codepoints. Duplicates are allowed; order is irrelevant. O(n log n).

Sourceval range_uchar : lo:Uchar.t -> hi:Uchar.t -> t

range_uchar ~lo ~hi is range on the two scalar values.

Sourceval of_char_list : char list -> t

of_char_list cs is of_list (List.map Char.code cs).

Sourceval of_uchar_list : Uchar.t list -> t

of_uchar_list us is of_list (List.map Uchar.to_int us). O(n log n).

Sourceval of_seq : int Seq.t -> t

of_seq s is the set of the codepoints of s. O(n log n).

Sourceval of_utf_8_string : string -> t

of_utf_8_string s is the set of scalar values occurring in s. Malformed bytes decode to U+FFFD following String.get_utf_8_uchar, and so contribute U+FFFD to the result rather than raising.

Sourceval of_intervals : (int * int) list -> t

of_intervals pairs is the union of the inclusive ranges (lo, hi) in pairs. Pairs may be unsorted, overlapping, or adjacent; pairs with lo > hi are ignored; bounds are validated and split around the surrogate block like range. O(n log n). Intended for tables of literals such as generated Unicode property data.

Sourcemodule Builder : sig ... end
Sourcemodule Lookup : sig ... end
Sourceval to_lookup : t -> Lookup.t

Compile t into a Lookup.t. Costs a pass over the pages up to the largest member, so compile once and reuse; pages that come out identical share one leaf, which is what keeps the pool proportional to the set.

Sourceval ascii_table : t -> string

ascii_table s is a 256-byte table for the ASCII fast path of a UTF-8 inner loop: tbl.[b] <> '\000' tests membership of byte b directly. Bytes 0x80..0xFF (UTF-8 lead/continuation bytes, not characters) map to false, so the raw input byte indexes the table with no masking; route multi-byte sequences through Lookup.mem_uchar on the decoded scalar instead.

Packed encoding

A compact serialization for embedding tables in generated code: a string constant compiles to a single data blob, whereas a list of interval literals compiles to per-pair allocation code. The format is stable and part of this interface; each endpoint of the canonical intervals as 3 big-endian bytes, 6 bytes per interval.

Sourceval of_packed_string : string -> t

of_packed_string s decodes a string produced by to_packed_string, and accepts exactly what that function emits: a length that is a multiple of six, and intervals with lo <= hi, in range, clear of the surrogate block, in increasing order and separated by at least one codepoint. Anything else raises Invalid_argument, adjacent intervals such as 1..10 and 11..20 included, canonical form having written them as one.

Sourceval of_packed_string_opt : string -> t option

of_packed_string returning None instead of raising, for decoding data from an untrusted source.

Sourceval to_packed_string : t -> string

to_packed_string t encodes the canonical intervals of t, lowest first, each endpoint as three big-endian bytes and lo before hi, so the result is 6 * num_intervals t bytes. Inverse of of_packed_string.

Set operations

Sourceval union : t -> t -> t

union t1 t2 is the set of scalar values in either.

Sourceval inter : t -> t -> t

inter t1 t2 is the set of scalar values in both.

Sourceval diff : t -> remove:t -> t

diff t ~remove is the set of scalar values in t but not in remove.

Sourceval comp : t -> t

comp t is diff all ~remove:t.

Sourceval xor : t -> t -> t

Symmetric difference: the values in exactly one of the two. One merge over the endpoints of both, like union and inter.

Sourceval union_list : t list -> t

union_list ts is the union of all of ts, and empty for the empty list. Accumulates into one builder and canonicalizes once, O(N log N) in the total interval count.

Sourceval inter_list : t list -> t

inter_list ts is the intersection of all of ts, and all for the empty list (the identity for intersection). One k-way sweep, so no intermediate set is built.

Sourceval add : t -> int -> t

add t cp is t with cp added. Returns t itself if cp is already a member; otherwise copies the interval array, so O(n). For repeated additions use Builder, which appends in amortized O(1) and canonicalizes once.

Sourceval remove : t -> int -> t

remove t cp is t with cp removed. Returns t itself if cp is not a member; otherwise O(n), as add.

Sourceval add_range : t -> lo:int -> hi:int -> t

add_range t ~lo ~hi is union t (range ~lo ~hi).

Sourceval remove_range : t -> lo:int -> hi:int -> t

remove_range t ~lo ~hi is diff t ~remove:(range ~lo ~hi).

Sourceval filter : (int -> bool) -> t -> t

filter f t keeps the codepoints of t satisfying f. O(cardinal); it visits every codepoint, so over a million calls on all. The number of runs it will emit is not known in advance, so the accumulator starts at t's interval count and grows: a predicate that fragments a large set pays for that growth.

Sourceval map : (int -> int) -> t -> t

map f t is the image of t under f. O(cardinal), and every result is validated, so f returning a surrogate or an out-of-range value raises Invalid_argument. f need not be injective or monotonic; the results are sorted and merged.

Partition refinement

A partition here is a collection of pairwise-disjoint non-empty blocks; it need not cover the codespace. The common refinement (or meet) of two partitions is the set of non-empty a inter b for a in one and b in the other, the coarsest partition that both are unions of.

Blocks on each side are disjoint, so the meet is a single merge over the interval endpoints: O(P + Q) in the total interval counts, where computing it pairwise would cost |p| * |q| intersections to find at most P + Q - 1 blocks.

Partition is that merge's working form, intervals tagged with an owning block index, so a chain of meets never materialises an intermediate block and each block's least element falls out of the sweep. Callers needing only one codepoint per block, such as a derivative-based DFA construction picking a character to derive on, can take Partition.representatives and never build the blocks. refine and refine_all are the convenience forms for callers already holding lists of sets.

Sourcemodule Partition : sig ... end
Sourceval refine : t list -> t list -> t list

refine p q is the common refinement of two partitions given as lists of disjoint blocks, as Partition.blocks (Partition.meet (of_blocks p) (of_blocks q)). Blocks come back in increasing order of least element. Raises Invalid_argument if either list has overlapping blocks.

Sourceval refine_all : t list list -> t list

refine_all ps refines every partition in ps together, building the block sets once at the end; [all] for the empty list. Prefer this to folding refine, which rebuilds them at every step.

Queries

Sourceval is_empty : t -> bool

is_empty t is true iff t contains no codepoints. O(1).

Sourceval is_singleton : t -> bool

is_singleton t is true iff t contains exactly one codepoint. O(1).

Sourceval is_all : t -> bool

is_all t is true iff t is all. O(1); prefer it to is_empty (comp t), which allocates a set to answer the same question.

Sourceval mem : t -> int -> bool

Membership test. O(log n) in the number of intervals.

Sourceval mem_char : t -> char -> bool

mem_char t c is mem t (Char.code c).

Sourceval mem_uchar : t -> Uchar.t -> bool

mem_uchar t u is mem t (Uchar.to_int u).

Sourceval next_elt_opt : t -> int -> int option

next_elt_opt t cp is the smallest member of t strictly greater than cp, if any. cp need not be a member, or even a scalar value, so this doubles as "seek to the next member beyond here". O(log n).

Sourceval prev_elt_opt : t -> int -> int option

prev_elt_opt t cp is the largest member of t strictly less than cp, if any. O(log n).

Sourceval exists : (int -> bool) -> t -> bool

exists f t is true iff f holds of some codepoint of t. Visits codepoints, so O(cardinal) in the worst case, though it stops early.

Sourceval for_all : (int -> bool) -> t -> bool

for_all f t is true iff f holds of every codepoint of t. O(cardinal) in the worst case, stopping at the first failure.

Sourceval cardinal : t -> int

Number of codepoints in the set. O(n) in the number of intervals.

Sourceval num_intervals : t -> int

Number of maximal contiguous runs in the set. O(1).

Sourceval subset : t -> of_:t -> bool

subset t ~of_ is true iff every scalar value of t is in of_.

Sourceval disjoint : t -> t -> bool

disjoint t1 t2 is true iff t1 and t2 share no scalar value.

Sourceval min_elt_opt : t -> int option

Smallest codepoint in the set, if any. O(1).

Sourceval max_elt_opt : t -> int option

Largest codepoint in the set, if any. O(1).

Sourceval choose_opt : t -> int option

An arbitrary element of the set, if any (currently the smallest). O(1).

Iteration

All traversal is in increasing codepoint order. Note that iter and fold visit every codepoint individually so on interval-dense sets such as all this is 0x110000 calls. Prefer iter_intervals when per-run processing suffices.

Sourceval iter : (int -> unit) -> t -> unit

iter f t applies f to each codepoint of t.

Sourceval iter_intervals : (int -> int -> unit) -> t -> unit

iter_intervals f t applies f lo hi to each maximal run lo..hi of t.

Sourceval fold : (int -> 'a -> 'a) -> t -> 'a -> 'a

fold f t init folds f over each codepoint of t.

Sourceval fold_intervals : (int -> int -> 'a -> 'a) -> t -> 'a -> 'a

fold_intervals f t init folds f lo hi over each maximal run of t.

Sourceval to_seq : t -> int Seq.t

The codepoints of t in increasing order. Lazy, so it can be consumed partially without paying for the whole set.

Sourceval to_seq_intervals : t -> (int * int) Seq.t

The maximal runs of t as inclusive (lo, hi) pairs, in increasing order.

Sourceval to_intervals : t -> (int * int) list

The maximal runs of the set as inclusive (lo, hi) pairs, in increasing order. Not the inverse of of_list, which takes codepoints; of_intervals is.

Comparison and hashing

Suitable for use with Map.Make, Set.Make and Hashtbl.Make, which is why equal and compare keep the unlabeled t -> t -> _ signatures those functors require. equal t1 t2 iff compare t1 t2 = 0, and equal sets hash identically, the internal representation being canonical.

Sourceval equal : t -> t -> bool

equal t1 t2 is true iff the two sets have the same members. The representation is canonical, so this compares the interval arrays: O(1) on physically equal sets and on sets of differing interval count.

Sourceval compare : t -> t -> int

A total order on sets. The order is representation-based (lexicographic over interval endpoints), rather than any set-theoretic order.

Sourceval hash : t -> int

A non-negative hash consistent with equal, mixing the interval count and every endpoint. The values themselves are an implementation detail and may change between releases, so do not persist them.

Printing

Sourceval pp : Format.formatter -> t -> unit

Prints the runs of the set, e.g. [97-122; 181; 223-246].

Sourceval to_string : t -> string

As pp, but into a string on a single line. The printers break at the formatter's margin; the string forms have none, so the result never contains a newline, however wide the set.

Sourceval pp_hex : Format.formatter -> t -> unit

As pp, but in U+XXXX notation: [U+0061-U+007A; U+00B5; U+00DF-U+00F6].

Sourceval to_hex_string : t -> string

As pp_hex, on a single line. See to_string.

Sourceval pp_class : Format.formatter -> t -> unit

A regex-style character class view, reading a set as characters rather than as numbers: {a-g j m-t}, {α-ω}, {é € 😀-😁}.

Members are written as themselves, UTF-8 encoded. The escapes cover the syntax (-, space, { and }), the backslash that introduces them, the C0 and C1 controls, and the rest of Unicode's whitespace (U+00A0, U+1680, U+2000 to U+200A, U+2028, U+2029, U+202F, U+205F, U+3000), which would otherwise be indistinguishable from the separator. They are written \0, \t, \n, \v, \f, \r or \u{XX}. Nothing else is escaped, so unassigned and private-use members reach the terminal as whatever it makes of them.

A view, not regex syntax; an engine would want a single bracketed class with no separators, and its own escaping rules.

Sourceval to_class_string : t -> string

As pp_class, on a single line. See to_string.