moddef_core/codec/
encode.rs1use crate::codec::bytes::{mask_for, words_from_bytes, words_from_u64};
8use crate::codec::decode::Ctx;
9use crate::codec::rat::Rat;
10use crate::desc::{PointDesc, ScaleMode, StorageType, ValueKind};
11use crate::error::{ConstraintKind, EncodeError};
12use crate::value::Value;
13
14pub fn encode(
16 p: &PointDesc<'_>,
17 v: &Value,
18 ctx: &Ctx<'_>,
19 out: &mut [u16],
20) -> Result<(), EncodeError> {
21 if out.len() < p.words() {
22 return Err(EncodeError::BufferTooSmall);
23 }
24 let out = &mut out[..p.words()];
25
26 match p.storage {
27 StorageType::Composed => return Err(EncodeError::Unsupported),
28 StorageType::F32 => {
29 let f = v.as_f64().ok_or(EncodeError::WrongValueType)? as f32;
30 words_from_u64(f.to_bits() as u64, p.byte_big, p.word_big, out);
31 return Ok(());
32 }
33 StorageType::F64 => {
34 let f = v.as_f64().ok_or(EncodeError::WrongValueType)?;
35 words_from_u64(f.to_bits(), p.byte_big, p.word_big, out);
36 return Ok(());
37 }
38 StorageType::StringAscii | StorageType::StringUtf8 | StorageType::BytesRaw => {
39 return Err(EncodeError::WrongValueType); }
41 _ => {}
42 }
43
44 let bits = p.storage.bits(out.len());
45 let raw: u64 = match (&p.value, v) {
46 (ValueKind::Flags(_), Value::Flags(mask)) => *mask & mask_for(bits),
47 (ValueKind::Fields(_), _) => return Err(EncodeError::Unsupported),
48 (ValueKind::Bool, v) => (v.as_i64().ok_or(EncodeError::WrongValueType)? != 0) as u64,
49 (ValueKind::DateTime(_), Value::DateTime(t)) => *t as u64 & mask_for(bits),
50 (ValueKind::Decimal, v) => {
51 let val = match v {
52 Value::F64(f) => Rat::from_f64(*f),
53 Value::I64(i) => Rat::int(*i),
54 Value::U64(u) => Rat::from_u64(*u),
55 _ => return Err(EncodeError::WrongValueType),
56 };
57 (encode_scaled(val, p, ctx)? as u64) & mask_for(bits)
58 }
59 (_, v) => {
60 let i = v.as_i64().ok_or(EncodeError::WrongValueType)?;
61 (i as u64) & mask_for(bits)
62 }
63 };
64
65 let raw = if p.storage == StorageType::Bcd {
66 int_to_bcd(raw as i64)
67 } else {
68 raw
69 };
70 words_from_u64(raw, p.byte_big, p.word_big, out);
71 Ok(())
72}
73
74pub fn encode_str(p: &PointDesc<'_>, s: &str, out: &mut [u16]) -> Result<(), EncodeError> {
76 if out.len() < p.words() {
77 return Err(EncodeError::BufferTooSmall);
78 }
79 words_from_bytes(s.as_bytes(), p.byte_big, p.word_big, &mut out[..p.words()]);
80 Ok(())
81}
82
83pub fn encode_bytes(p: &PointDesc<'_>, b: &[u8], out: &mut [u16]) -> Result<(), EncodeError> {
85 if out.len() < p.words() {
86 return Err(EncodeError::BufferTooSmall);
87 }
88 words_from_bytes(b, p.byte_big, p.word_big, &mut out[..p.words()]);
89 Ok(())
90}
91
92fn encode_scaled(mut val: Rat, p: &PointDesc<'_>, ctx: &Ctx<'_>) -> Result<i64, EncodeError> {
94 if let Some(o) = p.offset_add {
95 if o.den != 0 {
96 val = val.sub(Rat::new(o.num as i128, o.den as i128));
97 }
98 }
99 if let Some(sr) = &p.scale_ref {
100 let sf = ctx.get(sr.point_id).ok_or(EncodeError::UnresolvedRef)?;
101 match sr.mode {
102 ScaleMode::Pow10 => val = val.div(Rat::pow10(sf)),
103 ScaleMode::Multiply => {
104 let den = if sr.denominator == 0 {
105 1
106 } else {
107 sr.denominator
108 };
109 val = val.div(Rat::new(sf as i128, den as i128));
110 }
111 }
112 } else if let Some(s) = p.scale {
113 if s.den != 0 {
114 val = val.div(Rat::new(s.num as i128, s.den as i128));
115 }
116 }
117 Ok(val.round())
118}
119
120pub fn validate_write(p: &PointDesc<'_>, v: &Value) -> Result<(), ConstraintKind> {
122 let Some(w) = &p.write else { return Ok(()) };
123 let num = v.as_f64();
124
125 if !w.allowed.is_empty() {
126 let iv = num.map(|f| libm_round(f) as i64);
127 match iv {
128 Some(iv) if w.allowed.contains(&iv) => return Ok(()),
129 _ => return Err(ConstraintKind::AllowedValues),
130 }
131 }
132 let Some(num) = num else { return Ok(()) };
133 if let Some(min) = w.min {
134 if min.den != 0 && num < min.num as f64 / min.den as f64 {
135 return Err(ConstraintKind::Min);
136 }
137 }
138 if let Some(max) = w.max {
139 if max.den != 0 && num > max.num as f64 / max.den as f64 {
140 return Err(ConstraintKind::Max);
141 }
142 }
143 if let Some(step) = w.step {
144 if step.den != 0 && step.num != 0 {
145 let s = step.num as f64 / step.den as f64;
146 let base = w
147 .min
148 .filter(|m| m.den != 0)
149 .map(|m| m.num as f64 / m.den as f64)
150 .unwrap_or(0.0);
151 let k = (num - base) / s;
152 if (k - libm_round(k)).abs() > 1e-9 {
153 return Err(ConstraintKind::Step);
154 }
155 }
156 }
157 Ok(())
158}
159
160fn int_to_bcd(mut v: i64) -> u64 {
161 let mut raw: u64 = 0;
162 let mut shift = 0;
163 while v > 0 {
164 raw |= ((v % 10) as u64) << shift;
165 shift += 4;
166 v /= 10;
167 }
168 raw
169}
170
171#[inline]
174fn libm_round(f: f64) -> f64 {
175 if f >= 0.0 {
176 (f + 0.5) as i64 as f64
177 } else {
178 (f - 0.5) as i64 as f64
179 }
180}