Browse Source

Updated documentation.

Shaved around 100 bytes off vsprintf.o.  It's now 50 bytes smaller than
the old implementation and provides much more conformant semantics,
including the ability to return the number of characters that would have
been printed to the string had the buffer been big enough.  (iSCSI needs
this functionality).
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
25f5d114a0
2 changed files with 125 additions and 80 deletions
  1. 103
    45
      src/core/vsprintf.c
  2. 22
    35
      src/include/vsprintf.h

+ 103
- 45
src/core/vsprintf.c View File

@@ -1,15 +1,35 @@
1
+/*
2
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
1 19
 #include <stddef.h>
2 20
 #include <stdarg.h>
3 21
 #include <console.h>
4 22
 #include <errno.h>
5 23
 #include <vsprintf.h>
6 24
 
7
-#define CHAR_LEN	1
8
-#define SHORT_LEN	2
9
-#define INT_LEN		3
10
-#define LONG_LEN	4
11
-#define LONGLONG_LEN	5
12
-#define SIZE_T_LEN	6
25
+/** @file */
26
+
27
+#define CHAR_LEN	0	/**< "hh" length modifier */
28
+#define SHORT_LEN	1	/**< "h" length modifier */
29
+#define INT_LEN		2	/**< no length modifier */
30
+#define LONG_LEN	3	/**< "l" length modifier */
31
+#define LONGLONG_LEN	4	/**< "ll" length modifier */
32
+#define SIZE_T_LEN	5	/**< "z" length modifier */
13 33
 
14 34
 static uint8_t type_sizes[] = {
15 35
 	[CHAR_LEN]	= sizeof ( char ),
@@ -20,8 +40,6 @@ static uint8_t type_sizes[] = {
20 40
 	[SIZE_T_LEN]	= sizeof ( size_t ),
21 41
 };
22 42
 
23
-/** @file */
24
-
25 43
 /**
26 44
  * A printf context
27 45
  *
@@ -48,36 +66,80 @@ struct printf_context {
48 66
 	size_t max_len;
49 67
 };
50 68
 
69
+/**
70
+ * Use lower-case for hexadecimal digits
71
+ *
72
+ * Note that this value is set to 0x20 since that makes for very
73
+ * efficient calculations.  (Bitwise-ORing with @c LCASE converts to a
74
+ * lower-case character, for example.)
75
+ */
51 76
 #define LCASE 0x20
77
+
78
+/**
79
+ * Use "alternate form"
80
+ *
81
+ * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
82
+ * the number.
83
+ */
52 84
 #define ALT_FORM 0x02
53 85
 
54
-static char * format_hex ( char *buf, unsigned long long num, int width,
86
+/**
87
+ * Format a hexadecimal number
88
+ *
89
+ * @v end		End of buffer to contain number
90
+ * @v num		Number to format
91
+ * @v width		Minimum field width
92
+ * @ret ptr		End of buffer
93
+ *
94
+ * Fills a buffer in reverse order with a formatted hexadecimal
95
+ * number.  The number will be zero-padded to the specified width.
96
+ * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
97
+ * set.
98
+ *
99
+ * There must be enough space in the buffer to contain the largest
100
+ * number that this function can format.
101
+ */
102
+static char * format_hex ( char *end, unsigned long long num, int width,
55 103
 			   int flags ) {
56
-	char *ptr = buf;
104
+	char *ptr = end;
57 105
 	int case_mod;
58 106
 
59 107
 	/* Generate the number */
60 108
 	case_mod = flags & LCASE;
61 109
 	do {
62
-		*ptr++ = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
110
+		*(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
63 111
 		num >>= 4;
64 112
 	} while ( num );
65 113
 
66 114
 	/* Zero-pad to width */
67
-	while ( ( ptr - buf ) < width )
68
-		*ptr++ = '0';
115
+	while ( ( end - ptr ) < width )
116
+		*(--ptr) = '0';
69 117
 
70 118
 	/* Add "0x" or "0X" if alternate form specified */
71 119
 	if ( flags & ALT_FORM ) {
72
-		*ptr++ = 'X' | case_mod;
73
-		*ptr++ = '0';
120
+		*(--ptr) = 'X' | case_mod;
121
+		*(--ptr) = '0';
74 122
 	}
75 123
 
76 124
 	return ptr;
77 125
 }
78 126
 
79
-static char * format_decimal ( char *buf, signed long num, int width ) {
80
-	char *ptr = buf;
127
+/**
128
+ * Format a decimal number
129
+ *
130
+ * @v end		End of buffer to contain number
131
+ * @v num		Number to format
132
+ * @v width		Minimum field width
133
+ * @ret ptr		End of buffer
134
+ *
135
+ * Fills a buffer in reverse order with a formatted decimal number.
136
+ * The number will be space-padded to the specified width.
137
+ *
138
+ * There must be enough space in the buffer to contain the largest
139
+ * number that this function can format.
140
+ */
141
+static char * format_decimal ( char *end, signed long num, int width ) {
142
+	char *ptr = end;
81 143
 	int negative = 0;
82 144
 
83 145
 	/* Generate the number */
@@ -86,22 +148,21 @@ static char * format_decimal ( char *buf, signed long num, int width ) {
86 148
 		num = -num;
87 149
 	}
88 150
 	do {
89
-		*ptr++ = '0' + ( num % 10 );
151
+		*(--ptr) = '0' + ( num % 10 );
90 152
 		num /= 10;
91 153
 	} while ( num );
92 154
 
93 155
 	/* Add "-" if necessary */
94 156
 	if ( negative )
95
-		*ptr++ = '-';
157
+		*(--ptr) = '-';
96 158
 
97 159
 	/* Space-pad to width */
98
-	while ( ( ptr - buf ) < width )
99
-		*ptr++ = ' ';
160
+	while ( ( end - ptr ) < width )
161
+		*(--ptr) = ' ';
100 162
 
101 163
 	return ptr;
102 164
 }
103 165
 
104
-
105 166
 /**
106 167
  * Write a formatted string to a printf context
107 168
  *
@@ -114,11 +175,9 @@ int vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
114 175
 	int flags;
115 176
 	int width;
116 177
 	uint8_t *length;
117
-	int character;
118
-	unsigned long long hex;
119
-	signed long decimal;
120
-	char num_buf[32];
121 178
 	char *ptr;
179
+	char tmp_buf[32]; /* 32 is enough for all numerical formats.
180
+			   * Insane width fields could overflow this buffer. */
122 181
 
123 182
 	/* Initialise context */
124 183
 	ctx->len = 0;
@@ -166,22 +225,21 @@ int vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
166 225
 			}
167 226
 		}
168 227
 		/* Process conversion specifier */
228
+		ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
229
+		*ptr = '\0';
169 230
 		if ( *fmt == 'c' ) {
170
-			character = va_arg ( args, unsigned int );
171
-			ctx->handler ( ctx, character );
231
+			*(--ptr) = va_arg ( args, unsigned int );
172 232
 		} else if ( *fmt == 's' ) {
173 233
 			ptr = va_arg ( args, char * );
174
-			for ( ; *ptr ; ptr++ ) {
175
-				ctx->handler ( ctx, *ptr );
176
-			}
177 234
 		} else if ( *fmt == 'p' ) {
178
-			hex = ( intptr_t ) va_arg ( args, void * );
179
-			ptr = format_hex ( num_buf, hex, width,
235
+			intptr_t ptrval;
236
+
237
+			ptrval = ( intptr_t ) va_arg ( args, void * );
238
+			ptr = format_hex ( ptr, ptrval, width, 
180 239
 					   ( ALT_FORM | LCASE ) );
181
-			do {
182
-				ctx->handler ( ctx, *(--ptr) );
183
-			} while ( ptr != num_buf );
184 240
 		} else if ( ( *fmt & ~0x20 ) == 'X' ) {
241
+			unsigned long long hex;
242
+
185 243
 			flags |= ( *fmt & 0x20 ); /* LCASE */
186 244
 			if ( *length >= sizeof ( unsigned long long ) ) {
187 245
 				hex = va_arg ( args, unsigned long long );
@@ -190,22 +248,22 @@ int vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
190 248
 			} else {
191 249
 				hex = va_arg ( args, unsigned int );
192 250
 			}
193
-			ptr = format_hex ( num_buf, hex, width, flags );
194
-			do {
195
-				ctx->handler ( ctx, *(--ptr) );
196
-			} while ( ptr != num_buf );
251
+			ptr = format_hex ( ptr, hex, width, flags );
197 252
 		} else if ( *fmt == 'd' ) {
253
+			signed long decimal;
254
+
198 255
 			if ( *length >= sizeof ( signed long ) ) {
199 256
 				decimal = va_arg ( args, signed long );
200 257
 			} else {
201 258
 				decimal = va_arg ( args, signed int );
202 259
 			}
203
-			ptr = format_decimal ( num_buf, decimal, width );
204
-			do {
205
-				ctx->handler ( ctx, *(--ptr) );
206
-			} while ( ptr != num_buf );
260
+			ptr = format_decimal ( ptr, decimal, width );
207 261
 		} else {
208
-			ctx->handler ( ctx, *fmt );
262
+			*(--ptr) = *fmt;
263
+		}
264
+		/* Write out conversion result */
265
+		for ( ; *ptr ; ptr++ ) {
266
+			ctx->handler ( ctx, *ptr );
209 267
 		}
210 268
 	}
211 269
 

+ 22
- 35
src/include/vsprintf.h View File

@@ -3,44 +3,31 @@
3 3
 
4 4
 /** @file
5 5
  *
6
- * printf and friends.
6
+ * printf() and friends
7 7
  *
8
- * Etherboot's printf() functions understand the following format
9
- * specifiers:
8
+ * Etherboot's printf() functions understand the following subset of
9
+ * the standard C printf()'s format specifiers:
10 10
  *
11
- *	- Hexadecimal integers
12
- *		- @c %[#]x	- 4 bytes int (8 hex digits, lower case)
13
- *		- @c %[#]X	- 4 bytes int (8 hex digits, upper case)
14
- *		- @c %[#]lx	- 8 bytes long (16 hex digits, lower case)
15
- *		- @c %[#]lX	- 8 bytes long (16 hex digits, upper case)
16
- *		- @c %[#]hx	- 2 bytes int (4 hex digits, lower case)
17
- *		- @c %[#]hX	- 2 bytes int (4 hex digits, upper case)
18
- *		- @c %[#]hhx	- 1 byte int (2 hex digits, lower case)
19
- *		- @c %[#]hhX	- 1 byte int (2 hex digits, upper case)
20
- *		.
21
- *		If the optional # prefix is specified, the output will
22
- *		be prefixed with 0x (or 0X).
11
+ *	- Flag characters
12
+ *		- '#'		- Alternate form (i.e. "0x" prefix)
13
+ *		- '0'		- Zero-pad
14
+ *	- Field widths
15
+ *	- Length modifiers
16
+ *		- 'hh'		- Signed / unsigned char
17
+ *		- 'h'		- Signed / unsigned short
18
+ *		- 'l'		- Signed / unsigned long
19
+ *		- 'll'		- Signed / unsigned long long
20
+ *		- 'z'		- Signed / unsigned size_t
21
+ *	- Conversion specifiers
22
+ *		- 'd'		- Signed decimal
23
+ *		- 'x','X'	- Unsigned hexadecimal
24
+ *		- 'c'		- Character
25
+ *		- 's'		- String
26
+ *		- 'p'		- Pointer
23 27
  *
24
- *	- Other integers
25
- *		- @c %d		- decimal int
26
- *	.
27
- *	Note that any width specification (e.g. the @c 02 in @c %02x)
28
- *	will be accepted but ignored.
29
- *
30
- *	- Strings and characters
31
- *		- @c %c		- char
32
- *		- @c %s		- string
33
- *		- @c %m		- error message text (i.e. strerror(errno))
34
- *
35
- *	- Etherboot-specific specifiers
36
- *		- @c %@		- IP address in ddd.ddd.ddd.ddd notation
37
- *		- @c %!		- MAC address in xx:xx:xx:xx:xx:xx notation
38
- *
39
- *
40
- * @note Unfortunately, we cannot use <tt> __attribute__ (( format (
41
- * printf, ... ) )) </tt> to get automatic type checking on arguments,
42
- * because we use non-standard format characters such as @c %! and
43
- * @c %@.
28
+ * Hexadecimal numbers are always zero-padded to the specified field
29
+ * width (if any); decimal numbers are always space-padded.  Decimal
30
+ * long longs are not supported.
44 31
  *
45 32
  */
46 33
 

Loading…
Cancel
Save