// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
pub(all) struct LookupSegmentSingle {
first : UInt
last : UInt
value : UInt
} derive(Eq, Show, ToJson)
///|
pub(all) struct LookupSegmentArray {
first : UInt
last : UInt
values : Array[UInt]
} derive(Eq, Show, ToJson)
///|
pub(all) struct LookupSingle {
glyph : UInt
value : UInt
} derive(Eq, Show, ToJson)
///|
pub(all) enum LookupTable {
Format0(Array[UInt])
Format2(Array[LookupSegmentSingle])
Format4(Array[LookupSegmentArray])
Format6(Array[LookupSingle])
Format8(UInt, Array[UInt])
Format10(Int, UInt, Array[UInt])
} derive(Eq, Show, ToJson)
fn within_range(glyph : UInt, first : UInt, last : UInt) -> Bool {
glyph >= first && glyph <= last
}
fn LookupTable::value_for(self : LookupTable, glyph : UInt) -> UInt? {
match self {
Format0(values) => {
let idx = glyph.reinterpret_as_int()
if idx < 0 || idx >= values.length() {
None
} else {
Some(values[idx])
}
}
Format2(segments) => {
for seg in segments {
if within_range(glyph, seg.first, seg.last) {
return Some(seg.value)
}
}
None
}
Format4(segments) => {
for seg in segments {
if within_range(glyph, seg.first, seg.last) {
let idx = (glyph - seg.first).reinterpret_as_int()
if idx >= 0 && idx < seg.values.length() {
return Some(seg.values[idx])
}
}
}
None
}
Format6(entries) => {
for entry in entries {
if entry.glyph == glyph {
return Some(entry.value)
}
}
None
}
Format8(first, values) => {
if glyph < first {
return None
}
let idx = (glyph - first).reinterpret_as_int()
if idx < 0 || idx >= values.length() {
None
} else {
Some(values[idx])
}
}
Format10(_, first, values) => {
if glyph < first {
return None
}
let idx = (glyph - first).reinterpret_as_int()
if idx < 0 || idx >= values.length() {
None
} else {
Some(values[idx])
}
}
}
}
fn read_lookup_value(
data : BytesView,
offset : Int,
value_size : Int,
) -> Result[UInt, AatError] {
if value_size == 2 {
read_u16(data, offset)
} else if value_size == 4 {
read_u32(data, offset)
} else {
Err(InvalidFormat)
}
}
fn parse_format0(
data : BytesView,
offset : Int,
num_glyphs : Int,
value_size : Int,
) -> Result[LookupTable, AatError] {
if num_glyphs < 0 {
return Err(InvalidFormat)
}
let start = offset + 2
if start < 0 || start + num_glyphs * value_size > data.length() {
return Err(UnexpectedEof)
}
let values : Array[UInt] = []
for i in 0.. return Err(err)
Ok(value) => values.push(value)
}
}
Ok(Format0(values))
}
fn parse_binsearch_header(
data : BytesView,
offset : Int,
) -> Result[(Int, Int, Int), AatError] {
let unit_size = read_u16_int(data, offset)
let n_units = read_u16_int(data, offset + 2)
let search_range = read_u16_int(data, offset + 4)
let entry_selector = read_u16_int(data, offset + 6)
let range_shift = read_u16_int(data, offset + 8)
match (unit_size, n_units, search_range, entry_selector, range_shift) {
(Err(err), _, _, _, _) => Err(err)
(_, Err(err), _, _, _) => Err(err)
(_, _, Err(err), _, _) => Err(err)
(_, _, _, Err(err), _) => Err(err)
(_, _, _, _, Err(err)) => Err(err)
(Ok(unit_size), Ok(n_units), Ok(_), Ok(_), Ok(_)) => {
if unit_size < 0 || n_units < 0 {
return Err(InvalidFormat)
}
Ok((unit_size, n_units, offset + 10))
}
}
}
fn parse_format2(
data : BytesView,
offset : Int,
value_size : Int,
) -> Result[LookupTable, AatError] {
let header = parse_binsearch_header(data, offset + 2)
match header {
Err(err) => Err(err)
Ok((unit_size, n_units, array_offset)) => {
if unit_size < 4 + value_size {
return Err(InvalidFormat)
}
if array_offset < 0 || array_offset + n_units * unit_size > data.length() {
return Err(UnexpectedEof)
}
let segments : Array[LookupSegmentSingle] = []
for i in 0.. return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(last), Ok(first), Ok(value)) => {
if last == 0xffffU && first == 0xffffU {
continue
}
segments.push(LookupSegmentSingle::{ first, last, value })
}
}
}
Ok(Format2(segments))
}
}
}
fn parse_format4(
data : BytesView,
offset : Int,
value_size : Int,
) -> Result[LookupTable, AatError] {
let header = parse_binsearch_header(data, offset + 2)
match header {
Err(err) => Err(err)
Ok((unit_size, n_units, array_offset)) => {
if unit_size < 6 {
return Err(InvalidFormat)
}
if array_offset < 0 || array_offset + n_units * unit_size > data.length() {
return Err(UnexpectedEof)
}
let segments : Array[LookupSegmentArray] = []
for i in 0.. return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(last), Ok(first), Ok(values_offset)) => {
if last == 0xffffU && first == 0xffffU {
continue
}
let count = (last - first).reinterpret_as_int() + 1
if count < 0 {
return Err(InvalidFormat)
}
let values_start = offset + values_offset
if values_start < 0 || values_start + count * value_size > data.length() {
return Err(UnexpectedEof)
}
let values : Array[UInt] = []
for j in 0.. return Err(err)
Ok(value) => values.push(value)
}
}
segments.push(LookupSegmentArray::{ first, last, values })
}
}
}
Ok(Format4(segments))
}
}
}
fn parse_format6(
data : BytesView,
offset : Int,
value_size : Int,
) -> Result[LookupTable, AatError] {
let header = parse_binsearch_header(data, offset + 2)
match header {
Err(err) => Err(err)
Ok((unit_size, n_units, array_offset)) => {
if unit_size < 2 + value_size {
return Err(InvalidFormat)
}
if array_offset < 0 || array_offset + n_units * unit_size > data.length() {
return Err(UnexpectedEof)
}
let entries : Array[LookupSingle] = []
for i in 0.. return Err(err)
(_, Err(err)) => return Err(err)
(Ok(glyph), Ok(value)) => {
if glyph == 0xffffU {
continue
}
entries.push(LookupSingle::{ glyph, value })
}
}
}
Ok(Format6(entries))
}
}
}
fn parse_format8(
data : BytesView,
offset : Int,
value_size : Int,
) -> Result[LookupTable, AatError] {
let first = read_u16(data, offset + 2)
let glyph_count = read_u16_int(data, offset + 4)
match (first, glyph_count) {
(Err(err), _) => Err(err)
(_, Err(err)) => Err(err)
(Ok(first), Ok(glyph_count)) => {
if glyph_count < 0 {
return Err(InvalidFormat)
}
let start = offset + 6
if start < 0 || start + glyph_count * value_size > data.length() {
return Err(UnexpectedEof)
}
let values : Array[UInt] = []
for i in 0.. return Err(err)
Ok(value) => values.push(value)
}
}
Ok(Format8(first, values))
}
}
}
fn parse_format10(
data : BytesView,
offset : Int,
) -> Result[LookupTable, AatError] {
let value_size = read_u16_int(data, offset + 2)
let first = read_u16(data, offset + 4)
let glyph_count = read_u16_int(data, offset + 6)
match (value_size, first, glyph_count) {
(Err(err), _, _) => Err(err)
(_, Err(err), _) => Err(err)
(_, _, Err(err)) => Err(err)
(Ok(value_size), Ok(first), Ok(glyph_count)) => {
if value_size <= 0 || value_size > 4 || glyph_count < 0 {
return Err(InvalidFormat)
}
let start = offset + 8
if start < 0 || start + glyph_count * value_size > data.length() {
return Err(UnexpectedEof)
}
let values : Array[UInt] = []
for i in 0.. Result[LookupTable, AatError] {
let format = read_u16_int(data, offset)
match format {
Err(err) => Err(err)
Ok(format) => {
match format {
0 => parse_format0(data, offset, num_glyphs, value_size)
2 => parse_format2(data, offset, value_size)
4 => parse_format4(data, offset, value_size)
6 => parse_format6(data, offset, value_size)
8 => parse_format8(data, offset, value_size)
10 => parse_format10(data, offset)
_ => Err(InvalidFormat)
}
}
}
}
///|
fn parse_lookup(
data : BytesView,
offset : Int,
num_glyphs : Int,
) -> Result[LookupTable, AatError] {
parse_lookup_with_value_size(data, offset, num_glyphs, 2)
}