1use super::{BAD_LENGTH, HostType, NOT_ASSIGNABLE, OUT_OF_RANGE, SqlError, UNCONVERTIBLE, Value};
6use crate::codec;
7use crate::vocab::{SignClause, SignPosition};
8use numeric::Numproc;
9use zarch::check::ProgramCheck;
10use zarch::decimal::{self, Decimal};
11use zarch::ebcdic::CodePage;
12use zarch::hfp::{Hfp, Precision};
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum ReadError {
18 Check(ProgramCheck),
19 Sql(SqlError),
20}
21
22#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
25pub struct Written {
26 pub truncated_from: Option<usize>,
27}
28
29pub fn read(bytes: &[u8], ty: &HostType, page: &CodePage, numproc: Numproc) -> Result<Value, ReadError> {
31 let as_value = |d: Decimal, scale: u32| {
32 let magnitude = d.magnitude as i128;
33 Value::Decimal { value: if d.negative { -magnitude } else { magnitude }, scale }
34 };
35 Ok(match *ty {
36 HostType::SmallInt { signed } | HostType::Integer { signed } | HostType::BigInt { signed } => {
37 let v = integer(bytes, signed);
38 i64::try_from(v).map_or(Value::Decimal { value: v, scale: 0 }, Value::Int)
39 }
40 HostType::Decimal { scale, signed, .. } => as_value(codec::packed(bytes, signed, numproc).map_err(ReadError::Check)?, scale),
41 HostType::Zoned { scale, signed, sign, .. } => as_value(codec::zoned(bytes, signed, sign, numproc).map_err(ReadError::Check)?, scale),
42 HostType::Real => Value::Double(Hfp::from_bytes(Precision::Short, bytes).approx()),
43 HostType::Double => Value::Double(Hfp::from_bytes(Precision::Long, bytes).approx()),
44 HostType::Char(_) => Value::Char(page.decode(bytes)),
45 HostType::VarChar(max) => {
46 let len = i16::from_be_bytes([bytes[0], bytes[1]]);
47 if len < 0 || len as u32 > max {
48 return Err(ReadError::Sql(BAD_LENGTH));
49 }
50 Value::Char(page.decode(&bytes[2..2 + len as usize]))
51 }
52 HostType::Graphic(_) => Value::Char(page.decode_dbcs(bytes)),
53 HostType::VarGraphic(max) => {
54 let len = i16::from_be_bytes([bytes[0], bytes[1]]);
55 if len < 0 || len as u32 > max {
56 return Err(ReadError::Sql(BAD_LENGTH));
57 }
58 Value::Char(page.decode_dbcs(&bytes[2..2 + 2 * len as usize]))
59 }
60 HostType::Structure(_) => unreachable!("a host structure is read member by member"),
61 })
62}
63
64pub fn write(value: &Value, bytes: &mut [u8], ty: &HostType, page: &CodePage) -> Result<Written, SqlError> {
66 match *ty {
67 HostType::SmallInt { signed } | HostType::Integer { signed } | HostType::BigInt { signed } => store_integer(whole(value)?, bytes, signed)?,
68 HostType::Decimal { digits, scale, signed } => {
69 let v = fitted(scaled(value, scale)?, digits, signed)?;
70 decimal::encode(bytes, Decimal { negative: v < 0, magnitude: v.unsigned_abs() }).expect("a packed host variable is 1 to 16 bytes");
71 if !signed {
72 *bytes.last_mut().unwrap() |= 0x0F;
73 }
74 }
75 HostType::Zoned { digits, scale, signed, sign } => store_zoned(fitted(scaled(value, scale)?, digits, signed)?, bytes, signed, sign),
76 HostType::Real => bytes.copy_from_slice(&hfp_image(float(value)?, Precision::Short).ok_or(OUT_OF_RANGE)?),
77 HostType::Double => bytes.copy_from_slice(&hfp_image(float(value)?, Precision::Long).ok_or(OUT_OF_RANGE)?),
78 HostType::Char(_) => return store_text(&text(value, page)?, bytes, page),
79 HostType::VarChar(max) => {
80 let t = text(value, page)?;
81 let n = t.len().min(max as usize);
82 bytes[..2].copy_from_slice(&(n as u16).to_be_bytes());
83 bytes[2..2 + n].copy_from_slice(&t[..n]);
84 return Ok(Written { truncated_from: (t.len() > n).then_some(t.len()) });
85 }
86 HostType::Graphic(len) => {
87 let t = graphic(value, page)?;
88 let n = t.len().min(2 * len as usize);
89 bytes[..n].copy_from_slice(&t[..n]);
90 bytes[n..].fill(0x40);
91 return Ok(Written { truncated_from: (t.len() > n).then_some(t.len() / 2) });
92 }
93 HostType::VarGraphic(max) => {
94 let t = graphic(value, page)?;
95 let n = t.len().min(2 * max as usize);
96 bytes[..2].copy_from_slice(&((n / 2) as u16).to_be_bytes());
97 bytes[2..2 + n].copy_from_slice(&t[..n]);
98 return Ok(Written { truncated_from: (t.len() > n).then_some(t.len() / 2) });
99 }
100 HostType::Structure(_) => unreachable!("a host structure is written member by member"),
101 }
102 Ok(Written::default())
103}
104
105fn integer(bytes: &[u8], signed: bool) -> i128 {
106 let unsigned = bytes.iter().fold(0u128, |acc, &b| acc << 8 | u128::from(b)) as i128;
107 if signed && bytes[0] & 0x80 != 0 { unsigned - (1i128 << (8 * bytes.len())) } else { unsigned }
108}
109
110fn pow10(n: u32) -> Option<i128> {
111 10i128.checked_pow(n)
112}
113
114fn whole(value: &Value) -> Result<i128, SqlError> {
116 match *value {
117 Value::Int(v) => Ok(v.into()),
118 Value::Decimal { value, scale } => Ok(value / pow10(scale).ok_or(OUT_OF_RANGE)?),
119 Value::Double(f) if f.is_finite() && f.abs() < 1e38 => Ok(f.trunc() as i128),
120 Value::Double(_) => Err(OUT_OF_RANGE),
121 _ => Err(NOT_ASSIGNABLE),
122 }
123}
124
125fn scaled(value: &Value, scale: u32) -> Result<i128, SqlError> {
127 match *value {
128 Value::Int(v) => i128::from(v).checked_mul(pow10(scale).ok_or(OUT_OF_RANGE)?).ok_or(OUT_OF_RANGE),
129 Value::Decimal { value, scale: from } if from >= scale => Ok(value / pow10(from - scale).ok_or(OUT_OF_RANGE)?),
130 Value::Decimal { value, scale: from } => value.checked_mul(pow10(scale - from).ok_or(OUT_OF_RANGE)?).ok_or(OUT_OF_RANGE),
131 Value::Double(f) => {
132 let v = (f * 10f64.powi(scale as i32)).trunc();
133 if v.is_finite() && v.abs() < 1e38 { Ok(v as i128) } else { Err(OUT_OF_RANGE) }
134 }
135 _ => Err(NOT_ASSIGNABLE),
136 }
137}
138
139fn fitted(v: i128, digits: u32, signed: bool) -> Result<i128, SqlError> {
140 if (!signed && v < 0) || v.unsigned_abs() >= pow10(digits).ok_or(OUT_OF_RANGE)?.unsigned_abs() { Err(OUT_OF_RANGE) } else { Ok(v) }
141}
142
143fn store_integer(v: i128, bytes: &mut [u8], signed: bool) -> Result<(), SqlError> {
144 let bits = 8 * bytes.len() as u32;
145 let (low, high) = if signed { (-(1i128 << (bits - 1)), (1i128 << (bits - 1)) - 1) } else { (0, (1i128 << bits) - 1) };
146 if !(low..=high).contains(&v) {
147 return Err(OUT_OF_RANGE);
148 }
149 let image = v as u128;
150 for (i, b) in bytes.iter_mut().rev().enumerate() {
151 *b = (image >> (8 * i)) as u8;
152 }
153 Ok(())
154}
155
156fn store_zoned(v: i128, bytes: &mut [u8], signed: bool, sign: Option<SignClause>) {
160 let negative = v < 0;
161 let unpack = |zoned: &mut [u8]| {
162 let mut magnitude = v.unsigned_abs();
163 for byte in zoned.iter_mut().rev() {
164 *byte = 0xF0 | (magnitude % 10) as u8;
165 magnitude /= 10;
166 }
167 let (last, zone) = (zoned.len() - 1, if negative { 0xD0 } else { 0xC0 });
168 zoned[last] = zone | zoned[last] & 0x0F;
169 };
170 match sign {
171 Some(SignClause { separate: true, position }) => {
172 let n = bytes.len();
173 let (sign_at, digits_at) = if position == SignPosition::Leading { (0, 1..n) } else { (n - 1, 0..n - 1) };
174 unpack(&mut bytes[digits_at.clone()]);
175 bytes[digits_at.end - 1] |= 0xF0;
176 bytes[sign_at] = if negative { 0x60 } else { 0x4E };
177 }
178 Some(SignClause { separate: false, position: SignPosition::Leading }) => {
179 unpack(bytes);
180 let last = bytes.len() - 1;
181 let zone = bytes[last] & 0xF0;
182 bytes[last] |= 0xF0;
183 bytes[0] = zone | (bytes[0] & 0x0F);
184 }
185 _ => {
186 unpack(bytes);
187 if !signed {
188 *bytes.last_mut().unwrap() |= 0xF0;
189 }
190 }
191 }
192}
193
194fn float(value: &Value) -> Result<f64, SqlError> {
195 match *value {
196 Value::Int(v) => Ok(v as f64),
197 Value::Decimal { value, scale } => Ok(value as f64 / 10f64.powi(scale as i32)),
198 Value::Double(f) => Ok(f),
199 _ => Err(NOT_ASSIGNABLE),
200 }
201}
202
203fn hfp_image(f: f64, precision: Precision) -> Option<Vec<u8>> {
206 let len = precision.bytes();
207 if f == 0.0 {
208 return Some(vec![0; len]);
209 }
210 if !f.is_finite() {
211 return None;
212 }
213 let bits = f.to_bits();
214 let raw_exponent = ((bits >> 52) & 0x7FF) as i32;
215 let (mantissa, exponent) = match raw_exponent {
216 0 => (bits & ((1 << 52) - 1), -1074),
217 e => ((bits & ((1 << 52) - 1)) | 1 << 52, e - 1075),
218 };
219 let top = exponent + (64 - mantissa.leading_zeros() as i32);
220 let hex = top.div_euclid(4) + i32::from(top.rem_euclid(4) != 0);
221 let characteristic = hex + 64;
222 if !(0..=127).contains(&characteristic) {
223 return None;
224 }
225 let fraction_bits = if precision == Precision::Short { 24 } else { 56 };
226 let shift = exponent + fraction_bits - 4 * hex;
227 let fraction = if shift >= 0 { mantissa << shift } else { mantissa >> -shift };
228 let lead = u64::from(f.is_sign_negative()) << 7 | characteristic as u64;
229 Some(match precision {
230 Precision::Short => ((lead << 24 | fraction) as u32).to_be_bytes().to_vec(),
231 _ => (lead << 56 | fraction).to_be_bytes().to_vec(),
232 })
233}
234
235fn text(value: &Value, page: &CodePage) -> Result<Vec<u8>, SqlError> {
236 match value {
237 Value::Char(s) => page.encode(s).map_err(|_| UNCONVERTIBLE),
238 Value::Binary(b) => Ok(b.clone()),
239 _ => Err(NOT_ASSIGNABLE),
240 }
241}
242
243fn graphic(value: &Value, page: &CodePage) -> Result<Vec<u8>, SqlError> {
247 match value {
248 Value::Char(s) => page.dbcs().ok_or(UNCONVERTIBLE)?.encode(s, page.ccsid).map_err(|_| UNCONVERTIBLE),
249 _ => Err(NOT_ASSIGNABLE),
250 }
251}
252
253fn store_text(t: &[u8], bytes: &mut [u8], page: &CodePage) -> Result<Written, SqlError> {
255 let n = t.len().min(bytes.len());
256 bytes[..n].copy_from_slice(&t[..n]);
257 bytes[n..].fill(page.encode_char(' ').unwrap_or(0x40));
258 Ok(Written { truncated_from: (t.len() > bytes.len()).then_some(t.len()) })
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 fn page() -> &'static CodePage {
266 CodePage::by_ccsid(37).expect("CCSID 37 is carried")
267 }
268
269 fn get(bytes: &[u8], ty: &HostType) -> Result<Value, ReadError> {
270 read(bytes, ty, page(), Numproc::Nopfd)
271 }
272
273 fn put(value: Value, len: usize, ty: &HostType) -> (Vec<u8>, Result<Written, SqlError>) {
274 let mut bytes = vec![0xEE; len];
275 let r = write(&value, &mut bytes, ty, page());
276 (bytes, r)
277 }
278
279 const S5V2_PACKED: HostType = HostType::Decimal { digits: 7, scale: 2, signed: true };
280
281 #[test]
282 fn graphic_host_variables_are_dbcs_characters_through_the_code_page() {
283 let p939 = CodePage::by_ccsid(939).expect("CCSID 939 is carried");
284 assert_eq!(read(&[0x44, 0x81, 0x40, 0x40], &HostType::Graphic(2), p939, Numproc::Nopfd), Ok(Value::Char("あ\u{3000}".into())));
285 let mut bytes = vec![0xEE; 4];
286 assert_eq!(write(&Value::Char("日本語".into()), &mut bytes, &HostType::Graphic(2), p939), Ok(Written { truncated_from: Some(3) }));
287 assert_eq!(&bytes[..2], [0x45, 0x62]);
288 assert_eq!(write(&Value::Char("あ".into()), &mut bytes, &HostType::Graphic(2), p939), Ok(Written::default()));
289 assert_eq!(bytes, [0x44, 0x81, 0x40, 0x40], "padded with the DBCS space");
290 let mut varying = vec![0xEE; 8];
291 assert_eq!(write(&Value::Char("あ".into()), &mut varying, &HostType::VarGraphic(3), p939), Ok(Written::default()));
292 assert_eq!(&varying[..4], [0x00, 0x01, 0x44, 0x81]);
293 assert_eq!(read(&varying, &HostType::VarGraphic(3), p939, Numproc::Nopfd), Ok(Value::Char("あ".into())));
294 assert_eq!(read(&[0x00, 0x04, 0, 0, 0, 0, 0, 0], &HostType::VarGraphic(3), p939, Numproc::Nopfd), Err(ReadError::Sql(BAD_LENGTH)));
295 assert_eq!(put(Value::Char("あ".into()), 4, &HostType::Graphic(2)).1, Err(UNCONVERTIBLE), "CCSID 37 has no DBCS characters");
296 }
297
298 #[test]
299 fn binary_host_variables() {
300 assert_eq!(get(&[0xFF, 0xFE], &HostType::SmallInt { signed: true }), Ok(Value::Int(-2)));
301 assert_eq!(get(&[0xFF, 0xFE], &HostType::SmallInt { signed: false }), Ok(Value::Int(65534)));
302 assert_eq!(get(&[0, 0, 1, 0], &HostType::Integer { signed: true }), Ok(Value::Int(256)));
303 assert_eq!(put(Value::Int(-2), 2, &HostType::SmallInt { signed: true }).0, [0xFF, 0xFE]);
304 assert_eq!(put(Value::Int(40_000), 2, &HostType::SmallInt { signed: true }).1, Err(OUT_OF_RANGE));
305 assert_eq!(put(Value::Int(-1), 4, &HostType::Integer { signed: false }).1, Err(OUT_OF_RANGE));
306 assert_eq!(put(Value::Decimal { value: 12_399, scale: 2 }, 4, &HostType::Integer { signed: true }).0, [0, 0, 0, 123]);
307 }
308
309 #[test]
310 fn packed_host_variables() {
311 assert_eq!(get(&[0x01, 0x23, 0x45, 0x0D], &S5V2_PACKED), Ok(Value::Decimal { value: -123_450, scale: 2 }));
312 assert_eq!(get(&[0x01, 0x23, 0x45, 0x0A << 4], &S5V2_PACKED), Err(ReadError::Check(ProgramCheck::Data)));
313 assert_eq!(put(Value::Decimal { value: -123_450, scale: 2 }, 4, &S5V2_PACKED).0, [0x01, 0x23, 0x45, 0x0D]);
314 assert_eq!(put(Value::Decimal { value: 1_239, scale: 3 }, 4, &S5V2_PACKED).0, [0x00, 0x00, 0x12, 0x3C]);
315 assert_eq!(put(Value::Decimal { value: 12_345_600, scale: 2 }, 4, &S5V2_PACKED).1, Err(OUT_OF_RANGE));
316 assert_eq!(put(Value::Int(7), 2, &HostType::Decimal { digits: 3, scale: 0, signed: false }).0, [0x00, 0x7F]);
317 }
318
319 #[test]
320 fn zoned_host_variables() {
321 let trailing = HostType::Zoned { digits: 5, scale: 0, signed: true, sign: None };
322 assert_eq!(put(Value::Int(-123), 5, &trailing).0, [0xF0, 0xF0, 0xF1, 0xF2, 0xD3]);
323 assert_eq!(get(&[0xF0, 0xF0, 0xF1, 0xF2, 0xD3], &trailing), Ok(Value::Decimal { value: -123, scale: 0 }));
324 let leading_separate = HostType::Zoned { digits: 5, scale: 0, signed: true, sign: Some(SignClause { position: SignPosition::Leading, separate: true }) };
325 assert_eq!(put(Value::Int(-123), 6, &leading_separate).0, [0x60, 0xF0, 0xF0, 0xF1, 0xF2, 0xF3]);
326 assert_eq!(get(&[0x60, 0xF0, 0xF0, 0xF1, 0xF2, 0xF3], &leading_separate), Ok(Value::Decimal { value: -123, scale: 0 }));
327 let leading = HostType::Zoned { digits: 3, scale: 0, signed: true, sign: Some(SignClause { position: SignPosition::Leading, separate: false }) };
328 assert_eq!(put(Value::Int(45), 3, &leading).0, [0xC0, 0xF4, 0xF5]);
329 }
330
331 #[test]
332 fn zoned_host_variables_longer_than_sixteen_digits() {
333 let mut eighteen = vec![0xF0; 15];
334 eighteen.extend([0xF1, 0xF2, 0xD3]);
335 let trailing = HostType::Zoned { digits: 18, scale: 0, signed: true, sign: None };
336 assert_eq!(put(Value::Int(-123), 18, &trailing).0, eighteen);
337 assert_eq!(get(&eighteen, &trailing), Ok(Value::Decimal { value: -123, scale: 0 }));
338 let separate = HostType::Zoned { digits: 31, scale: 2, signed: true, sign: Some(SignClause { position: SignPosition::Leading, separate: true }) };
339 let (bytes, written) = put(Value::Decimal { value: 10i128.pow(30) + 5, scale: 2 }, 32, &separate);
340 assert_eq!((bytes[0], bytes[1], bytes[31], written), (0x4E, 0xF1, 0xF5, Ok(Written::default())));
341 let leading = HostType::Zoned { digits: 17, scale: 0, signed: true, sign: Some(SignClause { position: SignPosition::Leading, separate: false }) };
342 assert_eq!(put(Value::Int(-7), 17, &leading).0[..2], [0xD0, 0xF0]);
343 let unsigned = HostType::Zoned { digits: 20, scale: 0, signed: false, sign: None };
344 assert_eq!(put(Value::Int(9), 20, &unsigned).0[19], 0xF9);
345 }
346
347 #[test]
348 fn strings_pad_cut_and_report() {
349 let (bytes, r) = put(Value::Char("ABCDEFGHIJ".into()), 5, &HostType::Char(5));
350 assert_eq!((page().decode(&bytes), r), ("ABCDE".into(), Ok(Written { truncated_from: Some(10) })));
351 let (bytes, r) = put(Value::Char("AB".into()), 5, &HostType::Char(5));
352 assert_eq!((page().decode(&bytes), r), ("AB ".into(), Ok(Written::default())));
353 let (bytes, r) = put(Value::Char("HELLO".into()), 5, &HostType::VarChar(3));
354 assert_eq!((bytes[..2].to_vec(), page().decode(&bytes[2..]), r), (vec![0, 3], "HEL".into(), Ok(Written { truncated_from: Some(5) })));
355 let mut varchar = vec![0, 2];
356 varchar.extend(page().encode("OKXX").unwrap());
357 assert_eq!(get(&varchar, &HostType::VarChar(4)), Ok(Value::Char("OK".into())));
358 assert_eq!(get(&[0, 9, 0xC1, 0xC1], &HostType::VarChar(2)), Err(ReadError::Sql(BAD_LENGTH)));
359 assert_eq!(put(Value::Int(1), 5, &HostType::Char(5)).1, Err(NOT_ASSIGNABLE));
360 assert_eq!(put(Value::Char("1".into()), 2, &HostType::SmallInt { signed: true }).1, Err(NOT_ASSIGNABLE));
361 }
362
363 #[test]
364 fn floats_cross_between_ieee_and_hfp() {
365 assert_eq!(put(Value::Double(1.0), 8, &HostType::Double).0, [0x41, 0x10, 0, 0, 0, 0, 0, 0]);
366 assert_eq!(put(Value::Double(-2.5), 8, &HostType::Double).0, [0xC1, 0x28, 0, 0, 0, 0, 0, 0]);
367 assert_eq!(put(Value::Double(0.0), 4, &HostType::Real).0, [0, 0, 0, 0]);
368 assert_eq!(put(Value::Double(0.5), 4, &HostType::Real).0, [0x40, 0x80, 0, 0]);
369 for f in [1.0, -2.5, 0.1, 1234.5678, 1e-20, 6.02e23] {
370 let (bytes, _) = put(Value::Double(f), 8, &HostType::Double);
371 let Ok(Value::Double(back)) = get(&bytes, &HostType::Double) else { panic!() };
372 assert!((back - f).abs() <= f.abs() * 1e-15, "{f} came back as {back}");
373 }
374 assert_eq!(put(Value::Double(1e300), 8, &HostType::Double).1, Err(OUT_OF_RANGE));
375 }
376}