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 char, width = width as usize)
278 } else {
279 write!(w, "{:>width$}", data 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, 'b>(
310 format: *const c_char,
311 va_list: VaList<'a, 'b>,
312) -> VaListDisplay<'a, 'b> {
313 VaListDisplay {
314 format,
315 va_list,
316 written: Cell::new(0),
317 }
318}
319
320pub struct VaListDisplay<'a, 'b> {
337 format: *const c_char,
338 va_list: VaList<'a, 'b>,
339 written: Cell<c_int>,
340}
341
342impl VaListDisplay<'_, '_> {
343 pub fn bytes_written(&self) -> c_int {
345 self.written.get()
346 }
347}
348
349impl<'a, 'b> fmt::Display for VaListDisplay<'a, 'b> {
350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 unsafe {
352 let bytes = crate::format(self.format, self.va_list.clone().as_va_list(), fmt_write(f));
353 self.written.set(bytes);
354 if bytes < 0 {
355 Err(fmt::Error)
356 } else {
357 Ok(())
358 }
359 }
360 }
361}
362
363#[cfg(feature = "std")]
364mod yes_std {
365 use std::io;
366
367 use super::*;
368
369 struct FmtWriter<T: io::Write>(T, io::Result<()>);
370
371 impl<T: io::Write> fmt::Write for FmtWriter<T> {
372 fn write_str(&mut self, s: &str) -> fmt::Result {
373 match self.0.write_all(s.as_bytes()) {
374 Ok(()) => Ok(()),
375 Err(e) => {
376 self.1 = Err(e);
377 Err(fmt::Error)
378 }
379 }
380 }
381 }
382
383 struct IoWriteCounter<'a, T: io::Write>(&'a mut T, usize);
384
385 impl<'a, T: io::Write> io::Write for IoWriteCounter<'a, T> {
386 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
387 self.0.write_all(buf)?;
388 self.1 += buf.len();
389 Ok(buf.len())
390 }
391
392 fn flush(&mut self) -> io::Result<()> {
393 self.0.flush()
394 }
395 }
396
397 fn write_bytes(
398 w: &mut impl io::Write,
399 flags: Flags,
400 width: c_int,
401 precision: Option<c_int>,
402 b: &[u8],
403 ) -> io::Result<()> {
404 let precision = precision.unwrap_or(b.len() as c_int);
405 let b = b.get(..(b.len().min(precision as usize))).unwrap_or(&[]);
406
407 if flags.contains(Flags::LEFT_ALIGN) {
408 w.write_all(b)?;
409 for _ in 0..((width as usize).saturating_sub(b.len())) {
410 w.write_all(b" ")?;
411 }
412 Ok(())
413 } else {
414 for _ in 0..((width as usize).saturating_sub(b.len())) {
415 w.write_all(b" ")?;
416 }
417 w.write_all(b)
418 }
419 }
420
421 pub fn io_write(w: &mut impl io::Write) -> impl FnMut(Argument) -> c_int + '_ {
426 use io::Write;
427 move |Argument {
428 flags,
429 width,
430 precision,
431 specifier,
432 }| {
433 let mut w = IoWriteCounter(w, 0);
434 let mut w = &mut w;
435 let res = match specifier {
436 Specifier::Percent => w.write_all(b"%"),
437 Specifier::Bytes(data) => write_bytes(w, flags, width, precision, data),
438 Specifier::String(data) => write_bytes(w, flags, width, precision, data.to_bytes()),
439 _ => {
440 let mut writer = FmtWriter(&mut w, Ok(()));
441 fmt_write(&mut writer)(Argument {
442 flags,
443 width,
444 precision,
445 specifier,
446 });
447 writer.1
448 }
449 };
450 match res {
451 Ok(_) => w.1 as c_int,
452 Err(_) => -1,
453 }
454 }
455 }
456}