1use core::cell::Cell;
4use core::ffi::*;
5use core::fmt;
6use core::str::from_utf8;
7
8#[cfg(feature = "std")]
9pub use yes_std::*;
10
11use crate::{Argument, DoubleFormat, Flags, Specifier};
12
13struct DummyWriter(usize);
14
15impl fmt::Write for DummyWriter {
16 fn write_str(&mut self, s: &str) -> fmt::Result {
17 self.0 += s.len();
18 Ok(())
19 }
20}
21
22struct WriteCounter<'a, T: fmt::Write>(&'a mut T, usize);
23
24impl<'a, T: fmt::Write> fmt::Write for WriteCounter<'a, T> {
25 fn write_str(&mut self, s: &str) -> fmt::Result {
26 self.1 += s.len();
27 self.0.write_str(s)
28 }
29}
30
31fn write_str(
32 w: &mut impl fmt::Write,
33 flags: Flags,
34 width: c_int,
35 precision: Option<c_int>,
36 b: &[u8],
37) -> fmt::Result {
38 let string = from_utf8(b).map_err(|_| fmt::Error)?;
39 let precision = precision.unwrap_or(string.len() as c_int);
40 if flags.contains(Flags::LEFT_ALIGN) {
41 write!(
42 w,
43 "{:1$.prec$}",
44 string,
45 width as usize,
46 prec = precision as usize
47 )
48 } else {
49 write!(
50 w,
51 "{:>1$.prec$}",
52 string,
53 width as usize,
54 prec = precision as usize
55 )
56 }
57}
58
59macro_rules! define_numeric {
60 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr) => {
61 define_numeric!($w, $data, $flags, $width, $precision, "")
62 };
63 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr, $ty:expr) => {{
64 use fmt::Write;
65 if $flags.contains(Flags::LEFT_ALIGN) {
66 if $flags.contains(Flags::PREPEND_PLUS) {
67 write!(
68 $w,
69 concat!("{:<+width$.prec$", $ty, "}"),
70 $data,
71 width = $width as usize,
72 prec = $precision as usize
73 )
74 } else if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
75 write!(
76 $w,
77 concat!(" {:<width$.prec$", $ty, "}"),
78 $data,
79 width = ($width as usize).wrapping_sub(1),
80 prec = $precision as usize
81 )
82 } else {
83 write!(
84 $w,
85 concat!("{:<width$.prec$", $ty, "}"),
86 $data,
87 width = $width as usize,
88 prec = $precision as usize
89 )
90 }
91 } else if $flags.contains(Flags::PREPEND_PLUS) {
92 if $flags.contains(Flags::PREPEND_ZERO) {
93 write!(
94 $w,
95 concat!("{:+0width$.prec$", $ty, "}"),
96 $data,
97 width = $width as usize,
98 prec = $precision as usize
99 )
100 } else {
101 write!(
102 $w,
103 concat!("{:+width$.prec$", $ty, "}"),
104 $data,
105 width = $width as usize,
106 prec = $precision as usize
107 )
108 }
109 } else if $flags.contains(Flags::PREPEND_ZERO) {
110 if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
111 let mut d = DummyWriter(0);
112 let _ = write!(
113 d,
114 concat!("{:.prec$", $ty, "}"),
115 $data,
116 prec = $precision as usize
117 );
118 if d.0 + 1 > $width as usize {
119 $width += 1;
120 }
121 write!(
122 $w,
123 concat!(" {:0width$.prec$", $ty, "}"),
124 $data,
125 width = ($width as usize).wrapping_sub(1),
126 prec = $precision as usize
127 )
128 } else {
129 write!(
130 $w,
131 concat!("{:0width$.prec$", $ty, "}"),
132 $data,
133 width = $width as usize,
134 prec = $precision as usize
135 )
136 }
137 } else {
138 if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
139 let mut d = DummyWriter(0);
140 let _ = write!(
141 d,
142 concat!("{:.prec$", $ty, "}"),
143 $data,
144 prec = $precision as usize
145 );
146 if d.0 + 1 > $width as usize {
147 $width = d.0 as i32 + 1;
148 }
149 }
150 write!(
151 $w,
152 concat!("{:width$.prec$", $ty, "}"),
153 $data,
154 width = $width as usize,
155 prec = $precision as usize
156 )
157 }
158 }};
159}
160
161macro_rules! define_unumeric {
162 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr) => {
163 define_unumeric!($w, $data, $flags, $width, $precision, "")
164 };
165 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr, $ty:expr) => {{
166 if $flags.contains(Flags::LEFT_ALIGN) {
167 if $flags.contains(Flags::ALTERNATE_FORM) {
168 write!(
169 $w,
170 concat!("{:<#width$", $ty, "}"),
171 $data,
172 width = $width as usize
173 )
174 } else {
175 write!(
176 $w,
177 concat!("{:<width$", $ty, "}"),
178 $data,
179 width = $width as usize
180 )
181 }
182 } else if $flags.contains(Flags::ALTERNATE_FORM) {
183 if $flags.contains(Flags::PREPEND_ZERO) {
184 write!(
185 $w,
186 concat!("{:#0width$", $ty, "}"),
187 $data,
188 width = $width as usize
189 )
190 } else {
191 write!(
192 $w,
193 concat!("{:#width$", $ty, "}"),
194 $data,
195 width = $width as usize
196 )
197 }
198 } else if $flags.contains(Flags::PREPEND_ZERO) {
199 write!(
200 $w,
201 concat!("{:0width$", $ty, "}"),
202 $data,
203 width = $width as usize
204 )
205 } else {
206 write!(
207 $w,
208 concat!("{:width$", $ty, "}"),
209 $data,
210 width = $width as usize
211 )
212 }
213 }};
214}
215
216pub fn fmt_write(w: &mut impl fmt::Write) -> impl FnMut(Argument) -> c_int + '_ {
233 use fmt::Write;
234 move |Argument {
235 flags,
236 mut width,
237 precision,
238 specifier,
239 }| {
240 let mut w = WriteCounter(w, 0);
241 let w = &mut w;
242 let res = match specifier {
243 Specifier::Percent => w.write_char('%'),
244 Specifier::Bytes(data) => write_str(w, flags, width, precision, data),
245 Specifier::String(data) => write_str(w, flags, width, precision, data.to_bytes()),
246 Specifier::Hex(data) => {
247 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "x")
248 }
249 Specifier::UpperHex(data) => {
250 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "X")
251 }
252 Specifier::Octal(data) => {
253 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "o")
254 }
255 Specifier::Uint(data) => {
256 define_unumeric!(w, data, flags, width, precision.unwrap_or(0))
257 }
258 Specifier::Int(data) => define_numeric!(w, data, flags, width, precision.unwrap_or(0)),
259 Specifier::Double { value, format } => match format {
260 DoubleFormat::Normal
261 | DoubleFormat::UpperNormal
262 | DoubleFormat::Auto
263 | DoubleFormat::UpperAuto
264 | DoubleFormat::Hex
265 | DoubleFormat::UpperHex => {
266 define_numeric!(w, value, flags, width, precision.unwrap_or(6))
267 }
268 DoubleFormat::Scientific => {
269 define_numeric!(w, value, flags, width, precision.unwrap_or(6), "e")
270 }
271 DoubleFormat::UpperScientific => {
272 define_numeric!(w, value, flags, width, precision.unwrap_or(6), "E")
273 }
274 },
275 Specifier::Char(data) => {
276 if flags.contains(Flags::LEFT_ALIGN) {
277 write!(w, "{:width$}", data as u8 as char, width = width as usize)
278 } else {
279 write!(w, "{:>width$}", data as u8 as char, width = width as usize)
280 }
281 }
282 Specifier::Pointer(data) => {
283 if flags.contains(Flags::LEFT_ALIGN) {
284 write!(w, "{:<width$p}", data, width = width as usize)
285 } else if flags.contains(Flags::PREPEND_ZERO) {
286 write!(w, "{:0width$p}", data, width = width as usize)
287 } else {
288 write!(w, "{:width$p}", data, width = width as usize)
289 }
290 }
291 Specifier::WriteBytesWritten(_, _) => Err(Default::default()),
292 };
293 match res {
294 Ok(_) => w.1 as c_int,
295 Err(_) => -1,
296 }
297 }
298}
299
300pub unsafe fn display<'a>(format: *const c_char, va_list: VaList<'a>) -> VaListDisplay<'a> {
310 VaListDisplay {
311 format,
312 va_list,
313 written: Cell::new(0),
314 }
315}
316
317pub struct VaListDisplay<'a> {
334 format: *const c_char,
335 va_list: VaList<'a>,
336 written: Cell<c_int>,
337}
338
339impl VaListDisplay<'_> {
340 pub fn bytes_written(&self) -> c_int {
342 self.written.get()
343 }
344}
345
346impl<'a> fmt::Display for VaListDisplay<'a> {
347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348 unsafe {
349 let bytes = crate::format(self.format, self.va_list.clone(), fmt_write(f));
350 self.written.set(bytes);
351 if bytes < 0 { Err(fmt::Error) } else { Ok(()) }
352 }
353 }
354}
355
356#[cfg(feature = "std")]
357mod yes_std {
358 use std::io;
359
360 use super::*;
361
362 struct FmtWriter<T: io::Write>(T, io::Result<()>);
363
364 impl<T: io::Write> fmt::Write for FmtWriter<T> {
365 fn write_str(&mut self, s: &str) -> fmt::Result {
366 match self.0.write_all(s.as_bytes()) {
367 Ok(()) => Ok(()),
368 Err(e) => {
369 self.1 = Err(e);
370 Err(fmt::Error)
371 }
372 }
373 }
374 }
375
376 struct IoWriteCounter<'a, T: io::Write>(&'a mut T, usize);
377
378 impl<'a, T: io::Write> io::Write for IoWriteCounter<'a, T> {
379 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
380 self.0.write_all(buf)?;
381 self.1 += buf.len();
382 Ok(buf.len())
383 }
384
385 fn flush(&mut self) -> io::Result<()> {
386 self.0.flush()
387 }
388 }
389
390 fn write_bytes(
391 w: &mut impl io::Write,
392 flags: Flags,
393 width: c_int,
394 precision: Option<c_int>,
395 b: &[u8],
396 ) -> io::Result<()> {
397 let precision = precision.unwrap_or(b.len() as c_int);
398 let b = b.get(..(b.len().min(precision as usize))).unwrap_or(&[]);
399
400 if flags.contains(Flags::LEFT_ALIGN) {
401 w.write_all(b)?;
402 for _ in 0..((width as usize).saturating_sub(b.len())) {
403 w.write_all(b" ")?;
404 }
405 Ok(())
406 } else {
407 for _ in 0..((width as usize).saturating_sub(b.len())) {
408 w.write_all(b" ")?;
409 }
410 w.write_all(b)
411 }
412 }
413
414 pub fn io_write(w: &mut impl io::Write) -> impl FnMut(Argument) -> c_int + '_ {
419 use io::Write;
420 move |Argument {
421 flags,
422 width,
423 precision,
424 specifier,
425 }| {
426 let mut w = IoWriteCounter(w, 0);
427 let mut w = &mut w;
428 let res = match specifier {
429 Specifier::Percent => w.write_all(b"%"),
430 Specifier::Bytes(data) => write_bytes(w, flags, width, precision, data),
431 Specifier::String(data) => write_bytes(w, flags, width, precision, data.to_bytes()),
432 _ => {
433 let mut writer = FmtWriter(&mut w, Ok(()));
434 fmt_write(&mut writer)(Argument {
435 flags,
436 width,
437 precision,
438 specifier,
439 });
440 writer.1
441 }
442 };
443 match res {
444 Ok(_) => w.1 as c_int,
445 Err(_) => -1,
446 }
447 }
448 }
449}