From 0b84905c85ef398d75981fcbe9f5f109412d6f83 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 23 Sep 2023 15:43:11 +1000 Subject: [PATCH] Avoid excessive floating point precision when serializing --- rslib/src/storage/card/data.rs | 45 +++++++++++++++++++++++++--------- rslib/src/storage/card/mod.rs | 8 +++--- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/rslib/src/storage/card/data.rs b/rslib/src/storage/card/data.rs index a9151b89d..e4d87b5d7 100644 --- a/rslib/src/storage/card/data.rs +++ b/rslib/src/storage/card/data.rs @@ -5,9 +5,7 @@ use std::collections::HashMap; use rusqlite::types::FromSql; use rusqlite::types::FromSqlError; -use rusqlite::types::ToSqlOutput; use rusqlite::types::ValueRef; -use rusqlite::ToSql; use serde::Deserialize; use serde::Serialize; use serde_json::Value; @@ -78,6 +76,24 @@ impl CardData { } None } + + pub(crate) fn convert_to_json(&mut self) -> Result { + if let Some(v) = &mut self.fsrs_stability { + round_to_places(v, 1) + } + if let Some(v) = &mut self.fsrs_difficulty { + round_to_places(v, 3) + } + if let Some(v) = &mut self.fsrs_desired_retention { + round_to_places(v, 2) + } + serde_json::to_string(&self).map_err(Into::into) + } +} + +fn round_to_places(value: &mut f32, decimal_places: u32) { + let factor = 10_f32.powi(decimal_places as i32); + *value = (*value * factor).round() / factor; } impl FromSql for CardData { @@ -91,17 +107,9 @@ impl FromSql for CardData { } } -impl ToSql for CardData { - fn to_sql(&self) -> Result, rusqlite::Error> { - Ok(ToSqlOutput::Owned( - serde_json::to_string(self).unwrap().into(), - )) - } -} - /// Serialize the JSON `data` for a card. pub(crate) fn card_data_string(card: &Card) -> String { - serde_json::to_string(&CardData::from_card(card)).unwrap() + CardData::from_card(card).convert_to_json().unwrap() } fn meta_is_empty(s: &str) -> bool { @@ -143,4 +151,19 @@ mod test { assert!(validate_custom_data(r#"{"日本語": 5}"#).is_err()); assert!(validate_custom_data(&format!(r#"{{"foo": "{}"}}"#, "x".repeat(100))).is_err()); } + + #[test] + fn compact_floats() { + let mut data = CardData { + original_position: None, + fsrs_stability: Some(123.45678), + fsrs_difficulty: Some(1.234567), + fsrs_desired_retention: Some(0.987654), + custom_data: "".to_string(), + }; + assert_eq!( + data.convert_to_json().unwrap(), + r#"{"s":123.5,"d":1.235,"dr":0.99}"# + ); + } } diff --git a/rslib/src/storage/card/mod.rs b/rslib/src/storage/card/mod.rs index f9da74d05..22046f88e 100644 --- a/rslib/src/storage/card/mod.rs +++ b/rslib/src/storage/card/mod.rs @@ -126,7 +126,7 @@ impl super::SqliteStorage { card.original_due, card.original_deck_id, card.flags, - CardData::from_card(card), + CardData::from_card(card).convert_to_json()?, card.id, ])?; Ok(()) @@ -153,7 +153,7 @@ impl super::SqliteStorage { card.original_due, card.original_deck_id, card.flags, - CardData::from_card(card), + CardData::from_card(card).convert_to_json()?, ])?; card.id = CardId(self.db.last_insert_rowid()); Ok(()) @@ -181,7 +181,7 @@ impl super::SqliteStorage { card.original_due, card.original_deck_id, card.flags, - CardData::from_card(card), + CardData::from_card(card).convert_to_json()?, ]) .map(|n_rows| n_rows == 1) .map_err(Into::into) @@ -208,7 +208,7 @@ impl super::SqliteStorage { card.original_due, card.original_deck_id, card.flags, - CardData::from_card(card), + CardData::from_card(card).convert_to_json()?, ])?; Ok(())