From 7b2fbdc88b0b72bde7fc60059bb2d241df8d1156 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 14 Jun 2022 04:14:25 +0800 Subject: [PATCH] evict f64's from the kernel, and eliminate f32 parsing turns out these routines add quite a bit of bloat to the kernel for only cosmetic reasons. --- apps/repl/src/cmds/audio.rs | 37 +++++++++++++++-- services/root-keys/src/main.rs | 6 +-- services/shellchat/src/cmds/aes_cmd.rs | 2 +- services/shellchat/src/cmds/ecup.rs | 10 ++--- services/shellchat/src/cmds/engine.rs | 2 +- services/shellchat/src/cmds/memtest.rs | 2 +- services/shellchat/src/cmds/sensors.rs | 17 ++++---- services/shellchat/src/cmds/sha.rs | 2 +- services/shellchat/src/cmds/sleep.rs | 8 ++-- services/shellchat/src/cmds/test.rs | 42 ++++++++++++++++--- services/shellchat/src/cmds/trng_cmd.rs | 20 ++++----- services/status/src/main.rs | 24 ++++++----- services/status/src/time.rs | 54 +++++++++++++++++++------ 13 files changed, 161 insertions(+), 65 deletions(-) diff --git a/apps/repl/src/cmds/audio.rs b/apps/repl/src/cmds/audio.rs index 10f814de3..ce4cbeaec 100644 --- a/apps/repl/src/cmds/audio.rs +++ b/apps/repl/src/cmds/audio.rs @@ -30,6 +30,37 @@ const STOP_ID: usize = 1; const SAMPLE_RATE_HZ: f32 = 8000.0; // note to self: A4 = 440.0, E4 = 329.63, C4 = 261.63 +use std::num::ParseIntError; +/// this will parse a simple decimal into an i32, multiplied by 1000 +/// we do this because the full f32 parsing stuff is pretty heavy, some +/// 28kiB of code +#[inline(never)] +fn simple_kilofloat_parse(input: &str) -> core::result::Result { + if let Some((integer, fraction)) = input.split_once('.') { + let mut result = integer.parse::()? * 1000; + let mut significance = 100i32; + for (place, digit) in fraction.chars().enumerate() { + if place >= 3 { + break; + } + if let Some(d) = digit.to_digit(10) { + if result >= 0 { + result += (d as i32) * significance; + } else { + result -= (d as i32) * significance; + } + significance /= 10; + } else { + return "z".parse::() // you can't create a ParseIntError any other way + } + } + Ok(result) + } else { + let base = input.parse::()?; + Ok(base * 1000) + } +} + impl<'a> ShellCmdApi<'a> for Audio { cmd_api!(audio); @@ -42,15 +73,15 @@ impl<'a> ShellCmdApi<'a> for Audio { match sub_cmd { "tone" => { self.freq = if let Some(freq_str) = tokens.next() { - match freq_str.parse::() { - Ok(f) => f, + match simple_kilofloat_parse(freq_str) { + Ok(f) => (f as f32) / 1000.0, Err(_) => 440.0, } } else { 440.0 }; let mut duration: f32 = if let Some(duration_str) = tokens.next() { - duration_str.parse::().unwrap_or(0.5) + simple_kilofloat_parse(duration_str).unwrap_or(500) as f32 / 1000.0 } else { 0.5 }; diff --git a/services/root-keys/src/main.rs b/services/root-keys/src/main.rs index 7154db7d3..eb3aa97d5 100644 --- a/services/root-keys/src/main.rs +++ b/services/root-keys/src/main.rs @@ -578,9 +578,9 @@ fn main() -> ! { llio.boost_on(false).unwrap(); ticktimer.sleep_ms(50).unwrap(); // give some time for the voltage to move - let vbus = (llio.adc_vbus().unwrap() as f64) * 0.005033; - log::info!("Vbus is: {:.3}V", vbus); - if vbus > 1.5 { + let vbus = (llio.adc_vbus().unwrap() as u32) * 503; + log::info!("Vbus is: {}mV", vbus / 100); + if vbus > 150_000 { // 1.5V // if power is plugged in, request that it be removed modals.show_notification(t!("rootkeys.init.unplug_power", xous::LANG), None).expect("modals error"); log::info!("vbus is high, holding off on reboot"); diff --git a/services/shellchat/src/cmds/aes_cmd.rs b/services/shellchat/src/cmds/aes_cmd.rs index 60bb4951f..38e9c70b3 100644 --- a/services/shellchat/src/cmds/aes_cmd.rs +++ b/services/shellchat/src/cmds/aes_cmd.rs @@ -343,7 +343,7 @@ impl<'a> ShellCmdApi<'a> for Aes { xous::msg_scalar_unpack!(msg, pass, hw_mode, keybits, _, { let end = env.ticktimer.elapsed_ms(); - let elapsed: f64 = ((end - self.start_time.unwrap()) as f64) / (TEST_ITERS as f64 * (TEST_MAX_LEN / aes::BLOCK_SIZE) as f64); + let elapsed: f32= ((end - self.start_time.unwrap()) as f32) / (TEST_ITERS as f32 * (TEST_MAX_LEN / aes::BLOCK_SIZE) as f32); let modestr = if hw_mode != 0 { &"hw" } else { &"sw" }; if pass != 0 { write!(ret, "[{}] passed: {:.02}µs/block enc+dec AES{}", modestr, elapsed * 1000.0, keybits).unwrap(); diff --git a/services/shellchat/src/cmds/ecup.rs b/services/shellchat/src/cmds/ecup.rs index a8c7d058b..dc4de09ac 100644 --- a/services/shellchat/src/cmds/ecup.rs +++ b/services/shellchat/src/cmds/ecup.rs @@ -127,7 +127,7 @@ fn do_update(com: &mut com::Com, callback_conn: xous::CID, package: &[u8], pkg_o prog_addr += 1024; progress_ctr += 1; if (progress_ctr % 12) == 0 { - write!(update_str, "{:.0}% complete", ((prog_addr - flash_start) as f64 / image_len as f64) * 100.0).unwrap(); + write!(update_str, "{}% complete", ((prog_addr - flash_start) * 100) / image_len).unwrap(); Buffer::into_buf(update_str).unwrap().lend(callback_conn, CB_ID.load(Ordering::Relaxed)).unwrap(); update_str.clear(); } @@ -480,7 +480,7 @@ impl<'a> ShellCmdApi<'a> for EcUpdate { write!(ret, "Starting EC wf200 update").unwrap(); } "auto" => { - if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 1.5V // if power is plugged in, deny powerdown request write!(ret, "Can't EC auto update while charging. Unplug charging cable and try again.").unwrap(); return Ok(Some(ret)); @@ -523,13 +523,13 @@ impl<'a> ShellCmdApi<'a> for EcUpdate { } else { // otherwise, unpack it and use the first argument as a sub-opcode type xous::msg_scalar_unpack!(msg, result_code, progress, _, _, { let end = env.ticktimer.elapsed_ms(); - let elapsed: f64 = (end - self.start_time.unwrap()) as f64; + let elapsed = end - self.start_time.unwrap(); match FromPrimitive::from_usize(result_code) { Some(UpdateResult::PackageValid) => { write!(ret, "Firmware package validated in {}ms", elapsed).unwrap(); }, Some(UpdateResult::ProgramDone) => { - write!(ret, "Programming of {} bytes done in {:.1}s. Please restart EC with `ecup reset`.", progress, elapsed / 1000.0).unwrap(); + write!(ret, "Programming of {} bytes done in {}s. Please restart EC with `ecup reset`.", progress, elapsed / 1000).unwrap(); env.netmgr.connection_manager_run().unwrap(); self.in_progress = false; }, @@ -539,7 +539,7 @@ impl<'a> ShellCmdApi<'a> for EcUpdate { self.in_progress = false; }, Some(UpdateResult::Abort) => { - write!(ret, "Programming aborted in {:.1}s. Did you stage all the firmware objects?", elapsed / 1000.0).unwrap(); + write!(ret, "Programming aborted in {}s. Did you stage all the firmware objects?", elapsed / 1000).unwrap(); env.netmgr.connection_manager_run().unwrap(); self.in_progress = false; } diff --git a/services/shellchat/src/cmds/engine.rs b/services/shellchat/src/cmds/engine.rs index eff37f5b1..50db990a0 100644 --- a/services/shellchat/src/cmds/engine.rs +++ b/services/shellchat/src/cmds/engine.rs @@ -549,7 +549,7 @@ impl<'a> ShellCmdApi<'a> for Engine { xous::msg_scalar_unpack!(msg, passes, fails, result_type, iters, { let end = env.ticktimer.elapsed_ms(); - let elapsed: f64 = ((end - self.start_time.unwrap()) as f64) / iters as f64; + let elapsed: f32 = ((end - self.start_time.unwrap()) as f32) / iters as f32; match FromPrimitive::from_usize(result_type) { Some(BenchResult::EngineDone) => { write!(ret, "{}ms/check_iter; In total, Engine passed {} vectors, failed {} vectors", elapsed, passes, fails).unwrap(); diff --git a/services/shellchat/src/cmds/memtest.rs b/services/shellchat/src/cmds/memtest.rs index 6717c4484..723d4d65c 100644 --- a/services/shellchat/src/cmds/memtest.rs +++ b/services/shellchat/src/cmds/memtest.rs @@ -191,7 +191,7 @@ impl<'a> ShellCmdApi<'a> for Memtest { xous::msg_scalar_unpack!(msg, errs, iters, dummy, _, { let end = env.ticktimer.elapsed_ms(); - let elapsed: f64 = ((end - self.start_time.unwrap()) as f64) / iters as f64; + let elapsed: f32 = ((end - self.start_time.unwrap()) as f32) / iters as f32; write!(ret, "memtest finished: {} errs, {}ms/iter", errs, elapsed).unwrap(); log::info!("dummy var: {}", dummy); // just make sure it's not optimized out for any reason! }); diff --git a/services/shellchat/src/cmds/sensors.rs b/services/shellchat/src/cmds/sensors.rs index 3b17ca8a3..beff3d36d 100644 --- a/services/shellchat/src/cmds/sensors.rs +++ b/services/shellchat/src/cmds/sensors.rs @@ -18,14 +18,15 @@ impl<'a> ShellCmdApi<'a> for Sensors { use core::fmt::Write; let mut ret = String::<1024>::new(); - write!(ret, "Vbus {:.2}V\nVint {:.2}V\nVaux {:.2}V\nVbram {:.2}V\nUSB {:.2}|{:.2}V\nTemp {:.1}°C", - (env.llio.adc_vbus().unwrap() as f64) * 0.005033, - (env.llio.adc_vccint().unwrap() as f64) / 1365.0, - (env.llio.adc_vccaux().unwrap() as f64) / 1365.0, - (env.llio.adc_vccbram().unwrap() as f64) / 1365.0, - (env.llio.adc_usb_p().unwrap() as f64) / 1365.0, - (env.llio.adc_usb_n().unwrap() as f64) / 1365.0, - ((env.llio.adc_temperature().unwrap() as f64) * 0.12304) - 273.15, + let milli_celcius = (((env.llio.adc_temperature().unwrap() as u32) * 12304) - 27_315_000) / 100; + write!(ret, "Vbus {:.2}mV\nVint {:.2}mV\nVaux {:.2}mV\nVbram {:.2}mV\nUSB {:.2}|{:.2}mV\nTemp {}.{}°C", + ((env.llio.adc_vbus().unwrap() as u32) * 503) / 100, + ((env.llio.adc_vccint().unwrap() as u32) * 1000) / 1365, + ((env.llio.adc_vccaux().unwrap() as u32) * 1000) / 1365, + ((env.llio.adc_vccbram().unwrap() as u32) * 1000) / 1365, + ((env.llio.adc_usb_p().unwrap() as u32) * 1000) / 1365, + ((env.llio.adc_usb_n().unwrap() as u32) * 1000) / 1365, + milli_celcius / 1000, (milli_celcius % 1000) / 10 ).unwrap(); Ok(Some(ret)) diff --git a/services/shellchat/src/cmds/sha.rs b/services/shellchat/src/cmds/sha.rs index 101a88eed..e307e0343 100644 --- a/services/shellchat/src/cmds/sha.rs +++ b/services/shellchat/src/cmds/sha.rs @@ -254,7 +254,7 @@ impl<'a> ShellCmdApi<'a> for Sha { xous::msg_scalar_unpack!(msg, pass, first_time, hw_mode, _, { let end = env.ticktimer.elapsed_ms(); - let elapsed: f64 = ((end - self.start_time.unwrap()) as f64) / TEST_ITERS as f64; + let elapsed: f32 = ((end - self.start_time.unwrap()) as f32) / TEST_ITERS as f32; let modestr = if hw_mode != 0 { &"hw" } else { &"sw" }; if first_time != 0 { write!(ret, "[{}] first pass: {}ms/hash", modestr, elapsed).unwrap(); diff --git a/services/shellchat/src/cmds/sleep.rs b/services/shellchat/src/cmds/sleep.rs index fe004559e..f2c126aab 100644 --- a/services/shellchat/src/cmds/sleep.rs +++ b/services/shellchat/src/cmds/sleep.rs @@ -91,7 +91,7 @@ impl<'a> ShellCmdApi<'a> for Sleep { write!(ret, "Starting suspend/resume stress test. Hard reboot required to exit.").unwrap(); } "now" => { - if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 1.5V // if power is plugged in, deny powerdown request write!(ret, "System can't sleep while charging. Unplug charging cable and try again.").unwrap(); } else { @@ -121,7 +121,7 @@ impl<'a> ShellCmdApi<'a> for Sleep { } } "ship" => { - if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 1.5V // if power is plugged in, deny powerdown request write!(ret, "System can't go into ship mode while charging. Unplug charging cable and try again.").unwrap(); } else { @@ -147,7 +147,7 @@ impl<'a> ShellCmdApi<'a> for Sleep { } } "coldboot" => { - if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // if power is plugged in, deny powerdown request write!(ret, "System can't cold boot while charging. Unplug charging cable and try again.").unwrap(); } else { @@ -177,7 +177,7 @@ impl<'a> ShellCmdApi<'a> for Sleep { xous::create_thread_1(kill_thread, 0).unwrap(); } "killbounce" => { - if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // if power is plugged in, deny powerdown request write!(ret, "Unplug charging cable and try again.").unwrap(); } else { diff --git a/services/shellchat/src/cmds/test.rs b/services/shellchat/src/cmds/test.rs index 2abad0a3c..e6e1c4625 100644 --- a/services/shellchat/src/cmds/test.rs +++ b/services/shellchat/src/cmds/test.rs @@ -91,6 +91,36 @@ enum TestOp { ModalDrop, } +use std::num::ParseIntError; +/// this will parse a simple decimal into an i32, multiplied by 1000 +/// we do this because the full f32 parsing stuff is pretty heavy, some +/// 28kiB of code +#[inline(never)] +fn simple_kilofloat_parse(input: &str) -> core::result::Result { + if let Some((integer, fraction)) = input.split_once('.') { + let mut result = integer.parse::()? * 1000; + let mut significance = 100i32; + for (place, digit) in fraction.chars().enumerate() { + if place >= 3 { + break; + } + if let Some(d) = digit.to_digit(10) { + if result >= 0 { + result += (d as i32) * significance; + } else { + result -= (d as i32) * significance; + } + significance /= 10; + } else { + return "z".parse::() // you can't create a ParseIntError any other way + } + } + Ok(result) + } else { + let base = input.parse::()?; + Ok(base * 1000) + } +} impl<'a> ShellCmdApi<'a> for Test { cmd_api!(test); @@ -166,8 +196,8 @@ impl<'a> ShellCmdApi<'a> for Test { let mut ht = env.trng.get_health_tests().unwrap(); for _ in 0..3 { // run the test 3 times av_excurs = [ - (((ht.av_excursion[0].max as f64 - ht.av_excursion[0].min as f64) / 4096.0) * 1000.0) as u32, - (((ht.av_excursion[1].max as f64 - ht.av_excursion[1].min as f64) / 4096.0) * 1000.0) as u32, + (((ht.av_excursion[0].max as f32 - ht.av_excursion[0].min as f32) / 4096.0) * 1000.0) as u32, + (((ht.av_excursion[1].max as f32 - ht.av_excursion[1].min as f32) / 4096.0) * 1000.0) as u32, ]; // 78mv minimum excursion requirement for good entropy generation if av_excurs[0] < 78 { av_pass[0] = false; } @@ -310,8 +340,8 @@ impl<'a> ShellCmdApi<'a> for Test { } "astart" => { self.freq = if let Some(freq_str) = tokens.next() { - match freq_str.parse::() { - Ok(f) => f, + match simple_kilofloat_parse(freq_str) { + Ok(f) => (f as f32) / 1000.0, Err(_) => 440.0, } } else { @@ -400,7 +430,7 @@ impl<'a> ShellCmdApi<'a> for Test { log::info!("{}|ASTOP|", SENTINEL); } "oqc" => { - if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 0.005033 * 100_000 against 1.5V * 100_000 // if power is plugged in, deny powerdown request write!(ret, "Can't run OQC test while charging. Unplug charging cable and try again.").unwrap(); return Ok(Some(ret)); @@ -583,7 +613,7 @@ impl<'a> ShellCmdApi<'a> for Test { write!(ret, "devboot off").unwrap(); } "ship" => { - if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 0.005033 * 100_000 against 1.5V * 100_000 // if power is plugged in, deny powerdown request write!(ret, "System can't go into ship mode while charging. Unplug charging cable and try again.").unwrap(); } else { diff --git a/services/shellchat/src/cmds/trng_cmd.rs b/services/shellchat/src/cmds/trng_cmd.rs index fb2f075ae..f8a1f1f7a 100644 --- a/services/shellchat/src/cmds/trng_cmd.rs +++ b/services/shellchat/src/cmds/trng_cmd.rs @@ -42,19 +42,19 @@ impl<'a> ShellCmdApi<'a> for TrngCmd { } "excur" => { let ht = env.trng.get_health_tests().unwrap(); - write!(ret, "AV0: {:.0}/{:.0} mV\n" - ,(ht.av_excursion[0].min as f64 / 4096.0) * 1000.0 - ,(ht.av_excursion[0].max as f64 / 4096.0) * 1000.0 + write!(ret, "AV0: {}/{} mV\n" + ,((ht.av_excursion[0].min as u32 * 1000) / 4096) + ,((ht.av_excursion[0].max as u32 * 1000) / 4096) ).unwrap(); - write!(ret, "AV0 delta: {:.0} mV\n" - ,((ht.av_excursion[0].max as f64 - ht.av_excursion[0].min as f64) / 4096.0) * 1000.0 + write!(ret, "AV0 delta: {} mV\n" + ,(((ht.av_excursion[0].max as u32 - ht.av_excursion[0].min as u32) * 1000) / 4096) ).unwrap(); - write!(ret, "AV1: {:.0}/{:.0} mV\n" - ,(ht.av_excursion[1].min as f64 / 4096.0) * 1000.0 - ,(ht.av_excursion[1].max as f64 / 4096.0) * 1000.0 + write!(ret, "AV1: {}/{} mV\n" + ,((ht.av_excursion[1].min as u32 * 1000) / 4096) + ,((ht.av_excursion[1].max as u32 * 1000) / 4096) ).unwrap(); - write!(ret, "AV1 delta: {:.0} mV\n" - ,((ht.av_excursion[1].max as f64 - ht.av_excursion[1].min as f64) / 4096.0) * 1000.0 + write!(ret, "AV1 delta: {} mV\n" + ,(((ht.av_excursion[1].max as u32 - ht.av_excursion[1].min as u32) * 1000 ) / 4096) ).unwrap(); } "pump" => { diff --git a/services/status/src/main.rs b/services/status/src/main.rs index bf042c287..ebeb8e5db 100644 --- a/services/status/src/main.rs +++ b/services/status/src/main.rs @@ -377,17 +377,21 @@ fn main() -> ! { write!(&mut battstats_tv, "{}", t!("stats.measuring", xous::LANG)).unwrap(); } else { // toggle between two views of the data every time we have a status update - let mut wattage = stats.current as f32 / 1000.0 * stats.voltage as f32 / 1000.0; - let sign = if wattage > 0.005 { + let mut wattage_mw = (stats.current as i32 * stats.voltage as i32) / 1000i32; + let sign = if wattage_mw > 5 { '\u{2b06}' // up arrow - } else if wattage < -0.005 { + } else if wattage_mw < -5 { '\u{2b07}' // down arrow } else { '\u{1f50c}' // plugged in icon (e.g., fully charged, running on wall power now) }; - wattage = wattage.abs(); + wattage_mw = wattage_mw.abs(); if battstats_phase { - write!(&mut battstats_tv, "{:.3}W{}{:.2}V {}%", wattage, sign, stats.voltage as f32 / 1000.0, stats.soc).unwrap(); + write!(&mut battstats_tv, "{}.{}W{}{}.{}V {}%", + wattage_mw / 1000, wattage_mw % 1000, + sign, + stats.voltage as u32 / 1000, (stats.voltage as u32 % 1000) / 10, // 2 decimal places + stats.soc).unwrap(); } else { if let Some(ssid) = wifi_status.ssid { write!( @@ -431,7 +435,7 @@ fn main() -> ! { let (latest_activity, period) = llio .activity_instantaneous() .expect("couldn't get CPU activity"); - let activity_to_width = ((latest_activity as f32) / (period as f32)) * (cpuload_rect.width() - 4) as f32; + let activity_to_width = (((latest_activity * 1000) / (period)) * (cpuload_rect.width() - 4) as u32) / 1000; draw_list.push(GamObjectType::Rect( Rectangle::new_coords_with_style( cpuload_rect.tl().x + 2, @@ -503,7 +507,7 @@ fn main() -> ! { // confirm that the charger is in the right state. if stats.soc < 95 || stats.remaining_capacity < 1000 { // only request if we aren't fully charged, either by SOC or capacity metrics - if (llio.adc_vbus().unwrap() as f64) * 0.005033 > 4.45 { + if (llio.adc_vbus().unwrap() as u32) * 503 > 445_000 { // 0.005033 * 100_000 against 4.45V * 100_000 // 4.45V is our threshold for deciding if a cable is present // charging cable is present if !com.is_charging().expect("couldn't check charging state") { @@ -575,7 +579,7 @@ fn main() -> ! { stats_phase = stats_phase.wrapping_add(1); } Some(StatusOpcode::Reboot) => { - if ((llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 0.005033 * 100_000 against 1.5V * 100_000 // power plugged in, do a reboot using a warm boot method susres.reboot(true).expect("couldn't issue reboot command"); } else { @@ -638,14 +642,14 @@ fn main() -> ! { ).expect("couldn't trigger status update"); }), Some(StatusOpcode::TrySuspend) => { - if ((llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { modals.show_notification(t!("mainmenu.cant_sleep", xous::LANG), None).expect("couldn't notify that power is plugged in"); } else { susres.initiate_suspend().expect("couldn't initiate suspend op"); } }, Some(StatusOpcode::BatteryDisconnect) => { - if ((llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 { + if ((llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { modals.show_notification(t!("mainmenu.cant_sleep", xous::LANG), None).expect("couldn't notify that power is plugged in"); } else { gam.shipmode_blank_request().ok(); diff --git a/services/status/src/time.rs b/services/status/src/time.rs index 4a9fe6789..8335c1877 100644 --- a/services/status/src/time.rs +++ b/services/status/src/time.rs @@ -36,6 +36,7 @@ use gam::modal::*; // ntp imports use sntpc::{Error, NtpContext, NtpTimestampGenerator, NtpUdpSocket, Result}; use std::net::{SocketAddr, ToSocketAddrs, UdpSocket}; +use std::num::ParseIntError; /// This is a "well known name" used by `libstd` to connect to the time server /// Even thought it is "public" nobody connects to it directly, they connect to it via `libstd` @@ -478,6 +479,36 @@ fn to_bcd(binary: u8) -> u8 { (msd << 4) | lsd } +/// this will parse a simple decimal into an i32, multiplied by 1000 +/// we do this because the full f32 parsing stuff is pretty heavy, some +/// 28kiB of code +#[inline(never)] +fn simple_kilofloat_parse(input: &str) -> core::result::Result { + if let Some((integer, fraction)) = input.split_once('.') { + let mut result = integer.parse::()? * 1000; + let mut significance = 100i32; + for (place, digit) in fraction.chars().enumerate() { + if place >= 3 { + break; + } + if let Some(d) = digit.to_digit(10) { + if result >= 0 { + result += (d as i32) * significance; + } else { + result -= (d as i32) * significance; + } + significance /= 10; + } else { + return "z".parse::() // you can't create a ParseIntError any other way + } + } + Ok(result) + } else { + let base = input.parse::()?; + Ok(base * 1000) + } +} + pub fn start_time_ux(sid: xous::SID) { thread::spawn({ move || { @@ -517,15 +548,14 @@ pub fn start_time_ux(sid: xous::SID) { // a key exists, but nothing was written to it (length of key was 0 or inappropriate) if !tz_set { log::info!("{}RTC.TZ,{}", xous::BOOKEND_START, xous::BOOKEND_END); - let tz = modals.alert_builder(t!("rtc.timezone", xous::LANG)) + let tz_str = modals.alert_builder(t!("rtc.timezone", xous::LANG)) .field(None, Some(tz_ux_validator)) .build() .expect("couldn't get timezone") - .first() - .as_str() - .parse::().expect("pre-validated input failed to re-parse!"); + .first(); + let tz = simple_kilofloat_parse(tz_str.as_str()).expect("pre-validated input failed to re-parse!"); log::info!("got tz offset {}", tz); - tz_offset_ms = (tz * 3600.0 * 1000.0) as i64; + tz_offset_ms = (tz * 3600) as i64; xous::send_message(timeserver_cid, Message::new_scalar( crate::time::TimeOp::SetTzOffsetMs.to_usize().unwrap(), @@ -632,15 +662,15 @@ pub fn start_time_ux(sid: xous::SID) { continue; } - let tz = modals.alert_builder(t!("rtc.timezone", xous::LANG)) + let tz_str = modals.alert_builder(t!("rtc.timezone", xous::LANG)) .field(None, Some(tz_ux_validator)) .build() .expect("couldn't get timezone") - .first() - .as_str() - .parse::().expect("pre-validated input failed to re-parse!"); + .first(); + let tz = simple_kilofloat_parse(tz_str.as_str()) + .expect("pre-validated input failed to re-parse!"); log::info!("got tz offset {}", tz); - let tzoff_ms = (tz * 3600.0 * 1000.0) as i64; + let tzoff_ms = (tz * 3600) as i64; xous::send_message(timeserver_cid, Message::new_scalar( crate::time::TimeOp::SetTzOffsetMs.to_usize().unwrap(), @@ -678,8 +708,8 @@ pub(crate) enum ValidatorOp { fn tz_ux_validator(input: TextEntryPayload) -> Option { let text_str = input.as_str(); - match text_str.parse::() { - Ok(input) => if input < -12.0 || input > 14.0 { + match simple_kilofloat_parse(text_str) { + Ok(input) => if input < -12_000 || input > 14_000 { return Some(ValidatorErr::from_str(t!("rtc.range_err", xous::LANG))); }, _ => return Some(ValidatorErr::from_str(t!("rtc.integer_err", xous::LANG))),