From 01f4a4a50cb7e6404f0cfa2c374ca95dc38e26a8 Mon Sep 17 00:00:00 2001
From: Benjamin Koch <bbbsnowball@gmail.com>
Date: Sun, 11 Jun 2023 05:07:15 +0200
Subject: [PATCH] add ConstString class for building strings in constants

---
 firmware/rust1/src/bin/heizung.rs  |  51 +++----------
 firmware/rust1/src/program_info.rs | 114 ++++++++++++++++++++++++-----
 2 files changed, 107 insertions(+), 58 deletions(-)

diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs
index a0d06aa..9c00da0 100644
--- a/firmware/rust1/src/bin/heizung.rs
+++ b/firmware/rust1/src/bin/heizung.rs
@@ -25,7 +25,7 @@ use heapless::String;
 
 use heizung::i2c_scan::{self, ScanResultForBus};
 use heizung::modbus_server::{ModbusServer, ModbusRegisters, ModbusErrorCode, ModbusAdressMatch, U16Pusher};
-use heizung::program_info::{ProgramInfoBuilder, PinFunction};
+use heizung::program_info::{ProgramInfoBuilder, PinFunction, ConstString};
 use heizung::rs485::RS485;
 use heizung::uf2updater::UF2UpdateHandler;
 use heizung::watchdog::WatchdogFixed;
@@ -577,47 +577,18 @@ async fn dogfeeder(watchdog: peripherals::WATCHDOG) {
 const DEFAULT_MODBUS_CONFIG_BAUDRATE: u32 = 19200;
 //const DEFAULT_MODBUS_CONFIG_BAUD = 115200*2;
 const DEFAULT_MODBUS_CONFIG_PARITY: Parity = Parity::ParityEven;
-const DEFAULT_MODBUS_CONFIG_STRING: ([u8; 30], usize) = {
-    let mut x = [0 as u8; 30];
-    x[0] = '8' as u8;
-    x[1] = match DEFAULT_MODBUS_CONFIG_PARITY {
+const DEFAULT_MODBUS_CONFIG_STRING: ConstString<30> = {
+    let mut x = ConstString::new();
+    x = x.append_u32(DEFAULT_MODBUS_CONFIG_BAUDRATE);
+    x = x.push_str(" baud, ");
+    x = x.push_str("8");
+    x = x.push(match DEFAULT_MODBUS_CONFIG_PARITY {
         Parity::ParityEven => 'E',
         Parity::ParityOdd => 'O',
         Parity::ParityNone => 'N',
-    } as u8;
-    x[2] = '1' as u8;
-    x[3] = ',' as u8;
-    x[4] = ' ' as u8;
-
-    const fn reverse(mut acc: ([u8; 30], usize), index: usize) -> ([u8; 30], usize) {
-        if index >= acc.1/2 {
-            acc
-        } else {
-            let x = acc.0[index];
-            acc.0[index] = acc.0[acc.1-index-1];
-            acc.0[acc.1-index-1] = x;
-            reverse(acc, index+1)
-        }
-    }
-    const fn num_to_str(mut acc: [u8; 30], index: usize, value: u32) -> ([u8; 30], usize) {
-        if value == 0 && index > 0 {
-            reverse((acc, index), 0)
-        } else {
-            acc[index] = '0' as u8 + (value % 10) as u8;
-            num_to_str(acc, index+1, value/10)
-        }
-    }
-    const fn append(mut acc: ([u8; 30], usize), index: usize, source: ([u8; 30], usize)) -> ([u8; 30], usize) {
-        if index >= source.1 {
-            acc
-        } else {
-            acc.0[acc.1] = source.0[index];
-            acc.1 += 1;
-            append(acc, index+1, source)
-        }
-    }
-
-    append((x, 5), 0, num_to_str([0 as u8; 30], 0, DEFAULT_MODBUS_CONFIG_BAUDRATE))
+    } as u8);
+    x = x.push_str("1");
+    x
 };
 fn default_modbus_config() -> uart::Config {
     let mut uart_config = uart::Config::default();
@@ -810,7 +781,7 @@ pub static PROGRAM_INFO: [u8; 4096] = {
             (29, "VCC Measurement"),
         ])
         .program_feature_group(['C', '3'], 0x27a7c59b, "Modbus")
-        .feature_utf8_part::<{DEFAULT_MODBUS_CONFIG_STRING.1}>(&DEFAULT_MODBUS_CONFIG_STRING.0)
+        .feature_utf8(&DEFAULT_MODBUS_CONFIG_STRING.to_slice::<{DEFAULT_MODBUS_CONFIG_STRING.len()}>())
         .group_done()
         .build()
 };
diff --git a/firmware/rust1/src/program_info.rs b/firmware/rust1/src/program_info.rs
index eddfa31..8ad8cd0 100644
--- a/firmware/rust1/src/program_info.rs
+++ b/firmware/rust1/src/program_info.rs
@@ -41,7 +41,7 @@ pub enum PinFunction {
     NULL = 0xf,
 }
 
-const fn copy(mut buf: [u8; 4096], offset: usize, source: &[u8], source_offset: usize) -> [u8; 4096] {
+const fn copy<const N: usize>(mut buf: [u8; N], offset: usize, source: &[u8], source_offset: usize) -> [u8; N] {
     if source_offset >= source.len() {
         buf
     } else {
@@ -50,18 +50,6 @@ const fn copy(mut buf: [u8; 4096], offset: usize, source: &[u8], source_offset:
     }
 }
 
-const fn copy2<const N: usize, const N2: usize>(mut acc: [u8; N], source: &[u8], index: usize) -> [u8; N] {
-    if index >= N2 {
-        acc
-    } else {
-        acc[index] = source[index];
-        copy2::<N, N2>(acc, source, index+1)
-    }
-}
-const fn copy3<const N: usize, const N2: usize>(source: &[u8]) -> [u8; N] {
-    copy2::<N, N2>([0; N], source, 0)
-}
-
 impl ProgramInfoBuilder {
     pub const fn new(addr: u32, info_table_size: u32, copy_table_size: u32) -> Self {
         let copy_table_size = copy_table_size as usize;
@@ -416,12 +404,102 @@ impl ProgramInfoBuilderForGroup {
         self.builder = self.builder.bi_string_utf8(self.tag, self.id, name);
         self
     }
-    pub const fn feature_utf8_part<const N: usize>(mut self: Self, name: &[u8]) -> Self {
-        let name: [u8; N] = copy3::<N, N>(name);
-        self.builder = self.builder.bi_string_utf8(self.tag, self.id, &name);
-        self
-    }
     pub const fn group_done(self: Self) -> ProgramInfoBuilder {
         self.builder
     }
 }
+
+
+pub struct ConstString<const N: usize> {
+    buf: [u8; N],
+    len: usize,
+}
+
+impl<const N: usize> ConstString<N> {
+    pub const fn new() -> Self {
+        ConstString { buf: [0; N], len: 0 }
+    }
+
+    pub const fn len(self: &Self) -> usize {
+        self.len
+    }
+
+    pub const fn to_slice<const N2: usize>(self: &Self) -> [u8; N2] {
+        core::assert!(N2 == self.len);
+
+        const fn copy<const N2: usize>(mut acc: [u8; N2], source: &[u8], index: usize) -> [u8; N2] {
+            if index >= N2 {
+                acc
+            } else {
+                acc[index] = source[index];
+                copy::<N2>(acc, source, index+1)
+            }
+        }
+
+        copy::<N2>([0; N2], &self.buf, 0)
+    }
+
+    pub const fn push(mut self: Self, value: u8) -> Self {
+        self.buf[self.len] = value;
+        self.len += 1;
+        self
+    }
+
+    pub const fn push_char(self: Self, value: char) -> Self {
+        self.push(value as u8)
+    }
+
+    pub const fn push_many(mut self: Self, xs: &[u8], from: usize, len: usize) -> Self {
+        const fn run<const N: usize>(mut acc: [u8; N], index: usize, offset: usize, xs: &[u8], from: usize, len: usize) -> [u8; N] {
+            if index >= len {
+                acc
+            } else {
+                acc[offset + index] = xs[from + index];
+                run(acc, index+1, offset, xs, from, len)
+            }
+        }
+        self.buf = run(self.buf, 0, self.len, xs, from, len);
+        self.len += len;
+        self
+    }
+
+    pub const fn push_const_string<const N2: usize>(self: Self, value: ConstString<N2>) -> Self {
+        self.push_many(&value.buf, 0, value.len)
+    }
+
+    pub const fn push_str(self: Self, value: &str) -> Self {
+        let xs = value.as_bytes();
+        self.push_many(xs, 0, xs.len())
+    }
+
+    pub const fn reverse(mut self: Self) -> Self {
+        const fn run<const N: usize>(mut acc: ([u8; N], usize), index: usize) -> ([u8; N], usize) {
+            if index >= acc.1/2 {
+                acc
+            } else {
+                let x = acc.0[index];
+                acc.0[index] = acc.0[acc.1-index-1];
+                acc.0[acc.1-index-1] = x;
+                run(acc, index+1)
+            }
+        }
+        self.buf = run((self.buf, self.len), 0).0;
+        self
+    }
+
+    pub const fn from_u32(value: u32) -> Self {
+        const fn num_to_str<const N: usize>(mut acc: ConstString<N>, value: u32) -> ConstString<N> {
+            if value == 0 && acc.len > 0 {
+                acc.reverse()
+            } else {
+                acc = acc.push('0' as u8 + (value % 10) as u8);
+                num_to_str(acc, value/10)
+            }
+        }
+        num_to_str(Self::new(), value)
+    }
+
+    pub const fn append_u32(self: Self, value: u32) -> Self {
+        self.push_const_string(Self::from_u32(value))
+    }
+}
-- 
GitLab