1use core::ffi::*;
9use core::fmt;
10use core::str::from_utf8;
11
12use super::{Argument, DoubleFormat, Flags, Specifier};
13
14struct DummyWriter(usize);
15
16impl fmt::Write for DummyWriter {
17 fn write_str(&mut self, s: &str) -> fmt::Result {
18 self.0 += s.len();
19 Ok(())
20 }
21}
22
23struct WriteCounter<'a, T: fmt::Write>(&'a mut T, usize);
24
25impl<'a, T: fmt::Write> fmt::Write for WriteCounter<'a, T> {
26 fn write_str(&mut self, s: &str) -> fmt::Result {
27 self.1 += s.len();
28 self.0.write_str(s)
29 }
30}
31
32fn write_str(
33 w: &mut impl fmt::Write,
34 flags: Flags,
35 width: c_int,
36 precision: Option<c_int>,
37 b: &[u8],
38) -> fmt::Result {
39 let string = from_utf8(b).map_err(|_| fmt::Error)?;
40 let precision = precision.unwrap_or(string.len() as c_int);
41 if flags.contains(Flags::LEFT_ALIGN) {
42 write!(
43 w,
44 "{:1$.prec$}",
45 string,
46 width as usize,
47 prec = precision as usize
48 )
49 } else {
50 write!(
51 w,
52 "{:>1$.prec$}",
53 string,
54 width as usize,
55 prec = precision as usize
56 )
57 }
58}
59
60macro_rules! define_numeric {
61 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr) => {
62 define_numeric!($w, $data, $flags, $width, $precision, "")
63 };
64 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr, $ty:expr) => {{
65 use fmt::Write;
66 if $flags.contains(Flags::LEFT_ALIGN) {
67 if $flags.contains(Flags::PREPEND_PLUS) {
68 write!(
69 $w,
70 concat!("{:<+width$.prec$", $ty, "}"),
71 $data,
72 width = $width as usize,
73 prec = $precision as usize
74 )
75 } else if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
76 write!(
77 $w,
78 concat!(" {:<width$.prec$", $ty, "}"),
79 $data,
80 width = ($width as usize).wrapping_sub(1),
81 prec = $precision as usize
82 )
83 } else {
84 write!(
85 $w,
86 concat!("{:<width$.prec$", $ty, "}"),
87 $data,
88 width = $width as usize,
89 prec = $precision as usize
90 )
91 }
92 } else if $flags.contains(Flags::PREPEND_PLUS) {
93 if $flags.contains(Flags::PREPEND_ZERO) {
94 write!(
95 $w,
96 concat!("{:+0width$.prec$", $ty, "}"),
97 $data,
98 width = $width as usize,
99 prec = $precision as usize
100 )
101 } else {
102 write!(
103 $w,
104 concat!("{:+width$.prec$", $ty, "}"),
105 $data,
106 width = $width as usize,
107 prec = $precision as usize
108 )
109 }
110 } else if $flags.contains(Flags::PREPEND_ZERO) {
111 if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
112 let mut d = DummyWriter(0);
113 let _ = write!(
114 d,
115 concat!("{:.prec$", $ty, "}"),
116 $data,
117 prec = $precision as usize
118 );
119 if d.0 + 1 > $width as usize {
120 $width += 1;
121 }
122 write!(
123 $w,
124 concat!(" {:0width$.prec$", $ty, "}"),
125 $data,
126 width = ($width as usize).wrapping_sub(1),
127 prec = $precision as usize
128 )
129 } else {
130 write!(
131 $w,
132 concat!("{:0width$.prec$", $ty, "}"),
133 $data,
134 width = $width as usize,
135 prec = $precision as usize
136 )
137 }
138 } else {
139 if $flags.contains(Flags::PREPEND_SPACE) && !$data.is_sign_negative() {
140 let mut d = DummyWriter(0);
141 let _ = write!(
142 d,
143 concat!("{:.prec$", $ty, "}"),
144 $data,
145 prec = $precision as usize
146 );
147 if d.0 + 1 > $width as usize {
148 $width = d.0 as i32 + 1;
149 }
150 }
151 write!(
152 $w,
153 concat!("{:width$.prec$", $ty, "}"),
154 $data,
155 width = $width as usize,
156 prec = $precision as usize
157 )
158 }
159 }};
160}
161
162macro_rules! define_unumeric {
163 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr) => {
164 define_unumeric!($w, $data, $flags, $width, $precision, "")
165 };
166 ($w: expr, $data: expr, $flags: expr, $width: expr, $precision: expr, $ty:expr) => {{
167 if $flags.contains(Flags::LEFT_ALIGN) {
168 if $flags.contains(Flags::ALTERNATE_FORM) {
169 write!(
170 $w,
171 concat!("{:<#width$", $ty, "}"),
172 $data,
173 width = $width as usize
174 )
175 } else {
176 write!(
177 $w,
178 concat!("{:<width$", $ty, "}"),
179 $data,
180 width = $width as usize
181 )
182 }
183 } else if $flags.contains(Flags::ALTERNATE_FORM) {
184 if $flags.contains(Flags::PREPEND_ZERO) {
185 write!(
186 $w,
187 concat!("{:#0width$", $ty, "}"),
188 $data,
189 width = $width as usize
190 )
191 } else {
192 write!(
193 $w,
194 concat!("{:#width$", $ty, "}"),
195 $data,
196 width = $width as usize
197 )
198 }
199 } else if $flags.contains(Flags::PREPEND_ZERO) {
200 write!(
201 $w,
202 concat!("{:0width$", $ty, "}"),
203 $data,
204 width = $width as usize
205 )
206 } else {
207 write!(
208 $w,
209 concat!("{:width$", $ty, "}"),
210 $data,
211 width = $width as usize
212 )
213 }
214 }};
215}
216
217pub fn fmt_write(w: &mut impl fmt::Write) -> impl FnMut(Argument) -> c_int + '_ {
236 use fmt::Write;
237 move |Argument {
238 flags,
239 mut width,
240 precision,
241 specifier,
242 }| {
243 let mut w = WriteCounter(w, 0);
244 let w = &mut w;
245 let res = match specifier {
246 Specifier::Percent => w.write_char('%'),
247 Specifier::Bytes(data) => write_str(w, flags, width, precision, data),
248 Specifier::String(data) => write_str(w, flags, width, precision, data.to_bytes()),
249 Specifier::Hex(data) => {
250 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "x")
251 }
252 Specifier::UpperHex(data) => {
253 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "X")
254 }
255 Specifier::Octal(data) => {
256 define_unumeric!(w, data, flags, width, precision.unwrap_or(0), "o")
257 }
258 Specifier::Uint(data) => {
259 define_unumeric!(w, data, flags, width, precision.unwrap_or(0))
260 }
261 Specifier::Int(data) => define_numeric!(w, data, flags, width, precision.unwrap_or(0)),
262 Specifier::Double { value, format } => match format {
263 DoubleFormat::Normal
264 | DoubleFormat::UpperNormal
265 | DoubleFormat::Auto
266 | DoubleFormat::UpperAuto
267 | DoubleFormat::Hex
268 | DoubleFormat::UpperHex => {
269 define_numeric!(w, value, flags, width, precision.unwrap_or(6))
270 }
271 DoubleFormat::Scientific => {
272 define_numeric!(w, value, flags, width, precision.unwrap_or(6), "e")
273 }
274 DoubleFormat::UpperScientific => {
275 define_numeric!(w, value, flags, width, precision.unwrap_or(6), "E")
276 }
277 },
278 Specifier::Char(data) => {
279 if flags.contains(Flags::LEFT_ALIGN) {
280 write!(w, "{:width$}", data as u8 as char, width = width as usize)
281 } else {
282 write!(w, "{:>width$}", data as u8 as char, width = width as usize)
283 }
284 }
285 Specifier::Pointer(data) => {
286 if flags.contains(Flags::LEFT_ALIGN) {
287 write!(w, "{:<width$p}", data, width = width as usize)
288 } else if flags.contains(Flags::PREPEND_ZERO) {
289 write!(w, "{:0width$p}", data, width = width as usize)
290 } else {
291 write!(w, "{:width$p}", data, width = width as usize)
292 }
293 }
294 Specifier::WriteBytesWritten(_, _) => Err(Default::default()),
295 };
296 match res {
297 Ok(_) => w.1 as c_int,
298 Err(_) => -1,
299 }
300 }
301}