4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-01-19 12:59:21 +08:00
Matt Joyce ea99f21ce6 Add --enable-newlib-reent-thread-local option
By default, Newlib uses a huge object of type struct _reent to store
thread-specific data.  This object is returned by __getreent() if the
__DYNAMIC_REENT__ Newlib configuration option is defined.

The reentrancy structure contains for example errno and the standard input,
output, and error file streams.  This means that if an application only uses
errno it has a dependency on the file stream support even if it does not use
it.  This is an issue for lower end targets and applications which need to
qualify the software according to safety standards (for example ECSS-E-ST-40C,
ECSS-Q-ST-80C, IEC 61508, ISO 26262, DO-178, DO-330, DO-333).

If the new _REENT_THREAD_LOCAL configuration option is enabled, then struct
_reent is replaced by dedicated thread-local objects for each struct _reent
member.  The thread-local objects are defined in translation units which use
the corresponding object.
2022-07-13 06:55:46 +02:00

452 lines
8.5 KiB
C

/*
FUNCTION
<<ecvtbuf>>, <<fcvtbuf>>---double or float to string
INDEX
ecvtbuf
INDEX
fcvtbuf
SYNOPSIS
#include <stdio.h>
char *ecvtbuf(double <[val]>, int <[chars]>, int *<[decpt]>,
int *<[sgn]>, char *<[buf]>);
char *fcvtbuf(double <[val]>, int <[decimals]>, int *<[decpt]>,
int *<[sgn]>, char *<[buf]>);
DESCRIPTION
<<ecvtbuf>> and <<fcvtbuf>> produce (null-terminated) strings
of digits representating the <<double>> number <[val]>.
The only difference between <<ecvtbuf>> and <<fcvtbuf>> is the
interpretation of the second argument (<[chars]> or
<[decimals]>). For <<ecvtbuf>>, the second argument <[chars]>
specifies the total number of characters to write (which is
also the number of significant digits in the formatted string,
since these two functions write only digits). For <<fcvtbuf>>,
the second argument <[decimals]> specifies the number of
characters to write after the decimal point; all digits for
the integer part of <[val]> are always included.
Since <<ecvtbuf>> and <<fcvtbuf>> write only digits in the
output string, they record the location of the decimal point
in <<*<[decpt]>>>, and the sign of the number in <<*<[sgn]>>>.
After formatting a number, <<*<[decpt]>>> contains the number
of digits to the left of the decimal point. <<*<[sgn]>>>
contains <<0>> if the number is positive, and <<1>> if it is
negative. For both functions, you supply a pointer <[buf]> to
an area of memory to hold the converted string.
RETURNS
Both functions return a pointer to <[buf]>, the string
containing a character representation of <[val]>.
PORTABILITY
Neither function is ANSI C.
Supporting OS subroutines required: <<close>>, <<fstat>>, <<isatty>>,
<<lseek>>, <<read>>, <<sbrk>>, <<write>>.
*/
#include <_ansi.h>
#include <stdlib.h>
#include <string.h>
#include <reent.h>
#include "mprec.h"
#include "local.h"
#ifdef _REENT_THREAD_LOCAL
_Thread_local char *_tls_cvtbuf;
_Thread_local int _tls_cvtlen;
#endif
static void
print_f (struct _reent *ptr,
char *buf,
double invalue,
int ndigit,
char type,
int dot,
int mode)
{
int decpt;
int sign;
char *p, *start, *end;
start = p = _dtoa_r (ptr, invalue, mode, ndigit, &decpt, &sign, &end);
if (decpt == 9999)
{
strcpy (buf, p);
return;
}
while (*p && decpt > 0)
{
*buf++ = *p++;
decpt--;
}
/* Even if not in buffer */
while (decpt > 0)
{
*buf++ = '0';
decpt--;
}
if (dot || *p)
{
if (p == start)
*buf++ = '0';
if (decpt < 0 && ndigit > 0)
*buf++ = '.';
while (decpt < 0 && ndigit > 0)
{
*buf++ = '0';
decpt++;
ndigit--;
}
/* Print rest of stuff */
while (*p && ndigit > 0)
{
*buf++ = *p++;
ndigit--;
}
/* And trailing zeros */
while (ndigit > 0)
{
*buf++ = '0';
ndigit--;
}
}
*buf++ = 0;
}
/* Print number in e format with width chars after.
TYPE is one of 'e' or 'E'. It may also be one of 'g' or 'G' indicating
that _gcvt is calling us and we should remove trailing zeroes.
WIDTH is the number of digits of precision after the decimal point. */
static void
print_e (struct _reent *ptr,
char *buf,
double invalue,
int width,
char type,
int dot)
{
int sign;
char *end;
char *p;
int decpt;
int top;
int ndigit = width;
p = _dtoa_r (ptr, invalue, 2, width + 1, &decpt, &sign, &end);
if (decpt == 9999)
{
strcpy (buf, p);
return;
}
*buf++ = *p++;
if (ndigit > 0)
dot = 1;
while (*p && ndigit > 0)
{
if (dot) {
*buf++ = '.';
dot = 0;
}
*buf++ = *p++;
ndigit--;
}
/* Add trailing zeroes to fill out to ndigits unless this is 'g' format.
Also, convert g/G to e/E. */
if (type == 'g')
type = 'e';
else if (type == 'G')
type = 'E';
else
{
while (ndigit > 0)
{
if (dot) {
*buf++ = '.';
dot = 0;
}
*buf++ = '0';
ndigit--;
}
}
/* Add the exponent. */
*buf++ = type;
decpt--;
if (decpt < 0)
{
*buf++ = '-';
decpt = -decpt;
}
else
{
*buf++ = '+';
}
if (decpt > 99)
{
int top = decpt / 100;
*buf++ = top + '0';
decpt -= top * 100;
}
top = decpt / 10;
*buf++ = top + '0';
decpt -= top * 10;
*buf++ = decpt + '0';
*buf++ = 0;
}
#ifndef _REENT_ONLY
/* Undocumented behaviour: when given NULL as a buffer, return a
pointer to static space in the rent structure. This is only to
support ecvt and fcvt, which aren't ANSI anyway. */
char *
fcvtbuf (double invalue,
int ndigit,
int *decpt,
int *sign,
char *fcvt_buf)
{
struct _reent *reent = _REENT;
char *save;
char *p;
char *end;
int done = 0;
if (fcvt_buf == NULL)
{
if (_REENT_CVTLEN(reent) <= ndigit + 35)
{
if ((fcvt_buf = (char *) _realloc_r (reent, _REENT_CVTBUF(reent),
ndigit + 36)) == NULL)
return NULL;
_REENT_CVTLEN(reent) = ndigit + 36;
_REENT_CVTBUF(reent) = fcvt_buf;
}
fcvt_buf = _REENT_CVTBUF(reent) ;
}
save = fcvt_buf;
p = _dtoa_r (reent, invalue, 3, ndigit, decpt, sign, &end);
/* Now copy */
done = -*decpt;
while (p < end)
{
*fcvt_buf++ = *p++;
done++;
}
/* And unsuppress the trailing zeroes */
while (done < ndigit)
{
*fcvt_buf++ = '0';
done++;
}
*fcvt_buf++ = 0;
return save;
}
char *
ecvtbuf (double invalue,
int ndigit,
int *decpt,
int *sign,
char *fcvt_buf)
{
struct _reent *reent = _REENT;
char *save;
char *p;
char *end;
int done = 0;
if (fcvt_buf == NULL)
{
if (_REENT_CVTLEN(reent) <= ndigit)
{
if ((fcvt_buf = (char *) _realloc_r (reent, _REENT_CVTBUF(reent),
ndigit + 1)) == NULL)
return NULL;
_REENT_CVTLEN(reent) = ndigit + 1;
_REENT_CVTBUF(reent) = fcvt_buf;
}
fcvt_buf = _REENT_CVTBUF(reent) ;
}
save = fcvt_buf;
p = _dtoa_r (reent, invalue, 2, ndigit, decpt, sign, &end);
/* Now copy */
while (p < end)
{
*fcvt_buf++ = *p++;
done++;
}
/* And unsuppress the trailing zeroes */
while (done < ndigit)
{
*fcvt_buf++ = '0';
done++;
}
*fcvt_buf++ = 0;
return save;
}
#endif
char *
_gcvt (struct _reent *ptr,
double invalue,
int ndigit,
char *buf,
char type,
int dot)
{
char *save = buf;
if (invalue < 0)
{
invalue = -invalue;
}
if (invalue == 0)
{
*buf++ = '0';
*buf = '\0';
}
else
/* Which one to print ?
ANSI says that anything with more that 4 zeros after the . or more
than precision digits before is printed in e with the qualification
that trailing zeroes are removed from the fraction portion. */
if (0.0001 >= invalue || invalue >= _mprec_log10 (ndigit))
{
/* We subtract 1 from ndigit because in the 'e' format the precision is
the number of digits after the . but in 'g' format it is the number
of significant digits.
We defer changing type to e/E so that print_e() can know it's us
calling and thus should remove trailing zeroes. */
print_e (ptr, buf, invalue, ndigit - 1, type, dot);
}
else
{
int decpt;
int sign;
char *end;
char *p;
/* We always want ndigits of precision, even if that means printing
* a bunch of leading zeros for numbers < 1.0
*/
p = _dtoa_r (ptr, invalue, 2, ndigit, &decpt, &sign, &end);
if (decpt == 9999)
{
strcpy (buf, p);
return save;
}
while (*p && decpt > 0)
{
*buf++ = *p++;
decpt--;
ndigit--;
}
/* Even if not in buffer */
while (decpt > 0 && ndigit > 0)
{
*buf++ = '0';
decpt--;
ndigit--;
}
if (dot || *p)
{
if (buf == save)
*buf++ = '0';
*buf++ = '.';
/* Leading zeros don't count towards 'ndigit' */
while (decpt < 0)
{
*buf++ = '0';
decpt++;
}
/* Print rest of stuff */
while (*p && ndigit > 0)
{
*buf++ = *p++;
ndigit--;
}
/* And trailing zeros */
if (dot)
{
while (ndigit > 0)
{
*buf++ = '0';
ndigit--;
}
}
}
*buf++ = 0;
}
return save;
}
char *
_dcvt (struct _reent *ptr,
char *buffer,
double invalue,
int precision,
int width,
char type,
int dot)
{
switch (type)
{
case 'f':
case 'F':
print_f (ptr, buffer, invalue, precision, type, precision == 0 ? dot : 1, 3);
break;
case 'g':
case 'G':
if (precision == 0)
precision = 1;
_gcvt (ptr, invalue, precision, buffer, type, dot);
break;
case 'e':
case 'E':
print_e (ptr, buffer, invalue, precision, type, dot);
}
return buffer;
}