1115 lines
27 KiB
C
1115 lines
27 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.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;
|
|
|
|
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 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);
|
|
}
|
|
|
|
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_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);
|
|
}
|