1use core::{ffi::*, fmt};
19
20pub mod output;
21mod parser;
22use argument::*;
23pub use parser::format;
24
25pub mod argument {
26 use super::*;
27
28 bitflags::bitflags! {
29 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
34 pub struct Flags: u8 {
35 const LEFT_ALIGN = 0b00000001;
38 const PREPEND_PLUS = 0b00000010;
44 const PREPEND_SPACE = 0b00000100;
51 const PREPEND_ZERO = 0b00001000;
57 const THOUSANDS_GROUPING = 0b00010000;
60 const ALTERNATE_FORM = 0b00100000;
68 }
69 }
70
71 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
72 pub enum DoubleFormat {
73 Normal,
75 UpperNormal,
77 Scientific,
79 UpperScientific,
81 Auto,
83 UpperAuto,
85 Hex,
87 UpperHex,
89 }
90
91 impl DoubleFormat {
92 pub fn is_upper(self) -> bool {
94 use DoubleFormat::*;
95 matches!(self, UpperNormal | UpperScientific | UpperAuto | UpperHex)
96 }
97
98 pub fn set_upper(self, upper: bool) -> Self {
99 use DoubleFormat::*;
100 match self {
101 Normal | UpperNormal => {
102 if upper {
103 UpperNormal
104 } else {
105 Normal
106 }
107 }
108 Scientific | UpperScientific => {
109 if upper {
110 UpperScientific
111 } else {
112 Scientific
113 }
114 }
115 Auto | UpperAuto => {
116 if upper {
117 UpperAuto
118 } else {
119 Auto
120 }
121 }
122 Hex | UpperHex => {
123 if upper {
124 UpperHex
125 } else {
126 Hex
127 }
128 }
129 }
130 }
131 }
132
133 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
134 #[non_exhaustive]
135 pub enum SignedInt {
136 Int(c_int),
137 Char(c_schar),
138 Short(c_short),
139 Long(c_long),
140 LongLong(c_longlong),
141 Isize(isize),
142 }
143
144 impl From<SignedInt> for i64 {
145 fn from(num: SignedInt) -> Self {
146 #[allow(clippy::unnecessary_cast)]
148 match num {
149 SignedInt::Int(x) => x as i64,
150 SignedInt::Char(x) => x as i64,
151 SignedInt::Short(x) => x as i64,
152 SignedInt::Long(x) => x as i64,
153 SignedInt::LongLong(x) => x as i64,
154 SignedInt::Isize(x) => x as i64,
155 }
156 }
157 }
158
159 impl SignedInt {
160 pub fn is_sign_negative(self) -> bool {
161 match self {
162 SignedInt::Int(x) => x < 0,
163 SignedInt::Char(x) => x < 0,
164 SignedInt::Short(x) => x < 0,
165 SignedInt::Long(x) => x < 0,
166 SignedInt::LongLong(x) => x < 0,
167 SignedInt::Isize(x) => x < 0,
168 }
169 }
170 }
171
172 impl fmt::Display for SignedInt {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 match self {
175 SignedInt::Int(x) => fmt::Display::fmt(x, f),
176 SignedInt::Char(x) => fmt::Display::fmt(x, f),
177 SignedInt::Short(x) => fmt::Display::fmt(x, f),
178 SignedInt::Long(x) => fmt::Display::fmt(x, f),
179 SignedInt::LongLong(x) => fmt::Display::fmt(x, f),
180 SignedInt::Isize(x) => fmt::Display::fmt(x, f),
181 }
182 }
183 }
184
185 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
186 #[non_exhaustive]
187 pub enum UnsignedInt {
188 Int(c_uint),
189 Char(c_uchar),
190 Short(c_ushort),
191 Long(c_ulong),
192 LongLong(c_ulonglong),
193 Isize(usize),
194 }
195
196 impl From<UnsignedInt> for u64 {
197 fn from(num: UnsignedInt) -> Self {
198 #[allow(clippy::unnecessary_cast)]
200 match num {
201 UnsignedInt::Int(x) => x as u64,
202 UnsignedInt::Char(x) => x as u64,
203 UnsignedInt::Short(x) => x as u64,
204 UnsignedInt::Long(x) => x as u64,
205 UnsignedInt::LongLong(x) => x as u64,
206 UnsignedInt::Isize(x) => x as u64,
207 }
208 }
209 }
210
211 impl fmt::Display for UnsignedInt {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 match self {
214 UnsignedInt::Int(x) => fmt::Display::fmt(x, f),
215 UnsignedInt::Char(x) => fmt::Display::fmt(x, f),
216 UnsignedInt::Short(x) => fmt::Display::fmt(x, f),
217 UnsignedInt::Long(x) => fmt::Display::fmt(x, f),
218 UnsignedInt::LongLong(x) => fmt::Display::fmt(x, f),
219 UnsignedInt::Isize(x) => fmt::Display::fmt(x, f),
220 }
221 }
222 }
223
224 impl fmt::LowerHex for UnsignedInt {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self {
227 UnsignedInt::Int(x) => fmt::LowerHex::fmt(x, f),
228 UnsignedInt::Char(x) => fmt::LowerHex::fmt(x, f),
229 UnsignedInt::Short(x) => fmt::LowerHex::fmt(x, f),
230 UnsignedInt::Long(x) => fmt::LowerHex::fmt(x, f),
231 UnsignedInt::LongLong(x) => fmt::LowerHex::fmt(x, f),
232 UnsignedInt::Isize(x) => fmt::LowerHex::fmt(x, f),
233 }
234 }
235 }
236
237 impl fmt::UpperHex for UnsignedInt {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 match self {
240 UnsignedInt::Int(x) => fmt::UpperHex::fmt(x, f),
241 UnsignedInt::Char(x) => fmt::UpperHex::fmt(x, f),
242 UnsignedInt::Short(x) => fmt::UpperHex::fmt(x, f),
243 UnsignedInt::Long(x) => fmt::UpperHex::fmt(x, f),
244 UnsignedInt::LongLong(x) => fmt::UpperHex::fmt(x, f),
245 UnsignedInt::Isize(x) => fmt::UpperHex::fmt(x, f),
246 }
247 }
248 }
249
250 impl fmt::Octal for UnsignedInt {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 match self {
253 UnsignedInt::Int(x) => fmt::Octal::fmt(x, f),
254 UnsignedInt::Char(x) => fmt::Octal::fmt(x, f),
255 UnsignedInt::Short(x) => fmt::Octal::fmt(x, f),
256 UnsignedInt::Long(x) => fmt::Octal::fmt(x, f),
257 UnsignedInt::LongLong(x) => fmt::Octal::fmt(x, f),
258 UnsignedInt::Isize(x) => fmt::Octal::fmt(x, f),
259 }
260 }
261 }
262
263 #[derive(Debug, Copy, Clone, PartialEq)]
265 pub struct Argument<'a> {
266 pub flags: Flags,
267 pub width: c_int,
268 pub precision: Option<c_int>,
269 pub specifier: Specifier<'a>,
270 }
271
272 impl<'a> From<Specifier<'a>> for Argument<'a> {
273 fn from(specifier: Specifier<'a>) -> Self {
274 Self {
275 flags: Flags::empty(),
276 width: 0,
277 precision: None,
278 specifier,
279 }
280 }
281 }
282
283 #[derive(Debug, Copy, Clone, PartialEq)]
285 #[non_exhaustive]
286 pub enum Specifier<'a> {
287 Percent,
289 Int(SignedInt),
291 Uint(UnsignedInt),
293 Octal(UnsignedInt),
295 Double { value: f64, format: DoubleFormat },
297 Bytes(&'a [u8]),
299 String(&'a CStr),
305 Char(c_char),
307 Hex(UnsignedInt),
309 UpperHex(UnsignedInt),
311 Pointer(*const ()),
313 WriteBytesWritten(c_int, *const c_int),
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::{format, output};
327 use crate::va::{vararg_entry, VaListTag};
328 use alloc::string::String;
329 use libc::{c_char, c_int};
330
331 unsafe extern "C" fn fmt_into_impl(
335 out: *mut u8,
336 cap: usize,
337 fmt: *const c_char,
338 tag: *mut VaListTag,
339 ) -> c_int {
340 unsafe {
344 let mut s = String::new();
345 let n = format(fmt, &mut *tag, output::fmt_write(&mut s));
346 if n < 0 {
347 return n;
348 }
349 let bytes = s.as_bytes();
350 assert!(bytes.len() < cap, "test buffer too small");
351 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out, bytes.len());
352 *out.add(bytes.len()) = 0;
353 n
354 }
355 }
356
357 vararg_entry! {
358 #[no_mangle]
359 unsafe extern "C" fn __taproot_printf_test_fmt(
360 out: *mut u8,
361 cap: usize,
362 fmt: *const c_char,
363 ...
364 ) -> c_int => fmt_into_impl
365 }
366
367 mod decl {
371 use libc::{c_char, c_int};
372
373 unsafe extern "C" {
374 pub fn __taproot_printf_test_fmt(
375 out: *mut u8,
376 cap: usize,
377 fmt: *const c_char,
378 ...
379 ) -> c_int;
380 }
381 }
382
383 fn formatted(buf: &[u8], n: c_int) -> &[u8] {
384 assert!(n >= 0, "formatting failed: {n}");
385 let n = n as usize;
386 assert_eq!(buf[n], 0, "missing NUL terminator");
387 &buf[..n]
388 }
389
390 #[test]
391 fn formats_d_s_f_exactly() {
392 let mut buf = [0xAA_u8; 64];
393 let n = unsafe {
394 decl::__taproot_printf_test_fmt(
395 buf.as_mut_ptr(),
396 buf.len(),
397 c"%d %s %.2f".as_ptr(),
398 42 as c_int,
399 c"carrot".as_ptr(),
400 3.14159_f64,
401 )
402 };
403 assert_eq!(formatted(&buf, n), b"42 carrot 3.14");
404 assert_eq!(n, 14);
405 }
406
407 #[test]
408 fn width_precision_and_alignment() {
409 let mut buf = [0xAA_u8; 64];
412 let n = unsafe {
413 decl::__taproot_printf_test_fmt(
414 buf.as_mut_ptr(),
415 buf.len(),
416 c"%08.3f|%-6d|%5s".as_ptr(),
417 2.5_f64,
418 42 as c_int,
419 c"ab".as_ptr(),
420 )
421 };
422 assert_eq!(formatted(&buf, n), b"0002.500|42 | ab");
423 }
424
425 #[test]
426 fn star_width_comes_off_the_walk() {
427 let mut buf = [0xAA_u8; 32];
429 let n = unsafe {
430 decl::__taproot_printf_test_fmt(
431 buf.as_mut_ptr(),
432 buf.len(),
433 c"%*d".as_ptr(),
434 5 as c_int,
435 42 as c_int,
436 )
437 };
438 assert_eq!(formatted(&buf, n), b" 42");
439 }
440
441 #[test]
442 fn args_cross_the_overflow_area() {
443 let mut buf = [0xAA_u8; 64];
448 let n = unsafe {
449 decl::__taproot_printf_test_fmt(
450 buf.as_mut_ptr(),
451 buf.len(),
452 c"%d %d %d %d %d %d %d %.1f".as_ptr(),
453 1 as c_int,
454 2 as c_int,
455 3 as c_int,
456 4 as c_int,
457 5 as c_int,
458 6 as c_int,
459 7 as c_int,
460 8.5_f64,
461 )
462 };
463 assert_eq!(formatted(&buf, n), b"1 2 3 4 5 6 7 8.5");
464 }
465
466 #[test]
467 fn percent_n_is_rejected() {
468 let mut sink: c_int = 0;
472 let mut buf = [0xAA_u8; 32];
473 let n = unsafe {
474 decl::__taproot_printf_test_fmt(
475 buf.as_mut_ptr(),
476 buf.len(),
477 c"a%nb".as_ptr(),
478 &mut sink as *mut c_int,
479 )
480 };
481 assert_eq!(n, -1);
482 assert_eq!(sink, 0);
483 }
484}