Repository: image-rs/image-tiff
Pull request #119 (2021-03-17 00:28:29)

Implement missing tag types and signed 64-bit support


Patch:
diff --git a/src/bytecast.rs b/src/bytecast.rs
index ff14d3c..6e9d762 100644
--- a/src/bytecast.rs
+++ b/src/bytecast.rs
@@ -29,5 +29,6 @@ integral_slice_as_bytes!(i16, i16_as_ne_bytes, i16_as_ne_mut_bytes);
 integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes);
 integral_slice_as_bytes!(i32, i32_as_ne_bytes, i32_as_ne_mut_bytes);
 integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes);
+integral_slice_as_bytes!(i64, i64_as_ne_bytes, i64_as_ne_mut_bytes);
 integral_slice_as_bytes!(f32, f32_as_ne_bytes, f32_as_ne_mut_bytes);
 integral_slice_as_bytes!(f64, f64_as_ne_bytes, f64_as_ne_mut_bytes);
diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs
index 383792d..886fbeb 100644
--- a/src/decoder/ifd.rs
+++ b/src/decoder/ifd.rs
@@ -10,8 +10,8 @@ use crate::tags::{Tag, Type};
 use crate::{TiffError, TiffFormatError, TiffResult};
 
 use self::Value::{
-    Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Short,
-    Signed, SignedBig, Unsigned, UnsignedBig,
+    Ascii, Byte, Double, Float, Ifd, IfdBig, List, Rational, RationalBig, SRational, SRationalBig,
+    Short, Signed, SignedBig, Unsigned, UnsignedBig,
 };
 
 #[allow(unused_qualifications)]
@@ -31,6 +31,8 @@ pub enum Value {
     SRational(i32, i32),
     SRationalBig(i64, i64),
     Ascii(String),
+    Ifd(u32),
+    IfdBig(u64),
     #[doc(hidden)] // Do not match against this.
     __NonExhaustive,
 }
@@ -59,6 +61,8 @@ impl Value {
             Short(val) => Ok(val.into()),
             Unsigned(val) => Ok(val),
             UnsignedBig(val) => Ok(u32::try_from(val)?),
+            Ifd(val) => Ok(val),
+            IfdBig(val) => Ok(u32::try_from(val)?),
             val => Err(TiffError::FormatError(
                 TiffFormatError::UnsignedIntegerExpected(val),
             )),
@@ -80,6 +84,8 @@ impl Value {
             Short(val) => Ok(val.into()),
             Unsigned(val) => Ok(val.into()),
             UnsignedBig(val) => Ok(val),
+            Ifd(val) => Ok(val.into()),
+            IfdBig(val) => Ok(val),
             val => Err(TiffError::FormatError(
                 TiffFormatError::UnsignedIntegerExpected(val),
             )),
@@ -138,6 +144,8 @@ impl Value {
             RationalBig(numerator, denominator) => {
                 Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?])
             }
+            Ifd(val) => Ok(vec![val]),
+            IfdBig(val) => Ok(vec![u32::try_from(val)?]),
             Ascii(val) => Ok(val.chars().map(u32::from).collect()),
             val => Err(TiffError::FormatError(
                 TiffFormatError::UnsignedIntegerExpected(val),
@@ -254,6 +262,8 @@ impl Value {
             UnsignedBig(val) => Ok(vec![val]),
             Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
             RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
+            Ifd(val) => Ok(vec![val.into()]),
+            IfdBig(val) => Ok(vec![val]),
             Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()),
             val => Err(TiffError::FormatError(
                 TiffFormatError::UnsignedIntegerExpected(val),
@@ -342,8 +352,13 @@ impl Entry {
             * match self.type_ {
                 Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
                 Type::SHORT | Type::SSHORT => 2,
-                Type::LONG | Type::SLONG | Type::FLOAT => 4,
-                Type::LONG8 | Type::DOUBLE | Type::RATIONAL | Type::SRATIONAL => 8,
+                Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
+                Type::LONG8
+                | Type::SLONG8
+                | Type::DOUBLE
+                | Type::RATIONAL
+                | Type::SRATIONAL
+                | Type::IFD8 => 8,
                 Type::__NonExhaustive => unreachable!(),
             };
 
@@ -353,6 +368,7 @@ impl Entry {
             if decoder.bigtiff && value_bytes > 4 && value_bytes <= 8 {
                 return Ok(match self.type_ {
                     Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?),
+                    Type::SLONG8 => SignedBig(self.r(bo).read_i64()?),
                     Type::DOUBLE => Double(self.r(bo).read_f64()?),
                     Type::RATIONAL => {
                         let mut r = self.r(bo);
@@ -362,6 +378,7 @@ impl Entry {
                         let mut r = self.r(bo);
                         SRational(r.read_i32()?, r.read_i32()?)
                     }
+                    Type::IFD8 => IfdBig(self.r(bo).read_u64()?),
                     Type::BYTE
                     | Type::SBYTE
                     | Type::ASCII
@@ -370,7 +387,8 @@ impl Entry {
                     | Type::SSHORT
                     | Type::LONG
                     | Type::SLONG
-                    | Type::FLOAT => unreachable!(),
+                    | Type::FLOAT
+                    | Type::IFD => unreachable!(),
                     Type::__NonExhaustive => unreachable!(),
                 });
             }
@@ -396,6 +414,10 @@ impl Entry {
                     decoder.goto_offset(self.r(bo).read_u32()?)?;
                     UnsignedBig(decoder.read_long8()?)
                 }
+                Type::SLONG8 => {
+                    decoder.goto_offset(self.r(bo).read_u32()?)?;
+                    SignedBig(decoder.read_slong8()?)
+                }
                 Type::DOUBLE => {
                     decoder.goto_offset(self.r(bo).read_u32()?)?;
                     Double(decoder.read_double()?)
@@ -408,6 +430,11 @@ impl Entry {
                     decoder.goto_offset(self.r(bo).read_u32()?)?;
                     SRational(decoder.read_slong()?, decoder.read_slong()?)
                 }
+                Type::IFD => Ifd(self.r(bo).read_u32()?),
+                Type::IFD8 => {
+                    decoder.goto_offset(self.r(bo).read_u32()?)?;
+                    IfdBig(decoder.read_long8()?)
+                }
                 Type::__NonExhaustive => unreachable!(),
             });
         }
@@ -472,7 +499,22 @@ impl Entry {
                     }
                     return Ok(List(v));
                 }
-                Type::LONG8 | Type::RATIONAL | Type::SRATIONAL | Type::DOUBLE => unreachable!(),
+                Type::IFD => {
+                    let mut r = self.r(bo);
+                    let mut v = Vec::new();
+                    for _ in 0..self.count {
+                        v.push(Ifd(r.read_u32()?));
+                    }
+                    return Ok(List(v));
+                }
+                Type::LONG8
+                | Type::SLONG8
+                | Type::RATIONAL
+                | Type::SRATIONAL
+                | Type::DOUBLE
+                | Type::IFD8 => {
+                    unreachable!()
+                }
                 Type::__NonExhaustive => unreachable!(),
             }
         }
@@ -514,6 +556,15 @@ impl Entry {
             Type::LONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
                 Ok(UnsignedBig(decoder.read_long8()?))
             }),
+            Type::SLONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
+                Ok(SignedBig(decoder.read_slong8()?))
+            }),
+            Type::IFD => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
+                Ok(Ifd(decoder.read_long()?))
+            }),
+            Type::IFD8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
+                Ok(IfdBig(decoder.read_long8()?))
+            }),
             Type::UNDEFINED => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
                 Ok(Byte(u8::from(decoder.read_byte()?)))
             }),
diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs
index 8d4bf33..c345410 100644
--- a/src/decoder/mod.rs
+++ b/src/decoder/mod.rs
@@ -38,6 +38,8 @@ pub enum DecodingResult {
     I16(Vec<i16>),
     /// A vector of 32 bit signed ints
     I32(Vec<i32>),
+    /// A vector of 64 bit signed ints
+    I64(Vec<i64>),
 }
 
 impl DecodingResult {
@@ -113,6 +115,14 @@ impl DecodingResult {
         }
     }
 
+    fn new_i64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
+        if size > limits.decoding_buffer_size / 8 {
+            Err(TiffError::LimitsExceeded)
+        } else {
+            Ok(DecodingResult::I64(vec![0; size]))
+        }
+    }
+
     pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
         match *self {
             DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
@@ -124,6 +134,7 @@ impl DecodingResult {
             DecodingResult::I8(ref mut buf) => DecodingBuffer::I8(&mut buf[start..]),
             DecodingResult::I16(ref mut buf) => DecodingBuffer::I16(&mut buf[start..]),
             DecodingResult::I32(ref mut buf) => DecodingBuffer::I32(&mut buf[start..]),
+            DecodingResult::I64(ref mut buf) => DecodingBuffer::I64(&mut buf[start..]),
         }
     }
 }
@@ -146,8 +157,10 @@ pub enum DecodingBuffer<'a> {
     I8(&'a mut [i8]),
     /// A slice of 16 bits signed ints
     I16(&'a mut [i16]),
-    /// A slice of 16 bits signed ints
+    /// A slice of 32 bits signed ints
     I32(&'a mut [i32]),
+    /// A slice of 64 bits signed ints
+    I64(&'a mut [i64]),
 }
 
 impl<'a> DecodingBuffer<'a> {
@@ -162,6 +175,7 @@ impl<'a> DecodingBuffer<'a> {
             DecodingBuffer::I8(ref buf) => buf.len(),
             DecodingBuffer::I16(ref buf) => buf.len(),
             DecodingBuffer::I32(ref buf) => buf.len(),
+            DecodingBuffer::I64(ref buf) => buf.len(),
         }
     }
 
@@ -176,6 +190,7 @@ impl<'a> DecodingBuffer<'a> {
             DecodingBuffer::I8(_) => 1,
             DecodingBuffer::I16(_) => 2,
             DecodingBuffer::I32(_) => 4,
+            DecodingBuffer::I64(_) => 8,
         }
     }
 
@@ -193,6 +208,7 @@ impl<'a> DecodingBuffer<'a> {
             DecodingBuffer::I8(ref mut buf) => DecodingBuffer::I8(buf),
             DecodingBuffer::I16(ref mut buf) => DecodingBuffer::I16(buf),
             DecodingBuffer::I32(ref mut buf) => DecodingBuffer::I32(buf),
+            DecodingBuffer::I64(ref mut buf) => DecodingBuffer::I64(buf),
         }
     }
 }
@@ -323,6 +339,12 @@ impl Wrapping for i32 {
     }
 }
 
+impl Wrapping for i64 {
+    fn wrapping_add(&self, other: Self) -> Self {
+        i64::wrapping_add(*self, other)
+    }
+}
+
 fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()>
 where
     T: Copy + Wrapping,
@@ -395,6 +417,9 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
         DecodingBuffer::I32(buf) => {
             rev_hpredict_nsamp(buf, size, samples)?;
         }
+        DecodingBuffer::I64(buf) => {
+            rev_hpredict_nsamp(buf, size, samples)?;
+        }
     }
     Ok(())
 }
@@ -642,6 +667,11 @@ impl<R: Read + Seek> Decoder<R> {
         self.reader.read_u64()
     }
 
+    #[inline]
+    pub fn read_slong8(&mut self) -> Result<i64, io::Error> {
+        self.reader.read_i64()
+    }
+
     /// Reads a string
     #[inline]
     pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
@@ -969,6 +999,10 @@ impl<R: Read + Seek> Decoder<R> {
                 }
                 bytes / 8
             }
+            (ColorType::Gray(64), DecodingBuffer::I64(ref mut buffer)) => {
+                reader.read_i64_into(&mut buffer[..bytes / 8])?;
+                bytes / 8
+            }
             (ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => {
                 reader.read_u32_into(&mut buffer[..bytes / 4])?;
                 if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
@@ -1222,8 +1256,9 @@ impl<R: Read + Seek> Decoder<R> {
             },
             SampleFormat::Int => match max_sample_bits {
                 n if n <= 8 => DecodingResult::new_i8(buffer_size, &self.limits),
-                n if n == 16 => DecodingResult::new_i16(buffer_size, &self.limits),
-                n if (n > 16) & (n <= 32) => DecodingResult::new_i32(buffer_size, &self.limits),
+                n if n <= 16 => DecodingResult::new_i16(buffer_size, &self.limits),
+                n if n <= 32 => DecodingResult::new_i32(buffer_size, &self.limits),
+                n if n <= 64 => DecodingResult::new_i64(buffer_size, &self.limits),
                 n => Err(TiffError::UnsupportedError(
                     TiffUnsupportedError::UnsupportedBitsPerChannel(n),
                 )),
diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs
index cc5b31c..ce8d4fc 100644
--- a/src/decoder/stream.rs
+++ b/src/decoder/stream.rs
@@ -193,6 +193,35 @@ pub trait EndianReader: Read {
         Ok(())
     }
 
+    #[inline(always)]
+    fn read_i64_into(&mut self, buffer: &mut [i64]) -> Result<(), io::Error> {
+        self.read_exact(bytecast::i64_as_ne_mut_bytes(buffer))?;
+        match self.byte_order() {
+            ByteOrder::LittleEndian => {
+                for n in buffer {
+                    *n = i64::from_le(*n);
+                }
+            }
+            ByteOrder::BigEndian => {
+                for n in buffer {
+                    *n = i64::from_be(*n);
+                }
+            }
+        }
+        Ok(())
+    }
+
+    /// Reads an i64
+    #[inline(always)]
+    fn read_i64(&mut self) -> Result<i64, io::Error> {
+        let mut n = [0u8; 8];
+        self.read_exact(&mut n)?;
+        Ok(match self.byte_order() {
+            ByteOrder::LittleEndian => i64::from_le_bytes(n),
+            ByteOrder::BigEndian => i64::from_be_bytes(n),
+        })
+    }
+
     /// Reads an f32
     #[inline(always)]
     fn read_f32(&mut self) -> Result<f32, io::Error> {
diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs
index 301732e..71aba00 100644
--- a/src/encoder/mod.rs
+++ b/src/encoder/mod.rs
@@ -13,6 +13,14 @@ mod writer;
 use self::colortype::*;
 use self::writer::*;
 
+/// Type to represent tiff values of type `IFD`
+#[derive(Clone)]
+pub struct Ifd(pub u32);
+
+/// Type to represent tiff values of type `IFD8`
+#[derive(Clone)]
+pub struct Ifd8(pub u64);
+
 /// Type to represent tiff values of type `RATIONAL`
 #[derive(Clone)]
 pub struct Rational {
@@ -21,6 +29,7 @@ pub struct Rational {
 }
 
 /// Type to represent tiff values of type `SRATIONAL`
+#[derive(Clone)]
 pub struct SRational {
     pub n: i32,
     pub d: i32,
@@ -141,6 +150,21 @@ impl TiffValue for [u64] {
     }
 }
 
+impl TiffValue for [i64] {
+    const BYTE_LEN: u32 = 8;
+    const FIELD_TYPE: Type = Type::SLONG8;
+
+    fn count(&self) -> u32 {
+        self.len() as u32
+    }
+
+    fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
+        let slice = bytecast::i64_as_ne_bytes(self);
+        writer.write_bytes(slice)?;
+        Ok(())
+    }
+}
+
 impl TiffValue for [f32] {
     const BYTE_LEN: u32 = 4;
     const FIELD_TYPE: Type = Type::FLOAT;
@@ -173,6 +197,38 @@ impl TiffValue for [f64] {
     }
 }
 
+impl TiffValue for [Ifd] {
+    const BYTE_LEN: u32 = 4;
+    const FIELD_TYPE: Type = Type::IFD;
+
+    fn count(&self) -> u32 {
+        self.len() as u32
+    }
+
+    fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
+        for x in self {
+            x.write(writer)?;
+        }
+        Ok(())
+    }
+}
+
+impl TiffValue for [Ifd8] {
+    const BYTE_LEN: u32 = 8;
+    const FIELD_TYPE: Type = Type::IFD8;
+
+    fn count(&self) -> u32 {
+        self.len() as u32
+    }
+
+    fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
+        for x in self {
+            x.write(writer)?;
+        }
+        Ok(())
+    }
+}
+
 impl TiffValue for [Rational] {
     const BYTE_LEN: u32 = 8;
     const FIELD_TYPE: Type = Type::RATIONAL;
@@ -303,6 +359,20 @@ impl TiffValue for u64 {
     }
 }
 
+impl TiffValue for i64 {
+    const BYTE_LEN: u32 = 8;
+    const FIELD_TYPE: Type = Type::SLONG8;
+
+    fn count(&self) -> u32 {
+        1
+    }
+
+    fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
+        writer.write_i64(*self)?;
+        Ok(())
+    }
+}
+
 impl TiffValue for f32 {
     const BYTE_LEN: u32 = 4;
     const FIELD_TYPE: Type = Type::FLOAT;
@@ -331,6 +401,34 @@ impl TiffValue for f64 {
     }
 }
 
+impl TiffValue for Ifd {
+    const BYTE_LEN: u32 = 4;
+    const FIELD_TYPE: Type = Type::IFD;
+
+    fn count(&self) -> u32 {
+        1
+    }
+
+    fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
+        writer.write_u32(self.0)?;
+        Ok(())
+    }
+}
+
+impl TiffValue for Ifd8 {
+    const BYTE_LEN: u32 = 8;
+    const FIELD_TYPE: Type = Type::IFD8;
+
+    fn count(&self) -> u32 {
+        1
+    }
+
+    fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
+        writer.write_u64(self.0)?;
+        Ok(())
+    }
+}
+
 impl TiffValue for Rational {
     const BYTE_LEN: u32 = 8;
     const FIELD_TYPE: Type = Type::RATIONAL;
diff --git a/src/encoder/writer.rs b/src/encoder/writer.rs
index e3fba33..a8e61a7 100644
--- a/src/encoder/writer.rs
+++ b/src/encoder/writer.rs
@@ -81,6 +81,13 @@ impl<W: Write> TiffWriter<W> {
         Ok(())
     }
 
+    pub fn write_i64(&mut self, n: i64) -> Result<(), io::Error> {
+        self.writer.write_all(&n.to_ne_bytes())?;
+        self.offset += 8;
+
+        Ok(())
+    }
+
     pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> {
         self.writer.write_all(&u32::to_ne_bytes(n.to_bits()))?;
         self.offset += 4;
diff --git a/src/tags.rs b/src/tags.rs
index 5da23f4..4a0b50a 100644
--- a/src/tags.rs
+++ b/src/tags.rs
@@ -134,20 +134,38 @@ pub enum Tag(u16) unknown("A private or extension tag") {
 tags! {
 /// The type of an IFD entry (a 2 byte field).
 pub enum Type(u16) {
+    /// 8-bit unsigned integer
     BYTE = 1,
+    /// 8-bit byte that contains a 7-bit ASCII code; the last byte must be zero
     ASCII = 2,
+    /// 16-bit unsigned integer
     SHORT = 3,
+    /// 32-bit unsigned integer
     LONG = 4,
+    /// Fraction stored as two 32-bit unsigned integers
     RATIONAL = 5,
+    /// 8-bit signed integer
     SBYTE = 6,
+    /// 8-bit byte that may contain anything, depending on the field
     UNDEFINED = 7,
+    /// 16-bit signed integer
     SSHORT = 8,
+    /// 32-bit signed integer
     SLONG = 9,
+    /// Fraction stored as two 32-bit signed integers
     SRATIONAL = 10,
+    /// 32-bit IEEE floating point
     FLOAT = 11,
+    /// 64-bit IEEE floating point
     DOUBLE = 12,
+    /// 32-bit unsigned integer (offset)
+    IFD = 13,
     /// BigTIFF 64-bit unsigned integer
     LONG8 = 16,
+    /// BigTIFF 64-bit signed integer
+    SLONG8 = 17,
+    /// BigTIFF 64-bit unsigned integer (offset)
+    IFD8 = 18,
 }
 }
 


Test patch:
diff --git a/tests/encode_images.rs b/tests/encode_images.rs
index 7ba8eb3..4230935 100644
--- a/tests/encode_images.rs
+++ b/tests/encode_images.rs
@@ -1,7 +1,7 @@
 extern crate tiff;
 
 use tiff::decoder::{ifd, Decoder, DecodingResult};
-use tiff::encoder::{colortype, SRational, TiffEncoder};
+use tiff::encoder::{colortype, Ifd, Ifd8, SRational, TiffEncoder};
 use tiff::tags::Tag;
 use tiff::ColorType;
 
@@ -48,6 +48,53 @@ fn encode_decode() {
     }
 }
 
+#[test]
+fn test_encode_ifd() {
+    let mut data = Cursor::new(Vec::new());
+
+    {
+        let mut tiff = TiffEncoder::new(&mut data).unwrap();
+        let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
+        let encoder = image_encoder.encoder();
+
+        // Use the "reusable" tags section as per the TIFF6 spec
+        encoder.write_tag(Tag::Unknown(65000), Ifd(42u32)).unwrap();
+        encoder
+            .write_tag(Tag::Unknown(65001), &[Ifd(100u32)][..])
+            .unwrap();
+        encoder
+            .write_tag(Tag::Unknown(65002), &[Ifd(1u32), Ifd(2u32), Ifd(3u32)][..])
+            .unwrap();
+
+        encoder.write_tag(Tag::Unknown(65010), Ifd8(43u64)).unwrap();
+        encoder
+            .write_tag(Tag::Unknown(65011), &[Ifd8(100u64)][..])
+            .unwrap();
+        encoder
+            .write_tag(
+                Tag::Unknown(65012),
+                &[Ifd8(1u64), Ifd8(2u64), Ifd8(3u64)][..],
+            )
+            .unwrap();
+    }
+
+    // Rewind the cursor for reading
+    data.set_position(0);
+    {
+        let mut decoder = Decoder::new(&mut data).unwrap();
+
+        assert_eq!(decoder.assert_tag_u32(65000), 42);
+        assert_eq!(decoder.assert_tag_u32_vec(65000), [42]);
+        assert_eq!(decoder.assert_tag_u32_vec(65001), [100]);
+        assert_eq!(decoder.assert_tag_u32_vec(65002), [1, 2, 3]);
+
+        assert_eq!(decoder.assert_tag_u64(65010), 43);
+        assert_eq!(decoder.assert_tag_u64_vec(65010), [43]);
+        assert_eq!(decoder.assert_tag_u64_vec(65011), [100]);
+        assert_eq!(decoder.assert_tag_u64_vec(65012), [1, 2, 3]);
+    }
+}
+
 #[test]
 /// Test that attempting to encode when the input buffer is undersized returns
 /// an error rather than panicking.
@@ -182,6 +229,10 @@ trait AssertDecode {
     fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32>;
     fn assert_tag_i32(&mut self, tag: u16) -> i32;
     fn assert_tag_i32_vec(&mut self, tag: u16) -> Vec<i32>;
+    fn assert_tag_u64(&mut self, tag: u16) -> u64;
+    fn assert_tag_u64_vec(&mut self, tag: u16) -> Vec<u64>;
+    fn assert_tag_i64(&mut self, tag: u16) -> i64;
+    fn assert_tag_i64_vec(&mut self, tag: u16) -> Vec<i64>;
 }
 
 impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
@@ -203,6 +254,24 @@ impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
             .into_i32_vec()
             .unwrap()
     }
+    fn assert_tag_u64(&mut self, tag: u16) -> u64 {
+        self.get_tag(Tag::Unknown(tag)).unwrap().into_u64().unwrap()
+    }
+    fn assert_tag_u64_vec(&mut self, tag: u16) -> Vec<u64> {
+        self.get_tag(Tag::Unknown(tag))
+            .unwrap()
+            .into_u64_vec()
+            .unwrap()
+    }
+    fn assert_tag_i64(&mut self, tag: u16) -> i64 {
+        self.get_tag(Tag::Unknown(tag)).unwrap().into_i64().unwrap()
+    }
+    fn assert_tag_i64_vec(&mut self, tag: u16) -> Vec<i64> {
+        self.get_tag(Tag::Unknown(tag))
+            .unwrap()
+            .into_i64_vec()
+            .unwrap()
+    }
 }
 
 #[test]
@@ -289,12 +358,20 @@ fn test_signed() {
             .write_tag(Tag::Unknown(65022), &[-1_i32, 2][..])
             .unwrap();
 
+        encoder.write_tag(Tag::Unknown(65030), -1_i64).unwrap();
         encoder
-            .write_tag(Tag::Unknown(65030), make_srational(-1))
+            .write_tag(Tag::Unknown(65031), &[-1_i64][..])
+            .unwrap();
+        encoder
+            .write_tag(Tag::Unknown(65032), &[-1_i64, 2][..])
+            .unwrap();
+
+        encoder
+            .write_tag(Tag::Unknown(65040), make_srational(-1))
             .unwrap();
         encoder
             .write_tag(
-                Tag::Unknown(65031),
+                Tag::Unknown(65041),
                 &[make_srational(-1), make_srational(2)][..],
             )
             .unwrap();
@@ -321,8 +398,12 @@ fn test_signed() {
         assert_eq!(decoder.assert_tag_i32_vec(65021), [-1]);
         assert_eq!(decoder.assert_tag_i32_vec(65022), [-1, 2]);
 
-        assert_eq!(decoder.assert_tag_i32_vec(65030), [-1, 100]);
-        assert_eq!(decoder.assert_tag_i32_vec(65031), [-1_i32, 100, 2, 100]);
+        assert_eq!(decoder.assert_tag_i64(65030), -1);
+        assert_eq!(decoder.assert_tag_i64_vec(65031), [-1]);
+        assert_eq!(decoder.assert_tag_i64_vec(65032), [-1, 2]);
+
+        assert_eq!(decoder.assert_tag_i32_vec(65040), [-1, 100]);
+        assert_eq!(decoder.assert_tag_i32_vec(65041), [-1_i32, 100, 2, 100]);
     }
 }