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 if nanosecond > 999_999_999 {
67 return Err(ProtocolError::TtcDecode(
68 "invalid TIMESTAMP WITH TIME ZONE fraction",
69 ));
70 }
71 let mut bytes = Vec::with_capacity(ORA_TYPE_SIZE_TIMESTAMP_TZ as usize);
72 let date = encode_oracle_date(year, month, day, hour, minute, second)?;
73 bytes.extend_from_slice(&date);
74 bytes.extend_from_slice(&nanosecond.to_be_bytes());
75 bytes.push(TZ_HOUR_OFFSET);
76 bytes.push(TZ_MINUTE_OFFSET);
77 Ok(bytes)
78}
79
80pub fn decode_datetime_value(bytes: &[u8]) -> Result<QueryValue> {
81 if bytes.len() < ORA_TYPE_SIZE_DATE as usize {
82 return Err(ProtocolError::TtcDecode("DATE value too short"));
83 }
84 let mut year = (i32::from(bytes[0]) - 100) * 100 + i32::from(bytes[1]) - 100;
85 let mut month = bytes[2];
86 let mut day = bytes[3];
87 let mut hour = bytes[4].saturating_sub(1);
88 let mut minute = bytes[5].saturating_sub(1);
89 let mut second = bytes[6].saturating_sub(1);
90 let nanosecond = if bytes.len() >= ORA_TYPE_SIZE_TIMESTAMP as usize {
91 u32::from_be_bytes(
92 bytes[7..11]
93 .try_into()
94 .map_err(|_| ProtocolError::TtcDecode("invalid TIMESTAMP fraction"))?,
95 )
96 } else {
97 0
98 };
99 if bytes.len() >= ORA_TYPE_SIZE_TIMESTAMP_TZ as usize && bytes[11] != 0 && bytes[12] != 0 {
100 if bytes[11] & TNS_HAS_REGION_ID != 0 {
101 return Err(ProtocolError::UnsupportedFeature(
102 "named TIMESTAMP WITH TIME ZONE region",
103 ));
104 }
105 let offset_minutes = (i32::from(bytes[11]) - i32::from(TZ_HOUR_OFFSET)) * 60
106 + i32::from(bytes[12])
107 - i32::from(TZ_MINUTE_OFFSET);
108 (year, month, day, hour, minute, second) =
109 adjust_datetime_by_minutes(year, month, day, hour, minute, second, offset_minutes)?;
110 }
111 Ok(QueryValue::DateTime {
112 year,
113 month,
114 day,
115 hour,
116 minute,
117 second,
118 nanosecond,
119 })
120}
121
122pub(crate) fn adjust_datetime_by_minutes(
123 year: i32,
124 month: u8,
125 day: u8,
126 hour: u8,
127 minute: u8,
128 second: u8,
129 offset_minutes: i32,
130) -> Result<(i32, u8, u8, u8, u8, u8)> {
131 let days = days_from_civil(year, month, day)?;
132 let seconds_of_day = i64::from(hour) * 3_600 + i64::from(minute) * 60 + i64::from(second);
133 let total_seconds = days
134 .checked_mul(86_400)
135 .and_then(|value| value.checked_add(seconds_of_day))
136 .and_then(|value| value.checked_add(i64::from(offset_minutes) * 60))
137 .ok_or(ProtocolError::TtcDecode(
138 "TIMESTAMP WITH TIME ZONE offset overflow",
139 ))?;
140 let adjusted_days = total_seconds.div_euclid(86_400);
141 let adjusted_seconds = total_seconds.rem_euclid(86_400);
142 let (year, month, day) = civil_from_days(adjusted_days)?;
143 let hour = u8::try_from(adjusted_seconds / 3_600)
144 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP hour"))?;
145 let minute = u8::try_from((adjusted_seconds % 3_600) / 60)
146 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP minute"))?;
147 let second = u8::try_from(adjusted_seconds % 60)
148 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP second"))?;
149 Ok((year, month, day, hour, minute, second))
150}
151
152pub(crate) fn days_from_civil(year: i32, month: u8, day: u8) -> Result<i64> {
153 if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
154 return Err(ProtocolError::TtcDecode("invalid TIMESTAMP date"));
155 }
156 let year = year - i32::from(month <= 2);
157 let era = if year >= 0 { year } else { year - 399 } / 400;
158 let year_of_era = year - era * 400;
159 let month = i32::from(month);
160 let day = i32::from(day);
161 let month_prime = month + if month > 2 { -3 } else { 9 };
162 let day_of_year = (153 * month_prime + 2) / 5 + day - 1;
163 let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
164 Ok(i64::from(era) * 146_097 + i64::from(day_of_era) - 719_468)
165}
166
167pub(crate) fn civil_from_days(days: i64) -> Result<(i32, u8, u8)> {
168 let days = days + 719_468;
169 let era = if days >= 0 { days } else { days - 146_096 } / 146_097;
170 let day_of_era = days - era * 146_097;
171 let year_of_era =
172 (day_of_era - day_of_era / 1_460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
173 let year = year_of_era + era * 400;
174 let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
175 let month_prime = (5 * day_of_year + 2) / 153;
176 let day = day_of_year - (153 * month_prime + 2) / 5 + 1;
177 let month = month_prime + if month_prime < 10 { 3 } else { -9 };
178 let year = year + i64::from(month <= 2);
179 Ok((
180 i32::try_from(year)
181 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP year"))?,
182 u8::try_from(month)
183 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP month"))?,
184 u8::try_from(day)
185 .map_err(|_| ProtocolError::TtcDecode("invalid adjusted TIMESTAMP day"))?,
186 ))
187}
188
189pub(crate) fn encode_binary_double(value: f64) -> [u8; 8] {
190 let mut bytes = value.to_bits().to_be_bytes();
191 if bytes[0] & 0x80 == 0 {
192 bytes[0] |= 0x80;
193 } else {
194 for byte in &mut bytes {
195 *byte = !*byte;
196 }
197 }
198 bytes
199}
200
201pub(crate) fn encode_binary_float(value: f32) -> [u8; 4] {
202 let mut bytes = value.to_bits().to_be_bytes();
203 if bytes[0] & 0x80 == 0 {
204 bytes[0] |= 0x80;
205 } else {
206 for byte in &mut bytes {
207 *byte = !*byte;
208 }
209 }
210 bytes
211}
212
213pub(crate) fn decode_binary_float(bytes: &[u8]) -> Result<f32> {
214 let bytes: [u8; 4] = bytes
215 .try_into()
216 .map_err(|_| ProtocolError::TtcDecode("invalid BINARY_FLOAT length"))?;
217 let mut decoded = bytes;
218 if decoded[0] & 0x80 != 0 {
219 decoded[0] &= 0x7f;
220 } else {
221 for byte in &mut decoded {
222 *byte = !*byte;
223 }
224 }
225 Ok(f32::from_bits(u32::from_be_bytes(decoded)))
226}
227
228pub(crate) fn encode_interval_ds(days: i32, seconds: i32, microseconds: i32) -> Result<[u8; 11]> {
229 let mut bytes = [0u8; 11];
230 let wire_days = u32::try_from(i64::from(days) + TNS_DURATION_MID)
231 .map_err(|_| ProtocolError::TtcDecode("INTERVAL DS days out of range"))?;
232 bytes[..4].copy_from_slice(&wire_days.to_be_bytes());
233 let to_offset_byte = |value: i32| -> Result<u8> {
234 u8::try_from(value + TNS_DURATION_OFFSET)
235 .map_err(|_| ProtocolError::TtcDecode("INTERVAL DS component out of range"))
236 };
237 bytes[4] = to_offset_byte(seconds / 3600)?;
238 bytes[5] = to_offset_byte((seconds % 3600) / 60)?;
239 bytes[6] = to_offset_byte(seconds % 60)?;
240 let fseconds = i64::from(microseconds) * 1000;
241 let wire_fseconds = u32::try_from(fseconds + TNS_DURATION_MID)
242 .map_err(|_| ProtocolError::TtcDecode("INTERVAL DS fractional seconds out of range"))?;
243 bytes[7..].copy_from_slice(&wire_fseconds.to_be_bytes());
244 Ok(bytes)
245}
246
247pub(crate) fn decode_interval_ds(bytes: &[u8]) -> Result<QueryValue> {
248 if bytes.len() < 11 {
249 return Err(ProtocolError::TtcDecode("invalid INTERVAL DS length"));
250 }
251 let days_wire = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
252 let fseconds_wire = u32::from_be_bytes([bytes[7], bytes[8], bytes[9], bytes[10]]);
253 let to_component = |value: i64| -> Result<i32> {
254 i32::try_from(value).map_err(|_| ProtocolError::TtcDecode("INTERVAL DS out of range"))
255 };
256 Ok(QueryValue::IntervalDS {
257 days: to_component(i64::from(days_wire) - TNS_DURATION_MID)?,
258 hours: i32::from(bytes[4]) - TNS_DURATION_OFFSET,
259 minutes: i32::from(bytes[5]) - TNS_DURATION_OFFSET,
260 seconds: i32::from(bytes[6]) - TNS_DURATION_OFFSET,
261 fseconds: to_component(i64::from(fseconds_wire) - TNS_DURATION_MID)?,
262 })
263}
264
265pub(crate) fn encode_interval_ym(years: i32, months: i32) -> Result<[u8; 5]> {
269 let mut bytes = [0u8; 5];
270 let wire_years = u32::try_from(i64::from(years) + TNS_DURATION_MID)
271 .map_err(|_| ProtocolError::TtcDecode("INTERVAL YM years out of range"))?;
272 bytes[..4].copy_from_slice(&wire_years.to_be_bytes());
273 bytes[4] = u8::try_from(months + TNS_DURATION_OFFSET)
274 .map_err(|_| ProtocolError::TtcDecode("INTERVAL YM months out of range"))?;
275 Ok(bytes)
276}
277
278pub(crate) fn decode_interval_ym(bytes: &[u8]) -> Result<QueryValue> {
282 if bytes.len() < 5 {
283 return Err(ProtocolError::TtcDecode("invalid INTERVAL YM length"));
284 }
285 let years_wire = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
286 let years = i32::try_from(i64::from(years_wire) - TNS_DURATION_MID)
287 .map_err(|_| ProtocolError::TtcDecode("INTERVAL YM out of range"))?;
288 Ok(QueryValue::IntervalYM {
289 years,
290 months: i32::from(bytes[4]) - TNS_DURATION_OFFSET,
291 })
292}
293
294pub(crate) fn decode_binary_double(bytes: &[u8]) -> Result<f64> {
295 let bytes: [u8; 8] = bytes
296 .try_into()
297 .map_err(|_| ProtocolError::TtcDecode("invalid BINARY_DOUBLE length"))?;
298 let mut decoded = bytes;
299 if decoded[0] & 0x80 != 0 {
300 decoded[0] &= 0x7f;
301 } else {
302 for byte in &mut decoded {
303 *byte = !*byte;
304 }
305 }
306 Ok(f64::from_bits(u64::from_be_bytes(decoded)))
307}
308
309pub fn encode_number_text(value: &str) -> Result<Vec<u8>> {
314 let value = value.as_bytes();
315 if value.is_empty() {
316 return Err(ProtocolError::TtcDecode("empty NUMBER bind"));
317 }
318 if value.len() > NUMBER_AS_TEXT_CHARS {
319 return Err(ProtocolError::TtcDecode("NUMBER bind text too long"));
320 }
321
322 let mut pos = 0;
323 let mut is_negative = false;
324 if matches!(value.first(), Some(&b'-')) {
325 is_negative = true;
326 pos += 1;
327 }
328
329 let mut digits = Vec::with_capacity(NUMBER_AS_TEXT_CHARS);
330 while let Some(byte) = value.get(pos).copied() {
331 if matches!(byte, b'.' | b'e' | b'E') {
332 break;
333 }
334 if !byte.is_ascii_digit() {
335 return Err(ProtocolError::TtcDecode("invalid NUMBER bind"));
336 }
337 let digit = byte - b'0';
338 pos += 1;
339 if digit == 0 && digits.is_empty() {
340 continue;
341 }
342 digits.push(digit);
343 }
344 let mut decimal_point_index = i32::try_from(digits.len()).unwrap_or(i32::MAX);
345
346 if matches!(value.get(pos), Some(&b'.')) {
347 pos += 1;
348 while let Some(byte) = value.get(pos).copied() {
349 if matches!(byte, b'e' | b'E') {
350 break;
351 }
352 if !byte.is_ascii_digit() {
353 return Err(ProtocolError::TtcDecode("invalid NUMBER bind"));
354 }
355 let digit = byte - b'0';
356 pos += 1;
357 if digit == 0 && digits.is_empty() {
358 decimal_point_index -= 1;
359 continue;
360 }
361 digits.push(digit);
362 }
363 }
364
365 if matches!(value.get(pos).copied(), Some(b'e' | b'E')) {
366 pos += 1;
367 let mut exponent_is_negative = false;
368 if let Some(byte) = value.get(pos).copied() {
369 if byte == b'-' {
370 exponent_is_negative = true;
371 pos += 1;
372 } else if byte == b'+' {
373 pos += 1;
374 }
375 }
376 let exponent_start = pos;
377 while let Some(byte) = value.get(pos).copied() {
378 if !byte.is_ascii_digit() {
379 return Err(ProtocolError::TtcDecode("invalid NUMBER exponent"));
380 }
381 pos += 1;
382 }
383 if exponent_start == pos {
384 return Err(ProtocolError::TtcDecode("empty NUMBER exponent"));
385 }
386 let exponent_text = std::str::from_utf8(&value[exponent_start..pos])
387 .map_err(|_| ProtocolError::TtcDecode("invalid NUMBER exponent"))?;
388 let mut exponent = exponent_text
389 .parse::<i32>()
390 .map_err(|_| ProtocolError::TtcDecode("invalid NUMBER exponent"))?;
391 if exponent_is_negative {
392 exponent = -exponent;
393 }
394 decimal_point_index = decimal_point_index
401 .checked_add(exponent)
402 .ok_or(ProtocolError::TtcDecode("NUMBER bind out of range"))?;
403 }
404
405 if pos < value.len() {
406 return Err(ProtocolError::TtcDecode("invalid NUMBER bind suffix"));
407 }
408
409 while digits.last().is_some_and(|digit| *digit == 0) {
410 digits.pop();
411 }
412 if digits.len() > NUMBER_MAX_DIGITS || !(-129..=126).contains(&decimal_point_index) {
413 return Err(ProtocolError::TtcDecode("NUMBER bind out of range"));
414 }
415
416 let mut prepend_zero = false;
417 if decimal_point_index % 2 != 0 {
418 prepend_zero = true;
419 if !digits.is_empty() {
420 digits.push(0);
421 decimal_point_index += 1;
422 }
423 }
424 if digits.len() % 2 == 1 {
425 digits.push(0);
426 }
427
428 if digits.is_empty() {
429 return Ok(vec![128]);
430 }
431
432 let mut encoded = Vec::with_capacity(digits.len() / 2 + 2);
433 let exponent_on_wire = decimal_point_index / 2 + 192;
434 if !(0..=255).contains(&exponent_on_wire) {
435 return Err(ProtocolError::TtcDecode(
436 "NUMBER bind exponent out of range",
437 ));
438 }
439 let exponent_byte = exponent_on_wire as u8;
440 encoded.push(if is_negative {
441 !exponent_byte
442 } else {
443 exponent_byte
444 });
445
446 let mut digit_pos = 0;
447 for pair_num in 0..(digits.len() / 2) {
448 let mut digit = if pair_num == 0 && prepend_zero {
449 let digit = digits[digit_pos];
450 digit_pos += 1;
451 digit
452 } else {
453 let digit = digits[digit_pos] * 10 + digits[digit_pos + 1];
454 digit_pos += 2;
455 digit
456 };
457 if is_negative {
458 digit = 101 - digit;
459 } else {
460 digit += 1;
461 }
462 encoded.push(digit);
463 }
464
465 if is_negative && digits.len() < NUMBER_MAX_DIGITS {
466 encoded.push(102);
467 }
468
469 Ok(encoded)
470}
471
472pub fn decode_number_value(bytes: &[u8]) -> Result<QueryValue> {
473 Ok(QueryValue::Number(super::number::OracleNumber::from_wire(
474 bytes,
475 )?))
476}
477
478pub fn decode_number_text_into(
490 bytes: &[u8],
491 digits: &mut Vec<u8>,
492 text: &mut String,
493) -> Result<bool> {
494 match decode_number_parts(bytes, digits, text)? {
495 super::number::DecodedNumber::Text { is_integer } => Ok(is_integer),
497 super::number::DecodedNumber::Parts {
498 is_negative,
499 decimal_point_index,
500 is_integer,
501 } => {
502 format_number_digits(digits, is_negative, decimal_point_index, text);
503 Ok(is_integer)
504 }
505 }
506}
507
508pub(crate) fn decode_number_parts(
517 bytes: &[u8],
518 digits: &mut Vec<u8>,
519 text: &mut String,
520) -> Result<super::number::DecodedNumber> {
521 use super::number::DecodedNumber;
522
523 if bytes.len() > 21 {
524 return Err(ProtocolError::TtcDecode("encoded NUMBER too long"));
525 }
526 let Some(&first) = bytes.first() else {
527 return Err(ProtocolError::TtcDecode("empty NUMBER"));
528 };
529 let is_positive = first & 0x80 != 0;
530 digits.clear();
531 if bytes.len() == 1 {
532 if is_positive {
533 text.push('0');
534 } else {
535 text.push_str("-1e126");
536 }
537 return Ok(DecodedNumber::Text { is_integer: true });
538 }
539
540 let exponent_byte = if is_positive { first } else { !first };
541 let exponent = i16::from(exponent_byte) - 193;
542 let mut decimal_point_index = exponent * 2 + 2;
543 let mut end = bytes.len();
544 if !is_positive && bytes[end - 1] == 102 {
545 end -= 1;
546 }
547
548 for (index, encoded) in bytes.iter().enumerate().take(end).skip(1) {
549 let value = if is_positive {
550 encoded.saturating_sub(1)
551 } else {
552 101u8.saturating_sub(*encoded)
553 };
554
555 let first_digit = value / 10;
556 if first_digit == 0 && digits.is_empty() {
557 decimal_point_index -= 1;
558 } else if first_digit == 10 {
559 digits.push(1);
560 digits.push(0);
561 decimal_point_index += 1;
562 } else if first_digit != 0 || index > 0 {
563 digits.push(first_digit);
564 }
565
566 let second_digit = value % 10;
567 if second_digit != 0 || index < end - 1 {
568 digits.push(second_digit);
569 }
570 }
571
572 let len = i16::try_from(digits.len()).unwrap_or(i16::MAX);
576 let is_integer = decimal_point_index > 0 && decimal_point_index >= len;
577
578 Ok(DecodedNumber::Parts {
579 is_negative: !is_positive,
580 decimal_point_index,
581 is_integer,
582 })
583}
584
585pub(crate) fn decode_number_parts_stack(
594 bytes: &[u8],
595 digit_buf: &mut [u8],
596) -> Result<super::number::DecodedNumberStack> {
597 use super::number::DecodedNumberStack;
598
599 if bytes.len() > 21 {
600 return Err(ProtocolError::TtcDecode("encoded NUMBER too long"));
601 }
602 let Some(&first) = bytes.first() else {
603 return Err(ProtocolError::TtcDecode("empty NUMBER"));
604 };
605 let is_positive = first & 0x80 != 0;
606 if bytes.len() == 1 {
607 return Ok(DecodedNumberStack::Sentinel {
608 text: if is_positive { "0" } else { "-1e126" },
609 is_integer: true,
610 });
611 }
612
613 let exponent_byte = if is_positive { first } else { !first };
614 let exponent = i16::from(exponent_byte) - 193;
615 let mut decimal_point_index = exponent * 2 + 2;
616 let mut end = bytes.len();
617 if !is_positive && bytes[end - 1] == 102 {
618 end -= 1;
619 }
620
621 let mut len = 0usize;
622 let mut coeff: Option<i128> = Some(0);
629 let push = |buf: &mut [u8], d: u8, len: &mut usize, coeff: &mut Option<i128>| {
633 if *len < buf.len() {
634 buf[*len] = d;
635 *len += 1;
636 }
637 *coeff = coeff
638 .and_then(|acc| acc.checked_mul(10))
639 .and_then(|acc| acc.checked_add(i128::from(d)));
640 };
641
642 for (index, encoded) in bytes.iter().enumerate().take(end).skip(1) {
643 let value = if is_positive {
644 encoded.saturating_sub(1)
645 } else {
646 101u8.saturating_sub(*encoded)
647 };
648
649 let first_digit = value / 10;
650 if first_digit == 0 && len == 0 {
651 decimal_point_index -= 1;
652 } else if first_digit == 10 {
653 push(digit_buf, 1, &mut len, &mut coeff);
654 push(digit_buf, 0, &mut len, &mut coeff);
655 decimal_point_index += 1;
656 } else if first_digit != 0 || index > 0 {
657 push(digit_buf, first_digit, &mut len, &mut coeff);
658 }
659
660 let second_digit = value % 10;
661 if second_digit != 0 || index < end - 1 {
662 push(digit_buf, second_digit, &mut len, &mut coeff);
663 }
664 }
665
666 let len_i16 = i16::try_from(len).unwrap_or(i16::MAX);
667 let is_integer = decimal_point_index > 0 && decimal_point_index >= len_i16;
668
669 let coefficient = coeff.map(|acc| if is_positive { acc } else { -acc });
674
675 Ok(DecodedNumberStack::Parts {
676 digit_len: len,
677 is_negative: !is_positive,
678 decimal_point_index,
679 is_integer,
680 coefficient,
681 })
682}
683
684pub(crate) fn format_number_digits(
690 digits: &[u8],
691 is_negative: bool,
692 decimal_point_index: i16,
693 text: &mut String,
694) {
695 if is_negative {
696 text.push('-');
697 }
698 if decimal_point_index <= 0 {
699 text.push_str("0.");
700 for _ in decimal_point_index..0 {
701 text.push('0');
702 }
703 }
704 for (index, digit) in digits.iter().enumerate() {
705 if index > 0
706 && matches!(
707 i16::try_from(index)
708 .unwrap_or(i16::MAX)
709 .cmp(&decimal_point_index),
710 std::cmp::Ordering::Equal
711 )
712 {
713 text.push('.');
714 }
715 text.push(char::from(b'0' + *digit));
716 }
717 if decimal_point_index > i16::try_from(digits.len()).unwrap_or(i16::MAX) {
718 for _ in i16::try_from(digits.len()).unwrap_or(i16::MAX)..decimal_point_index {
719 text.push('0');
720 }
721 }
722}
723
724pub(crate) fn decode_text_value(bytes: &[u8], csfrm: u8) -> Result<String> {
725 if csfrm == CS_FORM_NCHAR {
726 let units = bytes
727 .chunks_exact(2)
728 .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
729 .collect::<Vec<_>>();
730 if units.len() * 2 != bytes.len() {
731 return Err(ProtocolError::TtcDecode("invalid UTF-16 text length"));
732 }
733 String::from_utf16(&units).map_err(|_| ProtocolError::TtcDecode("invalid UTF-16 text"))
734 } else {
735 String::from_utf8(bytes.to_vec())
736 .map_err(|_| ProtocolError::TtcDecode("invalid UTF-8 text"))
737 }
738}
739
740#[cfg(test)]
741mod tests {
742 use super::*;
743
744 #[test]
750 fn number_text_huge_exponent_rejected_not_panicked() {
751 let crafted = format!("{}e+2147483647", "1".repeat(160));
753 assert_eq!(crafted.len(), NUMBER_AS_TEXT_CHARS);
754 assert!(encode_number_text(&crafted).is_err());
755
756 let crafted_neg = format!("0.{}e-2147483647", "0".repeat(158));
758 assert!(encode_number_text(&crafted_neg).is_err());
759 }
760
761 #[test]
762 fn number_text_ordinary_values_still_encode() {
763 for ok in [
764 "0",
765 "1",
766 "-1",
767 "3.14159",
768 "1e10",
769 "-2.5e-3",
770 "12345678901234567890",
771 ] {
772 assert!(encode_number_text(ok).is_ok(), "expected {ok} to encode");
773 }
774 }
775}