Builds compact search tables from codepoint ranges and performs binary search over them.
The Unicode property modules build their search tables at compile time from the range data returned by functions such as Unicode.GeneralCategory.categories/0. The tables are stored as module attributes so they are embedded in the compiled module as literals and shared, not copied, at runtime.
Two table shapes are supported:
A value table, built with
new_value_table/1from a map ofvalue => ranges, returns the value associated with the range containing a codepoint viafind/3.A membership table, built with
new_membership_table/1from a list of ranges, answers whether a codepoint is within any range viamember?/2.
Binary search over these tables replaces the very large generated guard clauses previously used for codepoint lookup, reducing compile time by an order of magnitude and lookup time by approximately 10x.
Summary
Types
A tuple of range starts and range ends.
A codepoint range expressed as an inclusive 2-tuple.
A tuple of range starts, range ends and the value for each range.
Functions
Returns the value for the range containing a codepoint.
Returns whether any range in a membership table contains a codepoint.
Builds a membership table from a list of codepoint ranges.
Builds a value table from a map of values to codepoint ranges.
Types
A tuple of range starts and range ends.
@type range() :: {non_neg_integer(), non_neg_integer()}
A codepoint range expressed as an inclusive 2-tuple.
A tuple of range starts, range ends and the value for each range.
Functions
@spec find(value_table(), integer(), any()) :: any()
Returns the value for the range containing a codepoint.
Arguments
value_tableis a table built withnew_value_table/1.codepointis an integer codepoint.defaultis the term returned when no range containscodepoint.
Returns
- The value associated with the range containing
codepoint, ordefault.
Examples
iex> table = Unicode.RangeSearch.new_value_table(%{lower: [{?a, ?z}], upper: [{?A, ?Z}]})
iex> Unicode.RangeSearch.find(table, ?A, :none)
:upper
@spec member?(membership_table(), integer()) :: boolean()
Returns whether any range in a membership table contains a codepoint.
Arguments
membership_tableis a table built withnew_membership_table/1.codepointis an integer codepoint.
Returns
trueif a range containscodepoint, otherwisefalse.
Examples
iex> table = Unicode.RangeSearch.new_membership_table([{?0, ?9}])
iex> Unicode.RangeSearch.member?(table, ?7)
true
@spec new_membership_table([range(), ...]) :: membership_table()
Builds a membership table from a list of codepoint ranges.
Arguments
rangesis a list of 2-tuple codepoint ranges.
Returns
- A
membership_table/0suitable for use withmember?/2.
Ranges whose bounds are not integers (for example, multi-codepoint emoji sequence ranges) are ignored. Overlapping and adjacent ranges are merged.
Examples
iex> table = Unicode.RangeSearch.new_membership_table([{?a, ?z}, {?0, ?9}])
iex> Unicode.RangeSearch.member?(table, ?x)
true
iex> Unicode.RangeSearch.member?(table, ?!)
false
@spec new_value_table(%{required(any()) => [range(), ...]}) :: value_table()
Builds a value table from a map of values to codepoint ranges.
Arguments
range_mapis a map with any term as the key and a list of 2-tuple codepoint ranges as the value.
Returns
- A
value_table/0suitable for use withfind/3.
Ranges whose bounds are not integers (for example, multi-codepoint emoji sequence ranges) are ignored. Raises ArgumentError if ranges associated with different values overlap, since the table cannot then return a single value for a codepoint.
Examples
iex> table = Unicode.RangeSearch.new_value_table(%{lower: [{?a, ?z}], upper: [{?A, ?Z}]})
iex> Unicode.RangeSearch.find(table, ?q, :none)
:lower
iex> Unicode.RangeSearch.find(table, ?5, :none)
:none