diff --git a/ckb_cell_fs.h b/ckb_cell_fs.h new file mode 100644 index 0000000..04fd647 --- /dev/null +++ b/ckb_cell_fs.h @@ -0,0 +1,45 @@ +#ifndef CKB_C_STDLIB_CKB_CELL_FS_H +#define CKB_C_STDLIB_CKB_CELL_FS_H 1 + +typedef struct FSBlob { + uint32_t offset; + uint32_t length; +} FSBlob; + +typedef struct FSEntry { + FSBlob filename; + FSBlob content; +} FSEntry; + +typedef struct CellFileSystemNode { + uint32_t count; + FSEntry *files; + void *start; +} CellFileSystemNode; + +typedef struct CellFileSystem { + CellFileSystemNode *current; + struct CellFileSystem *next; +} CellFileSystem; + +typedef struct FSFile { + const char *filename; + const void *content; + uint32_t size; + // indicate how many active users there are, used to avoid excessive opening + // of the same file. + // Currently the only valid values are 1 and 0. + uint8_t rc; +} FSFile; + +int get_file(const CellFileSystem *fs, const char *filename, FSFile **f); + +int ckb_get_file(const char *filename, FSFile **file); + +int load_fs(CellFileSystem **fs, void *buf, uint64_t buflen); + +int ckb_load_fs(void *buf, uint64_t buflen); + +void ckb_reset_fs(); + +#endif diff --git a/libc/assert.h b/libc/assert.h new file mode 100644 index 0000000..6a2cf81 --- /dev/null +++ b/libc/assert.h @@ -0,0 +1,14 @@ +#ifndef CKB_C_STDLIB_ASSERT_H_ +#define CKB_C_STDLIB_ASSERT_H_ + +#include "ckb_syscall_apis.h" + +#define assert(s) \ + do { \ + if (!(s)) { \ + printf("Failed at %s:%d: %s\n", __FILE__, __LINE__, (#s)); \ + ckb_exit(-1); \ + } \ + } while (0) + +#endif // CKB_C_STDLIB_ASSERT_H_ diff --git a/libc/ctype.h b/libc/ctype.h new file mode 100644 index 0000000..0b3bded --- /dev/null +++ b/libc/ctype.h @@ -0,0 +1,16 @@ +#ifndef CKB_C_STDLIB_CTYPE_H_ +#define CKB_C_STDLIB_CTYPE_H_ +#ifdef __cplusplus +extern "C" { +#endif + +int islower(int); +int isupper(int); + +int tolower(int); +int toupper(int); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libc/errno.h b/libc/errno.h new file mode 100644 index 0000000..9e54e13 --- /dev/null +++ b/libc/errno.h @@ -0,0 +1,18 @@ +#ifndef CKB_C_STDLIB_ERRNO_H_ +#define CKB_C_STDLIB_ERRNO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +__attribute__((const)) +#endif +int *__errno_location(void); +#define errno (*__errno_location()) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/features.h b/libc/features.h new file mode 100644 index 0000000..654ec6c --- /dev/null +++ b/libc/features.h @@ -0,0 +1,39 @@ +#ifndef CKB_C_STDLIB_FEATURES_H_ +#define CKB_C_STDLIB_FEATURES_H_ + +#if defined(_ALL_SOURCE) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE 1 +#endif + +#if defined(_DEFAULT_SOURCE) && !defined(_BSD_SOURCE) +#define _BSD_SOURCE 1 +#endif + +#if !defined(_POSIX_SOURCE) && !defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE) && !defined(_GNU_SOURCE) && \ + !defined(_BSD_SOURCE) && !defined(__STRICT_ANSI__) +#define _BSD_SOURCE 1 +#define _XOPEN_SOURCE 700 +#endif + +#if __STDC_VERSION__ >= 199901L +#define __restrict restrict +#elif !defined(__GNUC__) +#define __restrict +#endif + +#if __STDC_VERSION__ >= 199901L || defined(__cplusplus) +#define __inline inline +#elif !defined(__GNUC__) +#define __inline +#endif + +#if __STDC_VERSION__ >= 201112L +#elif defined(__GNUC__) +#define _Noreturn __attribute__((__noreturn__)) +#else +#define _Noreturn +#endif + +#define __REDIR(x, y) __typeof__(x) x __asm__(#y) + +#endif diff --git a/libc/float.h b/libc/float.h new file mode 100644 index 0000000..745b908 --- /dev/null +++ b/libc/float.h @@ -0,0 +1,125 @@ +#ifndef CKB_C_STDLIB_FLOAT_H_ +#define CKB_C_STDLIB_FLOAT_H_ + +#include + +#undef FLT_MAX +#undef DBL_MAX +#undef LDBL_MAX +#define FLT_MAX __FLT_MAX__ +#define DBL_MAX __DBL_MAX__ +#define LDBL_MAX __LDBL_MAX__ + +typedef union { + double value; + struct { + uint32_t lsw; + uint32_t msw; + } parts; + struct { + uint64_t w; + } xparts; +} ieee_double_shape_type; + +/* Get two 32 bit ints from a double. */ + +#define EXTRACT_WORDS(ix0, ix1, d) \ + do { \ + ieee_double_shape_type ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ + } while (0) + +/* Get the more significant 32 bit int from a double. */ + +#define GET_HIGH_WORD(i, d) \ + do { \ + ieee_double_shape_type gh_u; \ + gh_u.value = (d); \ + (i) = gh_u.parts.msw; \ + } while (0) + +/* Get the less significant 32 bit int from a double. */ + +#define GET_LOW_WORD(i, d) \ + do { \ + ieee_double_shape_type gl_u; \ + gl_u.value = (d); \ + (i) = gl_u.parts.lsw; \ + } while (0) + +/* Set the more significant 32 bits of a double from an int. */ + +#define SET_HIGH_WORD(d, v) \ + do { \ + ieee_double_shape_type sh_u; \ + sh_u.value = (d); \ + sh_u.parts.msw = (v); \ + (d) = sh_u.value; \ + } while (0) + +/* Set the less significant 32 bits of a double from an int. */ + +#define SET_LOW_WORD(d, v) \ + do { \ + ieee_double_shape_type sl_u; \ + sl_u.value = (d); \ + sl_u.parts.lsw = (v); \ + (d) = sl_u.value; \ + } while (0) + +static const double ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ + ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ + two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */ + twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ + huge = 1.0e+300, tiny = 1.0e-300, Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ + Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ + Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ + Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ + Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ + Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ + Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +static const double zero = 0.0; + +static const double bp[] = + { + 1.0, + 1.5, +}, + dp_h[] = + { + 0.0, + 5.84962487220764160156e-01, +}, /* 0x3FE2B803, 0x40000000 */ + dp_l[] = + { + 0.0, + 1.35003920212974897128e-08, +}, /* 0x3E4CFDEB, 0x43CFD006 */ + one = 1.0, two = 2.0, two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ + L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ + L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ + L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ + L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ + L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ + L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ + P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ + P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ + P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ + P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ + P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */ + lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ + lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ + lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ + ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */ + cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ + cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ + cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ + ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ + ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ + ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ + +#endif diff --git a/libc/inttypes.h b/libc/inttypes.h new file mode 100644 index 0000000..8210d6f --- /dev/null +++ b/libc/inttypes.h @@ -0,0 +1,111 @@ +#ifndef CKB_C_STDLIB_INTTYPES_H_ +#define CKB_C_STDLIB_INTTYPES_H_ + +#include + +#if UINTPTR_MAX == UINT64_MAX +#define __PRI64 "l" +#define __PRIPTR "l" +#else +#define __PRI64 "ll" +#define __PRIPTR "" +#endif + +#define PRId8 "d" +#define PRId16 "d" +#define PRId32 "d" +#define PRId64 __PRI64 "d" + +#define PRIdLEAST8 "d" +#define PRIdLEAST16 "d" +#define PRIdLEAST32 "d" +#define PRIdLEAST64 __PRI64 "d" + +#define PRIdFAST8 "d" +#define PRIdFAST16 "d" +#define PRIdFAST32 "d" +#define PRIdFAST64 __PRI64 "d" + +#define PRIi8 "i" +#define PRIi16 "i" +#define PRIi32 "i" +#define PRIi64 __PRI64 "i" + +#define PRIiLEAST8 "i" +#define PRIiLEAST16 "i" +#define PRIiLEAST32 "i" +#define PRIiLEAST64 __PRI64 "i" + +#define PRIiFAST8 "i" +#define PRIiFAST16 "i" +#define PRIiFAST32 "i" +#define PRIiFAST64 __PRI64 "i" + +#define PRIo8 "o" +#define PRIo16 "o" +#define PRIo32 "o" +#define PRIo64 __PRI64 "o" + +#define PRIoLEAST8 "o" +#define PRIoLEAST16 "o" +#define PRIoLEAST32 "o" +#define PRIoLEAST64 __PRI64 "o" + +#define PRIoFAST8 "o" +#define PRIoFAST16 "o" +#define PRIoFAST32 "o" +#define PRIoFAST64 __PRI64 "o" + +#define PRIu8 "u" +#define PRIu16 "u" +#define PRIu32 "u" +#define PRIu64 __PRI64 "u" + +#define PRIuLEAST8 "u" +#define PRIuLEAST16 "u" +#define PRIuLEAST32 "u" +#define PRIuLEAST64 __PRI64 "u" + +#define PRIuFAST8 "u" +#define PRIuFAST16 "u" +#define PRIuFAST32 "u" +#define PRIuFAST64 __PRI64 "u" + +#define PRIx8 "x" +#define PRIx16 "x" +#define PRIx32 "x" +#define PRIx64 __PRI64 "x" + +#define PRIxLEAST8 "x" +#define PRIxLEAST16 "x" +#define PRIxLEAST32 "x" +#define PRIxLEAST64 __PRI64 "x" + +#define PRIxFAST8 "x" +#define PRIxFAST16 "x" +#define PRIxFAST32 "x" +#define PRIxFAST64 __PRI64 "x" + +#define PRIX8 "X" +#define PRIX16 "X" +#define PRIX32 "X" +#define PRIX64 __PRI64 "X" + +#define PRIXLEAST8 "X" +#define PRIXLEAST16 "X" +#define PRIXLEAST32 "X" +#define PRIXLEAST64 __PRI64 "X" + +#define PRIXFAST8 "X" +#define PRIXFAST16 "X" +#define PRIXFAST32 "X" +#define PRIXFAST64 __PRI64 "X" + +#define PRIdMAX __PRI64 "d" +#define PRIiMAX __PRI64 "i" +#define PRIoMAX __PRI64 "o" +#define PRIuMAX __PRI64 "u" +#define PRIxMAX __PRI64 "x" +#define PRIXMAX __PRI64 "X" + +#endif diff --git a/libc/locale.h b/libc/locale.h new file mode 100644 index 0000000..88f6cef --- /dev/null +++ b/libc/locale.h @@ -0,0 +1,41 @@ +#ifndef CKB_C_STDLIB_LOCALE_H_ +#define CKB_C_STDLIB_LOCALE_H_ +#ifdef __cplusplus +extern "C" { +#endif + +struct lconv { + char *decimal_point; + char *thousands_sep; + char *grouping; + + char *int_curr_symbol; + char *currency_symbol; + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; + char int_p_cs_precedes; + char int_p_sep_by_space; + char int_n_cs_precedes; + char int_n_sep_by_space; + char int_p_sign_posn; + char int_n_sign_posn; +}; + +struct lconv *localeconv(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/malloc.h b/libc/malloc.h new file mode 100644 index 0000000..ba8ccad --- /dev/null +++ b/libc/malloc.h @@ -0,0 +1,10 @@ +#ifndef CKB_C_STDLIB_MALLOC_H_ +#define CKB_C_STDLIB_MALLOC_H_ + +#include + +size_t malloc_usable_size(void *ptr); +void malloc_config(uintptr_t min, uintptr_t max); +size_t malloc_usage(); + +#endif // CKB_C_STDLIB_MALLOC_H_ diff --git a/libc/math.h b/libc/math.h new file mode 100644 index 0000000..66e9c94 --- /dev/null +++ b/libc/math.h @@ -0,0 +1,110 @@ +#ifndef CKB_C_STDLIB_MATH_H_ +#define CKB_C_STDLIB_MATH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NAN (__builtin_nanf("")) +#define INFINITY (__builtin_inff()) + +/* fp_force_eval ensures that the input value is computed when that's + otherwise unused. To prevent the constant folding of the input + expression, an additional fp_barrier may be needed or a compilation + mode that does so (e.g. -frounding-math in gcc). Then it can be + used to evaluate an expression for its fenv side-effects only. */ + +#ifndef fp_force_evalf +#define fp_force_evalf fp_force_evalf +static inline void fp_force_evalf(float x) { + volatile float y; + y = x; + (void)y; +} +#endif + +#ifndef fp_force_eval +#define fp_force_eval fp_force_eval +static inline void fp_force_eval(double x) { + volatile double y; + y = x; + (void)y; +} +#endif + +#ifndef fp_force_evall +#define fp_force_evall fp_force_evall +static inline void fp_force_evall(long double x) { + volatile long double y; + y = x; + (void)y; +} +#endif + +#define FORCE_EVAL(x) \ + do { \ + if (sizeof(x) == sizeof(float)) { \ + fp_force_evalf(x); \ + } else if (sizeof(x) == sizeof(double)) { \ + fp_force_eval(x); \ + } else { \ + fp_force_evall(x); \ + } \ + } while (0) + +double acos(double); + +double asin(double); + +double atan2(double, double); + +double cos(double); + +double cosh(double); + +double exp(double); + +double fabs(double); + +double log(double); + +double log2(double); + +double log10(double); + +double pow(double, double); + +double sin(double); + +double sinh(double); + +double sqrt(double); + +double tan(double); + +double tanh(double); + +double scalbn(double, int); + +double ldexp(double, int); + +double round(double x); +int isnan(double x); +int isfinite(double x); +double trunc(double x); +long int lrint(double x); +double floor(double x); +double cbrt(double x); +double fmod(double numer, double denom); +double fmin(double x, double y); +double fmax(double x, double y); +double hypot(double x, double y); +int signbit(double num); + +double ceil(double x); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/setjmp.h b/libc/setjmp.h new file mode 100644 index 0000000..c65aaad --- /dev/null +++ b/libc/setjmp.h @@ -0,0 +1,46 @@ +#ifndef CKB_C_STDLIB_SETJMP_H_ +#define CKB_C_STDLIB_SETJMP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// #include + +typedef unsigned long __jmp_buf[26]; + +typedef struct __jmp_buf_tag { + __jmp_buf __jb; + unsigned long __fl; + unsigned long __ss[128 / sizeof(long)]; +} jmp_buf[1]; + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +#define __setjmp_attr __attribute__((__returns_twice__)) +#else +#define __setjmp_attr +#endif + +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || \ + defined(_BSD_SOURCE) +typedef jmp_buf sigjmp_buf; +int sigsetjmp(sigjmp_buf, int) __setjmp_attr; +_Noreturn void siglongjmp(sigjmp_buf, int); +#endif + +int _setjmp(jmp_buf) __setjmp_attr; + +_Noreturn void _longjmp(jmp_buf, int); + +int setjmp(jmp_buf) __setjmp_attr; +_Noreturn void longjmp(jmp_buf, int); + +#define setjmp setjmp + +#undef __setjmp_attr + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/src/ckb_cell_fs.c b/libc/src/ckb_cell_fs.c new file mode 100644 index 0000000..6a3060e --- /dev/null +++ b/libc/src/ckb_cell_fs.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#include "ckb_cell_fs.h" + +static CellFileSystem *CELL_FILE_SYSTEM = NULL; + +int get_file(const CellFileSystem *fs, const char *filename, FSFile **f) { + if (fs == NULL) { + return -1; + } + FSFile *file = malloc(sizeof(FSFile)); + if (file == 0) { + return -1; + } + CellFileSystem *cfs = (CellFileSystem *)fs; + CellFileSystemNode *node = cfs->current; + while (node != NULL) { + for (uint32_t i = 0; i < node->count; i++) { + FSEntry entry = node->files[i]; + if (strcmp(filename, node->start + entry.filename.offset) == 0) { + // TODO: check the memory addresses are legal + file->filename = filename; + file->size = entry.content.length; + file->content = node->start + entry.content.offset; + file->rc = 1; + *f = file; + return 0; + } + } + if (cfs->next == NULL) { + break; + } + cfs = cfs->next; + node = cfs->current; + } + free(file); + return -1; +} + +int ckb_get_file(const char *filename, FSFile **file) { return get_file(CELL_FILE_SYSTEM, filename, file); } + +int load_fs(CellFileSystem **fs, void *buf, uint64_t buflen) { + if (fs == NULL || buf == NULL) { + return -1; + } + + CellFileSystemNode *node = (CellFileSystemNode *)malloc(sizeof(CellFileSystemNode)); + if (node == NULL) { + return -1; + } + + CellFileSystem *newfs = (CellFileSystem *)malloc(sizeof(CellFileSystem)); + if (newfs == NULL) { + free(node); + return -1; + } + + node->count = *(uint32_t *)buf; + if (node->count == 0) { + node->files = NULL; + node->start = NULL; + newfs->next = *fs; + newfs->current = node; + *fs = newfs; + return 0; + } + + node->files = (FSEntry *)malloc(sizeof(FSEntry) * node->count); + if (node->files == NULL) { + free(node); + free(newfs); + return -1; + } + node->start = buf + sizeof(node->count) + (sizeof(FSEntry) * node->count); + + FSEntry *entries = (FSEntry *)((char *)buf + sizeof(node->count)); + for (uint32_t i = 0; i < node->count; i++) { + FSEntry entry = entries[i]; + node->files[i] = entry; + } + + newfs->next = *fs; + newfs->current = node; + *fs = newfs; + return 0; +} + +int ckb_load_fs(void *buf, uint64_t buflen) { + int ret = load_fs(&CELL_FILE_SYSTEM, buf, buflen); + return ret; +} + +void ckb_reset_fs() { CELL_FILE_SYSTEM = NULL; } diff --git a/libc/src/ctype.c b/libc/src/ctype.c new file mode 100644 index 0000000..7110690 --- /dev/null +++ b/libc/src/ctype.c @@ -0,0 +1,15 @@ +#include + +int islower(int c) { return (unsigned)c - 'a' < 26; } + +int isupper(int c) { return (unsigned)c - 'A' < 26; } + +int tolower(int c) { + if (isupper(c)) return c | 32; + return c; +} + +int toupper(int c) { + if (islower(c)) return c & 0x5f; + return c; +} diff --git a/libc/src/locale.c b/libc/src/locale.c new file mode 100644 index 0000000..1967ff1 --- /dev/null +++ b/libc/src/locale.c @@ -0,0 +1,31 @@ +#include +#include + +static const struct lconv posix_lconv = { + .decimal_point = ".", + .thousands_sep = "", + .grouping = "", + .int_curr_symbol = "", + .currency_symbol = "", + .mon_decimal_point = "", + .mon_thousands_sep = "", + .mon_grouping = "", + .positive_sign = "", + .negative_sign = "", + .int_frac_digits = CHAR_MAX, + .frac_digits = CHAR_MAX, + .p_cs_precedes = CHAR_MAX, + .p_sep_by_space = CHAR_MAX, + .n_cs_precedes = CHAR_MAX, + .n_sep_by_space = CHAR_MAX, + .p_sign_posn = CHAR_MAX, + .n_sign_posn = CHAR_MAX, + .int_p_cs_precedes = CHAR_MAX, + .int_p_sep_by_space = CHAR_MAX, + .int_n_cs_precedes = CHAR_MAX, + .int_n_sep_by_space = CHAR_MAX, + .int_p_sign_posn = CHAR_MAX, + .int_n_sign_posn = CHAR_MAX, +}; + +struct lconv *localeconv(void) { return (void *)&posix_lconv; } diff --git a/libc/src/malloc.c b/libc/src/malloc.c new file mode 100644 index 0000000..86cb288 --- /dev/null +++ b/libc/src/malloc.c @@ -0,0 +1,398 @@ +#define CKB_MALLOC_DECLARATION_ONLY 1 +#include +#include +#include +#include + +#ifndef CKB_BRK_MIN +extern char _end[]; /* _end is set in the linker */ +#define CKB_BRK_MIN ((uintptr_t)&_end) +#endif +#ifndef CKB_BRK_MAX +#define CKB_BRK_MAX 0x00300000 +#endif + +struct chunk { + size_t psize, csize; + struct chunk *next, *prev; +}; + +struct bin { + volatile int lock[2]; + struct chunk *head; + struct chunk *tail; +}; + +#define CKB_SIZE_ALIGN (4 * sizeof(size_t)) +#define CKB_SIZE_MASK (-CKB_SIZE_ALIGN) +#define CKB_OVERHEAD (2 * sizeof(size_t)) +#define CKB_DONTCARE 16 +#define CKB_RECLAIM 163840 +#define CKB_MMAP_THRESHOLD (0x1c00 * CKB_SIZE_ALIGN) + +#define CKB_CHUNK_SIZE(c) ((c)->csize & -2) +#define CKB_CHUNK_PSIZE(c) ((c)->psize & -2) +#define CKB_PREV_CHUNK(c) ((struct chunk *)((char *)(c)-CKB_CHUNK_PSIZE(c))) +#define CKB_NEXT_CHUNK(c) ((struct chunk *)((char *)(c) + CKB_CHUNK_SIZE(c))) +#define CKB_MEM_TO_CHUNK(p) (struct chunk *)((char *)(p)-CKB_OVERHEAD) +#define CKB_CHUNK_TO_MEM(c) (void *)((char *)(c) + CKB_OVERHEAD) +#define CKB_BIN_TO_CHUNK(i) (CKB_MEM_TO_CHUNK(&mal.bins[i].head)) +#define CKB_C_INUSE ((size_t)1) +#define CKB_IS_MMAPPED(c) !((c)->csize & (CKB_C_INUSE)) +#define CKB_PAGE_SIZE 4096 +void __bin_chunk(struct chunk *); +int ckb_exit(int8_t code); +static inline void a_crash() { ckb_exit(-1); } +void free(void *p); + +static inline void a_and_64(volatile uint64_t *p, uint64_t v) { *p &= v; } + +static inline void a_or_64(volatile uint64_t *p, uint64_t v) { *p |= v; } + +static uintptr_t s_program_break = 0; +static uintptr_t s_brk_min = CKB_BRK_MIN; +static uintptr_t s_brk_max = CKB_BRK_MAX; + +void malloc_config(uintptr_t min, uintptr_t max) { + s_brk_min = min; + s_brk_max = max; + s_program_break = 0; +} + +size_t malloc_usage() { + size_t high = (size_t)s_program_break; + size_t low = (size_t)s_brk_min; + return high - low; +} + +void *_sbrk(uintptr_t incr) { + if (!s_program_break) { + s_program_break = s_brk_min; + s_program_break += -s_program_break & (CKB_PAGE_SIZE - 1); + } + if ((s_program_break + incr) > s_brk_max) { + return (void *)-1; + } + + uintptr_t start = s_program_break; + s_program_break += incr; + return (void *)start; +} + +static struct { + volatile uint64_t binmap; + struct bin bins[64]; + volatile int split_merge_lock[2]; +} mal; + +static inline void lock_bin(int i) { + if (!mal.bins[i].head) mal.bins[i].head = mal.bins[i].tail = CKB_BIN_TO_CHUNK(i); +} + +static inline void unlock_bin(int i) {} + +#if 0 +static int first_set(uint64_t x) { + // TODO: use RISC-V asm + static const char debruijn64[64] = { + 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, + 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, + 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, + 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12}; + static const char debruijn32[32] = { + 0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, 11, 20, 8, 4, 13, + 31, 22, 28, 18, 26, 10, 7, 12, 21, 17, 9, 6, 16, 5, 15, 14}; + if (sizeof(long) < 8) { + uint32_t y = x; + if (!y) { + y = x >> 32; + return 32 + debruijn32[(y & -y) * 0x076be629 >> 27]; + } + return debruijn32[(y & -y) * 0x076be629 >> 27]; + } + return debruijn64[(x & -x) * 0x022fdd63cc95386dull >> 58]; +} + +#else + +static int __attribute__((naked)) first_set(uint64_t x) { + __asm__(".byte 0x13, 0x15, 0x15, 0x60"); + __asm__("ret"); +} + +#endif + +static const unsigned char bin_tab[60] = { + 32, 33, 34, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, + 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, + 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, +}; + +static int bin_index(size_t x) { + x = x / CKB_SIZE_ALIGN - 1; + if (x <= 32) return x; + if (x < 512) return bin_tab[x / 8 - 4]; + if (x > 0x1c00) return 63; + return bin_tab[x / 128 - 4] + 16; +} + +static int bin_index_up(size_t x) { + x = x / CKB_SIZE_ALIGN - 1; + if (x <= 32) return x; + x--; + if (x < 512) return bin_tab[x / 8 - 4] + 1; + return bin_tab[x / 128 - 4] + 17; +} + +static void *__expand_heap(size_t *pn) { + size_t n = *pn; + n += -n & (CKB_PAGE_SIZE - 1); + + void *p = _sbrk(n); + if (p == (void *)-1) { + return 0; + } + *pn = n; + return p; +} + +static struct chunk *expand_heap(size_t n) { + static void *end; + void *p; + struct chunk *w; + + /* The argument n already accounts for the caller's chunk + * CKB_OVERHEAD needs, but if the heap can't be extended in-place, + * we need room for an extra zero-sized sentinel chunk. */ + n += CKB_SIZE_ALIGN; + + p = __expand_heap(&n); + if (!p) return 0; + /* If not just expanding existing space, we need to make a + * new sentinel chunk below the allocated space. */ + if (p != end) { + /* Valid/safe because of the prologue increment. */ + n -= CKB_SIZE_ALIGN; + p = (char *)p + CKB_SIZE_ALIGN; + w = CKB_MEM_TO_CHUNK(p); + w->psize = 0 | CKB_C_INUSE; + } + + /* Record new heap end and fill in footer. */ + end = (char *)p + n; + w = CKB_MEM_TO_CHUNK(end); + w->psize = n | CKB_C_INUSE; + w->csize = 0 | CKB_C_INUSE; + + /* Fill in header, which may be new or may be replacing a + * zero-size sentinel header at the old end-of-heap. */ + w = CKB_MEM_TO_CHUNK(p); + w->csize = n | CKB_C_INUSE; + + return w; +} + +static int adjust_size(size_t *n) { + /* Result of pointer difference must fit in ptrdiff_t. */ + if (*n - 1 > INT64_MAX - CKB_SIZE_ALIGN - CKB_PAGE_SIZE) { + if (*n) { + return -1; + } else { + *n = CKB_SIZE_ALIGN; + return 0; + } + } + *n = (*n + CKB_OVERHEAD + CKB_SIZE_ALIGN - 1) & CKB_SIZE_MASK; + return 0; +} + +static void unbin(struct chunk *c, int i) { + if (c->prev == c->next) a_and_64(&mal.binmap, ~(1ULL << i)); + c->prev->next = c->next; + c->next->prev = c->prev; + c->csize |= CKB_C_INUSE; + CKB_NEXT_CHUNK(c)->psize |= CKB_C_INUSE; +} + +static void bin_chunk(struct chunk *self, int i) { + self->next = CKB_BIN_TO_CHUNK(i); + self->prev = mal.bins[i].tail; + self->next->prev = self; + self->prev->next = self; + if (self->prev == CKB_BIN_TO_CHUNK(i)) a_or_64(&mal.binmap, 1ULL << i); +} + +static void trim(struct chunk *self, size_t n) { + size_t n1 = CKB_CHUNK_SIZE(self); + struct chunk *next, *split; + + if (n >= n1 - CKB_DONTCARE) return; + + next = CKB_NEXT_CHUNK(self); + split = (void *)((char *)self + n); + + split->psize = n | CKB_C_INUSE; + split->csize = n1 - n; + next->psize = n1 - n; + self->csize = n | CKB_C_INUSE; + + int i = bin_index(n1 - n); + lock_bin(i); + + bin_chunk(split, i); + + unlock_bin(i); +} + +void *malloc(size_t n) { + struct chunk *c; + int i, j; + uint64_t mask; + + if (adjust_size(&n) < 0) return 0; + + if (n >= CKB_MMAP_THRESHOLD) { + // TODO: don't support too large memory + return 0; + } + + i = bin_index_up(n); + if (i < 63 && (mal.binmap & (1ULL << i))) { + lock_bin(i); + c = mal.bins[i].head; + if (c != CKB_BIN_TO_CHUNK(i) && CKB_CHUNK_SIZE(c) - n <= CKB_DONTCARE) { + unbin(c, i); + unlock_bin(i); + return CKB_CHUNK_TO_MEM(c); + } + unlock_bin(i); + } + for (mask = mal.binmap & -(1ULL << i); mask; mask -= (mask & -mask)) { + j = first_set(mask); + lock_bin(j); + c = mal.bins[j].head; + if (c != CKB_BIN_TO_CHUNK(j)) { + unbin(c, j); + unlock_bin(j); + break; + } + unlock_bin(j); + } + if (!mask) { + c = expand_heap(n); + if (!c) { + return 0; + } + } + trim(c, n); + return CKB_CHUNK_TO_MEM(c); +} + +void *realloc(void *p, size_t n) { + struct chunk *self, *next; + size_t n0; + void *new; + + if (!p) return malloc(n); + + if (adjust_size(&n) < 0) return 0; + + self = CKB_MEM_TO_CHUNK(p); + n0 = CKB_CHUNK_SIZE(self); + + if (n <= n0 && n0 - n <= CKB_DONTCARE) return p; + + next = CKB_NEXT_CHUNK(self); + + /* Crash on corrupted footer (likely from buffer overflow) */ + if (next->psize != self->csize) a_crash(); + + if (n < n0) { + int i = bin_index_up(n); + int j = bin_index(n0); + if (i < j && (mal.binmap & (1ULL << i))) goto copy_realloc; + struct chunk *split = (void *)((char *)self + n); + self->csize = split->psize = n | CKB_C_INUSE; + split->csize = next->psize = (n0 - n) | CKB_C_INUSE; + __bin_chunk(split); + return CKB_CHUNK_TO_MEM(self); + } + + size_t nsize = next->csize & CKB_C_INUSE ? 0 : CKB_CHUNK_SIZE(next); + if (n0 + nsize >= n) { + int i = bin_index(nsize); + lock_bin(i); + if (!(next->csize & CKB_C_INUSE)) { + unbin(next, i); + unlock_bin(i); + next = CKB_NEXT_CHUNK(next); + self->csize = next->psize = (n0 + nsize) | CKB_C_INUSE; + trim(self, n); + return CKB_CHUNK_TO_MEM(self); + } + unlock_bin(i); + } +copy_realloc: + /* As a last resort, allocate a new chunk and copy to it. */ + new = malloc(n - CKB_OVERHEAD); + if (!new) return 0; + memcpy(new, p, (n < n0 ? n : n0) - CKB_OVERHEAD); + free(CKB_CHUNK_TO_MEM(self)); + return new; +} + +void __bin_chunk(struct chunk *self) { + struct chunk *next = CKB_NEXT_CHUNK(self); + + /* Crash on corrupted footer (likely from buffer overflow) */ + if (next->psize != self->csize) a_crash(); + + size_t osize = CKB_CHUNK_SIZE(self), size = osize; + + /* Since we hold split_merge_lock, only transition from free to + * in-use can race; in-use to free is impossible */ + size_t psize = self->psize & CKB_C_INUSE ? 0 : CKB_CHUNK_PSIZE(self); + size_t nsize = next->csize & CKB_C_INUSE ? 0 : CKB_CHUNK_SIZE(next); + + if (psize) { + int i = bin_index(psize); + lock_bin(i); + if (!(self->psize & CKB_C_INUSE)) { + struct chunk *prev = CKB_PREV_CHUNK(self); + unbin(prev, i); + self = prev; + size += psize; + } + unlock_bin(i); + } + if (nsize) { + int i = bin_index(nsize); + lock_bin(i); + if (!(next->csize & CKB_C_INUSE)) { + unbin(next, i); + next = CKB_NEXT_CHUNK(next); + size += nsize; + } + unlock_bin(i); + } + + int i = bin_index(size); + lock_bin(i); + + self->csize = size; + next->psize = size; + bin_chunk(self, i); + + unlock_bin(i); +} + +void free(void *p) { + if (!p) return; + struct chunk *self = CKB_MEM_TO_CHUNK(p); + __bin_chunk(self); +} + +size_t malloc_usable_size(void *ptr) { + struct chunk *c = CKB_MEM_TO_CHUNK(ptr); + return CKB_CHUNK_PSIZE(c); +} diff --git a/libc/src/math.c b/libc/src/math.c new file mode 100644 index 0000000..261257b --- /dev/null +++ b/libc/src/math.c @@ -0,0 +1,265 @@ +#include +#include +#include +#include + +#ifndef DBL_EPSILON +#define DBL_EPSILON 2.22044604925031308085e-16 +#endif + +double acos(double x) { + assert(0); + return 0; +} + +double asin(double x) { + assert(0); + return 0; +} + +double atan2(double x, double y) { + assert(0); + return 0; +} + +double cos(double x) { + assert(0); + return 0; +} + +double cosh(double x) { + assert(0); + return 0; +} + +double exp(double x) { + assert(0); + return 0; +} + +#define EPS DBL_EPSILON +double floor(double x) { + static const double toint = 1 / EPS; + union { + double f; + uint64_t i; + } u = {x}; + int e = u.i >> 52 & 0x7ff; + double y; + + if (e >= 0x3ff + 52 || x == 0) return x; + /* y = int(x) - x, where int(x) is an integer neighbor of x */ + if (u.i >> 63) + y = x - toint + toint - x; + else + y = x + toint - toint - x; + /* special case because of non-nearest rounding modes */ + if (e <= 0x3ff - 1) { + FORCE_EVAL(y); + return u.i >> 63 ? -1 : 0; + } + if (y > 0) return x + y - 1; + return x + y; +} + +double ceil(double x) { + static const double toint = 1 / EPS; + union { + double f; + uint64_t i; + } u = {x}; + int e = u.i >> 52 & 0x7ff; + double y; + + if (e >= 0x3ff + 52 || x == 0) return x; + /* y = int(x) - x, where int(x) is an integer neighbor of x */ + if (u.i >> 63) + y = x - toint + toint - x; + else + y = x + toint - toint - x; + /* special case because of non-nearest rounding modes */ + if (e <= 0x3ff - 1) { + FORCE_EVAL(y); + return u.i >> 63 ? -0.0 : 1; + } + if (y < 0) return x + y + 1; + return x + y; +} + +double fabs(double x) { + union { + double f; + uint64_t i; + } u = {x}; + u.i &= -1ULL / 2; + return u.f; +} + +double ldexp(double x, int n) { return scalbn(x, n); } + +double log2(double x) { + assert(0); + return 0; +} + +double log10(double x) { + assert(0); + return 0; +} + +double sin(double x) { + assert(0); + return 0; +} + +double sinh(double x) { + assert(0); + return 0; +} + +double sqrt(double x) { + assert(0); + return 0; +} + +double tan(double x) { + assert(0); + return 0; +} + +double tanh(double x) { + assert(0); + return 0; +} + +double rint(double x) { + static const double toint = 1 / EPS; + union { + double f; + uint64_t i; + } u = {x}; + int e = u.i >> 52 & 0x7ff; + int s = u.i >> 63; + double y; + + if (e >= 0x3ff + 52) return x; + if (s) + y = x - toint + toint; + else + y = x + toint - toint; + if (y == 0) return s ? -0.0 : 0; + return y; +} + +long int lrint(double x) { return rint(x); } + +double cbrt(double x) { + assert(0); + return 0; +} + +double fmod(double numer, double denom); + +double fmin(double x, double y) { return x > y ? y : x; } + +double fmax(double x, double y) { return x < y ? y : x; } + +double hypot(double x, double y) { + assert(0); + return 0; +} + +int signbit(double num) { return num > 0; } + +double frexp(double x, int *e) { + union { + double d; + uint64_t i; + } y = {x}; + int ee = y.i >> 52 & 0x7ff; + + if (!ee) { + if (x) { + x = frexp(x * 0x1p64, e); + *e -= 64; + } else + *e = 0; + return x; + } else if (ee == 0x7ff) { + return x; + } + + *e = ee - 0x3fe; + y.i &= 0x800fffffffffffffull; + y.i |= 0x3fe0000000000000ull; + return y.d; +} + +double fmod(double x, double y) { + union { + double f; + uint64_t i; + } ux = {x}, uy = {y}; + int ex = ux.i >> 52 & 0x7ff; + int ey = uy.i >> 52 & 0x7ff; + int sx = ux.i >> 63; + uint64_t i; + + /* in the followings uxi should be ux.i, but then gcc wrongly adds */ + /* float load/store to inner loops ruining performance and code size */ + uint64_t uxi = ux.i; + + if (uy.i << 1 == 0 || __builtin_isnan(y) || ex == 0x7ff) return (x * y) / (x * y); + if (uxi << 1 <= uy.i << 1) { + if (uxi << 1 == uy.i << 1) return 0 * x; + return x; + } + + /* normalize x and y */ + if (!ex) { + for (i = uxi << 12; i >> 63 == 0; ex--, i <<= 1) + ; + uxi <<= -ex + 1; + } else { + uxi &= -1ULL >> 12; + uxi |= 1ULL << 52; + } + if (!ey) { + for (i = uy.i << 12; i >> 63 == 0; ey--, i <<= 1) + ; + uy.i <<= -ey + 1; + } else { + uy.i &= -1ULL >> 12; + uy.i |= 1ULL << 52; + } + + /* x mod y */ + for (; ex > ey; ex--) { + i = uxi - uy.i; + if (i >> 63 == 0) { + if (i == 0) return 0 * x; + uxi = i; + } + uxi <<= 1; + } + i = uxi - uy.i; + if (i >> 63 == 0) { + if (i == 0) return 0 * x; + uxi = i; + } + for (; uxi >> 52 == 0; uxi <<= 1, ex--) + ; + + /* scale result */ + if (ex > 0) { + uxi -= 1ULL << 52; + uxi |= (uint64_t)ex << 52; + } else { + uxi >>= -ex + 1; + } + uxi |= (uint64_t)sx << 63; + ux.i = uxi; + return ux.f; +} + +int abs(int a) { return a > 0 ? a : -a; } diff --git a/libc/src/math_log.c b/libc/src/math_log.c new file mode 100644 index 0000000..b2c0827 --- /dev/null +++ b/libc/src/math_log.c @@ -0,0 +1,132 @@ +/* @(#)e_log.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +//__FBSDID("$FreeBSD: src/lib/msun/src/e_log.c,v 1.15 2008/03/29 16:37:59 das +// Exp $"); + +/* __ieee754_log(x) + * Return the logrithm of x + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include +#include +#include + +double log(double x) { + double hfsq, f, s, z, R, w, t1, t2, dk; + int32_t k, hx, i, j; + uint32_t lx; + + EXTRACT_WORDS(hx, lx, x); + + k = 0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx & 0x7fffffff) | lx) == 0) return -two54 / zero; /* log(+-0)=-inf */ + if (hx < 0) return (x - x) / zero; /* log(-#) = NaN */ + k -= 54; + x *= two54; /* subnormal number, scale up x */ + GET_HIGH_WORD(hx, x); + } + if (hx >= 0x7ff00000) return x + x; + k += (hx >> 20) - 1023; + hx &= 0x000fffff; + i = (hx + 0x95f64) & 0x100000; + SET_HIGH_WORD(x, hx | (i ^ 0x3ff00000)); /* normalize x or x/2 */ + k += (i >> 20); + f = x - 1.0; + if ((0x000fffff & (2 + hx)) < 3) { /* -2**-20 <= f < 2**-20 */ + if (f == zero) { + if (k == 0) { + return zero; + } else { + dk = (double)k; + return dk * ln2_hi + dk * ln2_lo; + } + } + R = f * f * (0.5 - 0.33333333333333333 * f); + if (k == 0) + return f - R; + else { + dk = (double)k; + return dk * ln2_hi - ((R - dk * ln2_lo) - f); + } + } + s = f / (2.0 + f); + dk = (double)k; + z = s * s; + i = hx - 0x6147a; + w = z * z; + j = 0x6b851 - hx; + t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); + t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); + i |= j; + R = t2 + t1; + if (i > 0) { + hfsq = 0.5 * f * f; + if (k == 0) + return f - (hfsq - s * (hfsq + R)); + else + return dk * ln2_hi - ((hfsq - (s * (hfsq + R) + dk * ln2_lo)) - f); + } else { + if (k == 0) + return f - s * (f - R); + else + return dk * ln2_hi - ((s * (f - R) - dk * ln2_lo) - f); + } +} diff --git a/libc/src/math_pow.c b/libc/src/math_pow.c new file mode 100644 index 0000000..25768b7 --- /dev/null +++ b/libc/src/math_pow.c @@ -0,0 +1,329 @@ +/* @(#)e_pow.c 1.5 04/04/22 SMI */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +//__FBSDID("$FreeBSD: src/lib/msun/src/e_pow.c,v 1.14 2011/10/21 06:26:07 das +// Exp $"); + +/* __ieee754_pow(x,y) return x**y + * + * n + * Method: Let x = 2 * (1+f) + * 1. Compute and return log2(x) in two pieces: + * log2(x) = w1 + w2, + * where w1 has 53-24 = 29 bit trailing zeros. + * 2. Perform y*log2(x) = n+y' by simulating muti-precision + * arithmetic, where |y'|<=0.5. + * 3. Return x**y = 2**n*exp(y'*log2) + * + * Special cases: + * 1. (anything) ** 0 is 1 + * 2. (anything) ** 1 is itself + * 3. (anything) ** NAN is NAN + * 4. NAN ** (anything except 0) is NAN + * 5. +-(|x| > 1) ** +INF is +INF + * 6. +-(|x| > 1) ** -INF is +0 + * 7. +-(|x| < 1) ** +INF is +0 + * 8. +-(|x| < 1) ** -INF is +INF + * 9. +-1 ** +-INF is NAN + * 10. +0 ** (+anything except 0, NAN) is +0 + * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 + * 12. +0 ** (-anything except 0, NAN) is +INF + * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF + * 14. -0 ** (odd integer) = -( +0 ** (odd integer) ) + * 15. +INF ** (+anything except 0,NAN) is +INF + * 16. +INF ** (-anything except 0,NAN) is +0 + * 17. -INF ** (anything) = -0 ** (-anything) + * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) + * 19. (-anything except 0 and inf) ** (non-integer) is NAN + * + * Accuracy: + * pow(x,y) returns x**y nearly rounded. In particular + * pow(integer,integer) + * always returns the correct integer provided it is + * representable. + * + * Constants : + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ +#include +#include +#include + +double copysign(double x, double y) { + uint32_t hx, hy; + GET_HIGH_WORD(hx, x); + GET_HIGH_WORD(hy, y); + SET_HIGH_WORD(x, (hx & 0x7fffffff) | (hy & 0x80000000)); + return x; +} + +double scalbn(double x, int n) { + int32_t k, hx, lx; + EXTRACT_WORDS(hx, lx, x); + k = (hx & 0x7ff00000) >> 20; /* extract exponent */ + if (k == 0) { /* 0 or subnormal x */ + if ((lx | (hx & 0x7fffffff)) == 0) return x; /* +-0 */ + x *= two54; + GET_HIGH_WORD(hx, x); + k = ((hx & 0x7ff00000) >> 20) - 54; + if (n < -50000) return tiny * x; /*underflow*/ + } + if (k == 0x7ff) return x + x; /* NaN or Inf */ + k = k + n; + if (k > 0x7fe) return huge * copysign(huge, x); /* overflow */ + if (k > 0) /* normal result */ + { + SET_HIGH_WORD(x, (hx & 0x800fffff) | (k << 20)); + return x; + } + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge * copysign(huge, x); /*overflow*/ + else + return tiny * copysign(tiny, x); /*underflow*/ + } + k += 54; /* subnormal result */ + SET_HIGH_WORD(x, (hx & 0x800fffff) | (k << 20)); + return x * twom54; +} + +double pow(double x, double y) { + double z, ax, z_h, z_l, p_h, p_l; + double y1, t1, t2, r, s, t, u, v, w; + int32_t i, j, k, yisint, n; + int32_t hx, hy, ix, iy; + uint32_t lx, ly; + + EXTRACT_WORDS(hx, lx, x); + EXTRACT_WORDS(hy, ly, y); + ix = hx & 0x7fffffff; + iy = hy & 0x7fffffff; + + /* y==zero: x**0 = 1 */ + if ((iy | ly) == 0) return one; + + /* x==1: 1**y = 1, even if y is NaN */ + if (hx == 0x3ff00000 && lx == 0) return one; + + /* y!=zero: result is NaN if either arg is NaN */ + if (ix > 0x7ff00000 || ((ix == 0x7ff00000) && (lx != 0)) || iy > 0x7ff00000 || ((iy == 0x7ff00000) && (ly != 0))) + return (x + 0.0) + (y + 0.0); + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if (hx < 0) { + if (iy >= 0x43400000) + yisint = 2; /* even integer y */ + else if (iy >= 0x3ff00000) { + k = (iy >> 20) - 0x3ff; /* exponent */ + if (k > 20) { + j = ly >> (52 - k); + if ((j << (52 - k)) == (int32_t)ly) yisint = 2 - (j & 1); + } else if (ly == 0) { + j = iy >> (20 - k); + if ((j << (20 - k)) == iy) yisint = 2 - (j & 1); + } + } + } + + /* special value of y */ + if (ly == 0) { + if (iy == 0x7ff00000) { /* y is +-inf */ + if (((ix - 0x3ff00000) | lx) == 0) + return one; /* (-1)**+-inf is NaN */ + else if (ix >= 0x3ff00000) /* (|x|>1)**+-inf = inf,0 */ + return (hy >= 0) ? y : zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy < 0) ? -y : zero; + } + if (iy == 0x3ff00000) { /* y is +-1 */ + if (hy < 0) + return one / x; + else + return x; + } + if (hy == 0x40000000) return x * x; /* y is 2 */ + if (hy == 0x40080000) return x * x * x; /* y is 3 */ + if (hy == 0x40100000) { /* y is 4 */ + u = x * x; + return u * u; + } + if (hy == 0x3fe00000) { /* y is 0.5 */ + if (hx >= 0) /* x >= +0 */ + return sqrt(x); + } + } + + ax = fabs(x); + /* special value of x */ + if (lx == 0) { + if (ix == 0x7ff00000 || ix == 0 || ix == 0x3ff00000) { + z = ax; /*x is +-0,+-inf,+-1*/ + if (hy < 0) z = one / z; /* z = (1/|x|) */ + if (hx < 0) { + if (((ix - 0x3ff00000) | yisint) == 0) { + z = (z - z) / (z - z); /* (-1)**non-int is NaN */ + } else if (yisint == 1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + } + + /* CYGNUS LOCAL + fdlibm-5.3 fix: This used to be + n = (hx>>31)+1; + but ANSI C says a right shift of a signed negative quantity is + implementation defined. */ + n = ((uint32_t)hx >> 31) - 1; + + /* (x<0)**(non-int) is NaN */ + if ((n | yisint) == 0) return (x - x) / (x - x); + + s = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if ((n | (yisint - 1)) == 0) s = -one; /* (-ve)**(odd int) */ + + /* |y| is huge */ + if (iy > 0x41e00000) { /* if |y| > 2**31 */ + if (iy > 0x43f00000) { /* if |y| > 2**64, must o/uflow */ + if (ix <= 0x3fefffff) return (hy < 0) ? huge * huge : tiny * tiny; + if (ix >= 0x3ff00000) return (hy > 0) ? huge * huge : tiny * tiny; + } + /* over/underflow if x is not close to one */ + if (ix < 0x3fefffff) return (hy < 0) ? s * huge * huge : s * tiny * tiny; + if (ix > 0x3ff00000) return (hy > 0) ? s * huge * huge : s * tiny * tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = ax - one; /* t has 20 trailing zeros */ + w = (t * t) * (0.5 - t * (0.3333333333333333333333 - t * 0.25)); + u = ivln2_h * t; /* ivln2_h has 21 sig. bits */ + v = t * ivln2_l - w * ivln2; + t1 = u + v; + SET_LOW_WORD(t1, 0); + t2 = v - (t1 - u); + } else { + double ss, s2, s_h, s_l, t_h, t_l; + n = 0; + /* take care subnormal number */ + if (ix < 0x00100000) { + ax *= two53; + n -= 53; + GET_HIGH_WORD(ix, ax); + } + n += ((ix) >> 20) - 0x3ff; + j = ix & 0x000fffff; + /* determine interval */ + ix = j | 0x3ff00000; /* normalize ix */ + if (j <= 0x3988E) + k = 0; /* |x|> 1) | 0x20000000) + 0x00080000 + (k << 18)); + t_l = ax - (t_h - bp[k]); + s_l = v * ((u - s_h * t_h) - s_h * t_l); + /* compute log(ax) */ + s2 = ss * ss; + r = s2 * s2 * (L1 + s2 * (L2 + s2 * (L3 + s2 * (L4 + s2 * (L5 + s2 * L6))))); + r += s_l * (s_h + ss); + s2 = s_h * s_h; + t_h = 3.0 + s2 + r; + SET_LOW_WORD(t_h, 0); + t_l = r - ((t_h - 3.0) - s2); + /* u+v = ss*(1+...) */ + u = s_h * t_h; + v = s_l * t_h + t_l * ss; + /* 2/(3log2)*(ss+...) */ + p_h = u + v; + SET_LOW_WORD(p_h, 0); + p_l = v - (p_h - u); + z_h = cp_h * p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l * p_h + p_l * cp + dp_l[k]; + /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (double)n; + t1 = (((z_h + z_l) + dp_h[k]) + t); + SET_LOW_WORD(t1, 0); + t2 = z_l - (((t1 - t) - dp_h[k]) - z_h); + } + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + y1 = y; + SET_LOW_WORD(y1, 0); + p_l = (y - y1) * t1 + y * t2; + p_h = y1 * t1; + z = p_l + p_h; + EXTRACT_WORDS(j, i, z); + if (j >= 0x40900000) { /* z >= 1024 */ + if (((j - 0x40900000) | i) != 0) /* if z > 1024 */ + return s * huge * huge; /* overflow */ + else { + if (p_l + ovt > z - p_h) return s * huge * huge; /* overflow */ + } + } else if ((j & 0x7fffffff) >= 0x4090cc00) { /* z <= -1075 */ + if (((j - 0xc090cc00) | i) != 0) /* z < -1075 */ + return s * tiny * tiny; /* underflow */ + else { + if (p_l <= z - p_h) return s * tiny * tiny; /* underflow */ + } + } + /* + * compute 2**(p_h+p_l) + */ + i = j & 0x7fffffff; + k = (i >> 20) - 0x3ff; + n = 0; + if (i > 0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j + (0x00100000 >> (k + 1)); + k = ((n & 0x7fffffff) >> 20) - 0x3ff; /* new k for n */ + t = zero; + SET_HIGH_WORD(t, n & ~(0x000fffff >> k)); + n = ((n & 0x000fffff) | 0x00100000) >> (20 - k); + if (j < 0) n = -n; + p_h -= t; + } + t = p_l + p_h; + SET_LOW_WORD(t, 0); + u = t * lg2_h; + v = (p_l - (t - p_h)) * lg2 + t * lg2_l; + z = u + v; + w = v - (z - u); + t = z * z; + t1 = z - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5)))); + r = (z * t1) / (t1 - two) - (w + z * w); + z = one - (r - z); + GET_HIGH_WORD(j, z); + j += (n << 20); + if ((j >> 20) <= 0) + z = scalbn(z, n); /* subnormal output */ + else + SET_HIGH_WORD(z, j); + return s * z; +} diff --git a/libc/src/printf.c b/libc/src/printf.c new file mode 100644 index 0000000..678449b --- /dev/null +++ b/libc/src/printf.c @@ -0,0 +1,1019 @@ +#undef CKB_C_STDLIB_PRINTF +#define CKB_MALLOC_DECLARATION_ONLY 1 + +// Code copied from +// https://github.com/mpaland/printf/tree/d3b984684bb8a8bdc48cc7a1abecb93ce59bbe3e + +#include +#include +#include +#include +#include + +/** + * Output a character to a custom device like UART, used by the printf() + * function This function is declared here only. You have to write your custom + * implementation somewhere \param character Character to output + */ +void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro + * defines and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not + * counting the terminating null character + */ +int printf_(const char *format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING + * (V)SNPRINTF INSTEAD! \param buffer A pointer to the buffer where to store the + * formatted string. MUST be big enough to store the output! \param format A + * string that specifies the format of the output \return The number of + * characters that are WRITTEN into the buffer, not counting the terminating + * null character + */ +int sprintf(char *buffer, const char *format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, + * including a terminating null character \param format A string that specifies + * the format of the output \param va A value identifying a variable arguments + * list \return The number of characters that COULD have been written into the + * buffer, not counting the terminating null character. A value equal or larger + * than count indicates truncation. Only when the returned value is non-negative + * and less than count, the string has been completely written. + */ +int snprintf_(char *buffer, size_t count, const char *format, ...); +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not + * counting the terminating null character + */ +#define vprintf vprintf_ +int vprintf_(const char *format, va_list va); + +/** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() + * output \param out An output function which takes one character and an + * argument pointer \param arg An argument pointer for user data passed to + * output function \param format A string that specifies the format of the + * output \return The number of characters that are sent to the output function, + * not counting the terminating null character + */ +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...); + +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// 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. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for +// speed on +// embedded systems with a very limited resources. These routines are +// thread safe and reentrant! Use this instead of the bloated +// standard/newlib printf cause these use malloc for printf (and may not +// be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +// #if defined(PRINTF_SUPPORT_FLOAT) +// #include +// #endif + +// output function type +typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void *arg); + void *arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) { + if (idx < maxlen) { + ((char *)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) { + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) { + (void)buffer; + (void)idx; + (void)maxlen; + if (character) { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) { + (void)idx; + (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by +// 'maxsize' +static inline unsigned int _strnlen_s(const char *str, size_t maxsize) { + const char *s; + for (s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) { return (ch >= '0') && (ch <= '9'); } + +// internal ASCII string to unsigned int conversion +unsigned int _atoi(const char **str) { + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +int atoi(const char *str) { + if (str[0] == '+') { + const char *sub = str + 1; + return +_atoi(&sub); + } + if (str[0] == '-') { + const char *sub = str + 1; + return -_atoi(&sub); + } + return _atoi(&str); +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, + unsigned int width, unsigned int flags) { + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, + bool negative, unsigned int base, unsigned int prec, unsigned int width, + unsigned int flags) { + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, + unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) { + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, + bool negative, unsigned long long base, unsigned int prec, unsigned int width, + unsigned int flags) { + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > +// PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, + unsigned int width, unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, + unsigned int width, unsigned int flags) { + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if (value != value) return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, + width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which + // could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } else if (diff < 0.5) { + } else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by +// Martijn Jasperse +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, + unsigned int width, unsigned int flags) { + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay + // (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln + // around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see + // https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside + // 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the + // exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) { + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch (*format) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } else if (*format == 'o') { + base = 8U; + } else if (*format == 'b') { + base = 2U; + } else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, + (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, + precision, width, flags); +#endif + } else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), + value < 0, base, precision, width, flags); + } else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) + : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) + : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), + value < 0, base, precision, width, flags); + } + } else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, + precision, width, flags); +#endif + } else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, + width, flags); + } else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) + : (flags & FLAGS_SHORT) + ? (unsigned short int)va_arg(va, unsigned int) + : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': { + const char *p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': { + width = sizeof(void *) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, + precision, width, flags); + } else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, + 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char *format, ...) { + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf(char *buffer, const char *format, ...) { + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf(char *buffer, size_t count, const char *format, ...) { + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char *format, va_list va) { + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf(char *buffer, size_t count, const char *format, va_list va) { + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) { + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} + +// Default PRINTF_BUFFER_SIZE +#ifndef CKB_C_STDLIB_PRINTF_BUFFER_SIZE +#define CKB_C_STDLIB_PRINTF_BUFFER_SIZE 2048 +#endif +// syscall +int ckb_debug(const char *s); + +int printf(const char *format, ...) { + static char buf[CKB_C_STDLIB_PRINTF_BUFFER_SIZE]; + va_list va; + va_start(va, format); + int ret = vsnprintf(buf, CKB_C_STDLIB_PRINTF_BUFFER_SIZE, format, va); + va_end(va); + ckb_debug(buf); + return ret; +} + +int ckb_printf(const char *format, ...) { + static char buf[CKB_C_STDLIB_PRINTF_BUFFER_SIZE]; + va_list va; + va_start(va, format); + int ret = vsnprintf(buf, CKB_C_STDLIB_PRINTF_BUFFER_SIZE, format, va); + va_end(va); + ckb_debug(buf); + return ret; +} + +int ckb_vprintf(const char *format, va_list va) { + static char buf[CKB_C_STDLIB_PRINTF_BUFFER_SIZE]; + int ret = vsnprintf(buf, CKB_C_STDLIB_PRINTF_BUFFER_SIZE, format, va); + ckb_debug(buf); + return ret; +} diff --git a/libc/src/stdio.c b/libc/src/stdio.c new file mode 100644 index 0000000..cddc5fb --- /dev/null +++ b/libc/src/stdio.c @@ -0,0 +1,341 @@ +#include +#include +#include +#include "ckb_syscall_apis.h" + +FILE *stdin; +FILE *stdout; +FILE *stderr; + +static int s_local_access_enabled = 0; +void enable_local_access(int b) { s_local_access_enabled = b; } + +static int s_fs_access_enabled = 0; +void enable_fs_access(int b) { s_fs_access_enabled = b; } +int fs_access_enabled() { return s_fs_access_enabled; } + +#define memory_barrier() asm volatile("fence" ::: "memory") + +static inline long __internal_syscall(long n, long _a0, long _a1, long _a2, long _a3, long _a4, long _a5) { + register long a0 asm("a0") = _a0; + register long a1 asm("a1") = _a1; + register long a2 asm("a2") = _a2; + register long a3 asm("a3") = _a3; + register long a4 asm("a4") = _a4; + register long a5 asm("a5") = _a5; + +#ifdef __riscv_32e + register long syscall_id asm("t0") = n; +#else + register long syscall_id asm("a7") = n; +#endif + + asm volatile("scall" : "+r"(a0) : "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(syscall_id)); + /* + * Syscalls might modify memory sent as pointer, adding a barrier here + * ensures gcc won't do incorrect optimization. + */ + memory_barrier(); + + return a0; +} + +#define ckb_syscall(n, a, b, c, d, e, f) \ + __internal_syscall(n, (long)(a), (long)(b), (long)(c), (long)(d), (long)(e), (long)(f)) + +#define NOT_IMPL(name) \ + do { \ + printf("The %s is not implemented in mocked_stdio.c ", #name); \ + ckb_exit(-1); \ + } while (0) + +FILE *allocfile() { + FILE *file = malloc(sizeof(FILE)); + file->file = 0; + file->offset = 0; + return file; +} + +void freefile(FILE *file) { + file->file->rc -= 1; + free((void *)file); +} + +int remove(const char *__filename) { + NOT_IMPL(remove); + return 0; +} + +int rename(const char *__old, const char *__new) { + NOT_IMPL(rename); + return 0; +} + +FILE *tmpfile(void) { + NOT_IMPL(tmpfile); + return 0; +} + +char *tmpnam(char *__s) { + NOT_IMPL(tmpnam); + return 0; +} + +char *tempnam(const char *__dir, const char *__pfx) { + NOT_IMPL(tempnam); + return 0; +} + +int fclose(FILE *stream) { + if (s_local_access_enabled) { + return ckb_syscall(9009, stream, 0, 0, 0, 0, 0); + } + if (!fs_access_enabled()) { + NOT_IMPL(fclose); + } + freefile(stream); + return 0; +} + +int fflush(FILE *__stream) { + NOT_IMPL(fflush); + return 0; +} + +FILE *fopen(const char *path, const char *mode) { + if (s_local_access_enabled) { + return (void *)ckb_syscall(9003, path, mode, 0, 0, 0, 0); + } + + if (!fs_access_enabled()) { + NOT_IMPL(fopen); + } + + FILE *file = allocfile(); + if (file == 0) { + return 0; + } + + int ret = ckb_get_file(path, &file->file); + if (ret != 0) { + return 0; + } + return file; +} + +FILE *freopen(const char *path, const char *mode, FILE *stream) { + if (s_local_access_enabled) { + return (void *)ckb_syscall(9004, path, mode, stream, 0, 0, 0); + } + NOT_IMPL(freopen); + return 0; +} + +void setbuf(FILE *__stream, char *__buf) { NOT_IMPL(setbuf); } + +int setvbuf(FILE *__stream, char *__buf, int __modes, size_t __n) { + NOT_IMPL(setvbuf); + return 0; +} + +int fprintf(FILE *__stream, const char *__format, ...) { + NOT_IMPL(fprintf); + return 0; +} + +int vfprintf(FILE *__s, const char *__format, ...) { + NOT_IMPL(vfprintf); + return 0; +} +int vsprintf(char *__s, const char *__format, ...) { + NOT_IMPL(vsprintf); + return 0; +} + +int fscanf(FILE *__stream, const char *__format, ...) { + NOT_IMPL(fscanf); + return 0; +} + +int scanf(const char *__format, ...) { + NOT_IMPL(scanf); + return 0; +} + +int sscanf(const char *__s, const char *__format, ...) { + NOT_IMPL(sscanf); + return 0; +}; + +int fgetc(FILE *stream) { + if (s_local_access_enabled) { + return ckb_syscall(9008, stream, 0, 0, 0, 0, 0); + } + if (!fs_access_enabled()) { + NOT_IMPL(fgetc); + } + if (stream == 0 || stream->file->rc == 0 || stream->offset == stream->file->size) { + return -1; // EOF + } + unsigned char *c = (unsigned char *)stream->file->content + stream->offset; + stream->offset++; + return *c; +} + +int getc(FILE *stream) { return fgetc(stream); } + +int getchar(void) { + NOT_IMPL(getchar); + return 0; +} + +int fputc(int __c, FILE *__stream) { + NOT_IMPL(fputc); + return 0; +} + +int putc(int __c, FILE *__stream) { + NOT_IMPL(putc); + return 0; +} + +int putchar(int __c) { + NOT_IMPL(putchar); + return 0; +} + +char *fgets(char *__s, int __n, FILE *__stream) { + NOT_IMPL(fgets); + return 0; +} + +char *gets(char *__s) { + NOT_IMPL(gets); + return 0; +} + +int getline(char **__lineptr, size_t *__n, FILE *__stream) { + NOT_IMPL(getline); + return 0; +} + +int fputs(const char *__s, FILE *__stream) { + NOT_IMPL(fputs); + return 0; +} + +int puts(const char *__s) { + NOT_IMPL(puts); + return 0; +} + +int ungetc(int __c, FILE *__stream) { + NOT_IMPL(ungetc); + return 0; +} + +int isvalidfile(FILE *stream) { + if (stream == 0 || stream->file->rc == 0) { + return 1; + } + return 0; +} + +void mustbevaildfile(FILE *stream) { + if (isvalidfile(stream) != 0) { + ckb_exit(1); + } +} + +size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream) { + if (s_local_access_enabled) { + return ckb_syscall(9005, ptr, size, nitems, stream, 0, 0); + } + if (!fs_access_enabled()) { + NOT_IMPL(fread); + } + mustbevaildfile(stream); + // TODO: How do we handle error here? + if (stream->offset == stream->file->size) { + return 0; + } + // TODO: handle the case size * nitems is greater than uint32_t max + // handle size * ntimes overflowing + uint32_t bytes_to_read = (uint32_t)size * (uint32_t)nitems; + if (bytes_to_read > stream->file->size - stream->offset) { + bytes_to_read = stream->file->size - stream->offset; + } + memcpy(ptr, stream->file->content + stream->offset, bytes_to_read); + stream->offset = stream->offset + bytes_to_read; + // The return value should be the number of items written to the ptr + uint32_t s = size; + return (bytes_to_read + s - 1) / s; +} + +size_t fwrite(const void *__ptr, size_t __size, size_t __n, FILE *__s) { + NOT_IMPL(fwrite); + return 0; +} + +int fseek(FILE *stream, long int offset, int whence) { + if (s_local_access_enabled) { + return ckb_syscall(9011, stream, offset, whence, 0, 0, 0); + } + NOT_IMPL(fseek); + return 0; +} + +long int ftell(FILE *stream) { + if (s_local_access_enabled) { + return ckb_syscall(9010, stream, 0, 0, 0, 0, 0); + } + NOT_IMPL(ftell); + return 0; +} + +void rewind(FILE *__stream) { NOT_IMPL(rewind); } + +void clearerr(FILE *__stream) { NOT_IMPL(clearerr); } + +int feof(FILE *stream) { + if (s_local_access_enabled) { + return ckb_syscall(9006, stream, 0, 0, 0, 0, 0); + } + if (!fs_access_enabled()) { + NOT_IMPL(feof); + } + if (stream->offset == stream->file->size) { + return 1; + } + return 0; +} + +int ferror(FILE *stream) { + if (s_local_access_enabled) { + return ckb_syscall(9007, stream, 0, 0, 0, 0, 0); + } + if (!fs_access_enabled()) { + NOT_IMPL(ferror); + } + if (stream == 0 || stream->file->rc == 0) { + return 1; + } + return 0; +} + +void perror(const char *__s) { NOT_IMPL(perror); } + +int fileno(FILE *__stream) { + NOT_IMPL(fileno); + return 0; +} + +FILE *popen(const char *__command, const char *__modes) { + NOT_IMPL(popen); + return 0; +} + +int pclose(FILE *__stream) { + NOT_IMPL(pclose); + return 0; +} diff --git a/libc/src/stdlib.c b/libc/src/stdlib.c new file mode 100644 index 0000000..3838377 --- /dev/null +++ b/libc/src/stdlib.c @@ -0,0 +1,275 @@ +#include +#include +#include "ckb_syscall_apis.h" + +int isspace(int c) { return c == ' ' || (unsigned)c - '\t' < 5; } + +// Copied from dietlibc +float strtof(const char *s, char **endptr) { + register const char *p = s; + register float value = 0.; + int sign = +1; + float factor; + unsigned int expo; + + while (isspace(*p)) p++; + + switch (*p) { + case '-': + sign = -1; /* fall through */ + case '+': + p++; + default: + break; + } + + while ((unsigned int)(*p - '0') < 10u) value = value * 10 + (*p++ - '0'); + + if (*p == '.') { + factor = 1.; + + p++; + while ((unsigned int)(*p - '0') < 10u) { + factor *= 0.1; + value += (*p++ - '0') * factor; + } + } + + if ((*p | 32) == 'e') { + expo = 0; + factor = 10.L; + + switch (*++p) { // ja hier weiß ich nicht, was mindestens nach einem + // 'E' folgenden MUSS. + case '-': + factor = 0.1; /* fall through */ + case '+': + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + value = 0.L; + p = s; + goto done; + } + + while ((unsigned int)(*p - '0') < 10u) expo = 10 * expo + (*p++ - '0'); + + while (1) { + if (expo & 1) value *= factor; + if ((expo >>= 1) == 0) break; + factor *= factor; + } + } + +done: + if (endptr != NULL) *endptr = (char *)p; + + return value * sign; +} + +// Convert char to an int in base `base`, +// `base` must be 10 or 16, return -1 on error. +int char2int(char ch, unsigned int base) { + if (ch >= '0' && ch <= '9') return ch - '0'; + if (base == 16) { + if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; + if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; + } + return -1; +} + +#define ldbltype long double +double strtod(const char *s, char **endptr) { + register const char *p = s; + register ldbltype value = 0.; + int sign = +1; + unsigned int base = 10; + ldbltype base_inverse = (ldbltype)1 / (ldbltype)base; + ldbltype factor; + unsigned int expo; + unsigned int has_digits = 0; + + while (isspace(*p)) p++; + + switch (*p) { + case '-': + sign = -1; /* fall through */ + case '+': + p++; + case '0': + p++; + if ((*p | 32) == 'x') { + base = 16; + base_inverse = (ldbltype)1 / (ldbltype)base; + p++; + } else { + p--; + } + default: + break; + } + + unsigned int current_value; + while ((current_value = char2int(*p, base)) != -1) { + p++; + value = value * base + current_value; + has_digits = 1; + } + + if (*p == '.') { + factor = 1.; + + p++; + while ((current_value = char2int(*p, base)) != -1) { + p++; + factor *= base_inverse; + value += current_value * factor; + has_digits = 1; + } + } + + if ((*p | 32) == 'e' && base == 10) { + expo = 0; + factor = 10.; + + switch (*++p) { // ja hier weiß ich nicht, was mindestens nach einem + // 'E' folgenden MUSS. + case '-': + factor = 0.1; /* fall through */ + case '+': + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + value = 0.; + p = s; + goto done; + } + + while ((unsigned int)(*p - '0') < 10u) expo = 10 * expo + (*p++ - '0'); + + while (1) { + if (expo & 1) value *= factor; + if ((expo >>= 1) == 0) break; + factor *= factor; + } + } + + if ((*p | 32) == 'p' && base == 16) { + // TODO: add specifier p support + // https://cplusplus.com/reference/cstdlib/strtod/ + // - A 0x or 0X prefix, then a sequence of hexadecimal digits (as in + // isxdigit) optionally containing a period which separates the whole + // and fractional number parts. Optionally followed by a power of 2 + // exponent (a p or P character followed by an optional sign and a + // sequence of hexadecimal digits). + } +done: + if (endptr != NULL) { + if (has_digits) { + *endptr = (char *)p; + } else { + *endptr = (char *)s; + } + } + + return value * sign; +} + +long double strtold(const char *s, char **endptr) { + register const char *p = s; + register long double value = 0.L; + int sign = +1; + long double factor; + unsigned int expo; + + while (isspace(*p)) p++; + + switch (*p) { + case '-': + sign = -1; /* fall through */ + case '+': + p++; + default: + break; + } + + while ((unsigned int)(*p - '0') < 10u) value = value * 10 + (*p++ - '0'); + + if (*p == '.') { + factor = 1.; + + p++; + while ((unsigned int)(*p - '0') < 10u) { + factor *= 0.1; + value += (*p++ - '0') * factor; + } + } + + if ((*p | 32) == 'e') { + expo = 0; + factor = 10.L; + + switch (*++p) { // ja hier weiß ich nicht, was mindestens nach einem + // 'E' folgenden MUSS. + case '-': + factor = 0.1; /* fall through */ + case '+': + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + value = 0.L; + p = s; + goto done; + } + + while ((unsigned int)(*p - '0') < 10u) expo = 10 * expo + (*p++ - '0'); + + while (1) { + if (expo & 1) value *= factor; + if ((expo >>= 1) == 0) break; + factor *= factor; + } + } + +done: + if (endptr != NULL) *endptr = (char *)p; + + return value * sign; +} + +void exit(int status) { ckb_exit(status); } + +void abort(void) { ckb_exit(-1); } diff --git a/libc/src/string.c b/libc/src/string.c new file mode 100644 index 0000000..223e697 --- /dev/null +++ b/libc/src/string.c @@ -0,0 +1,455 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ckb_syscall_apis.h" + +#define CKB_SS (sizeof(size_t)) +#define CKB_ALIGN (sizeof(size_t) - 1) +#define CKB_ONES ((size_t)-1 / UCHAR_MAX) +#define CKB_HIGHS (CKB_ONES * (UCHAR_MAX / 2 + 1)) +#define CKB_HASZERO(x) (((x)-CKB_ONES) & ~(x)&CKB_HIGHS) + +void *memchr(const void *src, int c, size_t n) { + const unsigned char *s = src; + c = (unsigned char)c; +#ifdef __GNUC__ + for (; ((uintptr_t)s & CKB_ALIGN) && n && *s != c; s++, n--) + ; + if (n && *s != c) { + typedef size_t __attribute__((__may_alias__)) word; + const word *w; + size_t k = CKB_ONES * c; + for (w = (const void *)s; n >= CKB_SS && !CKB_HASZERO(*w ^ k); w++, n -= CKB_SS) + ; + s = (const void *)w; + } +#endif + for (; n && *s != c; s++, n--) + ; + return n ? (void *)s : 0; +} + +#define BITOP(a, b, op) ((a)[(size_t)(b) / (8 * sizeof *(a))] op(size_t) 1 << ((size_t)(b) % (8 * sizeof *(a)))) + +char *__strchrnul(const char *s, int c) { + c = (unsigned char)c; + if (!c) return (char *)s + strlen(s); + + for (; *s && *(unsigned char *)s != c; s++) + ; + return (char *)s; +} + +char *strchr(const char *s, int c) { + char *r = __strchrnul(s, c); + return *(unsigned char *)r == (unsigned char)c ? r : 0; +} + +int strncmp(const char *_l, const char *_r, size_t n) { + const unsigned char *l = (void *)_l, *r = (void *)_r; + if (!n--) return 0; + for (; *l && *r && n && *l == *r; l++, r++, n--) + ; + return *l - *r; +} + +size_t strspn(const char *s, const char *c) { + const char *a = s; + size_t byteset[32 / sizeof(size_t)] = {0}; + + if (!c[0]) return 0; + if (!c[1]) { + for (; *s == *c; s++) + ; + return s - a; + } + + for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++) + ; + for (; *s && BITOP(byteset, *(unsigned char *)s, &); s++) + ; + return s - a; +} + +size_t strcspn(const char *s, const char *c) { + const char *a = s; + size_t byteset[32 / sizeof(size_t)]; + + if (!c[0] || !c[1]) return __strchrnul(s, *c) - a; + + memset(byteset, 0, sizeof byteset); + for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++) + ; + for (; *s && !BITOP(byteset, *(unsigned char *)s, &); s++) + ; + return s - a; +} + +char *strpbrk(const char *s, const char *b) { + s += strcspn(s, b); + return *s ? (char *)s : 0; +} + +static char *twobyte_strstr(const unsigned char *h, const unsigned char *n) { + uint16_t nw = n[0] << 8 | n[1], hw = h[0] << 8 | h[1]; + for (h++; *h && hw != nw; hw = hw << 8 | *++h) + ; + return *h ? (char *)h - 1 : 0; +} + +static char *threebyte_strstr(const unsigned char *h, const unsigned char *n) { + uint32_t nw = (uint32_t)n[0] << 24 | n[1] << 16 | n[2] << 8; + uint32_t hw = (uint32_t)h[0] << 24 | h[1] << 16 | h[2] << 8; + for (h += 2; *h && hw != nw; hw = (hw | *++h) << 8) + ; + return *h ? (char *)h - 2 : 0; +} + +static char *fourbyte_strstr(const unsigned char *h, const unsigned char *n) { + uint32_t nw = (uint32_t)n[0] << 24 | n[1] << 16 | n[2] << 8 | n[3]; + uint32_t hw = (uint32_t)h[0] << 24 | h[1] << 16 | h[2] << 8 | h[3]; + for (h += 3; *h && hw != nw; hw = hw << 8 | *++h) + ; + return *h ? (char *)h - 3 : 0; +} + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define BITOP(a, b, op) ((a)[(size_t)(b) / (8 * sizeof *(a))] op(size_t) 1 << ((size_t)(b) % (8 * sizeof *(a)))) + +static char *twoway_strstr(const unsigned char *h, const unsigned char *n) { + const unsigned char *z; + size_t l, ip, jp, k, p, ms, p0, mem, mem0; + size_t byteset[32 / sizeof(size_t)] = {0}; + size_t shift[256]; + + /* Computing length of needle and fill shift table */ + for (l = 0; n[l] && h[l]; l++) BITOP(byteset, n[l], |=), shift[n[l]] = l + 1; + if (n[l]) return 0; /* hit the end of h */ + + /* Compute maximal suffix */ + ip = -1; + jp = 0; + k = p = 1; + while (jp + k < l) { + if (n[ip + k] == n[jp + k]) { + if (k == p) { + jp += p; + k = 1; + } else + k++; + } else if (n[ip + k] > n[jp + k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + ms = ip; + p0 = p; + + /* And with the opposite comparison */ + ip = -1; + jp = 0; + k = p = 1; + while (jp + k < l) { + if (n[ip + k] == n[jp + k]) { + if (k == p) { + jp += p; + k = 1; + } else + k++; + } else if (n[ip + k] < n[jp + k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + if (ip + 1 > ms + 1) + ms = ip; + else + p = p0; + + /* Periodic needle? */ + if (memcmp(n, n + p, ms + 1)) { + mem0 = 0; + p = MAX(ms, l - ms - 1) + 1; + } else + mem0 = l - p; + mem = 0; + + /* Initialize incremental end-of-haystack pointer */ + z = h; + + /* Search loop */ + for (;;) { + /* Update incremental end-of-haystack pointer */ + if (z - h < l) { + /* Fast estimate for MAX(l,63) */ + size_t grow = l | 63; + const unsigned char *z2 = memchr(z, 0, grow); + if (z2) { + z = z2; + if (z - h < l) return 0; + } else + z += grow; + } + + /* Check last byte first; advance by shift on mismatch */ + if (BITOP(byteset, h[l - 1], &)) { + k = l - shift[h[l - 1]]; + if (k) { + if (k < mem) k = mem; + h += k; + mem = 0; + continue; + } + } else { + h += l; + mem = 0; + continue; + } + + /* Compare right half */ + for (k = MAX(ms + 1, mem); n[k] && n[k] == h[k]; k++) + ; + if (n[k]) { + h += k - ms; + mem = 0; + continue; + } + /* Compare left half */ + for (k = ms + 1; k > mem && n[k - 1] == h[k - 1]; k--) + ; + if (k <= mem) return (char *)h; + h += p; + mem = mem0; + } +} + +char *strstr(const char *h, const char *n) { + /* Return immediately on empty needle */ + if (!n[0]) return (char *)h; + + /* Use faster algorithms for short needles */ + h = strchr(h, *n); + if (!h || !n[1]) return (char *)h; + if (!h[1]) return 0; + if (!n[2]) return twobyte_strstr((void *)h, (void *)n); + if (!h[2]) return 0; + if (!n[3]) return threebyte_strstr((void *)h, (void *)n); + if (!h[3]) return 0; + if (!n[4]) return fourbyte_strstr((void *)h, (void *)n); + + return twoway_strstr((void *)h, (void *)n); +} + +/* Copied from + * https://github.com/bminor/musl/blob/46d1c7801bb509e1097e8fadbaf359367fa4ef0b/src/setjmp/riscv64/setjmp.S + */ +/* We need to use inline asm for easier compilation, + * https://stackoverflow.com/a/42358235. */ +/* We need __attribute__((naked)) to remove prologue and epilogue, + * https://stackoverflow.com/a/42637729 */ +__attribute__((naked)) int setjmp(jmp_buf b) { + asm volatile( + "sd s0, 0(a0)\n" + "sd s1, 8(a0)\n" + "sd s2, 16(a0)\n" + "sd s3, 24(a0)\n" + "sd s4, 32(a0)\n" + "sd s5, 40(a0)\n" + "sd s6, 48(a0)\n" + "sd s7, 56(a0)\n" + "sd s8, 64(a0)\n" + "sd s9, 72(a0)\n" + "sd s10, 80(a0)\n" + "sd s11, 88(a0)\n" + "sd sp, 96(a0)\n" + "sd ra, 104(a0)\n" + "li a0, 0\n" + "ret\n"); +} + +__attribute__((naked)) void longjmp(jmp_buf b, int n) { + asm volatile( + "ld s0, 0(a0)\n" + "ld s1, 8(a0)\n" + "ld s2, 16(a0)\n" + "ld s3, 24(a0)\n" + "ld s4, 32(a0)\n" + "ld s5, 40(a0)\n" + "ld s6, 48(a0)\n" + "ld s7, 56(a0)\n" + "ld s8, 64(a0)\n" + "ld s9, 72(a0)\n" + "ld s10, 80(a0)\n" + "ld s11, 88(a0)\n" + "ld sp, 96(a0)\n" + "ld ra, 104(a0)\n" + "seqz a0, a1\n" + "add a0, a0, a1\n" + "ret\n"); +} + +__attribute__((naked)) void _longjmp(jmp_buf b, int n) { + asm volatile( + "ld s0, 0(a0)\n" + "ld s1, 8(a0)\n" + "ld s2, 16(a0)\n" + "ld s3, 24(a0)\n" + "ld s4, 32(a0)\n" + "ld s5, 40(a0)\n" + "ld s6, 48(a0)\n" + "ld s7, 56(a0)\n" + "ld s8, 64(a0)\n" + "ld s9, 72(a0)\n" + "ld s10, 80(a0)\n" + "ld s11, 88(a0)\n" + "ld sp, 96(a0)\n" + "ld ra, 104(a0)\n" + "seqz a0, a1\n" + "add a0, a0, a1\n" + "ret\n"); +} + +int strcoll(const char *l, const char *r) { return strcmp(l, r); } + +int *__errno_location(void) { + static int error = -1; + return &error; +} + +char *strerror(int e) { + static char *errorstr = "There is an error"; + return errorstr; +} + + +#define X(x) (((x) / 256 | (x)*256) % 65536) + +const unsigned short **__ctype_b_loc(void) { + static const unsigned short table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, X(0x200), X(0x200), X(0x200), X(0x200), + X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x320), X(0x220), X(0x220), X(0x220), X(0x220), X(0x200), + X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), + X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x200), X(0x160), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), + X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), + X(0x8d8), X(0x8d8), X(0x8d8), X(0x8d8), X(0x8d8), X(0x8d8), X(0x8d8), X(0x8d8), X(0x8d8), X(0x8d8), X(0x4c0), + X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x8d5), X(0x8d5), X(0x8d5), X(0x8d5), X(0x8d5), + X(0x8d5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), + X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x8c5), X(0x4c0), + X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x4c0), X(0x8d6), X(0x8d6), X(0x8d6), X(0x8d6), X(0x8d6), X(0x8d6), + X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), + X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x8c6), X(0x4c0), X(0x4c0), + X(0x4c0), X(0x4c0), X(0x200), 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + static const unsigned short *const ptable = table + 128; + return (void *)&ptable; +} + +const int32_t **__ctype_toupper_loc(void) { + static const int32_t table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 91, + 92, 93, 94, 95, 96, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 123, 124, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + static const int32_t *const ptable = table + 128; + + return (void *)&ptable; +} + +const int32_t **__ctype_tolower_loc(void) { + static const int32_t table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 'a', 'b', 'c', 'd', 'e', + 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 91, + 92, 93, 94, 95, 96, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 123, 124, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + static const int32_t *const ptable = table + 128; + + return (void *)&ptable; +} + +char *getenv(const char *name) { return 0; } + +static void *__memrchr(const void *m, int c, size_t n) { + const unsigned char *s = m; + c = (unsigned char)c; + while (n--) + if (s[n] == c) return (void *)(s + n); + return 0; +} + +char *strrchr(const char *s, int character) { return __memrchr(s, character, strlen(s) + 1); } + +char *strcat(char *dest, const char *src) { + strcpy(dest + strlen(dest), src); + return dest; +} diff --git a/libc/stddef.h b/libc/stddef.h index ad71d9d..dbe70fc 100644 --- a/libc/stddef.h +++ b/libc/stddef.h @@ -5,4 +5,7 @@ #define NULL ((void*)0) #include +typedef signed long ptrdiff_t; +typedef long long intmax_t; + #endif /* CKB_C_STDLIB_STDDEF_H_ */ diff --git a/libc/stdint.h b/libc/stdint.h index 54a62cb..a75c90a 100644 --- a/libc/stdint.h +++ b/libc/stdint.h @@ -22,4 +22,28 @@ #define SIZE_MAX UINT64_MAX +#define INTPTR_MIN (-9223372036854775807L - 1) +#define INTPTR_MAX (9223372036854775807L) +#define UINTPTR_MAX (18446744073709551615UL) + +#define INT8_C(c) c +#define INT16_C(c) c +#define INT32_C(c) c + +#define UINT8_C(c) c +#define UINT16_C(c) c +#define UINT32_C(c) c##U + +#if UINTPTR_MAX == UINT64_MAX +#define INT64_C(c) c##L +#define UINT64_C(c) c##UL +#define INTMAX_C(c) c##L +#define UINTMAX_C(c) c##UL +#else +#define INT64_C(c) c##LL +#define UINT64_C(c) c##ULL +#define INTMAX_C(c) c##LL +#define UINTMAX_C(c) c##ULL +#endif + #endif /* CKB_C_STDLIB_STDINT_H_ */ diff --git a/libc/stdio.h b/libc/stdio.h index c626b88..180230b 100644 --- a/libc/stdio.h +++ b/libc/stdio.h @@ -56,4 +56,112 @@ int printf(const char* format, ...); int ckb_printf(const char* format, ...); int ckb_debug(const char* s); +#include +#include +#include "ckb_cell_fs.h" + +#define BUFSIZ 512 +#define EOF (-1) +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define _IOFBF 0 +#define _IOLBF 1 +#define _IONBF 2 + +typedef struct FILE { + FSFile *file; + uint32_t offset; +} FILE; + +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; + +int remove(const char *__filename); + +int rename(const char *__old, const char *__new); + +FILE *tmpfile(void); + +char *tmpnam(char *__s); + +char *tempnam(const char *__dir, const char *__pfx); + +int fclose(FILE *__stream); +int fflush(FILE *__stream); + +FILE *fopen(const char *__filename, const char *__modes); +FILE *freopen(const char *__filename, const char *__modes, FILE *__stream); + +void setbuf(FILE *__stream, char *__buf); +int setvbuf(FILE *__stream, char *__buf, int __modes, size_t __n); + +int fprintf(FILE *__stream, const char *__format, ...); + +int sprintf(char *__s, const char *__format, ...); + +int vfprintf(FILE *__s, const char *__format, ...); + +int vsprintf(char *__s, const char *__format, ...); +int vsnprintf(char *__s, size_t __maxlen, const char *__format, ...); +int snprintf(char *__s, size_t __maxlen, const char *__format, ...); +int snprintf_(char *__s, size_t __maxlen, const char *__format, ...); + +int fscanf(FILE *__stream, const char *__format, ...); + +int scanf(const char *__format, ...); + +int sscanf(const char *__s, const char *__format, ...); + +int fgetc(FILE *__stream); + +int getc(FILE *__stream); + +int getchar(void); + +int fputc(int __c, FILE *__stream); + +int putc(int __c, FILE *__stream); + +int putchar(int __c); + +char *fgets(char *__s, int __n, FILE *__stream); +char *gets(char *__s); + +int getline(char **__lineptr, size_t *__n, FILE *__stream); + +int fputs(const char *__s, FILE *__stream); + +int puts(const char *__s); + +int ungetc(int __c, FILE *__stream); + +size_t fread(void *__ptr, size_t __size, size_t __n, FILE *__stream); + +size_t fwrite(const void *__ptr, size_t __size, size_t __n, FILE *__s); + +int fseek(FILE *__stream, long int __off, int __whence); + +long int ftell(FILE *__stream); + +void rewind(FILE *__stream); + +void clearerr(FILE *__stream); + +int feof(FILE *__stream); + +int ferror(FILE *__stream); + +void perror(const char *__s); + +int fileno(FILE *__stream); + +FILE *popen(const char *__command, const char *__modes); + +int pclose(FILE *__stream); + +void enable_local_access(int); + #endif /* CKB_C_STDLIB_STDIO_H_ */ diff --git a/libc/stdlib.h b/libc/stdlib.h index c01d4ce..5eab4c5 100644 --- a/libc/stdlib.h +++ b/libc/stdlib.h @@ -13,4 +13,14 @@ int rand(void); void *bsearch(const void *key, const void *base, size_t nel, size_t width, int (*cmp)(const void *, const void *)); +float strtof(const char *__restrict, char **__restrict); +double strtod(const char *__restrict, char **__restrict); +long double strtold(const char *__restrict, char **__restrict); +int atoi(const char *); + +int abs(int); +void exit(int); +void abort(void); +#define alloca __builtin_alloca + #endif /* CKB_C_STDLIB_STDLIB_H_ */ diff --git a/libc/string.h b/libc/string.h index 9754efb..9ca7eba 100644 --- a/libc/string.h +++ b/libc/string.h @@ -13,4 +13,15 @@ size_t strlen(const char *s); int strcmp(const char *l, const char *r); char *strstr(const char *, const char *); +char *strchr(const char *, int); +int strncmp(const char *_l, const char *_r, size_t n); +char *strpbrk(const char *, const char *); +size_t strcspn(const char *, const char *); +size_t strspn(const char *, const char *); +void *memchr(const void *, int, size_t); +char *strrchr(const char *str, int character); +char *strcat(char *destination, const char *source); +int strcoll(const char *, const char *); +char *strerror(int); + #endif /* CKB_C_STDLIB_STRING_H_ */