/// SPSS system files can declare IBM 370 and DEC VAX floating-point
/// representations, but no file that uses either of these has ever been found
/// in the wild, so this code does not handle them.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum Endian {
/// Big-endian: MSB at lowest address.
+ #[cfg_attr(target_endian = "big", default)]
Big,
/// Little-endian: LSB at lowest address.
+ #[cfg_attr(target_endian = "little", default)]
Little,
}
impl Endian {
- #[cfg(target_endian = "big")]
- pub const NATIVE: Endian = Endian::Big;
- #[cfg(target_endian = "little")]
- pub const NATIVE: Endian = Endian::Little;
-
pub fn identify_u32(expected_value: u32, bytes: [u8; 4]) -> Option<Self> {
let as_big: u32 = Endian::Big.parse(bytes);
let as_little: u32 = Endian::Little.parse(bytes);
}
}
- pub fn to_smallvec(self, mut value: u64, n: usize) -> SmallVec<[u8; 16]> {
+ pub fn to_smallvec<const N: usize>(self, mut value: u64, n: usize) -> SmallVec<[u8; N]> {
debug_assert!(n <= 8);
let mut vec = SmallVec::new();
value <<= 8 * (8 - n);
use crate::{
calendar::{calendar_offset_to_gregorian, day_of_year, month_name, short_month_name},
dictionary::Value,
+ endian::ToBytes,
format::{Category, Decimal, Format, NumberStyle, Settings, Type},
- settings::Settings as PsppSettings,
+ settings::{EndianSettings, Settings as PsppSettings},
};
pub struct DisplayValue<'a, 'b> {
format: Format,
settings: &'b Settings,
+ endian: EndianSettings,
value: &'a Value,
encoding: &'static Encoding,
}
impl<'a, 'b> DisplayValue<'a, 'b> {
pub fn new(format: Format, value: &'a Value, encoding: &'static Encoding) -> Self {
+ let settings = PsppSettings::global();
Self {
format,
value,
encoding,
- settings: &PsppSettings::global().formats,
+ settings: &settings.formats,
+ endian: settings.endian,
}
}
pub fn with_settings(self, settings: &'b Settings) -> Self {
Self { settings, ..self }
}
+ pub fn with_endian(self, endian: EndianSettings) -> Self {
+ Self { endian, ..self }
+ }
fn fmt_binary(&self, f: &mut Formatter) -> FmtResult {
let output = self.to_binary().unwrap();
for b in output {
Type::P => Some(self.p(number)),
Type::PK => Some(self.pk(number)),
Type::IB => Some(self.ib(number)),
+ Type::PIB => Some(self.pib(number)),
+ Type::RB => Some(self.rb(number)),
_ => None,
}
}
} else {
integer
};
- PsppSettings::global()
+ self.endian
+ .output_integer_format
+ .to_smallvec(integer, self.format.w())
+ }
+
+ fn pib(&self, number: Option<f64>) -> SmallVec<[u8; 16]> {
+ let number = number.map_or(0.0, |number| (number * power10(self.format.d())).round());
+ let number = if number >= power256(self.format.w) || number < 0.0 {
+ 0.0
+ } else {
+ number
+ };
+ let integer = number.abs() as u64;
+ self.endian
.output_integer_format
.to_smallvec(integer, self.format.w())
}
+
+ fn rb(&self, number: Option<f64>) -> SmallVec<[u8; 16]> {
+ let number = number.unwrap_or(-f64::MAX);
+ let bytes: [u8; 8] = self.endian.output_float_format.to_bytes(number);
+ let mut vec = SmallVec::new();
+ vec.extend_from_slice(&bytes);
+ vec.resize(self.format.w(), 0);
+ vec
+ }
}
struct LegacyFormat {
use crate::{
dictionary::Value,
+ endian::Endian,
format::{AbstractFormat, Format, Settings, Type, UncheckedFormat, CC},
lex::{
scan::StringScanner,
segment::Syntax,
token::{Punct, Token},
},
- settings::Settings as PsppSettings,
+ settings::EndianSettings,
};
fn test(name: &str) {
.with_cc(CC::C, "((,[,],))".parse().unwrap())
.with_cc(CC::D, ",XXX,,-".parse().unwrap())
.with_cc(CC::E, ",,YYY,-".parse().unwrap());
- let mut value = 0.0;
+ let mut value = Some(0.0);
let mut value_name = String::new();
for (line_number, line) in input.lines().map(|r| r.unwrap()).enumerate() {
let line = line.trim();
value = if let Some(Token::Punct(Punct::Exp)) = tokens.get(1) {
assert_eq!(tokens.len(), 3);
let exponent = tokens[2].as_number().unwrap();
- number.powf(exponent)
+ Some(number.powf(exponent))
} else {
assert_eq!(tokens.len(), 1);
- *number
+ Some(*number)
};
value_name = String::from(line);
}
+ Token::End => {
+ value = None;
+ value_name = String::from(line);
+ }
Token::Id(id) => {
let format: UncheckedFormat =
id.0.as_str()
let format: Format = format.try_into().unwrap();
assert_eq!(tokens.get(1), Some(&Token::Punct(Punct::Colon)));
let expected = tokens[2].as_string().unwrap();
- let actual = Value::Number(Some(value))
+ let actual = Value::Number(value)
.display(format, UTF_8)
.with_settings(&settings)
.to_string();
test("cce.txt");
}
+ #[test]
+ fn pibhex() {
+ test("pibhex.txt");
+ }
+
#[test]
fn leading_zeros() {
struct Test {
let mut value = None;
let mut value_name = String::new();
+ let endian = EndianSettings {
+ output_integer_format: Endian::Big,
+ output_float_format: Endian::Big,
+ ..EndianSettings::default()
+ };
for (line_number, line) in input.lines().map(|r| r.unwrap()).enumerate() {
let line = line.trim();
let line_number = line_number + 1;
let mut actual = SmallVec::<[u8; 16]>::new();
Value::Number(value)
.display(format, UTF_8)
+ .with_endian(endian)
.write(&mut actual, UTF_8)
.unwrap();
let mut actual_s = SmallString::<[u8; 32]>::new();
}
#[test]
- fn test_p() {
+ fn p() {
test_binhex("p.txt");
}
#[test]
- fn test_pk() {
+ fn pk() {
test_binhex("pk.txt");
}
#[test]
- fn test_ib() {
+ fn ib() {
test_binhex("ib.txt");
}
+
+ #[test]
+ fn pib() {
+ test_binhex("pib.txt");
+ }
+
+ #[test]
+ fn rb() {
+ test_binhex("rb.txt");
+ }
}
}
}
-pub struct Settings {
- pub look: Arc<Look>,
-
+#[derive(Copy, Clone, Default, PartialEq, Eq)]
+pub struct EndianSettings {
/// Endianness for reading IB and PIB formats.
pub input_integer_format: Endian,
/// Endianness for writing RB and RBHEX formats.
pub output_float_format: Endian,
+}
+
+pub struct Settings {
+ pub look: Arc<Look>,
/// `MDISPLAY`: how to display matrices in `MATRIX`...`END MATRIX`.
pub matrix_display: MatrixDisplay,
pub global: Compatibility,
pub syntax: Compatibility,
pub formats: FormatSettings,
+ pub endian: EndianSettings,
pub small: f64,
pub show_values: Show,
pub show_variables: Show,
fn default() -> Self {
Self {
look: Arc::new(Look::default()),
- input_integer_format: Endian::NATIVE,
- input_float_format: Endian::NATIVE,
- output_integer_format: Endian::NATIVE,
- output_float_format: Endian::NATIVE,
matrix_display: MatrixDisplay::default(),
view_length: 24,
view_width: 79,
global: Compatibility::default(),
syntax: Compatibility::default(),
formats: FormatSettings::default(),
+ endian: EndianSettings::default(),
small: 0.0001,
show_values: Show::default(),
show_variables: Show::default(),