1use core::cell::Cell;
4use core::ffi::VaList;
5use core::fmt;
6use core::str::from_utf8;
7
8use cty::*;
9
10#[cfg(feature = "std")]
11pub use yes_std::*;
12
13use crate::{Argument, DoubleFormat, Flags, Specifier};
14
15struct DummyWriter(usize);
16
17impl fmt::Write for DummyWriter {
18 fn write_str(&mut self, s: &str) -> fmt::Result {
19 self.0 += s.len();
20 Ok(())
21 }
22}
23
24struct WriteCounter<'a, T: fmt::Write>(&'a mut T, usize);
25
26impl<'a, T: fmt::Write> fmt::Write for WriteCounter<'a, T> {
27 fn write_str(&mut self, s: &str) -> fmt::Result {
28 self.1 += s.len();
29 self.0.write_str(s)
30 }
31}
32
33fn write_str(
34 w: &mut impl fmt::Write,
35 flags: Flags,
36 width: c_int,
37 precision: Option<c_int>,
38 b: &[u8],
39) -> fmt::Result {
40 let string = from_utf8(b).map_err(|_| fmt::Error)?;
41 let precision = precision.unwrap_or(string.len() as c_int);
42 if flags.contains(Flags::LEFT_ALIGN) {
43 write!(
44 w,
45 "{:1$.prec$}",
46 string,
47 width as usize,
48 prec = precision as usize
49 )
50 } else {
51 write!(
52 w,
53 "{:>1$.prec$}",
54 string,
55 width as usize,
56 prec = precision as usize
57 )
58 }
59}
60
61macro_rules! define_numeric {
62 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr) => {
63 define_numeric!($w, $data, $flags, $width, $precision, "")
64 };
65 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr, $ty:expr) => {{
66 use fmt::Write;
67 if $flags.contains(Flags::LEFT_ALIGN) {
68 if $flags.contains(Flags::PREPEND_PLUS) {
69 write!(
70 $w,
71 concat!("{:<+width$.prec$", $ty, "}"),
72 $data,
73 width = $width as usize,
74 prec = $precision as usize
75 )
76 } else if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
77 write!(
78 $w,
79 concat!(" {:<width$.prec$", $ty, "}"),
80 $data,
81 width = ($width as usize).wrapping_sub(1),
82 prec = $precision as usize
83 )
84 } else {
85 write!(
86 $w,
87 concat!("{:<width$.prec$", $ty, "}"),
88 $data,
89 width = $width as usize,
90 prec = $precision as usize
91 )
92 }
93 } else if $flags.contains(Flags::PREPEND_PLUS) {
94 if $flags.contains(Flags::PREPEND_ZERO) {
95 write!(
96 $w,
97 concat!("{:+0width$.prec$", $ty, "}"),
98 $data,
99 width = $width as usize,
100 prec = $precision as usize
101 )
102 } else {
103 write!(
104 $w,
105 concat!("{:+width$.prec$", $ty, "}"),
106 $data,
107 width = $width as usize,
108 prec = $precision as usize
109 )
110 }
111 } else if $flags.contains(Flags::PREPEND_ZERO) {
112 if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
113 let mut d = DummyWriter(0);
114 let _ = write!(
115 d,
116 concat!("{:.prec$", $ty, "}"),
117 $data,
118 prec = $precision as usize
119 );
120 if d.0 + 1 > $width as usize {
121 $width += 1;
122 }
123 write!(
124 $w,
125 concat!(" {:0width$.prec$", $ty, "}"),
126 $data,
127 width = ($width as usize).wrapping_sub(1),
128 prec = $precision as usize
129 )
130 } else {
131 write!(
132 $w,
133 concat!("{:0width$.prec$", $ty, "}"),
134 $data,
135 width = $width as usize,
136 prec = $precision as usize
137 )
138 }
139 } else {
140 if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
141 let mut d = DummyWriter(0);
142 let _ = write!(
143 d,
144 concat!("{:.prec$", $ty, "}"),
145 $data,
146 prec = $precision as usize
147 );
148 if d.0 + 1 > $width as usize {
149 $width = d.0 as i32 + 1;
150 }
151 }
152 write!(
153 $w,
154 concat!("{:width$.prec$", $ty, "}"),
155 $data,
156 width = $width as usize,
157 prec = $precision as usize
158 )
159 }
160 }};
161}
162
163macro_rules! define_unumeric {
164 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr) => {
165 define_unumeric!($w, $data, $flags, $width, $precision, "")
166 };
167 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr, $ty:expr) => {{
168 if $flags.contains(Flags::LEFT_ALIGN) {
169 if $flags.contains(Flags::ALTERNATE_FORM) {
170 write!(
171 $w,
172 concat!("{:<#width$", $ty, "}"),
173 $data,
174 width = $width as usize
175 )
176 } else {
177 write!(
178 $w,
179 concat!("{:<width$", $ty, "}"),
180 $data,
181 width = $width as usize
182 )
183 }
184 } else if $flags.contains(Flags::ALTERNATE_FORM) {
185 if $flags.contains(Flags::PREPEND_ZERO) {
186 write!(
187 $w,
188 concat!("{:#0width$", $ty, "}"),
189 $data,
190 width = $width as usize
191 )
192 } else {
193 write!(
194 $w,
195 concat!("{:#width$", $ty, "}"),
196 $data,
197 width = $width as usize
198 )
199 }
200 } else if $flags.contains(Flags::PREPEND_ZERO) {
201 write!(
202 $w,
203 concat!("{:0width$", $ty, "}"),
204 $data,
205 width = $width as usize
206 )
207 } else {
208 write!(
209 $w,
210 concat!("{:width$", $ty, "}"),
211 $data,
212 width = $width as usize
213 )
214 }
215 }};
216}
217
218pub 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 char, width = width as usize)
280 } else {
281 write!(w, "{:>width$}", data 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, 'b>(
312 format: *const c_char,
313 va_list: VaList<'a, 'b>,
314) -> VaListDisplay<'a, 'b> {
315 VaListDisplay {
316 format,
317 va_list,
318 written: Cell::new(0),
319 }
320}
321
322pub struct VaListDisplay<'a, 'b> {
342 format: *const c_char,
343 va_list: VaList<'a, 'b>,
344 written: Cell<c_int>,
345}
346
347impl VaListDisplay<'_, '_> {
348 pub fn bytes_written(&self) -> c_int {
350 self.written.get()
351 }
352}
353
354impl<'a, 'b> fmt::Display for VaListDisplay<'a, 'b> {
355 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356 unsafe {
357 let bytes = crate::format(self.format, self.va_list.clone().as_va_list(), fmt_write(f));
358 self.written.set(bytes);
359 if bytes < 0 {
360 Err(fmt::Error)
361 } else {
362 Ok(())
363 }
364 }
365 }
366}
367
368#[cfg(feature = "std")]
369mod yes_std {
370 use std::io;
371
372 use super::*;
373
374 struct FmtWriter<T: io::Write>(T, io::Result<()>);
375
376 impl<T: io::Write> fmt::Write for FmtWriter<T> {
377 fn write_str(&mut self, s: &str) -> fmt::Result {
378 match self.0.write_all(s.as_bytes()) {
379 Ok(()) => Ok(()),
380 Err(e) => {
381 self.1 = Err(e);
382 Err(fmt::Error)
383 }
384 }
385 }
386 }
387
388 struct IoWriteCounter<'a, T: io::Write>(&'a mut T, usize);
389
390 impl<'a, T: io::Write> io::Write for IoWriteCounter<'a, T> {
391 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
392 self.0.write_all(buf)?;
393 self.1 += buf.len();
394 Ok(buf.len())
395 }
396
397 fn flush(&mut self) -> io::Result<()> {
398 self.0.flush()
399 }
400 }
401
402 fn write_bytes(
403 w: &mut impl io::Write,
404 flags: Flags,
405 width: c_int,
406 precision: Option<c_int>,
407 b: &[u8],
408 ) -> io::Result<()> {
409 let precision = precision.unwrap_or(b.len() as c_int);
410 let b = b.get(..(b.len().min(precision as usize))).unwrap_or(&[]);
411
412 if flags.contains(Flags::LEFT_ALIGN) {
413 w.write_all(b)?;
414 for _ in 0..((width as usize).saturating_sub(b.len())) {
415 w.write_all(b" ")?;
416 }
417 Ok(())
418 } else {
419 for _ in 0..((width as usize).saturating_sub(b.len())) {
420 w.write_all(b" ")?;
421 }
422 w.write_all(b)
423 }
424 }
425
426 pub fn io_write(w: &mut impl io::Write) -> impl FnMut(Argument) -> c_int + '_ {
431 use io::Write;
432 move |Argument {
433 flags,
434 width,
435 precision,
436 specifier,
437 }| {
438 let mut w = IoWriteCounter(w, 0);
439 let mut w = &mut w;
440 let res = match specifier {
441 Specifier::Percent => w.write_all(b"%"),
442 Specifier::Bytes(data) => write_bytes(w, flags, width, precision, data),
443 Specifier::String(data) => write_bytes(w, flags, width, precision, data.to_bytes()),
444 _ => {
445 let mut writer = FmtWriter(&mut w, Ok(()));
446 fmt_write(&mut writer)(Argument {
447 flags,
448 width,
449 precision,
450 specifier,
451 });
452 writer.1
453 }
454 };
455 match res {
456 Ok(_) => w.1 as c_int,
457 Err(_) => -1,
458 }
459 }
460 }
461}