chiark / gitweb /
zcoord: Provide plus_offset method
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 11 Mar 2021 21:24:37 +0000 (21:24 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 11 Mar 2021 21:30:33 +0000 (21:30 +0000)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
base/zcoord.rs

index a656f9846bcb809b1474a54a48381ffe9640bb63..0f955f6c2e4ac4ed8fc4c174676cc836dba51438 100644 (file)
 //       limb to 0000000000, start again: decrement rightmost nonzero
 //       limb by 1, with borrow, then add two limbs vvvvvvvvvv, and
 //       redo.
+//
+//    Given a base value, and an u32 "offset multiplier", produce a value
+//
+//       The resulting values are all greater than the base, and
+//       in the same order as the provided offset multipliers.
+//       The function is deterministic
 
 use std::cmp::{max, Ordering};
 use std::convert::{TryFrom, TryInto};
@@ -76,6 +82,8 @@ use serde_with::DeserializeFromStr;
 use serde_with::SerializeDisplay;
 use thiserror::Error;
 
+use crate::misc::default;
+
 //---------- core definitions ----------
 
 pub type RangeCount = u32;
@@ -623,7 +631,7 @@ mod innards {
 
   pub(super)
   struct Header {
-    pub taillen: u16,
+    pub taillen: u16, // in characters
   }
 
   #[repr(C)]
@@ -711,6 +719,31 @@ mod innards {
         layout(taillen)
       }
     }
+
+    #[throws(Overflow)]
+    pub fn plus_offset(&self, offset: u32) -> Self { unsafe {
+      let (old_header, old_tail) = ptrs(self.0.as_ptr());
+      let old_taillen = old_header.as_ref().unwrap().taillen;
+      let new_taillen = old_taillen
+        .checked_add(TEXT_PER_LIMB as Taillen).ok_or(Overflow)?;
+      let old_taillen: usize = old_taillen.into();
+      let new_limb = lv(
+        (offset as RawLimbVal) << (BITS_PER_LIMB - 32) |
+         1                     << (BITS_PER_LIMB - 33)
+      );
+      let mut buf: [u8; TEXT_PER_LIMB] = default();
+      buf[0] = b'_';
+      new_limb.to_str_buf((&mut buf[1..TEXT_PER_LIMB]).try_into().unwrap());
+      ZCoord::alloc_unsafe(new_taillen, |new_tail| {
+        ptr::copy_nonoverlapping(old_tail,
+                                 new_tail,
+                                 old_taillen);
+
+        ptr::copy_nonoverlapping(buf.as_ptr(),
+                                 new_tail.add(old_taillen),
+                                 TEXT_PER_LIMB);
+      })
+    } }
   }
 
   impl Drop for ZCoord {
@@ -931,4 +964,15 @@ mod test {
     let mut it = mkr(None,Some("fvvq000000"),0).unwrap();
     it.nxt(None);
   }
+
+  #[test]
+  fn plus_offset() {
+    let z: ZCoord = "3o00000000".parse().unwrap();
+    assert_eq!(z.plus_offset(0).unwrap(),
+               "3o00000000_0000004000".parse().unwrap());
+    assert_eq!(z.plus_offset(1).unwrap(),
+               "3o00000000_000000c000".parse().unwrap());
+    assert_eq!(z.plus_offset(0xffffffff).unwrap(),
+               "3o00000000_vvvvvvs000".parse().unwrap());
+  }
 }