9040 lines
253 KiB
C
Raw Normal View History

2024-12-08 16:19:17 +01:00
// RETOOR - Dec 5 2024
#ifndef SORM_H
#define SORM_H
#ifndef SORM_STR_H
#define SORM_STR_H
// RETOOR - Nov 28 2024
// MIT License
// ===========
// Copyright (c) 2024 Retoor
// 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.
#ifndef RLIB_H
#define RLIB_H
// BEGIN OF RLIB
/*
* Line below will be filtered by rmerge
<script language="Javva script" type="woeiii" src="Pony.html" after-tag="after
tag" />
*/
#ifndef RTYPES_H
#define RTYPES_H
#ifdef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#ifndef _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#include <stdbool.h>
#include <stdint.h> // uint
#include <string.h>
#include <sys/types.h> // ulong
#ifndef ulonglong
#define ulonglong unsigned long long
#endif
#ifndef uint
typedef unsigned int uint;
#endif
#ifndef byte
typedef unsigned char byte;
#endif
#ifdef _POSIX_C_SOURCE_TEMP
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
#undef _POSIX_C_SOURCE_TEMP
#else
#undef _POSIX_C_SOURCE
#endif
#endif
#ifndef NSOCK_H
#define NSOCK_H
#ifndef RMALLOC_H
#define RMALLOC_H
#ifndef RMALLOC_OVERRIDE
#define RMALLOC_OVERRIDE 1
#endif
#ifdef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#ifndef _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#ifndef ulonglong
#define ulonglong unsigned long long
#endif
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef RTEMP_H
#define RTEMP_H
#include <pthread.h>
#ifndef RTEMPC_SLOT_COUNT
#define RTEMPC_SLOT_COUNT 20
#endif
#ifndef RTEMPC_SLOT_SIZE
#define RTEMPC_SLOT_SIZE 1024 * 64 * 128
#endif
bool _rtempc_initialized = 0;
pthread_mutex_t _rtempc_thread_lock;
bool rtempc_use_mutex = true;
byte _current_rtempc_slot = 1;
char _rtempc_buffer[RTEMPC_SLOT_COUNT][RTEMPC_SLOT_SIZE];
char *rtempc(char *data) {
if (rtempc_use_mutex) {
if (!_rtempc_initialized) {
_rtempc_initialized = true;
pthread_mutex_init(&_rtempc_thread_lock, NULL);
}
pthread_mutex_lock(&_rtempc_thread_lock);
}
uint current_rtempc_slot = _current_rtempc_slot;
_rtempc_buffer[current_rtempc_slot][0] = 0;
strcpy(_rtempc_buffer[current_rtempc_slot], data);
_current_rtempc_slot++;
if (_current_rtempc_slot == RTEMPC_SLOT_COUNT) {
_current_rtempc_slot = 0;
}
if (rtempc_use_mutex)
pthread_mutex_unlock(&_rtempc_thread_lock);
return _rtempc_buffer[current_rtempc_slot];
}
#define sstring(_pname, _psize) \
static char _##_pname[_psize]; \
_##_pname[0] = 0; \
char *_pname = _##_pname;
#define string(_pname, _psize) \
char _##_pname[_psize]; \
_##_pname[0] = 0; \
char *_pname = _##_pname;
#define sreset(_pname, _psize) _pname = _##_pname;
#define sbuf(val) rtempc(val)
#endif
#ifdef _POSIX_C_SOURCE_TEMP
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
#undef _POSIX_C_SOURCE_TEMP
#else
#undef _POSIX_C_SOURCE
#endif
ulonglong rmalloc_count = 0;
ulonglong rmalloc_alloc_count = 0;
ulonglong rmalloc_free_count = 0;
ulonglong rmalloc_total_bytes_allocated = 0;
void *_rmalloc_prev_realloc_obj = NULL;
size_t _rmalloc_prev_realloc_obj_size = 0;
void *rmalloc(size_t size) {
void *result;
while (!(result = malloc(size))) {
fprintf(stderr, "Warning: malloc failed, trying again.\n");
}
rmalloc_count++;
rmalloc_alloc_count++;
rmalloc_total_bytes_allocated += size;
return result;
}
void *rcalloc(size_t count, size_t size) {
void *result;
while (!(result = calloc(count, size))) {
fprintf(stderr, "Warning: calloc failed, trying again.\n");
}
rmalloc_alloc_count++;
rmalloc_count++;
rmalloc_total_bytes_allocated += count * size;
return result;
}
void *rrealloc(void *obj, size_t size) {
if (!obj) {
rmalloc_count++;
}
rmalloc_alloc_count++;
if (obj == _rmalloc_prev_realloc_obj) {
rmalloc_total_bytes_allocated += size - _rmalloc_prev_realloc_obj_size;
_rmalloc_prev_realloc_obj_size = size - _rmalloc_prev_realloc_obj_size;
} else {
_rmalloc_prev_realloc_obj_size = size;
}
void *result;
while (!(result = realloc(obj, size))) {
fprintf(stderr, "Warning: realloc failed, trying again.\n");
}
_rmalloc_prev_realloc_obj = result;
return result;
}
char *rstrdup(const char *s) {
if (!s)
return NULL;
char *result;
size_t size = strlen(s) + 1;
result = rmalloc(size);
memcpy(result, s, size);
rmalloc_total_bytes_allocated += size;
return result;
}
void *rfree(void *obj) {
rmalloc_count--;
rmalloc_free_count++;
free(obj);
return NULL;
}
#if RMALLOC_OVERRIDE
#define malloc rmalloc
#define calloc rcalloc
#define realloc rrealloc
#define free rfree
#define strdup rstrdup
#endif
char *rmalloc_lld_format(ulonglong num) {
char res[100];
res[0] = 0;
sprintf(res, "%'lld", num);
char *resp = res;
while (*resp) {
if (*resp == ',')
*resp = '.';
resp++;
}
return sbuf(res);
}
char *rmalloc_bytes_format(int factor, ulonglong num) {
char *sizes[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
if (num > 1024) {
return rmalloc_bytes_format(factor + 1, num / 1024);
}
char res[100];
sprintf(res, "%s %s", rmalloc_lld_format(num), sizes[factor]);
return sbuf(res);
}
char *rmalloc_stats() {
static char res[200];
res[0] = 0;
// int original_locale = localeconv();
setlocale(LC_NUMERIC, "en_US.UTF-8");
sprintf(res, "Memory usage: %s, %s (re)allocated, %s unqiue free'd, %s in use.", rmalloc_bytes_format(0, rmalloc_total_bytes_allocated),
rmalloc_lld_format(rmalloc_alloc_count), rmalloc_lld_format(rmalloc_free_count),
rmalloc_lld_format(rmalloc_count));
// setlocale(LC_NUMERIC, original_locale);
setlocale(LC_NUMERIC, "");
return res;
}
#endif
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#ifndef RLIB_RIO
#define RLIB_RIO
#include <dirent.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/dir.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <unistd.h>
#ifndef RSTRING_LIST_H
#define RSTRING_LIST_H
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
typedef struct rstring_list_t {
unsigned int size;
unsigned int count;
char **strings;
} rstring_list_t;
rstring_list_t *rstring_list_new() {
rstring_list_t *rsl = (rstring_list_t *)malloc(sizeof(rstring_list_t));
memset(rsl, 0, sizeof(rstring_list_t));
return rsl;
}
void rstring_list_free(rstring_list_t *rsl) {
for (unsigned int i = 0; i < rsl->size; i++) {
free(rsl->strings[i]);
}
if (rsl->strings)
free(rsl->strings);
free(rsl);
rsl = NULL;
}
void rstring_list_add(rstring_list_t *rsl, char *str) {
if (rsl->count == rsl->size) {
rsl->size++;
rsl->strings = (char **)realloc(rsl->strings, sizeof(char *) * rsl->size);
}
rsl->strings[rsl->count] = strdup(str);
rsl->count++;
}
bool rstring_list_contains(rstring_list_t *rsl, char *str) {
for (unsigned int i = 0; i < rsl->count; i++) {
if (!strcmp(rsl->strings[i], str))
return true;
}
return false;
}
#endif
bool rfile_exists(char *path) {
struct stat s;
return !stat(path, &s);
}
void rjoin_path(char *p1, char *p2, char *output) {
output[0] = 0;
strcpy(output, p1);
if (output[strlen(output) - 1] != '/') {
char slash[] = "/";
strcat(output, slash);
}
if (p2[0] == '/') {
p2++;
}
strcat(output, p2);
}
int risprivatedir(const char *path) {
struct stat statbuf;
if (stat(path, &statbuf) != 0) {
perror("stat");
return -1;
}
if (!S_ISDIR(statbuf.st_mode)) {
return -2;
}
if ((statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == S_IRWXU) {
return 1; // Private (owner has all permissions, others have none)
}
return 0;
}
bool risdir(const char *path) { return !risprivatedir(path); }
void rforfile(char *path, void callback(char *)) {
if (!rfile_exists(path))
return;
DIR *dir = opendir(path);
struct dirent *d;
while ((d = readdir(dir)) != NULL) {
if (!d)
break;
if ((d->d_name[0] == '.' && strlen(d->d_name) == 1) || d->d_name[1] == '.') {
continue;
}
char full_path[4096];
rjoin_path(path, d->d_name, full_path);
if (risdir(full_path)) {
callback(full_path);
rforfile(full_path, callback);
} else {
callback(full_path);
}
}
closedir(dir);
}
bool rfd_wait(int fd, int ms) {
fd_set read_fds;
struct timeval timeout;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
timeout.tv_sec = 0;
timeout.tv_usec = 1000 * ms;
int ret = select(fd + 1, &read_fds, NULL, NULL, &timeout);
return ret > 0 && FD_ISSET(fd, &read_fds);
}
bool rfd_wait_forever(int fd) {
while ((!rfd_wait(fd, 10))) {
}
return true;
}
size_t rfile_size(char *path) {
struct stat s;
stat(path, &s);
return s.st_size;
}
size_t rfile_readb(char *path, void *data, size_t size) {
FILE *fd = fopen(path, "r");
if (!fd) {
return 0;
}
size_t bytes_read = fread(data, sizeof(char), size, fd);
fclose(fd);
((char *)data)[bytes_read] = 0;
return bytes_read;
}
#endif
int *nsock_socks = NULL;
int *nsock_readable = NULL;
void **nsock_data = NULL;
int nsock_server_fd = 0;
int nsock_max_socket_fd = 0;
typedef enum nsock_type_t { NSOCK_NONE = 0, NSOCK_SERVER, NSOCK_CLIENT, NSOCK_UPSTREAM } nsock_type_t;
typedef struct nsock_it {
int fd;
int *upstreams;
bool connected;
bool downstream;
unsigned int upstream_count;
nsock_type_t type;
} nsock_t;
nsock_t **nsocks = NULL;
int nsocks_count = 0;
void (*nsock_on_connect)(int fd) = NULL;
void (*nsock_on_data)(int fd) = NULL;
void (*nsock_on_close)(int fd) = NULL;
void nsock_on_before_data(int fd);
nsock_t *nsock_get(int fd) {
if (nsock_socks[fd] == 0) {
return NULL;
}
if (fd >= nsocks_count || nsocks[fd] == NULL) {
if (fd >= nsocks_count) {
nsocks_count = fd + 1;
nsocks = (nsock_t **)realloc(nsocks, sizeof(nsock_t *) * sizeof(nsock_t) * (nsocks_count));
nsocks[fd] = (nsock_t *)calloc(1, sizeof(nsock_t));
}
nsocks[fd]->upstreams = NULL;
nsocks[fd]->fd = fd;
nsocks[fd]->connected = false;
nsocks[fd]->downstream = false;
nsocks[fd]->upstream_count = 0;
nsocks[fd]->type = NSOCK_CLIENT;
return nsocks[fd];
}
return nsocks[fd];
}
void nsock_close(int fd) {
if (nsock_on_close)
nsock_on_close(fd);
nsock_t *sock = nsock_get(fd);
if (sock) {
for (unsigned int i = 0; i < sock->upstream_count; i++) {
nsock_close(sock->upstreams[i]);
sock->upstreams[i] = 0;
}
if (sock->upstream_count) {
free(sock->upstreams);
}
sock->upstream_count = 0;
sock->connected = false;
}
nsock_socks[fd] = 0;
close(fd);
}
nsock_t *nsock_create(int fd, nsock_type_t type) {
if (fd <= 0)
return NULL;
nsock_socks[fd] = fd;
nsock_t *sock = nsock_get(fd);
sock->connected = true;
sock->downstream = false;
sock->type = type;
return sock;
}
int *nsock_init(int socket_count) {
if (nsock_socks) {
return nsock_socks;
}
nsock_socks = (int *)calloc(1, sizeof(int) * sizeof(int *) * socket_count + 1);
if (nsock_data) {
free(nsock_data);
nsock_data = NULL;
}
nsock_data = (void **)malloc(sizeof(void **) * socket_count + 1);
nsock_socks[socket_count] = -1;
return nsock_socks;
}
void nsock_free() {
if (nsock_socks)
free(nsock_socks);
if (nsock_readable)
free(nsock_readable);
nsock_server_fd = 0;
nsock_max_socket_fd = 0;
if (nsock_data) {
exit(1);
}
}
void nsock_add_upstream(int source, int target, bool downstream) {
if (!nsock_socks[target])
return;
if (!nsock_socks[source])
return;
nsock_t *sock = nsock_get(source);
nsock_t *sock_target = nsock_get(target);
sock_target->type = NSOCK_UPSTREAM;
sock->upstreams = (int *)realloc(sock->upstreams, sizeof(int) * (sock->upstream_count + 1));
sock->downstream = downstream;
sock->upstreams[sock->upstream_count] = target;
sock->upstream_count++;
}
void *nsock_get_data(int socket) { return nsock_data[socket]; }
void nsock_set_data(int socket, void *data) { nsock_data[socket] = data; }
int nsock_connect(const char *host, unsigned int port) {
char port_str[10] = {0};
sprintf(port_str, "%d", port);
int status;
int socket_fd = 0;
struct addrinfo hints;
struct addrinfo *res;
struct addrinfo *p;
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return false;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(host, port_str, &hints, &res)) != 0) {
return 0;
}
for (p = res; p != NULL; p = p->ai_next) {
if ((socket_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
continue;
}
if (connect(socket_fd, p->ai_addr, p->ai_addrlen) == -1) {
close(socket_fd);
continue;
}
break;
}
if (p == NULL) {
freeaddrinfo(res);
return 0;
}
freeaddrinfo(res);
if (socket_fd) {
if (nsock_socks == NULL) {
nsock_init(2048);
}
nsock_socks[socket_fd] = socket_fd;
nsock_t *sock = nsock_create(socket_fd, NSOCK_CLIENT);
sock->connected = true;
}
return socket_fd;
}
void nsock_listen(int port) {
int server_fd;
struct sockaddr_in address;
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket failed");
exit(EXIT_FAILURE);
}
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
if (listen(server_fd, 8096) < 0) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
nsock_server_fd = server_fd;
}
int *nsock_select(suseconds_t timeout) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = timeout;
int server_fd = nsock_server_fd;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(server_fd, &rfds);
int *socks = nsock_socks;
fd_set efds;
FD_ZERO(&efds);
nsock_max_socket_fd = server_fd;
for (int i = 0; socks[i] != -1; i++) {
if (i == server_fd)
continue;
;
if (!socks[i])
continue;
if (socks[i] > nsock_max_socket_fd) {
nsock_max_socket_fd = socks[i];
}
FD_SET(socks[i], &rfds);
FD_SET(socks[i], &efds);
}
int activity = select(nsock_max_socket_fd + 1, &rfds, NULL, &efds, timeout == 0 ? NULL : &tv);
if ((activity < 0) && (errno != EINTR)) {
perror("Select error\n");
return NULL;
} else if (activity == 0) {
return NULL;
}
if (FD_ISSET(server_fd, &rfds)) {
struct sockaddr_in address;
int addrlen = sizeof(address);
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
int new_socket = 0;
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
perror("Accept failed");
} else {
nsock_socks[new_socket] = new_socket;
nsock_create(new_socket, NSOCK_CLIENT);
if (nsock_on_connect)
nsock_on_connect(new_socket);
if (new_socket > nsock_max_socket_fd)
nsock_max_socket_fd = new_socket;
}
}
if (nsock_readable) {
free(nsock_readable);
}
nsock_readable = (int *)calloc(1, sizeof(int *) + sizeof(int) * (nsock_max_socket_fd + 2));
nsock_readable[nsock_max_socket_fd + 1] = -1;
nsock_readable[0] = 0;
int readable_count = 0;
for (int i = 0; i < nsock_max_socket_fd + 1; i++) {
nsock_t *sock = nsock_get(i);
if (!sock)
continue;
if (FD_ISSET(i, &efds)) {
nsock_close(nsock_socks[i]);
nsock_socks[i] = 0;
nsock_readable[i] = 0;
} else if (FD_ISSET(i, &rfds) && i != server_fd) {
nsock_readable[i] = i;
readable_count++;
nsock_on_before_data(i);
} else {
nsock_readable[i] = 0;
sock->connected = false;
}
}
return nsock_readable;
}
unsigned char *nsock_read(int fd, int length) {
if (!nsock_socks[fd])
return NULL;
unsigned char *buffer = (unsigned char *)malloc(length + 1);
int bytes_read = read(fd, buffer, length);
if (bytes_read <= 0) {
nsock_close(fd);
return NULL;
}
buffer[bytes_read] = 0;
return buffer;
}
unsigned char *nsock_read_all(int fd, int length) {
if (!nsock_socks[fd])
return NULL;
unsigned char *buffer = (unsigned char *)malloc(length + 1);
int bytes_read = 0;
while (bytes_read < length) {
int bytes_chunk = read(fd, buffer + bytes_read, length - bytes_read);
if (bytes_chunk <= 0) {
nsock_close(fd);
return NULL;
}
bytes_read += bytes_chunk;
}
buffer[bytes_read] = 0;
return buffer;
}
int nsock_write_all(int fd, unsigned char *data, int length) {
if (!nsock_socks[fd])
return 0;
int bytes_written = 0;
while (bytes_written < length) {
int bytes_chunk = write(fd, data + bytes_written, length - bytes_written);
if (bytes_chunk <= 0) {
nsock_close(fd);
return 0;
}
bytes_written += bytes_chunk;
}
return bytes_written;
}
int nsock_execute_upstream(int source, size_t buffer_size) {
int result = 0;
nsock_t *sock = nsock_get(source);
unsigned char data[buffer_size];
memset(data, 0, buffer_size);
int bytes_read = read(source, data, buffer_size);
if (bytes_read <= 0) {
nsock_close(source);
return 0;
}
bool downstreamed = false;
for (unsigned int i = 0; i < sock->upstream_count; i++) {
if (!nsock_socks[sock->upstreams[i]])
continue;
int bytes_sent = nsock_write_all(sock->upstreams[i], data, bytes_read);
if (bytes_sent <= 0) {
nsock_close(sock->upstreams[i]);
continue;
}
if (sock->downstream && downstreamed == false) {
downstreamed = true;
unsigned char data[4096];
memset(data, 0, 4096);
int bytes_read = read(sock->upstreams[i], data, 4096);
if (bytes_read <= 0) {
nsock_close(source);
return 0;
}
int bytes_sent = nsock_write_all(sock->fd, data, bytes_read);
if (bytes_sent <= 0) {
nsock_close(sock->upstreams[i]);
return 0;
}
}
result++;
}
return result;
}
void nsock_on_before_data(int fd) {
if (!nsock_socks[fd])
return;
nsock_t *sock = nsock_get(fd);
if (sock->upstream_count) {
int upstreamed_to_count = nsock_execute_upstream(fd, 4096);
if (!upstreamed_to_count) {
nsock_close(fd);
}
return;
} else if (sock->type == NSOCK_UPSTREAM) {
while (rfd_wait(sock->fd, 0)) {
unsigned char *data = nsock_read(fd, 4096);
(void)data;
}
}
if (nsock_on_data)
nsock_on_data(fd);
}
void nsock(int port, void (*on_connect)(int fd), void (*on_data)(int fd), void (*on_close)(int fd)) {
nsock_init(2048);
nsock_listen(port);
nsock_on_connect = on_connect;
nsock_on_data = on_data;
nsock_on_close = on_close;
int serve_in_terminal = nsock_on_connect == NULL && nsock_on_data == NULL && nsock_on_close == NULL;
while (1) {
int *readable = nsock_select(0);
if (!serve_in_terminal)
continue;
if (!readable)
continue;
for (int i = 0; readable[i] != -1; i++) {
if (!readable[i])
continue;
char buffer[1024] = {0};
int bytes_read = read(readable[i], buffer, 1);
buffer[bytes_read] = 0;
if (bytes_read <= 0) {
nsock_close(readable[i]);
continue;
}
if (write(readable[i], buffer, bytes_read) <= 0) {
nsock_close(readable[i]);
continue;
}
}
}
}
#endif
#ifndef UUID_H
#define UUID_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct {
unsigned char bytes[16];
} UUID;
void generate_random_bytes(unsigned char *bytes, size_t len) {
for (size_t i = 0; i < len; i++) {
bytes[i] = rand() % 256;
}
}
UUID generate_uuid4(void) {
UUID uuid;
generate_random_bytes(uuid.bytes, 16);
uuid.bytes[6] &= 0x0f;
uuid.bytes[6] |= 0x40;
uuid.bytes[8] &= 0x3f;
uuid.bytes[8] |= 0x80;
return uuid;
}
void uuid_to_string(UUID uuid, char *str) {
sprintf(str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.bytes[0], uuid.bytes[1], uuid.bytes[2],
uuid.bytes[3], uuid.bytes[4], uuid.bytes[5], uuid.bytes[6], uuid.bytes[7], uuid.bytes[8], uuid.bytes[9], uuid.bytes[10],
uuid.bytes[11], uuid.bytes[12], uuid.bytes[13], uuid.bytes[14], uuid.bytes[15]);
}
char *uuid4() {
srand(time(NULL));
UUID uuid = generate_uuid4();
char str[37];
uuid_to_string(uuid, str);
return sbuf(str);
}
#endif
#ifndef RNET_H
#define RNET_H
#ifdef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#ifndef _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef _POSIX_C_SOURCE_TEMP
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
#undef _POSIX_C_SOURCE_TEMP
#else
#undef _POSIX_C_SOURCE
#endif
#define NET_SOCKET_MAX_CONNECTIONS 50000
typedef struct rnet_socket_t {
int fd;
char name[50];
void *data;
size_t bytes_received;
size_t bytes_sent;
bool connected;
void (*on_read)(struct rnet_socket_t *);
void (*on_close)(struct rnet_socket_t *);
void (*on_connect)(struct rnet_socket_t *);
} rnet_socket_t;
typedef struct rnet_select_result_t {
int server_fd;
rnet_socket_t **sockets;
unsigned int socket_count;
} rnet_select_result_t;
typedef struct rnet_server_t {
int socket_fd;
rnet_socket_t **sockets;
unsigned int socket_count;
unsigned int port;
unsigned int backlog;
rnet_select_result_t *select_result;
int max_fd;
void (*on_connect)(rnet_socket_t *socket);
void (*on_close)(rnet_socket_t *socket);
void (*on_read)(rnet_socket_t *socket);
} rnet_server_t;
void rnet_select_result_free(rnet_select_result_t *result);
int net_socket_accept(int server_fd);
int net_socket_connect(const char *, unsigned int);
int net_socket_init();
rnet_server_t *net_socket_serve(unsigned int port, unsigned int backlog);
rnet_select_result_t *net_socket_select(rnet_server_t *server);
rnet_socket_t *net_socket_wait(rnet_socket_t *socket_fd);
bool net_set_non_blocking(int sock);
bool net_socket_bind(int sock, unsigned int port);
bool net_socket_listen(int sock, unsigned int backlog);
char *net_socket_name(int sock);
size_t net_socket_write(rnet_socket_t *, unsigned char *, size_t);
rnet_socket_t *get_net_socket_by_fd(int);
unsigned char *net_socket_read(rnet_socket_t *, unsigned int buff_size);
void _net_socket_close(int sock);
void net_socket_close(rnet_socket_t *sock);
rnet_server_t *rnet_server_new(int socket_fd, unsigned int port, unsigned int backlog) {
rnet_server_t *server = malloc(sizeof(rnet_server_t));
server->socket_fd = socket_fd;
server->sockets = NULL;
server->socket_count = 0;
server->port = port;
server->backlog = backlog;
server->max_fd = -1;
server->select_result = NULL;
server->on_connect = NULL;
server->on_close = NULL;
server->on_read = NULL;
return server;
}
rnet_server_t *rnet_server_add_socket(rnet_server_t *server, rnet_socket_t *sock) {
server->sockets = realloc(server->sockets, sizeof(rnet_socket_t *) * (server->socket_count + 1));
server->sockets[server->socket_count] = sock;
server->socket_count++;
sock->on_read = server->on_read;
sock->on_connect = server->on_connect;
sock->on_close = server->on_close;
sock->connected = true;
return server;
}
rnet_socket_t sockets[NET_SOCKET_MAX_CONNECTIONS] = {0};
unsigned long sockets_connected = 0;
int net_socket_max_fd = 0;
unsigned long sockets_total = 0;
unsigned long sockets_disconnected = 0;
unsigned long sockets_concurrent_record = 0;
unsigned long sockets_errors = 0;
bool net_set_non_blocking(int sock) {
int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {
perror("fcntl");
return false;
}
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl");
return false;
}
return true;
}
int net_socket_init() {
int socket_fd = -1;
memset(sockets, 0, sizeof(sockets));
int opt = 1;
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket failed.\n");
return false;
}
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("Setsockopt failed.\n");
close(socket_fd);
return false;
}
net_set_non_blocking(socket_fd);
return socket_fd;
}
char *net_socket_name(int fd) {
rnet_socket_t *rnet_socket = get_net_socket_by_fd(fd);
if (rnet_socket) {
return rnet_socket->name;
;
}
// If socket disconnected or is no client from server
return NULL;
}
bool net_socket_bind(int socket_fd, unsigned int port) {
struct sockaddr_in address;
address.sin_family = AF_INET; // IPv4
address.sin_addr.s_addr = INADDR_ANY; // Bind to any available address
address.sin_port = htons(port); // Convert port to network byte order
if (bind(socket_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
close(socket_fd);
return false;
}
return true;
}
int net_socket_connect(const char *host, unsigned int port) {
char port_str[10] = {0};
sprintf(port_str, "%d", port);
int status;
int socket_fd = -1;
struct addrinfo hints;
struct addrinfo *res;
struct addrinfo *p;
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return false;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(host, port_str, &hints, &res)) != 0) {
return -1;
}
for (p = res; p != NULL; p = p->ai_next) {
if ((socket_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
continue;
}
if (connect(socket_fd, p->ai_addr, p->ai_addrlen) == -1) {
close(socket_fd);
continue;
}
break;
}
if (p == NULL) {
freeaddrinfo(res);
return -1;
}
freeaddrinfo(res);
return socket_fd;
}
bool net_socket_listen(int socket_fd, unsigned int backlog) {
if (listen(socket_fd, backlog) < 0) { // '3' is the backlog size
perror("Listen failed");
close(socket_fd);
return false;
}
return true;
}
rnet_server_t *net_socket_serve(unsigned int port, unsigned int backlog) {
signal(SIGPIPE, SIG_IGN);
int socket_fd = net_socket_init();
net_socket_bind(socket_fd, port);
net_socket_listen(socket_fd, backlog);
return rnet_server_new(socket_fd, port, backlog);
}
int net_socket_accept(int net_socket_server_fd) {
struct sockaddr_in address;
int addrlen = sizeof(address);
int new_socket = -1;
if ((new_socket = accept(net_socket_server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
close(new_socket);
return -1;
} else {
return new_socket;
}
}
/*
static void net_socket_stats(WrenVM *vm)
{
wrenSetSlotNewList(vm, 0);
wrenSetSlotString(vm, 1, "sockets_total");
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotDouble(vm, 1, (double)sockets_total);
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotString(vm, 1, "sockets_concurrent_record");
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotDouble(vm, 1, (double)sockets_concurrent_record);
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotString(vm, 1, "sockets_connected");
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotDouble(vm, 1, (double)sockets_connected);
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotString(vm, 1, "sockets_disconnected");
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotDouble(vm, 1, (double)sockets_disconnected);
wrenInsertInList(vm, 0, -1, 1);
}*/
size_t net_socket_write(rnet_socket_t *sock, unsigned char *message, size_t size) {
ssize_t sent_total = 0;
ssize_t sent = 0;
ssize_t to_send = size;
while ((sent = send(sock->fd, message, to_send, 0))) {
if (sent == -1) {
sockets_errors++;
net_socket_close(sock);
break;
}
if (sent == 0) {
printf("EDGE CASE?\n");
exit(1);
sockets_errors++;
net_socket_close(sock);
break;
}
sent_total += sent;
if (sent_total == to_send)
break;
}
return sent_total;
}
unsigned char *net_socket_read(rnet_socket_t *sock, unsigned int buff_size) {
if (buff_size > 1024 * 1024 + 1) {
perror("Buffer too big. Maximum is 1024*1024.\n");
exit(1);
}
static unsigned char buffer[1024 * 1024];
buffer[0] = 0;
ssize_t received = recv(sock->fd, buffer, buff_size, 0);
if (received <= 0) {
buffer[0] = 0;
net_socket_close(sock);
if (received < 0) {
sockets_errors++;
return NULL;
}
}
buffer[received + 1] = 0;
sock->bytes_received = received;
return buffer;
}
rnet_socket_t *net_socket_wait(rnet_socket_t *sock) {
if (!sock)
return NULL;
if (sock->fd == -1)
return NULL;
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(sock->fd, &read_fds);
int max_socket_fd = sock->fd;
int activity = select(max_socket_fd + 1, &read_fds, NULL, NULL, NULL);
if ((activity < 0) && (errno != EINTR)) {
// perror("Select error");
net_socket_close(sock);
return NULL;
}
if (FD_ISSET(sock->fd, &read_fds)) {
return sock;
}
return NULL;
}
void rnet_safe_str(char *str, size_t length) {
if (!str || !length || !*str)
return;
for (unsigned int i = 0; i < length; i++) {
if (str[i] < 32 || str[i] > 126)
if (str[i] != 0)
str[i] = '.';
}
str[length] = 0;
}
rnet_select_result_t *rnet_new_socket_select_result(int socket_fd) {
rnet_select_result_t *result = (rnet_select_result_t *)malloc(sizeof(rnet_select_result_t));
memset(result, 0, sizeof(rnet_select_result_t));
result->server_fd = socket_fd;
result->socket_count = 0;
result->sockets = NULL;
return result;
}
void rnet_select_result_add(rnet_select_result_t *result, rnet_socket_t *sock) {
result->sockets = realloc(result->sockets, sizeof(rnet_socket_t *) * (result->socket_count + 1));
result->sockets[result->socket_count] = sock;
result->socket_count++;
}
void rnet_select_result_free(rnet_select_result_t *result) { free(result); }
rnet_select_result_t *net_socket_select(rnet_server_t *server) {
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(server->socket_fd, &read_fds);
server->max_fd = server->socket_fd;
int socket_fd = -1;
for (unsigned int i = 0; i < server->socket_count; i++) {
socket_fd = server->sockets[i]->fd;
if (!server->sockets[i]->connected) {
continue;
}
if (socket_fd > 0) {
FD_SET(socket_fd, &read_fds);
if (socket_fd > server->max_fd) {
server->max_fd = socket_fd;
}
}
}
int new_socket = -1;
struct sockaddr_in address;
int addrlen = sizeof(struct sockaddr_in);
int activity = select(server->max_fd + 1, &read_fds, NULL, NULL, NULL);
if ((activity < 0) && (errno != EINTR)) {
perror("Select error\n");
return NULL;
}
if (FD_ISSET(server->socket_fd, &read_fds)) {
if ((new_socket = accept(server->socket_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
perror("Accept failed\n");
return NULL;
}
// net_set_non_blocking(new_socket);
char name[50] = {0};
sprintf(name, "fd:%.4d:ip:%12s:port:%.6d", new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
rnet_socket_t *sock_obj = NULL;
for (unsigned int i = 0; i < server->socket_count; i++) {
if (server->sockets && server->sockets[i]->fd == -1) {
sock_obj = server->sockets[i];
}
}
if (!sock_obj) {
sock_obj = (rnet_socket_t *)malloc(sizeof(rnet_socket_t));
rnet_server_add_socket(server, sock_obj);
}
sock_obj->fd = new_socket;
strcpy(sock_obj->name, name);
sockets_connected++;
sockets_total++;
sockets_concurrent_record = sockets_connected > sockets_concurrent_record ? sockets_connected : sockets_concurrent_record;
if (new_socket > net_socket_max_fd) {
net_socket_max_fd = new_socket;
}
sock_obj->connected = true;
sock_obj->on_connect(sock_obj);
}
rnet_select_result_t *result = rnet_new_socket_select_result(server->socket_fd);
unsigned int readable_count = 0;
for (unsigned int i = 0; i < server->socket_count; i++) {
if (server->sockets[i]->fd == -1)
continue;
if (FD_ISSET(server->sockets[i]->fd, &read_fds)) {
rnet_select_result_add(result, server->sockets[i]);
readable_count++;
if (server->sockets[i]->on_read) {
server->sockets[i]->on_read(server->sockets[i]);
}
}
}
if (server->select_result) {
rnet_select_result_free(server->select_result);
server->select_result = NULL;
}
if (readable_count == 0)
rnet_select_result_free(result);
return readable_count ? result : NULL;
}
rnet_socket_t *get_net_socket_by_fd(int sock) {
for (int i = 0; i < net_socket_max_fd; i++) {
if (sockets[i].fd == sock) {
return &sockets[i];
}
}
return NULL;
}
void _net_socket_close(int sock) {
if (sock > 0) {
sockets_connected--;
sockets_disconnected++;
if (sock > 0) {
if (close(sock) == -1) {
perror("Error closing socket.\n");
}
}
}
}
void net_socket_close(rnet_socket_t *sock) {
sock->connected = false;
if (sock->on_close)
sock->on_close(sock);
_net_socket_close(sock->fd);
sock->fd = -1;
}
#undef _POSIX_C_SOURCE
#endif
#include <stdio.h>
#ifndef RLIB_RARGS_H
#define RLIB_RARGS_H
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
bool rargs_isset(int argc, char *argv[], char *key) {
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], key)) {
return true;
}
}
return false;
}
char *rargs_get_option_string(int argc, char *argv[], char *key, const char *def) {
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], key)) {
if (i < argc - 1) {
return argv[i + 1];
}
}
}
return (char *)def;
}
int rargs_get_option_int(int argc, char *argv[], char *key, int def) {
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], key)) {
if (i < argc - 1) {
return atoi(argv[i + 1]);
}
}
}
return def;
}
bool rargs_get_option_bool(int argc, char *argv[], char *key, bool def) {
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], key)) {
if (i < argc - 1) {
if (!strcmp(argv[i + 1], "false"))
return false;
if (!strcmp(argv[i + 1], "0"))
return false;
return true;
}
}
}
return def;
}
#endif
#ifndef RCAT_H
#define RCAT_H
#include <stdio.h>
#include <stdlib.h>
void rcat(char *filename) {
FILE *f = fopen(filename, "rb");
if (!f) {
printf("rcat: couldn't open \"%s\" for read.\n", filename);
return;
}
unsigned char c;
while ((c = fgetc(f)) && !feof(f)) {
printf("%c", c);
}
fclose(f);
fflush(stdout);
}
int rcat_main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: [filename]\n");
return 1;
}
rcat(argv[1]);
return 0;
}
#endif
#ifndef RLIZA_H
#define RLIZA_H
#ifndef RBUFFER_H
#define RBUFFER_H
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct rbuffer_t {
unsigned char *data;
unsigned char *_data;
size_t size;
size_t pos;
bool eof;
} rbuffer_t;
rbuffer_t *rbuffer_new(unsigned char *data, size_t size);
void rbuffer_free(rbuffer_t *rfb);
void rbuffer_reset(rbuffer_t *rfb);
void rbuffer_write(rbuffer_t *rfb, const unsigned char *data, size_t size);
size_t rbuffer_push(rbuffer_t *rfb, unsigned char);
unsigned char rbuffer_pop(rbuffer_t *rfb);
unsigned char *rbuffer_expect(rbuffer_t *rfb, char *options, char *ignore);
void rbuffer_set(rbuffer_t *rfb, const unsigned char *data, size_t size);
void rbuffer_set(rbuffer_t *rfb, const unsigned char *data, size_t size) {
if (rfb->_data) {
free(rfb->_data);
rfb->_data = NULL;
rfb->data = NULL;
rfb->eof = true;
}
if (size) {
rfb->_data = (unsigned char *)malloc(size);
memcpy(rfb->_data, data, size);
rfb->data = rfb->_data;
rfb->eof = false;
}
rfb->size = size;
rfb->pos = 0;
}
rbuffer_t *rbuffer_new(unsigned char *data, size_t size) {
rbuffer_t *rfb = (rbuffer_t *)malloc(sizeof(rbuffer_t));
if (size) {
rfb->_data = (unsigned char *)malloc(size);
memcpy(rfb->_data, data, size);
rfb->eof = false;
} else {
rfb->_data = NULL;
rfb->eof = true;
}
rfb->size = size;
rfb->pos = 0;
rfb->data = rfb->_data;
return rfb;
}
void rbuffer_free(rbuffer_t *rfb) {
if (rfb->_data)
free(rfb->_data);
free(rfb);
}
size_t rbuffer_push(rbuffer_t *rfb, unsigned char c) {
if (rfb->pos < rfb->size) {
rfb->_data[rfb->pos++] = c;
return 1;
}
rfb->_data = realloc(rfb->_data, rfb->size ? rfb->size + 1 : rfb->size + 2);
rfb->_data[rfb->pos++] = c;
rfb->size++;
return rfb->pos;
}
void rbuffer_write(rbuffer_t *rfb, const unsigned char *data, size_t size) {
unsigned char *data_ptr = (unsigned char *)data;
for (size_t i = 0; i < size; i++) {
rbuffer_push(rfb, data_ptr[i]);
}
}
unsigned char rbuffer_peek(rbuffer_t *rfb) {
unsigned char result = EOF;
if (rfb->pos != rfb->size) {
result = rfb->_data[rfb->pos];
return result;
}
rfb->eof = true;
return EOF;
}
unsigned char rbuffer_pop(rbuffer_t *rfb) {
unsigned char result = EOF;
if (rfb->pos <= rfb->size) {
result = rfb->_data[rfb->pos];
rfb->pos++;
rfb->data++;
if (rfb->pos == rfb->size) {
rfb->eof = true;
}
return result;
}
rfb->eof = true;
return result;
}
void rbuffer_reset(rbuffer_t *rfb) {
rfb->data = rfb->_data;
rfb->pos = 0;
}
unsigned char ustrncmp(const unsigned char *s1, const unsigned char *s2, size_t n) {
return strncmp((char *)s1, (char *)s2, n);
while (n && *s1 == *s2) {
n--;
s1++;
s2++;
}
return *s1 != *s2;
}
size_t ustrlen(const unsigned char *s) { return strlen((char *)s); }
unsigned char *rbuffer_to_string(rbuffer_t *rfb) {
unsigned char *result = rfb->_data;
rfb->_data = NULL;
rfb->data = NULL;
rbuffer_free(rfb);
return result;
}
unsigned char *rbuffer_match_option(rbuffer_t *rfb, char *options) {
char *option = NULL;
char options_cpy[1024] = {0};
strcpy(options_cpy, options);
char *memory = options_cpy;
while ((option = strtok_r(option == NULL ? memory : NULL, "|", &memory)) != NULL) {
size_t option_length = strlen(option);
if (option_length > rfb->size - rfb->pos) {
continue;
}
if (!strcmp(option, "\\d") && *rfb->data >= '0' && *rfb->data <= '9') {
return rfb->data;
}
if (rfb->size - rfb->pos >= 5 && !strcmp(option, "\\b") &&
((!ustrncmp(rfb->data, (unsigned char *)"true", 4) || !ustrncmp(rfb->data, (unsigned char *)"false", 5)))) {
return rfb->data;
}
if (!ustrncmp(rfb->data, (unsigned char *)option, option_length)) {
return rfb->data;
}
}
return NULL;
}
unsigned char *rbuffer_expect(rbuffer_t *rfb, char *options, char *ignore) {
while (rfb->pos < rfb->size) {
if (rbuffer_match_option(rfb, options) != NULL) {
return rfb->data;
}
if (rbuffer_match_option(rfb, ignore)) {
printf("SKIP:%s\n", rfb->data);
rbuffer_pop(rfb);
continue;
}
break;
}
return NULL;
}
unsigned char *rbuffer_consume(rbuffer_t *rfb, char *options, char *ignore) {
unsigned char *result = NULL;
if ((result = rbuffer_expect(rfb, options, ignore)) != NULL) {
rbuffer_pop(rfb);
}
return result;
}
#endif
#ifndef RSTRING_H
#define RSTRING_H
#ifndef RMATH_H
#define RMATH_H
#include <math.h>
#ifndef ceil
double ceil(double x) {
if (x == (double)(long long)x) {
return x;
} else if (x > 0.0) {
return (double)(long long)x + 1.0;
} else {
return (double)(long long)x;
}
}
#endif
#ifndef floor
double floor(double x) {
if (x >= 0.0) {
return (double)(long long)x;
} else {
double result = (double)(long long)x;
return (result == x) ? result : result - 1.0;
}
}
#endif
#ifndef modf
double modf(double x, double *iptr) {
double int_part = (x >= 0.0) ? floor(x) : ceil(x);
*iptr = int_part;
return x - int_part;
}
#endif
#endif
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
char *rstrtimestamp() {
time_t current_time;
time(&current_time);
struct tm *local_time = localtime(&current_time);
static char time_string[100];
time_string[0] = 0;
strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", local_time);
return time_string;
}
ulonglong _r_generate_key_current = 0;
char *_rcat_int_int(int a, int b) {
static char res[20];
res[0] = 0;
sprintf(res, "%d%d", a, b);
return res;
}
char *_rcat_int_double(int a, double b) {
static char res[20];
res[0] = 0;
sprintf(res, "%d%f", a, b);
return res;
}
char *_rcat_charp_int(char *a, int b) {
char res[20];
sprintf(res, "%c", b);
return strcat(a, res);
}
char *_rcat_charp_double(char *a, double b) {
char res[20];
sprintf(res, "%f", b);
return strcat(a, res);
}
char *_rcat_charp_charp(char *a, char *b) {
;
return strcat(a, b);
}
char *_rcat_charp_char(char *a, char b) {
char extra[] = {b, 0};
return strcat(a, extra);
}
char *_rcat_charp_bool(char *a, bool *b) {
if (b) {
return strcat(a, "true");
} else {
return strcat(a, "false");
}
}
#define rcat(x, y) \
_Generic((x), \
int: _Generic((y), int: _rcat_int_int, double: _rcat_int_double, char *: _rcat_charp_charp), \
char *: _Generic((y), \
int: _rcat_charp_int, \
double: _rcat_charp_double, \
char *: _rcat_charp_charp, \
char: _rcat_charp_char, \
bool: _rcat_charp_bool))((x), (y))
char *rgenerate_key() {
_r_generate_key_current++;
static char key[100];
key[0] = 0;
sprintf(key, "%lld", _r_generate_key_current);
return key;
}
char *rformat_number(long long lnumber) {
static char formatted[1024];
char number[1024] = {0};
sprintf(number, "%lld", lnumber);
int len = strlen(number);
int commas_needed = (len - 1) / 3;
int new_len = len + commas_needed;
formatted[new_len] = '\0';
int i = len - 1;
int j = new_len - 1;
int count = 0;
while (i >= 0) {
if (count == 3) {
formatted[j--] = '.';
count = 0;
}
formatted[j--] = number[i--];
count++;
}
if (lnumber < 0)
formatted[j--] = '-';
return formatted;
}
bool rstrextractdouble(char *str, double *d1) {
for (size_t i = 0; i < strlen(str); i++) {
if (isdigit(str[i])) {
str += i;
sscanf(str, "%lf", d1);
return true;
}
}
return false;
}
void rstrstripslashes(const char *content, char *result) {
size_t content_length = strlen((char *)content);
unsigned int index = 0;
for (unsigned int i = 0; i < content_length; i++) {
char c = content[i];
if (c == '\\') {
i++;
c = content[i];
if (c == 'r') {
c = '\r';
} else if (c == 't') {
c = '\t';
} else if (c == 'b') {
c = '\b';
} else if (c == 'n') {
c = '\n';
} else if (c == 'f') {
c = '\f';
} else if (c == '\\') {
// No need tbh
c = '\\';
i++;
}
}
result[index] = c;
index++;
}
result[index] = 0;
}
int rstrstartswith(const char *s1, const char *s2) {
if (s1 == NULL)
return s2 == NULL;
if (s1 == s2 || s2 == NULL || *s2 == 0)
return true;
size_t len_s2 = strlen(s2);
size_t len_s1 = strlen(s1);
if (len_s2 > len_s1)
return false;
return !strncmp(s1, s2, len_s2);
}
bool rstrendswith(const char *s1, const char *s2) {
if (s1 == NULL)
return s2 == NULL;
if (s1 == s2 || s2 == NULL || *s2 == 0)
return true;
size_t len_s2 = strlen(s2);
size_t len_s1 = strlen(s1);
if (len_s2 > len_s1) {
return false;
}
s1 += len_s1 - len_s2;
return !strncmp(s1, s2, len_s2);
}
void rstraddslashes(const char *content, char *result) {
size_t content_length = strlen((char *)content);
unsigned int index = 0;
for (unsigned int i = 0; i < content_length; i++) {
if (content[i] == '\r') {
result[index] = '\\';
index++;
result[index] = 'r';
index++;
continue;
} else if (content[i] == '\t') {
result[index] = '\\';
index++;
result[index] = 't';
index++;
continue;
} else if (content[i] == '\n') {
result[index] = '\\';
index++;
result[index] = 'n';
index++;
continue;
} else if (content[i] == '\\') {
result[index] = '\\';
index++;
result[index] = '\\';
index++;
continue;
} else if (content[i] == '\b') {
result[index] = '\\';
index++;
result[index] = 'b';
index++;
continue;
} else if (content[i] == '\f') {
result[index] = '\\';
index++;
result[index] = 'f';
index++;
continue;
} else if (content[i] == '"') {
result[index] = '\\';
index++;
result[index] = '"';
index++;
continue;
}
result[index] = content[i];
index++;
result[index] = 0;
}
}
int rstrip_whitespace(char *input, char *output) {
output[0] = 0;
int count = 0;
size_t len = strlen(input);
for (size_t i = 0; i < len; i++) {
if (input[i] == '\t' || input[i] == ' ' || input[i] == '\n') {
continue;
}
count = i;
size_t j;
for (j = 0; j < len - count; j++) {
output[j] = input[j + count];
}
output[j] = '\0';
break;
}
return count;
}
/*
* Converts "pony" to \"pony\". Addslashes does not
* Converts "pony\npony" to "pony\n"
* "pony"
*/
void rstrtocstring(const char *input, char *output) {
int index = 0;
char clean_input[strlen(input) * 2];
char *iptr = clean_input;
rstraddslashes(input, clean_input);
output[index] = '"';
index++;
while (*iptr) {
if (*iptr == '"') {
output[index] = '\\';
output++;
} else if (*iptr == '\\' && *(iptr + 1) == 'n') {
output[index] = '\\';
output++;
output[index] = 'n';
output++;
output[index] = '"';
output++;
output[index] = '\n';
output++;
output[index] = '"';
output++;
iptr++;
iptr++;
continue;
}
output[index] = *iptr;
index++;
iptr++;
}
if (output[index - 1] == '"' && output[index - 2] == '\n') {
output[index - 1] = 0;
} else if (output[index - 1] != '"') {
output[index] = '"';
output[index + 1] = 0;
}
}
size_t rstrtokline(char *input, char *output, size_t offset, bool strip_nl) {
size_t len = strlen(input);
output[0] = 0;
size_t new_offset = 0;
size_t j;
size_t index = 0;
for (j = offset; j < len + offset; j++) {
if (input[j] == 0) {
index++;
break;
}
index = j - offset;
output[index] = input[j];
if (output[index] == '\n') {
index++;
break;
}
}
output[index] = 0;
new_offset = index + offset;
if (strip_nl) {
if (output[index - 1] == '\n') {
output[index - 1] = 0;
}
}
return new_offset;
}
void rstrjoin(char **lines, size_t count, char *glue, char *output) {
output[0] = 0;
for (size_t i = 0; i < count; i++) {
strcat(output, lines[i]);
if (i != count - 1)
strcat(output, glue);
}
}
int rstrsplit(char *input, char **lines) {
int index = 0;
size_t offset = 0;
char line[1024];
while ((offset = rstrtokline(input, line, offset, false)) && *line) {
if (!*line) {
break;
}
lines[index] = (char *)malloc(strlen(line) + 1);
strcpy(lines[index], line);
index++;
}
return index;
}
bool rstartswithnumber(char *str) { return isdigit(str[0]); }
void rstrmove2(char *str, unsigned int start, size_t length, unsigned int new_pos) {
size_t str_len = strlen(str);
char new_str[str_len + 1];
memset(new_str, 0, str_len);
if (start < new_pos) {
strncat(new_str, str + length, str_len - length - start);
new_str[new_pos] = 0;
strncat(new_str, str + start, length);
strcat(new_str, str + strlen(new_str));
memset(str, 0, str_len);
strcpy(str, new_str);
} else {
strncat(new_str, str + start, length);
strncat(new_str, str, start);
strncat(new_str, str + start + length, str_len - start);
memset(str, 0, str_len);
strcpy(str, new_str);
}
new_str[str_len] = 0;
}
void rstrmove(char *str, unsigned int start, size_t length, unsigned int new_pos) {
size_t str_len = strlen(str);
if (start >= str_len || new_pos >= str_len || start + length > str_len) {
return;
}
char temp[length + 1];
strncpy(temp, str + start, length);
temp[length] = 0;
if (start < new_pos) {
memmove(str + start, str + start + length, new_pos - start);
strncpy(str + new_pos - length + 1, temp, length);
} else {
memmove(str + new_pos + length, str + new_pos, start - new_pos);
strncpy(str + new_pos, temp, length);
}
}
int cmp_line(const void *left, const void *right) {
char *l = *(char **)left;
char *r = *(char **)right;
char lstripped[strlen(l) + 1];
rstrip_whitespace(l, lstripped);
char rstripped[strlen(r) + 1];
rstrip_whitespace(r, rstripped);
double d1, d2;
bool found_d1 = rstrextractdouble(lstripped, &d1);
bool found_d2 = rstrextractdouble(rstripped, &d2);
if (found_d1 && found_d2) {
double frac_part1;
double int_part1;
frac_part1 = modf(d1, &int_part1);
double frac_part2;
double int_part2;
frac_part2 = modf(d2, &int_part2);
if (d1 == d2) {
return strcmp(lstripped, rstripped);
} else if (frac_part1 && frac_part2) {
return d1 > d2;
} else if (frac_part1 && !frac_part2) {
return 1;
} else if (frac_part2 && !frac_part1) {
return -1;
} else if (!frac_part1 && !frac_part2) {
return d1 > d2;
}
}
return 0;
}
int rstrsort(char *input, char *output) {
char **lines = (char **)malloc(strlen(input) * 10);
int line_count = rstrsplit(input, lines);
qsort(lines, line_count, sizeof(char *), cmp_line);
rstrjoin(lines, line_count, "", output);
for (int i = 0; i < line_count; i++) {
free(lines[i]);
}
free(lines);
return line_count;
}
#endif
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum rliza_type_t {
RLIZA_STRING = 's',
RLIZA_BOOLEAN = 'b',
RLIZA_NUMBER = 'n',
RLIZA_OBJECT = 'o',
RLIZA_ARRAY = 'a',
RLIZA_NULL = 0,
RLIZA_KEY = 'k',
RLIZA_INTEGER = 'i'
} rliza_type_t;
typedef struct rliza_t {
rliza_type_t type;
struct rliza_t *value;
char *key;
union {
char *string;
bool boolean;
double number;
struct rliza_t **map;
long long integer;
} content;
unsigned int count;
char *(*get_string)(struct rliza_t *, char *);
long long (*get_integer)(struct rliza_t *, char *);
double (*get_number)(struct rliza_t *, char *);
bool (*get_boolean)(struct rliza_t *, char *);
struct rliza_t *(*get_array)(struct rliza_t *, char *);
struct rliza_t *(*get_object)(struct rliza_t *, char *);
void (*set_string)(struct rliza_t *, char *, char *);
void (*set_integer)(struct rliza_t *, char *, long long);
void (*set_number)(struct rliza_t *, char *, double);
void (*set_boolean)(struct rliza_t *, char *, bool);
void (*set_array)(struct rliza_t *self, char *key, struct rliza_t *array);
void (*set_object)(struct rliza_t *self, char *key, struct rliza_t *object);
} rliza_t;
void rliza_free(rliza_t *rliza) {
if (rliza->key) {
free(rliza->key);
rliza->key = NULL;
}
if (rliza->value) {
rliza_free(rliza->value);
rliza->value = NULL;
}
// if (rliza->content.array) {
// printf("JAAAA\n");
// }
// if (rliza->content.object) {
// rliza_free(rliza->content.object);
// rliza->content.object = NULL;
//}
if (rliza->type == RLIZA_STRING) {
if (rliza->content.string) {
free(rliza->content.string);
rliza->content.string = NULL;
// else if (rliza->type == RLIZA_NUMBER) {
// printf("STDring freed\n");
}
} else if (rliza->type == RLIZA_OBJECT || rliza->type == RLIZA_ARRAY) {
if (rliza->content.map) {
for (unsigned int i = 0; i < rliza->count; i++) {
rliza_free(rliza->content.map[i]);
}
free(rliza->content.map);
}
}
// free(rliza->content.array);
//}
free(rliza);
}
rliza_t *rliza_new(rliza_type_t type);
rliza_t *rliza_new_string(char *string);
rliza_t *rliza_new_null();
rliza_t *rliza_new_boolean(bool value);
rliza_t *rliza_new_number(double value);
rliza_t *rliza_new_integer(long long value);
rliza_t *rliza_new_key_value(char *key, rliza_t *value);
rliza_t *rliza_new_key_string(char *key, char *string);
rliza_t *rliza_new_key_bool(char *key, bool value);
rliza_t *rliza_new_key_number(char *key, double value);
void rliza_push(rliza_t *self, rliza_t *obj);
void rliza_push_object(rliza_t *self, rliza_t *object);
void rliza_set_object(rliza_t *self, char *key, rliza_t *object);
void rliza_set_string(rliza_t *self, char *key, char *string);
void rliza_set_boolean(rliza_t *self, char *key, bool value);
void rliza_set_number(rliza_t *self, char *key, double value);
void rliza_set_integer(rliza_t *self, char *key, long long value);
char *rliza_get_string(rliza_t *self, char *key);
long long rliza_get_integer(rliza_t *self, char *key);
double rliza_get_number(rliza_t *self, char *key);
bool rliza_get_boolean(rliza_t *self, char *key);
rliza_t *rliza_get_array(rliza_t *self, char *key);
rliza_t *rliza_get_object(rliza_t *self, char *key);
void rliza_set_array(rliza_t *self, char *key, rliza_t *array);
char *rliza_dumps(rliza_t *rliza);
rliza_t *rliza_loads(char **content);
rliza_t *_rliza_loads(char **content);
char *rliza_get_string(rliza_t *self, char *key) {
for (unsigned int i = 0; i < self->count; i++) {
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
if (self->content.map[i]->type == RLIZA_STRING || self->content.map[i]->type == RLIZA_NULL) {
return self->content.map[i]->content.string;
}
}
}
return NULL;
}
long long rliza_get_integer(rliza_t *self, char *key) {
for (unsigned int i = 0; i < self->count; i++) {
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
if (self->content.map[i]->type == RLIZA_INTEGER || self->content.map[i]->type == RLIZA_NULL) {
return self->content.map[i]->content.integer;
}
}
}
return 0;
}
double rliza_get_number(rliza_t *self, char *key) {
for (unsigned int i = 0; i < self->count; i++) {
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
if (self->content.map[i]->type == RLIZA_NUMBER || self->content.map[i]->type == RLIZA_NULL) {
return self->content.map[i]->content.number;
}
}
}
return 0;
}
bool rliza_get_boolean(rliza_t *self, char *key) {
for (unsigned int i = 0; i < self->count; i++) {
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
if (self->content.map[i]->type == RLIZA_BOOLEAN || self->content.map[i]->type == RLIZA_NULL) {
return self->content.map[i]->content.boolean;
}
}
}
return false;
}
rliza_t *rliza_get_object(rliza_t *self, char *key) {
for (unsigned int i = 0; i < self->count; i++) {
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
return self->content.map[i];
}
}
return NULL;
}
rliza_t *rliza_get_array(rliza_t *self, char *key) {
for (unsigned int i = 0; i < self->count; i++) {
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
if (self->content.map[i]->type == RLIZA_ARRAY || self->content.map[i]->type == RLIZA_NULL) {
return self->content.map[i];
}
}
}
return NULL;
}
rliza_t *rliza_new_null() {
rliza_t *rliza = rliza_new(RLIZA_NULL);
return rliza;
}
rliza_t *rliza_new_string(char *string) {
rliza_t *rliza = rliza_new(RLIZA_STRING);
if (string == NULL) {
rliza->type = RLIZA_NULL;
rliza->content.string = NULL;
return rliza;
} else {
rliza->content.string = strdup(string);
}
return rliza;
}
rliza_t *rliza_new_boolean(bool value) {
rliza_t *rliza = rliza_new(RLIZA_BOOLEAN);
rliza->content.boolean = value;
return rliza;
}
rliza_t *rliza_new_number(double value) {
rliza_t *rliza = rliza_new(RLIZA_NUMBER);
rliza->content.number = value;
return rliza;
}
rliza_t *rliza_new_integer(long long value) {
rliza_t *rliza = rliza_new(RLIZA_INTEGER);
rliza->content.integer = value;
return rliza;
}
rliza_t *rliza_new_key_array(char *key) {
rliza_t *rliza = rliza_new(RLIZA_ARRAY);
rliza->key = strdup(key);
return rliza;
}
rliza_t *rliza_new_key_value(char *key, rliza_t *value) {
rliza_t *rliza = rliza_new(RLIZA_OBJECT);
if (key) {
rliza->key = strdup(key);
}
rliza->value = value;
return rliza;
}
rliza_t *rliza_new_key_string(char *key, char *string) {
rliza_t *rliza = rliza_new_key_value(key, rliza_new_string(string));
return rliza;
}
rliza_t *rliza_new_key_bool(char *key, bool value) {
rliza_t *rliza = rliza_new_key_value(key, rliza_new_boolean(value));
return rliza;
}
rliza_t *rliza_new_key_number(char *key, double value) {
rliza_t *rliza = rliza_new_key_value(key, rliza_new_number(value));
return rliza;
}
void rliza_set_null(rliza_t *self, char *key) {
rliza_t *obj = rliza_get_object(self, key);
if (!obj) {
obj = rliza_new_null();
obj->key = strdup(key);
rliza_push_object(self, obj);
}
if (obj->type == RLIZA_OBJECT) {
rliza_free(obj->value);
obj->value = NULL;
} else if (obj->type == RLIZA_STRING) {
if (obj->content.string)
free(obj->content.string);
obj->content.string = NULL;
} else if (obj->type == RLIZA_ARRAY) {
for (unsigned int i = 0; i < obj->count; i++) {
rliza_free(obj->content.map[i]);
}
} else if (obj->type == RLIZA_NUMBER) {
obj->content.number = 0;
} else if (obj->type == RLIZA_INTEGER) {
obj->content.integer = 0;
}
obj->type = RLIZA_NULL;
}
rliza_t *rliza_duplicate(rliza_t *rliza) {
if (!rliza)
return NULL;
char *str = rliza_dumps(rliza);
char *strp = str;
rliza_t *obj = rliza_loads(&strp);
free(str);
return obj;
}
rliza_t *rliza_new_object(rliza_t *obj) {
rliza_t *rliza = rliza_new(RLIZA_OBJECT);
rliza->value = obj;
return rliza;
}
void rliza_set_object(rliza_t *self, char *key, rliza_t *value) {
rliza_t *obj = rliza_duplicate(value);
obj->key = strdup(key);
obj->type = RLIZA_OBJECT;
rliza_push(self, obj);
}
void rliza_set_string(rliza_t *self, char *key, char *string) {
rliza_t *obj = rliza_get_object(self, key);
if (!obj) {
obj = rliza_new_string(string);
obj->key = strdup(key);
obj->type = RLIZA_STRING;
rliza_push_object(self, obj);
} else {
obj->content.string = strdup(string);
}
}
void rliza_set_array(rliza_t *self, char *key, rliza_t *array) {
rliza_t *obj = rliza_get_object(self, key);
if (obj)
rliza_free(obj);
if (array->key) {
free(array->key);
array->key = strdup(key);
}
rliza_push_object(self, array);
}
void rliza_set_number(rliza_t *self, char *key, double value) {
rliza_t *obj = rliza_get_object(self, key);
if (!obj) {
obj = rliza_new_number(value);
obj->key = strdup(key);
obj->type = RLIZA_NUMBER;
rliza_push_object(self, obj);
} else {
obj->content.number = value;
}
}
void rliza_push_object(rliza_t *self, rliza_t *object) {
self->content.map = realloc(self->content.map, (sizeof(rliza_t **)) * (self->count + 1));
self->content.map[self->count] = object;
self->count++;
}
void rliza_set_integer(rliza_t *self, char *key, long long value) {
rliza_t *obj = rliza_get_object(self, key);
if (!obj) {
obj = rliza_new_integer(value);
obj->key = strdup(key);
obj->type = RLIZA_INTEGER;
rliza_push_object(self, obj);
} else {
obj->content.integer = value;
}
}
void rliza_set_boolean(rliza_t *self, char *key, bool value) {
rliza_t *obj = rliza_get_object(self, key);
if (!obj) {
obj = rliza_new_boolean(value);
obj->key = strdup(key);
obj->type = RLIZA_BOOLEAN;
rliza_push_object(self, obj);
} else {
obj->content.boolean = value;
}
}
rliza_t *rliza_new(rliza_type_t type) {
rliza_t *rliza = (rliza_t *)calloc(1, sizeof(rliza_t));
rliza->type = type;
rliza->get_boolean = rliza_get_boolean;
rliza->get_integer = rliza_get_integer;
rliza->get_number = rliza_get_number;
rliza->get_string = rliza_get_string;
rliza->get_array = rliza_get_array;
rliza->get_object = rliza_get_object;
rliza->set_string = rliza_set_string;
rliza->set_number = rliza_set_number;
rliza->set_boolean = rliza_set_boolean;
rliza->set_integer = rliza_set_integer;
rliza->set_array = rliza_set_array;
rliza->set_object = rliza_set_object;
return rliza;
}
void *rliza_coalesce(void *result, void *default_value) {
if (result == NULL)
return default_value;
return result;
}
char *rliza_seek_string(char **content, char **options) {
while (**content == ' ' || **content == '\n' || **content == '\t' || **content == '\r') {
(*content)++;
}
if (**content == 0) {
return NULL;
}
char *option = NULL;
unsigned int option_index = 0;
while (true) {
option = options[option_index];
if (option == NULL)
break;
option_index++;
if (option[0] == 'd') {
if (**content >= '0' && **content <= '9') {
return (char *)*content;
}
} else if (!strncmp(option, *content, strlen(option))) {
return (char *)*content;
}
}
return *content;
}
char *rliza_extract_quotes(char **content) {
rbuffer_t *buffer = rbuffer_new(NULL, 0);
assert(**content == '"');
char previous = 0;
while (true) {
(*content)++;
if (!**content) {
rbuffer_free(buffer);
return NULL;
}
if (**content == '"' && previous != '\\') {
break;
}
rbuffer_push(buffer, **content);
previous = **content;
}
assert(**content == '"');
(*content)++;
rbuffer_push(buffer, 0);
char *result = (char *)rbuffer_to_string(buffer);
return result;
}
rliza_t *_rliza_loads(char **content) {
static char *seek_for1[] = {"[", "{", "\"", "d", "true", "false", "null", NULL};
char *token = (char *)rliza_seek_string(content, seek_for1);
if (!token)
return NULL;
rliza_t *rliza = rliza_new(RLIZA_NULL);
if (**content == '"') {
char *extracted = rliza_extract_quotes(content);
if (!extracted) {
rliza_free(rliza);
return NULL;
}
// char *extracted_with_slashes = (char *)malloc(strlen((char *)extracted) * 2 + 1);
// rstraddslashes(extracted, extracted_with_slashes);
rliza->type = RLIZA_STRING;
rliza->content.string = extracted; // extracted_with_slashes; // extracted_without_slashes;
// free(extracted);
return rliza;
} else if (**content == '{') {
rliza->type = RLIZA_OBJECT;
(*content)++;
char *result = NULL;
static char *seek_for2[] = {"\"", ",", "}", NULL};
while ((result = (char *)rliza_seek_string(content, seek_for2)) != NULL && *result) {
if (!**content) {
rliza_free(rliza);
return NULL;
}
if (**content == ',') {
(*content)++;
if (!**content) {
rliza_free(rliza);
return NULL;
}
continue;
}
char *key = NULL;
if (**content == '"') {
key = rliza_extract_quotes((char **)content);
if (!key || !*key) {
rliza_free(rliza);
return NULL;
}
char *escaped_key = (char *)malloc(strlen((char *)key) * 2 + 1);
rstrstripslashes((char *)key, escaped_key);
static char *seek_for3[] = {":", NULL};
char *devider = rliza_seek_string(content, seek_for3);
if (!devider || !*devider) {
free(escaped_key);
free(key);
rliza_free(rliza);
return NULL;
}
(*content)++;
if (!**content) {
free(key);
free(escaped_key);
rliza_free(rliza);
return NULL;
}
rliza_t *value = _rliza_loads(content);
if (!value) {
free(key);
free(escaped_key);
rliza_free(rliza);
return NULL;
}
if (value->key)
free(value->key);
value->key = escaped_key;
free(key);
rliza_push_object(rliza, value);
} else if (**content == '}') {
break;
} else {
// Parse error
rliza_free(rliza);
return NULL;
}
};
if ((**content != '}')) {
rliza_free(rliza);
return NULL;
}
(*content)++;
return rliza;
} else if (**content == '[') {
rliza->type = RLIZA_ARRAY;
(*content)++;
char *result;
static char *seek_for4[] = {"[", "{", "\"", "d", ",", "]", "null", "true", "false", NULL};
while ((result = (char *)rliza_seek_string(content, seek_for4)) != NULL && *result) {
if (**content == ',') {
(*content)++;
} else if (**content == ']') {
break;
}
rliza_t *obj = _rliza_loads(content);
if (!obj) {
rliza_free(rliza);
return NULL;
}
rliza_push(rliza, obj);
if (!**content) {
rliza_free(rliza);
return NULL;
}
}
if (**content != ']') {
rliza_free(rliza);
return NULL;
}
(*content)++;
return rliza;
} else if (**content >= '0' && **content <= '9') {
char *ptr = *content;
bool is_decimal = false;
while (**content) {
if (**content == '.') {
is_decimal = true;
} else if (!isdigit(**content)) {
break;
}
(*content)++;
}
if (*(*content - 1) == '.') {
rliza_free(rliza);
return NULL;
}
if (!**content) {
rliza_free(rliza);
return NULL;
}
if (is_decimal) {
rliza->type = RLIZA_NUMBER;
rliza->content.number = strtod(ptr, NULL);
} else {
rliza->type = RLIZA_INTEGER;
rliza->content.integer = strtoll(ptr, NULL, 10);
}
return rliza;
} else if (!strncmp(*content, "true", 4)) {
rliza->type = RLIZA_BOOLEAN;
rliza->content.boolean = true;
*content += 4;
return rliza;
} else if (!strncmp(*content, "false", 5)) {
rliza->type = RLIZA_BOOLEAN;
rliza->content.boolean = false;
*content += 5;
return rliza;
} else if (!strncmp(*content, "null", 4)) {
rliza->type = RLIZA_NULL;
*content += 4;
return rliza;
}
// Parsing error
rliza_free(rliza);
return NULL;
}
rliza_t *rliza_loads(char **content) {
if (!content || !**content) {
return NULL;
}
char *original_content = *content;
rliza_t *result = _rliza_loads(content);
if (!result) {
*content = original_content;
}
return result;
}
char *rliza_dumps(rliza_t *rliza) {
size_t size = 4096;
char *content = (char *)calloc(size, sizeof(char));
content[0] = 0;
if (rliza->type == RLIZA_INTEGER) {
if (rliza->key) {
sprintf(content, "\"%s\":%lld", rliza->key, rliza->content.integer);
} else {
sprintf(content, "%lld", rliza->content.integer);
}
} else if (rliza->type == RLIZA_STRING) {
// char *escaped_string = (char *)calloc(strlen((char *)rliza->content.string) * 2 + 1024,sizeof(char));
char *escaped_string = rliza->content.string;
// rstrstripslashes((char *)rliza->content.string, escaped_string);
size_t min_size = strlen((char *)escaped_string) + (rliza->key ? strlen(rliza->key) : 0) + 1024;
if (size < min_size) {
size = min_size + 1;
content = realloc(content, size);
}
if (rliza->key) {
char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 20);
rstrstripslashes((char *)rliza->key, escaped_key);
if (strlen(content) > size) {
size = size + strlen(escaped_string) + 20;
content = realloc(content, size);
}
sprintf(content, "\"%s\":\"%s\"", escaped_key, escaped_string);
free(escaped_key);
} else {
size = size + strlen(escaped_string) + 20;
content = realloc(content, size);
sprintf(content, "\"%s\"", escaped_string);
}
// free(escaped_string);
} else if (rliza->type == RLIZA_NUMBER) {
if (rliza->key) {
sprintf(content, "\"%s\":%f", rliza->key, rliza->content.number);
} else {
sprintf(content, "%f", rliza->content.number);
}
int last_zero = 0;
bool beyond_dot = false;
for (size_t i = 0; i < strlen(content); i++) {
if (content[i] == '.') {
beyond_dot = true;
} else if (beyond_dot == true) {
if (content[i - 1] != '.') {
if (content[i] == '0') {
if (!last_zero)
last_zero = i;
} else {
last_zero = 0;
}
}
}
}
if (last_zero != 0) {
content[last_zero] = 0;
}
} else if (rliza->type == RLIZA_BOOLEAN) {
if (rliza->key) {
sprintf(content, "\"%s\":%s", rliza->key, rliza->content.boolean ? "true" : "false");
} else {
sprintf(content, "%s", rliza->content.boolean ? "true" : "false");
}
} else if (rliza->type == RLIZA_OBJECT) {
strcat(content, "{");
if (rliza->key) {
strcat(content, "\"");
strcat(content, rliza->key);
strcat(content, "\":{");
}
// bool add_braces = false;
for (unsigned i = 0; i < rliza->count; i++) {
char *content_chunk = rliza_dumps(rliza->content.map[i]);
char *content_chunk_stripped = content_chunk;
if (*content_chunk_stripped == '{') {
content_chunk_stripped++;
content_chunk_stripped[strlen(content_chunk_stripped) - 1] = 0;
}
if (strlen(content_chunk_stripped) + strlen(content) > size) {
size += strlen(content_chunk_stripped) + 20;
content = realloc(content, size);
}
strcat(content, content_chunk_stripped);
free(content_chunk);
strcat(content, ",");
}
if (content[strlen(content) - 1] == ',') {
content[strlen(content) - 1] = '\0';
if (rliza->key) {
strcat(content, "}");
}
}
strcat(content, "}");
} else if (rliza->type == RLIZA_ARRAY) {
if (rliza->key) {
char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 1);
rstraddslashes((char *)rliza->key, escaped_key);
if (strlen(escaped_key) > size) {
size = strlen(escaped_key) + 10;
content = realloc(content, size);
}
sprintf(content, "\"%s\":[", escaped_key);
free(escaped_key);
} else
strcpy(content, "[");
for (unsigned i = 0; i < rliza->count; i++) {
char *content_chunk = rliza_dumps(rliza->content.map[i]);
char *content_chunk_stripped = content_chunk;
if (*content_chunk_stripped == '{') {
// content_chunk_stripped++;
// content_chunk_stripped[strlen(content_chunk_stripped) - 1] = 0;
}
if (strlen(content_chunk_stripped) + strlen(content) > size) {
size += strlen(content_chunk_stripped) + 20;
content = realloc(content, size);
}
strcat(content, content_chunk_stripped);
free(content_chunk);
strcat(content, ",");
}
if (content[strlen(content) - 1] != '[')
content[strlen(content) - 1] = 0;
strcat(content, "]");
} else if (rliza->type == RLIZA_NULL) {
if (rliza->key) {
char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 1);
rstraddslashes((char *)rliza->key, escaped_key);
sprintf(content, "\"%s\":null", escaped_key);
free(escaped_key);
} else
strcpy(content, "null");
}
return content;
}
void rliza_dumpss(rliza_t *rliza) {
char *output = rliza_dumps(rliza);
printf("%s\n", output);
free(output);
}
void rliza_push(rliza_t *self, rliza_t *obj) { rliza_push_object(self, obj); }
int rliza_validate(char *json_content) {
if (!json_content || !*json_content) {
return false;
}
char *json_contentp = json_content;
rliza_t *to_object = _rliza_loads(&json_contentp);
if (to_object) {
rliza_free(to_object);
return json_contentp - json_content;
}
return false;
}
#endif
#ifndef RCOV_H
#define RCOV_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef RBENCH_H
#define RBENCH_H
#ifndef RPRINT_H
#define RPRINT_H
#ifndef RLIB_TIME
#define RLIB_TIME
#ifndef _POSIX_C_SOURCE_199309L
#define _POSIX_C_SOURCE_199309L
#endif
#include <sys/time.h>
#include <time.h>
#undef _POSIX_C_SOURCE_199309L
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 1
#endif
typedef uint64_t nsecs_t;
void nsleep(nsecs_t nanoseconds);
void tick() { nsleep(1); }
typedef unsigned long long msecs_t;
nsecs_t nsecs() {
unsigned int lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((uint64_t)hi << 32) | lo;
}
msecs_t rnsecs_to_msecs(nsecs_t nsecs) { return nsecs / 1000 / 1000; }
nsecs_t rmsecs_to_nsecs(msecs_t msecs) { return msecs * 1000 * 1000; }
msecs_t usecs() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (long long)(tv.tv_sec) * 1000000 + (long long)(tv.tv_usec);
}
msecs_t msecs() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (long long)(tv.tv_sec) * 1000 + (tv.tv_usec / 1000);
}
char *msecs_strs(msecs_t ms) {
static char str[22];
str[0] = 0;
sprintf(str, "%f", ms * 0.001);
for (int i = strlen(str); i > 0; i--) {
if (str[i] > '0')
break;
str[i] = 0;
}
return str;
}
char *msecs_strms(msecs_t ms) {
static char str[22];
str[0] = 0;
sprintf(str, "%lld", ms);
return str;
}
char *msecs_str(long long ms) {
static char result[30];
result[0] = 0;
if (ms > 999) {
char *s = msecs_strs(ms);
sprintf(result, "%ss", s);
} else {
char *s = msecs_strms(ms);
sprintf(result, "%sMs", s);
}
return result;
}
void nsleep(nsecs_t nanoseconds) {
long seconds = 0;
int factor = 0;
while (nanoseconds > 1000000000) {
factor++;
nanoseconds = nanoseconds / 10;
}
if (factor) {
seconds = 1;
factor--;
while (factor) {
seconds = seconds * 10;
factor--;
}
}
struct timespec req = {seconds, nanoseconds};
struct timespec rem;
nanosleep(&req, &rem);
}
void ssleep(double s) {
long nanoseconds = (long)(1000000000 * s);
// long seconds = 0;
// struct timespec req = {seconds, nanoseconds};
// struct timespec rem;
nsleep(nanoseconds);
}
void msleep(long miliseonds) {
long nanoseconds = miliseonds * 1000000;
nsleep(nanoseconds);
}
char *format_time(int64_t nanoseconds) {
char output[1024];
size_t output_size = sizeof(output);
output[0] = 0;
if (nanoseconds < 1000) {
// Less than 1 microsecond
snprintf(output, output_size, "%ldns", nanoseconds);
} else if (nanoseconds < 1000000) {
// Less than 1 millisecond
double us = nanoseconds / 1000.0;
snprintf(output, output_size, "%.2fµs", us);
} else if (nanoseconds < 1000000000) {
// Less than 1 second
double ms = nanoseconds / 1000000.0;
snprintf(output, output_size, "%.2fms", ms);
} else {
// 1 second or more
double s = nanoseconds / 1000000000.0;
if (s > 60 * 60) {
s = s / 60 / 60;
snprintf(output, output_size, "%.2fh", s);
} else if (s > 60) {
s = s / 60;
snprintf(output, output_size, "%.2fm", s);
} else {
snprintf(output, output_size, "%.2fs", s);
}
}
return sbuf(output);
}
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
long rpline_number = 0;
nsecs_t rprtime = 0;
int8_t _env_rdisable_colors = -1;
bool _rprint_enable_colors = true;
bool rprint_is_color_enabled() {
if (_env_rdisable_colors == -1) {
_env_rdisable_colors = getenv("RDISABLE_COLORS") != NULL;
}
if (_env_rdisable_colors) {
_rprint_enable_colors = false;
}
return _rprint_enable_colors;
}
void rprint_disable_colors() { _rprint_enable_colors = false; }
void rprint_enable_colors() { _rprint_enable_colors = true; }
void rprint_toggle_colors() { _rprint_enable_colors = !_rprint_enable_colors; }
void rclear() { printf("\033[2J"); }
void rprintpf(FILE *f, const char *prefix, const char *format, va_list args) {
char *pprefix = (char *)prefix;
char *pformat = (char *)format;
bool reset_color = false;
bool press_any_key = false;
char new_format[4096];
bool enable_color = rprint_is_color_enabled();
memset(new_format, 0, 4096);
int new_format_length = 0;
char temp[1000];
memset(temp, 0, 1000);
if (enable_color && pprefix[0]) {
strcat(new_format, pprefix);
new_format_length += strlen(pprefix);
reset_color = true;
}
while (true) {
if (pformat[0] == '\\' && pformat[1] == 'i') {
strcat(new_format, "\e[3m");
new_format_length += strlen("\e[3m");
reset_color = true;
pformat++;
pformat++;
} else if (pformat[0] == '\\' && pformat[1] == 'u') {
strcat(new_format, "\e[4m");
new_format_length += strlen("\e[4m");
reset_color = true;
pformat++;
pformat++;
} else if (pformat[0] == '\\' && pformat[1] == 'b') {
strcat(new_format, "\e[1m");
new_format_length += strlen("\e[1m");
reset_color = true;
pformat++;
pformat++;
} else if (pformat[0] == '\\' && pformat[1] == 'C') {
press_any_key = true;
rpline_number++;
pformat++;
pformat++;
reset_color = false;
} else if (pformat[0] == '\\' && pformat[1] == 'k') {
press_any_key = true;
rpline_number++;
pformat++;
pformat++;
} else if (pformat[0] == '\\' && pformat[1] == 'c') {
rpline_number++;
strcat(new_format, "\e[2J\e[H");
new_format_length += strlen("\e[2J\e[H");
pformat++;
pformat++;
} else if (pformat[0] == '\\' && pformat[1] == 'L') {
rpline_number++;
temp[0] = 0;
sprintf(temp, "%ld", rpline_number);
strcat(new_format, temp);
new_format_length += strlen(temp);
pformat++;
pformat++;
} else if (pformat[0] == '\\' && pformat[1] == 'l') {
rpline_number++;
temp[0] = 0;
sprintf(temp, "%.5ld", rpline_number);
strcat(new_format, temp);
new_format_length += strlen(temp);
pformat++;
pformat++;
} else if (pformat[0] == '\\' && pformat[1] == 'T') {
nsecs_t nsecs_now = nsecs();
nsecs_t end = rprtime ? nsecs_now - rprtime : 0;
temp[0] = 0;
sprintf(temp, "%s", format_time(end));
strcat(new_format, temp);
new_format_length += strlen(temp);
rprtime = nsecs_now;
pformat++;
pformat++;
} else if (pformat[0] == '\\' && pformat[1] == 't') {
rprtime = nsecs();
pformat++;
pformat++;
} else {
new_format[new_format_length] = *pformat;
new_format_length++;
if (!*pformat)
break;
// printf("%c",*pformat);
pformat++;
}
}
if (reset_color) {
strcat(new_format, "\e[0m");
new_format_length += strlen("\e[0m");
}
new_format[new_format_length] = 0;
vfprintf(f, new_format, args);
fflush(stdout);
if (press_any_key) {
nsecs_t s = nsecs();
fgetc(stdin);
rprtime += nsecs() - s;
}
}
void rprintp(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "", format, args);
va_end(args);
}
void rprintf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "", format, args);
va_end(args);
}
void rprint(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "", format, args);
va_end(args);
}
#define printf rprint
// Print line
void rprintlf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\\l", format, args);
va_end(args);
}
void rprintl(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\\l", format, args);
va_end(args);
}
// Black
void rprintkf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[30m", format, args);
va_end(args);
}
void rprintk(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[30m", format, args);
va_end(args);
}
// Red
void rprintrf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[31m", format, args);
va_end(args);
}
void rprintr(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[31m", format, args);
va_end(args);
}
// Green
void rprintgf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[32m", format, args);
va_end(args);
}
void rprintg(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[32m", format, args);
va_end(args);
}
// Yellow
void rprintyf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[33m", format, args);
va_end(args);
}
void rprinty(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[33m", format, args);
va_end(args);
}
// Blue
void rprintbf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[34m", format, args);
va_end(args);
}
void rprintb(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[34m", format, args);
va_end(args);
}
// Magenta
void rprintmf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[35m", format, args);
va_end(args);
}
void rprintm(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[35m", format, args);
va_end(args);
}
// Cyan
void rprintcf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[36m", format, args);
va_end(args);
}
void rprintc(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[36m", format, args);
va_end(args);
}
// White
void rprintwf(FILE *f, const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[37m", format, args);
va_end(args);
}
void rprintw(const char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[37m", format, args);
va_end(args);
}
#endif
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#ifndef RLIB_TERMINAL_H
#define RLIB_TERMINAL_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef RTEST_H
#define RTEST_H
#ifndef REMO_H
#define REMO_H
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
typedef struct {
const char *str;
const char *description;
} remo_t;
remo_t remo[] = {
{"\U0001F600", "Grinning Face"}, // 😀
{"\U0001F601", "Beaming Face with Smiling Eyes"}, // 😁
{"\U0001F602", "Face with Tears of Joy"}, // 😂
{"\U0001F923", "Rolling on the Floor Laughing"}, // 🤣
{"\U0001F603", "Grinning Face with Big Eyes"}, // 😃
{"\U0001F604", "Grinning Face with Smiling Eyes"}, // 😄
{"\U0001F609", "Winking Face"}, // 😉
{"\U0001F60A", "Smiling Face with Smiling Eyes"}, // 😊
{"\U0001F60D", "Smiling Face with Heart-Eyes"}, // 😍
{"\U0001F618", "Face Blowing a Kiss"}, // 😘
{"\U0001F617", "Kissing Face"}, // 😗
{"\U0001F61A", "Kissing Face with Closed Eyes"}, // 😚
{"\U0001F642", "Slightly Smiling Face"}, // 🙂
{"\U0001F643", "Upside-Down Face"}, // 🙃
{"\U0001F970", "Smiling Face with Hearts"}, // 🥰
{"\U0001F60B", "Face Savoring Food"}, // 😋
{"\U0001F61B", "Face with Tongue"}, // 😛
{"\U0001F61C", "Winking Face with Tongue"}, // 😜
{"\U0001F92A", "Zany Face"}, // 🤪
{"\U0001F929", "Star-Struck"}, // 🤩
{"\U0001F631", "Face Screaming in Fear"}, // 😱
{"\U0001F62D", "Loudly Crying Face"}, // 😭
{"\U0001F624", "Face with Steam From Nose"}, // 😤
{"\U0001F620", "Angry Face"}, // 😠
{"\U0001F621", "Pouting Face"}, // 😡
{"\U0001F47B", "Ghost"}, // 👻
{"\U0001F480", "Skull"}, // 💀
{"\U0001F4A9", "Pile of Poo"}, // 💩
{"\U0001F47D", "Alien"}, // 👽
// Geometric Shapes
{"\U000025A0", "Black Square"}, // ■
{"\U000025B2", "Upward Triangle"}, // ▲
{"\U000025CF", "Black Circle"}, // ●
{"\U000025CB", "White Circle"}, // ○
{"\U00002B1B", "Large Black Square"}, // ⬛
{"\U00002B1C", "Large White Square"}, // ⬜
// Mathematical Symbols
{"\U00002200", "For All"}, // ∀
{"\U00002203", "Exists"}, // ∃
{"\U00002205", "Empty Set"}, // ∅
{"\U00002207", "Nabla"}, // ∇
{"\U0000220F", "N-Ary Product"}, // ∏
{"\U00002212", "Minus Sign"}, //
{"\U0000221E", "Infinity"}, // ∞
// Arrows
{"\U00002190", "Left Arrow"}, // ←
{"\U00002191", "Up Arrow"}, // ↑
{"\U00002192", "Right Arrow"}, // →
{"\U00002193", "Down Arrow"}, // ↓
{"\U00002195", "Up Down Arrow"}, // ↕
{"\U00002197", "Up Right Arrow"}, // ↗
{"\U00002198", "Down Right Arrow"}, // ↘
{"\U000027A1", "Black Right Arrow"}, // ➡️
// Dingbats
{"\U00002714", "Check Mark"}, // ✔️
{"\U00002716", "Heavy Multiplication X"}, // ✖️
{"\U00002728", "Sparkles"}, // ✨
{"\U00002757", "Exclamation Mark"}, // ❗
{"\U0000274C", "Cross Mark"}, // ❌
{"\U00002795", "Heavy Plus Sign"}, //
// Miscellaneous Symbols
{"\U00002600", "Sun"}, // ☀️
{"\U00002614", "Umbrella with Rain Drops"}, // ☔
{"\U00002620", "Skull and Crossbones"}, // ☠️
{"\U000026A0", "Warning Sign"}, // ⚠️
{"\U000026BD", "Soccer Ball"}, // ⚽
{"\U000026C4", "Snowman"}, // ⛄
// Stars and Asterisks
{"\U00002733", "Eight Pointed Black Star"}, // ✳️
{"\U00002734", "Eight Spoked Asterisk"}, // ✴️
{"\U00002B50", "White Star"}, // ⭐
{"\U0001F31F", "Glowing Star"}, // 🌟
{"\U00002728", "Sparkles"}, // ✨
// Animals and Nature
{"\U0001F98A", "Fox"}, // 🦊
{"\U0001F415", "Dog"}, // 🐕
{"\U0001F431", "Cat Face"}, // 🐱
{"\U0001F435", "Monkey Face"}, // 🐵
{"\U0001F408", "Black Cat"}, // 🐈
{"\U0001F98C", "Deer"}, // 🦌
{"\U0001F344", "Mushroom"}, // 🍄
{"\U0001F333", "Tree"}, // 🌳
// Weather and Space Symbols
{"\U0001F308", "Rainbow"}, // 🌈
{"\U0001F320", "Shooting Star"}, // 🌠
{"\U00002600", "Sun"}, // ☀️
{"\U00002601", "Cloud"}, // ☁️
{"\U000026A1", "High Voltage"}, // ⚡
{"\U0001F525", "Fire"}, // 🔥
{"\U000026C4", "Snowman"}, // ⛄
{"\U0001F30A", "Water Wave"}, // 🌊
// Transport and Map Symbols
{"\U0001F68C", "Bus"}, // 🚌
{"\U0001F697", "Car"}, // 🚗
{"\U0001F6B2", "Bicycle"}, // 🚲
{"\U0001F6A2", "Ship"}, // 🚢
{"\U0001F681", "Helicopter"}, // 🚁
{"\U0001F680", "Rocket"}, // 🚀
{"\U0001F6EB", "Airplane"}, // 🛫
// Currency Symbols
{"\U00000024", "Dollar Sign"}, // $
{"\U000000A3", "Pound Sign"}, // £
{"\U000000A5", "Yen Sign"}, // ¥
{"\U000020AC", "Euro Sign"}, // €
{"\U0001F4B5", "Dollar Banknote"}, // 💵
{"\U0001F4B4", "Yen Banknote"}, // 💴
// Card Suits
{"\U00002660", "Black Spade Suit"}, // ♠️
{"\U00002663", "Black Club Suit"}, // ♣️
{"\U00002665", "Black Heart Suit"}, // ♥️
{"\U00002666", "Black Diamond Suit"}, // ♦️
{"\U0001F0CF", "Joker Card"}, // 🃏
// Office Supplies and Objects
{"\U0001F4DA", "Books"}, // 📚
{"\U0001F4D7", "Green Book"}, // 📗
{"\U0001F4C8", "Chart with Upwards Trend"}, // 📈
{"\U0001F4C9", "Chart with Downwards Trend"}, // 📉
{"\U0001F4B0", "Money Bag"}, // 💰
{"\U0001F4B8", "Money with Wings"}, // 💸
{"\U0001F4E6", "Package"}, // 📦
// Miscellaneous Symbols
{"\U00002757", "Exclamation Mark"}, // ❗
{"\U00002714", "Check Mark"}, // ✔️
{"\U0000274C", "Cross Mark"}, // ❌
{"\U00002705", "Check Mark Button"}, // ✅
{"\U00002B50", "White Star"}, // ⭐
{"\U0001F31F", "Glowing Star"}, // 🌟
{"\U0001F4A1", "Light Bulb"}, // 💡
{"\U0001F4A3", "Bomb"}, // 💣
{"\U0001F4A9", "Pile of Poo"}, // 💩
// Musical Symbols
{"\U0001F3B5", "Musical Note"}, // 🎵
{"\U0001F3B6", "Multiple Musical Notes"}, // 🎶
{"\U0001F3BC", "Musical Score"}, // 🎼
{"\U0001F399", "Studio Microphone"}, // 🎙️
{"\U0001F3A4", "Microphone"}, // 🎤
// Food and Drink
{"\U0001F35F", "Cheese Wedge"}, // 🧀
{"\U0001F355", "Slice of Pizza"}, // 🍕
{"\U0001F32D", "Taco"}, // 🌮
{"\U0001F37D", "Beer Mug"}, // 🍻
{"\U0001F96B", "Cup with Straw"}, // 🥤
{"\U0001F32E", "Hot Pepper"}, // 🌶️
{"\U0001F95A", "Potato"}, // 🥔
// Zodiac Signs
{"\U00002600", "Aries"}, // ♈
{"\U00002601", "Taurus"}, // ♉
{"\U00002602", "Gemini"}, // ♊
{"\U00002603", "Cancer"}, // ♋
{"\U00002604", "Leo"}, // ♌
{"\U00002605", "Virgo"}, // ♍
{"\U00002606", "Libra"}, // ♎
{"\U00002607", "Scorpio"}, // ♏
{"\U00002608", "Sagittarius"}, // ♐
{"\U00002609", "Capricorn"}, // ♑
{"\U0000260A", "Aquarius"}, // ♒
{"\U0000260B", "Pisces"}, // ♓
// Miscellaneous Shapes
{"\U0001F4C8", "Chart Increasing"}, // 📈
{"\U0001F4C9", "Chart Decreasing"}, // 📉
{"\U0001F4CA", "Bar Chart"}, // 📊
{"\U0001F7E6", "Orange Circle"}, // 🟠
{"\U0001F7E7", "Yellow Circle"}, // 🟡
{"\U0001F7E8", "Green Circle"}, // 🟢
{"\U0001F7E9", "Blue Circle"}, // 🔵
{"\U0001F7EA", "Purple Circle"}, // 🟣
// Flags
{"\U0001F1E6\U0001F1E9", "Flag of France"}, // 🇫🇷
{"\U0001F1E8\U0001F1E6", "Flag of Germany"}, // 🇩🇪
{"\U0001F1FA\U0001F1F8", "Flag of United States"}, // 🇺🇸
{"\U0001F1E7\U0001F1F7", "Flag of Canada"}, // 🇨🇦
{"\U0001F1EE\U0001F1F2", "Flag of Italy"}, // 🇮🇹
{"\U0001F1F8\U0001F1EC", "Flag of Australia"}, // 🇦🇺
{"\U0001F1F3\U0001F1F4", "Flag of Spain"}, // 🇪🇸
// Additional Miscellaneous Symbols
{"\U0001F4A5", "Collision"}, // 💥
{"\U0001F4A6", "Sweat Droplets"}, // 💦
{"\U0001F4A8", "Dashing Away"}, // 💨
{"\U0001F50B", "Battery"}, // 🔋
{"\U0001F4BB", "Laptop Computer"}, // 💻
{"\U0001F4DE", "Telephone"}, // 📞
{"\U0001F4E7", "Incoming Envelope"}, // 📧
};
size_t remo_count = sizeof(remo) / sizeof(remo[0]);
void rstrtolower(const char *input, char *output) {
while (*input) {
*output = tolower(*input);
input++;
output++;
}
*output = 0;
}
bool rstrinstr(const char *haystack, const char *needle) {
char lower1[strlen(haystack) + 1];
char lower2[strlen(needle) + 1];
rstrtolower(haystack, lower1);
rstrtolower(needle, lower2);
return strstr(lower1, lower2) ? true : false;
}
void remo_print() {
for (size_t i = 0; i < remo_count; i++) {
printf("%s - %s\n", remo[i].str, remo[i].description);
}
}
const char *remo_get(char *name) {
for (size_t i = 0; i < remo_count; i++) {
if (rstrinstr(remo[i].description, name)) {
return remo[i].str;
}
}
return NULL;
}
#endif
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#define debug(fmt, ...) printf("%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__);
char *rcurrent_banner;
int rassert_count = 0;
unsigned short rtest_is_first = 1;
unsigned int rtest_fail_count = 0;
int rtest_end(char *content) {
// Returns application exit code. 0 == success
printf("%s", content);
printf("\n@assertions: %d\n", rassert_count);
printf("@memory: %s%s\n", rmalloc_stats(), rmalloc_count == 0 ? remo_get("rainbow") : "fire");
if (rmalloc_count != 0) {
printf("MEMORY ERROR %s\n", remo_get("cross mark"));
return rtest_fail_count > 0;
}
return rtest_fail_count > 0;
}
void rtest_test_banner(char *content, char *file) {
if (rtest_is_first == 1) {
char delimiter[] = ".";
char *d = delimiter;
char f[2048];
strcpy(f, file);
printf("%s tests", strtok(f, d));
rtest_is_first = 0;
setvbuf(stdout, NULL, _IONBF, 0);
}
printf("\n - %s ", content);
}
bool rtest_test_true_silent(char *expr, int res, int line) {
rassert_count++;
if (res) {
return true;
}
rprintrf(stderr, "\nERROR on line %d: %s", line, expr);
rtest_fail_count++;
return false;
}
bool rtest_test_true(char *expr, int res, int line) {
rassert_count++;
if (res) {
fprintf(stdout, "%s", remo_get("Slightly Smiling Face"));
return true;
}
rprintrf(stderr, "\nERROR %s on line %d: %s\n", remo_get("skull"), line, expr);
rtest_fail_count++;
return false;
}
bool rtest_test_false_silent(char *expr, int res, int line) { return rtest_test_true_silent(expr, !res, line); }
bool rtest_test_false(char *expr, int res, int line) { return rtest_test_true(expr, !res, line); }
void rtest_test_skip(char *expr, int line) { rprintgf(stderr, "\n @skip(%s) on line %d\n", expr, line); }
void rtest_test_assert(char *expr, int res, int line) {
if (rtest_test_true(expr, res, line)) {
return;
}
rtest_end("");
exit(40);
}
#define rtest_banner(content) \
rcurrent_banner = content; \
rtest_test_banner(content, __FILE__);
#define rtest_true(expr) rtest_test_true(#expr, expr, __LINE__);
#define rtest_assert(expr) \
{ \
int __valid = expr ? 1 : 0; \
rtest_test_true(#expr, __valid, __LINE__); \
}; \
;
#define rassert(expr) \
{ \
int __valid = expr ? 1 : 0; \
rtest_test_true(#expr, __valid, __LINE__); \
}; \
;
#define rtest_asserts(expr) \
{ \
int __valid = expr ? 1 : 0; \
rtest_test_true_silent(#expr, __valid, __LINE__); \
};
#define rasserts(expr) \
{ \
int __valid = expr ? 1 : 0; \
rtest_test_true_silent(#expr, __valid, __LINE__); \
};
#define rtest_false(expr) \
rprintf(" [%s]\t%s\t\n", expr == 0 ? "OK" : "NOK", #expr); \
assert_count++; \
assert(#expr);
#define rtest_skip(expr) rtest_test_skip(#expr, __LINE__);
FILE *rtest_create_file(char *path, char *content) {
FILE *fd = fopen(path, "wb");
char c;
int index = 0;
while ((c = content[index]) != 0) {
fputc(c, fd);
index++;
}
fclose(fd);
fd = fopen(path, "rb");
return fd;
}
void rtest_delete_file(char *path) { unlink(path); }
#endif
char *rfcaptured = NULL;
void rfcapture(FILE *f, char *buff, size_t size) {
rfcaptured = buff;
setvbuf(f, rfcaptured, _IOFBF, size);
}
void rfstopcapture(FILE *f) { setvbuf(f, 0, _IOFBF, 0); }
bool _r_disable_stdout_toggle = false;
FILE *_r_original_stdout = NULL;
bool rr_enable_stdout() {
if (_r_disable_stdout_toggle)
return false;
if (!_r_original_stdout) {
stdout = fopen("/dev/null", "rb");
return false;
}
if (_r_original_stdout && _r_original_stdout != stdout) {
fclose(stdout);
}
stdout = _r_original_stdout;
return true;
}
bool rr_disable_stdout() {
if (_r_disable_stdout_toggle) {
return false;
}
if (_r_original_stdout == NULL) {
_r_original_stdout = stdout;
}
if (stdout == _r_original_stdout) {
stdout = fopen("/dev/null", "rb");
return true;
}
return false;
}
bool rr_toggle_stdout() {
if (!_r_original_stdout) {
rr_disable_stdout();
return true;
} else if (stdout != _r_original_stdout) {
rr_enable_stdout();
return true;
} else {
rr_disable_stdout();
return true;
}
}
typedef struct rprogressbar_t {
unsigned long current_value;
unsigned long min_value;
unsigned long max_value;
unsigned int length;
bool changed;
double percentage;
unsigned int width;
unsigned long draws;
FILE *fout;
} rprogressbar_t;
rprogressbar_t *rprogressbar_new(long min_value, long max_value, unsigned int width, FILE *fout) {
rprogressbar_t *pbar = (rprogressbar_t *)malloc(sizeof(rprogressbar_t));
pbar->min_value = min_value;
pbar->max_value = max_value;
pbar->current_value = min_value;
pbar->width = width;
pbar->draws = 0;
pbar->length = 0;
pbar->changed = false;
pbar->fout = fout ? fout : stdout;
return pbar;
}
void rprogressbar_free(rprogressbar_t *pbar) { free(pbar); }
void rprogressbar_draw(rprogressbar_t *pbar) {
if (!pbar->changed) {
return;
} else {
pbar->changed = false;
}
pbar->draws++;
char draws_text[22];
draws_text[0] = 0;
sprintf(draws_text, "%ld", pbar->draws);
char *draws_textp = draws_text;
// bool draws_text_len = strlen(draws_text);
char bar_begin_char = ' ';
char bar_progress_char = ' ';
char bar_empty_char = ' ';
char bar_end_char = ' ';
char content[4096] = {0};
char bar_content[1024];
char buff[2048] = {0};
bar_content[0] = '\r';
bar_content[1] = bar_begin_char;
unsigned int index = 2;
for (unsigned long i = 0; i < pbar->length; i++) {
if (*draws_textp) {
bar_content[index] = *draws_textp;
draws_textp++;
} else {
bar_content[index] = bar_progress_char;
}
index++;
}
char infix[] = "\033[0m";
for (unsigned long i = 0; i < strlen(infix); i++) {
bar_content[index] = infix[i];
index++;
}
for (unsigned long i = 0; i < pbar->width - pbar->length; i++) {
bar_content[index] = bar_empty_char;
index++;
}
bar_content[index] = bar_end_char;
bar_content[index + 1] = '\0';
sprintf(buff, "\033[43m%s\033[0m \033[33m%.2f%%\033[0m ", bar_content, pbar->percentage * 100);
strcat(content, buff);
if (pbar->width == pbar->length) {
strcat(content, "\r");
for (unsigned long i = 0; i < pbar->width + 10; i++) {
strcat(content, " ");
}
strcat(content, "\r");
}
fprintf(pbar->fout, "%s", content);
fflush(pbar->fout);
}
bool rprogressbar_update(rprogressbar_t *pbar, unsigned long value) {
if (value == pbar->current_value) {
return false;
}
pbar->current_value = value;
pbar->percentage = (double)pbar->current_value / (double)(pbar->max_value - pbar->min_value);
unsigned long new_length = (unsigned long)(pbar->percentage * pbar->width);
pbar->changed = new_length != pbar->length;
if (pbar->changed) {
pbar->length = new_length;
rprogressbar_draw(pbar);
return true;
}
return false;
}
size_t rreadline(char *data, size_t len, bool strip_ln) {
__attribute__((unused)) char *unused = fgets(data, len, stdin);
size_t length = strlen(data);
if (length && strip_ln)
data[length - 1] = 0;
return length;
}
void rlib_test_progressbar() {
rtest_banner("Progress bar");
rprogressbar_t *pbar = rprogressbar_new(0, 1000, 10, stderr);
rprogressbar_draw(pbar);
// No draws executed, nothing to show
rassert(pbar->draws == 0);
rprogressbar_update(pbar, 500);
rassert(pbar->percentage == 0.5);
rprogressbar_update(pbar, 500);
rprogressbar_update(pbar, 501);
rprogressbar_update(pbar, 502);
// Should only have drawn one time since value did change, but percentage
// did not
rassert(pbar->draws == 1);
// Changed is false because update function calls draw
rassert(pbar->changed == false);
rprogressbar_update(pbar, 777);
rassert(pbar->percentage == 0.777);
rprogressbar_update(pbar, 1000);
rassert(pbar->percentage == 1);
}
#endif
#define RBENCH(times, action) \
{ \
unsigned long utimes = (unsigned long)times; \
nsecs_t start = nsecs(); \
for (unsigned long i = 0; i < utimes; i++) { \
{ \
action; \
} \
} \
nsecs_t end = nsecs(); \
printf("%s\n", format_time(end - start)); \
}
#define RBENCHP(times, action) \
{ \
printf("\n"); \
nsecs_t start = nsecs(); \
unsigned int prev_percentage = 0; \
unsigned long utimes = (unsigned long)times; \
for (unsigned long i = 0; i < utimes; i++) { \
unsigned int percentage = ((long double)i / (long double)times) * 100; \
int percentage_changed = percentage != prev_percentage; \
__attribute__((unused)) int first = i == 0; \
__attribute__((unused)) int last = i == utimes - 1; \
{ action; }; \
if (percentage_changed) { \
printf("\r%d%%", percentage); \
fflush(stdout); \
\
prev_percentage = percentage; \
} \
} \
nsecs_t end = nsecs(); \
printf("\r%s\n", format_time(end - start)); \
}
struct rbench_t;
typedef struct rbench_function_t {
#ifdef __cplusplus
void (*call)();
#else
void(*call);
#endif
char name[256];
char group[256];
void *arg;
void *data;
bool first;
bool last;
int argc;
unsigned long times_executed;
nsecs_t average_execution_time;
nsecs_t total_execution_time;
} rbench_function_t;
typedef struct rbench_t {
unsigned int function_count;
rbench_function_t functions[100];
rbench_function_t *current;
rprogressbar_t *progress_bar;
bool show_progress;
int winner;
bool stdout;
unsigned long times;
bool silent;
nsecs_t execution_time;
#ifdef __cplusplus
void (*add_function)(struct rbench_t *r, const char *name, const char *group, void (*)());
#else
void (*add_function)(struct rbench_t *r, const char *name, const char *group, void *);
#endif
void (*rbench_reset)(struct rbench_t *r);
struct rbench_t *(*execute)(struct rbench_t *r, long times);
struct rbench_t *(*execute1)(struct rbench_t *r, long times, void *arg1);
struct rbench_t *(*execute2)(struct rbench_t *r, long times, void *arg1, void *arg2);
struct rbench_t *(*execute3)(struct rbench_t *r, long times, void *arg1, void *arg2, void *arg3);
} rbench_t;
FILE *_rbench_stdout = NULL;
FILE *_rbench_stdnull = NULL;
void rbench_toggle_stdout(rbench_t *r) {
if (!r->stdout) {
if (_rbench_stdout == NULL) {
_rbench_stdout = stdout;
}
if (_rbench_stdnull == NULL) {
_rbench_stdnull = fopen("/dev/null", "wb");
}
if (stdout == _rbench_stdout) {
stdout = _rbench_stdnull;
} else {
stdout = _rbench_stdout;
}
}
}
void rbench_restore_stdout(rbench_t *r) {
if (r->stdout)
return;
if (_rbench_stdout) {
stdout = _rbench_stdout;
}
if (_rbench_stdnull) {
fclose(_rbench_stdnull);
_rbench_stdnull = NULL;
}
}
rbench_t *rbench_new();
rbench_t *_rbench = NULL;
rbench_function_t *rbf;
rbench_t *rbench() {
if (_rbench == NULL) {
_rbench = rbench_new();
}
return _rbench;
}
typedef void *(*rbench_call)();
typedef void *(*rbench_call1)(void *);
typedef void *(*rbench_call2)(void *, void *);
typedef void *(*rbench_call3)(void *, void *, void *);
#ifdef __cplusplus
void rbench_add_function(rbench_t *rp, const char *name, const char *group, void (*call)()) {
#else
void rbench_add_function(rbench_t *rp, const char *name, const char *group, void *call) {
#endif
rbench_function_t *f = &rp->functions[rp->function_count];
rp->function_count++;
f->average_execution_time = 0;
f->total_execution_time = 0;
f->times_executed = 0;
f->call = call;
strcpy(f->name, name);
strcpy(f->group, group);
}
void rbench_reset_function(rbench_function_t *f) {
f->average_execution_time = 0;
f->times_executed = 0;
f->total_execution_time = 0;
}
void rbench_reset(rbench_t *rp) {
for (unsigned int i = 0; i < rp->function_count; i++) {
rbench_reset_function(&rp->functions[i]);
}
}
int rbench_get_winner_index(rbench_t *r) {
int winner = 0;
nsecs_t time = 0;
for (unsigned int i = 0; i < r->function_count; i++) {
if (time == 0 || r->functions[i].total_execution_time < time) {
winner = i;
time = r->functions[i].total_execution_time;
}
}
return winner;
}
bool rbench_was_last_function(rbench_t *r) {
for (unsigned int i = 0; i < r->function_count; i++) {
if (i == r->function_count - 1 && r->current == &r->functions[i])
return true;
}
return false;
}
rbench_function_t *rbench_execute_prepare(rbench_t *r, int findex, long times, int argc) {
rbench_toggle_stdout(r);
if (findex == 0) {
r->execution_time = 0;
}
rbench_function_t *rf = &r->functions[findex];
rf->argc = argc;
rbf = rf;
r->current = rf;
if (r->show_progress)
r->progress_bar = rprogressbar_new(0, times, 20, stderr);
r->times = times;
// printf(" %s:%s gets executed for %ld times with %d
// arguments.\n",rf->group, rf->name, times,argc);
rbench_reset_function(rf);
return rf;
}
void rbench_execute_finish(rbench_t *r) {
rbench_toggle_stdout(r);
if (r->progress_bar) {
free(r->progress_bar);
r->progress_bar = NULL;
}
r->current->average_execution_time = r->current->total_execution_time / r->current->times_executed;
;
// printf(" %s:%s finished executing in
// %s\n",r->current->group,r->current->name,
// format_time(r->current->total_execution_time));
// rbench_show_results_function(r->current);
if (rbench_was_last_function(r)) {
rbench_restore_stdout(r);
unsigned int winner_index = rbench_get_winner_index(r);
r->winner = winner_index + 1;
if (!r->silent)
rprintgf(stderr, "Benchmark results:\n");
nsecs_t total_time = 0;
for (unsigned int i = 0; i < r->function_count; i++) {
rbf = &r->functions[i];
total_time += rbf->total_execution_time;
bool is_winner = winner_index == i;
if (is_winner) {
if (!r->silent)
rprintyf(stderr, " > %s:%s:%s\n", format_time(rbf->total_execution_time), rbf->group, rbf->name);
} else {
if (!r->silent)
rprintbf(stderr, " %s:%s:%s\n", format_time(rbf->total_execution_time), rbf->group, rbf->name);
}
}
if (!r->silent)
rprintgf(stderr, "Total execution time: %s\n", format_time(total_time));
}
rbench_restore_stdout(r);
rbf = NULL;
r->current = NULL;
}
struct rbench_t *rbench_execute(rbench_t *r, long times) {
for (unsigned int i = 0; i < r->function_count; i++) {
rbench_function_t *f = rbench_execute_prepare(r, i, times, 0);
rbench_call c = (rbench_call)f->call;
nsecs_t start = nsecs();
f->first = true;
c();
f->first = false;
f->last = false;
f->times_executed++;
for (int j = 1; j < times; j++) {
c();
f->times_executed++;
f->last = f->times_executed == r->times - 1;
if (r->progress_bar) {
rprogressbar_update(r->progress_bar, f->times_executed);
}
}
f->total_execution_time = nsecs() - start;
r->execution_time += f->total_execution_time;
rbench_execute_finish(r);
}
return r;
}
struct rbench_t *rbench_execute1(rbench_t *r, long times, void *arg1) {
for (unsigned int i = 0; i < r->function_count; i++) {
rbench_function_t *f = rbench_execute_prepare(r, i, times, 1);
rbench_call1 c = (rbench_call1)f->call;
nsecs_t start = nsecs();
f->first = true;
c(arg1);
f->first = false;
f->last = false;
f->times_executed++;
for (int j = 1; j < times; j++) {
c(arg1);
f->times_executed++;
f->last = f->times_executed == r->times - 1;
if (r->progress_bar) {
rprogressbar_update(r->progress_bar, f->times_executed);
}
}
f->total_execution_time = nsecs() - start;
r->execution_time += f->total_execution_time;
rbench_execute_finish(r);
}
return r;
}
struct rbench_t *rbench_execute2(rbench_t *r, long times, void *arg1, void *arg2) {
for (unsigned int i = 0; i < r->function_count; i++) {
rbench_function_t *f = rbench_execute_prepare(r, i, times, 2);
rbench_call2 c = (rbench_call2)f->call;
nsecs_t start = nsecs();
f->first = true;
c(arg1, arg2);
f->first = false;
f->last = false;
f->times_executed++;
for (int j = 1; j < times; j++) {
c(arg1, arg2);
f->times_executed++;
f->last = f->times_executed == r->times - 1;
if (r->progress_bar) {
rprogressbar_update(r->progress_bar, f->times_executed);
}
}
f->total_execution_time = nsecs() - start;
r->execution_time += f->total_execution_time;
rbench_execute_finish(r);
}
return r;
}
struct rbench_t *rbench_execute3(rbench_t *r, long times, void *arg1, void *arg2, void *arg3) {
for (unsigned int i = 0; i < r->function_count; i++) {
rbench_function_t *f = rbench_execute_prepare(r, i, times, 3);
rbench_call3 c = (rbench_call3)f->call;
nsecs_t start = nsecs();
f->first = true;
c(arg1, arg2, arg3);
f->first = false;
f->last = false;
f->times_executed++;
for (int j = 1; j < times; j++) {
c(arg1, arg2, arg3);
f->times_executed++;
f->last = f->times_executed == r->times - 1;
if (r->progress_bar) {
rprogressbar_update(r->progress_bar, f->times_executed);
}
}
f->total_execution_time = nsecs() - start;
rbench_execute_finish(r);
}
return r;
}
rbench_t *rbench_new() {
rbench_t *r = (rbench_t *)malloc(sizeof(rbench_t));
memset(r, 0, sizeof(rbench_t));
r->add_function = rbench_add_function;
r->rbench_reset = rbench_reset;
r->execute1 = rbench_execute1;
r->execute2 = rbench_execute2;
r->execute3 = rbench_execute3;
r->execute = rbench_execute;
r->stdout = true;
r->silent = false;
r->winner = 0;
r->show_progress = true;
return r;
}
void rbench_free(rbench_t *r) { free(r); }
#endif
bool check_lcov() {
char buffer[1024 * 64];
FILE *fp;
fp = popen("lcov --help", "r");
if (fp == NULL) {
return false;
}
if (fgets(buffer, sizeof(buffer), fp) == NULL) {
return false;
}
pclose(fp);
return strstr(buffer, "lcov: not found") ? false : true;
}
int rcov_main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: [source.c]\n");
return 1;
}
char argstr[4096] = {0};
for (int i = 2; i < argc; i++) {
strcat(argstr, argv[i]);
strcat(argstr, " ");
}
if (!check_lcov()) {
printf("lcov is not installed. Please execute `sudo apt install lcov`.\n");
return 1;
}
char *source_file = argv[1];
char *commands[] = {"rm -f *.gcda 2>/dev/null",
"rm -f *.gcno 2>/dev/null",
"rm -f %s.coverage.info 2>/dev/null",
"gcc -pg -fprofile-arcs -ftest-coverage -g -o %s_coverage.o %s",
"./%s_coverage.o",
"lcov --capture --directory . --output-file %s.coverage.info",
"genhtml %s.coverage.info --output-directory /tmp/%s.coverage",
"rm -f *.gcda 2>/dev/null",
"rm -f *.gcno 2>/dev/null",
"rm -f %s.coverage.info 2>/dev/null", //"cat gmon.out",
"gprof %s_coverage.o gmon.out > output.rcov_analysis",
"rm -f gmon.out",
"cat output.rcov_analysis",
"rm output.rcov_analysis",
"rm -f %s_coverage.o",
"google-chrome /tmp/%s.coverage/index.html"};
uint command_count = sizeof(commands) / sizeof(commands[0]);
RBENCH(1,{
for (uint i = 0; i < command_count; i++) {
char *formatted_command = sbuf("");
sprintf(formatted_command, commands[i], source_file, source_file);
// printf("%s\n", formatted_command);
if (formatted_command[0] == '.' && formatted_command[1] == '/') {
strcat(formatted_command, " ");
strcat(formatted_command, argstr);
}
if (system(formatted_command)) {
printf("`%s` returned non-zero code.\n", formatted_command);
}
});
}
return 0;
}
#endif
#ifndef RHTTP_H
#define RHTTP_H
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define BUFF_SIZE 8096
#define RHTTP_MAX_CONNECTIONS 100
int rhttp_opt_error = 1;
int rhttp_opt_warn = 1;
int rhttp_opt_info = 1;
int rhttp_opt_port = 8080;
int rhttp_opt_debug = 0;
int rhttp_opt_request_logging = 0;
int rhttp_sock = 0;
int rhttp_opt_buffered = 0;
int rhttp_c = 0;
int rhttp_c_mutex_initialized = 0;
pthread_mutex_t rhttp_c_mutex;
char rhttp_opt_host[1024] = "0.0.0.0";
unsigned int rhttp_connections_handled = 0;
typedef struct rhttp_header_t {
char *name;
char *value;
struct rhttp_header_t *next;
} rhttp_header_t;
typedef struct rhttp_request_t {
int c;
int closed;
bool keep_alive;
nsecs_t start;
char *raw;
char *line;
char *body;
char *method;
char *path;
char *version;
void *context;
unsigned int bytes_received;
rhttp_header_t *headers;
} rhttp_request_t;
char *rhttp_current_timestamp() {
time_t current_time;
time(&current_time);
struct tm *local_time = localtime(&current_time);
static char time_string[100];
time_string[0] = 0;
strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", local_time);
return time_string;
}
void rhttp_logs(const char *prefix, const char *level, const char *format, va_list args) {
char buf[strlen(format) + BUFSIZ + 1];
buf[0] = 0;
sprintf(buf, "%s%s %s %s\e[0m", prefix, rhttp_current_timestamp(), level, format);
vfprintf(stdout, buf, args);
}
void rhttp_log_info(const char *format, ...) {
if (!rhttp_opt_info)
return;
va_list args;
va_start(args, format);
rhttp_logs("\e[32m", "INFO ", format, args);
va_end(args);
}
void rhttp_log_debug(const char *format, ...) {
if (!rhttp_opt_debug)
return;
va_list args;
va_start(args, format);
if (rhttp_opt_debug)
rhttp_logs("\e[33m", "DEBUG", format, args);
va_end(args);
}
void rhttp_log_warn(const char *format, ...) {
if (!rhttp_opt_warn)
return;
va_list args;
va_start(args, format);
rhttp_logs("\e[34m", "WARN ", format, args);
va_end(args);
}
void rhttp_log_error(const char *format, ...) {
if (!rhttp_opt_error)
return;
va_list args;
va_start(args, format);
rhttp_logs("\e[35m", "ERROR", format, args);
va_end(args);
}
void http_request_init(rhttp_request_t *r) {
r->raw = NULL;
r->line = NULL;
r->body = NULL;
r->method = NULL;
r->path = NULL;
r->version = NULL;
r->start = 0;
r->headers = NULL;
r->bytes_received = 0;
r->closed = 0;
}
void rhttp_free_header(rhttp_header_t *h) {
if (!h)
return;
rhttp_header_t *next = h->next;
free(h->name);
free(h->value);
free(h);
if (next)
rhttp_free_header(next);
}
void rhttp_rhttp_free_headers(rhttp_request_t *r) {
if (!r->headers)
return;
rhttp_free_header(r->headers);
r->headers = NULL;
}
rhttp_header_t *rhttp_parse_headers(rhttp_request_t *s) {
int first = 1;
char *body = strdup(s->body);
char *body_original = body;
while (body && *body) {
char *line = __strtok_r(first ? body : NULL, "\r\n", &body);
if (!line)
break;
rhttp_header_t *h = (rhttp_header_t *)malloc(sizeof(rhttp_header_t));
h->name = NULL;
h->value = NULL;
h->next = NULL;
char *name = __strtok_r(line, ": ", &line);
first = 0;
if (!name) {
rhttp_free_header(h);
break;
}
h->name = strdup(name);
char *value = __strtok_r(NULL, "\r\n", &line);
if (!value) {
rhttp_free_header(h);
break;
}
h->value = value ? strdup(value + 1) : strdup("");
h->next = s->headers;
s->headers = h;
}
free(body_original);
return s->headers;
}
void rhttp_free_request(rhttp_request_t *r) {
if (r->raw) {
free(r->raw);
free(r->body);
free(r->method);
free(r->path);
free(r->version);
rhttp_rhttp_free_headers(r);
}
free(r);
}
long rhttp_header_get_long(rhttp_request_t *r, const char *name) {
rhttp_header_t *h = r->headers;
while (h) {
if (!strcmp(h->name, name))
return strtol(h->value, NULL, 10);
h = h->next;
}
return -1;
}
char *rhttp_header_get_string(rhttp_request_t *r, const char *name) {
rhttp_header_t *h = r->headers;
while (h) {
if (!strcmp(h->name, name))
return h->value && *h->value ? h->value : NULL;
h = h->next;
}
return NULL;
}
void rhttp_print_header(rhttp_header_t *h) { rhttp_log_debug("Header: <%s> \"%s\"\n", h->name, h->value); }
void rhttp_print_headers(rhttp_header_t *h) {
while (h) {
rhttp_print_header(h);
h = h->next;
}
}
void rhttp_print_request_line(rhttp_request_t *r) { rhttp_log_info("%s %s %s\n", r->method, r->path, r->version); }
void rhttp_print_request(rhttp_request_t *r) {
rhttp_print_request_line(r);
if (rhttp_opt_debug)
rhttp_print_headers(r->headers);
}
void rhttp_close(rhttp_request_t *r) {
if (!r)
return;
if (!r->closed)
close(r->c);
rhttp_free_request(r);
}
rhttp_request_t *rhttp_parse_request(int s) {
rhttp_request_t *request = (rhttp_request_t *)malloc(sizeof(rhttp_request_t));
http_request_init(request);
char buf[BUFF_SIZE] = {0};
request->c = s;
int breceived = 0;
while (!rstrendswith(buf, "\r\n\r\n")) {
int chunk_size = read(s, buf + breceived, 1);
if (chunk_size <= 0) {
close(request->c);
request->closed = 1;
return request;
}
breceived += chunk_size;
}
if (breceived <= 0) {
close(request->c);
request->closed = 1;
return request;
}
buf[breceived] = '\0';
char *original_buf = buf;
char *b = original_buf;
request->raw = strdup(b);
b = original_buf;
char *line = strtok(b, "\r\n");
b = original_buf;
char *body = b + strlen(line) + 2;
request->body = strdup(body);
b = original_buf;
char *method = strtok(b, " ");
char *path = strtok(NULL, " ");
char *version = strtok(NULL, " ");
request->bytes_received = breceived;
request->line = line;
request->start = nsecs();
request->method = strdup(method);
request->path = strdup(path);
request->version = strdup(version);
request->headers = NULL;
request->keep_alive = false;
if (rhttp_parse_headers(request)) {
char *keep_alive_string = rhttp_header_get_string(request, "Connection");
if (keep_alive_string && !strcmp(keep_alive_string, "keep-alive")) {
request->keep_alive = 1;
}
}
return request;
}
void rhttp_close_server() {
close(rhttp_sock);
close(rhttp_c);
printf("Connections handled: %d\n", rhttp_connections_handled);
printf("Gracefully closed\n");
exit(0);
}
size_t rhttp_send_drain(int s, void *tsend, size_t to_send_len) {
if (to_send_len == 0 && *(unsigned char *)tsend) {
to_send_len = strlen(tsend);
}
unsigned char *to_send = (unsigned char *)malloc(to_send_len);
unsigned char *to_send_original = to_send;
memcpy(to_send, tsend, to_send_len);
// to_send[to_send_len] = '\0';
long bytes_sent = 0;
long bytes_sent_total = 0;
while (1) {
bytes_sent = send(s, to_send + bytes_sent_total, to_send_len - bytes_sent_total, 0);
if (bytes_sent <= 0) {
bytes_sent_total = 0;
break;
}
bytes_sent_total += bytes_sent;
if (bytes_sent_total == (long)to_send_len) {
break;
} else if (!bytes_sent) {
bytes_sent_total = 0;
// error
break;
} else {
rhttp_log_info("Extra send of %d/%d bytes.\n", bytes_sent_total, to_send_len);
}
}
free(to_send_original);
return bytes_sent_total;
}
typedef int (*rhttp_request_handler_t)(rhttp_request_t *r);
void rhttp_serve(const char *host, int port, int backlog, int request_logging, int request_debug, rhttp_request_handler_t handler,
void *context) {
signal(SIGPIPE, SIG_IGN);
rhttp_sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(host ? host : "0.0.0.0");
rhttp_opt_debug = request_debug;
rhttp_opt_request_logging = request_logging;
int opt = 1;
setsockopt(rhttp_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if (bind(rhttp_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
printf("Binding error\n");
exit(1);
}
listen(rhttp_sock, backlog);
while (1) {
struct sockaddr_in client_addr;
int addrlen = sizeof(client_addr);
rhttp_c = accept(rhttp_sock, (struct sockaddr *)&client_addr, (socklen_t *)&addrlen);
rhttp_connections_handled++;
while (true) {
rhttp_request_t *r = rhttp_parse_request(rhttp_c);
r->context = context;
if (!r->closed) {
if (!handler(r) && !r->closed) {
rhttp_close(r);
}
}
if (!r->keep_alive && !r->closed) {
rhttp_close(r);
} else if (r->keep_alive && !r->closed) {
}
if (r->closed) {
break;
}
rhttp_free_request(r);
}
}
}
unsigned int rhttp_calculate_number_char_count(unsigned int number) {
unsigned int width = 1;
unsigned int tcounter = number;
while (tcounter / 10 >= 1) {
tcounter = tcounter / 10;
width++;
}
return width;
}
int rhttp_file_response(rhttp_request_t *r, char *path) {
if (!*path)
return 0;
FILE *f = fopen(path, "rb");
if (f == NULL)
return 0;
size_t file_size = rfile_size(path);
char response[1024] = {0};
char content_type_header[100] = {0};
char *ext = strstr(path, ".");
char *text_extensions = ".h,.c,.html";
if (strstr(text_extensions, ext)) {
sprintf(content_type_header, "Content-Type: %s\r\n", "text/html");
}
sprintf(response, "HTTP/1.1 200 OK\r\n%sContent-Length:%ld\r\n\r\n", content_type_header, file_size);
if (!rhttp_send_drain(r->c, response, 0)) {
rhttp_log_error("Error sending file: %s\n", path);
}
size_t bytes = 0;
size_t bytes_sent = 0;
unsigned char file_buff[1024];
while ((bytes = fread(file_buff, sizeof(char), sizeof(file_buff), f))) {
if (!rhttp_send_drain(r->c, file_buff, bytes)) {
rhttp_log_error("Error sending file during chunking: %s\n", path);
}
bytes_sent += bytes;
}
if (bytes_sent != file_size) {
rhttp_send_drain(r->c, file_buff, file_size - bytes_sent);
}
close(r->c);
fclose(f);
return 1;
};
int rhttp_file_request_handler(rhttp_request_t *r) {
char *path = r->path;
while (*path == '/' || *path == '.')
path++;
if (strstr(path, "..")) {
return 0;
}
return rhttp_file_response(r, path);
};
unsigned int counter = 100000000;
int rhttp_counter_request_handler(rhttp_request_t *r) {
if (!strncmp(r->path, "/counter", strlen("/counter"))) {
counter++;
unsigned int width = rhttp_calculate_number_char_count(counter);
char to_send2[1024] = {0};
sprintf(to_send2,
"HTTP/1.1 200 OK\r\nContent-Length: %d\r\nConnection: "
"close\r\n\r\n%d",
width, counter);
rhttp_send_drain(r->c, to_send2, 0);
close(r->c);
return 1;
}
return 0;
}
int rhttp_root_request_handler(rhttp_request_t *r) {
if (!strcmp(r->path, "/")) {
char to_send[1024] = {0};
sprintf(to_send, "HTTP/1.1 200 OK\r\nContent-Length: 3\r\nConnection: "
"close\r\n\r\nOk!");
rhttp_send_drain(r->c, to_send, 0);
close(r->c);
return 1;
}
return 0;
}
int rhttp_error_404_handler(rhttp_request_t *r) {
char to_send[1024] = {0};
sprintf(to_send, "HTTP/1.1 404 Document not found\r\nContent-Length: "
"0\r\nConnection: close\r\n\r\n");
rhttp_send_drain(r->c, to_send, 0);
close(r->c);
return 1;
}
int rhttp_default_request_handler(rhttp_request_t *r) {
if (rhttp_opt_debug || rhttp_opt_request_logging)
rhttp_print_request(r);
if (rhttp_counter_request_handler(r)) {
// Counter handler
rhttp_log_info("Counter handler found for: %s\n", r->path);
} else if (rhttp_root_request_handler(r)) {
// Root handler
rhttp_log_info("Root handler found for: %s\n", r->path);
} else if (rhttp_file_request_handler(r)) {
rhttp_log_info("File %s sent\n", r->path);
} else if (rhttp_error_404_handler(r)) {
rhttp_log_warn("Error 404 for: %s\n", r->path);
// Error handler
} else {
rhttp_log_warn("No handler found for: %s\n", r->path);
close(rhttp_c);
}
return 0;
}
int rhttp_main(int argc, char *argv[]) {
setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
int opt;
while ((opt = getopt(argc, argv, "p:drh:bewi")) != -1) {
switch (opt) {
case 'i':
rhttp_opt_info = 1;
rhttp_opt_warn = 1;
rhttp_opt_error = 1;
break;
case 'e':
rhttp_opt_error = 1;
rhttp_opt_warn = 0;
rhttp_opt_info = 0;
break;
case 'w':
rhttp_opt_warn = 1;
rhttp_opt_error = 1;
rhttp_opt_info = 0;
break;
case 'p':
rhttp_opt_port = atoi(optarg);
break;
case 'b':
rhttp_opt_buffered = 1;
printf("Logging is buffered. Output may be incomplete.\n");
break;
case 'h':
strcpy(rhttp_opt_host, optarg);
break;
case 'd':
printf("Debug enabled\n");
rhttp_opt_debug = 1;
rhttp_opt_warn = 1;
rhttp_opt_info = 1;
rhttp_opt_error = 1;
break;
case 'r':
printf("Request logging enabled\n");
rhttp_opt_request_logging = 1;
break;
default:
printf("Usage: %s [-p port] [-h host] [-b]\n", argv[0]);
return 1;
}
}
printf("Starting server on: %s:%d\n", rhttp_opt_host, rhttp_opt_port);
if (rhttp_opt_buffered)
setvbuf(stdout, NULL, _IOFBF, BUFSIZ);
rhttp_serve(rhttp_opt_host, rhttp_opt_port, 1024, rhttp_opt_request_logging, rhttp_opt_debug, rhttp_default_request_handler, NULL);
return 0;
}
/* CLIENT CODE */
typedef struct rhttp_client_request_t {
char *host;
int port;
char *path;
bool is_done;
char *request;
char *response;
pthread_t thread;
int bytes_received;
} rhttp_client_request_t;
rhttp_client_request_t *rhttp_create_request(const char *host, int port, const char *path) {
rhttp_client_request_t *r = (rhttp_client_request_t *)malloc(sizeof(rhttp_client_request_t));
char request_line[4096] = {0};
sprintf(request_line,
"GET %s HTTP/1.1\r\n"
"Host: localhost:8000\r\n"
"Connection: close\r\n"
"Accept: */*\r\n"
"User-Agent: mhttpc\r\n"
"Accept-Language: en-US,en;q=0.5\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"\r\n",
path);
r->request = strdup(request_line);
r->host = strdup(host);
r->port = port;
r->path = strdup(path);
r->is_done = false;
r->response = NULL;
r->bytes_received = 0;
return r;
}
int rhttp_execute_request(rhttp_client_request_t *r) {
int s = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(r->port);
addr.sin_addr.s_addr = inet_addr(r->host);
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
return 0;
}
send(s, r->request, strlen(r->request), 0);
char buf[1024 * 1024] = {0};
int ret = recv(s, buf, 1024 * 1024, 0);
if (ret > 0) {
r->response = strdup(buf);
}
close(s);
return ret;
}
void rhttp_reset_request(rhttp_client_request_t *r) {
free(r->response);
r->is_done = false;
r->response = NULL;
r->bytes_received = 0;
}
void rhttp_free_client_request(rhttp_client_request_t *r) {
if (r->request)
free(r->request);
if (r->response)
free(r->response);
if (r->host)
free(r->host);
if (r->path)
free(r->path);
free(r);
}
void rhttp_client_bench(int workers, int times, const char *host, int port, const char *path) {
rhttp_client_request_t *requests[workers];
while (times > 0) {
for (int i = 0; i < workers && times; i++) {
requests[i] = rhttp_create_request(host, port, path);
rhttp_execute_request(requests[i]);
times--;
}
}
}
char *rhttp_client_get(const char *host, int port, const char *path) {
if (!rhttp_c_mutex_initialized) {
rhttp_c_mutex_initialized = 1;
pthread_mutex_init(&rhttp_c_mutex, NULL);
}
char http_response[1024 * 1024];
http_response[0] = 0;
rhttp_client_request_t *r = rhttp_create_request(host, port, path);
unsigned int reconnects = 0;
unsigned int reconnects_max = 100000;
while (!rhttp_execute_request(r)) {
reconnects++;
tick();
if (reconnects == reconnects_max) {
fprintf(stderr, "Maxium reconnects exceeded for %s:%d\n", host, port);
rhttp_free_client_request(r);
return NULL;
}
}
r->is_done = true;
char *body = r->response ? strstr(r->response, "\r\n\r\n") : NULL;
pthread_mutex_lock(&rhttp_c_mutex);
if (body) {
strcpy(http_response, body + 4);
} else {
strcpy(http_response, r->response);
}
rhttp_free_client_request(r);
char *result = sbuf(http_response);
pthread_mutex_unlock(&rhttp_c_mutex);
return result;
}
/*END CLIENT CODE */
#endif
#ifndef RJSON_H
#define RJSON_H
typedef struct rjson_t {
char *content;
size_t length;
size_t size;
} rjson_t;
rjson_t *rjson() {
rjson_t *json = rmalloc(sizeof(rjson_t));
json->size = 1024;
json->length = 0;
json->content = (char *)rmalloc(json->size);
json->content[0] = 0;
return json;
}
void rjson_write(rjson_t *rjs, char *content) {
size_t len = strlen(content);
while (rjs->size < rjs->length + len + 1) {
rjs->content = realloc(rjs->content, rjs->size + 1024);
rjs->size += 1024;
}
strcat(rjs->content, content);
rjs->length += len;
}
void rjson_object_start(rjson_t *rjs) {
if (rstrendswith(rjs->content, "}"))
rjson_write(rjs, ",");
rjson_write(rjs, "{");
}
void rjson_object_close(rjson_t *rjs) {
if (rstrendswith(rjs->content, ",")) {
rjs->content[rjs->length - 1] = 0;
rjs->length--;
}
rjson_write(rjs, "}");
}
void rjson_array_start(rjson_t *rjs) {
if (rjs->length && (rstrendswith(rjs->content, "}") || rstrendswith(rjs->content, "]")))
rjson_write(rjs, ",");
rjson_write(rjs, "[");
}
void rjson_array_close(rjson_t *rjs) {
if (rstrendswith(rjs->content, ",")) {
rjs->content[rjs->length - 1] = 0;
rjs->length--;
}
rjson_write(rjs, "]");
}
void rjson_kv_string(rjson_t *rjs, char *key, char *value) {
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
rjson_write(rjs, ",");
}
rjson_write(rjs, "\"");
rjson_write(rjs, key);
rjson_write(rjs, "\":\"");
char *value_str = (char *)rmalloc(strlen(value) + 4096);
rstraddslashes(value, value_str);
rjson_write(rjs, value_str);
free(value_str);
rjson_write(rjs, "\"");
}
void rjson_kv_int(rjson_t *rjs, char *key, ulonglong value) {
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
rjson_write(rjs, ",");
}
rjson_write(rjs, "\"");
rjson_write(rjs, key);
rjson_write(rjs, "\":");
char value_str[100] = {0};
sprintf(value_str, "%lld", value);
rjson_write(rjs, value_str);
}
void rjson_kv_number(rjson_t *rjs, char *key, ulonglong value) {
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
rjson_write(rjs, ",");
}
rjson_write(rjs, "\"");
rjson_write(rjs, key);
rjson_write(rjs, "\":");
rjson_write(rjs, "\"");
rjson_write(rjs, sbuf(rformat_number(value)));
rjson_write(rjs, "\"");
}
void rjson_kv_bool(rjson_t *rjs, char *key, int value) {
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
rjson_write(rjs, ",");
}
rjson_write(rjs, "\"");
rjson_write(rjs, key);
rjson_write(rjs, "\":");
rjson_write(rjs, value > 0 ? "true" : "false");
}
void rjson_kv_duration(rjson_t *rjs, char *key, nsecs_t value) {
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
rjson_write(rjs, ",");
}
rjson_write(rjs, "\"");
rjson_write(rjs, key);
rjson_write(rjs, "\":");
rjson_write(rjs, "\"");
rjson_write(rjs, sbuf(format_time(value)));
rjson_write(rjs, "\"");
}
void rjson_free(rjson_t *rsj) {
free(rsj->content);
free(rsj);
}
void rjson_key(rjson_t *rsj, char *key) {
rjson_write(rsj, "\"");
rjson_write(rsj, key);
rjson_write(rsj, "\":");
}
#endif
#ifndef RAUTOCOMPLETE_H
#define RAUTOCOMPLETE_H
#define R4_DEBUG
#ifndef RREX4_H
#define RREX4_H
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define R4_DEBUG_a
#ifdef R4_DEBUG
static int _r4_debug = 1;
#else
static int _r4_debug = 0;
#endif
static char *_format_function_name(const char *name) {
static char result[100];
result[0] = 0;
char *new_name = (char *)name;
new_name += 11;
if (new_name[0] == '_')
new_name += 1;
if (strlen(new_name) == 0) {
return " -";
}
strcpy(result, new_name);
return result;
}
#define DEBUG_VALIDATE_FUNCTION \
if (_r4_debug || r4->debug) \
printf("DEBUG: %s %s <%s> \"%s\"\n", _format_function_name(__func__), r4->valid ? "valid" : "INVALID", r4->expr, r4->str);
struct r4_t;
void r4_enable_debug() { _r4_debug = true; }
void r4_disable_debug() { _r4_debug = false; }
typedef bool (*r4_function)(struct r4_t *);
typedef struct r4_t {
bool debug;
bool valid;
bool in_block;
bool is_greedy;
bool in_range;
unsigned int backtracking;
unsigned int loop_count;
unsigned int in_group;
unsigned int match_count;
unsigned int validation_count;
unsigned int start;
unsigned int end;
unsigned int length;
bool (*functions[254])(struct r4_t *);
bool (*slash_functions[254])(struct r4_t *);
char *_str;
char *_expr;
char *match;
char *str;
char *expr;
char *str_previous;
char *expr_previous;
char **matches;
} r4_t;
static bool v4_initiated = false;
typedef bool (*v4_function_map)(r4_t *);
v4_function_map v4_function_map_global[256];
v4_function_map v4_function_map_slash[256];
v4_function_map v4_function_map_block[256];
void r4_free_matches(r4_t *r) {
if (!r)
return;
if (r->match) {
free(r->match);
r->match = NULL;
}
if (!r->match_count) {
return;
}
for (unsigned i = 0; i < r->match_count; i++) {