use crate::value::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Variant {
Classic,
#[cfg(feature = "bigtiff")]
Big,
}
impl Variant {
#[must_use]
pub fn magic(self) -> u16 {
match self {
Variant::Classic => 42,
#[cfg(feature = "bigtiff")]
Variant::Big => 43,
}
}
#[must_use]
pub fn header_size(self) -> usize {
match self {
Variant::Classic => 8,
#[cfg(feature = "bigtiff")]
Variant::Big => 16,
}
}
#[must_use]
pub fn entry_size(self) -> usize {
match self {
Variant::Classic => 12,
#[cfg(feature = "bigtiff")]
Variant::Big => 20,
}
}
#[must_use]
pub fn count_size(self) -> usize {
match self {
Variant::Classic => 2,
#[cfg(feature = "bigtiff")]
Variant::Big => 8,
}
}
#[must_use]
pub fn offset_size(self) -> usize {
match self {
Variant::Classic => 4,
#[cfg(feature = "bigtiff")]
Variant::Big => 8,
}
}
#[must_use]
pub fn inline_threshold(self) -> usize {
self.offset_size()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Field {
pub tag: u16,
pub value: Value,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Ifd {
fields: Vec<Field>,
}
impl Ifd {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn fields(&self) -> &[Field] {
&self.fields
}
#[must_use]
pub fn get(&self, tag: u16) -> Option<&Value> {
self.fields
.binary_search_by_key(&tag, |f| f.tag)
.ok()
.map(|i| &self.fields[i].value)
}
#[must_use]
pub fn get_u32(&self, tag: u16) -> Option<u32> {
self.get(tag).and_then(Value::as_u32)
}
#[must_use]
pub fn get_u32_vec(&self, tag: u16) -> Option<Vec<u32>> {
self.get(tag).and_then(Value::as_u32_vec)
}
pub fn set(&mut self, tag: u16, value: Value) {
match self.fields.binary_search_by_key(&tag, |f| f.tag) {
Ok(i) => self.fields[i].value = value,
Err(i) => self.fields.insert(i, Field { tag, value }),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn variant_layout_constants() {
assert_eq!(Variant::Classic.magic(), 42);
assert_eq!(Variant::Classic.header_size(), 8);
assert_eq!(Variant::Classic.entry_size(), 12);
assert_eq!(Variant::Classic.count_size(), 2);
assert_eq!(Variant::Classic.offset_size(), 4);
assert_eq!(Variant::Classic.inline_threshold(), 4);
}
#[cfg(feature = "bigtiff")]
#[test]
fn bigtiff_variant_layout_constants() {
assert_eq!(Variant::Big.magic(), 43);
assert_eq!(Variant::Big.header_size(), 16);
assert_eq!(Variant::Big.entry_size(), 20);
assert_eq!(Variant::Big.count_size(), 8);
assert_eq!(Variant::Big.offset_size(), 8);
assert_eq!(Variant::Big.inline_threshold(), 8);
}
#[test]
fn ifd_keeps_fields_sorted_and_replaces() {
let mut ifd = Ifd::new();
ifd.set(259, Value::Short(vec![1]));
ifd.set(256, Value::Short(vec![4]));
ifd.set(257, Value::Short(vec![3]));
let order: Vec<u16> = ifd.fields().iter().map(|f| f.tag).collect();
assert_eq!(order, vec![256, 257, 259]);
ifd.set(256, Value::Short(vec![8]));
assert_eq!(ifd.get_u32(256), Some(8));
assert_eq!(ifd.fields().len(), 3);
}
}