Name | KHR_formatted |
Name strings | KD_KHR_formatted |
Contributors | Tim Renouf, Phil Huxley |
Contacts | Khronos OpenKODE working group |
Status | Approved by OpenKODE working group February 2008 |
Version | Version 10, 2009-04-02 |
Number | 5 |
Dependencies | Requires OpenKODE Core 1.0. This extension is written based on the wording of the OpenKODE Core 1.0 specification. This extension also extends the KD_KHR_float64 extension when present, and is written based on the wording of version 6 of that extension. |
Many applications use formatted string processing for a variety of reasons: saving and restoring state, file loading, and formatting for use in output, for example in a game console window. The purpose of this extension is to reduce the time and risk of introducing errors when porting an existing game to OpenKODE, as well as providing highly useful functionality.
This extension also specifies OpenKODE equivalents of the
C89 variable argument type va_list
and macros va_start
and va_end
.
This is enough to allow an application to implement its own
printf-like function that uses va_start
and
passes the va_list
into
kdVsnprintf
.
This extension is specified such that it can be implemented using
the existing OpenKODE Core function kdFtostr
for its floating point conversion, avoiding the need for additional
floating point conversion code.
When this extension is present, its facilities are accessed by including its header file:
#include <KD/KHR_formatted.h>
Initialize a variable argument list.
This macro initializes the variable argument list
ap
so that it contains the variable
arguments of unknown number and type passed to the containing
function. parmN
is the last formal
parameter of the function, before the ...
syntax that indicates that the function accepts variable
arguments.
KD_VA_START_KHR
is a macro, which means that
it is undefined how many times each of its arguments is referenced.
After using this macro, if the containing function returns
without
KD_VA_END_KHR
being
used on ap
first, then undefined behavior
results.
Get the next argument from a variable argument list.
KD_VA_ARG_INT32_KHR(ap
) KD_VA_ARG_INT_KHR(ap
) KD_VA_ARG_INT64_KHR(ap
) KD_VA_ARG_FLOAT32_KHR(ap
) KD_VA_ARG_PTR_KHR(ap
)
Each of these macros reads the next argument from the variable
argument list ap
, updating
ap
to step it beyond that argument.
KD_VA_ARG_INT32_KHR
,
KD_VA_ARG_INT_KHR
,
KD_VA_ARG_INT64_KHR
,
KD_VA_ARG_FLOAT32_KHR
or
KD_VA_ARG_PTR_KHR
is used for an argument of type
KDint32,
KDint,
KDint64,
KDfloat32 or any pointer type, respectively.
Using the wrong macro variant for the type of the argument,
or using one of these macros when there are no more arguments
to read, results in undefined behavior.
The KD_VA_ARG_*_KHR
macros are based on C89
va_arg
. va_arg
takes the
type of the argument; this is not done in OpenKODE Core as
there is no portable way of knowing what each type is promoted to
in a C variable argument function call. This is a particular issue
with KDfloat32, which is likely to be promoted to
double, which does not have an equivalent
OpenKODE Core type.
Finish using a variable argument list.
This macro indicates that the application has finished using
the variable argument list ap
set up by
KD_VA_START_KHR
.
If ap
is not a variable argument list
that has been initialized in this function invocation with
KD_VA_START_KHR
and has not already been the subject of a
KD_VA_END_KHR
, then undefined behavior
results.
Formatted output to a buffer.
KDint kdSnprintfKHR( | KDchar * | buf, |
KDsize | bufsize, | |
const KDchar * | format, | |
...) ; |
KDint kdVsnprintfKHR( | KDchar * | buf, |
KDsize | bufsize, | |
const KDchar * | format, | |
KDVaListKHR | ap) ; |
KDint kdSprintfKHR( | KDchar * | buf, |
const KDchar * | format, | |
...) ; |
KDint kdVsprintfKHR( | KDchar * | buf, |
const KDchar * | format, | |
KDVaListKHR | ap) ; |
These functions write a null-terminated formatted string based on the
null-terminated string format
, with certain
sequences substituted by values of further arguments, into the buffer
buf
.
kdSnprintfKHR
and
kdSprintfKHR
take the arguments to be
formatted as a variable argument list.
kdVsnprintfKHR
and
kdVsprintfKHR
take the arguments as a single
KDVaListKHR value, from which the arguments are retrieved.
The function does not call KD_VA_END_KHR before returning.
The output is written as a null-terminated string
into the buffer pointed to by buf
. For
kdSnprintfKHR
and
kdVsnprintfKHR
, bufsize
is the size of the buffer, and the functions never write beyond
that size.
If buf
does not point to writable memory
long enough for the shorter of the requested output or the
supplied bufsize
(as applicable), or if
format
is not a readable null-terminated
string, then undefined behavior results.
On success, the functions return the length of the written
string without its null termination.
For kdSnprintfKHR
and
kdVsnprintfKHR
,
if the output (with null termination) would not fit
into bufsize
bytes, then the function returns what the length would have been
if the buffer had been large enough, and null terminates the
output so it just fits in the buffer.
The null-terminated format string consists of zero or more directives.
A single character other than %
is a directive
that causes that character to be written unchanged to the output.
A pair of percent signs %%
is a directive
that causes a single percent sign to be written to the output.
A percent sign %
otherwise starts a conversion
specification, ending with a conversion character. A conversion
specification causes one or more of the further arguments to be
used up, typically to convert one to string format and output it.
A percent sign followed by characters not forming a valid conversion specification causes undefined behavior.
If the conversion specifier is invalid, allowing undefined behavior, an implementation is recommended to terminate the application in some way, rather than outputting nothing or something and appearing to succeed. This reduces the danger of the application programmer accidentally relying on non-portable behavior of the format string.
A conversion specification consists, in this order, of:
the initial %
character;
zero or more flags;
optional field width;
optional precision;
optional length modifier;
conversion character.
The flags can be, in any order:
-
(minus sign) specifies that the
converted argument is left justified in its field.
+
(plus sign) specifies that a converted
signed number is always printed with a sign.
Space character specifies that if the first character of a converted signed number is not a sign, a space character is prefixed.
0
(zero character) specifies that a
converted number is padded to the field width with zero
characters. 0
is ignored if the
-
flag is also present.
For integer conversions, 0
is ignored if
a precision is also specified. For floating point conversions,
specifying both 0
and a precision causes
undefined behavior.
#
alters certain conversions: a number
converted to octal always starts with a zero digit;
a number converted to hexadecimal has 0x
or 0X
(respectively, for conversion
characters x
and X
)
prefixed; a converted floating-point number always has
a decimal point, even if there are no digits after it;
for g
and G
conversions,
trailing zeros are not removed.
When present, the field width specifies the minimum width of
the field into which the argument will be converted. The
field width is specified either as a non-negative decimal
integer, or as a *
character, which causes
the next argument (which must be a AKDint
)
to be read and used as the minimum field width. Even in the
*
case, a negative field width is taken to be
a field width of its absolute value, together with a
-
flag.
Note that a (constant) field width never starts with
0
; any 0
character
is treated as a flag rather than part of the field width.
When present, the precision specifies the maximum number of
characters to use from a string, or the minimum number of
digits in an integer conversion (leading zeros are added
to make up the minimum; an explicit precision of 0 used on a
value of 0 prints no digits at all; the default precision is 1),
or the exact number of digits after
the decimal point in a
e
, E
, f
or F
conversion, or the maximum number
of significant digits in a g
or
G
conversion.
The precision is specified as a
period character, then nothing (giving a precision of 0),
or a decimal integer, or a *
character,
causing the next argument (which must be a
AKDint
) to be read and used as the
precision. A negative precision is treated the same as no
precision specification at all.
The optional length modifier is one of:
h
specifies that an integer conversion
has a KDint16 or KDuint16
argument, or an n
conversion character
has a KDint16 * argument;
l
(one lower-case letter L) is ignored
for an integer conversion;
ll
(two lower-case letter Ls) specifies that
an integer conversion has a KDint64
or
KDuint64 argument.
Attempting to use a length modifier and conversion character combination not specified above makes the conversion specification invalid.
The conversion character is one of:
d
or i
reads the next
argument, which must be KDint or KDuint,
or as specified by any length modifier, and outputs in
signed decimal notation;
o
reads the next
argument, which must be KDint or KDuint,
or as specified by any length modifier, and outputs in
unsigned octal notation;
x
or X
reads the next
argument, which must be KDint or KDuint,
or as specified by any length modifier, and outputs in
unsigned hexadecimal notation, with non-decimal digits in the
same case as the conversion character;
u
reads the next
argument, which must be KDint or KDuint,
or as specified by any length modifier, and outputs in
unsigned decimal notation;
c
reads the next
argument, which must be KDchar or KDint8
or KDuint8,
and outputs the single character;
s
reads the next
argument, which must be a
const KDchar * value that either points to
a null-terminated string or is KD_NULL
and outputs the string (truncated by any precision specification),
or an implementation-defined representation of
KD_NULL
;
e
or E
reads the next
argument, which must be KDfloat32,
or KDfloat64KHR (when the KD_KHR_float64
extension is also present),
and outputs a
rounded representation: a minus sign if applicable, then a
single digit (non-zero if the value is non-zero), then the
decimal point, then the number of digits specified by the
precision (6 if not specified), then a e
or
E
character (respectively for the two
conversion characters), then a plus or minus sign, then
two or more digits of exponent (with no leading zeros unless
a two digit exponent). If the precision is zero and there is
no #
flag, the decimal point is omitted. If
the value is zero the exponent is zero.
The converted output for a KDfloat32 is allowed to be up to one out in its ninth significant digit. Thus digits beyond the ninth have undefined values.
The converted output for a KDfloat64 is allowed to be up to one out in its seventeenth significant digit. Thus digits beyond the seventeenth have undefined values.
An argument representing an infinity or
not-a-number is converted as if by
kdFtostr
, in the same case
(upper or lower) as the conversion character.
The specification of kdFtostr
regarding infinity and not-a-number was tightened in
OpenKODE Core 1.0.1, so the exact semantics depend on the
version of OpenKODE Core being implemented.
f
or F
reads the next argument, which must be KDfloat32,
or KDfloat64KHR (when the KD_KHR_float64
extension is also present),
and outputs a rounded representation:
a minus sign if applicable, then at least one digit,
then the decimal point, then the number of digits specified
by the precision (6 if not specified).
If the precision is zero
and there is no #
flag, the decimal point
is omitted.
The converted output for a KDfloat32 is allowed to be up to one out in its ninth significant digit. Thus digits beyond the ninth have undefined values.
The converted output for a KDfloat64 is allowed to be up to one out in its seventeenth significant digit. Thus digits beyond the seventeenth have undefined values.
An argument representing an infinity or not-a-number
is converted in the same way as for
e
or E
.
g
or G
reads the next argument, which must be KDfloat32,
or KDfloat64KHR (when the KD_KHR_float64
extension is also present),
and outputs a rounded representation in one of these
forms:
as per
e
or E
(for g
or G
respectively)
if the exponent is
more than or equal to the specified precision and not zero,
or the exponent is less than -4;
otherwise, as per
f
or F
(for g
or G
respectively).
In either case, the precision (6 if not specified, 1 if specified as 0) determines the number of significant digits in the mantissa.
p
reads the next
argument, which must be of some pointer type, and outputs
an implementation-defined representation of the pointer;
n
reads the next
argument, which must be KDint *, or as modified by
any length specifier, and writes the number of characters
output so far in this call into the location pointed to by the
value. Nothing is output.
If an argument is not of the type expected by the conversion specification, then undefined behavior results. However, certain discrepancies are allowed:
KDint64 and KDuint64 may be used interchangeably;
KDint and KDuint may be used interchangeably;
KDint32, KDuint32, KDint16, KDuint16, KDint8, KDuint8 and KDchar may be used interchangeably;
KDint * and KDuint * may be used interchangeably;
KDint16 * and KDuint16 * may be used interchangeably.
kdSprintfKHR
and kdVsprintfKHR
are based on the C89 functions
sprintf
and vsprintf
.
kdSnprintfKHR
and kdVsnprintfKHR
have an API specification based
on C99 snprintf
and vsnprintf
(which differs in return value
from SUSv2 in the case that the output does not fit in the buffer).
The format conversions are those in C89,
except for the addition of
the C99 ll
length modifier to convert 64-bit integer values.
The statement that a floating-point conversion can be up to one
out in its ninth significant digit is intended to match
kdFtostr
, so that
this function can be implemented in terms of that one, or
vice versa.
Further differences from C standards concern the argument types.
OpenKODE Core uses
KDint16 or KDuint16
instead of short or unsigned short,
and
KDint or KDuint
instead of int or unsigned int,
and (for the C99-like ll
length modifier)
KDint64 or KDuint64
instead of long long or unsigned long long.
Formatted output to an open file.
KDint kdFprintfKHR( | KDFile * | file, |
const KDchar * | format, | |
...) ; |
KDint kdVfprintfKHR( | KDFile * | file, |
const KDchar * | format, | |
KDVaListKHR | ap) ; |
These functions write a formatted string based on the
null-terminated string format
, with certain
sequences substituted by values of further arguments,
to the open-for-writing file file
.
kdFprintfKHR
takes the arguments to be
formatted as a variable argument list.
kdVfprintfKHR
takes the arguments as a single
KDVaListKHR value, from which the arguments are retrieved.
The function does not call KD_VA_END_KHR before returning.
Formatting occurs in the same way as in
kdSnprintfKHR
.
If file
is not an open file,
or if
format
is not a readable null-terminated
string, then undefined behavior results.
On success, the function returns the number of characters
written (excluding the null termination).
On failure, it returns -1, sets the
file’s error indicator (as returned by
kdFerror
)
and stores one of the error codes listed below into
the error indicator returned by
kdGetError
.
Formatted output to the platform’s debug logging facility.
This function writes a formatted string based on the
null-terminated string format
, with certain
sequences substituted by values of further arguments,
to the same place as
kdLogMessage
.
Unlike that function, it does not automatically append a newline
character.
Formatting occurs in the same way as in
kdSnprintfKHR
.
If
format
is not a readable null-terminated
string, then undefined behavior results.
The function returns the number of characters written (excluding the null termination). It cannot fail, and specifically cannot run out of memory.
kdLogMessagefKHR
allows a developer to add a convenient
single line to a program to aid in its debugging by logging some
variable values and so on. Although
kdLogMessage
could be used for this, it is inconvenient to use when number
values need to be output.
See the rationale for kdSnprintfKHR
for a discussion of the format conversions.
Read formatted input from a buffer.
KDint kdSscanfKHR( | const KDchar * | str, |
const KDchar * | format, | |
...) ; |
KDint kdVsscanfKHR( | const KDchar * | str, |
const KDchar * | format, | |
KDVaListKHR | ap) ; |
These functions scan the null-terminated string
pointed to by str
as directed by the null-terminated format string
format
, with certain sequences in the format
string causing part of the input to be converted and the result
stored in a location pointed to by one of the further arguments.
kdSscanfKHR
takes the further arguments
as a variable argument list.
kdVsscanfKHR
takes the further arguments
as a single
KDVaListKHR value, from which the arguments are retrieved.
The function does not call KD_VA_END_KHR before returning.
If buf
does not point to readable memory
long enough to read as directed by the format string (a readable
null-terminated string always satisfies this criterion),
or if
format
is not a readable null-terminated
string, then undefined behavior results.
The functions return the number of input items successfully
matched and assigned before the end (the null termination)
of the format string is reached, or before a matching failure occurs.
If the end of the input string str
is reached,
then this is an input failure
and the function returns KD_EOF
.
The null-terminated format string consists of zero or more directives. If processing of a directive fails, then the functions return without reading any further input.
A directive is one of:
a sequence of one or more whitespace characters (each character is space, newline, form-feed, carriage return, horizontal tab or vertical tab), which matches the maximal sequence of zero or more whitespace characters in the input;
a character other than whitespace or %
,
which matches that exact character in the input;
a %%
sequence, which matches one
%
character in the input;
a conversion specification, starting with a %
character, which causes a sequence of characters to be read and
converted according to the specification, and the result placed
in the location pointed to by the next of the further arguments.
If the next input item does not match the conversion specification,
the conversion fails and the functions stop reading input and
return.
A percent sign followed by characters not forming a valid conversion specification causes undefined behavior.
If the conversion specifier is invalid, allowing undefined behavior, an implementation is recommended to terminate the application in some way, rather than reading and converting nothing or something and appearing to succeed. This reduces the danger of the application programmer accidentally relying on non-portable behavior of the format string.
A conversion specification consists, in this order, of:
the initial %
character;
an optional *
character to suppress
assignment;
an optional field width specifier;
an optional length modifier character;
the conversion specifier.
Except where otherwise specified, initial whitespace is skipped before starting a conversion.
The optional *
character in the conversion
specification causes
assignment to be suppressed: once the conversion has been
performed, the result is discarded without using the next
of the further arguments and without incrementing the count
of items successfully matched and assigned.
The field width is optionally specified by a strictly positive decimal integer. After scanning any initial whitespace where applicable, the field width is the maximum number of characters that will be considered part of the field and used in the conversion.
The length modifier is an optional character sequence that modifies the size of the type of the location (pointed to by the next of the further arguments) that receives the converted value. The length modifier is one of:
h
specifies that a conversion specifier of
d
, i
, o
,
u
, x
, X
or n
stores a KDint16 or KDuint16.
l
(lower-case L) is accepted and ignored for
a conversion specifier of
d
, i
, o
,
u
, x
, X
or n
.
ll
(double lower-case L) specifies that
a conversion specifier of
d
, i
, o
,
u
, x
, X
or n
stores a KDint64 or KDuint64.
The conversion specifier is one of:
d
reads an optionally signed decimal
integer. The value is stored as a KDint
(unless there is a length modifier).
i
reads an optionally signed decimal,
octal (starts with 0
0 or
hexadecimal (prefixed with 0x
or
0X
)
integer. The value is stored as a KDint
(unless there is a length modifier).
o
reads an optionally signed octal
integer. The value is stored as a KDuint
(unless there is a length modifier).
x
reads an optionally signed and
optionally 0x
or 0X
prefixed hexadecimal
integer. The value is stored as a KDuint
(unless there is a length modifier).
e
,
f
or
g
reads a floating point number, as if by
kdStrtof
.
The value is stored as a KDfloat32.
The specification of kdStrtof
regarding infinity, not-a-number and hexadecimal
floating point was tightened in
OpenKODE Core 1.0.1, so the exact semantics depend on the
version of OpenKODE.
c
reads exactly the number of characters
specified in the field width (1 if not specified), and
copies them without null termination into the KDchar
array pointed to by the argument. For this conversion
specifier, the usual skipping of initial whitespace is
suppressed.
s
reads a sequence of non-whitespace
characters, and copies them with an additional null termination
into the KDchar
array pointed to by the argument.
A conversion specifier consisting of [
followed by one or more characters other than
]
(the scanset)
(the first character in the scanset can be [
,
but cannot be ^
),
then a final ]
matches one or more
characters in the input where each is in the scanset.
The matching character sequence is copied with an additional
null termination into the KDchar array pointed
to by the argument.
A conversion specifier consisting of [^
followed by one or more characters other than
]
(the scanset)
(the first character in the scanset can be [
),
then a final ]
matches one or more
characters in the input where each is not in the scanset.
The matching character sequence is copied with an additional
null termination into the KDchar array pointed
to by the argument.
p
reads a pointer value in an
implementation-defined format which includes the format
used by %p
in
kdSnprintfKHR
.
The value is stored as a void *.
n
does not read any input (not even initial
whitespace), and stores the current count of bytes consumed
as a KDint.
This conversion specifier thus consumes an argument,
but does not itself increment the count of items successfully
matched and assigned.
If an argument is not of the pointer type expected by the conversion specification (i.e. the argument is not of type pointer to the type that the conversion specification stores, other than a change of signedness), or the location pointed to by the argument is not writable and big enough for the value being stored, then undefined behavior results.
kdSscanfKHR
and kdVsscanfKHR
are based on
on C89 sscanf
, with the addition
of C99's ll
length modifier.
There are some differences from C standards in the argument types.
OpenKODE Core uses
KDint16 * or KDuint16 *
instead of short * or unsigned short *,
and
KDint * or KDuint *
instead of int * or unsigned int *,
and (for the C99-like ll
length modifier)
KDint64 * or KDuint64 *
instead of long long * or unsigned long long *.
Read formatted input from a file.
KDint kdFscanfKHR( | KDFile * | file, |
const KDchar * | format, | |
...) ; |
KDint kdVfscanfKHR( | KDFile * | file, |
const KDchar * | format, | |
KDVaListKHR | ap) ; |
These functions read the open file
as directed by the null-terminated format string
format
, with certain sequences in the format
string causing part of the input to be converted and the result
stored in a location pointed to by one of the further arguments.
kdFscanfKHR
takes the further arguments
as a variable argument list.
kdVfscanfKHR
takes the further arguments
as a single
KDVaListKHR value, from which the arguments are retrieved.
The function does not call KD_VA_END_KHR before returning.
format
directs the reading and conversion
of the data in the same way as in
kdSscanfKHR
.
If file
is not an open file,
or if
format
is not a readable null-terminated
string, then undefined behavior results.
The functions return the number of input items successfully
matched and assigned before the end (the null termination)
of the format string is reached, or a matching failure occurs.
If end-of-file is reached, the function sets
the file’s end-of-file indicator (as returned by
kdFEOF
)
and returns KD_EOF
.
If an error occurs reading the file, the function sets the
file’s error indicator (as returned by
kdFerror
)
and stores one of the error codes listed below into
the error indicator returned by
kdGetError
.
Corrected kdSscanfKHR %n conversion to store the number of bytes consumed, not the number of conversions performed, in line with C and POSIX standards.
Changed back to being based on OpenKODE Core 1.0 specification.
Rephrased kdSnprintfKHR treatment of floating-point infinity or not-a-number to be the same as kdFtostr, and noted that this changed in OpenKODE 1.0.1.
Noted that the kdStrtof treatment of infinity, not-a-number and hexadecimal floating point changed in OpenKODE 1.0.1, so this affects the semantics of kdSscanfKHR.
Clarified that a precision of 0 in an integer conversion with a value of 0 prints no digits at all. Specified that an integer conversion has a default precision of 1. Allowed for three digit exponent. Stated that the 0 flag, when combined with a precision, is ignored for integer conversions, and causes undefined behavior for floating point conversions. Fixed typos.
Added KD_VA_ARG_*_KHR macros for extracting variable arguments one at a time.