1#![forbid(unsafe_code)]
2
3use super::*;
4
5pub(crate) fn encode_oracle_date(
6 year: i32,
7 month: u8,
8 day: u8,
9 hour: u8,
10 minute: u8,
11 second: u8,
12) -> Result<[u8; ORA_TYPE_SIZE_DATE as usize]> {
13 if !(1..=9999).contains(&year)
14 || !(1..=12).contains(&month)
15 || !(1..=31).contains(&day)
16 || hour > 23
17 || minute > 59
18 || second > 59
19 {
20 return Err(ProtocolError::TtcDecode("invalid DATE bind"));
21 }
22 let century = year / 100 + 100;
23 let year_in_century = year % 100 + 100;
24 Ok([
25 u8::try_from(century).map_err(|_| ProtocolError::TtcDecode("invalid DATE century"))?,
26 u8::try_from(year_in_century).map_err(|_| ProtocolError::TtcDecode("invalid DATE year"))?,
27 month,
28 day,
29 hour + 1,
30 minute + 1,
31 second + 1,
32 ])
33}
34
35pub(crate) fn encode_oracle_timestamp(
36 year: i32,
37 month: u8,
38 day: u8,
39 hour: u8,
40 minute: u8,
41 second: u8,
42 nanosecond: u32,
43) -> Result<Vec<u8>> {
44 if nanosecond > 999_999_999 {
45 return Err(ProtocolError::TtcDecode("invalid TIMESTAMP fraction"));
46 }
47 let date = encode_oracle_date(year, month, day, hour, minute, second)?;
48 if nanosecond == 0 {
49 return Ok(date.to_vec());
50 }
51 let mut bytes = Vec::with_capacity(ORA_TYPE_SIZE_TIMESTAMP as usize);
52 bytes.extend_from_slice(&date);
53 bytes.extend_from_slice(&nanosecond.to_be_bytes());
54 Ok(bytes)
55}
56
57pub(crate) fn encode_oracle_timestamp_tz(
58 year: i32,
59 month: u8,
60 day: u8,
61 hour: u8,
62 minute: u8,
63 second: u8,
64 nanosecond: u32,
65) -> Result<Vec<u8>> {
66 encode_oracle_timestamp_tz_with_offset(year, month, day, hour, minute, second, nanosecond, 0)
67}
68
69#[allow(clippy::too_many_arguments)]
70pub(crate) fn encode_oracle_timestamp_tz_with_offset(
71 year: i32,
72 month: u8,
73 day: u8,
74 hour: u8,
75 minute: u8,
76 second: u8,
77 nanosecond: u32,
78 offset_minutes: i32,
79) -> Result<Vec<u8>> {
80 if nanosecond > 999_999_999 {
81 return Err(ProtocolError::TtcDecode(
82 "invalid TIMESTAMP WITH TIME ZONE fraction",
83 ));
84 }
85 let offset_hours = offset_minutes / 60;
86 let offset_minute_part = offset_minutes % 60;
87 let encoded_hour = offset_hours + i32::from(TZ_HOUR_OFFSET);
88 let encoded_minute = offset_minute_part + i32::from(TZ_MINUTE_OFFSET);
89 let encoded_hour = u8::try_from(encoded_hour)
90 .map_err(|_| ProtocolError::TtcDecode("invalid TIMESTAMP WITH TIME ZONE offset hour"))?;
91 let encoded_minute = u8::try_from(encoded_minute)
92 .map_err(|_| ProtocolError::TtcDecode("invalid TIMESTAMP WITH TIME ZONE offset minute"))?;
93 let mut bytes = Vec::with_capacity(ORA_TYPE_SIZE_TIMESTAMP_TZ as usize);
94 let date = encode_oracle_date(year, month, day, hour, minute, second)?;
95 bytes.extend_from_slice(&date);
96 bytes.extend_from_slice(&nanosecond.to_be_bytes());
97 bytes.push(encoded_hour);
98 bytes.push(encoded_minute);
99 Ok(bytes)
100}
101
102pub fn decode_datetime_value(bytes: &[u8]) -> Result<QueryValue> {
103 if bytes.len() < ORA_TYPE_SIZE_DATE as usize {
104 return Err(ProtocolError::TtcDecode("DATE value too short"));
105 }
106 let year = (i32::from(bytes[0]) - 100) * 100 + i32::from(bytes[1]) - 100;
107 let month = bytes[2];
108 let day = bytes[3];
109 let hour = bytes[4].saturating_sub(1);
110 let minute = bytes[5].saturating_sub(1);
111 let second = bytes[6].saturating_sub(1);
112 let nanosecond = if bytes.len() >= ORA_TYPE_SIZE_TIMESTAMP as usize {
113 u32::from_be_bytes(
114 bytes[7..11]
115 .try_into()
116 .map_err(|_| ProtocolError::TtcDecode("invalid TIMESTAMP fraction"))?,
117 )
118 } else {
119 0
120 };
121 if bytes.len() >= ORA_TYPE_SIZE_TIMESTAMP_TZ as usize && bytes[11] != 0 && bytes[12] != 0 {
122 if bytes[11] & TNS_HAS_REGION_ID != 0 {
123 return Err(ProtocolError::UnsupportedFeature(
124 "named TIMESTAMP WITH TIME ZONE region",
125 ));
126 }
127 let offset_minutes = (i32::from(bytes[11]) - i32::from(TZ_HOUR_OFFSET)) * 60
128 + i32::from(bytes[12])
129 - i32::from(TZ_MINUTE_OFFSET);
130 return Ok(QueryValue::TimestampTz {
131 year,
132 month,
133 day,
134 hour,
135 minute,
136 second,
137 nanosecond,
138 offset_minutes,
139 });
140 }
141 Ok(QueryValue::DateTime {
142 year,
143 month,
144 day,
145 hour,
146 minute,
147 second,
148 nanosecond,
149 })
150}
151
152pub(crate) fn adjust_datetime_by_minutes(
153 year: i32,
154 month: u8,
155 day: u8,
156 hour: u8,
157 minute: u8,
158 second: u8,
159 offset_minutes: i32,
160) -> Result<(i32, u8, u8, u8, u8, u8)> {
161 let days = days_from_civil(year, month, day)?;
162 let seconds_of_day = i64::from(hour) * 3_600 + i64::from(minute) * 60 + i64::from(second);
163 let total_seconds = days
164 .checked_mul(86_400)
165 .and_then(|value| value.checked_add(seconds_of_day))
166 .and_then(|value| value.checked_add(i64::from(offset_minutes) * 60))
167 .ok_or(ProtocolError::TtcDecode(
168 "TIMESTAMP WITH TIME ZONE offset overflow",
169 ))?;
170 let adjusted_days = total_seconds.div_euclid(86_400);
171 let adjusted_seconds = total_seconds.rem_euclid(86_400);
172 let (year, month, day) = civil_from_days(adjusted_days)?;
173 let hour = u8::try_from(adjusted_seconds / 3_600)
174 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP hour"))?;
175 let minute = u8::try_from((adjusted_seconds % 3_600) / 60)
176 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP minute"))?;
177 let second = u8::try_from(adjusted_seconds % 60)
178 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP second"))?;
179 Ok((year, month, day, hour, minute, second))
180}
181
182pub(crate) fn days_from_civil(year: i32, month: u8, day: u8) -> Result<i64> {
183 if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
184 return Err(ProtocolError::TtcDecode("invalid TIMESTAMP date"));
185 }
186 let year = year - i32::from(month <= 2);
187 let era = if year >= 0 { year } else { year - 399 } / 400;
188 let year_of_era = year - era * 400;
189 let month = i32::from(month);
190 let day = i32::from(day);
191 let month_prime = month + if month > 2 { -3 } else { 9 };
192 let day_of_year = (153 * month_prime + 2) / 5 + day - 1;
193 let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
194 Ok(i64::from(era) * 146_097 + i64::from(day_of_era) - 719_468)
195}
196
197pub(crate) fn civil_from_days(days: i64) -> Result<(i32, u8, u8)> {
198 let days = days + 719_468;
199 let era = if days >= 0 { days } else { days - 146_096 } / 146_097;
200 let day_of_era = days - era * 146_097;
201 let year_of_era =
202 (day_of_era - day_of_era / 1_460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
203 let year = year_of_era + era * 400;
204 let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
205 let month_prime = (5 * day_of_year + 2) / 153;
206 let day = day_of_year - (153 * month_prime + 2) / 5 + 1;
207 let month = month_prime + if month_prime < 10 { 3 } else { -9 };
208 let year = year + i64::from(month <= 2);
209 Ok((
210 i32::try_from(year)
211 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP year"))?,
212 u8::try_from(month)
213 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP month"))?,
214 u8::try_from(day)
215 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP day"))?,
216 ))
217}
218
219pub(crate) fn encode_binary_double(value: f64) -> [u8; 8] {
220 let mut bytes = value.to_bits().to_be_bytes();
221 if bytes[0] & 0x80 == 0 {
222 bytes[0] |= 0x80;
223 } else {
224 for byte in &mut bytes {
225 *byte = !*byte;
226 }
227 }
228 bytes
229}
230
231pub(crate) fn encode_binary_float(value: f32) -> [u8; 4] {
232 let mut bytes = value.to_bits().to_be_bytes();
233 if bytes[0] & 0x80 == 0 {
234 bytes[0] |= 0x80;
235 } else {
236 for byte in &mut bytes {
237 *byte = !*byte;
238 }
239 }
240 bytes
241}
242
243pub(crate) fn decode_binary_float(bytes: &[u8]) -> Result<f32> {
244 let bytes: [u8; 4] = bytes
245 .try_into()
246 .map_err(|_| ProtocolError::TtcDecode("invalid BINARY_FLOAT length"))?;
247 let mut decoded = bytes;
248 if decoded[0] & 0x80 != 0 {
249 decoded[0] &= 0x7f;
250 } else {
251 for byte in &mut decoded {
252 *byte = !*byte;
253 }
254 }
255 Ok(f32::from_bits(u32::from_be_bytes(decoded)))
256}
257
258pub(crate) fn encode_interval_ds(days: i32, seconds: i32, nanoseconds: i32) -> Result<[u8; 11]> {
259 let mut bytes = [0u8; 11];
260 let wire_days = u32::try_from(i64::from(days) + TNS_DURATION_MID)
261 .map_err(|_| ProtocolError::TtcDecode("INTERVAL DS days out of range"))?;
262 bytes[..4].copy_from_slice(&wire_days.to_be_bytes());
263 let to_offset_byte = |value: i32| -> Result<u8> {
264 u8::try_from(value + TNS_DURATION_OFFSET)
265 .map_err(|_| ProtocolError::TtcDecode("INTERVAL DS component out of range"))
266 };
267 bytes[4] = to_offset_byte(seconds / 3600)?;
268 bytes[5] = to_offset_byte((seconds % 3600) / 60)?;
269 bytes[6] = to_offset_byte(seconds % 60)?;
270 let fseconds = i64::from(nanoseconds);
271 let wire_fseconds = u32::try_from(fseconds + TNS_DURATION_MID)
272 .map_err(|_| ProtocolError::TtcDecode("INTERVAL DS fractional seconds out of range"))?;
273 bytes[7..].copy_from_slice(&wire_fseconds.to_be_bytes());
274 Ok(bytes)
275}
276
277pub(crate) fn decode_interval_ds(bytes: &[u8]) -> Result<QueryValue> {
278 if bytes.len() < 11 {
279 return Err(ProtocolError::TtcDecode("invalid INTERVAL DS length"));
280 }
281 let days_wire = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
282 let fseconds_wire = u32::from_be_bytes([bytes[7], bytes[8], bytes[9], bytes[10]]);
283 let to_component = |value: i64| -> Result<i32> {
284 i32::try_from(value).map_err(|_| ProtocolError::TtcDecode("INTERVAL DS out of range"))
285 };
286 Ok(QueryValue::IntervalDS {
287 days: to_component(i64::from(days_wire) - TNS_DURATION_MID)?,
288 hours: i32::from(bytes[4]) - TNS_DURATION_OFFSET,
289 minutes: i32::from(bytes[5]) - TNS_DURATION_OFFSET,
290 seconds: i32::from(bytes[6]) - TNS_DURATION_OFFSET,
291 fseconds: to_component(i64::from(fseconds_wire) - TNS_DURATION_MID)?,
292 })
293}
294
295pub(crate) fn encode_interval_ym(years: i32, months: i32) -> Result<[u8; 5]> {
299 let mut bytes = [0u8; 5];
300 let wire_years = u32::try_from(i64::from(years) + TNS_DURATION_MID)
301 .map_err(|_| ProtocolError::TtcDecode("INTERVAL YM years out of range"))?;
302 bytes[..4].copy_from_slice(&wire_years.to_be_bytes());
303 bytes[4] = u8::try_from(months + TNS_DURATION_OFFSET)
304 .map_err(|_| ProtocolError::TtcDecode("INTERVAL YM months out of range"))?;
305 Ok(bytes)
306}
307
308pub(crate) fn decode_interval_ym(bytes: &[u8]) -> Result<QueryValue> {
312 if bytes.len() < 5 {
313 return Err(ProtocolError::TtcDecode("invalid INTERVAL YM length"));
314 }
315 let years_wire = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
316 let years = i32::try_from(i64::from(years_wire) - TNS_DURATION_MID)
317 .map_err(|_| ProtocolError::TtcDecode("INTERVAL YM out of range"))?;
318 Ok(QueryValue::IntervalYM {
319 years,
320 months: i32::from(bytes[4]) - TNS_DURATION_OFFSET,
321 })
322}
323
324pub(crate) fn decode_binary_double(bytes: &[u8]) -> Result<f64> {
325 let bytes: [u8; 8] = bytes
326 .try_into()
327 .map_err(|_| ProtocolError::TtcDecode("invalid BINARY_DOUBLE length"))?;
328 let mut decoded = bytes;
329 if decoded[0] & 0x80 != 0 {
330 decoded[0] &= 0x7f;
331 } else {
332 for byte in &mut decoded {
333 *byte = !*byte;
334 }
335 }
336 Ok(f64::from_bits(u64::from_be_bytes(decoded)))
337}
338
339pub fn encode_number_text(value: &str) -> Result<Vec<u8>> {
344 let value = value.as_bytes();
345 if value.is_empty() {
346 return Err(ProtocolError::TtcDecode("empty NUMBER bind"));
347 }
348 if value.len() > NUMBER_AS_TEXT_CHARS {
349 return Err(ProtocolError::TtcDecode("NUMBER bind text too long"));
350 }
351
352 let mut pos = 0;
353 let mut is_negative = false;
354 if matches!(value.first(), Some(&b'-')) {
355 is_negative = true;
356 pos += 1;
357 }
358
359 let mut digits = Vec::with_capacity(NUMBER_AS_TEXT_CHARS);
360 while let Some(byte) = value.get(pos).copied() {
361 if matches!(byte, b'.' | b'e' | b'E') {
362 break;
363 }
364 if !byte.is_ascii_digit() {
365 return Err(ProtocolError::TtcDecode("invalid NUMBER bind"));
366 }
367 let digit = byte - b'0';
368 pos += 1;
369 if digit == 0 && digits.is_empty() {
370 continue;
371 }
372 digits.push(digit);
373 }
374 let mut decimal_point_index = i32::try_from(digits.len()).unwrap_or(i32::MAX);
375
376 if matches!(value.get(pos), Some(&b'.')) {
377 pos += 1;
378 while let Some(byte) = value.get(pos).copied() {
379 if matches!(byte, b'e' | b'E') {
380 break;
381 }
382 if !byte.is_ascii_digit() {
383 return Err(ProtocolError::TtcDecode("invalid NUMBER bind"));
384 }
385 let digit = byte - b'0';
386 pos += 1;
387 if digit == 0 && digits.is_empty() {
388 decimal_point_index -= 1;
389 continue;
390 }
391 digits.push(digit);
392 }
393 }
394
395 if matches!(value.get(pos).copied(), Some(b'e' | b'E')) {
396 pos += 1;
397 let mut exponent_is_negative = false;
398 if let Some(byte) = value.get(pos).copied() {
399 if byte == b'-' {
400 exponent_is_negative = true;
401 pos += 1;
402 } else if byte == b'+' {
403 pos += 1;
404 }
405 }
406 let exponent_start = pos;
407 while let Some(byte) = value.get(pos).copied() {
408 if !byte.is_ascii_digit() {
409 return Err(ProtocolError::TtcDecode("invalid NUMBER exponent"));
410 }
411 pos += 1;
412 }
413 if exponent_start == pos {
414 return Err(ProtocolError::TtcDecode("empty NUMBER exponent"));
415 }
416 let exponent_text = std::str::from_utf8(&value[exponent_start..pos])
417 .map_err(|_| ProtocolError::TtcDecode("invalid NUMBER exponent"))?;
418 let mut exponent = exponent_text
419 .parse::<i32>()
420 .map_err(|_| ProtocolError::TtcDecode("invalid NUMBER exponent"))?;
421 if exponent_is_negative {
422 exponent = -exponent;
423 }
424 decimal_point_index = decimal_point_index
431 .checked_add(exponent)
432 .ok_or(ProtocolError::TtcDecode("NUMBER bind out of range"))?;
433 }
434
435 if pos < value.len() {
436 return Err(ProtocolError::TtcDecode("invalid NUMBER bind suffix"));
437 }
438
439 while digits.last().is_some_and(|digit| *digit == 0) {
440 digits.pop();
441 }
442 if digits.len() > NUMBER_MAX_DIGITS || !(-129..=126).contains(&decimal_point_index) {
443 return Err(ProtocolError::TtcDecode("NUMBER bind out of range"));
444 }
445
446 let mut prepend_zero = false;
447 if decimal_point_index % 2 != 0 {
448 prepend_zero = true;
449 if !digits.is_empty() {
450 digits.push(0);
451 decimal_point_index += 1;
452 }
453 }
454 if digits.len() % 2 == 1 {
455 digits.push(0);
456 }
457
458 if digits.is_empty() {
459 return Ok(vec![128]);
460 }
461
462 let mut encoded = Vec::with_capacity(digits.len() / 2 + 2);
463 let exponent_on_wire = decimal_point_index / 2 + 192;
464 if !(0..=255).contains(&exponent_on_wire) {
465 return Err(ProtocolError::TtcDecode(
466 "NUMBER bind exponent out of range",
467 ));
468 }
469 let exponent_byte = exponent_on_wire as u8;
470 encoded.push(if is_negative {
471 !exponent_byte
472 } else {
473 exponent_byte
474 });
475
476 let mut digit_pos = 0;
477 for pair_num in 0..(digits.len() / 2) {
478 let mut digit = if pair_num == 0 && prepend_zero {
479 let digit = digits[digit_pos];
480 digit_pos += 1;
481 digit
482 } else {
483 let digit = digits[digit_pos] * 10 + digits[digit_pos + 1];
484 digit_pos += 2;
485 digit
486 };
487 if is_negative {
488 digit = 101 - digit;
489 } else {
490 digit += 1;
491 }
492 encoded.push(digit);
493 }
494
495 if is_negative && digits.len() < NUMBER_MAX_DIGITS {
496 encoded.push(102);
497 }
498
499 Ok(encoded)
500}
501
502pub fn decode_number_value(bytes: &[u8]) -> Result<QueryValue> {
503 Ok(QueryValue::Number(super::number::OracleNumber::from_wire(
504 bytes,
505 )?))
506}
507
508pub fn decode_number_text_into(
520 bytes: &[u8],
521 digits: &mut Vec<u8>,
522 text: &mut String,
523) -> Result<bool> {
524 match decode_number_parts(bytes, digits, text)? {
525 super::number::DecodedNumber::Text { is_integer } => Ok(is_integer),
527 super::number::DecodedNumber::Parts {
528 is_negative,
529 decimal_point_index,
530 is_integer,
531 } => {
532 format_number_digits(digits, is_negative, decimal_point_index, text);
533 Ok(is_integer)
534 }
535 }
536}
537
538pub(crate) fn decode_number_parts(
547 bytes: &[u8],
548 digits: &mut Vec<u8>,
549 text: &mut String,
550) -> Result<super::number::DecodedNumber> {
551 use super::number::DecodedNumber;
552
553 if bytes.len() > 21 {
554 return Err(ProtocolError::TtcDecode("encoded NUMBER too long"));
555 }
556 let Some(&first) = bytes.first() else {
557 return Err(ProtocolError::TtcDecode("empty NUMBER"));
558 };
559 let is_positive = first & 0x80 != 0;
560 digits.clear();
561 if bytes.len() == 1 {
562 if is_positive {
563 text.push('0');
564 } else {
565 text.push_str("-1e126");
566 }
567 return Ok(DecodedNumber::Text { is_integer: true });
568 }
569
570 let exponent_byte = if is_positive { first } else { !first };
571 let exponent = i16::from(exponent_byte) - 193;
572 let mut decimal_point_index = exponent * 2 + 2;
573 let mut end = bytes.len();
574 if !is_positive && bytes[end - 1] == 102 {
575 end -= 1;
576 }
577
578 for (index, encoded) in bytes.iter().enumerate().take(end).skip(1) {
579 let value = if is_positive {
580 encoded.saturating_sub(1)
581 } else {
582 101u8.saturating_sub(*encoded)
583 };
584
585 let first_digit = value / 10;
586 if first_digit == 0 && digits.is_empty() {
587 decimal_point_index -= 1;
588 } else if first_digit == 10 {
589 digits.push(1);
590 digits.push(0);
591 decimal_point_index += 1;
592 } else if first_digit != 0 || index > 0 {
593 digits.push(first_digit);
594 }
595
596 let second_digit = value % 10;
597 if second_digit != 0 || index < end - 1 {
598 digits.push(second_digit);
599 }
600 }
601
602 let len = i16::try_from(digits.len()).unwrap_or(i16::MAX);
606 let is_integer = decimal_point_index > 0 && decimal_point_index >= len;
607
608 Ok(DecodedNumber::Parts {
609 is_negative: !is_positive,
610 decimal_point_index,
611 is_integer,
612 })
613}
614
615pub(crate) fn decode_number_parts_stack(
624 bytes: &[u8],
625 digit_buf: &mut [u8],
626) -> Result<super::number::DecodedNumberStack> {
627 use super::number::DecodedNumberStack;
628
629 if bytes.len() > 21 {
630 return Err(ProtocolError::TtcDecode("encoded NUMBER too long"));
631 }
632 let Some(&first) = bytes.first() else {
633 return Err(ProtocolError::TtcDecode("empty NUMBER"));
634 };
635 let is_positive = first & 0x80 != 0;
636 if bytes.len() == 1 {
637 return Ok(DecodedNumberStack::Sentinel {
638 text: if is_positive { "0" } else { "-1e126" },
639 is_integer: true,
640 });
641 }
642
643 let exponent_byte = if is_positive { first } else { !first };
644 let exponent = i16::from(exponent_byte) - 193;
645 let mut decimal_point_index = exponent * 2 + 2;
646 let mut end = bytes.len();
647 if !is_positive && bytes[end - 1] == 102 {
648 end -= 1;
649 }
650
651 let mut len = 0usize;
652 let mut coeff: Option<i128> = Some(0);
659 let push = |buf: &mut [u8], d: u8, len: &mut usize, coeff: &mut Option<i128>| {
663 if *len < buf.len() {
664 buf[*len] = d;
665 *len += 1;
666 }
667 *coeff = coeff
668 .and_then(|acc| acc.checked_mul(10))
669 .and_then(|acc| acc.checked_add(i128::from(d)));
670 };
671
672 for (index, encoded) in bytes.iter().enumerate().take(end).skip(1) {
673 let value = if is_positive {
674 encoded.saturating_sub(1)
675 } else {
676 101u8.saturating_sub(*encoded)
677 };
678
679 let first_digit = value / 10;
680 if first_digit == 0 && len == 0 {
681 decimal_point_index -= 1;
682 } else if first_digit == 10 {
683 push(digit_buf, 1, &mut len, &mut coeff);
684 push(digit_buf, 0, &mut len, &mut coeff);
685 decimal_point_index += 1;
686 } else if first_digit != 0 || index > 0 {
687 push(digit_buf, first_digit, &mut len, &mut coeff);
688 }
689
690 let second_digit = value % 10;
691 if second_digit != 0 || index < end - 1 {
692 push(digit_buf, second_digit, &mut len, &mut coeff);
693 }
694 }
695
696 let len_i16 = i16::try_from(len).unwrap_or(i16::MAX);
697 let is_integer = decimal_point_index > 0 && decimal_point_index >= len_i16;
698
699 let coefficient = coeff.map(|acc| if is_positive { acc } else { -acc });
704
705 Ok(DecodedNumberStack::Parts {
706 digit_len: len,
707 is_negative: !is_positive,
708 decimal_point_index,
709 is_integer,
710 coefficient,
711 })
712}
713
714pub(crate) fn format_number_digits(
720 digits: &[u8],
721 is_negative: bool,
722 decimal_point_index: i16,
723 text: &mut String,
724) {
725 if is_negative {
726 text.push('-');
727 }
728 if decimal_point_index <= 0 {
729 text.push_str("0.");
730 for _ in decimal_point_index..0 {
731 text.push('0');
732 }
733 }
734 for (index, digit) in digits.iter().enumerate() {
735 if index > 0
736 && matches!(
737 i16::try_from(index)
738 .unwrap_or(i16::MAX)
739 .cmp(&decimal_point_index),
740 std::cmp::Ordering::Equal
741 )
742 {
743 text.push('.');
744 }
745 text.push(char::from(b'0' + *digit));
746 }
747 if decimal_point_index > i16::try_from(digits.len()).unwrap_or(i16::MAX) {
748 for _ in i16::try_from(digits.len()).unwrap_or(i16::MAX)..decimal_point_index {
749 text.push('0');
750 }
751 }
752}
753
754pub(crate) fn decode_text_value(bytes: &[u8], csfrm: u8) -> Result<String> {
755 if csfrm == CS_FORM_NCHAR {
756 let units = bytes
757 .chunks_exact(2)
758 .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
759 .collect::<Vec<_>>();
760 if units.len() * 2 != bytes.len() {
761 return Err(ProtocolError::TtcDecode("invalid UTF-16 text length"));
762 }
763 String::from_utf16(&units).map_err(|_| ProtocolError::TtcDecode("invalid UTF-16 text"))
764 } else {
765 String::from_utf8(bytes.to_vec())
766 .map_err(|_| ProtocolError::TtcDecode("invalid UTF-8 text"))
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773
774 #[test]
780 fn number_text_huge_exponent_rejected_not_panicked() {
781 let crafted = format!("{}e+2147483647", "1".repeat(160));
783 assert_eq!(crafted.len(), NUMBER_AS_TEXT_CHARS);
784 assert!(encode_number_text(&crafted).is_err());
785
786 let crafted_neg = format!("0.{}e-2147483647", "0".repeat(158));
788 assert!(encode_number_text(&crafted_neg).is_err());
789 }
790
791 #[test]
792 fn number_text_ordinary_values_still_encode() {
793 for ok in [
794 "0",
795 "1",
796 "-1",
797 "3.14159",
798 "1e10",
799 "-2.5e-3",
800 "12345678901234567890",
801 ] {
802 assert!(encode_number_text(ok).is_ok(), "expected {ok} to encode");
803 }
804 }
805
806 #[test]
807 fn interval_ds_roundtrip_preserves_nanoseconds() {
808 let wire = encode_interval_ds(2, 3 * 3600 + 4 * 60 + 5, 123_456_789)
809 .expect("encode nanosecond interval");
810 assert_eq!(
811 decode_interval_ds(&wire).expect("decode nanosecond interval"),
812 QueryValue::IntervalDS {
813 days: 2,
814 hours: 3,
815 minutes: 4,
816 seconds: 5,
817 fseconds: 123_456_789,
818 }
819 );
820 }
821
822 #[test]
823 fn interval_ds_rejects_truncated_wire_value() {
824 let wire = encode_interval_ds(-2, -(3 * 3600 + 4 * 60 + 5), -123_456_789)
825 .expect("encode negative interval");
826 assert!(decode_interval_ds(&wire[..10]).is_err());
827 }
828
829 #[test]
830 fn number_decode_heap_and_stack_paths_match_edge_corpus() {
831 for (text, is_integer) in [
832 ("0", true),
833 ("0.01", false),
834 ("0.1", false),
835 ("0.99", false),
836 ("9.9", false),
837 ("10", true),
838 ("99", true),
839 ("100", true),
840 ("101", true),
841 ("-0.01", false),
842 ("-0.1", false),
843 ("-0.99", false),
844 ("-9.9", false),
845 ("-10", true),
846 ("-99", true),
847 ("-100", true),
848 ("-101", true),
849 ("12345678901234567890123456789012345678", true),
850 ("-12345678901234567890123456789012345678", true),
851 ] {
852 let wire = encode_number_text(text).expect("encode NUMBER edge");
853 let mut digits = Vec::new();
854 let mut decoded = String::new();
855 let decoded_is_integer =
856 decode_number_text_into(&wire, &mut digits, &mut decoded).expect("decode text");
857 let owned = decode_number_value(&wire).expect("decode owned NUMBER");
858
859 assert_eq!(decoded, text, "canonical NUMBER text for wire {wire:02x?}");
860 assert_eq!(
861 decoded_is_integer, is_integer,
862 "integer flag for NUMBER wire {wire:02x?}"
863 );
864 assert_eq!(
865 owned.as_number_text().as_deref(),
866 Some(decoded.as_str()),
867 "owned stack decode must match heap text decode for {text}"
868 );
869 }
870 }
871
872 #[test]
873 fn number_part_decoders_emit_exact_digits_decimal_and_coefficient() {
874 use super::super::number::{DecodedNumber, DecodedNumberStack, MAX_DIGITS};
875
876 struct Case<'a> {
877 wire: &'a [u8],
878 digits: &'a [u8],
879 is_negative: bool,
880 decimal_point_index: i16,
881 is_integer: bool,
882 coefficient: Option<i128>,
883 text: &'a str,
884 }
885
886 let cases = [
887 Case {
891 wire: &[193, 101],
892 digits: &[1, 0],
893 is_negative: false,
894 decimal_point_index: 3,
895 is_integer: true,
896 coefficient: Some(10),
897 text: "100",
898 },
899 Case {
903 wire: &[193, 1, 2],
904 digits: &[0, 0, 1],
905 is_negative: false,
906 decimal_point_index: 1,
907 is_integer: false,
908 coefficient: Some(1),
909 text: "0.01",
910 },
911 Case {
913 wire: &[62, 100, 102],
914 digits: &[1],
915 is_negative: true,
916 decimal_point_index: 1,
917 is_integer: true,
918 coefficient: Some(-1),
919 text: "-1",
920 },
921 Case {
925 wire: &[
926 211, 13, 35, 57, 79, 91, 13, 35, 57, 79, 91, 13, 35, 57, 79, 91, 13, 35, 57,
927 79, 91,
928 ],
929 digits: &[
930 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6,
931 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
932 ],
933 is_negative: false,
934 decimal_point_index: 38,
935 is_integer: false,
936 coefficient: Some(123456789012345678901234567890123456789),
937 text: "12345678901234567890123456789012345678.9",
938 },
939 ];
940
941 for case in cases {
942 let mut heap_digits = Vec::new();
943 let mut text = String::new();
944 let decoded = decode_number_parts(case.wire, &mut heap_digits, &mut text)
945 .expect("heap NUMBER part decode");
946 assert!(
947 matches!(decoded, DecodedNumber::Parts { .. }),
948 "expected heap NUMBER parts for {:02x?}",
949 case.wire
950 );
951 if let DecodedNumber::Parts {
952 is_negative,
953 decimal_point_index,
954 is_integer,
955 } = decoded
956 {
957 assert_eq!(
958 heap_digits, case.digits,
959 "heap digits for {:02x?}",
960 case.wire
961 );
962 assert_eq!(
963 is_negative, case.is_negative,
964 "heap sign for {:02x?}",
965 case.wire
966 );
967 assert_eq!(
968 decimal_point_index, case.decimal_point_index,
969 "heap decimal point for {:02x?}",
970 case.wire
971 );
972 assert_eq!(
973 is_integer, case.is_integer,
974 "heap integer flag for {:02x?}",
975 case.wire
976 );
977 format_number_digits(&heap_digits, is_negative, decimal_point_index, &mut text);
978 assert_eq!(text, case.text, "heap text for {:02x?}", case.wire);
979 }
980
981 let mut stack_digits = [0u8; MAX_DIGITS];
982 let decoded = decode_number_parts_stack(case.wire, &mut stack_digits)
983 .expect("stack NUMBER part decode");
984 assert!(
985 matches!(decoded, DecodedNumberStack::Parts { .. }),
986 "expected stack NUMBER parts for {:02x?}",
987 case.wire
988 );
989 if let DecodedNumberStack::Parts {
990 digit_len,
991 is_negative,
992 decimal_point_index,
993 is_integer,
994 coefficient,
995 } = decoded
996 {
997 assert_eq!(
998 &stack_digits[..digit_len],
999 case.digits,
1000 "stack digits for {:02x?}",
1001 case.wire
1002 );
1003 assert_eq!(
1004 is_negative, case.is_negative,
1005 "stack sign for {:02x?}",
1006 case.wire
1007 );
1008 assert_eq!(
1009 decimal_point_index, case.decimal_point_index,
1010 "stack decimal point for {:02x?}",
1011 case.wire
1012 );
1013 assert_eq!(
1014 is_integer, case.is_integer,
1015 "stack integer flag for {:02x?}",
1016 case.wire
1017 );
1018 assert_eq!(
1019 coefficient, case.coefficient,
1020 "stack coefficient for {:02x?}",
1021 case.wire
1022 );
1023 let mut stack_text = String::new();
1024 format_number_digits(
1025 &stack_digits[..digit_len],
1026 is_negative,
1027 decimal_point_index,
1028 &mut stack_text,
1029 );
1030 assert_eq!(stack_text, case.text, "stack text for {:02x?}", case.wire);
1031 }
1032 }
1033 }
1034}