///|
/// A structural fault or an explicit limit of the supported AVIF profile.
/// offset is an absolute byte offset in the original file. Codes beginning
/// UNSUPPORTED_ or LIMIT_ do not assert that the input violates AVIF.
pub(all) struct AvifDiagnostic {
code : String
box_type : String
offset : Int64
item_id : Int64?
detail : String
} derive(Debug)
///|
pub extend AvifDiagnostic with Debug::{to_repr}
///|
/// A resolved, nonempty item extent contained in one mdat or idat payload.
/// Shared ranges between different items are permitted.
pub(all) struct AvifExtent {
item_id : Int64
offset : Int64
length : Int64
storage : String
} derive(Debug)
///|
pub extend AvifExtent with Debug::{to_repr}
///|
/// One primary-item binding, in ipma association order. index is the original
/// one-based ipco index; offset locates the property's box header in the file.
/// interpreted means only the documented ispe/av1C/pixi field subset was checked,
/// not that the complete property semantics or AV1 payload were validated.
pub(all) struct AvifPropertyAssociation {
index : Int32
box_type : String
offset : Int64
essential : Bool
interpreted : Bool
} derive(Debug)
///|
pub extend AvifPropertyAssociation with Debug::{to_repr}
///|
/// Successful validation of the documented structural subset, not AV1 decoding
/// or full AVIF conformance. Dimensions are the primary item's stored ispe.
pub(all) struct AvifStructure {
primary_item_id : Int64
width : Int32
height : Int32
item_count : Int32
primary_properties : Array[AvifPropertyAssociation]
extents : Array[AvifExtent]
} derive(Debug)
///|
pub extend AvifStructure with Debug::{to_repr}
///|
priv suberror AvifStructureFailure {
AvifFault(AvifDiagnostic)
}
///|
priv struct AvifNode {
kind : String
header : Int64
payload : Int64
end : Int64
}
///|
priv struct AvifBudget {
mut boxes : Int32
mut associations : Int32
mut extents : Int32
}
///|
priv struct AvifBinding {
index : Int32
offset : Int64
essential : Bool
}
///|
priv struct AvifCursor {
bytes : Bytes
mut position : Int32
end : Int32
kind : String
item : Int64?
}
///|
fn[T] avif_fault(
code : String,
kind : String,
offset : Int64,
item : Int64?,
detail : String,
) -> T raise AvifStructureFailure {
raise AvifStructureFailure::AvifFault({
code,
box_type: kind,
offset,
item_id: item,
detail,
})
}
///|
fn avif_need(
condition : Bool,
code : String,
kind : String,
offset : Int64,
item : Int64?,
detail : String,
) -> Unit raise AvifStructureFailure {
if !condition {
avif_fault(code, kind, offset, item, detail)
}
}
///|
fn AvifCursor::uint(
self : AvifCursor,
width : Int32,
) -> Int64 raise AvifStructureFailure {
let offset = Int32::to_int64(self.position)
avif_need(
width >= 0 && width <= 8 && width <= self.end - self.position,
"TRUNCATED_FIELD",
self.kind,
offset,
self.item,
"字段超出所属盒边界",
)
if width == 8 {
avif_need(
self.bytes[self.position].to_int() < 128,
"UNSUPPORTED_UINT64",
self.kind,
offset,
self.item,
"无符号 64 位值超过本实现的 Int64 范围",
)
}
let mut value = 0L
for _ in 0.. Unit raise AvifStructureFailure {
avif_need(
self.position == self.end,
"TRAILING_FIELDS",
self.kind,
Int32::to_int64(self.position),
self.item,
"盒内存在未消费的字段或计数字段不匹配",
)
}
///|
fn AvifCursor::fullbox(
self : AvifCursor,
) -> (Int32, Int64) raise AvifStructureFailure {
let version = Int64::to_int(self.uint(1))
let flags = self.uint(3)
(version, flags)
}
///|
fn avif_node_kind(bytes : Bytes, offset : Int32) -> String {
for
(tag, name) in [
(b"ftyp", "ftyp"),
(b"meta", "meta"),
(b"mdat", "mdat"),
(b"idat", "idat"),
(b"pitm", "pitm"),
(b"iinf", "iinf"),
(b"infe", "infe"),
(b"iloc", "iloc"),
(b"iprp", "iprp"),
(b"ipco", "ipco"),
(b"ipma", "ipma"),
(b"ispe", "ispe"),
(b"av1C", "av1C"),
(b"pixi", "pixi"),
(b"av01", "av01"),
(b"Exif", "Exif"),
(b"mime", "mime"),
(b"grid", "grid"),
(b"uuid", "uuid"),
(b"free", "free"),
(b"skip", "skip"),
] {
if box_type_is(bytes, offset, tag) {
return name
}
}
let name = StringBuilder()
for i in 0..<4 {
name.write_char(bytes[offset + i].to_char())
}
name.to_string()
}
///|
fn avif_cursor(
bytes : Bytes,
node : AvifNode,
item : Int64?,
) -> AvifCursor raise AvifStructureFailure {
avif_need(
node.end <= Int32::to_int64(bytes.length()),
"LIMIT_METADATA",
node.kind,
node.header,
item,
"所需元数据不完整地位于提供的前缀中(上限 4 MiB)",
)
{
bytes,
position: Int64::to_int(node.payload),
end: Int64::to_int(node.end),
kind: node.kind,
item,
}
}
///|
/// All arithmetic is checked against the enclosing range before addition.
/// A last mdat can extend beyond the supplied prefix without being read.
fn avif_nodes(
bytes : Bytes,
start : Int64,
end : Int64,
budget : AvifBudget,
top_level~ : Bool = false,
) -> Array[AvifNode] raise AvifStructureFailure {
let nodes : Array[AvifNode] = []
let mut position = start
while position < end {
avif_need(
budget.boxes < 4096,
"LIMIT_BOXES",
"container",
position,
None,
"所访问的盒总数超过 4096",
)
budget.boxes += 1
avif_need(
end - position >= 8,
"TRUNCATED_BOX",
"container",
position,
None,
"剩余空间不足一个盒头",
)
avif_need(
position <= Int32::to_int64(bytes.length()) - 8L,
"LIMIT_METADATA",
"container",
position,
None,
"盒头超出提供的元数据前缀",
)
let index = Int64::to_int(position)
let kind = avif_node_kind(bytes, index + 4)
let mut header_size = 8L
let size32 = be_u32(bytes, index)
let size = if size32 == 0L {
avif_need(
top_level,
"BOX_SIZE_ZERO",
kind,
position,
None,
"size=0 只能用于延伸至文件末尾的顶层盒",
)
end - position
} else if size32 == 1L {
avif_need(
end - position >= 16L &&
position <= Int32::to_int64(bytes.length()) - 16L,
"TRUNCATED_BOX",
kind,
position,
None,
"扩展盒头不完整",
)
avif_need(
bytes[index + 8].to_int() < 128,
"UNSUPPORTED_UINT64",
kind,
position + 8L,
None,
"扩展盒大小超过 Int64 范围",
)
header_size = 16L
be_u64(bytes, index + 8)
} else {
size32
}
if kind == "uuid" {
header_size += 16L
}
avif_need(
size >= header_size && size <= end - position,
"BOX_RANGE",
kind,
position,
None,
"声明盒大小超出所属容器",
)
avif_need(
position <= Int32::to_int64(bytes.length()) - header_size,
"LIMIT_METADATA",
kind,
position,
None,
"完整盒头(含 UUID user type)必须位于提供的元数据前缀中",
)
nodes.push({
kind,
header: position,
payload: position + header_size,
end: position + size,
})
position += size
}
nodes
}
///|
fn avif_optional_node(
nodes : Array[AvifNode],
kind : String,
) -> AvifNode? raise AvifStructureFailure {
let mut found = None
for node in nodes {
if node.kind == kind {
avif_need(
found is None,
"DUPLICATE_BOX",
kind,
node.header,
None,
"唯一盒出现多次",
)
found = Some(node)
}
}
found
}
///|
fn avif_required_node(
nodes : Array[AvifNode],
kind : String,
parent : AvifNode,
) -> AvifNode raise AvifStructureFailure {
match avif_optional_node(nodes, kind) {
Some(node) => node
None =>
avif_fault(
"MISSING_BOX",
kind,
parent.payload,
None,
"缺少必需的结构盒",
)
}
}
///|
fn avif_item_types(
bytes : Bytes,
node : AvifNode,
budget : AvifBudget,
) -> Map[Int64, String] raise AvifStructureFailure {
let cursor = avif_cursor(bytes, node, None)
let (version, flags) = cursor.fullbox()
avif_need(
version <= 1 && flags == 0L,
"UNSUPPORTED_IINF",
"iinf",
node.payload,
None,
"仅支持版本 0/1 且 flags 为零的 iinf",
)
let count = cursor.uint(if version == 0 { 2 } else { 4 })
avif_need(
count > 0L && count <= 256L,
"LIMIT_ITEMS",
"iinf",
node.payload,
None,
"声明 item 数量必须在 1–256 范围",
)
let children = avif_nodes(
bytes,
Int32::to_int64(cursor.position),
node.end,
budget,
)
let entries : Array[AvifNode] = []
for entry in children {
if entry.kind != "free" && entry.kind != "skip" {
entries.push(entry)
}
}
avif_need(
Int32::to_int64(entries.length()) == count,
"ITEM_COUNT",
"iinf",
node.payload,
None,
"entry_count 与实际 infe 数量不一致",
)
let items : Map[Int64, String] = Map([])
for entry in entries {
avif_need(
entry.kind == "infe",
"ITEM_ENTRY",
"iinf",
entry.header,
None,
"iinf 条目必须为 infe",
)
let info = avif_cursor(bytes, entry, None)
let (info_version, info_flags) = info.fullbox()
avif_need(
(info_version == 2 || info_version == 3) && info_flags <= 1L,
"UNSUPPORTED_INFE",
"infe",
entry.payload,
None,
"仅支持带 item_type 的 infe 版本 2/3",
)
let id_offset = Int32::to_int64(info.position)
let id = info.uint(if info_version == 2 { 2 } else { 4 })
avif_need(
id > 0L && !items.contains(id),
"ITEM_ID",
"infe",
id_offset,
Some(id),
"item_ID 为零或重复",
)
avif_need(
info.uint(2) == 0L,
"UNSUPPORTED_PROTECTION",
"infe",
Int32::to_int64(info.position - 2),
Some(id),
"不支持受保护的 item",
)
let tag_offset = info.position
ignore(info.uint(4))
let kind = avif_node_kind(bytes, tag_offset)
let mut terminated = false
while info.position < info.end {
if info.uint(1) == 0L {
terminated = true
break
}
}
avif_need(
terminated,
"ITEM_NAME",
"infe",
entry.payload,
Some(id),
"item_name 缺少结尾的零字节",
)
// Type-specific MIME/URI suffixes are outside this structural profile.
if kind == "av01" {
info.finish()
}
items.set(id, kind)
}
items
}
///|
fn avif_associations(
bytes : Bytes,
nodes : Array[AvifNode],
properties : Array[AvifNode],
items : Map[Int64, String],
budget : AvifBudget,
) -> Map[Int64, Array[AvifBinding]] raise AvifStructureFailure {
let associations : Map[Int64, Array[AvifBinding]] = Map([])
let variants : Map[Int32, Bool] = Map([])
for node in nodes {
if node.kind == "free" || node.kind == "skip" {
continue
}
avif_need(
node.kind == "ipco" || node.kind == "ipma",
"UNSUPPORTED_IPRP_CHILD",
"iprp",
node.header,
None,
"本子集只处理 ipco、ipma 和填充盒",
)
if node.kind != "ipma" {
continue
}
let cursor = avif_cursor(bytes, node, None)
let (version, flags) = cursor.fullbox()
avif_need(
version <= 1 && flags <= 1L,
"UNSUPPORTED_IPMA",
"ipma",
node.payload,
None,
"仅支持 ipma 版本 0/1、flags 0/1",
)
let variant = version * 2 + Int64::to_int(flags)
avif_need(
!variants.contains(variant),
"DUPLICATE_IPMA",
"ipma",
node.payload,
None,
"相同 version/flags 的 ipma 出现多次",
)
variants.set(variant, true)
let count = cursor.uint(4)
avif_need(
count <= 256L,
"LIMIT_ITEMS",
"ipma",
node.payload,
None,
"ipma 条目超过 256",
)
let mut previous_id = 0L
for _ in 0.. previous_id && !associations.contains(id),
"ASSOCIATION_ITEM_ORDER",
"ipma",
offset,
Some(id),
"item_ID 必须递增,且在所有 ipma 中唯一",
)
previous_id = id
avif_need(
items.contains(id),
"UNDECLARED_ITEM",
"ipma",
offset,
Some(id),
"属性关联引用了未声明的 item",
)
let property_count = Int64::to_int(cursor.uint(1))
avif_need(
property_count <= 4096 - budget.associations,
"LIMIT_ASSOCIATIONS",
"ipma",
offset,
Some(id),
"属性关联总数超过 4096",
)
budget.associations += property_count
let bindings : Array[AvifBinding] = []
for _ in 0.. 0 {
bindings.push({ index, offset: property_offset, essential, })
}
}
associations.set(id, bindings)
}
cursor.finish()
}
associations
}
///|
fn avif_location_width(
width : Int32,
node : AvifNode,
) -> Unit raise AvifStructureFailure {
avif_need(
width == 0 || width == 4 || width == 8,
"UNSUPPORTED_ILOC_WIDTH",
"iloc",
node.payload,
None,
"iloc offset/length/base/index 字段仅支持 0、4、8 字节",
)
}
///|
fn avif_locations(
bytes : Bytes,
node : AvifNode,
items : Map[Int64, String],
primary : Int64,
media : Array[AvifNode],
idat : AvifNode?,
file_size : Int64,
budget : AvifBudget,
) -> Array[AvifExtent] raise AvifStructureFailure {
let cursor = avif_cursor(bytes, node, None)
let (version, flags) = cursor.fullbox()
avif_need(
version <= 2 && flags == 0L,
"UNSUPPORTED_ILOC",
"iloc",
node.payload,
None,
"仅支持 iloc 版本 0/1/2 且 flags 为零",
)
let sizes = Int64::to_int(cursor.uint(1))
let base_sizes = Int64::to_int(cursor.uint(1))
let offset_size = sizes >> 4
let length_size = sizes & 15
let base_size = base_sizes >> 4
let index_size = if version == 0 { 0 } else { base_sizes & 15 }
avif_location_width(offset_size, node)
avif_location_width(length_size, node)
avif_location_width(base_size, node)
avif_location_width(index_size, node)
avif_need(
version != 0 || (base_sizes & 15) == 0,
"RESERVED_BITS",
"iloc",
node.payload + 5L,
None,
"iloc 版本 0 的保留位必须为零",
)
let count = cursor.uint(if version == 2 { 4 } else { 2 })
avif_need(
count <= 256L,
"LIMIT_ITEMS",
"iloc",
node.payload,
None,
"iloc 条目超过 256",
)
let seen : Map[Int64, Bool] = Map([])
let extents : Array[AvifExtent] = []
for _ in 0.. 0,
"UNSUPPORTED_EMPTY_ITEM",
"iloc",
item_offset,
Some(id),
"不支持没有显式数据区间的 item",
)
avif_need(
extent_count <= 4096 - budget.extents,
"LIMIT_EXTENTS",
"iloc",
item_offset,
Some(id),
"数据区间总数超过 4096",
)
budget.extents += extent_count
let (origin, limit, storage) = if construction == 0L {
(0L, file_size, "mdat")
} else {
match idat {
Some(data) => (data.payload, data.end - data.payload, "idat")
None =>
avif_fault(
"MISSING_IDAT",
"iloc",
item_offset,
Some(id),
"construction_method=1 但 meta 中没有 idat",
)
}
}
for _ in 0.. 0L,
"UNSUPPORTED_IMPLICIT_LENGTH",
"iloc",
field_offset,
Some(id),
"需要显式的正 extent_length",
)
avif_need(
base <= limit && offset <= limit - base,
"EXTENT_RANGE",
"iloc",
field_offset,
Some(id),
"base_offset + extent_offset 超出数据来源范围",
)
let relative = base + offset
avif_need(
length <= limit - relative,
"EXTENT_RANGE",
"iloc",
field_offset,
Some(id),
"数据区间尾部超出数据来源范围",
)
let absolute = origin + relative
if construction == 0L {
let mut contained = false
for data in media {
if absolute >= data.payload &&
absolute <= data.end &&
length <= data.end - absolute {
contained = true
break
}
}
avif_need(
contained,
"EXTENT_NOT_IN_MDAT",
"iloc",
field_offset,
Some(id),
"数据区间不完整地位于一个 mdat 的有效载荷内",
)
}
extents.push({ item_id: id, offset: absolute, length, storage, })
}
}
cursor.finish()
avif_need(
seen.contains(primary),
"MISSING_PRIMARY_LOCATION",
"iloc",
node.payload,
Some(primary),
"主图没有数据位置条目",
)
extents
}
///|
fn avif_primary_properties(
bytes : Bytes,
properties : Array[AvifNode],
bindings : Array[AvifBinding],
primary : Int64,
parent : AvifNode,
) -> (Int32, Int32, Array[AvifPropertyAssociation]) raise AvifStructureFailure {
let selected : Array[AvifNode] = []
let evidence : Array[AvifPropertyAssociation] = []
for binding in bindings {
let property = properties[binding.index - 1]
let interpreted = property.kind == "ispe" ||
property.kind == "av1C" ||
property.kind == "pixi"
avif_need(
!binding.essential || interpreted,
"UNSUPPORTED_ESSENTIAL_PROPERTY",
"ipma",
binding.offset,
Some(primary),
"主图属性 \{property.kind} 标记为 essential,但不在已解释的属性子集内",
)
selected.push(property)
evidence.push({
index: binding.index,
box_type: property.kind,
offset: property.header,
essential: binding.essential,
interpreted,
})
}
let ispe = avif_required_node(selected, "ispe", parent)
let config = avif_required_node(selected, "av1C", parent)
let cursor = avif_cursor(bytes, ispe, Some(primary))
let (version, flags) = cursor.fullbox()
avif_need(
version == 0 && flags == 0L,
"UNSUPPORTED_ISPE",
"ispe",
ispe.payload,
Some(primary),
"仅支持版本零的 ispe",
)
let width = cursor.uint(4)
let height = cursor.uint(4)
cursor.finish()
avif_need(
width > 0L &&
height > 0L &&
width <= 2_147_483_647L &&
height <= 2_147_483_647L,
"DIMENSIONS",
"ispe",
ispe.payload + 4L,
Some(primary),
"尺寸必须为正且在 Int32 范围",
)
let codec = avif_cursor(bytes, config, Some(primary))
avif_need(
codec.uint(1) == 0x81L,
"AV1C_HEADER",
"av1C",
config.payload,
Some(primary),
"av1C marker/version 必须为 0x81",
)
ignore(codec.uint(1))
let coding = codec.uint(1)
ignore(codec.uint(1))
let high_depth = (coding & 0x40L) != 0L
let twelve_bit = (coding & 0x20L) != 0L
avif_need(
!twelve_bit || high_depth,
"AV1C_DEPTH",
"av1C",
config.payload + 2L,
Some(primary),
"twelve_bit 不能在 high_bitdepth 为零时设置",
)
let depth = if twelve_bit { 12L } else if high_depth { 10L } else { 8L }
let channels = if (coding & 0x10L) != 0L { 1L } else { 3L }
match avif_optional_node(selected, "pixi") {
Some(pixi) => {
let pixels = avif_cursor(bytes, pixi, Some(primary))
let (pixel_version, pixel_flags) = pixels.fullbox()
avif_need(
pixel_version == 0 && pixel_flags == 0L,
"UNSUPPORTED_PIXI",
"pixi",
pixi.payload,
Some(primary),
"仅支持版本零的 pixi",
)
avif_need(
pixels.uint(1) == channels,
"PIXI_CHANNELS",
"pixi",
pixi.payload + 4L,
Some(primary),
"pixi 通道数与 av1C 不一致",
)
for _ in 0.. ()
}
(Int64::to_int(width), Int64::to_int(height), evidence)
}
///|
fn avif_inspect(
prefix : Bytes,
file_size : Int64,
) -> AvifStructure raise AvifStructureFailure {
avif_need(
file_size >= Int32::to_int64(prefix.length()) &&
prefix.length() <= 4_194_304,
"LIMIT_METADATA",
"file",
0L,
None,
"前缀必须不超过文件大小及 4 MiB 上限",
)
let budget : AvifBudget = { boxes: 0, associations: 0, extents: 0, }
let root : AvifNode = {
kind: "file",
header: 0L,
payload: 0L,
end: file_size,
}
let top = avif_nodes(prefix, 0L, file_size, budget, top_level=true)
avif_need(
top.length() > 0 && top[0].kind == "ftyp",
"MISSING_FTYP",
"file",
0L,
None,
"首盒必须为 ftyp",
)
let ftyp = avif_required_node(top, "ftyp", root)
avif_need(
ftyp.end - ftyp.payload >= 8L && (ftyp.end - ftyp.payload - 8L) % 4L == 0L,
"FTYP_FIELDS",
"ftyp",
ftyp.payload,
None,
"品牌盒必须含主品牌、版本及完整的兼容品牌字段",
)
ignore(avif_cursor(prefix, ftyp, None))
let mut still_brand = box_type_is(
prefix,
Int64::to_int(ftyp.payload),
b"avif",
)
let mut brand_offset = ftyp.payload + 8L
while brand_offset < ftyp.end {
if box_type_is(prefix, Int64::to_int(brand_offset), b"avif") {
still_brand = true
}
brand_offset += 4L
}
avif_need(
still_brand,
"UNSUPPORTED_BRAND",
"ftyp",
ftyp.payload,
None,
"需要主品牌或兼容品牌 avif;仅有 avis 的序列不在本结构子集内",
)
let meta = avif_required_node(top, "meta", root)
let cursor = avif_cursor(prefix, meta, None)
let (version, flags) = cursor.fullbox()
avif_need(
version == 0 && flags == 0L,
"UNSUPPORTED_META",
"meta",
meta.payload,
None,
"仅支持版本零的 meta",
)
let children = avif_nodes(
prefix,
Int32::to_int64(cursor.position),
meta.end,
budget,
)
let pitm = avif_required_node(children, "pitm", meta)
let primary_reader = avif_cursor(prefix, pitm, None)
let (primary_version, primary_flags) = primary_reader.fullbox()
avif_need(
primary_version <= 1 && primary_flags == 0L,
"UNSUPPORTED_PITM",
"pitm",
pitm.payload,
None,
"仅支持 pitm 版本 0/1",
)
let primary = primary_reader.uint(if primary_version == 0 { 2 } else { 4 })
primary_reader.finish()
let items = avif_item_types(
prefix,
avif_required_node(children, "iinf", meta),
budget,
)
avif_need(
items.contains(primary),
"UNDECLARED_PRIMARY",
"pitm",
pitm.payload + 4L,
Some(primary),
"主图 item 未在 iinf 中声明",
)
avif_need(
items.get(primary) == Some("av01"),
"UNSUPPORTED_PRIMARY_TYPE",
"pitm",
pitm.payload + 4L,
Some(primary),
"当前结构子集只支持直接 av01 主图,未支持 grid 等派生主图",
)
let iprp = avif_required_node(children, "iprp", meta)
let property_boxes = avif_nodes(prefix, iprp.payload, iprp.end, budget)
let ipco = avif_required_node(property_boxes, "ipco", iprp)
for child in property_boxes {
if child.kind == "free" || child.kind == "skip" {
continue
}
avif_need(
child.kind == "ipco",
"PROPERTY_BOX_ORDER",
"iprp",
child.header,
None,
"ipco 必须位于属性关联盒之前",
)
break
}
let properties = avif_nodes(prefix, ipco.payload, ipco.end, budget)
let associations = avif_associations(
prefix, property_boxes, properties, items, budget,
)
let selected = match associations.get(primary) {
Some(indexes) => indexes
None =>
avif_fault(
"MISSING_PRIMARY_ASSOCIATION",
"iprp",
iprp.payload,
Some(primary),
"主图没有属性关联条目",
)
}
let (width, height, primary_properties) = avif_primary_properties(
prefix, properties, selected, primary, ipco,
)
let media : Array[AvifNode] = []
for node in top {
if node.kind == "mdat" {
media.push(node)
}
}
let extents = avif_locations(
prefix,
avif_required_node(children, "iloc", meta),
items,
primary,
media,
avif_optional_node(children, "idat"),
file_size,
budget,
)
{
primary_item_id: primary,
width,
height,
item_count: items.length(),
primary_properties,
extents,
}
}
///|
/// Inspect a complete file's leading prefix plus its actual file size.
/// Limits: 4 MiB prefix; 4096 visited boxes, associations and extents;
/// 256 declared items. All required metadata and top-level box headers must
/// be in the prefix; a final mdat payload may extend beyond it.
pub fn inspect_avif_structure(
prefix : Bytes,
file_size : Int64,
) -> Result[AvifStructure, AvifDiagnostic] {
let structure = avif_inspect(prefix, file_size) catch {
AvifStructureFailure::AvifFault(diagnostic) => return Err(diagnostic)
}
Ok(structure)
}
///|
fn avif_diagnostic_message(diagnostic : AvifDiagnostic) -> String {
let item = match diagnostic.item_id {
Some(id) => " item=\{id}"
None => ""
}
"AVIF[\{diagnostic.code}] box=\{diagnostic.box_type} offset=\{diagnostic.offset}\{item}: \{diagnostic.detail}"
}