chiark / gitweb /
use new zcoord
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 4 Oct 2020 21:48:57 +0000 (22:48 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 4 Oct 2020 21:48:57 +0000 (22:48 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/api.rs
src/bigfloat.rs
src/cmdlistener.rs
src/commands.rs
src/gamestate.rs
src/global.rs
src/imports.rs
src/session.rs
src/updates.rs
src/utils.rs

index d218421cd8cebb52ca5fb9238f5266d83b01ac1b..1a8bebdcf307569c55575a5878bfd1e5dcedc3f9 100644 (file)
@@ -278,8 +278,8 @@ impl ApiPieceOp for ApiPieceRaise {
   fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId,
         _p: &dyn Piece, _: &dyn Lens) -> PieceUpdateFromOp {
     let pc = gs.pieces.byid_mut(piece).unwrap();
-    pc.zlevel = ZLevel { z : self.z, zg : gs.gen };
-    let update = PieceUpdateOp::SetZLevel(pc.zlevel);
+    pc.zlevel = ZLevel { z : self.z.clone(), zg : gs.gen };
+    let update = PieceUpdateOp::SetZLevel(());
     (WhatResponseToClientOp::Predictable,
      update, vec![])
   }
index dca45701fb65bac18c3c767e1244f466da5a9ed3..97e0fa98af364657c8e508d567368724adc8582d 100644 (file)
@@ -42,6 +42,9 @@ mod innards {
   #[serde(try_from="&str")]
   pub struct Bigfloat(Innards);
 
+  unsafe impl Send for Bigfloat { }
+  unsafe impl Sync for Bigfloat { }
+
   type Innards = NonNull<u8>;
 
   pub(in super)
@@ -79,13 +82,25 @@ mod innards {
 
   impl Bigfloat {
     pub(in super)
-    fn from_parts(sign: Sign, exp: Sz, limbs: &[Limb]) -> Bigfloat {
-      let nlimbs : Sz = limbs.len().try_into().expect("limb count overflow");
+    fn from_limbs<L, I>(sign: Sign, exp: Sz, nlimbs: Sz, mut limbs: I)
+                        -> Bigfloat 
+    where L: Into<Limb> + Debug,
+          I: Iterator<Item=L>
+    {
       unsafe {
         let p = alloc::alloc(layout(nlimbs));
         let (p_header, p_limbs) = ptrs(p);
         ptr::write(p_header, Header { sign, exp, nlimbs });
-        ptr::copy_nonoverlapping(limbs.as_ptr(), p_limbs, nlimbs as usize);
+        let mut count = 0..(nlimbs as usize);
+        loop {
+          match (limbs.next(), count.next()) {
+            (None, None) => break,
+            (Some(l), Some(i)) => {
+              p_limbs.add(i).write(l.into());
+            },
+            x => panic!("unexpected {:?}", x),
+          }
+        }
         Bigfloat(NonNull::new(p).unwrap())
       }
     }
@@ -102,7 +117,7 @@ mod innards {
 
     #[allow(dead_code)] // xxx
     pub(in super)
-    fn as_mut_limbs(&self) -> (&Header, &mut [Limb]) {
+    fn as_mut_limbs(&mut self) -> (&Header, &mut [Limb]) {
       unsafe {
         let (h, l) = ptrs(self.0.as_ptr());
         let h = h.as_mut().unwrap();
@@ -110,6 +125,12 @@ mod innards {
         (h, limbs)
       }
     }
+
+    pub(in super)
+    fn from_parts(sign: Sign, exp: Sz, limbs: &[Limb]) -> Bigfloat {
+      let nlimbs : Sz = limbs.len().try_into().expect("limb count overflow");
+      Self::from_limbs(sign, exp, nlimbs, limbs.iter().cloned())
+    }
   }
 
   impl Drop for Bigfloat {
@@ -137,6 +158,12 @@ mod innards {
 
 }
 
+impl Default for Bigfloat {
+  fn default() -> Bigfloat {
+    Bigfloat::from_parts(Pos, 0, &[default()])
+  }
+}
+
 impl From<LimbVal> for Limb {
   fn from(u: LimbVal) -> Limb {
     assert_eq!(0, u >> 48);
@@ -224,6 +251,26 @@ impl Bigfloat {
   }
 }
 
+#[derive(Error,Debug,Copy,Clone,Serialize,Deserialize)]
+pub struct Overflow;
+display_as_debug!(Overflow);
+
+impl From<TryFromIntError> for Overflow {
+  fn from(_: TryFromIntError) -> Overflow { Overflow }
+}
+
+impl TryFrom<&Mutable> for Bigfloat {
+  type Error = Overflow;
+  #[throws(Overflow)]
+  fn try_from(m: &Mutable) -> Bigfloat {
+    let exp = (-m.limbs.counter()).try_into()?;
+    let nlimbs = m.limbs.len().try_into()?;
+    let slices = m.limbs.inner().as_slices();
+    let it = slices.0.iter().chain(slices.1.iter()).cloned();
+    Bigfloat::from_limbs(m.sign, exp, nlimbs, it)
+  }
+}
+
 impl Mutable {
   pub fn add(&mut self, rhs: u32) {
     self.add_to_limb(0, rhs);
index 075a30549f24eb0432092326cb949df7cf677664..333644292909e03767d5a6b07013bcb064417500 100644 (file)
@@ -263,16 +263,17 @@ fn execute_game_insn(cs: &CommandStream,
 
       let mut updates = Vec::with_capacity(count as usize);
       let mut pos = pos.unwrap_or(DEFAULT_POS_START);
-      for i in 0..count {
+      let mut z = gs.max_z.clone_mut();
+      for _ in 0..count {
         let p = info.load()?;
         let face = face.unwrap_or_default();
         if p.nfaces() <= face.into() {
           throw!(SpecError::FaceNotFound);
         }
-        let z = gs.max_z.add(i + 1);
+        z.add(1);
         let pc = PieceState {
           held: None,
-          zlevel: ZLevel { z, zg: gs.gen },
+          zlevel: ZLevel { z: (&z).try_into()?, zg: gs.gen },
           lastclient: Default::default(),
           gen_before_lastclient: Generation(0),
           pinned: pinned.unwrap_or(false),
@@ -325,7 +326,7 @@ fn execute_for_game(cs: &CommandStream, ig: &mut InstanceGuard,
 
 #[derive(Debug,Default)]
 struct UpdateHandlerBulk {
-  pieces : slotmap::SparseSecondaryMap<PieceId, PieceUpdateOp<()>>,
+  pieces : slotmap::SparseSecondaryMap<PieceId, PieceUpdateOp<(),()>>,
   logs : bool,
   raw : Vec<PreparedUpdateEntry>,
 }
index 8a09ed0e27008cc60b4145b87785db2523783bd2..f2bf55a6feb94ad13b3ab5555eb993ae4c73a38b 100644 (file)
@@ -92,6 +92,7 @@ pub enum MgmtError {
   PieceNotFound,
   LimitExceeded,
   ServerFailure(String),
+  ZCoordinateOverflow(#[from] bigfloat::Overflow),
   BadGlob { pat: String, msg: String },
   BadSpec(#[from] SpecError),
 }
index 06ef716240573e52988e455291c970c0ded700a2..15d5f1067f8a37985cf2050b1068c2e946bcbeae 100644 (file)
@@ -19,11 +19,7 @@ pub struct Generation (pub u64);
 
 visible_slotmap_key!{ VisiblePieceId('.') }
 
-#[derive(Debug,Copy,Clone,PartialEq,PartialOrd)]
-#[derive(Serialize,Deserialize,Default)]
-#[serde(into="f64")]
-#[serde(try_from="f64")]
-pub struct ZCoord(f64);
+pub type ZCoord = Bigfloat;
 
 #[derive(Clone,Serialize,Deserialize,Eq,Ord,PartialEq,PartialOrd)]
 #[serde(transparent)]
@@ -33,7 +29,7 @@ pub const DEFAULT_TABLE_SIZE : Pos = PosC([ 400, 200 ]);
 
 // ---------- general data types ----------
 
-#[derive(Debug,Copy,Clone,Serialize,Deserialize,Eq,PartialEq,Ord,PartialOrd)]
+#[derive(Debug,Clone,Serialize,Deserialize,Eq,PartialEq,Ord,PartialOrd)]
 pub struct ZLevel {
   pub z: ZCoord,
   pub zg: Generation,
@@ -155,35 +151,6 @@ impl Display for Generation {
   }
 }
 
-impl TryFrom<f64> for ZCoord {
-  type Error = OnlineError;
-  #[throws(OnlineError)]
-  fn try_from(v: f64) -> ZCoord {
-    if !v.is_finite() { throw!(OnlineError::InvalidZCoord) }
-    ZCoord(v)
-  }
-}
-impl From<ZCoord> for f64 {
-  fn from(v: ZCoord) -> f64 { v.0 }
-}
-impl Ord for ZCoord {
-  fn cmp(&self, other: &Self) -> cmp::Ordering {
-    self.0.partial_cmp(&other.0).unwrap()
-  }
-}
-impl Eq for ZCoord { }
-impl Display for ZCoord {
-  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-    Display::fmt(&self.0,f)
-  }
-}
-
-impl ZCoord {
-  pub fn add(&self, v: u32) -> ZCoord {
-    ZCoord(self.0 + (v as f64))
-  }
-}
-
 pub trait ClampTable : Sized {
   fn clamped(self, range: Self) -> (Self, bool);
 }
@@ -235,7 +202,7 @@ impl PieceState {
       pos        : self.pos,
       held       : self.held,
       svg        : p.make_defs(pri)?,
-      z          : self.zlevel.z,
+      z          : self.zlevel.z.clone(),
       zg         : self.zlevel.zg,
       pinned     : self.pinned,
       uos        : p.ui_operations()?,
index 47d800f6780a7c9915684f1bc89a21f8792678e5..b7c62d1959322e3c0a22db787e9071b03a0e68fe 100644 (file)
@@ -393,7 +393,7 @@ impl InstanceGuard<'_> {
       // These parts are straightforward and correct
       table_size : self.c.g.gs.table_size,
       gen : self.c.g.gs.gen,
-      max_z : self.gs.max_z,
+      max_z : self.gs.max_z.clone(),
       players,
       // These have special handling
       log : Default::default(),
index f8809df7dfdb09b6762595951e2746a33c0f85fb..6d93599b592f12794d3a94ee59f7ad5cdd8f27b0 100644 (file)
@@ -19,7 +19,7 @@ pub use std::str::FromStr;
 pub use std::iter;
 pub use std::iter::repeat_with;
 pub use std::collections::VecDeque;
-pub use std::num::Wrapping;
+pub use std::num::{Wrapping, TryFromIntError};
 pub use std::cmp::{self,min,max,Ordering};
 pub use std::error::Error;
 pub use std::marker::PhantomData;
@@ -111,6 +111,7 @@ pub use crate::utils::*;
 pub use crate::spec::*;
 pub use crate::debugreader::DebugReader;
 pub use crate::shapelib;
+pub use crate::bigfloat::{self, Bigfloat};
 
 pub use nix::unistd::Uid;
 
index e24bd370d0613ba9267699f10eab3118defc6eb0..a940e2dc074a0159913f0fe243d9997e6ad7463f 100644 (file)
@@ -104,7 +104,7 @@ fn session(form : Json<SessionForm>) -> Result<Template,OE> {
 
       let for_info = SessionPieceLoadJson {
         held : &pr.held,
-        z  : pr.zlevel.z,
+        z  : pr.zlevel.z.clone(),
         zg : pr.zlevel.zg,
         pinned : pr.pinned,
         uos : &p.ui_operations()?,
index 2fde6220cc04fc45b048aeceb23f78765cee5902..45113316f6eb14989f86cc07ce7faed2ef9f3c22 100644 (file)
@@ -16,7 +16,7 @@ pub struct ClientSequence(u64);
 
 #[derive(Debug)] // not Default
 pub struct ExecuteGameChangeUpdates {
-  pub pcs: Vec<(PieceId,PieceUpdateOp<()>)>,
+  pub pcs: Vec<(PieceId,PieceUpdateOp<(),()>)>,
   pub log: Vec<LogEntry>,
   pub raw: Option<Vec<PreparedUpdateEntry>>,
 }
@@ -44,7 +44,7 @@ pub enum PreparedUpdateEntry {
   Piece {
     by_client: IsResponseToClientOp,
     piece : VisiblePieceId,
-    op : PieceUpdateOp<PreparedPieceState>,
+    op : PieceUpdateOp<PreparedPieceState,ZLevel>,
   },
   SetTableSize(Pos),
   Log (Arc<LogEntry>),
@@ -65,16 +65,16 @@ pub struct PreparedPieceState {
 // ---------- piece updates ----------
 
 #[derive(Debug,Serialize)]
-pub enum PieceUpdateOp<NS> {
+pub enum PieceUpdateOp<NS,ZL> {
   Delete(),
   Insert(NS),
   Modify(NS),
   Move(Pos),
-  SetZLevel(ZLevel),
+  SetZLevel(ZL),
 }
 
 pub type PieceUpdateFromOp = (WhatResponseToClientOp,
-                              PieceUpdateOp<()>, Vec<LogEntry>);
+                              PieceUpdateOp<(),()>, Vec<LogEntry>);
 pub type PieceUpdateResult = Result<PieceUpdateFromOp, ApiPieceOpError>;
 
 // ---------- for traansmission ----------
@@ -95,7 +95,7 @@ enum TransmitUpdateEntry<'u> {
   },
   Piece {
     piece : VisiblePieceId,
-    op : PieceUpdateOp<&'u PreparedPieceState>,
+    op : PieceUpdateOp<&'u PreparedPieceState, &'u ZLevel>,
   },
   RecordedUnpredictable {
     piece : VisiblePieceId,
@@ -187,7 +187,7 @@ impl PreparedUpdateEntry {
 
 // ---------- PieceUpdatesOp ----------
 
-impl<NS> PieceUpdateOp<NS> {
+impl<NS,ZC> PieceUpdateOp<NS,ZC> {
   pub fn new_state(&self) -> Option<&NS> {
     use PieceUpdateOp::*;
     match self {
@@ -198,8 +198,10 @@ impl<NS> PieceUpdateOp<NS> {
       SetZLevel(_) => None,
     }
   }
-  pub fn try_map_new_state<NS2,E:Error, F: FnOnce(NS) -> Result<NS2,E>>
-    (self, f:F) -> Result<PieceUpdateOp<NS2>,E>
+  pub fn try_map<NS2, ZC2, E:Error,
+                 F: FnOnce(NS) -> Result<NS2,E>,
+                 G: FnOnce(ZC) -> Result<ZC2,E>
+                 > (self, f:F, g:G) -> Result<PieceUpdateOp<NS2,ZC2>,E>
   {
     use PieceUpdateOp::*;
     Ok(match self {
@@ -207,33 +209,41 @@ impl<NS> PieceUpdateOp<NS> {
       Insert(ns) => Insert(f(ns)?),
       Modify(ns) => Modify(f(ns)?),
       Move(pos) => Move(pos),
-      SetZLevel(zl) => SetZLevel(zl),
+      SetZLevel(zl) => SetZLevel(g(zl)?),
     })
   }
-  pub fn map_ref_new_state(&self) -> PieceUpdateOp<&NS> {
+  pub fn map_ref(&self) -> PieceUpdateOp<&NS,&ZC> {
     use PieceUpdateOp::*;
     match self {
       Delete() => Delete(),
       Insert(ns) => Insert(ns),
       Modify(ns) => Modify(ns),
       Move(pos) => Move(*pos),
-      SetZLevel(zl) => SetZLevel(*zl),
+      SetZLevel(zl) => SetZLevel(zl),
     }
   }
-  pub fn map_new_state<NS2,F: FnOnce(NS) -> NS2>(self, f:F)
-                            -> PieceUpdateOp<NS2> {
+  pub fn map<NS2,ZC2,
+             F: FnOnce(NS) -> NS2,
+             G: FnOnce(ZC) -> ZC2
+             > (self, f:F, g:G) -> PieceUpdateOp<NS2,ZC2>
+  {
     #[derive(Error,Debug)]
     enum Never { }
-    self.try_map_new_state(|ns| <Result<_,Never>>::Ok(f(ns))).unwrap()
+    self.try_map(
+      |ns| <Result<_,Never>>::Ok(f(ns)),
+      |zc| <Result<_,Never>>::Ok(g(zc)),
+    ).unwrap()
   }
-  pub fn new_z_generation(&self) -> Option<Generation> {
+  pub fn new_z_generation(&self) -> Option<Generation>
+    where ZC: Borrow<ZLevel>
+  {
     use PieceUpdateOp::*;
     match self {
       Delete() => None,
       Insert(_) => None,
       Modify(_) => None,
       Move(_) => None,
-      SetZLevel(ZLevel{zg,..}) => Some(*zg),
+      SetZLevel(l) => Some(l.borrow().zg),
     }
   }
 }
@@ -318,7 +328,7 @@ impl<'r> PrepareUpdatesBuffer<'r> {
 
   #[throws(InternalError)]
   fn piece_update_fallible(&mut self, piece: PieceId,
-                           update: PieceUpdateOp<()>,
+                           update: PieceUpdateOp<(),()>,
                            lens: &dyn Lens) -> PreparedUpdateEntry {
     type WRC = WhatResponseToClientOp;
 
@@ -330,7 +340,7 @@ impl<'r> PrepareUpdatesBuffer<'r> {
       self.g.pieces.get(piece),
     ) {
       (Ok(pc), Some(p)) => {
-        gs.max_z.update_max(pc.zlevel.z);
+        gs.max_z.update_max(&pc.zlevel.z);
 
         if let Some(new_lastclient) = match self.by_client {
           Some((WRC::Predictable,tclient,_)) |
@@ -344,12 +354,15 @@ impl<'r> PrepareUpdatesBuffer<'r> {
         pc.gen = gen;
         let pri_for_all = lens.svg_pri(piece,pc,Default::default());
 
-        let update = update.try_map_new_state(
-          |_|{
+        let update = update.try_map(
+          |()|{
             let mut ns = pc.prep_piecestate(p.as_ref(), &pri_for_all)?;
             lens.massage_prep_piecestate(&mut ns);
             <Result<_,InternalError>>::Ok(ns)
           },
+          |()|{
+            Ok(pc.zlevel.clone())
+          }
         )?;
 
         (update, pri_for_all.id)
@@ -366,7 +379,7 @@ impl<'r> PrepareUpdatesBuffer<'r> {
     }
   }
 
-  pub fn piece_update(&mut self, piece: PieceId, update: PieceUpdateOp<()>,
+  pub fn piece_update(&mut self, piece: PieceId, update: PieceUpdateOp<(),()>,
                       lens: &dyn Lens) {
     // Caller needs us to be infallible since it is too late by
     // this point to back out a game state change.
@@ -455,7 +468,7 @@ impl PreparedUpdate {
               let zg = op.new_z_generation();
               TUE::Recorded { piece, cseq, zg, svg: ns.map(|ns| &ns.svg) }
             },
-            FTG::Piece => TUE::Piece { piece, op: op.map_ref_new_state() },
+            FTG::Piece => TUE::Piece { piece, op: op.map_ref() },
             FTG::Exactly(x) => x,
           }
         },
index c46dee1d99f1c1776975edfa6a23a2ee926bb789..9913f4eef6b424f878ab5605ed58d5a18f996586 100644 (file)
@@ -2,10 +2,10 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 // There is NO WARRANTY.
 
-pub trait OrdExt : Ord + Sized {
-  fn update_max(&mut self, new: Self) {
-    if new > *self { *self = new }
+pub trait OrdExt : Ord + Sized + Clone {
+  fn update_max(&mut self, new: &Self) {
+    if *new > *self { *self = new.clone() }
   }
 }
-impl<T> OrdExt for T where T : Ord + Sized {
+impl<T> OrdExt for T where T : Ord + Sized + Clone {
 }