use serde::{Serialize, Serializer, Deserialize};
use thiserror::Error;
+//---------- core definitions ----------
+
const BITS_PER_DIGIT : usize = 5;
const DIGITS_PER_LIMB : usize = 10;
-const DEFAULT_TEXT : &[u8] = b"gggggggggg";
const DELTA : LimbVal = Wrapping(0x4000_0000);
const ZERO : LimbVal = Wrapping(0);
type RawLimbVal = u64;
type LimbVal = Wrapping<RawLimbVal>;
+#[derive(Error,Clone,Copy,Debug)]
+#[error("error parsing zcoord (z value)")]
+pub struct ParseError;
+
+//---------- Mutabel ----------
+
+#[derive(Clone,Debug)]
+pub struct Mutable {
+ limbs: Vec<LimbVal>,
+}
+
+impl ZCoord {
+ pub fn clone_mut(&self) -> Mutable {
+ let tail = self.tail();
+ let nlimbs = (tail.len() + 1) / TEXT_PER_LIMB;
+ let mut limbs = Vec::with_capacity(nlimbs+2);
+ for lt in tail.chunks(TEXT_PER_LIMB) {
+ let s = str::from_utf8(<[0..DIGITS_PER_LIMB]).unwrap();
+ let v = RawLimbVal::from_str_radix(s, 1 << BITS_PER_DIGIT).unwrap();
+ limbs.push(Wrapping(v));
+ }
+ Mutable { limbs }
+ }
+}
+
+#[derive(Error,Debug,Copy,Clone,Serialize,Deserialize)]
+#[error("Z coordinate overflow")]
+pub struct Overflow;
+
+impl From<TryFromIntError> for Overflow {
+ fn from(_: TryFromIntError) -> Overflow { Overflow }
+}
+
+impl Mutable {
+ #[throws(Overflow)]
+ pub fn increment(&mut self) -> ZCoord {
+ 'attempt: loop {
+ let mut i = self.limbs.len() - 1;
+ let mut delta = DELTA;
+
+ if (||{
+ loop {
+ let nv = self.limbs[i] + delta;
+ self.limbs[i] = nv & LIMB_MASK;
+ if nv < LIMB_MODULUS { return Some(()) }
+ if i == 0 { return None }
+ i -= 1;
+ delta = ONE;
+ }
+ })() == Some(()) { break 'attempt }
+
+ // undo
+ loop {
+ if i >= self.limbs.len() { break }
+ else if i == self.limbs.len()-1 { delta = DELTA; }
+ let nv = self.limbs[i] - delta;
+ self.limbs[i] = nv & LIMB_MASK;
+ i += 1;
+ }
+ self.limbs.push(ZERO);
+ self.limbs.push(ZERO);
+ }
+ self.repack()?
+ }
+
+ #[throws(Overflow)]
+ pub fn repack(&self) -> ZCoord { self.try_into()? }
+}
+
+//---------- main features of a Zcoord ----------
+
+impl ZCoord {
+ pub fn as_str(&self) -> &str {
+ let tail = self.tail();
+ str::from_utf8(tail).unwrap()
+ }
+
+ pub fn to_string(&self) -> String {
+ self.as_str().to_string()
+ }
+}
+
+impl Display for ZCoord {
+ #[throws(fmt::Error)]
+ fn fmt(&self, f: &mut Formatter) {
+ write!(f, "{}", self.as_str())?
+ }
+}
+
+impl Debug for ZCoord {
+ #[throws(fmt::Error)]
+ fn fmt(&self, f: &mut Formatter) {
+ write!(f, r#"Bf""#)?;
+ <ZCoord as Display>::fmt(self, f)?;
+ write!(f, r#"""#)?;
+ }
+}
+
+impl Ord for ZCoord {
+ fn cmp(&self, other: &ZCoord) -> Ordering {
+ let at = self.tail();
+ let bt = other.tail();
+ at.cmp(bt)
+ }
+}
+impl PartialOrd for ZCoord {
+ fn partial_cmp(&self, other: &ZCoord) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+impl Eq for ZCoord {
+}
+impl PartialEq for ZCoord {
+ fn eq(&self, other: &ZCoord) -> bool {
+ self.cmp(other) == Ordering::Equal
+ }
+}
+
+impl TryFrom<&str> for ZCoord {
+ type Error = ParseError;
+ #[throws(ParseError)]
+ fn try_from(s: &str) -> ZCoord {
+ ZCoord::from_str(s).ok_or(ParseError)?
+ }
+}
+
+impl Serialize for ZCoord {
+ fn serialize<S:Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
+ s.serialize_str(self.as_str())
+ }
+}
+
+//---------- construction of ZCoord contents ---------
+//
+// We can panic if this code is buggy, but not compromise safety.
+
+const DEFAULT_TEXT : &[u8] = b"gggggggggg";
+
+impl Default for ZCoord {
+ fn default() -> ZCoord {
+ ZCoord::alloc_copy(DEFAULT_TEXT).unwrap()
+ }
+}
+
+impl ZCoord {
+ #[throws(as Option)]
+ pub fn from_str(s: &str) -> Self {
+ let s = s.as_bytes();
+ let nomlen = s.len() + 1;
+ if nomlen % TEXT_PER_LIMB !=0 { None? }
+ for lt in s.chunks(TEXT_PER_LIMB) {
+ if !lt[0..DIGITS_PER_LIMB].iter().all(
+ |c: &u8| {
+ (b'0'..=b'9').contains(&c) ||
+ (b'a'..=b'v').contains(&c)
+ }) { None? }
+ match lt[DIGITS_PER_LIMB..] { [] | [b'_'] => (), _ => None? };
+ }
+ if &s[s.len() - DIGITS_PER_LIMB.. ] == b"0000000000" { None? }
+ ZCoord::alloc_copy(s).ok()?
+ }
+}
+
+impl TryFrom<&Mutable> for ZCoord {
+ type Error = Overflow;
+ #[throws(Overflow)]
+ fn try_from(m: &Mutable) -> ZCoord {
+ let taillen = (m.limbs.len() * TEXT_PER_LIMB - 1).try_into()?;
+ let mut bf = ZCoord::alloc(taillen);
+ let mut w = bf.tail_mut();
+ for mut l in m.limbs.iter().cloned() {
+ if l >= LIMB_MODULUS { throw!(Overflow) };
+ for p in w[0..DIGITS_PER_LIMB].rchunks_exact_mut(1) {
+ let v = (l & DIGIT_MASK).0 as u8;
+ p[0] = if v < 10 { b'0' + v } else { (b'a' - 10) + v };
+ l >>= BITS_PER_DIGIT;
+ }
+ if let Some(p) = w.get_mut(DIGITS_PER_LIMB) {
+ *p = b'_';
+ } else {
+ break;
+ }
+ w = &mut w[TEXT_PER_LIMB..];
+ }
+ bf
+ }
+}
+
+//---------- innards, unsafe ----------
+
mod innards {
use super::*;
use std::mem::{self, align_of, size_of};
}
-impl Default for ZCoord {
- fn default() -> ZCoord {
- ZCoord::alloc_copy(DEFAULT_TEXT).unwrap()
- }
-}
-
-#[derive(Clone,Debug)]
-pub struct Mutable {
- limbs: Vec<LimbVal>,
-}
-
-impl TryFrom<&Mutable> for ZCoord {
- type Error = Overflow;
- #[throws(Overflow)]
- fn try_from(m: &Mutable) -> ZCoord {
- let taillen = (m.limbs.len() * TEXT_PER_LIMB - 1).try_into()?;
- let mut bf = ZCoord::alloc(taillen);
- let mut w = bf.tail_mut();
- for mut l in m.limbs.iter().cloned() {
- if l >= LIMB_MODULUS { throw!(Overflow) };
- for p in w[0..DIGITS_PER_LIMB].rchunks_exact_mut(1) {
- let v = (l & DIGIT_MASK).0 as u8;
- p[0] = if v < 10 { b'0' + v } else { (b'a' - 10) + v };
- l >>= BITS_PER_DIGIT;
- }
- if let Some(p) = w.get_mut(DIGITS_PER_LIMB) {
- *p = b'_';
- } else {
- break;
- }
- w = &mut w[TEXT_PER_LIMB..];
- }
- bf
- }
-}
-
-impl ZCoord {
- #[throws(as Option)]
- pub fn from_str(s: &str) -> Self {
- let s = s.as_bytes();
- let nomlen = s.len() + 1;
- if nomlen % TEXT_PER_LIMB !=0 { None? }
- for lt in s.chunks(TEXT_PER_LIMB) {
- if !lt[0..DIGITS_PER_LIMB].iter().all(
- |c: &u8| {
- (b'0'..=b'9').contains(&c) ||
- (b'a'..=b'v').contains(&c)
- }) { None? }
- match lt[DIGITS_PER_LIMB..] { [] | [b'_'] => (), _ => None? };
- }
- if &s[s.len() - DIGITS_PER_LIMB.. ] == b"0000000000" { None? }
- ZCoord::alloc_copy(s).ok()?
- }
-
- pub fn clone_mut(&self) -> Mutable {
- let tail = self.tail();
- let nlimbs = (tail.len() + 1) / TEXT_PER_LIMB;
- let mut limbs = Vec::with_capacity(nlimbs+2);
- for lt in tail.chunks(TEXT_PER_LIMB) {
- let s = str::from_utf8(<[0..DIGITS_PER_LIMB]).unwrap();
- let v = RawLimbVal::from_str_radix(s, 1 << BITS_PER_DIGIT).unwrap();
- limbs.push(Wrapping(v));
- }
- Mutable { limbs }
- }
-
- pub fn as_str(&self) -> &str {
- let tail = self.tail();
- str::from_utf8(tail).unwrap()
- }
-
- pub fn to_string(&self) -> String {
- self.as_str().to_string()
- }
-}
-
-#[derive(Error,Debug,Copy,Clone,Serialize,Deserialize)]
-#[error("Z coordinate overflow")]
-pub struct Overflow;
-
-impl From<TryFromIntError> for Overflow {
- fn from(_: TryFromIntError) -> Overflow { Overflow }
-}
-
-impl Mutable {
- #[throws(Overflow)]
- pub fn increment(&mut self) -> ZCoord {
- 'attempt: loop {
- let mut i = self.limbs.len() - 1;
- let mut delta = DELTA;
-
- if (||{
- loop {
- let nv = self.limbs[i] + delta;
- self.limbs[i] = nv & LIMB_MASK;
- if nv < LIMB_MODULUS { return Some(()) }
- if i == 0 { return None }
- i -= 1;
- delta = ONE;
- }
- })() == Some(()) { break 'attempt }
-
- // undo
- loop {
- if i >= self.limbs.len() { break }
- else if i == self.limbs.len()-1 { delta = DELTA; }
- let nv = self.limbs[i] - delta;
- self.limbs[i] = nv & LIMB_MASK;
- i += 1;
- }
- self.limbs.push(ZERO);
- self.limbs.push(ZERO);
- }
- self.repack()?
- }
-
- #[throws(Overflow)]
- pub fn repack(&self) -> ZCoord { self.try_into()? }
-}
-
-impl Display for ZCoord {
- #[throws(fmt::Error)]
- fn fmt(&self, f: &mut Formatter) {
- write!(f, "{}", self.as_str())?
- }
-}
-impl Debug for ZCoord {
- #[throws(fmt::Error)]
- fn fmt(&self, f: &mut Formatter) {
- write!(f, r#"Bf""#)?;
- <ZCoord as Display>::fmt(self, f)?;
- write!(f, r#"""#)?;
- }
-}
-
-impl Ord for ZCoord {
- fn cmp(&self, other: &ZCoord) -> Ordering {
- let at = self.tail();
- let bt = other.tail();
- at.cmp(bt)
- }
-}
-impl PartialOrd for ZCoord {
- fn partial_cmp(&self, other: &ZCoord) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-impl Eq for ZCoord {
-}
-impl PartialEq for ZCoord {
- fn eq(&self, other: &ZCoord) -> bool {
- self.cmp(other) == Ordering::Equal
- }
-}
-
-#[derive(Error,Clone,Copy,Debug)]
-#[error("error parsing zcoord (z value)")]
-pub struct ParseError;
-
-impl TryFrom<&str> for ZCoord {
- type Error = ParseError;
- #[throws(ParseError)]
- fn try_from(s: &str) -> ZCoord {
- ZCoord::from_str(s).ok_or(ParseError)?
- }
-}
-
-impl Serialize for ZCoord {
- fn serialize<S:Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
- s.serialize_str(self.as_str())
- }
-}
+//---------- tests ----------
#[cfg(test)]
mod test {