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}