1use super::arg::Arg;
3use super::fmt_fp::format_float;
4use super::locale::Locale;
5use bstr::{BStr, ByteSlice as _};
6use std::io::{self, Write as IoWrite};
7use std::mem;
8use std::result::Result;
9
10#[derive(Debug, PartialEq, Eq)]
12pub enum Error {
13 BadFormatString,
15 MissingArg,
17 BadArgType,
19 Overflow,
21 Io(io::ErrorKind),
23}
24
25impl From<io::Error> for Error {
27 fn from(err: io::Error) -> Error {
28 Error::Io(err.kind())
29 }
30}
31
32#[derive(Debug, Copy, Clone, Default)]
33pub(super) struct ModifierFlags {
34 pub alt_form: bool, pub zero_pad: bool, pub left_adj: bool, pub pad_pos: bool, pub mark_pos: bool, pub grouped: bool, }
41
42impl ModifierFlags {
43 fn try_set(&mut self, c: u8) -> bool {
46 match c {
47 b'#' => self.alt_form = true,
48 b'0' => self.zero_pad = true,
49 b'-' => self.left_adj = true,
50 b' ' => self.pad_pos = true,
51 b'+' => self.mark_pos = true,
52 b'\'' => self.grouped = true,
53 _ => return false,
54 }
55 true
56 }
57}
58
59#[derive(Debug, Copy, Clone, PartialEq, Eq)]
62#[allow(non_camel_case_types)]
63enum ConversionPrefix {
64 Empty,
65 hh,
66 h,
67 l,
68 ll,
69 j,
70 t,
71 z,
72 L,
73}
74
75#[derive(Debug, Copy, Clone, PartialEq, Eq)]
76#[allow(non_camel_case_types)]
77#[rustfmt::skip]
78pub(super) enum ConversionSpec {
79 d, o, u, x, X,
82
83 n,
85
86 a, A, e, E, f, F, g, G,
88
89 p,
91
92 c, s,
94}
95
96impl ConversionSpec {
97 fn supports_prefix(self, prefix: ConversionPrefix) -> bool {
99 use ConversionPrefix::*;
100 use ConversionSpec::*;
101 if matches!(prefix, Empty) {
102 return true;
104 }
105 match self {
106 d | o | u | x | X | n => matches!(prefix, hh | h | l | ll | j | t | z),
107 a | A | e | E | f | F | g | G => matches!(prefix, l | L),
108 p => false,
109 c | s => false,
110 }
111 }
112
113 #[inline]
116 pub(super) fn is_lower(self) -> bool {
117 use ConversionSpec::*;
118 match self {
119 d | o | u | x | n | a | e | f | g | p | c | s => true,
120 X | A | E | F | G => false,
121 }
122 }
123
124 fn from_byte(cc: u8) -> Option<Self> {
126 use ConversionSpec::*;
127 let res = match cc {
128 b'd' | b'i' => d,
129 b'o' => o,
130 b'u' => u,
131 b'x' => x,
132 b'X' => X,
133 b'n' => n,
134 b'a' => a,
135 b'A' => A,
136 b'e' => e,
137 b'E' => E,
138 b'f' => f,
139 b'F' => F,
140 b'g' => g,
141 b'G' => G,
142 b'p' => p,
143 b'c' => c,
144 b's' => s,
145 _ => return None,
146 };
147 Some(res)
148 }
149}
150
151trait FormatString<'a> {
152 fn is_empty(&self) -> bool;
153 fn at(&self, index: usize) -> Option<u8>;
154 fn advance_by(&mut self, n: usize);
155 fn take_literal(&mut self) -> &'a BStr;
156}
157
158impl<'a> FormatString<'a> for &'a BStr {
159 fn is_empty(&self) -> bool {
160 self.len() == 0
161 }
162
163 fn at(&self, index: usize) -> Option<u8> {
164 self.get(index).copied()
165 }
166
167 fn advance_by(&mut self, n: usize) {
168 debug_assert!(
169 n <= self.len(),
170 "FormatString::advance_by(): index out of bounds"
171 );
172 *self = self[n..].as_bstr();
173 }
174
175 fn take_literal(&mut self) -> &'a BStr {
176 let non_percents: usize = self.iter().take_while(|&&c| c != b'%').count();
177 let percent_pairs = self[non_percents..]
179 .iter()
180 .take_while(|&&c| c == b'%')
181 .count()
182 / 2;
183 let (prefix, rest) = self.split_at(non_percents + percent_pairs * 2);
184 *self = rest.as_bstr();
185 prefix[..prefix.len() - percent_pairs].as_bstr()
187 }
188}
189
190fn get_int<'a>(fmt: &mut impl FormatString<'a>) -> Result<usize, Error> {
195 use Error::Overflow;
196 let mut i: usize = 0;
197 while let Some(digit) = fmt.at(0).and_then(|c| {
198 if c.is_ascii_digit() {
199 Some(c - b'0')
200 } else {
201 None
202 }
203 }) {
204 i = i.checked_mul(10).ok_or(Overflow)?;
205 i = i.checked_add(usize::from(digit)).ok_or(Overflow)?;
206 fmt.advance_by(1);
207 }
208 Ok(i)
209}
210
211fn get_prefix<'a>(fmt: &mut impl FormatString<'a>) -> ConversionPrefix {
213 use ConversionPrefix as CP;
214 let prefix = match fmt.at(0).unwrap_or(b'\0') {
215 b'h' if fmt.at(1) == Some(b'h') => CP::hh,
216 b'h' => CP::h,
217 b'l' if fmt.at(1) == Some(b'l') => CP::ll,
218 b'l' => CP::l,
219 b'j' => CP::j,
220 b't' => CP::t,
221 b'z' => CP::z,
222 b'L' => CP::L,
223 _ => CP::Empty,
224 };
225 fmt.advance_by(match prefix {
226 CP::Empty => 0,
227 CP::hh | CP::ll => 2,
228 _ => 1,
229 });
230 prefix
231}
232
233fn get_specifier<'a>(fmt: &mut impl FormatString<'a>) -> Result<ConversionSpec, Error> {
236 let prefix = get_prefix(fmt);
237 let spec = fmt
238 .at(0)
239 .and_then(ConversionSpec::from_byte)
240 .ok_or(Error::BadFormatString)?;
241 if !spec.supports_prefix(prefix) {
242 return Err(Error::BadFormatString);
243 }
244 fmt.advance_by(1);
245 Ok(spec)
246}
247
248fn c_string_prefix(fmt: &BStr) -> &BStr {
249 let len = fmt.iter().position(|&c| c == b'\0').unwrap_or(fmt.len());
250 fmt[..len].as_bstr()
251}
252
253fn check_printf_count(count: usize) -> Result<usize, Error> {
254 if count > i32::MAX as usize {
255 return Err(Error::Overflow);
256 }
257 Ok(count)
258}
259
260fn add_printf_count(count: usize, add: usize) -> Result<usize, Error> {
261 check_printf_count(count.checked_add(add).ok_or(Error::Overflow)?)
262}
263
264pub(crate) trait FormatSink {
265 fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), Error>;
266
267 fn write_repeat(&mut self, byte: u8, count: usize) -> Result<(), Error> {
268 assert!(matches!(byte, b'0' | b' '));
269 const ZEROS: &[u8] = b"0000000000000000";
270 const SPACES: &[u8] = b" ";
271 let bytes = if byte == b'0' { ZEROS } else { SPACES };
272 let mut remaining = count;
273 while remaining > 0 {
274 let size = remaining.min(bytes.len());
275 self.write_bytes(&bytes[..size])?;
276 remaining -= size;
277 }
278 Ok(())
279 }
280}
281
282struct IoSink<'a, W: IoWrite + ?Sized> {
283 output: &'a mut W,
284}
285
286impl<W: IoWrite + ?Sized> FormatSink for IoSink<'_, W> {
287 fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> {
288 self.output.write_all(bytes)?;
289 Ok(())
290 }
291}
292
293struct SliceSink<'a> {
294 buffer: &'a mut [u8],
295 len: usize,
296}
297
298impl FormatSink for SliceSink<'_> {
299 fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> {
300 let remaining = self.buffer.len().saturating_sub(self.len);
301 let stored = remaining.min(bytes.len());
302 if stored != 0 {
303 self.buffer[self.len..self.len + stored].copy_from_slice(&bytes[..stored]);
304 self.len += stored;
305 }
306 Ok(())
307 }
308
309 fn write_repeat(&mut self, byte: u8, count: usize) -> Result<(), Error> {
310 assert!(matches!(byte, b'0' | b' '));
311 let remaining = self.buffer.len().saturating_sub(self.len);
312 let stored = remaining.min(count);
313 if stored != 0 {
314 self.buffer[self.len..self.len + stored].fill(byte);
315 self.len += stored;
316 }
317 Ok(())
318 }
319}
320
321pub fn printf_locale_to_slice(
322 buffer: &mut [u8],
323 fmt: &BStr,
324 locale: &Locale,
325 args: &mut [Arg],
326) -> Result<usize, Error> {
327 let mut sink = SliceSink { buffer, len: 0 };
328 format_locale(&mut sink, fmt, locale, args)
329}
330
331pub(super) fn pad(
333 f: &mut (impl FormatSink + ?Sized),
334 c: u8,
335 min_width: usize,
336 current_width: usize,
337) -> Result<(), Error> {
338 assert!(matches!(c, b'0' | b' '));
339 if current_width >= min_width {
340 return Ok(());
341 }
342 f.write_repeat(c, min_width - current_width)
343}
344
345fn format_unsigned_digits(
346 storage: &mut [u8; 64],
347 mut value: u64,
348 radix: u64,
349 uppercase: bool,
350) -> &[u8] {
351 debug_assert!(matches!(radix, 8 | 10 | 16));
352 debug_assert_ne!(value, 0);
353
354 let digits = if uppercase {
355 b"0123456789ABCDEF"
356 } else {
357 b"0123456789abcdef"
358 };
359 let mut index = storage.len();
360
361 while value != 0 {
362 index -= 1;
363 storage[index] = digits[(value % radix) as usize];
364 value /= radix;
365 }
366
367 &storage[index..]
368}
369
370pub fn sprintf_locale<W: IoWrite + ?Sized>(
396 f: &mut W,
397 fmt: &BStr,
398 locale: &Locale,
399 args: &mut [Arg],
400) -> Result<usize, Error> {
401 let mut sink = IoSink { output: f };
402 format_locale(&mut sink, fmt, locale, args)
403}
404
405fn format_locale(
406 f: &mut (impl FormatSink + ?Sized),
407 fmt: &BStr,
408 locale: &Locale,
409 args: &mut [Arg],
410) -> Result<usize, Error> {
411 use ConversionSpec as CS;
412 let mut s = c_string_prefix(fmt);
413 let mut args = args.iter_mut();
414 let mut out_len: usize = 0;
415 let mut float_buf = None;
416 'main: while !s.is_empty() {
417 let lit = s.take_literal();
419 if !lit.is_empty() {
420 f.write_bytes(lit.as_ref())?;
421 out_len = add_printf_count(out_len, lit.len())?;
422 continue 'main;
423 }
424
425 debug_assert_eq!(s.at(0), Some(b'%'));
427 s.advance_by(1);
428
429 let mut flags = ModifierFlags::default();
431 while flags.try_set(s.at(0).unwrap_or(b'\0')) {
432 s.advance_by(1);
433 }
434 if flags.left_adj {
435 flags.zero_pad = false;
436 }
437
438 let desired_width = if s.at(0) == Some(b'*') {
440 let arg_width = args.next().ok_or(Error::MissingArg)?.as_sint()?;
441 s.advance_by(1);
442 if arg_width < 0 {
443 flags.left_adj = true;
444 }
445 arg_width
446 .unsigned_abs()
447 .try_into()
448 .map_err(|_| Error::Overflow)?
449 } else {
450 get_int(&mut s)?
451 };
452 check_printf_count(desired_width)?;
453
454 let mut desired_precision: Option<usize> = if s.at(0) == Some(b'.') && s.at(1) == Some(b'*')
456 {
457 s.advance_by(2);
460 let p = args.next().ok_or(Error::MissingArg)?.as_sint()?;
461 p.try_into().ok()
462 } else if s.at(0) == Some(b'.') {
463 s.advance_by(1);
464 Some(get_int(&mut s)?)
465 } else {
466 None
467 };
468 if let Some(precision) = desired_precision {
469 check_printf_count(precision)?;
470 }
471
472 let conv_spec = get_specifier(&mut s)?;
474 let arg = args.next().ok_or(Error::MissingArg)?;
475 let mut prefix = b"".as_slice();
476
477 if flags.grouped && !matches!(conv_spec, CS::d | CS::u | CS::f | CS::F) {
480 return Err(Error::BadFormatString);
481 }
482
483 let spec_is_numeric = matches!(conv_spec, CS::d | CS::u | CS::o | CS::p | CS::x | CS::X);
487 if spec_is_numeric && desired_precision.is_some() {
488 flags.zero_pad = false;
489 }
490
491 let mut body_storage = [0u8; 64];
494 let body = match conv_spec {
495 CS::n => {
496 arg.set_count(out_len)?;
497 continue 'main;
498 }
499 CS::e | CS::f | CS::g | CS::a | CS::E | CS::F | CS::G | CS::A => {
500 let float = arg.as_float()?;
502 let buf = float_buf.get_or_insert_with(|| Vec::with_capacity(64));
503 buf.clear();
504 let len = format_float(
505 f,
506 float,
507 desired_width,
508 desired_precision,
509 flags,
510 locale,
511 conv_spec,
512 buf,
513 )?;
514 out_len = add_printf_count(out_len, len)?;
515 continue 'main;
516 }
517 CS::p => {
518 const PTR_HEX_DIGITS: usize = 2 * mem::size_of::<*const u8>();
519 desired_precision = desired_precision.map(|p| p.max(PTR_HEX_DIGITS));
520 let uint = arg.as_uint()?;
521 if uint == 0 {
522 &[][..]
523 } else {
524 prefix = b"0x";
525 format_unsigned_digits(&mut body_storage, uint, 16, false)
526 }
527 }
528 CS::x | CS::X => {
529 let lower = conv_spec.is_lower();
532 let uint = arg.as_wrapping_sint()?;
533 if uint == 0 {
534 &[][..]
535 } else {
536 if flags.alt_form {
537 prefix = if lower { b"0x" } else { b"0X" };
538 }
539 format_unsigned_digits(&mut body_storage, uint, 16, !lower)
540 }
541 }
542 CS::o => {
543 let uint = arg.as_uint()?;
544 let body = if uint == 0 {
545 &[][..]
546 } else {
547 format_unsigned_digits(&mut body_storage, uint, 8, false)
548 };
549 if flags.alt_form && desired_precision.unwrap_or(0) <= body.len() + 1 {
550 desired_precision = Some(body.len() + 1);
551 }
552 body
553 }
554 CS::u => {
555 let uint = arg.as_uint()?;
556 if uint == 0 {
557 &[][..]
558 } else {
559 format_unsigned_digits(&mut body_storage, uint, 10, false)
560 }
561 }
562 CS::d => {
563 let arg_i = arg.as_sint()?;
564 if arg_i < 0 {
565 prefix = b"-";
566 } else if flags.mark_pos {
567 prefix = b"+";
568 } else if flags.pad_pos {
569 prefix = b" ";
570 }
571 if arg_i == 0 {
572 &[][..]
573 } else {
574 format_unsigned_digits(&mut body_storage, arg_i.unsigned_abs(), 10, false)
575 }
576 }
577 CS::c => {
578 flags.zero_pad = false;
579 body_storage[0] = arg.as_uchar()?;
580 &body_storage[..1]
581 }
582 CS::s => {
583 let s = arg.as_bstr()?;
584 flags.zero_pad = false;
585 let scan_limit =
586 desired_precision.map_or(s.len(), |precision| precision.min(s.len()));
587 let len = s[..scan_limit]
588 .iter()
589 .position(|&c| c == b'\0')
590 .unwrap_or(scan_limit);
591 desired_precision = Some(len);
592 &s[..len]
593 }
594 };
595 if spec_is_numeric && body.is_empty() {
597 debug_assert_eq!(arg.as_uint().unwrap(), 0);
598 }
599
600 let wants_grouping = flags.grouped && locale.thousands_sep.is_some();
603 let body_width = match wants_grouping {
604 true => body.len() + locale.separator_count(body.len()),
606 false => body.len(),
607 };
608
609 let desired_precision = if !spec_is_numeric {
613 desired_precision.unwrap_or(body_width)
614 } else {
615 desired_precision.unwrap_or(1).max(body_width)
616 };
617
618 let prefix_width = prefix.len();
619 let unpadded_width = prefix_width
620 .checked_add(desired_precision)
621 .ok_or(Error::Overflow)?;
622 let width = desired_width.max(unpadded_width);
623
624 if !flags.left_adj && !flags.zero_pad {
626 pad(f, b' ', width, unpadded_width)?;
627 }
628
629 f.write_bytes(prefix)?;
631
632 if !flags.left_adj && flags.zero_pad {
634 pad(f, b'0', width, unpadded_width)?;
635 }
636
637 pad(f, b'0', desired_precision, body_width)?;
640
641 if wants_grouping {
643 f.write_bytes(&locale.apply_grouping(body))?;
644 } else {
645 f.write_bytes(body)?;
646 }
647
648 if flags.left_adj {
650 pad(f, b' ', width, unpadded_width)?;
651 }
652
653 out_len = add_printf_count(out_len, width)?;
654 }
655
656 Ok(out_len)
657}