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 + '_ {
235 use fmt::Write;
236 move |Argument {
237 flags,
238 mut width,
239 precision,
240 specifier,
241 }| {
242 let mut w = WriteCounter(w, 0);
243 let w = &mut w;
244 let res = match specifier {
245 Specifier::Percent => w.write_char('%'),
246 Specifier::Bytes(data) => write_str(w, flags, width, precision, data),
247 Specifier::String(data) => write_str(w, flags, width, precision, data.to_bytes()),
248 Specifier::Hex(data) => {
249 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "x")
250 }
251 Specifier::UpperHex(data) => {
252 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "X")
253 }
254 Specifier::Octal(data) => {
255 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "o")
256 }
257 Specifier::Uint(data) => {
258 define_unumeric!(w, data, flags, width, precision.unwrap_or(0))
259 }
260 Specifier::Int(data) => define_numeric!(w, data, flags, width, precision.unwrap_or(0)),
261 Specifier::Double { value, format } => match format {
262 DoubleFormat::Normal
263 | DoubleFormat::UpperNormal
264 | DoubleFormat::Auto
265 | DoubleFormat::UpperAuto
266 | DoubleFormat::Hex
267 | DoubleFormat::UpperHex => {
268 define_numeric!(w, value, flags, width, precision.unwrap_or(6))
269 }
270 DoubleFormat::Scientific => {
271 define_numeric!(w, value, flags, width, precision.unwrap_or(6), "e")
272 }
273 DoubleFormat::UpperScientific => {
274 define_numeric!(w, value, flags, width, precision.unwrap_or(6), "E")
275 }
276 },
277 Specifier::Char(data) => {
278 if flags.contains(Flags::LEFT_ALIGN) {
279 write!(w, "{:width$}", data as u8 as char, width = width as usize)
280 } else {
281 write!(w, "{:>width$}", data as u8 as char, width = width as usize)
282 }
283 }
284 Specifier::Pointer(data) => {
285 if flags.contains(Flags::LEFT_ALIGN) {
286 write!(w, "{:<width$p}", data, width = width as usize)
287 } else if flags.contains(Flags::PREPEND_ZERO) {
288 write!(w, "{:0width$p}", data, width = width as usize)
289 } else {
290 write!(w, "{:width$p}", data, width = width as usize)
291 }
292 }
293 Specifier::WriteBytesWritten(_, _) => Err(Default::default()),
294 };
295 match res {
296 Ok(_) => w.1 as c_int,
297 Err(_) => -1,
298 }
299 }
300}
301
302pub unsafe fn display<'a>(format: *const c_char, va_list: VaList<'a>) -> VaListDisplay<'a> {
312 VaListDisplay {
313 format,
314 va_list,
315 written: Cell::new(0),
316 }
317}
318
319pub struct VaListDisplay<'a> {
336 format: *const c_char,
337 va_list: VaList<'a>,
338 written: Cell<c_int>,
339}
340
341impl VaListDisplay<'_> {
342 pub fn bytes_written(&self) -> c_int {
344 self.written.get()
345 }
346}
347
348impl<'a> fmt::Display for VaListDisplay<'a> {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 unsafe {
351 let bytes = crate::format(self.format, self.va_list.clone(), fmt_write(f));
352 self.written.set(bytes);
353 if bytes < 0 { Err(fmt::Error) } else { Ok(()) }
354 }
355 }
356}
357
358#[cfg(feature = "std")]
359mod yes_std {
360 use std::io;
361
362 use super::*;
363
364 struct FmtWriter<T: io::Write>(T, io::Result<()>);
365
366 impl<T: io::Write> fmt::Write for FmtWriter<T> {
367 fn write_str(&mut self, s: &str) -> fmt::Result {
368 match self.0.write_all(s.as_bytes()) {
369 Ok(()) => Ok(()),
370 Err(e) => {
371 self.1 = Err(e);
372 Err(fmt::Error)
373 }
374 }
375 }
376 }
377
378 struct IoWriteCounter<'a, T: io::Write>(&'a mut T, usize);
379
380 impl<'a, T: io::Write> io::Write for IoWriteCounter<'a, T> {
381 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
382 self.0.write_all(buf)?;
383 self.1 += buf.len();
384 Ok(buf.len())
385 }
386
387 fn flush(&mut self) -> io::Result<()> {
388 self.0.flush()
389 }
390 }
391
392 fn write_bytes(
393 w: &mut impl io::Write,
394 flags: Flags,
395 width: c_int,
396 precision: Option<c_int>,
397 b: &[u8],
398 ) -> io::Result<()> {
399 let precision = precision.unwrap_or(b.len() as c_int);
400 let b = b.get(..(b.len().min(precision as usize))).unwrap_or(&[]);
401
402 if flags.contains(Flags::LEFT_ALIGN) {
403 w.write_all(b)?;
404 for _ in 0..((width as usize).saturating_sub(b.len())) {
405 w.write_all(b" ")?;
406 }
407 Ok(())
408 } else {
409 for _ in 0..((width as usize).saturating_sub(b.len())) {
410 w.write_all(b" ")?;
411 }
412 w.write_all(b)
413 }
414 }
415
416 pub fn io_write(w: &mut impl io::Write) -> impl FnMut(Argument) -> c_int + '_ {
421 use io::Write;
422 move |Argument {
423 flags,
424 width,
425 precision,
426 specifier,
427 }| {
428 let mut w = IoWriteCounter(w, 0);
429 let mut w = &mut w;
430 let res = match specifier {
431 Specifier::Percent => w.write_all(b"%"),
432 Specifier::Bytes(data) => write_bytes(w, flags, width, precision, data),
433 Specifier::String(data) => write_bytes(w, flags, width, precision, data.to_bytes()),
434 _ => {
435 let mut writer = FmtWriter(&mut w, Ok(()));
436 fmt_write(&mut writer)(Argument {
437 flags,
438 width,
439 precision,
440 specifier,
441 });
442 writer.1
443 }
444 };
445 match res {
446 Ok(_) => w.1 as c_int,
447 Err(_) => -1,
448 }
449 }
450 }
451}