slovo/runtime/runtime.c

1628 lines
40 KiB
C

#define _POSIX_C_SOURCE 200809L
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
typedef struct {
int32_t len;
int32_t *data;
} __glagol_vec_i32;
typedef struct {
int32_t len;
int64_t *data;
} __glagol_vec_i64;
typedef struct {
int32_t len;
double *data;
} __glagol_vec_f64;
typedef struct {
int32_t len;
bool *data;
} __glagol_vec_bool;
typedef struct {
int32_t len;
char **data;
} __glagol_vec_string;
static int32_t __glagol_process_argc_value = 0;
static char **__glagol_process_argv_value = NULL;
static bool __glagol_time_base_initialized = false;
static struct timespec __glagol_time_base;
#define __GLAGOL_FILE_HANDLE_LIMIT 1024
static FILE *__glagol_file_handles[__GLAGOL_FILE_HANDLE_LIMIT];
#define __GLAGOL_NET_HANDLE_LIMIT 1024
static int __glagol_net_handles[__GLAGOL_NET_HANDLE_LIMIT];
static int64_t __glagol_result_i32_encode(uint32_t status, int32_t payload) {
uint64_t encoded = ((uint64_t)status << 32) | (uint32_t)payload;
return (int64_t)encoded;
}
static int64_t __glagol_result_i32_err(void) {
return __glagol_result_i32_encode(1, 1);
}
static int32_t __glagol_net_handle_insert(int fd) {
if (fd < 0 || fd == INT_MAX) {
return 0;
}
for (int32_t handle = 1; handle < __GLAGOL_NET_HANDLE_LIMIT; handle++) {
if (__glagol_net_handles[handle] == 0) {
__glagol_net_handles[handle] = fd + 1;
return handle;
}
}
return 0;
}
static int __glagol_net_handle_get(int32_t handle) {
if (handle <= 0 || handle >= __GLAGOL_NET_HANDLE_LIMIT) {
return -1;
}
int stored = __glagol_net_handles[handle];
return stored == 0 ? -1 : stored - 1;
}
static int __glagol_net_handle_take(int32_t handle) {
int fd = __glagol_net_handle_get(handle);
if (fd >= 0) {
__glagol_net_handles[handle] = 0;
}
return fd;
}
static bool __glagol_net_connect_port_is_valid(int32_t port) {
return port > 0 && port <= 65535;
}
static bool __glagol_net_bind_port_is_valid(int32_t port) {
return port >= 0 && port <= 65535;
}
static struct sockaddr_in __glagol_net_loopback_addr(int32_t port) {
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons((uint16_t)port);
return addr;
}
void print_i32(int32_t value) {
printf("%d\n", value);
}
void print_i64(int64_t value) {
printf("%" PRId64 "\n", value);
}
void print_u32(uint32_t value) {
printf("%" PRIu32 "\n", value);
}
void print_u64(uint64_t value) {
printf("%" PRIu64 "\n", value);
}
void print_f64(double value) {
printf("%.17g\n", value);
}
void print_string(const char *value) {
puts(value);
}
void print_bool(bool value) {
puts(value ? "true" : "false");
}
int32_t string_len(const char *value) {
return (int32_t)strlen(value);
}
void __glagol_io_eprint(const char *value) {
fputs(value, stderr);
}
char *__glagol_io_read_stdin_result(void) {
size_t capacity = 1024;
size_t length = 0;
char *value = malloc(capacity);
if (value == NULL) {
return NULL;
}
for (;;) {
if (length == capacity) {
if (capacity > SIZE_MAX / 2) {
free(value);
return NULL;
}
size_t next_capacity = capacity * 2;
char *next = realloc(value, next_capacity);
if (next == NULL) {
free(value);
return NULL;
}
value = next;
capacity = next_capacity;
}
size_t available = capacity - length;
size_t read_count = fread(value + length, 1, available, stdin);
length += read_count;
if (read_count < available) {
if (ferror(stdin)) {
free(value);
return NULL;
}
break;
}
}
if (length == capacity) {
if (capacity == SIZE_MAX) {
free(value);
return NULL;
}
char *next = realloc(value, capacity + 1);
if (next == NULL) {
free(value);
return NULL;
}
value = next;
}
value[length] = '\0';
return value;
}
void __glagol_process_init(int32_t argc, char **argv) {
__glagol_process_argc_value = argc;
__glagol_process_argv_value = argv;
}
int32_t __glagol_process_argc(void) {
return __glagol_process_argc_value;
}
static void __glagol_process_arg_trap(void) {
fputs("slovo runtime error: process argument index out of bounds\n", stderr);
exit(1);
}
char *__glagol_process_arg(int32_t index) {
if (index < 0 || index >= __glagol_process_argc_value || __glagol_process_argv_value == NULL) {
__glagol_process_arg_trap();
}
return __glagol_process_argv_value[index];
}
char *__glagol_process_arg_result(int32_t index) {
if (index < 0 || index >= __glagol_process_argc_value || __glagol_process_argv_value == NULL) {
return NULL;
}
return __glagol_process_argv_value[index];
}
char *__glagol_env_get(const char *name) {
char *value = getenv(name);
return value == NULL ? "" : value;
}
char *__glagol_env_get_result(const char *name) {
return getenv(name);
}
static char *__glagol_read_remaining_text(FILE *file) {
size_t capacity = 1024;
size_t length = 0;
char *value = malloc(capacity);
if (value == NULL) {
return NULL;
}
for (;;) {
if (length == capacity) {
if (capacity > SIZE_MAX / 2) {
free(value);
return NULL;
}
size_t next_capacity = capacity * 2;
char *next = realloc(value, next_capacity);
if (next == NULL) {
free(value);
return NULL;
}
value = next;
capacity = next_capacity;
}
size_t available = capacity - length;
size_t read_count = fread(value + length, 1, available, file);
length += read_count;
if (read_count < available) {
if (ferror(file)) {
free(value);
return NULL;
}
break;
}
}
if (length == capacity) {
if (capacity == SIZE_MAX) {
free(value);
return NULL;
}
char *next = realloc(value, capacity + 1);
if (next == NULL) {
free(value);
return NULL;
}
value = next;
}
value[length] = '\0';
return value;
}
static FILE *__glagol_file_handle_get(int32_t handle) {
if (handle <= 0 || handle >= __GLAGOL_FILE_HANDLE_LIMIT) {
return NULL;
}
return __glagol_file_handles[handle];
}
static int32_t __glagol_file_handle_insert(FILE *file) {
for (int32_t handle = 1; handle < __GLAGOL_FILE_HANDLE_LIMIT; handle++) {
if (__glagol_file_handles[handle] == NULL) {
__glagol_file_handles[handle] = file;
return handle;
}
}
return 0;
}
static void __glagol_fs_read_trap(void) {
fputs("slovo runtime error: file read failed\n", stderr);
exit(1);
}
char *__glagol_fs_read_text(const char *path) {
FILE *file = fopen(path, "rb");
if (file == NULL) {
__glagol_fs_read_trap();
}
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
__glagol_fs_read_trap();
}
long length = ftell(file);
if (length < 0) {
fclose(file);
__glagol_fs_read_trap();
}
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
__glagol_fs_read_trap();
}
size_t size = (size_t)length;
char *value = malloc(size + 1);
if (value == NULL) {
fclose(file);
__glagol_fs_read_trap();
}
if (size > 0 && fread(value, 1, size, file) != size) {
free(value);
fclose(file);
__glagol_fs_read_trap();
}
if (fclose(file) != 0) {
free(value);
__glagol_fs_read_trap();
}
value[size] = '\0';
return value;
}
char *__glagol_fs_read_text_result(const char *path) {
FILE *file = fopen(path, "rb");
if (file == NULL) {
return NULL;
}
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return NULL;
}
long length = ftell(file);
if (length < 0) {
fclose(file);
return NULL;
}
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return NULL;
}
size_t size = (size_t)length;
char *value = malloc(size + 1);
if (value == NULL) {
fclose(file);
return NULL;
}
if (size > 0 && fread(value, 1, size, file) != size) {
free(value);
fclose(file);
return NULL;
}
if (fclose(file) != 0) {
free(value);
return NULL;
}
value[size] = '\0';
return value;
}
int32_t __glagol_fs_write_text(const char *path, const char *text) {
FILE *file = fopen(path, "wb");
if (file == NULL) {
return 1;
}
size_t len = strlen(text);
if (len > 0 && fwrite(text, 1, len, file) != len) {
fclose(file);
return 1;
}
if (fclose(file) != 0) {
return 1;
}
return 0;
}
int32_t __glagol_fs_write_text_result(const char *path, const char *text) {
return __glagol_fs_write_text(path, text);
}
bool __glagol_fs_exists(const char *path) {
struct stat info;
return stat(path, &info) == 0;
}
bool __glagol_fs_is_file(const char *path) {
struct stat info;
return stat(path, &info) == 0 && S_ISREG(info.st_mode);
}
bool __glagol_fs_is_dir(const char *path) {
struct stat info;
return stat(path, &info) == 0 && S_ISDIR(info.st_mode);
}
int32_t __glagol_fs_remove_file_result(const char *path) {
return unlink(path) == 0 ? 0 : 1;
}
int32_t __glagol_fs_create_dir_result(const char *path) {
return mkdir(path, 0777) == 0 ? 0 : 1;
}
int64_t __glagol_fs_open_text_read_result(const char *path) {
FILE *file = fopen(path, "rb");
if (file == NULL) {
return __glagol_result_i32_err();
}
int32_t handle = __glagol_file_handle_insert(file);
if (handle == 0) {
fclose(file);
return __glagol_result_i32_err();
}
return __glagol_result_i32_encode(0, handle);
}
char *__glagol_fs_read_open_text_result(int32_t handle) {
FILE *file = __glagol_file_handle_get(handle);
if (file == NULL) {
return NULL;
}
return __glagol_read_remaining_text(file);
}
int32_t __glagol_fs_close_result(int32_t handle) {
FILE *file = __glagol_file_handle_get(handle);
if (file == NULL) {
return 1;
}
__glagol_file_handles[handle] = NULL;
return fclose(file) == 0 ? 0 : 1;
}
int64_t __glagol_net_tcp_connect_loopback_result(int32_t port) {
if (!__glagol_net_connect_port_is_valid(port)) {
return __glagol_result_i32_err();
}
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
return __glagol_result_i32_err();
}
struct sockaddr_in addr = __glagol_net_loopback_addr(port);
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
close(fd);
return __glagol_result_i32_err();
}
int32_t handle = __glagol_net_handle_insert(fd);
if (handle == 0) {
close(fd);
return __glagol_result_i32_err();
}
return __glagol_result_i32_encode(0, handle);
}
int64_t __glagol_net_tcp_listen_loopback_result(int32_t port) {
if (!__glagol_net_bind_port_is_valid(port)) {
return __glagol_result_i32_err();
}
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
return __glagol_result_i32_err();
}
int enabled = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) != 0) {
close(fd);
return __glagol_result_i32_err();
}
struct sockaddr_in addr = __glagol_net_loopback_addr(port);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
close(fd);
return __glagol_result_i32_err();
}
if (listen(fd, 1) != 0) {
close(fd);
return __glagol_result_i32_err();
}
int32_t handle = __glagol_net_handle_insert(fd);
if (handle == 0) {
close(fd);
return __glagol_result_i32_err();
}
return __glagol_result_i32_encode(0, handle);
}
int64_t __glagol_net_tcp_bound_port_result(int32_t handle) {
int fd = __glagol_net_handle_get(handle);
if (fd < 0) {
return __glagol_result_i32_err();
}
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
if (getsockname(fd, (struct sockaddr *)&addr, &addr_len) != 0) {
return __glagol_result_i32_err();
}
return __glagol_result_i32_encode(0, (int32_t)ntohs(addr.sin_port));
}
int64_t __glagol_net_tcp_accept_result(int32_t listener) {
int fd = __glagol_net_handle_get(listener);
if (fd < 0) {
return __glagol_result_i32_err();
}
int accepted;
do {
accepted = accept(fd, NULL, NULL);
} while (accepted < 0 && errno == EINTR);
if (accepted < 0) {
return __glagol_result_i32_err();
}
int32_t handle = __glagol_net_handle_insert(accepted);
if (handle == 0) {
close(accepted);
return __glagol_result_i32_err();
}
return __glagol_result_i32_encode(0, handle);
}
char *__glagol_net_tcp_read_all_result(int32_t handle) {
int fd = __glagol_net_handle_get(handle);
if (fd < 0) {
return NULL;
}
size_t capacity = 1024;
size_t length = 0;
char *value = malloc(capacity);
if (value == NULL) {
return NULL;
}
for (;;) {
if (length == capacity) {
if (capacity > SIZE_MAX / 2) {
free(value);
return NULL;
}
size_t next_capacity = capacity * 2;
char *next = realloc(value, next_capacity);
if (next == NULL) {
free(value);
return NULL;
}
value = next;
capacity = next_capacity;
}
size_t available = capacity - length;
if (available > (size_t)SSIZE_MAX) {
available = (size_t)SSIZE_MAX;
}
ssize_t read_count = recv(fd, value + length, available, 0);
if (read_count < 0) {
if (errno == EINTR) {
continue;
}
free(value);
return NULL;
}
if (read_count == 0) {
break;
}
length += (size_t)read_count;
}
if (length == capacity) {
if (capacity == SIZE_MAX) {
free(value);
return NULL;
}
char *next = realloc(value, capacity + 1);
if (next == NULL) {
free(value);
return NULL;
}
value = next;
}
value[length] = '\0';
return value;
}
int32_t __glagol_net_tcp_write_text_result(int32_t handle, const char *text) {
int fd = __glagol_net_handle_get(handle);
if (fd < 0) {
return 1;
}
size_t length = strlen(text);
size_t written = 0;
while (written < length) {
size_t remaining = length - written;
if (remaining > (size_t)SSIZE_MAX) {
remaining = (size_t)SSIZE_MAX;
}
#ifdef MSG_NOSIGNAL
ssize_t sent = send(fd, text + written, remaining, MSG_NOSIGNAL);
#else
ssize_t sent = send(fd, text + written, remaining, 0);
#endif
if (sent < 0) {
if (errno == EINTR) {
continue;
}
return 1;
}
if (sent == 0) {
return 1;
}
written += (size_t)sent;
}
return 0;
}
int32_t __glagol_net_tcp_close_result(int32_t handle) {
int fd = __glagol_net_handle_take(handle);
if (fd < 0) {
return 1;
}
return close(fd) == 0 ? 0 : 1;
}
static void __glagol_allocation_trap(void) {
fputs("slovo runtime error: string allocation failed\n", stderr);
exit(1);
}
char *__glagol_string_concat(const char *left, const char *right) {
size_t left_len = strlen(left);
size_t right_len = strlen(right);
if (left_len > SIZE_MAX - right_len - 1) {
__glagol_allocation_trap();
}
char *value = malloc(left_len + right_len + 1);
if (value == NULL) {
__glagol_allocation_trap();
}
memcpy(value, left, left_len);
memcpy(value + left_len, right, right_len + 1);
return value;
}
static char __glagol_json_hex_digit(unsigned char value) {
return value < 10u ? (char)('0' + value) : (char)('A' + (value - 10u));
}
static void __glagol_json_add_size(size_t *total, size_t amount) {
if (*total > SIZE_MAX - amount) {
__glagol_allocation_trap();
}
*total += amount;
}
char *__glagol_json_quote_string(const char *text) {
if (text == NULL) {
text = "";
}
size_t escaped_len = 2u;
const unsigned char *cursor = (const unsigned char *)text;
while (*cursor != '\0') {
switch (*cursor) {
case '"':
case '\\':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
__glagol_json_add_size(&escaped_len, 2u);
break;
default:
__glagol_json_add_size(&escaped_len, *cursor < 0x20u ? 6u : 1u);
break;
}
cursor++;
}
if (escaped_len == SIZE_MAX) {
__glagol_allocation_trap();
}
char *value = malloc(escaped_len + 1u);
if (value == NULL) {
__glagol_allocation_trap();
}
char *out = value;
*out++ = '"';
cursor = (const unsigned char *)text;
while (*cursor != '\0') {
unsigned char ch = *cursor;
switch (ch) {
case '"':
*out++ = '\\';
*out++ = '"';
break;
case '\\':
*out++ = '\\';
*out++ = '\\';
break;
case '\b':
*out++ = '\\';
*out++ = 'b';
break;
case '\f':
*out++ = '\\';
*out++ = 'f';
break;
case '\n':
*out++ = '\\';
*out++ = 'n';
break;
case '\r':
*out++ = '\\';
*out++ = 'r';
break;
case '\t':
*out++ = '\\';
*out++ = 't';
break;
default:
if (ch < 0x20u) {
*out++ = '\\';
*out++ = 'u';
*out++ = '0';
*out++ = '0';
*out++ = __glagol_json_hex_digit((unsigned char)(ch >> 4u));
*out++ = __glagol_json_hex_digit((unsigned char)(ch & 0x0Fu));
} else {
*out++ = (char)ch;
}
break;
}
cursor++;
}
*out++ = '"';
*out = '\0';
return value;
}
static char *__glagol_num_u64_to_string_impl(uint64_t magnitude, bool negative) {
char reversed[20];
size_t digit_count = 0;
do {
reversed[digit_count] = (char)(0x30u + (magnitude % 10u));
digit_count++;
magnitude /= 10u;
} while (magnitude != 0u);
size_t sign_count = negative ? 1u : 0u;
char *value = malloc(sign_count + digit_count + 1u);
if (value == NULL) {
__glagol_allocation_trap();
}
size_t cursor = 0;
if (negative) {
value[cursor] = (char)0x2du;
cursor++;
}
for (size_t i = 0; i < digit_count; i++) {
value[cursor + i] = reversed[digit_count - i - 1u];
}
value[cursor + digit_count] = '\0';
return value;
}
static char *__glagol_num_i64_to_string_impl(int64_t value) {
bool negative = value < 0;
uint64_t magnitude = negative
? (uint64_t)(-(value + 1)) + 1u
: (uint64_t)value;
return __glagol_num_u64_to_string_impl(magnitude, negative);
}
char *__glagol_num_i32_to_string(int32_t value) {
return __glagol_num_i64_to_string_impl((int64_t)value);
}
char *__glagol_num_u32_to_string(uint32_t value) {
return __glagol_num_u64_to_string_impl((uint64_t)value, false);
}
char *__glagol_num_i64_to_string(int64_t value) {
return __glagol_num_i64_to_string_impl(value);
}
char *__glagol_num_u64_to_string(uint64_t value) {
return __glagol_num_u64_to_string_impl(value, false);
}
char *__glagol_num_f64_to_string(double value) {
int length = snprintf(NULL, 0, "%.17f", value);
if (length < 0) {
__glagol_allocation_trap();
}
char *text = malloc((size_t)length + 1u);
if (text == NULL) {
__glagol_allocation_trap();
}
int written = snprintf(text, (size_t)length + 1u, "%.17f", value);
if (written != length) {
free(text);
__glagol_allocation_trap();
}
char *dot = strchr(text, '.');
if (dot != NULL) {
char *last = text + strlen(text) - 1u;
while (last > dot + 1 && *last == '0') {
*last = '\0';
last--;
}
}
return text;
}
bool __glagol_string_eq(const char *left, const char *right) {
return strcmp(left, right) == 0;
}
static int64_t __glagol_string_parse_i32_result_encode(uint32_t status, int32_t payload) {
uint64_t encoded = ((uint64_t)status << 32) | (uint32_t)payload;
return (int64_t)encoded;
}
static int64_t __glagol_string_parse_i32_result_err(void) {
return __glagol_string_parse_i32_result_encode(1, 1);
}
int64_t __glagol_string_parse_i32_result(const char *text) {
if (text == NULL || text[0] == '\0') {
return __glagol_string_parse_i32_result_err();
}
const unsigned char *cursor = (const unsigned char *)text;
bool negative = false;
if (*cursor == '-') {
negative = true;
cursor++;
if (*cursor == '\0') {
return __glagol_string_parse_i32_result_err();
}
}
int64_t limit = negative ? 2147483648LL : 2147483647LL;
int64_t value = 0;
while (*cursor != '\0') {
unsigned char byte = *cursor;
if (byte < '0' || byte > '9') {
return __glagol_string_parse_i32_result_err();
}
int64_t digit = (int64_t)(byte - '0');
if (value > (limit - digit) / 10) {
return __glagol_string_parse_i32_result_err();
}
value = value * 10 + digit;
cursor++;
}
int32_t parsed;
if (negative) {
parsed = value == 2147483648LL ? INT32_MIN : (int32_t)-value;
} else {
parsed = (int32_t)value;
}
return __glagol_string_parse_i32_result_encode(0, parsed);
}
static int64_t __glagol_string_parse_u32_result_err(void) {
return __glagol_string_parse_i32_result_encode(1, 1);
}
int64_t __glagol_string_parse_u32_result(const char *text) {
if (text == NULL || text[0] == '\0') {
return __glagol_string_parse_u32_result_err();
}
const unsigned char *cursor = (const unsigned char *)text;
uint64_t value = 0;
while (*cursor != '\0') {
unsigned char byte = *cursor;
if (byte < '0' || byte > '9') {
return __glagol_string_parse_u32_result_err();
}
uint64_t digit = (uint64_t)(byte - '0');
if (value > (((uint64_t)UINT32_MAX) - digit) / 10u) {
return __glagol_string_parse_u32_result_err();
}
value = value * 10u + digit;
cursor++;
}
return __glagol_string_parse_i32_result_encode(0, (int32_t)(uint32_t)value);
}
int32_t __glagol_string_parse_i64_result(const char *text, int64_t *out) {
if (out == NULL || text == NULL || text[0] == '\0') {
return 1;
}
const unsigned char *cursor = (const unsigned char *)text;
bool negative = false;
if (*cursor == '-') {
negative = true;
cursor++;
if (*cursor == '\0') {
return 1;
}
}
uint64_t limit = negative ? ((uint64_t)INT64_MAX + 1u) : (uint64_t)INT64_MAX;
uint64_t value = 0;
while (*cursor != '\0') {
unsigned char byte = *cursor;
if (byte < '0' || byte > '9') {
return 1;
}
uint64_t digit = (uint64_t)(byte - '0');
if (value > (limit - digit) / 10u) {
return 1;
}
value = value * 10u + digit;
cursor++;
}
if (negative) {
*out = value == ((uint64_t)INT64_MAX + 1u) ? INT64_MIN : -(int64_t)value;
} else {
*out = (int64_t)value;
}
return 0;
}
int32_t __glagol_string_parse_u64_result(const char *text, uint64_t *out) {
if (out == NULL || text == NULL || text[0] == '\0') {
return 1;
}
const unsigned char *cursor = (const unsigned char *)text;
uint64_t value = 0;
while (*cursor != '\0') {
unsigned char byte = *cursor;
if (byte < '0' || byte > '9') {
return 1;
}
uint64_t digit = (uint64_t)(byte - '0');
if (value > (UINT64_MAX - digit) / 10u) {
return 1;
}
value = value * 10u + digit;
cursor++;
}
*out = value;
return 0;
}
static size_t __glagol_consume_ascii_digits(const unsigned char *text, size_t index) {
while (text[index] >= '0' && text[index] <= '9') {
index++;
}
return index;
}
static bool __glagol_string_is_ascii_decimal_f64(const char *text) {
if (text == NULL || text[0] == '\0') {
return false;
}
const unsigned char *bytes = (const unsigned char *)text;
size_t index = 0;
if (bytes[index] == '-') {
index++;
if (bytes[index] == '\0') {
return false;
}
}
size_t whole_start = index;
index = __glagol_consume_ascii_digits(bytes, index);
size_t whole_digits = index - whole_start;
if (whole_digits == 0 || bytes[index] != '.') {
return false;
}
index++;
size_t fractional_start = index;
index = __glagol_consume_ascii_digits(bytes, index);
size_t fractional_digits = index - fractional_start;
if (fractional_digits == 0) {
return false;
}
return bytes[index] == '\0';
}
int32_t __glagol_string_parse_f64_result(const char *text, double *out) {
if (out == NULL || !__glagol_string_is_ascii_decimal_f64(text)) {
return 1;
}
errno = 0;
char *end = NULL;
double value = strtod(text, &end);
if (errno == ERANGE || end == text || end == NULL || *end != '\0' || !isfinite(value)) {
return 1;
}
*out = value;
return 0;
}
int32_t __glagol_string_parse_bool_result(const char *text, bool *out) {
if (out == NULL || text == NULL) {
return 1;
}
if (strcmp(text, "true") == 0) {
*out = true;
return 0;
}
if (strcmp(text, "false") == 0) {
*out = false;
return 0;
}
return 1;
}
static void __glagol_vec_i32_allocation_trap(void) {
fputs("slovo runtime error: vector allocation failed\n", stderr);
exit(1);
}
static void __glagol_vec_i32_index_trap(void) {
fputs("slovo runtime error: vector index out of bounds\n", stderr);
exit(1);
}
__glagol_vec_i32 *__glagol_vec_i32_empty(void) {
__glagol_vec_i32 *value = malloc(sizeof(__glagol_vec_i32));
if (value == NULL) {
__glagol_vec_i32_allocation_trap();
}
value->len = 0;
value->data = NULL;
return value;
}
__glagol_vec_i32 *__glagol_vec_i32_append(const __glagol_vec_i32 *source, int32_t element) {
if (source->len < 0 || source->len == INT32_MAX) {
__glagol_vec_i32_allocation_trap();
}
size_t old_len = (size_t)source->len;
size_t new_len = old_len + 1;
if (new_len > SIZE_MAX / sizeof(int32_t)) {
__glagol_vec_i32_allocation_trap();
}
__glagol_vec_i32 *value = malloc(sizeof(__glagol_vec_i32));
if (value == NULL) {
__glagol_vec_i32_allocation_trap();
}
int32_t *data = malloc(new_len * sizeof(int32_t));
if (data == NULL) {
free(value);
__glagol_vec_i32_allocation_trap();
}
if (old_len > 0) {
memcpy(data, source->data, old_len * sizeof(int32_t));
}
data[old_len] = element;
value->len = (int32_t)new_len;
value->data = data;
return value;
}
int32_t __glagol_vec_i32_len(const __glagol_vec_i32 *value) {
return value->len;
}
int32_t __glagol_vec_i32_index(const __glagol_vec_i32 *value, int32_t index) {
if (index < 0 || index >= value->len) {
__glagol_vec_i32_index_trap();
}
return value->data[index];
}
bool __glagol_vec_i32_eq(const __glagol_vec_i32 *left, const __glagol_vec_i32 *right) {
if (left->len != right->len) {
return false;
}
if (left->len == 0) {
return true;
}
return memcmp(left->data, right->data, (size_t)left->len * sizeof(int32_t)) == 0;
}
static void __glagol_vec_i64_allocation_trap(void) {
fputs("slovo runtime error: vector allocation failed\n", stderr);
exit(1);
}
static void __glagol_vec_i64_index_trap(void) {
fputs("slovo runtime error: vector index out of bounds\n", stderr);
exit(1);
}
__glagol_vec_i64 *__glagol_vec_i64_empty(void) {
__glagol_vec_i64 *value = malloc(sizeof(__glagol_vec_i64));
if (value == NULL) {
__glagol_vec_i64_allocation_trap();
}
value->len = 0;
value->data = NULL;
return value;
}
__glagol_vec_i64 *__glagol_vec_i64_append(const __glagol_vec_i64 *source, int64_t element) {
if (source->len < 0 || source->len == INT32_MAX) {
__glagol_vec_i64_allocation_trap();
}
size_t old_len = (size_t)source->len;
size_t new_len = old_len + 1;
if (new_len > SIZE_MAX / sizeof(int64_t)) {
__glagol_vec_i64_allocation_trap();
}
__glagol_vec_i64 *value = malloc(sizeof(__glagol_vec_i64));
if (value == NULL) {
__glagol_vec_i64_allocation_trap();
}
int64_t *data = malloc(new_len * sizeof(int64_t));
if (data == NULL) {
free(value);
__glagol_vec_i64_allocation_trap();
}
if (old_len > 0) {
memcpy(data, source->data, old_len * sizeof(int64_t));
}
data[old_len] = element;
value->len = (int32_t)new_len;
value->data = data;
return value;
}
int32_t __glagol_vec_i64_len(const __glagol_vec_i64 *value) {
return value->len;
}
int64_t __glagol_vec_i64_index(const __glagol_vec_i64 *value, int32_t index) {
if (index < 0 || index >= value->len) {
__glagol_vec_i64_index_trap();
}
return value->data[index];
}
bool __glagol_vec_i64_eq(const __glagol_vec_i64 *left, const __glagol_vec_i64 *right) {
if (left->len != right->len) {
return false;
}
if (left->len == 0) {
return true;
}
return memcmp(left->data, right->data, (size_t)left->len * sizeof(int64_t)) == 0;
}
static void __glagol_vec_f64_allocation_trap(void) {
fputs("slovo runtime error: vector allocation failed\n", stderr);
exit(1);
}
static void __glagol_vec_f64_index_trap(void) {
fputs("slovo runtime error: vector index out of bounds\n", stderr);
exit(1);
}
__glagol_vec_f64 *__glagol_vec_f64_empty(void) {
__glagol_vec_f64 *value = malloc(sizeof(__glagol_vec_f64));
if (value == NULL) {
__glagol_vec_f64_allocation_trap();
}
value->len = 0;
value->data = NULL;
return value;
}
__glagol_vec_f64 *__glagol_vec_f64_append(const __glagol_vec_f64 *source, double element) {
if (source->len < 0 || source->len == INT32_MAX) {
__glagol_vec_f64_allocation_trap();
}
size_t old_len = (size_t)source->len;
size_t new_len = old_len + 1;
if (new_len > SIZE_MAX / sizeof(double)) {
__glagol_vec_f64_allocation_trap();
}
__glagol_vec_f64 *value = malloc(sizeof(__glagol_vec_f64));
if (value == NULL) {
__glagol_vec_f64_allocation_trap();
}
double *data = malloc(new_len * sizeof(double));
if (data == NULL) {
free(value);
__glagol_vec_f64_allocation_trap();
}
if (old_len > 0) {
memcpy(data, source->data, old_len * sizeof(double));
}
data[old_len] = element;
value->len = (int32_t)new_len;
value->data = data;
return value;
}
int32_t __glagol_vec_f64_len(const __glagol_vec_f64 *value) {
return value->len;
}
double __glagol_vec_f64_index(const __glagol_vec_f64 *value, int32_t index) {
if (index < 0 || index >= value->len) {
__glagol_vec_f64_index_trap();
}
return value->data[index];
}
bool __glagol_vec_f64_eq(const __glagol_vec_f64 *left, const __glagol_vec_f64 *right) {
if (left->len != right->len) {
return false;
}
for (int32_t index = 0; index < left->len; index++) {
if (left->data[index] != right->data[index]) {
return false;
}
}
return true;
}
static void __glagol_vec_bool_allocation_trap(void) {
fputs("slovo runtime error: vector allocation failed\n", stderr);
exit(1);
}
static void __glagol_vec_bool_index_trap(void) {
fputs("slovo runtime error: vector index out of bounds\n", stderr);
exit(1);
}
__glagol_vec_bool *__glagol_vec_bool_empty(void) {
__glagol_vec_bool *value = malloc(sizeof(__glagol_vec_bool));
if (value == NULL) {
__glagol_vec_bool_allocation_trap();
}
value->len = 0;
value->data = NULL;
return value;
}
__glagol_vec_bool *__glagol_vec_bool_append(const __glagol_vec_bool *source, bool element) {
if (source->len < 0 || source->len == INT32_MAX) {
__glagol_vec_bool_allocation_trap();
}
size_t old_len = (size_t)source->len;
size_t new_len = old_len + 1;
if (new_len > SIZE_MAX / sizeof(bool)) {
__glagol_vec_bool_allocation_trap();
}
__glagol_vec_bool *value = malloc(sizeof(__glagol_vec_bool));
if (value == NULL) {
__glagol_vec_bool_allocation_trap();
}
bool *data = malloc(new_len * sizeof(bool));
if (data == NULL) {
free(value);
__glagol_vec_bool_allocation_trap();
}
if (old_len > 0) {
memcpy(data, source->data, old_len * sizeof(bool));
}
data[old_len] = element;
value->len = (int32_t)new_len;
value->data = data;
return value;
}
int32_t __glagol_vec_bool_len(const __glagol_vec_bool *value) {
return value->len;
}
bool __glagol_vec_bool_index(const __glagol_vec_bool *value, int32_t index) {
if (index < 0 || index >= value->len) {
__glagol_vec_bool_index_trap();
}
return value->data[index];
}
bool __glagol_vec_bool_eq(const __glagol_vec_bool *left, const __glagol_vec_bool *right) {
if (left->len != right->len) {
return false;
}
if (left->len == 0) {
return true;
}
return memcmp(left->data, right->data, (size_t)left->len * sizeof(bool)) == 0;
}
static void __glagol_vec_string_allocation_trap(void) {
fputs("slovo runtime error: vector allocation failed\n", stderr);
exit(1);
}
static void __glagol_vec_string_index_trap(void) {
fputs("slovo runtime error: vector index out of bounds\n", stderr);
exit(1);
}
__glagol_vec_string *__glagol_vec_string_empty(void) {
__glagol_vec_string *value = malloc(sizeof(__glagol_vec_string));
if (value == NULL) {
__glagol_vec_string_allocation_trap();
}
value->len = 0;
value->data = NULL;
return value;
}
__glagol_vec_string *__glagol_vec_string_append(
const __glagol_vec_string *source,
const char *element
) {
if (source->len < 0 || source->len == INT32_MAX) {
__glagol_vec_string_allocation_trap();
}
size_t old_len = (size_t)source->len;
size_t new_len = old_len + 1;
if (new_len > SIZE_MAX / sizeof(char *)) {
__glagol_vec_string_allocation_trap();
}
__glagol_vec_string *value = malloc(sizeof(__glagol_vec_string));
if (value == NULL) {
__glagol_vec_string_allocation_trap();
}
char **data = malloc(new_len * sizeof(char *));
if (data == NULL) {
free(value);
__glagol_vec_string_allocation_trap();
}
if (old_len > 0) {
memcpy(data, source->data, old_len * sizeof(char *));
}
data[old_len] = (char *)element;
value->len = (int32_t)new_len;
value->data = data;
return value;
}
int32_t __glagol_vec_string_len(const __glagol_vec_string *value) {
return value->len;
}
char *__glagol_vec_string_index(const __glagol_vec_string *value, int32_t index) {
if (index < 0 || index >= value->len) {
__glagol_vec_string_index_trap();
}
return value->data[index];
}
bool __glagol_vec_string_eq(const __glagol_vec_string *left, const __glagol_vec_string *right) {
if (left->len != right->len) {
return false;
}
for (int32_t index = 0; index < left->len; index++) {
if (strcmp(left->data[index], right->data[index]) != 0) {
return false;
}
}
return true;
}
static void __glagol_time_monotonic_unavailable_trap(void) {
fputs("slovo runtime error: monotonic time unavailable\n", stderr);
exit(1);
}
static void __glagol_time_sleep_negative_trap(void) {
fputs("slovo runtime error: sleep_ms negative duration\n", stderr);
exit(1);
}
static void __glagol_time_sleep_failed_trap(void) {
fputs("slovo runtime error: sleep failed\n", stderr);
exit(1);
}
int32_t __glagol_time_monotonic_ms(void) {
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
__glagol_time_monotonic_unavailable_trap();
}
if (!__glagol_time_base_initialized) {
__glagol_time_base = now;
__glagol_time_base_initialized = true;
return 0;
}
time_t seconds = now.tv_sec - __glagol_time_base.tv_sec;
long nanoseconds = now.tv_nsec - __glagol_time_base.tv_nsec;
if (nanoseconds < 0) {
seconds -= 1;
nanoseconds += 1000000000L;
}
if (seconds < 0) {
return 0;
}
if (seconds > INT32_MAX / 1000) {
return INT32_MAX;
}
int64_t milliseconds = (int64_t)seconds * 1000 + nanoseconds / 1000000L;
if (milliseconds > INT32_MAX) {
return INT32_MAX;
}
return (int32_t)milliseconds;
}
void __glagol_time_sleep_ms(int32_t ms) {
if (ms < 0) {
__glagol_time_sleep_negative_trap();
}
struct timespec remaining;
remaining.tv_sec = ms / 1000;
remaining.tv_nsec = (long)(ms % 1000) * 1000000L;
while (nanosleep(&remaining, &remaining) != 0) {
if (errno != EINTR) {
__glagol_time_sleep_failed_trap();
}
}
}
static void __glagol_random_i32_unavailable_trap(void) {
fputs("slovo runtime error: random i32 unavailable\n", stderr);
exit(1);
}
int32_t __glagol_random_i32(void) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
__glagol_random_i32_unavailable_trap();
}
uint32_t value = 0;
unsigned char *cursor = (unsigned char *)&value;
size_t remaining = sizeof(value);
while (remaining > 0) {
ssize_t n = read(fd, cursor, remaining);
if (n < 0) {
if (errno == EINTR) {
continue;
}
close(fd);
__glagol_random_i32_unavailable_trap();
}
if (n == 0) {
close(fd);
__glagol_random_i32_unavailable_trap();
}
cursor += n;
remaining -= (size_t)n;
}
if (close(fd) != 0) {
__glagol_random_i32_unavailable_trap();
}
return (int32_t)(value & INT32_MAX);
}
void __glagol_array_bounds_trap(void) {
fputs("slovo runtime error: array index out of bounds\n", stderr);
exit(1);
}
void __glagol_unwrap_some_trap(void) {
fputs("slovo runtime error: unwrap_some on none\n", stderr);
exit(1);
}
void __glagol_unwrap_ok_trap(void) {
fputs("slovo runtime error: unwrap_ok on err\n", stderr);
exit(1);
}
void __glagol_unwrap_err_trap(void) {
fputs("slovo runtime error: unwrap_err on ok\n", stderr);
exit(1);
}