Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/lazy/binary/raw/v1_1/binary_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,22 +258,23 @@ impl<'a> BinaryBuffer<'a> {
Ok((value, remaining_input))
}

pub fn read_fixed_uint_as_lazy_value(self, encoding: BinaryValueEncoding) -> ParseResult<'a, LazyRawBinaryValue_1_1<'a>> {
pub fn read_fixed_int_type_as_lazy_value(self, encoding: BinaryValueEncoding) -> ParseResult<'a, LazyRawBinaryValue_1_1<'a>> {
use BinaryValueEncoding::*;
let size_in_bytes = match encoding {
BinaryValueEncoding::UInt8 => 1,
BinaryValueEncoding::UInt16 => 2,
BinaryValueEncoding::UInt32 => 4,
BinaryValueEncoding::UInt64 => 8,
_ => return IonResult::illegal_operation(format!("invalid binary encoding for fixed uint: {encoding:?}")),
UInt8 | Int8 => 1,
UInt16 | Int16 => 2,
UInt32 | Int32 => 4,
UInt64 | Int64 => 8,
_ => return IonResult::illegal_operation(format!("invalid binary encoding for fixed int or uint: {encoding:?}")),
};

if self.len() < size_in_bytes {
return IonResult::incomplete("a uint", self.offset());
return IonResult::incomplete("a(n) uint/int", self.offset());
}

let matched_input = self.slice(0, size_in_bytes);
let remaining_input = self.slice_to_end(size_in_bytes);
let value = LazyRawBinaryValue_1_1::for_fixed_uint(matched_input, encoding);
let value = LazyRawBinaryValue_1_1::for_fixed_int_type(matched_input, encoding);
Ok((value, remaining_input))
}

Expand Down
8 changes: 6 additions & 2 deletions src/lazy/binary/raw/v1_1/e_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,15 @@ impl<'top> Iterator for BinaryEExpArgsInputIter<'top> {
enc@ ParameterEncoding::UInt8 |
enc@ ParameterEncoding::UInt16 |
enc@ ParameterEncoding::UInt32 |
enc@ ParameterEncoding::UInt64
enc@ ParameterEncoding::UInt64 |
enc@ ParameterEncoding::Int8 |
enc@ ParameterEncoding::Int16 |
enc@ ParameterEncoding::Int32 |
enc@ ParameterEncoding::Int64
=> {
let binary_enc = try_or_some_err!(enc.try_into());
let (fixed_uint_lazy_value, remaining) = try_or_some_err! {
self.remaining_args_buffer.read_fixed_uint_as_lazy_value(binary_enc)
self.remaining_args_buffer.read_fixed_int_type_as_lazy_value(binary_enc)
};
let value_ref = &*self
.remaining_args_buffer
Expand Down
6 changes: 5 additions & 1 deletion src/lazy/binary/raw/v1_1/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ pub enum BinaryValueEncoding {
UInt16,
UInt32,
UInt64,
Int8,
Int16,
Int32,
Int64,
}

#[derive(Debug, Copy, Clone)]
Expand Down Expand Up @@ -364,7 +368,7 @@ impl<'top> LazyRawBinaryValue_1_1<'top> {
}
}

pub(crate) fn for_fixed_uint(input: BinaryBuffer<'top>, encoding: BinaryValueEncoding) -> Self {
pub(crate) fn for_fixed_int_type(input: BinaryBuffer<'top>, encoding: BinaryValueEncoding) -> Self {
let encoded_value = EncodedBinaryValue {
encoding,
header: Header {
Expand Down
25 changes: 24 additions & 1 deletion src/lazy/encoder/binary/v1_1/fixed_int.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::io::Write;

use crate::decimal::coefficient::Coefficient;
use crate::result::IonFailure;
use crate::result::{IonError, IonFailure};
use crate::{Int, IonResult};

use num_traits::{PrimInt, Signed};

/// An Ion 1.1 encoding primitive that represents a fixed-length signed integer.
#[derive(Debug)]
pub struct FixedInt {
Expand Down Expand Up @@ -100,6 +102,27 @@
Self::write_i128(output, value.data)
}

/// Writes the given Into<Int> as a taggless int value with the specified primitive size

Check warning on line 105 in src/lazy/encoder/binary/v1_1/fixed_int.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, all)

unclosed HTML tag `Int`

Check warning on line 105 in src/lazy/encoder/binary/v1_1/fixed_int.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, all)

unclosed HTML tag `Int`
/// represented by I.
#[inline]
pub fn write_as_int<I: PrimInt + Signed>(output: &mut impl Write, value: impl Into<Int>) -> IonResult<()> {
let size_in_bytes = std::mem::size_of::<I>();
let value: i128 = value.into().data;
let encoded_bytes = value.to_le_bytes();
let max_value: i128 = num_traits::cast(I::max_value())
.ok_or(IonError::encoding_error("Unable to represent bounds for value as 128bit value"))?;
let min_value: i128 = num_traits::cast(I::min_value())
.ok_or(IonError::encoding_error("Unable to represent bounds for value as 128bit value"))?;

if !(min_value..=max_value).contains(&value) {
return IonResult::encoding_error(format!("provided signed integer value does not fit within {size_in_bytes} bytes(s)"));
}

output.write_all(&encoded_bytes[..size_in_bytes])?;

Ok(())
}

#[inline]
pub fn encoded_size(value: impl Into<Int>) -> usize {
let value = value.into();
Expand Down
8 changes: 8 additions & 0 deletions src/lazy/encoder/binary/v1_1/value_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,10 @@ impl<'value, 'top> ValueWriter for BinaryEExpParameterValueWriter_1_1<'value, 't
PE::UInt64 => value
.try_into()
.and_then(|uint: UInt| FixedUInt::write_as_uint::<u64>(self.buffer, uint)),
PE::Int8 => FixedInt::write_as_int::<i8>(self.buffer, value),
PE::Int16 => FixedInt::write_as_int::<i16>(self.buffer, value),
PE::Int32 => FixedInt::write_as_int::<i32>(self.buffer, value),
PE::Int64 => FixedInt::write_as_int::<i64>(self.buffer, value),
PE::FlexUInt => value
.try_into()
.and_then(|uint: UInt| FlexUInt::write(self.buffer, uint))
Expand Down Expand Up @@ -1135,6 +1139,10 @@ impl<'value, 'top> ValueWriter for BinaryEExpParameterValueWriter_1_1<'value, 't
PE::UInt64 => value
.try_into()
.and_then(|uint: UInt| FixedUInt::write_as_uint::<u64>(self.buffer, uint)),
PE::Int8 => FixedInt::write_as_int::<i8>(self.buffer, value),
PE::Int16 => FixedInt::write_as_int::<i16>(self.buffer, value),
PE::Int32 => FixedInt::write_as_int::<i32>(self.buffer, value),
PE::Int64 => FixedInt::write_as_int::<i64>(self.buffer, value),
PE::FlexUInt => value
.try_into()
.and_then(|uint: UInt| FlexUInt::write(self.buffer, uint))
Expand Down
82 changes: 80 additions & 2 deletions src/lazy/encoder/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@

/// Returns a reference to the macro signature parameter for which the next argument corresponds.
/// If no more parameters remain in the signature, returns `None`.
pub fn current_parameter(&self) -> Option<&Parameter> {

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (al2-arm, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (al2-x86, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (windows, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (al2-arm, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (al2-x86, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, default)

methods `current_parameter` and `expect_next_parameter` are never used

Check warning on line 947 in src/lazy/encoder/writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (windows, default)

methods `current_parameter` and `expect_next_parameter` are never used
self.raw_eexp_writer.current_parameter()
}

Expand Down Expand Up @@ -1375,7 +1375,7 @@

mod eexp_parameter_validation {
use super::*;
use num_traits::{PrimInt, Unsigned};
use num_traits::{PrimInt, Signed, Unsigned};
use rstest::*;

#[test]
Expand Down Expand Up @@ -1508,7 +1508,15 @@
#[case::uint16("(macro foo (uint16::x) (%x))", 5, "5")]
#[case::uint32("(macro foo (uint32::x) (%x))", 5, "5")]
#[case::uint64("(macro foo (uint64::x) (%x))", 5, "5")]
fn tagless_uint_encoding(#[case] macro_source: &str, #[case] input: i64, #[case] expected: &str) -> IonResult<()> {
#[case::int8_neg("(macro foo (int8::x) (%x))", -5, "-5")]
#[case::int8_pos("(macro foo (int8::x) (%x))", 5, "5")]
#[case::int16_neg("(macro foo (int16::x) (%x))", -5, "-5")]
#[case::int16_pos("(macro foo (int16::x) (%x))", 5, "5")]
#[case::int32_neg("(macro foo (int32::x) (%x))", -5, "-5")]
#[case::int32_pos("(macro foo (int32::x) (%x))", 5, "5")]
#[case::int64_neg("(macro foo (int64::x) (%x))", -5, "-5")]
#[case::int64_pos("(macro foo (int64::x) (%x))", 5, "5")]
fn tagless_int_type_encoding(#[case] macro_source: &str, #[case] input: i64, #[case] expected: &str) -> IonResult<()> {
use crate::{Int, Element};

// write_int
Expand Down Expand Up @@ -1541,6 +1549,10 @@
Ok(())
}

/// Ensure that writing fixed int values outside the range of the tagless type's size is
/// caught as an error. This test uses the '_input' field to get the type of the primitive
/// we want to write in order to retrieve the maximum values for the primitive via
/// num_traits.
#[rstest]
#[case::uint8("(macro foo (uint8::x) (%x))", 5u8)]
#[case::uint16("(macro foo (uint16::x) (%x))", 5u16)]
Expand All @@ -1567,6 +1579,11 @@
Ok(())
}

/// Ensure that writing fixed int values outside the range of the tagless type's size is
/// caught as an error. This test uses the '_input' field to get the type of the primitive
/// we want to write in order to retrieve the maximum values for the primitive via
/// num_traits. This test differs from the above test in that it uses `write_i64` and
/// cannot handle 64bit values.
#[rstest]
#[case::uint8("(macro foo (uint8::x) (%x))", 5u8)]
#[case::uint16("(macro foo (uint16::x) (%x))", 5u16)]
Expand All @@ -1593,5 +1610,66 @@

Ok(())
}

/// Ensure that writing fixed int values outside the range of the tagless type's size is
/// caught as an error. This test uses the '_input' field to get the type of the primitive
/// we want to write in order to retrieve the minimum and maximum values for the primitive
/// via num_traits.
#[rstest]
#[case::int8("(macro foo (int8::x) (%x))", 5i8)]
#[case::uint16("(macro foo (int16::x) (%x))", 5i16)]
#[case::uint32("(macro foo (int32::x) (%x))", 5i32)]
#[case::uint64("(macro foo (int64::x) (%x))", 5i64)]
fn tagless_int_encoding_write_int_fails<T: PrimInt + Signed>(#[case] macro_source: &str, #[case] _input: T) -> IonResult<()> {
let max_int = T::max_value();
let max_int_plus_one = num_traits::cast::cast::<_, i128>(max_int).unwrap() + 1i128;
let min_int = T::min_value();
let min_int_minus_one = num_traits::cast::cast::<_, i128>(min_int).unwrap() - 1i128;

let mut writer = Writer::new(v1_1::Binary, Vec::new())?;
let foo = writer.compile_macro(macro_source)?;
let mut eexp_writer = writer.eexp_writer(&foo)?;
let result = eexp_writer.write_int(&max_int_plus_one.into());
assert!(result.is_err(), "unexpected success");

let mut writer = Writer::new(v1_1::Binary, Vec::new())?;
let foo = writer.compile_macro(macro_source)?;
let mut eexp_writer = writer.eexp_writer(&foo)?;
let result = eexp_writer.write_int(&min_int_minus_one.into());
assert!(result.is_err(), "unexpected success");

Ok(())
}

/// Ensure that writing fixed int values outside the range of the tagless type's size is
/// caught as an error. This test uses the '_input' field to get the type of the primitive
/// we want to write in order to retrieve the minimum and maximum values for the primitive
/// via num_traits. This test differs from the above test in that it uses `write_i64` and
/// cannot handle 64bit values.
#[rstest]
#[case::int8("(macro foo (int8::x) (%x))", 5i8)]
#[case::uint16("(macro foo (int16::x) (%x))", 5i16)]
#[case::uint32("(macro foo (int32::x) (%x))", 5i32)]
fn tagless_int_encoding_write_i64_fails<T: PrimInt + Signed>(#[case] macro_source: &str, #[case] _input: T) -> IonResult<()> {
let max_int = T::max_value();
let max_int_plus_one = num_traits::cast::cast::<_, i128>(max_int).unwrap() + 1i128;
let min_int = T::min_value();
let min_int_minus_one = num_traits::cast::cast::<_, i128>(min_int).unwrap() - 1i128;

let mut writer = Writer::new(v1_1::Binary, Vec::new())?;
let foo = writer.compile_macro(macro_source)?;
let mut eexp_writer = writer.eexp_writer(&foo)?;
let result = eexp_writer.write_i64(max_int_plus_one.try_into().unwrap());
assert!(result.is_err(), "unexpected success");

let mut writer = Writer::new(v1_1::Binary, Vec::new())?;
let foo = writer.compile_macro(macro_source)?;
let mut eexp_writer = writer.eexp_writer(&foo)?;
let result = eexp_writer.write_i64(min_int_minus_one.try_into().unwrap());
assert!(result.is_err(), "unexpected success");

Ok(())
}

}
}
4 changes: 4 additions & 0 deletions src/lazy/expanded/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ impl TemplateCompiler {
"uint16" => Ok(ParameterEncoding::UInt16),
"uint32" => Ok(ParameterEncoding::UInt32),
"uint64" => Ok(ParameterEncoding::UInt64),
"int8" => Ok(ParameterEncoding::Int8),
"int16" => Ok(ParameterEncoding::Int16),
"int32" => Ok(ParameterEncoding::Int32),
"int64" => Ok(ParameterEncoding::Int64),
_ => IonResult::decoding_error(format!(
"unrecognized encoding '{encoding_name}' specified for parameter"
)),
Expand Down
12 changes: 12 additions & 0 deletions src/lazy/expanded/macro_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,18 @@ fn write_macro_signature_as_ion<V: ValueWriter>(
ParameterEncoding::UInt64 => value_writer
.with_annotations("uint64")?
.write_symbol(param.name())?,
ParameterEncoding::Int8 => value_writer
.with_annotations("int8")?
.write_symbol(param.name())?,
ParameterEncoding::Int16 => value_writer
.with_annotations("int16")?
.write_symbol(param.name())?,
ParameterEncoding::Int32 => value_writer
.with_annotations("int32")?
.write_symbol(param.name())?,
ParameterEncoding::Int64 => value_writer
.with_annotations("int64")?
.write_symbol(param.name())?,
ParameterEncoding::MacroShaped(_) => todo!(),
};
let cardinality_modifier = match param.cardinality() {
Expand Down
12 changes: 12 additions & 0 deletions src/lazy/expanded/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@
UInt16,
UInt32,
UInt64,
Int8,
Int16,
Int32,
Int64,
// TODO: tagless types, including fixed-width types and macros
MacroShaped(Arc<MacroDef>),
}
Expand All @@ -225,6 +229,10 @@
UInt16 => write!(f, "uint16"),
UInt32 => write!(f, "uint32"),
UInt64 => write!(f, "uint64"),
Int8 => write!(f, "int8"),
Int16 => write!(f, "int16"),
Int32 => write!(f, "int32"),
Int64 => write!(f, "int64"),
MacroShaped(m) => write!(f, "{}", m.name().unwrap_or("<anonymous>")),
}
}
Expand All @@ -243,6 +251,10 @@
ParameterEncoding::UInt16 => Ok(BinaryValueEncoding::UInt16),
ParameterEncoding::UInt32 => Ok(BinaryValueEncoding::UInt32),
ParameterEncoding::UInt64 => Ok(BinaryValueEncoding::UInt64),
ParameterEncoding::Int8 => Ok(BinaryValueEncoding::Int8),
ParameterEncoding::Int16 => Ok(BinaryValueEncoding::Int16),
ParameterEncoding::Int32 => Ok(BinaryValueEncoding::Int32),
ParameterEncoding::Int64 => Ok(BinaryValueEncoding::Int64),
}
}
}
Expand Down Expand Up @@ -616,7 +628,7 @@
/// * The next value produced by continuing the evaluation of a macro in progress (when the evaluator is not empty)
/// * The next macro invocation from the stream (when the evaluator is empty)
/// * `None` when the stream and evaluator are both exhausted
pub fn next_value_expr(&mut self) -> Option<IonResult<ValueExpr<'top, D>>> {

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (al2-arm, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (al2-x86, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (windows, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (al2-arm, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (al2-x86, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, experimental-ion-hash)

method `next_value_expr` is never used

Check warning on line 631 in src/lazy/expanded/template.rs

View workflow job for this annotation

GitHub Actions / Build and Test (windows, experimental-ion-hash)

method `next_value_expr` is never used
// If the evaluator's stack is not empty, give it the opportunity to yield a value.
if let Some(value) = try_or_some_err!(self.evaluator.next()) {
return Some(Ok(ValueExpr::ValueLiteral(value)));
Expand Down
Loading