487 lines
8.8 KiB
C
487 lines
8.8 KiB
C
/**
|
|
* See Copyright Notice in picrin.h
|
|
*/
|
|
|
|
#include "picconf.h"
|
|
|
|
#ifndef PIC_USE_LIBC
|
|
# define PIC_USE_LIBC 1
|
|
#endif
|
|
|
|
#ifndef PIC_USE_PORT
|
|
# define PIC_USE_PORT 1
|
|
#endif
|
|
|
|
#ifndef PIC_USE_CALLCC
|
|
# define PIC_USE_CALLCC 1
|
|
#endif
|
|
|
|
#ifndef PIC_USE_READ
|
|
# define PIC_USE_READ 1
|
|
#endif
|
|
|
|
#ifndef PIC_USE_WRITE
|
|
# define PIC_USE_WRITE 1
|
|
#endif
|
|
|
|
#ifndef PIC_USE_EVAL
|
|
# define PIC_USE_EVAL 1
|
|
#endif
|
|
|
|
#ifndef PIC_USE_FILE
|
|
# define PIC_USE_FILE 1
|
|
#endif
|
|
|
|
#ifndef PIC_USE_ERROR
|
|
# define PIC_USE_ERROR 1
|
|
#endif
|
|
|
|
#if !PIC_USE_PORT && PIC_USE_READ
|
|
# error PIC_USE_READ requires PIC_USE_PORT
|
|
#endif
|
|
#if !PIC_USE_PORT && PIC_USE_WRITE
|
|
# error PIC_USE_WRITE requires PIC_USE_PORT
|
|
#endif
|
|
#if !PIC_USE_PORT && PIC_USE_FILE
|
|
# error PIC_USE_FILE requires PIC_USE_PORT
|
|
#endif
|
|
#if !PIC_USE_LIBC && PIC_USE_FILE
|
|
# error PIC_USE_FILE requires PIC_USE_LIBC
|
|
#endif
|
|
#if !PIC_USE_LIBC && PIC_USE_CALLCC
|
|
# error PIC_USE_CALLCC requires PIC_USE_LIBC
|
|
#endif
|
|
#if !PIC_USE_CALLCC && PIC_USE_ERROR
|
|
# error PIC_USE_ERROR requires PIC_USE_CALLCC
|
|
#endif
|
|
|
|
#if PIC_USE_CALLCC
|
|
# include <setjmp.h>
|
|
# define PIC_JMPBUF jmp_buf
|
|
# define PIC_SETJMP(buf) setjmp(buf)
|
|
#else
|
|
# define PIC_JMPBUF char
|
|
# define PIC_SETJMP(buf) ((void)(buf), 0)
|
|
#endif
|
|
|
|
#ifndef PIC_BUFSIZ
|
|
# define PIC_BUFSIZ 1024
|
|
#endif
|
|
|
|
#ifndef PIC_ARENA_SIZE
|
|
# define PIC_ARENA_SIZE (8 * 1024)
|
|
#endif
|
|
|
|
#ifndef PIC_HEAP_PAGE_SIZE
|
|
# define PIC_HEAP_PAGE_SIZE (4 * 1024 * 1024)
|
|
#endif
|
|
|
|
#ifndef PIC_PAGE_REQUEST_THRESHOLD
|
|
# define PIC_PAGE_REQUEST_THRESHOLD(total) ((total) * 77 / 100)
|
|
#endif
|
|
|
|
/* check compatibility */
|
|
|
|
#if __STDC_VERSION__ >= 199901L
|
|
# include <stdbool.h>
|
|
#else
|
|
# define bool char
|
|
# define true 1
|
|
# define false 0
|
|
#endif
|
|
|
|
#if __STDC_VERSION__ >= 199901L
|
|
# include <stddef.h>
|
|
#elif ! defined(offsetof)
|
|
# define offsetof(s,m) ((size_t)(&(((s *)0)->m) - 0))
|
|
#endif
|
|
|
|
#if __STDC_VERSION__ >= 199901L
|
|
# include <stdint.h>
|
|
#else
|
|
# if INT_MAX > 2147483640L /* imported from luaconf.h */
|
|
typedef int int32_t;
|
|
typedef unsigned int uint32_t;
|
|
# else
|
|
typedef long int32_t;
|
|
typedef unsigned long uint32_t;
|
|
# endif
|
|
#endif
|
|
|
|
#if __STDC_VERSION__ >= 201112L
|
|
# include <stdnoreturn.h>
|
|
# define PIC_NORETURN noreturn
|
|
#elif __GNUC__ || __clang__
|
|
# define PIC_NORETURN __attribute__((noreturn))
|
|
#else
|
|
# define PIC_NORETURN
|
|
#endif
|
|
|
|
/*
|
|
* normalize inline keyword; PIC_*_INLINE macros have the same semantics as c99
|
|
*/
|
|
|
|
#if __STDC_VERSION__ >= 199901L
|
|
# define PIC_STATIC_INLINE static inline
|
|
#elif __GNUC__ || __clang__
|
|
# define PIC_STATIC_INLINE static __inline__
|
|
#else
|
|
# define PIC_STATIC_INLINE static
|
|
#endif
|
|
|
|
#if defined __GNUC__ && !defined __GNUC_STDC_INLINE__ && !defined __GNUC_GNU_INLINE__
|
|
# define __GNUC_GNU_INLINE__ 1
|
|
#endif
|
|
|
|
/* PIC_INLINE does not necessarily unify identical definitions */
|
|
#if defined __GNUC_GNU_INLINE__
|
|
# define PIC_EXTERN_INLINE inline
|
|
# define PIC_INLINE extern inline
|
|
#elif __STDC_VERSION__ >= 199901L
|
|
# define PIC_EXTERN_INLINE extern inline
|
|
# define PIC_INLINE inline
|
|
#else
|
|
# define PIC_EXTERN_INLINE
|
|
# define PIC_INLINE static
|
|
#endif
|
|
|
|
#if defined(__cplusplus)
|
|
# define PIC_UNUSED(v)
|
|
#elif __GNUC__ || __clang__
|
|
# define PIC_UNUSED(v) v __attribute__((unused))
|
|
#else
|
|
# define PIC_UNUSED(v) v
|
|
#endif
|
|
|
|
#define PIC_GENSYM2_(x,y) PIC_G##x##_##y##_
|
|
#define PIC_GENSYM1_(x,y) PIC_GENSYM2_(x,y)
|
|
#if defined(__COUNTER__)
|
|
# define PIC_GENSYM(x) PIC_GENSYM1_(__COUNTER__,x)
|
|
#else
|
|
# define PIC_GENSYM(x) PIC_GENSYM1_(__LINE__,x)
|
|
#endif
|
|
|
|
#if __GNUC__
|
|
# define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
|
|
#endif
|
|
#if GCC_VERSION >= 40500 || __clang__
|
|
# define PIC_UNREACHABLE() (assert(false), __builtin_unreachable())
|
|
#else
|
|
# define PIC_UNREACHABLE() (assert(false))
|
|
#endif
|
|
#if __GNUC__
|
|
# undef GCC_VERSION
|
|
#endif
|
|
|
|
#define PIC_SWAP(type,a,b) PIC_SWAP_HELPER_(type, PIC_GENSYM(tmp), a, b)
|
|
#define PIC_SWAP_HELPER_(type,tmp,a,b) \
|
|
do { \
|
|
type tmp = (a); \
|
|
(a) = (b); \
|
|
(b) = tmp; \
|
|
} while (0)
|
|
|
|
|
|
#if PIC_USE_LIBC
|
|
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
|
|
#else
|
|
|
|
# define assert(v) (void)0
|
|
|
|
PIC_STATIC_INLINE int
|
|
isspace(int c)
|
|
{
|
|
return c == ' ' || c == '\t' || c == '\r' || c == '\v' || c == '\f' || c == '\n';
|
|
}
|
|
|
|
PIC_STATIC_INLINE int
|
|
tolower(int c)
|
|
{
|
|
return ('A' <= c && c <= 'Z') ? c - 'A' + 'a' : c;
|
|
}
|
|
|
|
PIC_STATIC_INLINE int
|
|
isdigit(int c)
|
|
{
|
|
return '0' <= c && c <= '9';
|
|
}
|
|
|
|
PIC_STATIC_INLINE char *
|
|
strchr(const char *s, int c)
|
|
{
|
|
do {
|
|
if (*s == c)
|
|
return (char *)s;
|
|
} while (*s++ != '\0');
|
|
return NULL;
|
|
}
|
|
|
|
PIC_STATIC_INLINE size_t
|
|
strlen(const char *s)
|
|
{
|
|
size_t l = 0;
|
|
|
|
while (*s++) {
|
|
l++;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
PIC_STATIC_INLINE int
|
|
strcmp(const char *s1, const char *s2)
|
|
{
|
|
while (*s1 && *s1 == *s2) {
|
|
s1++;
|
|
s2++;
|
|
}
|
|
return (unsigned)*s1 - (unsigned)*s2;
|
|
}
|
|
|
|
PIC_STATIC_INLINE long
|
|
strtol(const char *nptr, char **endptr, int base)
|
|
{
|
|
long l = 0;
|
|
char c;
|
|
int n;
|
|
|
|
while (1) {
|
|
c = *nptr;
|
|
if ('0' <= c && c <= '9')
|
|
n = c - '0';
|
|
else if ('a' <= c && c <= 'z')
|
|
n = c - 'a' + 10;
|
|
else if ('A' <= c && c <= 'Z')
|
|
n = c - 'A' + 10;
|
|
else
|
|
goto exit;
|
|
|
|
if (base <= n)
|
|
goto exit;
|
|
|
|
l = l * base + n;
|
|
nptr++;
|
|
}
|
|
exit:
|
|
if (endptr)
|
|
*endptr = (char *)nptr;
|
|
return l;
|
|
}
|
|
|
|
PIC_STATIC_INLINE void *
|
|
memset(void *s, int n, size_t c)
|
|
{
|
|
char *p = s;
|
|
|
|
while (c-- > 0) {
|
|
*p++ = n;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
PIC_STATIC_INLINE void *
|
|
memcpy(void *dst, const void *src, size_t n)
|
|
{
|
|
const char *s = src;
|
|
char *d = dst;
|
|
|
|
while (n-- > 0) {
|
|
*d++ = *s++;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
PIC_STATIC_INLINE void *
|
|
memmove(void *dst, const void *src, size_t n)
|
|
{
|
|
const char *s = src;
|
|
char *d = dst;
|
|
|
|
if (d <= s || d >= s + n) {
|
|
memcpy(dst, src, n);
|
|
} else {
|
|
s += n;
|
|
d += n;
|
|
while (n-- > 0) {
|
|
*--d = *--s;
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
PIC_STATIC_INLINE int
|
|
memcmp(const void *b1, const void *b2, size_t n)
|
|
{
|
|
const char *s1 = b1, *s2 = b2;
|
|
|
|
while (*s1 == *s2 && n-- > 0) {
|
|
s1++;
|
|
s2++;
|
|
}
|
|
return (unsigned)*s1 - (unsigned)*s2;
|
|
}
|
|
|
|
PIC_STATIC_INLINE char *
|
|
strcpy(char *dst, const char *src)
|
|
{
|
|
char *d = dst;
|
|
|
|
while ((*dst++ = *src++) != 0);
|
|
|
|
return d;
|
|
}
|
|
|
|
PIC_STATIC_INLINE double
|
|
atof(const char *nptr)
|
|
{
|
|
int c;
|
|
double f, g, h;
|
|
int exp, s, i, e;
|
|
unsigned u;
|
|
|
|
/* note that picrin_read always assures that *nptr is a digit, never a '+' or '-' */
|
|
/* in other words, the result of atof will always be positive */
|
|
|
|
/* mantissa */
|
|
/* pre '.' */
|
|
u = *nptr++ - '0';
|
|
while (isdigit(c = *nptr)) {
|
|
u = u * 10 + (*nptr++ - '0');
|
|
}
|
|
if (c == '.') {
|
|
nptr++;
|
|
/* after '.' */
|
|
g = 0, e = 0;
|
|
while (isdigit(c = *nptr)) {
|
|
g = g * 10 + (*nptr++ - '0');
|
|
e++;
|
|
}
|
|
h = 1.0;
|
|
while (e-- > 0) {
|
|
h /= 10;
|
|
}
|
|
f = u + g * h;
|
|
}
|
|
else {
|
|
f = u;
|
|
}
|
|
/* suffix, i.e., exponent */
|
|
s = 0;
|
|
exp = 0;
|
|
c = *nptr;
|
|
|
|
if (c == 'e' && c == 'E') {
|
|
nptr++;
|
|
switch ((c = *nptr++)) {
|
|
case '-':
|
|
s = 1;
|
|
case '+':
|
|
c = *nptr++;
|
|
default:
|
|
exp = c - '0';
|
|
while (isdigit(c = *nptr)) {
|
|
exp = exp * 10 + (*nptr++ - '0');
|
|
}
|
|
}
|
|
}
|
|
e = 10;
|
|
for (i = 0; exp; ++i) {
|
|
if ((exp & 1) != 0) {
|
|
f = s ? f / e : (f * e);
|
|
}
|
|
e *= e;
|
|
exp >>= 1;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
#endif
|
|
|
|
PIC_STATIC_INLINE double
|
|
pic_atod(const char *str)
|
|
{
|
|
return atof(str);
|
|
}
|
|
|
|
#if PIC_USE_FILE
|
|
# include <stdio.h>
|
|
#endif
|
|
|
|
#if PIC_USE_LIBC
|
|
|
|
PIC_STATIC_INLINE void
|
|
pic_dtoa(double dval, char *buf)
|
|
{
|
|
sprintf(buf, "%g", dval);
|
|
}
|
|
|
|
#else
|
|
|
|
PIC_STATIC_INLINE void
|
|
pic_dtoa(double dval, char *buf)
|
|
{
|
|
# define fabs(x) ((x) >= 0 ? (x) : -(x))
|
|
long lval, tlval;
|
|
int ival;
|
|
int scnt, ecnt, cnt = 0;
|
|
if (dval < 0) {
|
|
dval = -dval;
|
|
buf[cnt++] = '-';
|
|
}
|
|
lval = tlval = (long)dval;
|
|
scnt = cnt;
|
|
do {
|
|
buf[cnt++] = '0' + (tlval % 10);
|
|
} while ((tlval /= 10) != 0);
|
|
ecnt = cnt;
|
|
while (scnt < ecnt) {
|
|
char c = buf[scnt];
|
|
buf[scnt++] = buf[--ecnt];
|
|
buf[ecnt] = c;
|
|
}
|
|
buf[cnt++] = '.';
|
|
dval -= lval;
|
|
if ((ival = fabs(dval) * 1e4 + 0.5) == 0) {
|
|
buf[cnt++] = '0';
|
|
buf[cnt++] = '0';
|
|
buf[cnt++] = '0';
|
|
buf[cnt++] = '0';
|
|
} else {
|
|
if (ival < 1000) buf[cnt++] = '0';
|
|
if (ival < 100) buf[cnt++] = '0';
|
|
if (ival < 10) buf[cnt++] = '0';
|
|
scnt = cnt;
|
|
do {
|
|
buf[cnt++] = '0' + (ival % 10);
|
|
} while ((ival /= 10) != 0);
|
|
ecnt = cnt;
|
|
while (scnt < ecnt) {
|
|
char c = buf[scnt];
|
|
buf[scnt++] = buf[--ecnt];
|
|
buf[ecnt] = c;
|
|
}
|
|
}
|
|
buf[cnt] = 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* optional features available? */
|
|
|
|
#if (defined(__GNUC__) || defined(__clang__)) && ! defined(__STRICT_ANSI__)
|
|
# define PIC_DIRECT_THREADED_VM 1
|
|
#else
|
|
# define PIC_DIRECT_THREADED_VM 0
|
|
#endif
|
|
|
|
#if __x86_64__ && (defined(__GNUC__) || defined(__clang__)) && ! defined(__STRICT_ANSI__)
|
|
# include <stdint.h>
|
|
# define PIC_NAN_BOXING 1
|
|
#else
|
|
# define PIC_NAN_BOXING 0
|
|
#endif
|