Browse Source

stdio: move the printf implementation from gint

master
Lephenixnoir 8 months ago
parent
commit
f52e0923bc
Signed by: Lephenixnoir GPG Key ID: 1BBA026E13FC0495
  1. 22
      3rdparty/grisu2b_59_56/LICENSE
  2. 14
      3rdparty/grisu2b_59_56/README
  3. 58
      3rdparty/grisu2b_59_56/diy_fp.h
  4. 110
      3rdparty/grisu2b_59_56/double.h
  5. 27
      3rdparty/grisu2b_59_56/grisu2.h
  6. 95
      3rdparty/grisu2b_59_56/grisu2b_59_56.c
  7. 32
      3rdparty/grisu2b_59_56/k_comp.h
  8. 27
      3rdparty/grisu2b_59_56/powers.h
  9. 36
      3rdparty/grisu2b_59_56/powers_ten_round64.h
  10. 30
      CMakeLists.txt
  11. 8
      README.md
  12. 226
      include/fxlibc/printf.h
  13. 71
      include/stdio.h
  14. 26
      src/libc/stdio/dprintf.c
  15. 18
      src/libc/stdio/fprintf.c
  16. 58
      src/libc/stdio/internal/printf.h
  17. 208
      src/libc/stdio/internal/printf_actions.c
  18. 48
      src/libc/stdio/internal/printf_common.c
  19. 127
      src/libc/stdio/internal/printf_options.c
  20. 27
      src/libc/stdio/printf.c
  21. 295
      src/libc/stdio/printf/format_fp.c
  22. 302
      src/libc/stdio/printf/format_usual.c
  23. 292
      src/libc/stdio/printf/print.c
  24. 26
      src/libc/stdio/snprintf.c
  25. 27
      src/libc/stdio/sprintf.c
  26. 47
      src/libc/stdio/vdprintf.c
  27. 12
      src/libc/stdio/vfprintf.c
  28. 12
      src/libc/stdio/vprintf.c
  29. 43
      src/libc/stdio/vsnprintf.c
  30. 17
      src/libc/stdio/vsprintf.c

22
3rdparty/grisu2b_59_56/LICENSE

@ -0,0 +1,22 @@
Copyright (c) 2009 Florian Loitsch
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

14
3rdparty/grisu2b_59_56/README

@ -0,0 +1,14 @@
Grisu is an algorithm by Florian Loitsch to print the decimal representation of
floating-point numbers.
The original code from which this folder is extracted can be found at
<https://drive.google.com/open?id=0BwvYOx00EwKmejFIMjRORTFLcTA>.
Only a minimal variation, the Grisu2 rounding algorithm with α=-59 and γ=-56,
is present here. The code has been adapted to fit the limited runtime as well
as the use of OpenLibm.
See the original paper at <https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf>
and Florian Loitsch's home page at <https://florian.loitsch.com/publications>.
The files are licensed under the permissive conditions of the attached LICENSE.

58
3rdparty/grisu2b_59_56/diy_fp.h

@ -0,0 +1,58 @@
/*
Copyright (c) 2009 Florian Loitsch
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <stdint.h>
typedef struct diy_fp_t {
uint64_t f;
int e;
} diy_fp_t;
static diy_fp_t minus(diy_fp_t x, diy_fp_t y) {
diy_fp_t r = {.f = x.f - y.f, .e = x.e};
return r;
}
/*
static diy_fp_t minus(diy_fp_t x, diy_fp_t y) {
assert(x.e == y.e);
assert(x.f >= y.f);
diy_fp_t r = {.f = x.f - y.f, .e = x.e};
return r;
}
*/
static diy_fp_t multiply(diy_fp_t x, diy_fp_t y) {
uint64_t a,b,c,d,ac,bc,ad,bd,tmp,h;
diy_fp_t r; uint64_t M32 = 0xFFFFFFFF;
a = x.f >> 32; b = x.f & M32;
c = y.f >> 32; d = y.f & M32;
ac = a*c; bc = b*c; ad = a*d; bd = b*d;
tmp = (bd>>32) + (ad&M32) + (bc&M32);
tmp += 1U << 31; /// mult_round
r.f = ac+(ad>>32)+(bc>>32)+(tmp >>32);
r.e = x.e + y.e + 64;
return r;
}

110
3rdparty/grisu2b_59_56/double.h

@ -0,0 +1,110 @@
/*
Copyright (c) 2009 Florian Loitsch
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include "diy_fp.h"
#include "powers.h"
#include <stdbool.h>
typedef union {
double d;
uint64_t n;
} converter_t;
static uint64_t double_to_uint64(double d) { converter_t tmp; tmp.d = d; return tmp.n; }
static double uint64_to_double(uint64_t d64) { converter_t tmp; tmp.n = d64; return tmp.d; }
#define DP_SIGNIFICAND_SIZE 52
#define DP_EXPONENT_BIAS (0x3FF + DP_SIGNIFICAND_SIZE)
#define DP_MIN_EXPONENT (-DP_EXPONENT_BIAS)
#define DP_EXPONENT_MASK 0x7FF0000000000000
#define DP_SIGNIFICAND_MASK 0x000FFFFFFFFFFFFF
#define DP_HIDDEN_BIT 0x0010000000000000
static diy_fp_t normalize_diy_fp(diy_fp_t in) {
diy_fp_t res = in;
/* Normalize now */
/* the original number could have been a denormal. */
while (! (res.f & DP_HIDDEN_BIT))
{
res.f <<= 1;
res.e--;
}
/* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
res.f <<= (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 1);
res.e = res.e - (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 1);
return res;
}
static diy_fp_t double2diy_fp(double d) {
uint64_t d64 = double_to_uint64(d);
int biased_e = (d64 & DP_EXPONENT_MASK) >> DP_SIGNIFICAND_SIZE;
uint64_t significand = (d64 & DP_SIGNIFICAND_MASK);
diy_fp_t res;
if (biased_e != 0)
{
res.f = significand + DP_HIDDEN_BIT;
res.e = biased_e - DP_EXPONENT_BIAS;
} else {
res.f = significand;
res.e = DP_MIN_EXPONENT + 1;
}
return res;
}
static diy_fp_t normalize_boundary(diy_fp_t in) {
diy_fp_t res = in;
/* Normalize now */
/* the original number could have been a denormal. */
while (! (res.f & (DP_HIDDEN_BIT << 1)))
{
res.f <<= 1;
res.e--;
}
/* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
res.f <<= (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 2);
res.e = res.e - (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 2);
return res;
}
static void normalized_boundaries(double d, diy_fp_t* out_m_minus, diy_fp_t* out_m_plus) {
diy_fp_t v = double2diy_fp(d);
diy_fp_t pl, mi;
bool significand_is_zero = v.f == DP_HIDDEN_BIT;
pl.f = (v.f << 1) + 1; pl.e = v.e - 1;
pl = normalize_boundary(pl);
if (significand_is_zero)
{
mi.f = (v.f << 2) - 1;
mi.e = v.e - 2;
} else {
mi.f = (v.f << 1) - 1;
mi.e = v.e - 1;
}
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*out_m_plus = pl;
*out_m_minus = mi;
}

27
3rdparty/grisu2b_59_56/grisu2.h

@ -0,0 +1,27 @@
/*
Copyright (c) 2009 Florian Loitsch
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
void grisu2(double v, char* buffer, int* length, int* K);

95
3rdparty/grisu2b_59_56/grisu2b_59_56.c

@ -0,0 +1,95 @@
/*
Copyright (c) 2009 Florian Loitsch
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "diy_fp.h"
#include "k_comp.h"
#include "double.h"
#include "powers.h"
#include <stdbool.h>
#include "grisu2.h"
#include <stdint.h>
#define TEN9 1000000000
void grisu_round(char* buffer, int len,
uint64_t delta, uint64_t rest,
uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w &&
delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest+ten_kappa - wp_w))
{
buffer[len-1]--; rest += ten_kappa;
}
}
void digit_gen(diy_fp_t W, diy_fp_t Mp, diy_fp_t delta,
char* buffer, int* len, int* K) {
uint32_t div; int d,kappa; diy_fp_t one, wp_w;
wp_w = minus(Mp, W);
one.f = ((uint64_t) 1) << -Mp.e; one.e = Mp.e;
uint32_t p1 = Mp.f >> -one.e; /// Mp_cut
uint64_t p2 = Mp.f & (one.f - 1);
*len = 0; kappa = 10; div = TEN9;
while (kappa > 0) {
d = p1 / div;
if (d || *len) buffer[(*len)++] = '0' + d; /// Mp_inv1
p1 %= div; kappa--;
uint64_t tmp = (((uint64_t)p1)<<-one.e)+p2;
if (tmp <= delta.f) { /// Mp_delta
*K += kappa;
grisu_round(buffer, *len, delta.f, tmp, ((uint64_t)div) << -one.e, wp_w.f);
return;
}
div /= 10;
}
uint64_t unit = 1;
while (1) {
p2 *= 10; delta.f *= 10; unit *= 10;
d = p2 >> -one.e;
if (d || *len) buffer[(*len)++] = '0' + d;
p2 &= one.f - 1; kappa--;
if (p2 < delta.f) {
*K += kappa;
grisu_round(buffer, *len, delta.f, p2, one.f, wp_w.f*unit);
return;
}
}
}
void grisu2(double v, char* buffer, int* length, int* K) {
diy_fp_t w_m, w_p;
int q = 64, alpha = -59, gamma = -56; int pos;
normalized_boundaries(v, &w_m, &w_p);
diy_fp_t w = normalize_diy_fp(double2diy_fp(v));
int mk = k_comp(w_p.e + q, alpha, gamma);
diy_fp_t c_mk = cached_power(mk);
diy_fp_t W = multiply(w, c_mk);
diy_fp_t Wp = multiply(w_p, c_mk);
diy_fp_t Wm = multiply(w_m, c_mk);
Wm.f++; Wp.f--;
diy_fp_t delta = minus(Wp, Wm);
*K = -mk;
digit_gen(W, Wp, delta, buffer, length, K);
}

32
3rdparty/grisu2b_59_56/k_comp.h

@ -0,0 +1,32 @@
/*
Copyright (c) 2009 Florian Loitsch
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <openlibm.h>
#define D_1_LOG2_10 0.30102999566398114 // 1 / lg(10)
static int k_comp(int e, int alpha, int gamma) {
return ceil((alpha-e+63) * D_1_LOG2_10);
}

27
3rdparty/grisu2b_59_56/powers.h

@ -0,0 +1,27 @@
/*
Copyright (c) 2009 Florian Loitsch
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include "diy_fp.h"
#include "powers_ten_round64.h"

36
3rdparty/grisu2b_59_56/powers_ten_round64.h

File diff suppressed because one or more lines are too long

30
CMakeLists.txt

@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.15)
project(FxLibc VERSION 1.0.0 LANGUAGES C ASM)
set(CMAKE_INSTALL_MESSAGE LAZY)
# Options
# * -DFXLIBC_TARGET=<vhex-sh, vhex-x86, casiowin-fx, casiowin-cg, gint>
@ -79,6 +81,8 @@ endif()
# Building
set(SOURCES
# 3rdparty
3rdparty/grisu2b_59_56/grisu2b_59_56.c
# assert
src/libc/assert/assert.c
# ctype
@ -110,19 +114,21 @@ set(SOURCES
src/libc/signal/signal.c
src/libc/signal/raise.c
# stdio
src/libc/stdio/vsnprintf.c
src/libc/stdio/sprintf.c
src/libc/stdio/dprintf.c
src/libc/stdio/snprintf.c
src/libc/stdio/puts.c
src/libc/stdio/vsprintf.c
src/libc/stdio/fprintf.c
src/libc/stdio/printf.c
src/libc/stdio/printf/format_fp.c
src/libc/stdio/printf/format_usual.c
src/libc/stdio/printf/print.c
src/libc/stdio/putc.c
src/libc/stdio/internal/printf_actions.c
src/libc/stdio/internal/printf_options.c
src/libc/stdio/internal/printf_common.c
src/libc/stdio/internal/printf.h
src/libc/stdio/puts.c
src/libc/stdio/snprintf.c
src/libc/stdio/sprintf.c
src/libc/stdio/vdprintf.c
src/libc/stdio/printf.c
src/libc/stdio/vfprintf.c
src/libc/stdio/vprintf.c
src/libc/stdio/vsnprintf.c
src/libc/stdio/vsprintf.c
# stdlib
src/libc/stdlib/abort.c
src/libc/stdlib/abs.c
@ -179,6 +185,10 @@ set(SOURCES
src/libc/string/strtok.c
src/libc/string/strxfrm.c)
# Silence extended warnings on Grisu2b code
set_source_files_properties(3rdparty/grisu2b_59_56/grisu2b_59_56.c PROPERTIES
COMPILE_OPTIONS "-Wno-all;-Wno-extra")
if(vhex-generic IN_LIST TARGET_FOLDERS)
# TODO
endif()

8
README.md

@ -108,6 +108,14 @@ This work is licensed under a CC0 1.0 Universal License. To view a copy of this
license, visit: https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt
Or see the LICENSE file.
FxLibc also includes third-party code that is distributed under its own
license. Currently, this includes:
* A stripped-down version of the [Grisu2b floating-point representation
algorithm](https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
with α=-59 and γ=-56, by Florian Loitsch. See `src/3rdparty/grisu2b_59_56/README`
for details, and [the original code here](https://drive.google.com/open?id=0BwvYOx00EwKmejFIMjRORTFLcTA).
---
### Special thanks to

226
include/fxlibc/printf.h

@ -0,0 +1,226 @@
#ifndef __FXLIBC_PRINTF_H__
#define __FXLIBC_PRINTF_H__
/* This headers covers fxlibc-specific extensions to the *printf API. */
#include <bits/types/FILE.h>
#include <stdint.h>
#include <stdarg.h>
/*
** Output specification; only one of the targets may be non-trivial.
** -> str != NULL
** -> fp != NULL
** -> fd >= 0
** The size field can be set independently.
*/
struct __printf_output {
/* Final output, after buffering */
char * restrict str;
FILE *fp;
int fd;
/* Size of the final output */
size_t size;
/* The following members are set by __printf; do not write to them. */
/* Current output buffer, output position, and buffer limit */
char *buffer, *ptr, *limit;
/* Number of characters written so far */
size_t count;
};
/* Generic formatted printing. */
int __printf(
struct __printf_output * restrict __out,
char const * restrict __format,
va_list *__args);
/*
** Enable floating-point formatters %e, %E, %f, %F, %g, and %G. The formats are
** disabled by default because the representation algorithm has tables of
** powers of 10 and quite a bit of code, resulting in 10-15 kiB of additional
** size in every binary.
**
** Calling this functions pulls object files with floating-point representation
** code from the fxlibc library and registers formatters for all 6
** floating-point formats.
*/
void __printf_enable_fp(void);
/* Format extension API. */
/* Standard format. */
struct __printf_format {
/* Minimal length of formatted string (padding can be added). */
uint16_t length;
/* How much significant characters of data, meaning varies. */
int16_t precision;
/*
** Size specifier for integers (%o, %x, %i, %d, %u), may be one of:
** (0) char (8-bit)
** (1) short (16-bit)
** (2) int (32-bit)
** (3) long (32-bit)
** (4) long long (64-bit)
*/
uint8_t size;
/* (#) Alternative form: base prefixes, decimal point. */
uint8_t alternative :1;
/* ( ) Add a blank sign before nonnegative numbers. */
uint8_t blank_sign :1;
/* (+) Always add a sign before a number (overrides ' '). */
uint8_t force_sign :1;
/*
** Alignment options, from lowest priority to highest priority:
** NUL By default, align right
** (0) Left-pad numerical values with zeros
** (-) Align left by adding space on the right, dropping zeros
*/
uint8_t alignment;
/* Format specifier. */
char spec;
};
/*
** Type of format functions.
** -> __spec is the specifier letter (eg. "d" in "%d")
** -> __opts are the length, precision, sign, alignment, etc. options
** -> __args is a pointer to the variable list of arguments to read from
*/
typedef void __printf_formatter_t(
struct __printf_output * restrict __out,
struct __printf_format * restrict __opts,
va_list * restrict __args
);
/*
** Register a new format.
**
** The formatter designated by the specified lowercase or uppercase letter
** (eg 'p' or 'P') is registered. This functions allows overriding default
** formatters, but this is very much discouraged. Letters with special meaning
** in the standard cannot be changed. A formatted can be removed of disabled by
** registering NULL.
**
** Here are used characters in the C standard:
**
** a: Hexadecimal floating-point A: Hexadecimal floating-point
** b: _ B: _
** c: Character C: Deprecated synonym for lc
** d: Decimal integer D: _
** e: Exponent floating-point E: Exponent floating-point
** f: Floating-point F: Floating-point
** g: General floating-point G: General: floating-point
** h: short or char size H: _
** i: Integer I: Locale-aware digits
** j: intmax_t size J: _
** k: _ K: _
** l: long or long long size L: long double size
** m: Error message from errno M: _
** n: Number of characters written N: _
** o: Octal integer O: _
** p: Pointer P: _
** q: Nonstandard synonym for ll Q: _
** r: _ R: _
** s: String S: Deprecated synonym for ls
** t: ptrdiff_t size T: _
** u: Unsigned decimal integer U: _
** v: _ V: _
** w: _ W: _
** x: Hexadecimal integer X: Hexadecimal integer
** y: _ Y: _
** z: size_t size Z: Old synonym for z
*/
void __printf_register(int __spec, __printf_formatter_t *__format);
/* Functions for formatters to output characters. */
/* Flush the buffer. (Don't call this directly.) */
void __printf_flush(struct __printf_output *__out);
/* Output a single character in the buffer (and possibly flush it). */
static inline void __printf_out(struct __printf_output *__out,
int __c)
{
if(__out->ptr >= __out->limit) __printf_flush(__out);
*(__out->ptr++) = __c;
}
/* Output the same character __n times. */
static inline void __printf_outn(struct __printf_output *__out,
int __c, int __n)
{
while(__n-- > 0) __printf_out(__out, __c);
}
/* Output a string. */
static inline void __printf_outstr(struct __printf_output *__out,
char const *__str, int __n)
{
for(int i = 0; i < __n; i++) __printf_out(__out, __str[i]);
}
/* Helper functions for formatters. */
/*
** Format geometry helper. The structure of a format is as follows:
**
** sign v |< zeros >| |< content >|
** _ _ _ _ _ _ _ _ _ + 0 x 0 0 0 0 0 0 8 a c 7 . 3 c _ _ _ _ _ _ _ _ _ _
** |< left_spaces >| ^^^ prefix |< right_spaces >|
**
** The sign character is absent if sign=0, the prefix is specified by length
** and is also absent if prefix=0.
*/
struct __printf_geometry
{
uint16_t left_spaces; /* Spaces before content */
uint8_t sign; /* Sign character (NUL, ' ', '+' or '-') */
uint8_t prefix; /* Base prefix ('0', '0x', etc) length */
uint16_t zeros; /* For integer displays, number of zeros */
uint16_t content; /* Content length in bytes */
uint16_t right_spaces; /* Spaces after content */
/* Style of display:
_PRINTF_GENERIC Sign ignored, 0-padding ignored
_PRINTF_INTEGER .precision causes 0-padding
_PRINTF_NUMERIC No effect */
enum { _PRINTF_GENERIC = 0, _PRINTF_INTEGER, _PRINTF_NUMERIC } style;
};
/*
** Calculate the geometry of a format.
**
** The caller provides as input __opt (as it received in the formatter
** function), and the following attributes of __geometry:
**
** - prefix: the length of the desired prefix (if unused, 0)
** - content: the natural content length for the provided data
** - sign: the sign of the input ('+' or '-'); for _PRINTF_GENERIC, 0
** - style, which affects the meaning of options
**
** This function outputs:
** - sign: will be changed to ' ' or NUL (0) depending on options
** - All fields of __geometry that are not part of the input
**
** The algorithm for laying out the format is as follows.
** 1. For numerical and integer formats, turn a "+" sign into " " if
** __opt->blank_sign is set, "+" if __opt->force_sign is set, NUL otherwise.
** 2. Compute the total amount of padding needed to reach __opt->length.
** 3. In integer style, if a precision is specified and more than content
** length plus sign and prefix, turn some padding into zeros.
** 4. If numerical and integer styles, if __opt->alignment == '0' turn all the
** padding into zeros.
** 5. Turn remaining padding into spaces at the left (if opt->alignment == NUL)
** or right (if opt->alignment == '-').
*/
void __printf_compute_geometry(
struct __printf_format *__opt,
struct __printf_geometry *__geometry);
#endif /* __FXLIBC_PRINTF_H__ */

71
include/stdio.h

@ -5,6 +5,7 @@
#include <stdarg.h>
#include <bits/types/FILE.h>
/* Standard input, output and error streams. */
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
@ -13,17 +14,71 @@ extern FILE *stderr;
#define stdout stdout
#define stderr stderr
/* *printf() familly - formatted output conversion. */
extern int printf(const char *restrict format, ...);
extern int dprintf(int fd, const char *restrict format, ...);
extern int sprintf(char *restrict str, const char *restrict format, ...);
extern int snprintf(char *restrict str, size_t size, const char *restrict format, ...);
extern int vdprintf(int fd, const char *restrict format, va_list ap);
extern int vsprintf(char *restrict str, const char *restrict format, va_list ap);
extern int vsnprintf(char *restrict str, size_t size, const char *restrict format, va_list ap);
/*
** Formatted input/output functions.
**
** These functions implement most of printf(3)'s features, including:
** - Signed and unsigned integer formats (%d, %i, %o, %u, %x, %X)
** - Character, string and pointer formats (%c, %s, %p)
** - Character count and strerror() shorthand formats (%n, %m)
** - Format options (0, #, -, (space), length, precision)
** - Parameter length (hh, h, l, ll, z)
** - Limiting the size of the output and still returning the whole length
** - If __printf_enable_fp() from <fxlibc/printf.h> is called: floating-point
** formats (%e, %E, %f, %F, %g, %G) (disabled by default to save space)
**
** They do not support:
** - Hexadecimal floating-point (%a, %A)
** - Some size modifiers: long double (L), intmax_t (j), ptrdiff_t (t), and the
** nonstandard synonyms q (ll) and Z (z)
** - Dynamic length field (*)
** - Thousands separators (') and locale-aware digits (I)
** - Nonstandard synonyms %C (%lc) and %S (%ls)
**
** There are extensions, namely to allow for custom conversions to be added.
** One custom conversion can be enabled with __printf_enable_fixed() from
** <fxlibc/printf.h>: a decimal fixed-point format %k which is like %d but
** with a decimal point. See <fxlibc/printf.h> for details.
*/
/* Formatted print to file. */
extern int fprintf(FILE * restrict __fp, char const * restrict __format, ...);
/* Formatted print to stdout. */
extern int printf(char const * restrict __format, ...);
/* Formatted print to string (with limited size). */
extern int snprintf(char * restrict __str, size_t __size,
char const * restrict __format, ...);
/* Formatted print to string (with unlimited size!). */
extern int sprintf(char * restrict __str, char const * restrict __format, ...);
/* Formatted print to file (variable argument list). */
extern int vfprintf(FILE * restrict __fp, char const * restrict __format,
va_list __args);
/* Formatted print to stdout (variable argument list). */
extern int vprintf(char const * restrict __format, va_list __args);
/* Formatted print to string (limited size, variable argument list). */
extern int vsnprintf(char * restrict __str, size_t __size,
char const * restrict __format, va_list __args);
/* Formatted print to string (unlimited size!, variable argument list). */
extern int vsprintf(char * restrict __str, char const * restrict __format,
va_list __args);
/* putx() - display char / string */
extern int putchar(int c);
extern int puts(const char *s);
/* Extensions. */
/* Formatted print to file descriptor. */
extern int dprintf(int __fd, char const * restrict __format, ...);
/* Formatted print to file descriptor (variable argument list). */
extern int vdprintf(int __fd, char const * restrict __format, va_list __args);
#endif /*__STDIO_H__*/

26
src/libc/stdio/dprintf.c

@ -1,16 +1,20 @@
#include <stdio.h>
#include <fxlibc/printf.h>
/*
** The function dprintf() is the same as fprintf() except that it outputs to a
** file descriptor, fd, instead of to a stdio stream.
*/
int dprintf(int fd, const char *restrict format, ...)
int dprintf(int fd, char const * restrict fmt, ...)
{
va_list ap;
int ret;
if(fd < 0) return -1;
va_start(ap, format);
ret = vdprintf(fd, format, ap);
va_end(ap);
return (ret);
struct __printf_output out = {
.fd = fd,
.size = 65536,
};
va_list args;
va_start(args, fmt);
int count = __printf(&out, fmt, &args);
va_end(args);
return count;
}

18
src/libc/stdio/fprintf.c

@ -0,0 +1,18 @@
#include <stdio.h>
#include <fxlibc/printf.h>
int fprintf(FILE * restrict fp, char const * restrict fmt, ...)
{
struct __printf_output out = {
.fp = fp,
.size = 65536,
};
va_list args;
va_start(args, fmt);
int count = __printf(&out, fmt, &args);
va_end(args);
return count;
}

58
src/libc/stdio/internal/printf.h

@ -1,58 +0,0 @@
#ifndef _SRC_STDIO_INTERNAL_PRINTF_H__
#define _SRC_STDIO_INTERNAL_PRINTF_H__
#include <stddef.h>
#include <stdint.h>
//---
// Internal printf() part
//---
#define PRINTF_INTERNAL_BUFFER_SIZE 32
// internal structure used by any printf function familly
struct printf_opt
{
// Internal buffer to avoid syscall flux
char buffer[PRINTF_INTERNAL_BUFFER_SIZE];
size_t buffer_cursor;
// Common part
int counter;
va_list ap;
// *dprintf part
int fd;
// *sprintf part
char *str;
size_t str_size;
// For string / fd common support
void (*disp_char)(struct printf_opt *opt, char n);
void (*disp_fflush)(struct printf_opt *opt);
// Printf-options
struct {
uint8_t diez : 1;
uint8_t zero : 1;
uint8_t minus : 1;
uint8_t space : 1;
uint8_t plus : 1;
uint8_t const : 3;
} flags;
int width;
int precision;
int uppercase;
int lenght;
// Internal format management.
char sign;
char base[2];
char format[32];
int digits;
};
// Internal symbols used to define all actions possibility
extern void (*action[26])(struct printf_opt *opt, char n);
#endif /*_SRC_STDIO_INTERNAL_PRINTF_H__*/

208
src/libc/stdio/internal/printf_actions.c

@ -1,208 +0,0 @@
#include <stdio.h>
// internal depency
#include "printf.h"
// Define all actions
static void action_str(struct printf_opt *op, char n);
static void action_ptr(struct printf_opt *op, char n);
static void action_int(struct printf_opt *op, char n);
static void action_uint(struct printf_opt *op, char n);
static void action_char(struct printf_opt *op, char n);
// Define all actions which can be used
void (*action[26])(struct printf_opt *opt, char n) = {
NULL, NULL, action_char, action_int,
NULL, NULL, NULL, NULL,
action_int, NULL, NULL, NULL,
NULL, NULL, action_uint, action_ptr,
NULL, NULL, action_str, NULL,
action_uint, NULL, NULL, action_uint,
NULL, NULL,
};
//---
// Disp part
//---
static void base_to_str(struct printf_opt *opt, uint32_t num, int base,
int digits)
{
char *hexa = "0123456789abcdef";
if (opt->uppercase == 1)
hexa = "0123456789ABCDEF";
opt->digits = 0;
while (num != 0 || opt->digits < digits) {
opt->format[opt->digits++] = hexa[num % base];
num = num / base;
}
}
static void disp_format(struct printf_opt *opt)
{
// display pre symbols
if (opt->sign != '\0')
(*opt->disp_char)(opt, opt->sign);
if (opt->base[0] != '\0')
(*opt->disp_char)(opt, opt->base[0]);
if (opt->base[1] != '\0')
(*opt->disp_char)(opt, opt->base[1]);
// padding
if (opt->flags.minus == 1 && opt->width > opt->digits) {
int total = opt->digits + (opt->sign != '\0') +
(opt->base[0] != '\0') + (opt->base[1] != '\0');
total = opt->width - total;
while (--total >= 0)
(*opt->disp_char)(opt,
(opt->flags.zero == 1) ? '0' : ' ');
}
// Display number
int saved_digits = opt->digits;
while (--opt->digits >= 0)
(*opt->disp_char)(opt, opt->format[opt->digits]);
// padding
if (opt->flags.minus == 0 && opt->width > saved_digits) {
int total = saved_digits + (opt->sign != '\0') +
(opt->base[0] != '\0') + (opt->base[1] != '\0');
total = opt->width - total;
while (--total >= 0)
(*opt->disp_char)(opt, ' ');
}
}
//---
// Args part
//---
static uint32_t get_arg_i(struct printf_opt *opt)
{
switch (opt->lenght) {
case 0:
return ((signed char)va_arg(opt->ap, int));
case 1:
return ((short int)va_arg(opt->ap, int));
case 2:
return (va_arg(opt->ap, long int));
case 3:
return (va_arg(opt->ap, long long int));
case 4:
return (va_arg(opt->ap, intmax_t));
case 5:
return (va_arg(opt->ap, size_t));
case 6:
return (va_arg(opt->ap, ptrdiff_t));
}
return (va_arg(opt->ap, int));
}
static uint32_t get_arg_u(struct printf_opt *opt)
{
switch (opt->lenght) {
case 0:
return ((unsigned char)va_arg(opt->ap, int));
case 1:
return ((unsigned short int)va_arg(opt->ap, int));
case 2:
return (va_arg(opt->ap, unsigned long int));
case 3:
return (va_arg(opt->ap, unsigned long long int));
case 4:
return (va_arg(opt->ap, intmax_t));
case 5:
return (va_arg(opt->ap, size_t));
case 6:
return (va_arg(opt->ap, ptrdiff_t));
}
return (va_arg(opt->ap, unsigned int));
}
//---
// Actions part.
//---
static void action_str(struct printf_opt *opt, char n)
{
const char *str;
(void)n;
str = va_arg(opt->ap, const char *);
while (*str != '\0')
(*opt->disp_char)(opt, *(str++));
}
static void action_char(struct printf_opt *opt, char n)
{
n = (char)va_arg(opt->ap, int);
(*opt->disp_char)(opt, n);
}
static void action_ptr(struct printf_opt *opt, char n)
{
(void)n;
opt->sign = '@';
opt->base[0] = '0';
opt->base[1] = 'x';
base_to_str(opt, (uintptr_t)va_arg(opt->ap, void*), 16, 8);
disp_format(opt);
}
static void action_int(struct printf_opt *opt, char n)
{
int64_t num;
// Get data and check negative value
// FIXME: max negative value can not be reversed
(void)n;
num = get_arg_i(opt);
if (num < 0) {
opt->sign = '-';
num = -num;
} else if (opt->flags.space == 1 || opt->flags.plus == 1) {
opt->sign = (opt->flags.plus == 1) ? '+' : ' ';
}
// Generate / display number
base_to_str(opt, num, 10, 1);
disp_format(opt);
}
static void action_uint(struct printf_opt *opt, char n)
{
uint32_t num;
int base;
// Get appropriate base
switch (n) {
case 'o':
base = 8;
break;
case 'x':
base = 16;
break;
default:
base = 10;
break;
}
// Display extra symbols if needed
if (opt->flags.diez == 1) {
if (n == 'o') {
opt->base[0] = '0';
} else if (n == 'x') {
opt->base[0] = '0';
opt->base[1] = (opt->uppercase == 1) ? 'X' : 'x';
}
}
// Get number
num = get_arg_u(opt);
// Generate / display number
base_to_str(opt, num, base, 1);
disp_format(opt);
}

48
src/libc/stdio/internal/printf_common.c

@ -1,48 +0,0 @@
#include <stdio.h>
// internal depency
#include "printf.h"
//TODO: precision handling
int printf_common(struct printf_opt *opt, const char *restrict format)
{
extern int printf_get_options(struct printf_opt *opt,
const char *restrict format);
int saved_p;
char tmp;
int p;
p = -1;
opt->counter = 0;
opt->buffer_cursor = 0;
while (format[++p] != '\0') {
// Check printable char
if (format[p] != '%' || format[p + 1] == '%') {
tmp = format[p];
if (format[p] == '%')
p = p + 1;
(*opt->disp_char)(opt,tmp);
continue;
}
// Get options
saved_p = p;
p = p + printf_get_options(opt, &format[p + 1]);
// Check arg validity
if (((format[p + 1] >= 'a' && format[p + 1] <= 'z') ||
(format[p + 1] >= 'A' && format[p + 1] <= 'Z')) &&
action[(format[p + 1] | 0x20) - 'a'] != NULL) {
tmp = (format[p + 1] | 0x20) - 'a';
(*action[(int)tmp]) (opt,tmp);
p = p + 1;
continue;
}
// Default, print the %
(*opt->disp_char)(opt, '%');
p = saved_p;
}
(*opt->disp_fflush)(opt);
return (opt->counter);
}

127
src/libc/stdio/internal/printf_options.c

@ -1,127 +0,0 @@
#include <stdio.h>
// internal depency
#include "printf.h"
static int get_flags(struct printf_opt *opt, const char *restrict format)
{
int i;
i = -1;
opt->flags.diez = 0;
opt->flags.zero = 0;
opt->flags.minus = 0;
opt->flags.space = 0;
opt->flags.plus = 0;
while (format[++i] != '\0') {
switch (format[i]) {
case '#':
opt->flags.diez = 1;
break;
case '0':
opt->flags.zero = 1;
break;
case '-':
opt->flags.minus = 1;
break;
case ' ':
opt->flags.space = 1;
break;
case '+':
opt->flags.plus = 1;
break;
default:
return (i);
}
}
return (i);
}
static int get_width(struct printf_opt *opt, const char *restrict format)
{
// Check dynamic width
if (format[0] == '*') {
opt->width = va_arg(opt->ap, int);
return (1);
}
// Check error
int i = -1;
opt->width = 0;
if (format[0] == '0')
return (0);
// Get static width
while (format[++i] >= '0' && format[i] <= '9')
opt->width = (opt->width * 10) + (format[i] - '0');
return (i);
}
static int get_precision(struct printf_opt *opt, const char *restrict format)
{
// Check if precision is specified
if (format[0] != '.')
return (0);
// Check dynamic precision
if (format[1] == '*') {
opt->precision = va_arg(opt->ap, int);
return (2);
}
// Get static precision
int i = 0;
opt->precision = 0;
while (format[++i] >= '0' && format[i] <= '9')
opt->precision = (opt->precision * 10) + (format[i] - '0');
// Check default precision
if (i == 0)
opt->precision = 1;
return (i);
}
static int get_lenght(struct printf_opt *opt, const char *restrict format)
{
opt->lenght = -1;
switch (format[0]) {
case 'h':
opt->lenght = (format[1] == 'h') ? 1 : 0;
break;
case 'l':
opt->lenght = (format[1] == 'l') ? 3 : 2;
break;
case 'j':
opt->lenght = 4;
break;
case 'z':
opt->lenght = 5;
break;
case 't':
opt->lenght = 6;
break;
default:
return (0);
}
return ((opt->lenght == 1 || opt->lenght == 3) ? 2 : 1);
}
int printf_get_options(struct printf_opt *opt, const char *restrict format)
{
int i;
// Wipe internal format infos
opt->sign = '\0';
opt->base[0] = '\0';
opt->base[1] = '\0';
// Get generals opetions
i = get_flags(opt, &format[0]);
i += get_width(opt, &format[i]);
i += get_precision(opt, &format[i]);
i += get_lenght(opt, &format[i]);
// Check upper case actions
opt->uppercase = (format[i] == 'X') ? 1 : 0;
return (i);
}

27
src/libc/stdio/printf.c

@ -1,19 +1,18 @@
#include <stdio.h>
#include <unistd.h>
#include <fxlibc/printf.h>
/*
** printf() write the output under the control of a format string that specifies
** how subsequent arguments (or arguments accessed via the variable-length
** argument facilities of stdarg(3)) are converted for output then write to
** the STDOUT.
*/
int printf(const char *restrict format, ...)
int printf(char const * restrict fmt, ...)
{
va_list ap;
int ret;
struct __printf_output out = {
.fp = stdout,
.size = 65536,
};
va_start(ap, format);
ret = vdprintf(STDOUT_FILENO, format, ap);
va_end(ap);
return (ret);
va_list args;
va_start(args, fmt);
int count = __printf(&out, fmt, &args);
va_end(args);
return count;
}

295
src/libc/stdio/printf/format_fp.c

@ -0,0 +1,295 @@
#include <stddef.h>
#include <fxlibc/printf.h>
#include "../../../../3rdparty/grisu2b_59_56/grisu2.h"
#define min(x, y) ({ \
__auto_type _x = (x); \
__auto_type _y = (y); \
(_x < _y) ? (_x) : (_y); \
})
//---
// String generation for doubles
//---
/* The grisu2() function does not have a size limit so we generate into a
buffer large enough to hold the result. */
static char digit_buffer[50];
/* Round a number at the specified place (which can be out of bounds). An extra
byte before the buffer should be secured to leave room for a new digit in
case a carry reaches there. Returns the new start-of-buffer.
@buffer Buffer with generated digits, updated if needed
@length Number of digits generated in the buffer, updated if needed
@e Location of decimal dot relative to integer in buffer
@place Decimal to place to round to */
static void round_str(char **buffer_ptr, int *length, int e, int place)
{
char *buffer = *buffer_ptr;
/* Interpret place as relative to buffer indices */
place += *length + e - 1;
/* Specified place is out-of-bounds */
if(place < 0 || place >= *length - 1) return;
/* Next digit is 0..4 so rounding has no effect */
if(buffer[place + 1] < '5') return;
/* Propagate carries if needed */
while(place >= -1)
{
buffer[place]++;
if(buffer[place] <= '9') break;
buffer[place] = '0';
place--;
}
/* Add one digit if needed */
if(place >= 0) return;
(*buffer_ptr)--;
(*length)++;
}
/* Remove zeros at the end of the digits, reducing [length] accordingly. */
static int remove_zeros(char *buffer, int length, int unremovable)
{
int removed = 0;
while(length > unremovable && buffer[length - 1] == '0')
{
buffer[length - 1] = 0;
length--;
removed++;
}
return removed;
}
/* Handles infinities and NaNs. */
static int special_notation(struct __printf_output *out, double v, int upper)
{
if(__builtin_isinf(v) && v < 0)
{
__printf_outstr(out, upper ? "-INF" : "-inf", 4);
return 1;
}
if(__builtin_isinf(v))
{
__printf_outstr(out, upper ? "INF" : "inf", 3);
return 1;
}
if(__builtin_isnan(v))
{
__printf_outstr(out, upper ? "NAN" : "nan", 3);
return 1;
}
return 0;
}
/* Prints decimal explicitly for %f and %g. */
static void direct_notation(
struct __printf_output *out,
struct __printf_format *opt,
struct __printf_geometry g,
char *digits, int length, int e)
{
/* Number of characters for decimal part, including dot */
int dec_chars = opt->precision + (opt->precision > 0);
/* See case discussion below */
g.content = (length + e >= 0) ? length + e + dec_chars : 1 + dec_chars;
__printf_compute_geometry(opt, &g);
__printf_outn(out, ' ', g.left_spaces);
if(g.sign) __printf_out(out, g.sign);
__printf_outn(out, '0', g.zeros);
int pre = opt->precision;
if(e >= 0) /* xxxxxx00[.00] */
{
/* Decimal dot is after digits; rounding never occurs */
__printf_outstr(out, digits, length);
__printf_outn(out, '0', e);
if(pre > 0)
{
__printf_out(out, '.');
__printf_outn(out, '0', pre);