diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-07-16 02:41:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:52 -0400 |
commit | b39a734097d5095d63eb9c709a6aaf965633bb01 (patch) | |
tree | 78afaae4c1229163279b902bacf99445dc16e767 /lib/vsprintf.c | |
parent | 4b4e5a1411c8b970983fb6022db1da31c4f5c301 (diff) |
vsprintf.c: optimizing, part 1 (easy and obvious stuff)
* There is no point in having full "0...9a...z" constant vector,
if we use only "0...9a...f" (and "x" for "0x").
* Post-decrement usually needs a few more instructions, so use
pre decrement instead where makes sense:
- while (i < precision--) {
+ while (i <= --precision) {
* if base != 10 (=> base 8 or 16), we can avoid using division
in a loop and use mask/shift, obtaining much faster conversion.
(More complex optimization for base 10 case is in the second patch).
Overall, size vsprintf.o shows ~80 bytes smaller text section
with this patch applied.
Signed-off-by: Douglas W Jones <jones@cs.uiowa.edu>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib/vsprintf.c')
-rw-r--r-- | lib/vsprintf.c | 73 |
1 files changed, 45 insertions, 28 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 017290241261..e94b4bd25bc5 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c | |||
@@ -143,12 +143,14 @@ static int skip_atoi(const char **s) | |||
143 | #define SPECIAL 32 /* 0x */ | 143 | #define SPECIAL 32 /* 0x */ |
144 | #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ | 144 | #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ |
145 | 145 | ||
146 | static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) | 146 | static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type) |
147 | { | 147 | { |
148 | char c,sign,tmp[66]; | 148 | char sign,tmp[66]; |
149 | const char *digits; | 149 | const char *digits; |
150 | static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; | 150 | /* we are called with base 8, 10 or 16, only, thus don't need "g..." */ |
151 | static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | 151 | static const char small_digits[] = "0123456789abcdefx"; /* "ghijklmnopqrstuvwxyz"; */ |
152 | static const char large_digits[] = "0123456789ABCDEFX"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ | ||
153 | int need_pfx = ((type & SPECIAL) && base != 10); | ||
152 | int i; | 154 | int i; |
153 | 155 | ||
154 | digits = (type & LARGE) ? large_digits : small_digits; | 156 | digits = (type & LARGE) ? large_digits : small_digits; |
@@ -156,7 +158,6 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i | |||
156 | type &= ~ZEROPAD; | 158 | type &= ~ZEROPAD; |
157 | if (base < 2 || base > 36) | 159 | if (base < 2 || base > 36) |
158 | return NULL; | 160 | return NULL; |
159 | c = (type & ZEROPAD) ? '0' : ' '; | ||
160 | sign = 0; | 161 | sign = 0; |
161 | if (type & SIGN) { | 162 | if (type & SIGN) { |
162 | if ((signed long long) num < 0) { | 163 | if ((signed long long) num < 0) { |
@@ -171,64 +172,80 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i | |||
171 | size--; | 172 | size--; |
172 | } | 173 | } |
173 | } | 174 | } |
174 | if (type & SPECIAL) { | 175 | if (need_pfx) { |
176 | size--; | ||
175 | if (base == 16) | 177 | if (base == 16) |
176 | size -= 2; | ||
177 | else if (base == 8) | ||
178 | size--; | 178 | size--; |
179 | } | 179 | } |
180 | |||
181 | /* generate full string in tmp[], in reverse order */ | ||
180 | i = 0; | 182 | i = 0; |
181 | if (num == 0) | 183 | if (num == 0) |
182 | tmp[i++]='0'; | 184 | tmp[i++] = '0'; |
183 | else while (num != 0) | 185 | else if (base != 10) { /* 8 or 16 */ |
184 | tmp[i++] = digits[do_div(num,base)]; | 186 | int mask = base - 1; |
187 | int shift = 3; | ||
188 | if (base == 16) shift = 4; | ||
189 | do { | ||
190 | tmp[i++] = digits[((unsigned char)num) & mask]; | ||
191 | num >>= shift; | ||
192 | } while (num); | ||
193 | } else do { /* generic code, works for any base */ | ||
194 | tmp[i++] = digits[do_div(num,10 /*base*/)]; | ||
195 | } while (num); | ||
196 | |||
197 | /* printing 100 using %2d gives "100", not "00" */ | ||
185 | if (i > precision) | 198 | if (i > precision) |
186 | precision = i; | 199 | precision = i; |
200 | /* leading space padding */ | ||
187 | size -= precision; | 201 | size -= precision; |
188 | if (!(type&(ZEROPAD+LEFT))) { | 202 | if (!(type & (ZEROPAD+LEFT))) { |
189 | while(size-->0) { | 203 | while(--size >= 0) { |
190 | if (buf < end) | 204 | if (buf < end) |
191 | *buf = ' '; | 205 | *buf = ' '; |
192 | ++buf; | 206 | ++buf; |
193 | } | 207 | } |
194 | } | 208 | } |
209 | /* sign */ | ||
195 | if (sign) { | 210 | if (sign) { |
196 | if (buf < end) | 211 | if (buf < end) |
197 | *buf = sign; | 212 | *buf = sign; |
198 | ++buf; | 213 | ++buf; |
199 | } | 214 | } |
200 | if (type & SPECIAL) { | 215 | /* "0x" / "0" prefix */ |
201 | if (base==8) { | 216 | if (need_pfx) { |
202 | if (buf < end) | 217 | if (buf < end) |
203 | *buf = '0'; | 218 | *buf = '0'; |
204 | ++buf; | 219 | ++buf; |
205 | } else if (base==16) { | 220 | if (base == 16) { |
206 | if (buf < end) | ||
207 | *buf = '0'; | ||
208 | ++buf; | ||
209 | if (buf < end) | 221 | if (buf < end) |
210 | *buf = digits[33]; | 222 | *buf = digits[16]; /* for arbitrary base: digits[33]; */ |
211 | ++buf; | 223 | ++buf; |
212 | } | 224 | } |
213 | } | 225 | } |
226 | /* zero or space padding */ | ||
214 | if (!(type & LEFT)) { | 227 | if (!(type & LEFT)) { |
215 | while (size-- > 0) { | 228 | char c = (type & ZEROPAD) ? '0' : ' '; |
229 | while (--size >= 0) { | ||
216 | if (buf < end) | 230 | if (buf < end) |
217 | *buf = c; | 231 | *buf = c; |
218 | ++buf; | 232 | ++buf; |
219 | } | 233 | } |
220 | } | 234 | } |
221 | while (i < precision--) { | 235 | /* hmm even more zero padding? */ |
236 | while (i <= --precision) { | ||
222 | if (buf < end) | 237 | if (buf < end) |
223 | *buf = '0'; | 238 | *buf = '0'; |
224 | ++buf; | 239 | ++buf; |
225 | } | 240 | } |
226 | while (i-- > 0) { | 241 | /* actual digits of result */ |
242 | while (--i >= 0) { | ||
227 | if (buf < end) | 243 | if (buf < end) |
228 | *buf = tmp[i]; | 244 | *buf = tmp[i]; |
229 | ++buf; | 245 | ++buf; |
230 | } | 246 | } |
231 | while (size-- > 0) { | 247 | /* trailing space padding */ |
248 | while (--size >= 0) { | ||
232 | if (buf < end) | 249 | if (buf < end) |
233 | *buf = ' '; | 250 | *buf = ' '; |
234 | ++buf; | 251 | ++buf; |
@@ -276,7 +293,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) | |||
276 | used for unknown buffer sizes. */ | 293 | used for unknown buffer sizes. */ |
277 | if (unlikely((int) size < 0)) { | 294 | if (unlikely((int) size < 0)) { |
278 | /* There can be only one.. */ | 295 | /* There can be only one.. */ |
279 | static int warn = 1; | 296 | static char warn = 1; |
280 | WARN_ON(warn); | 297 | WARN_ON(warn); |
281 | warn = 0; | 298 | warn = 0; |
282 | return 0; | 299 | return 0; |