diff options
Diffstat (limited to 'lib/vsprintf.c')
-rw-r--r-- | lib/vsprintf.c | 173 |
1 files changed, 146 insertions, 27 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 017290241261..6b6734df6d2d 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c | |||
@@ -135,6 +135,103 @@ static int skip_atoi(const char **s) | |||
135 | return i; | 135 | return i; |
136 | } | 136 | } |
137 | 137 | ||
138 | /* Decimal conversion is by far the most typical, and is used | ||
139 | * for /proc and /sys data. This directly impacts e.g. top performance | ||
140 | * with many processes running. We optimize it for speed | ||
141 | * using code from | ||
142 | * http://www.cs.uiowa.edu/~jones/bcd/decimal.html | ||
143 | * (with permission from the author, Douglas W. Jones). */ | ||
144 | |||
145 | /* Formats correctly any integer in [0,99999]. | ||
146 | * Outputs from one to five digits depending on input. | ||
147 | * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */ | ||
148 | static char* put_dec_trunc(char *buf, unsigned q) | ||
149 | { | ||
150 | unsigned d3, d2, d1, d0; | ||
151 | d1 = (q>>4) & 0xf; | ||
152 | d2 = (q>>8) & 0xf; | ||
153 | d3 = (q>>12); | ||
154 | |||
155 | d0 = 6*(d3 + d2 + d1) + (q & 0xf); | ||
156 | q = (d0 * 0xcd) >> 11; | ||
157 | d0 = d0 - 10*q; | ||
158 | *buf++ = d0 + '0'; /* least significant digit */ | ||
159 | d1 = q + 9*d3 + 5*d2 + d1; | ||
160 | if (d1 != 0) { | ||
161 | q = (d1 * 0xcd) >> 11; | ||
162 | d1 = d1 - 10*q; | ||
163 | *buf++ = d1 + '0'; /* next digit */ | ||
164 | |||
165 | d2 = q + 2*d2; | ||
166 | if ((d2 != 0) || (d3 != 0)) { | ||
167 | q = (d2 * 0xd) >> 7; | ||
168 | d2 = d2 - 10*q; | ||
169 | *buf++ = d2 + '0'; /* next digit */ | ||
170 | |||
171 | d3 = q + 4*d3; | ||
172 | if (d3 != 0) { | ||
173 | q = (d3 * 0xcd) >> 11; | ||
174 | d3 = d3 - 10*q; | ||
175 | *buf++ = d3 + '0'; /* next digit */ | ||
176 | if (q != 0) | ||
177 | *buf++ = q + '0'; /* most sign. digit */ | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | return buf; | ||
182 | } | ||
183 | /* Same with if's removed. Always emits five digits */ | ||
184 | static char* put_dec_full(char *buf, unsigned q) | ||
185 | { | ||
186 | /* BTW, if q is in [0,9999], 8-bit ints will be enough, */ | ||
187 | /* but anyway, gcc produces better code with full-sized ints */ | ||
188 | unsigned d3, d2, d1, d0; | ||
189 | d1 = (q>>4) & 0xf; | ||
190 | d2 = (q>>8) & 0xf; | ||
191 | d3 = (q>>12); | ||
192 | |||
193 | /* Possible ways to approx. divide by 10 */ | ||
194 | /* gcc -O2 replaces multiply with shifts and adds */ | ||
195 | // (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386) | ||
196 | // (x * 0x67) >> 10: 1100111 | ||
197 | // (x * 0x34) >> 9: 110100 - same | ||
198 | // (x * 0x1a) >> 8: 11010 - same | ||
199 | // (x * 0x0d) >> 7: 1101 - same, shortest code (on i386) | ||
200 | |||
201 | d0 = 6*(d3 + d2 + d1) + (q & 0xf); | ||
202 | q = (d0 * 0xcd) >> 11; | ||
203 | d0 = d0 - 10*q; | ||
204 | *buf++ = d0 + '0'; | ||
205 | d1 = q + 9*d3 + 5*d2 + d1; | ||
206 | q = (d1 * 0xcd) >> 11; | ||
207 | d1 = d1 - 10*q; | ||
208 | *buf++ = d1 + '0'; | ||
209 | |||
210 | d2 = q + 2*d2; | ||
211 | q = (d2 * 0xd) >> 7; | ||
212 | d2 = d2 - 10*q; | ||
213 | *buf++ = d2 + '0'; | ||
214 | |||
215 | d3 = q + 4*d3; | ||
216 | q = (d3 * 0xcd) >> 11; /* - shorter code */ | ||
217 | /* q = (d3 * 0x67) >> 10; - would also work */ | ||
218 | d3 = d3 - 10*q; | ||
219 | *buf++ = d3 + '0'; | ||
220 | *buf++ = q + '0'; | ||
221 | return buf; | ||
222 | } | ||
223 | /* No inlining helps gcc to use registers better */ | ||
224 | static noinline char* put_dec(char *buf, unsigned long long num) | ||
225 | { | ||
226 | while (1) { | ||
227 | unsigned rem; | ||
228 | if (num < 100000) | ||
229 | return put_dec_trunc(buf, num); | ||
230 | rem = do_div(num, 100000); | ||
231 | buf = put_dec_full(buf, rem); | ||
232 | } | ||
233 | } | ||
234 | |||
138 | #define ZEROPAD 1 /* pad with zero */ | 235 | #define ZEROPAD 1 /* pad with zero */ |
139 | #define SIGN 2 /* unsigned/signed long */ | 236 | #define SIGN 2 /* unsigned/signed long */ |
140 | #define PLUS 4 /* show plus */ | 237 | #define PLUS 4 /* show plus */ |
@@ -143,12 +240,14 @@ static int skip_atoi(const char **s) | |||
143 | #define SPECIAL 32 /* 0x */ | 240 | #define SPECIAL 32 /* 0x */ |
144 | #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ | 241 | #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ |
145 | 242 | ||
146 | static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) | 243 | static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type) |
147 | { | 244 | { |
148 | char c,sign,tmp[66]; | 245 | char sign,tmp[66]; |
149 | const char *digits; | 246 | const char *digits; |
150 | static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; | 247 | /* we are called with base 8, 10 or 16, only, thus don't need "g..." */ |
151 | static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | 248 | static const char small_digits[] = "0123456789abcdefx"; /* "ghijklmnopqrstuvwxyz"; */ |
249 | static const char large_digits[] = "0123456789ABCDEFX"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ | ||
250 | int need_pfx = ((type & SPECIAL) && base != 10); | ||
152 | int i; | 251 | int i; |
153 | 252 | ||
154 | digits = (type & LARGE) ? large_digits : small_digits; | 253 | digits = (type & LARGE) ? large_digits : small_digits; |
@@ -156,7 +255,6 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i | |||
156 | type &= ~ZEROPAD; | 255 | type &= ~ZEROPAD; |
157 | if (base < 2 || base > 36) | 256 | if (base < 2 || base > 36) |
158 | return NULL; | 257 | return NULL; |
159 | c = (type & ZEROPAD) ? '0' : ' '; | ||
160 | sign = 0; | 258 | sign = 0; |
161 | if (type & SIGN) { | 259 | if (type & SIGN) { |
162 | if ((signed long long) num < 0) { | 260 | if ((signed long long) num < 0) { |
@@ -171,64 +269,85 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i | |||
171 | size--; | 269 | size--; |
172 | } | 270 | } |
173 | } | 271 | } |
174 | if (type & SPECIAL) { | 272 | if (need_pfx) { |
273 | size--; | ||
175 | if (base == 16) | 274 | if (base == 16) |
176 | size -= 2; | ||
177 | else if (base == 8) | ||
178 | size--; | 275 | size--; |
179 | } | 276 | } |
277 | |||
278 | /* generate full string in tmp[], in reverse order */ | ||
180 | i = 0; | 279 | i = 0; |
181 | if (num == 0) | 280 | if (num == 0) |
182 | tmp[i++]='0'; | 281 | tmp[i++] = '0'; |
183 | else while (num != 0) | 282 | /* Generic code, for any base: |
283 | else do { | ||
184 | tmp[i++] = digits[do_div(num,base)]; | 284 | tmp[i++] = digits[do_div(num,base)]; |
285 | } while (num != 0); | ||
286 | */ | ||
287 | else if (base != 10) { /* 8 or 16 */ | ||
288 | int mask = base - 1; | ||
289 | int shift = 3; | ||
290 | if (base == 16) shift = 4; | ||
291 | do { | ||
292 | tmp[i++] = digits[((unsigned char)num) & mask]; | ||
293 | num >>= shift; | ||
294 | } while (num); | ||
295 | } else { /* base 10 */ | ||
296 | i = put_dec(tmp, num) - tmp; | ||
297 | } | ||
298 | |||
299 | /* printing 100 using %2d gives "100", not "00" */ | ||
185 | if (i > precision) | 300 | if (i > precision) |
186 | precision = i; | 301 | precision = i; |
302 | /* leading space padding */ | ||
187 | size -= precision; | 303 | size -= precision; |
188 | if (!(type&(ZEROPAD+LEFT))) { | 304 | if (!(type & (ZEROPAD+LEFT))) { |
189 | while(size-->0) { | 305 | while(--size >= 0) { |
190 | if (buf < end) | 306 | if (buf < end) |
191 | *buf = ' '; | 307 | *buf = ' '; |
192 | ++buf; | 308 | ++buf; |
193 | } | 309 | } |
194 | } | 310 | } |
311 | /* sign */ | ||
195 | if (sign) { | 312 | if (sign) { |
196 | if (buf < end) | 313 | if (buf < end) |
197 | *buf = sign; | 314 | *buf = sign; |
198 | ++buf; | 315 | ++buf; |
199 | } | 316 | } |
200 | if (type & SPECIAL) { | 317 | /* "0x" / "0" prefix */ |
201 | if (base==8) { | 318 | if (need_pfx) { |
202 | if (buf < end) | 319 | if (buf < end) |
203 | *buf = '0'; | 320 | *buf = '0'; |
204 | ++buf; | 321 | ++buf; |
205 | } else if (base==16) { | 322 | if (base == 16) { |
206 | if (buf < end) | ||
207 | *buf = '0'; | ||
208 | ++buf; | ||
209 | if (buf < end) | 323 | if (buf < end) |
210 | *buf = digits[33]; | 324 | *buf = digits[16]; /* for arbitrary base: digits[33]; */ |
211 | ++buf; | 325 | ++buf; |
212 | } | 326 | } |
213 | } | 327 | } |
328 | /* zero or space padding */ | ||
214 | if (!(type & LEFT)) { | 329 | if (!(type & LEFT)) { |
215 | while (size-- > 0) { | 330 | char c = (type & ZEROPAD) ? '0' : ' '; |
331 | while (--size >= 0) { | ||
216 | if (buf < end) | 332 | if (buf < end) |
217 | *buf = c; | 333 | *buf = c; |
218 | ++buf; | 334 | ++buf; |
219 | } | 335 | } |
220 | } | 336 | } |
221 | while (i < precision--) { | 337 | /* hmm even more zero padding? */ |
338 | while (i <= --precision) { | ||
222 | if (buf < end) | 339 | if (buf < end) |
223 | *buf = '0'; | 340 | *buf = '0'; |
224 | ++buf; | 341 | ++buf; |
225 | } | 342 | } |
226 | while (i-- > 0) { | 343 | /* actual digits of result */ |
344 | while (--i >= 0) { | ||
227 | if (buf < end) | 345 | if (buf < end) |
228 | *buf = tmp[i]; | 346 | *buf = tmp[i]; |
229 | ++buf; | 347 | ++buf; |
230 | } | 348 | } |
231 | while (size-- > 0) { | 349 | /* trailing space padding */ |
350 | while (--size >= 0) { | ||
232 | if (buf < end) | 351 | if (buf < end) |
233 | *buf = ' '; | 352 | *buf = ' '; |
234 | ++buf; | 353 | ++buf; |
@@ -276,7 +395,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) | |||
276 | used for unknown buffer sizes. */ | 395 | used for unknown buffer sizes. */ |
277 | if (unlikely((int) size < 0)) { | 396 | if (unlikely((int) size < 0)) { |
278 | /* There can be only one.. */ | 397 | /* There can be only one.. */ |
279 | static int warn = 1; | 398 | static char warn = 1; |
280 | WARN_ON(warn); | 399 | WARN_ON(warn); |
281 | warn = 0; | 400 | warn = 0; |
282 | return 0; | 401 | return 0; |