Implement IB.
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 25 Mar 2025 16:17:00 +0000 (09:17 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 25 Mar 2025 16:17:00 +0000 (09:17 -0700)
rust/pspp/src/endian.rs
rust/pspp/src/format/display.rs

index ba47a3001aa252e07b55b909359b8c330ef66538..ae3cd99c1466b0fda9ded0637e22fb16a1e4550c 100644 (file)
@@ -1,3 +1,5 @@
+use smallvec::SmallVec;
+
 /// The endianness for integer and floating-point numbers in SPSS system files.
 ///
 /// SPSS system files can declare IBM 370 and DEC VAX floating-point
@@ -37,11 +39,33 @@ impl Endian {
             _ => None,
         }
     }
+
+    pub fn to_smallvec(self, mut value: u64, n: usize) -> SmallVec<[u8; 16]> {
+        debug_assert!(n <= 8);
+        let mut vec = SmallVec::new();
+        value <<= 8 * (8 - n);
+        for _ in 0..n {
+            vec.push((value >> 56) as u8);
+            value <<= 8;
+        }
+        if self == Endian::Little {
+            vec.reverse();
+        }
+        vec
+    }
 }
 
 pub trait ToBytes<T, const N: usize> {
     fn to_bytes(self, value: T) -> [u8; N];
 }
+impl ToBytes<u64, 8> for Endian {
+    fn to_bytes(self, value: u64) -> [u8; 8] {
+        match self {
+            Endian::Big => u64::to_be_bytes(value),
+            Endian::Little => u64::to_le_bytes(value),
+        }
+    }
+}
 impl ToBytes<i64, 8> for Endian {
     fn to_bytes(self, value: i64) -> [u8; 8] {
         match self {
index c7a42b836a51f82e09ff4fa7b92afce9cca3dd59..ca1545240a29b813db554210558b6329d43d0b5f 100644 (file)
@@ -560,6 +560,7 @@ impl<'a, 'b> DisplayValue<'a, 'b> {
         match self.format.type_() {
             Type::P => Some(self.p(number)),
             Type::PK => Some(self.pk(number)),
+            Type::IB => Some(self.ib(number)),
             _ => None,
         }
     }
@@ -613,6 +614,26 @@ impl<'a, 'b> DisplayValue<'a, 'b> {
         let (_valid, output) = self.bcd(number, self.format.w() * 2);
         output
     }
+
+    fn ib(&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) / 2.0 - 1.0
+            || number < -power256(self.format.w) / 2.0
+        {
+            0.0
+        } else {
+            number
+        };
+        let integer = number.abs() as u64;
+        let integer = if number < 0.0 {
+            (-(integer as i64)) as u64
+        } else {
+            integer
+        };
+        PsppSettings::global()
+            .output_integer_format
+            .to_smallvec(integer, self.format.w())
+    }
 }
 
 struct LegacyFormat {
@@ -1008,6 +1029,7 @@ mod test {
             segment::Syntax,
             token::{Punct, Token},
         },
+        settings::Settings as PsppSettings,
     };
 
     fn test(name: &str) {
@@ -1204,6 +1226,7 @@ mod test {
         let input = BufReader::new(File::open(&filename).unwrap());
         let mut value = None;
         let mut value_name = String::new();
+
         for (line_number, line) in input.lines().map(|r| r.unwrap()).enumerate() {
             let line = line.trim();
             let line_number = line_number + 1;
@@ -1259,4 +1282,9 @@ mod test {
     fn test_pk() {
         test_binhex("pk.txt");
     }
+
+    #[test]
+    fn test_ib() {
+        test_binhex("ib.txt");
+    }
 }