Implement missing tag types and signed 64-bit support

This commit is contained in:
Kal Conley committed 2021-03-17 01:24:32 +01:00
1 parent 2cba04dfe7
commit d086e289dd
8 files changed
+334 -14

No files matched your search

+1
View File
@@ -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);
+57 -6
View File
@@ -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()?)))
}),
+38 -3
View File
@@ -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),
)),
+29
View File
@@ -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> {
+98
View File
@@ -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;
+7
View File
@@ -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;
+18
View File
@@ -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,
}
}
+86 -5
View File
@@ -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]);
}
}