taproot_c_scape/printf_impl/
parser.rs1use core::ffi::*;
8
9use super::{Argument, DoubleFormat, Flags, SignedInt, Specifier, UnsignedInt};
10use crate::va::VaListTag;
11
12fn next_char(sub: &[u8]) -> &[u8] {
13 sub.get(1..).unwrap_or(&[])
14}
15
16fn parse_flags(mut sub: &[u8]) -> (Flags, &[u8]) {
18 let mut flags: Flags = Flags::empty();
19 while let Some(&ch) = sub.first() {
20 flags.insert(match ch {
21 b'-' => Flags::LEFT_ALIGN,
22 b'+' => Flags::PREPEND_PLUS,
23 b' ' => Flags::PREPEND_SPACE,
24 b'0' => Flags::PREPEND_ZERO,
25 b'\'' => Flags::THOUSANDS_GROUPING,
26 b'#' => Flags::ALTERNATE_FORM,
27 _ => break,
28 });
29 sub = next_char(sub)
30 }
31 (flags, sub)
32}
33
34unsafe fn parse_width<'a>(mut sub: &'a [u8], args: &mut VaListTag) -> (c_int, &'a [u8]) {
36 let mut width: c_int = 0;
37 if sub.first() == Some(&b'*') {
38 return (unsafe { args.arg() }, next_char(sub));
39 }
40 while let Some(&ch) = sub.first() {
41 match ch {
42 b'0'..=b'9' => width = width * 10 + (ch & 0x0f) as c_int,
44 _ => break,
45 }
46 sub = next_char(sub);
47 }
48 (width, sub)
49}
50
51unsafe fn parse_precision<'a>(sub: &'a [u8], args: &mut VaListTag) -> (Option<c_int>, &'a [u8]) {
53 match sub.first() {
54 Some(&b'.') => {
55 let (prec, sub) = unsafe { parse_width(next_char(sub), args) };
56 (Some(prec), sub)
57 }
58 _ => (None, sub),
59 }
60}
61
62#[derive(Debug, Copy, Clone)]
63enum Length {
64 Int,
65 Char,
67 Short,
69 Long,
71 LongLong,
73 Usize,
75 Isize,
77}
78
79impl Length {
80 unsafe fn parse_signed(self, args: &mut VaListTag) -> SignedInt {
81 match self {
82 Length::Int => SignedInt::Int(unsafe { args.arg() }),
83 Length::Char => SignedInt::Char(unsafe { args.arg::<c_int>() } as c_schar),
84 Length::Short => SignedInt::Short(unsafe { args.arg::<c_int>() } as c_short),
85 Length::Long => SignedInt::Long(unsafe { args.arg() }),
86 Length::LongLong => SignedInt::LongLong(unsafe { args.arg() }),
87 Length::Usize | Length::Isize => SignedInt::Isize(unsafe { args.arg() }),
89 }
90 }
91 unsafe fn parse_unsigned(self, args: &mut VaListTag) -> UnsignedInt {
92 match self {
93 Length::Int => UnsignedInt::Int(unsafe { args.arg() }),
94 Length::Char => UnsignedInt::Char(unsafe { args.arg::<c_uint>() } as c_uchar),
95 Length::Short => UnsignedInt::Short(unsafe { args.arg::<c_uint>() } as c_ushort),
96 Length::Long => UnsignedInt::Long(unsafe { args.arg() }),
97 Length::LongLong => UnsignedInt::LongLong(unsafe { args.arg() }),
98 Length::Usize | Length::Isize => UnsignedInt::Isize(unsafe { args.arg() }),
100 }
101 }
102}
103
104fn parse_length(sub: &[u8]) -> (Length, &[u8]) {
106 match sub.first().copied() {
107 Some(b'h') => match sub.get(1).copied() {
108 Some(b'h') => (Length::Char, sub.get(2..).unwrap_or(&[])),
109 _ => (Length::Short, next_char(sub)),
110 },
111 Some(b'l') => match sub.get(1).copied() {
112 Some(b'l') => (Length::LongLong, sub.get(2..).unwrap_or(&[])),
113 _ => (Length::Long, next_char(sub)),
114 },
115 Some(b'z') => (Length::Usize, next_char(sub)),
116 Some(b't') => (Length::Isize, next_char(sub)),
117 _ => (Length::Int, sub),
118 }
119}
120
121pub unsafe fn format(
129 format: *const c_char,
130 args: &mut VaListTag,
131 mut handler: impl FnMut(Argument) -> c_int,
132) -> c_int {
133 let str = unsafe { CStr::from_ptr(format).to_bytes() };
134 let mut iter = str.split(|&c| c == b'%').peekable();
138 let mut written = 0;
139
140 macro_rules! err {
141 ($ex: expr) => {{
142 let res = $ex;
143 if res < 0 {
144 return -1;
145 } else {
146 written += res;
147 }
148 }};
149 }
150 if let Some(begin) = iter.next() {
151 err!(handler(Specifier::Bytes(begin).into()));
152 }
153 let mut last_was_percent = false;
154 while let Some(sub) = iter.next() {
155 let has_next = iter.peek().is_some();
156 if last_was_percent {
157 err!(handler(Specifier::Bytes(sub).into()));
158 last_was_percent = false;
159 continue;
160 }
161 let (flags, sub) = parse_flags(sub);
162 let (width, sub) = unsafe { parse_width(sub, args) };
163 let (precision, sub) = unsafe { parse_precision(sub, args) };
164 let (length, sub) = parse_length(sub);
165 let ch = sub.first().unwrap_or(if has_next { &b'%' } else { &0 });
166 err!(handler(Argument {
167 flags,
168 width,
169 precision,
170 specifier: match ch {
171 b'%' => {
172 last_was_percent = true;
173 Specifier::Percent
174 }
175 b'd' | b'i' => Specifier::Int(unsafe { length.parse_signed(args) }),
176 b'x' => Specifier::Hex(unsafe { length.parse_unsigned(args) }),
177 b'X' => Specifier::UpperHex(unsafe { length.parse_unsigned(args) }),
178 b'u' => Specifier::Uint(unsafe { length.parse_unsigned(args) }),
179 b'o' => Specifier::Octal(unsafe { length.parse_unsigned(args) }),
180 b'f' | b'F' => Specifier::Double {
181 value: unsafe { args.arg() },
182 format: DoubleFormat::Normal.set_upper(ch.is_ascii_uppercase()),
183 },
184 b'e' | b'E' => Specifier::Double {
185 value: unsafe { args.arg() },
186 format: DoubleFormat::Scientific.set_upper(ch.is_ascii_uppercase()),
187 },
188 b'g' | b'G' => Specifier::Double {
189 value: unsafe { args.arg() },
190 format: DoubleFormat::Auto.set_upper(ch.is_ascii_uppercase()),
191 },
192 b'a' | b'A' => Specifier::Double {
193 value: unsafe { args.arg() },
194 format: DoubleFormat::Hex.set_upper(ch.is_ascii_uppercase()),
195 },
196 b's' => {
197 let arg: *mut c_char = unsafe { args.arg() };
198 if arg.is_null() {
201 Specifier::Bytes(b"(null)")
202 } else {
203 Specifier::String(unsafe { CStr::from_ptr(arg) })
204 }
205 }
206 b'c' => {
207 trait CharToInt {
208 type IntType;
209 }
210
211 impl CharToInt for c_schar {
212 type IntType = c_int;
213 }
214
215 impl CharToInt for c_uchar {
216 type IntType = c_uint;
217 }
218
219 Specifier::Char(
220 unsafe { args.arg::<<c_char as CharToInt>::IntType>() } as c_char
221 )
222 }
223 b'p' => Specifier::Pointer(unsafe { args.arg() }),
224 b'n' => Specifier::WriteBytesWritten(written, unsafe { args.arg() }),
225 _ => return -1,
226 },
227 }));
228 err!(handler(Specifier::Bytes(next_char(sub)).into()));
229 }
230 written
231}