diff --git a/.github/workflows/file_checks.yml b/.github/workflows/file_checks.yml new file mode 100644 index 0000000..a462c32 --- /dev/null +++ b/.github/workflows/file_checks.yml @@ -0,0 +1,18 @@ +name: Format checks + +on: + push: + branches: [ "main" ] + + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: C format check + run: find functions/ -iname '*.h' -o -iname '*.c' | clang-format --dry-run -Werror --files=/dev/stdin --style=LLVM \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d28f46c --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2025 ETH Zurich EASL + +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. diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 120000 index 0000000..ea5b606 --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..452bcd7 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +## Overview + +This repository contains examples of how to write functions to run on [Dandelion](https://github.com/eth-easl/dandelion) + +The different folders contain examples and more information for the supported languages. + +Curretly we only have full support for C and C++. +Examples for how to write functions, setup builds and other details can be found in the `c_functons` folder. + +There is also support for functions compiled to Wasm. +The tooling to compile functions this way is in the `wasm-compiler` folder. \ No newline at end of file diff --git a/functions/CMakeLists.txt b/c_functions/CMakeLists.txt similarity index 100% rename from functions/CMakeLists.txt rename to c_functions/CMakeLists.txt diff --git a/c_functions/README.md b/c_functions/README.md new file mode 100644 index 0000000..f0a5959 --- /dev/null +++ b/c_functions/README.md @@ -0,0 +1,29 @@ +# Functions + +This folder contains a series of functions used for basic functionality tests or as example applications. +The functions are split into multiple executables that can be built individually. + +All C and C++ functions depend on the [dandelion SDK](https://github.com/eth-easl/dandelion). +Functions can either only use the lowest level interface `dandelion_runtime` or the `dlibc`/`dlibcxx`. +The runtime provides basic wrapper to start a function and interact directly with input and output sets and buffers. +The libc variants also support reading and writing inputs and outputs to a filesystem abstraction, which is further explained in the SDK documentation. + +# Building + +The build for this repository is set up to allow building functions for all backends and architectures supported by the SDK. +To set this up use the following commands: + +``` +mkdir build +cd build +cmake -DPLATFORM= -DARCHITECTURE= -DCMAKE_BUILD_TYPE- . +``` + +The platforms supported are `CHERI`, `MMU_FREEBSD`, `MMU_LINUX`, `KVM`, `WASM`, `DEBUG`. \ +We support `x86_64` and `aarch64` architectures. \ +The build type can be either `Debug` or `Release`. + +When one of the parameters is not given, it defaults to the `DEBUG` platform, the build system architecture and `Debug` build type. + +The CMake in this project is set up to automatically download the latest version of the dandelion SDK when it is initially set up. +The easiest way to use a different version, is to replace the `dandelion_sdk` folder in the build folder with a symlink to the version to be used. \ No newline at end of file diff --git a/functions/basic/CMakeLists.txt b/c_functions/basic/CMakeLists.txt similarity index 100% rename from functions/basic/CMakeLists.txt rename to c_functions/basic/CMakeLists.txt diff --git a/functions/basic/basic.c b/c_functions/basic/basic.c similarity index 100% rename from functions/basic/basic.c rename to c_functions/basic/basic.c diff --git a/functions/busy/CMakeLists.txt b/c_functions/busy/CMakeLists.txt similarity index 100% rename from functions/busy/CMakeLists.txt rename to c_functions/busy/CMakeLists.txt diff --git a/functions/busy/busy.c b/c_functions/busy/busy.c similarity index 100% rename from functions/busy/busy.c rename to c_functions/busy/busy.c diff --git a/functions/busy_hybrid/CMakeLists.txt b/c_functions/busy_hybrid/CMakeLists.txt similarity index 100% rename from functions/busy_hybrid/CMakeLists.txt rename to c_functions/busy_hybrid/CMakeLists.txt diff --git a/c_functions/busy_hybrid/busy_hybrid.c b/c_functions/busy_hybrid/busy_hybrid.c new file mode 100644 index 0000000..d8b01ab --- /dev/null +++ b/c_functions/busy_hybrid/busy_hybrid.c @@ -0,0 +1,289 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "syscall.h" +#include "unistd.h" + +#define ARRAY_ITEMS 16 +#define BUFFER_SIZE 1 * 1024 * 1024 +#define FETCH_REQUEST_PATH "/data/get_request" +#define STORE_PREAMBLE_PATH "/preamble/post_request" +#define STORE_RESPONSE_PATH "/store_request/post_response" + +typedef struct data_item { + int8_t value[ARRAY_ITEMS]; +} data_item; + +uint16_t my_htons(uint16_t port) { + return ((port >> 8) & 0x00FF) | ((port << 8) & 0xFF00); +} + +int my_inet_pton(int af, const char *src, void *dst) { + if (af == LINUX_AF_INET) { + uint8_t array[4] = {}; + for (int i = 0; i < 4; i++) { + char *end; + long value = strtol(src, &end, 10); + if (value < 0 || value > 255) { + return 0; + } + + if (*end != '\0' && *end != '.' && i < 3) { + return 0; + } + src = end + 1; + array[i] = (uint8_t)value; + } + + struct in_addr *addr = (struct in_addr *)dst; + memcpy(&addr->s_addr, array, 4); // Copy the 4 bytes into s_addr + + return 1; // Success + } + return -1; // Unsupported address family +} + +int extract_ip_port(const char *http_request, char *ip, size_t ip_len, + uint16_t *port) { + const char *prefix = "http://"; + const char *start = strstr(http_request, prefix); + if (!start) + return -1; + + start += strlen(prefix); + const char *colon = strchr(start, ':'); + if (!colon) + return -2; + + const char *slash = strchr(colon, '/'); + if (!slash) + return -3; + + // Extract IP + size_t ip_size = colon - start; + if (ip_size >= ip_len) + return -4; + strncpy(ip, start, ip_size); + ip[ip_size] = '\0'; + + // Extract port + char port_str[6] = {0}; + size_t port_size = slash - colon - 1; + if (port_size >= sizeof(port_str)) + return -5; + strncpy(port_str, colon + 1, port_size); + *port = atoi(port_str); + + return 0; +} + +int connect_to_server(const char *ip, uint16_t port) { + struct sockaddr_in server_addr; + + int sockfd = linux_socket(LINUX_AF_INET, LINUX_SOCK_STREAM, 0); + if (sockfd < 0) { + perror("Socket creation failed"); + return -1; + } + + server_addr.sin_family = LINUX_AF_INET; + server_addr.sin_port = my_htons(port); + + if (my_inet_pton(LINUX_AF_INET, ip, &server_addr.sin_addr) != 1) { + perror("Invalid IP address"); + close(sockfd); + return -1; + } + + int err = linux_connect(sockfd, &server_addr, sizeof(server_addr)); + if (err < 0) { + printf("Error: %s\n", strerror(-err)); + printf("size of server_addr: %ld\n", sizeof(server_addr)); + perror("Socket connection failed"); + close(sockfd); + return -1; + } + + return sockfd; +} + +ssize_t send_http_request(int sockfd, const char *request, size_t request_size, + char *response, size_t response_size) { + if (linux_send(sockfd, request, request_size, 0) < 0) { + perror("Error sending request"); + return -1; + } + + // TODO: ensure the entire HTTP response has been received + ssize_t total_received = 0, bytes; + // while ((bytes = linux_recv(sockfd, response + total_received, + // response_size - total_received - 1, 0)) > 0) { + // total_received += bytes; + // if (total_received >= response_size - 1) break; + // } + // blocks and waits forever if there is no next packate, could validate HTTP + // sice on the packages to see if we need more, this works for now + total_received = linux_recv(sockfd, response + total_received, + response_size - total_received - 1, 0); + if (total_received < 0) { + perror("Error receiving response"); + return -1; + } + response[total_received] = '\0'; + return total_received; +} + +void parse_http_body(const char *response, size_t response_size, + char **response_body, size_t *response_body_size) { + *response_body = strstr(response, "\r\n\r\n"); + if (*response_body == NULL) + return; + *response_body += 4; + *response_body_size = response_size - (*response_body - response); +} + +size_t read_file_to_string(const char *filename, char **content) { + // Open file in read mode + FILE *file = fopen(filename, "r"); + if (file == NULL) { + perror("Error opening file"); + } + + // Get file size + fseek(file, 0, SEEK_END); + size_t file_size = ftell(file); + rewind(file); + + // Allocate memory for content (+1 for null terminator) + *content = (char *)malloc(file_size + 1); + if (*content == NULL) { + perror("Error allocating memory"); + } + + // Read file into the buffer + size_t bytes_read = fread(*content, 1, file_size, file); + (*content)[bytes_read] = '\0'; // Add null terminator + fclose(file); + + return bytes_read; // Return the length of the content +} + +int main() { + char *fetch_preamble = NULL; + size_t fetch_preamble_len = + read_file_to_string(FETCH_REQUEST_PATH, &fetch_preamble); + if (fetch_preamble == NULL) + return 1; + + char server_ip[16]; + uint16_t server_port; + if (extract_ip_port(fetch_preamble, server_ip, 16, &server_port) < 0) + return 2; + + // fetching data + int sock = connect_to_server(server_ip, server_port); + if (sock < 0) + return 3; + + size_t fetch_request_len = fetch_preamble_len + 4; + char *fetch_request = malloc(fetch_request_len); + memcpy(fetch_request, fetch_preamble, fetch_preamble_len); + memcpy(fetch_request + fetch_preamble_len, "\r\n\r\n", 4); + + char *fetch_response = (char *)malloc(BUFFER_SIZE); + if (fetch_response == NULL) { + perror("Error allocating memory"); + return 4; + } + ssize_t fetch_response_len = send_http_request( + sock, fetch_request, fetch_request_len, fetch_response, BUFFER_SIZE); + if (fetch_response_len < 0) + return 5; + close(sock); + + char *fetch_response_body; + size_t fetch_response_body_len; + parse_http_body(fetch_response, fetch_response_len, &fetch_response_body, + &fetch_response_body_len); + if (fetch_response_body == NULL) + return 6; + + uint64_t iterations = *(uint64_t *)fetch_response_body; + data_item *data_items = (data_item *)(fetch_response_body + 8); + + // the igreggated statistics we want to collect + // sum, average, variance, min, max + int64_t sum = 0; + int64_t min = 256; + int64_t max = -256; + + size_t struct_index = 0; + size_t value_index = 0; + size_t max_index = (fetch_response_body_len - 8) / sizeof(data_item); + for (size_t iteration = 0; iteration < iterations; iteration++) { + for (size_t array_index = 0; array_index < ARRAY_ITEMS; array_index++) { + int8_t value = data_items[struct_index].value[array_index]; + if (value > max) + max = value; + if (value < min) + min = value; + sum += value; + } + // using size_t which has no sign, guarantees that the index is not + // negative when computing the remainder + struct_index = + (struct_index + data_items[struct_index].value[value_index]) % + max_index; + value_index = (value_index + 1) % ARRAY_ITEMS; + } + + char *store_preamble = NULL; + size_t store_preamble_len = + read_file_to_string(STORE_PREAMBLE_PATH, &store_preamble); + if (store_preamble == NULL) + return 7; + store_preamble_len -= 2; // omit \n\n at the end + + char header[] = "\r\ncontent-length: 24\r\n\r\n"; // 3 * sizeof(int64_t) + size_t store_request_len = + store_preamble_len + (sizeof(header) - 1) + 3 * sizeof(int64_t); + char *store_request = malloc(store_request_len); + char *current_ptr = store_request; + memcpy(current_ptr, store_preamble, store_preamble_len); + current_ptr += store_preamble_len; + memcpy(current_ptr, header, sizeof(header) - 1); + current_ptr += sizeof(header) - 1; + memcpy(current_ptr, &sum, sizeof(int64_t)); + current_ptr += sizeof(int64_t); + memcpy(current_ptr, &min, sizeof(int64_t)); + current_ptr += sizeof(int64_t); + memcpy(current_ptr, &max, sizeof(int64_t)); + current_ptr += sizeof(int64_t); + + // posting data + char *store_response = fetch_response; + sock = connect_to_server(server_ip, server_port); + if (sock < 0) + return 3; + ssize_t store_response_len = send_http_request( + sock, store_request, store_request_len, store_response, BUFFER_SIZE); + if (store_response_len < 0) + return 8; + close(sock); + + FILE *store_response_file = fopen(STORE_RESPONSE_PATH, "wb"); + if (store_response_file == NULL) { + perror("Error opening file"); + return 9; + } + fwrite(store_response, 1, store_response_len, store_response_file); + fclose(store_response_file); + + return 0; +} diff --git a/functions/busy_hybrid/linux_syscalls/CMakeLists.txt b/c_functions/busy_hybrid/linux_syscalls/CMakeLists.txt similarity index 100% rename from functions/busy_hybrid/linux_syscalls/CMakeLists.txt rename to c_functions/busy_hybrid/linux_syscalls/CMakeLists.txt diff --git a/functions/busy_hybrid/linux_syscalls/arch/aarch64/syscall_arch.h b/c_functions/busy_hybrid/linux_syscalls/arch/aarch64/syscall_arch.h similarity index 100% rename from functions/busy_hybrid/linux_syscalls/arch/aarch64/syscall_arch.h rename to c_functions/busy_hybrid/linux_syscalls/arch/aarch64/syscall_arch.h diff --git a/functions/busy_hybrid/linux_syscalls/arch/x86_64/syscall_arch.h b/c_functions/busy_hybrid/linux_syscalls/arch/x86_64/syscall_arch.h similarity index 100% rename from functions/busy_hybrid/linux_syscalls/arch/x86_64/syscall_arch.h rename to c_functions/busy_hybrid/linux_syscalls/arch/x86_64/syscall_arch.h diff --git a/functions/example_app_hybrid/linux_syscalls/syscall.h b/c_functions/busy_hybrid/linux_syscalls/syscall.h similarity index 74% rename from functions/example_app_hybrid/linux_syscalls/syscall.h rename to c_functions/busy_hybrid/linux_syscalls/syscall.h index 17c166a..723eceb 100644 --- a/functions/example_app_hybrid/linux_syscalls/syscall.h +++ b/c_functions/busy_hybrid/linux_syscalls/syscall.h @@ -18,12 +18,12 @@ #define LINUX_SOCK_STREAM 1 // poll defines -#define POLLIN 0x0001 -#define POLLPRI 0x0002 -#define POLLOUT 0x0004 -#define POLLERR 0x0008 -#define POLLHUP 0x0010 -#define POLLNVAL 0x0020 +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 #ifndef __scc #define __scc(X) ((long)(X)) @@ -63,34 +63,33 @@ struct sockaddr { }; struct in_addr { - uint32_t s_addr; + uint32_t s_addr; }; struct sockaddr_in { - unsigned short sin_family; - uint16_t sin_port; - struct in_addr sin_addr; - unsigned char sin_zero[sizeof (struct sockaddr) - - sizeof(unsigned short) - - sizeof(uint16_t) - - sizeof(struct in_addr)]; + unsigned short sin_family; + uint16_t sin_port; + struct in_addr sin_addr; + unsigned char sin_zero[sizeof(struct sockaddr) - sizeof(unsigned short) - + sizeof(uint16_t) - sizeof(struct in_addr)]; }; -static inline int linux_connect(int socketfd, const struct sockaddr_in* addr, int addrlen){ +static inline int linux_connect(int socketfd, const struct sockaddr_in *addr, + int addrlen) { return __syscall(SYS_connect, socketfd, addr, addrlen); } -static inline ptrdiff_t linux_send(int socketfd, const void* buf, size_t len, int flags){ +static inline ptrdiff_t linux_send(int socketfd, const void *buf, size_t len, + int flags) { return __syscall(SYS_sendto, socketfd, buf, len, flags, NULL, 0); } -static inline ptrdiff_t linux_recv(int socketfd, void* buf, size_t len, int flags){ +static inline ptrdiff_t linux_recv(int socketfd, void *buf, size_t len, + int flags) { return __syscall(SYS_recvfrom, socketfd, buf, len, flags, NULL, NULL); } -static inline int linux_close(int fd){ - return __syscall(SYS_close, fd); -} +static inline int linux_close(int fd) { return __syscall(SYS_close, fd); } struct pollfd { int fd; @@ -98,7 +97,7 @@ struct pollfd { short revents; }; -static inline int linux_ppoll(struct pollfd* fds, unsigned int nfds) { +static inline int linux_ppoll(struct pollfd *fds, unsigned int nfds) { return __syscall(SYS_ppoll, fds, nfds, NULL, NULL); } diff --git a/functions/busy_libc/CMakeLists.txt b/c_functions/busy_libc/CMakeLists.txt similarity index 100% rename from functions/busy_libc/CMakeLists.txt rename to c_functions/busy_libc/CMakeLists.txt diff --git a/c_functions/busy_libc/busy_libc.c b/c_functions/busy_libc/busy_libc.c new file mode 100644 index 0000000..6297f62 --- /dev/null +++ b/c_functions/busy_libc/busy_libc.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dandelion/runtime.h" +#include "unistd.h" + +#define ARRAY_ITEMS 16 +#define FETCH_REQUEST_PATH "/data/get_request" +#define STORE_PREAMBLE_PATH "/preamble/post_request" +#define STORE_RESPONSE_PATH "/store_request/post_response" + +typedef struct data_item { + int8_t value[ARRAY_ITEMS]; +} data_item; + +size_t read_file_to_string(const char *filename, char **content) { + // Open file in read mode + FILE *file = fopen(filename, "r"); + if (file == NULL) { + perror("Error opening file"); + } + + // Get file size + fseek(file, 0, SEEK_END); + size_t file_size = ftell(file); + rewind(file); + + // Allocate memory for content (+1 for null terminator) + *content = (char *)malloc(file_size + 1); + if (*content == NULL) { + perror("Error allocating memory"); + } + + // Read file into the buffer + size_t bytes_read = fread(*content, 1, file_size, file); + (*content)[bytes_read] = '\0'; // Add null terminator + fclose(file); + + return bytes_read; // Return the length of the content +} + +int main() { + // use dandelion runtime interface directly to avoid data copy + IoBuffer *data_buffer = dandelion_get_input(0, 0); + char *data = (char *)data_buffer->data; + size_t data_len = data_buffer->data_len; + // char *data = NULL; + // size_t data_len = read_file_to_string(FETCH_REQUEST_PATH, &data); + // if (data == NULL) + // return 1; + + uint64_t iterations = *(uint64_t *)data; + data_item *data_items = (data_item *)(data + 8); + + // the igreggated statistics we want to collect + // sum, average, variance, min, max + int64_t sum = 0; + int64_t min = 256; + int64_t max = -256; + + size_t struct_index = 0; + size_t value_index = 0; + size_t max_index = (data_len - 8) / sizeof(data_item); + for (size_t iteration = 0; iteration < iterations; iteration++) { + for (size_t array_index = 0; array_index < ARRAY_ITEMS; array_index++) { + int8_t value = data_items[struct_index].value[array_index]; + if (value > max) + max = value; + if (value < min) + min = value; + sum += value; + } + // using size_t which has no sign, guarantees that the index is not + // negative when computing the remainder + struct_index = + (struct_index + data_items[struct_index].value[value_index]) % + max_index; + value_index = (value_index + 1) % ARRAY_ITEMS; + } + + char *store_preamble = NULL; + size_t store_preamble_len = + read_file_to_string(STORE_PREAMBLE_PATH, &store_preamble); + if (store_preamble == NULL) + return 7; + store_preamble_len -= 2; // omit \n\n at the end + + char header[] = "\nContent-Length: 24\n\n"; // 3 * sizeof(int64_t) + size_t store_request_len = + store_preamble_len + (sizeof(header) - 1) + 3 * sizeof(int64_t); + char *store_request = malloc(store_request_len); + char *current_ptr = store_request; + memcpy(current_ptr, store_preamble, store_preamble_len); + current_ptr += store_preamble_len; + memcpy(current_ptr, header, sizeof(header) - 1); + current_ptr += sizeof(header) - 1; + memcpy(current_ptr, &sum, sizeof(int64_t)); + current_ptr += sizeof(int64_t); + memcpy(current_ptr, &min, sizeof(int64_t)); + current_ptr += sizeof(int64_t); + memcpy(current_ptr, &max, sizeof(int64_t)); + current_ptr += sizeof(int64_t); + + FILE *store_response_file = fopen(STORE_RESPONSE_PATH, "wb"); + if (store_response_file == NULL) { + perror("Error opening file"); + return 9; + } + fwrite(store_request, 1, store_request_len, store_response_file); + fclose(store_response_file); + + return 0; +} diff --git a/functions/cfg++_template.txt b/c_functions/cfg++_template.txt similarity index 100% rename from functions/cfg++_template.txt rename to c_functions/cfg++_template.txt diff --git a/functions/cfg_template.txt b/c_functions/cfg_template.txt similarity index 100% rename from functions/cfg_template.txt rename to c_functions/cfg_template.txt diff --git a/functions/compression/CMakeLists.txt b/c_functions/compression/CMakeLists.txt similarity index 100% rename from functions/compression/CMakeLists.txt rename to c_functions/compression/CMakeLists.txt diff --git a/functions/compression/compression.c b/c_functions/compression/compression.c similarity index 92% rename from functions/compression/compression.c rename to c_functions/compression/compression.c index 9826247..a86011e 100644 --- a/functions/compression/compression.c +++ b/c_functions/compression/compression.c @@ -27,7 +27,9 @@ void *realloc_sized(void *old, size_t old_size, size_t new_size) { } #define STBI_WRITE_NO_STDIO -#define STBIW_ASSERT(x) {} +#define STBIW_ASSERT(x) \ + { \ + } #define STBIW_MALLOC(size) dandelion_alloc(size, 8) #define STBIW_FREE(ptr) dandelion_free(ptr) #define STBIW_REALLOC_SIZED(ptr, old_size, new_size) \ diff --git a/functions/compression/qoi.h b/c_functions/compression/qoi.h similarity index 56% rename from functions/compression/qoi.h rename to c_functions/compression/qoi.h index f2800b0..9a3a48d 100644 --- a/functions/compression/qoi.h +++ b/c_functions/compression/qoi.h @@ -24,10 +24,10 @@ stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and // Encode and store an RGBA buffer to the file system. The qoi_desc describes // the input pixel data. qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){ - .width = 1920, - .height = 1080, - .channels = 4, - .colorspace = QOI_SRGB + .width = 1920, + .height = 1080, + .channels = 4, + .colorspace = QOI_SRGB }); // Load and decode a QOI image from the file system into a 32bbp RGBA buffer. @@ -64,11 +64,12 @@ A QOI file has a 14 byte header, followed by any number of data "chunks" and an 8-byte end marker. struct qoi_header_t { - char magic[4]; // magic bytes "qoif" - uint32_t width; // image width in pixels (BE) - uint32_t height; // image height in pixels (BE) - uint8_t channels; // 3 = RGB, 4 = RGBA - uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear + char magic[4]; // magic bytes "qoif" + uint32_t width; // image width in pixels (BE) + uint32_t height; // image height in pixels (BE) + uint8_t channels; // 3 = RGB, 4 = RGBA + uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels +linear }; Images are encoded row by row, left to right, top to bottom. The decoder and @@ -91,7 +92,7 @@ the color value. In the encoder, if the pixel value at the index matches the current pixel, this index position is written to the stream as QOI_OP_INDEX. The hash function for the index is: - index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 + index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All @@ -153,8 +154,8 @@ The alpha value remains unchanged from the previous pixel. The green channel is used to indicate the general direction of change and is encoded in 6 bits. The red and blue channels (dr and db) base their diffs off of the green channel difference and are encoded in 4 bits. I.e.: - dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g) - db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g) + dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g) + db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g) The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while "250 + 7" will result in 1. @@ -207,7 +208,6 @@ The alpha value remains unchanged from the previous pixel. */ - /* ----------------------------------------------------------------------------- Header - Public functions */ @@ -224,20 +224,20 @@ filled with the description read from the file header (for qoi_read and qoi_decode). The colorspace in this qoi_desc is an enum where - 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel - 1 = all channels are linear + 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel + 1 = all channels are linear You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely informative. It will be saved to the file header, but does not affect how chunks are en-/decoded. */ -#define QOI_SRGB 0 +#define QOI_SRGB 0 #define QOI_LINEAR 1 typedef struct { - unsigned int width; - unsigned int height; - unsigned char channels; - unsigned char colorspace; + unsigned int width; + unsigned int height; + unsigned char channels; + unsigned char colorspace; } qoi_desc; #ifndef QOI_NO_STDIO @@ -251,7 +251,6 @@ failed) or the number of bytes written on success. */ int qoi_write(const char *filename, const void *data, const qoi_desc *desc); - /* Read and decode a QOI image from the file system. If channels is 0, the number of channels from the file header is used. If channels is 3 or 4 the output format will be forced into this number of channels. @@ -266,7 +265,6 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels); #endif /* QOI_NO_STDIO */ - /* Encode raw RGB or RGBA pixels into a QOI image in memory. The function either returns NULL on failure (invalid parameters or malloc @@ -277,7 +275,6 @@ The returned qoi data should be free()d after use. */ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len); - /* Decode a QOI image from memory. The function either returns NULL on failure (invalid parameters or malloc @@ -288,13 +285,11 @@ The returned pixel data should be free()d after use. */ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); - #ifdef __cplusplus } #endif #endif /* QOI_H */ - /* ----------------------------------------------------------------------------- Implementation */ @@ -303,26 +298,27 @@ Implementation */ #include #ifndef QOI_MALLOC - #define QOI_MALLOC(sz) malloc(sz) - #define QOI_FREE(p) free(p) +#define QOI_MALLOC(sz) malloc(sz) +#define QOI_FREE(p) free(p) #endif #ifndef QOI_ZEROARR - #define QOI_ZEROARR(a) memset((a),0,sizeof(a)) +#define QOI_ZEROARR(a) memset((a), 0, sizeof(a)) #endif -#define QOI_OP_INDEX 0x00 /* 00xxxxxx */ -#define QOI_OP_DIFF 0x40 /* 01xxxxxx */ -#define QOI_OP_LUMA 0x80 /* 10xxxxxx */ -#define QOI_OP_RUN 0xc0 /* 11xxxxxx */ -#define QOI_OP_RGB 0xfe /* 11111110 */ -#define QOI_OP_RGBA 0xff /* 11111111 */ +#define QOI_OP_INDEX 0x00 /* 00xxxxxx */ +#define QOI_OP_DIFF 0x40 /* 01xxxxxx */ +#define QOI_OP_LUMA 0x80 /* 10xxxxxx */ +#define QOI_OP_RUN 0xc0 /* 11xxxxxx */ +#define QOI_OP_RGB 0xfe /* 11111110 */ +#define QOI_OP_RGBA 0xff /* 11111111 */ -#define QOI_MASK_2 0xc0 /* 11000000 */ +#define QOI_MASK_2 0xc0 /* 11000000 */ -#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11) -#define QOI_MAGIC \ - (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ - ((unsigned int)'i') << 8 | ((unsigned int)'f')) +#define QOI_COLOR_HASH(C) \ + (C.rgba.r * 3 + C.rgba.g * 5 + C.rgba.b * 7 + C.rgba.a * 11) +#define QOI_MAGIC \ + (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ + ((unsigned int)'i') << 8 | ((unsigned int)'f')) #define QOI_HEADER_SIZE 14 /* 2GB is the max file size that this implementation can safely handle. We guard @@ -332,317 +328,290 @@ enough for anybody. */ #define QOI_PIXELS_MAX ((unsigned int)400000000) typedef union { - struct { unsigned char r, g, b, a; } rgba; - unsigned int v; + struct { + unsigned char r, g, b, a; + } rgba; + unsigned int v; } qoi_rgba_t; -static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1}; +static const unsigned char qoi_padding[8] = {0, 0, 0, 0, 0, 0, 0, 1}; static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { - bytes[(*p)++] = (0xff000000 & v) >> 24; - bytes[(*p)++] = (0x00ff0000 & v) >> 16; - bytes[(*p)++] = (0x0000ff00 & v) >> 8; - bytes[(*p)++] = (0x000000ff & v); + bytes[(*p)++] = (0xff000000 & v) >> 24; + bytes[(*p)++] = (0x00ff0000 & v) >> 16; + bytes[(*p)++] = (0x0000ff00 & v) >> 8; + bytes[(*p)++] = (0x000000ff & v); } static unsigned int qoi_read_32(const unsigned char *bytes, int *p) { - unsigned int a = bytes[(*p)++]; - unsigned int b = bytes[(*p)++]; - unsigned int c = bytes[(*p)++]; - unsigned int d = bytes[(*p)++]; - return a << 24 | b << 16 | c << 8 | d; + unsigned int a = bytes[(*p)++]; + unsigned int b = bytes[(*p)++]; + unsigned int c = bytes[(*p)++]; + unsigned int d = bytes[(*p)++]; + return a << 24 | b << 16 | c << 8 | d; } void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { - int i, max_size, p, run; - int px_len, px_end, px_pos, channels; - unsigned char *bytes; - const unsigned char *pixels; - qoi_rgba_t index[64]; - qoi_rgba_t px, px_prev; - - if ( - data == NULL || out_len == NULL || desc == NULL || - desc->width == 0 || desc->height == 0 || - desc->channels < 3 || desc->channels > 4 || - desc->colorspace > 1 || - desc->height >= QOI_PIXELS_MAX / desc->width - ) { - return NULL; - } - - max_size = - desc->width * desc->height * (desc->channels + 1) + - QOI_HEADER_SIZE + sizeof(qoi_padding); - - p = 0; - bytes = (unsigned char *) QOI_MALLOC(max_size); - if (!bytes) { - return NULL; - } - - qoi_write_32(bytes, &p, QOI_MAGIC); - qoi_write_32(bytes, &p, desc->width); - qoi_write_32(bytes, &p, desc->height); - bytes[p++] = desc->channels; - bytes[p++] = desc->colorspace; - - - pixels = (const unsigned char *)data; - - QOI_ZEROARR(index); - - run = 0; - px_prev.rgba.r = 0; - px_prev.rgba.g = 0; - px_prev.rgba.b = 0; - px_prev.rgba.a = 255; - px = px_prev; - - px_len = desc->width * desc->height * desc->channels; - px_end = px_len - desc->channels; - channels = desc->channels; - - for (px_pos = 0; px_pos < px_len; px_pos += channels) { - px.rgba.r = pixels[px_pos + 0]; - px.rgba.g = pixels[px_pos + 1]; - px.rgba.b = pixels[px_pos + 2]; - - if (channels == 4) { - px.rgba.a = pixels[px_pos + 3]; - } - - if (px.v == px_prev.v) { - run++; - if (run == 62 || px_pos == px_end) { - bytes[p++] = QOI_OP_RUN | (run - 1); - run = 0; - } - } - else { - int index_pos; - - if (run > 0) { - bytes[p++] = QOI_OP_RUN | (run - 1); - run = 0; - } - - index_pos = QOI_COLOR_HASH(px) % 64; - - if (index[index_pos].v == px.v) { - bytes[p++] = QOI_OP_INDEX | index_pos; - } - else { - index[index_pos] = px; - - if (px.rgba.a == px_prev.rgba.a) { - signed char vr = px.rgba.r - px_prev.rgba.r; - signed char vg = px.rgba.g - px_prev.rgba.g; - signed char vb = px.rgba.b - px_prev.rgba.b; - - signed char vg_r = vr - vg; - signed char vg_b = vb - vg; - - if ( - vr > -3 && vr < 2 && - vg > -3 && vg < 2 && - vb > -3 && vb < 2 - ) { - bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2); - } - else if ( - vg_r > -9 && vg_r < 8 && - vg > -33 && vg < 32 && - vg_b > -9 && vg_b < 8 - ) { - bytes[p++] = QOI_OP_LUMA | (vg + 32); - bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8); - } - else { - bytes[p++] = QOI_OP_RGB; - bytes[p++] = px.rgba.r; - bytes[p++] = px.rgba.g; - bytes[p++] = px.rgba.b; - } - } - else { - bytes[p++] = QOI_OP_RGBA; - bytes[p++] = px.rgba.r; - bytes[p++] = px.rgba.g; - bytes[p++] = px.rgba.b; - bytes[p++] = px.rgba.a; - } - } - } - px_prev = px; - } - - for (i = 0; i < (int)sizeof(qoi_padding); i++) { - bytes[p++] = qoi_padding[i]; - } - - *out_len = p; - return bytes; + int i, max_size, p, run; + int px_len, px_end, px_pos, channels; + unsigned char *bytes; + const unsigned char *pixels; + qoi_rgba_t index[64]; + qoi_rgba_t px, px_prev; + + if (data == NULL || out_len == NULL || desc == NULL || desc->width == 0 || + desc->height == 0 || desc->channels < 3 || desc->channels > 4 || + desc->colorspace > 1 || desc->height >= QOI_PIXELS_MAX / desc->width) { + return NULL; + } + + max_size = desc->width * desc->height * (desc->channels + 1) + + QOI_HEADER_SIZE + sizeof(qoi_padding); + + p = 0; + bytes = (unsigned char *)QOI_MALLOC(max_size); + if (!bytes) { + return NULL; + } + + qoi_write_32(bytes, &p, QOI_MAGIC); + qoi_write_32(bytes, &p, desc->width); + qoi_write_32(bytes, &p, desc->height); + bytes[p++] = desc->channels; + bytes[p++] = desc->colorspace; + + pixels = (const unsigned char *)data; + + QOI_ZEROARR(index); + + run = 0; + px_prev.rgba.r = 0; + px_prev.rgba.g = 0; + px_prev.rgba.b = 0; + px_prev.rgba.a = 255; + px = px_prev; + + px_len = desc->width * desc->height * desc->channels; + px_end = px_len - desc->channels; + channels = desc->channels; + + for (px_pos = 0; px_pos < px_len; px_pos += channels) { + px.rgba.r = pixels[px_pos + 0]; + px.rgba.g = pixels[px_pos + 1]; + px.rgba.b = pixels[px_pos + 2]; + + if (channels == 4) { + px.rgba.a = pixels[px_pos + 3]; + } + + if (px.v == px_prev.v) { + run++; + if (run == 62 || px_pos == px_end) { + bytes[p++] = QOI_OP_RUN | (run - 1); + run = 0; + } + } else { + int index_pos; + + if (run > 0) { + bytes[p++] = QOI_OP_RUN | (run - 1); + run = 0; + } + + index_pos = QOI_COLOR_HASH(px) % 64; + + if (index[index_pos].v == px.v) { + bytes[p++] = QOI_OP_INDEX | index_pos; + } else { + index[index_pos] = px; + + if (px.rgba.a == px_prev.rgba.a) { + signed char vr = px.rgba.r - px_prev.rgba.r; + signed char vg = px.rgba.g - px_prev.rgba.g; + signed char vb = px.rgba.b - px_prev.rgba.b; + + signed char vg_r = vr - vg; + signed char vg_b = vb - vg; + + if (vr > -3 && vr < 2 && vg > -3 && vg < 2 && vb > -3 && vb < 2) { + bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2); + } else if (vg_r > -9 && vg_r < 8 && vg > -33 && vg < 32 && + vg_b > -9 && vg_b < 8) { + bytes[p++] = QOI_OP_LUMA | (vg + 32); + bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8); + } else { + bytes[p++] = QOI_OP_RGB; + bytes[p++] = px.rgba.r; + bytes[p++] = px.rgba.g; + bytes[p++] = px.rgba.b; + } + } else { + bytes[p++] = QOI_OP_RGBA; + bytes[p++] = px.rgba.r; + bytes[p++] = px.rgba.g; + bytes[p++] = px.rgba.b; + bytes[p++] = px.rgba.a; + } + } + } + px_prev = px; + } + + for (i = 0; i < (int)sizeof(qoi_padding); i++) { + bytes[p++] = qoi_padding[i]; + } + + *out_len = p; + return bytes; } void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { - const unsigned char *bytes; - unsigned int header_magic; - unsigned char *pixels; - qoi_rgba_t index[64]; - qoi_rgba_t px; - int px_len, chunks_len, px_pos; - int p = 0, run = 0; - - if ( - data == NULL || desc == NULL || - (channels != 0 && channels != 3 && channels != 4) || - size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding) - ) { - return NULL; - } - - bytes = (const unsigned char *)data; - - header_magic = qoi_read_32(bytes, &p); - desc->width = qoi_read_32(bytes, &p); - desc->height = qoi_read_32(bytes, &p); - desc->channels = bytes[p++]; - desc->colorspace = bytes[p++]; - - if ( - desc->width == 0 || desc->height == 0 || - desc->channels < 3 || desc->channels > 4 || - desc->colorspace > 1 || - header_magic != QOI_MAGIC || - desc->height >= QOI_PIXELS_MAX / desc->width - ) { - return NULL; - } - - if (channels == 0) { - channels = desc->channels; - } - - px_len = desc->width * desc->height * channels; - pixels = (unsigned char *) QOI_MALLOC(px_len); - if (!pixels) { - return NULL; - } - - QOI_ZEROARR(index); - px.rgba.r = 0; - px.rgba.g = 0; - px.rgba.b = 0; - px.rgba.a = 255; - - chunks_len = size - (int)sizeof(qoi_padding); - for (px_pos = 0; px_pos < px_len; px_pos += channels) { - if (run > 0) { - run--; - } - else if (p < chunks_len) { - int b1 = bytes[p++]; - - if (b1 == QOI_OP_RGB) { - px.rgba.r = bytes[p++]; - px.rgba.g = bytes[p++]; - px.rgba.b = bytes[p++]; - } - else if (b1 == QOI_OP_RGBA) { - px.rgba.r = bytes[p++]; - px.rgba.g = bytes[p++]; - px.rgba.b = bytes[p++]; - px.rgba.a = bytes[p++]; - } - else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { - px = index[b1]; - } - else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { - px.rgba.r += ((b1 >> 4) & 0x03) - 2; - px.rgba.g += ((b1 >> 2) & 0x03) - 2; - px.rgba.b += ( b1 & 0x03) - 2; - } - else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) { - int b2 = bytes[p++]; - int vg = (b1 & 0x3f) - 32; - px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f); - px.rgba.g += vg; - px.rgba.b += vg - 8 + (b2 & 0x0f); - } - else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) { - run = (b1 & 0x3f); - } - - index[QOI_COLOR_HASH(px) % 64] = px; - } - - pixels[px_pos + 0] = px.rgba.r; - pixels[px_pos + 1] = px.rgba.g; - pixels[px_pos + 2] = px.rgba.b; - - if (channels == 4) { - pixels[px_pos + 3] = px.rgba.a; - } - } - - return pixels; + const unsigned char *bytes; + unsigned int header_magic; + unsigned char *pixels; + qoi_rgba_t index[64]; + qoi_rgba_t px; + int px_len, chunks_len, px_pos; + int p = 0, run = 0; + + if (data == NULL || desc == NULL || + (channels != 0 && channels != 3 && channels != 4) || + size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)) { + return NULL; + } + + bytes = (const unsigned char *)data; + + header_magic = qoi_read_32(bytes, &p); + desc->width = qoi_read_32(bytes, &p); + desc->height = qoi_read_32(bytes, &p); + desc->channels = bytes[p++]; + desc->colorspace = bytes[p++]; + + if (desc->width == 0 || desc->height == 0 || desc->channels < 3 || + desc->channels > 4 || desc->colorspace > 1 || header_magic != QOI_MAGIC || + desc->height >= QOI_PIXELS_MAX / desc->width) { + return NULL; + } + + if (channels == 0) { + channels = desc->channels; + } + + px_len = desc->width * desc->height * channels; + pixels = (unsigned char *)QOI_MALLOC(px_len); + if (!pixels) { + return NULL; + } + + QOI_ZEROARR(index); + px.rgba.r = 0; + px.rgba.g = 0; + px.rgba.b = 0; + px.rgba.a = 255; + + chunks_len = size - (int)sizeof(qoi_padding); + for (px_pos = 0; px_pos < px_len; px_pos += channels) { + if (run > 0) { + run--; + } else if (p < chunks_len) { + int b1 = bytes[p++]; + + if (b1 == QOI_OP_RGB) { + px.rgba.r = bytes[p++]; + px.rgba.g = bytes[p++]; + px.rgba.b = bytes[p++]; + } else if (b1 == QOI_OP_RGBA) { + px.rgba.r = bytes[p++]; + px.rgba.g = bytes[p++]; + px.rgba.b = bytes[p++]; + px.rgba.a = bytes[p++]; + } else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { + px = index[b1]; + } else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { + px.rgba.r += ((b1 >> 4) & 0x03) - 2; + px.rgba.g += ((b1 >> 2) & 0x03) - 2; + px.rgba.b += (b1 & 0x03) - 2; + } else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) { + int b2 = bytes[p++]; + int vg = (b1 & 0x3f) - 32; + px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f); + px.rgba.g += vg; + px.rgba.b += vg - 8 + (b2 & 0x0f); + } else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) { + run = (b1 & 0x3f); + } + + index[QOI_COLOR_HASH(px) % 64] = px; + } + + pixels[px_pos + 0] = px.rgba.r; + pixels[px_pos + 1] = px.rgba.g; + pixels[px_pos + 2] = px.rgba.b; + + if (channels == 4) { + pixels[px_pos + 3] = px.rgba.a; + } + } + + return pixels; } #ifndef QOI_NO_STDIO #include int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { - FILE *f = fopen(filename, "wb"); - int size, err; - void *encoded; - - if (!f) { - return 0; - } - - encoded = qoi_encode(data, desc, &size); - if (!encoded) { - fclose(f); - return 0; - } - - fwrite(encoded, 1, size, f); - fflush(f); - err = ferror(f); - fclose(f); - - QOI_FREE(encoded); - return err ? 0 : size; + FILE *f = fopen(filename, "wb"); + int size, err; + void *encoded; + + if (!f) { + return 0; + } + + encoded = qoi_encode(data, desc, &size); + if (!encoded) { + fclose(f); + return 0; + } + + fwrite(encoded, 1, size, f); + fflush(f); + err = ferror(f); + fclose(f); + + QOI_FREE(encoded); + return err ? 0 : size; } void *qoi_read(const char *filename, qoi_desc *desc, int channels) { - FILE *f = fopen(filename, "rb"); - int size, bytes_read; - void *pixels, *data; - - if (!f) { - return NULL; - } - - fseek(f, 0, SEEK_END); - size = ftell(f); - if (size <= 0 || fseek(f, 0, SEEK_SET) != 0) { - fclose(f); - return NULL; - } - - data = QOI_MALLOC(size); - if (!data) { - fclose(f); - return NULL; - } - - bytes_read = fread(data, 1, size, f); - fclose(f); - pixels = (bytes_read != size) ? NULL : qoi_decode(data, bytes_read, desc, channels); - QOI_FREE(data); - return pixels; + FILE *f = fopen(filename, "rb"); + int size, bytes_read; + void *pixels, *data; + + if (!f) { + return NULL; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + if (size <= 0 || fseek(f, 0, SEEK_SET) != 0) { + fclose(f); + return NULL; + } + + data = QOI_MALLOC(size); + if (!data) { + fclose(f); + return NULL; + } + + bytes_read = fread(data, 1, size, f); + fclose(f); + pixels = (bytes_read != size) ? NULL + : qoi_decode(data, bytes_read, desc, channels); + QOI_FREE(data); + return pixels; } #endif /* QOI_NO_STDIO */ diff --git a/functions/compression/stb_image_write.h b/c_functions/compression/stb_image_write.h similarity index 100% rename from functions/compression/stb_image_write.h rename to c_functions/compression/stb_image_write.h diff --git a/functions/dirigent_busy/CMakeLists.txt b/c_functions/dirigent_busy/CMakeLists.txt similarity index 100% rename from functions/dirigent_busy/CMakeLists.txt rename to c_functions/dirigent_busy/CMakeLists.txt diff --git a/c_functions/dirigent_busy/dirigent_busy.c b/c_functions/dirigent_busy/dirigent_busy.c new file mode 100644 index 0000000..a7ab4f5 --- /dev/null +++ b/c_functions/dirigent_busy/dirigent_busy.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unistd.h" + +int main(int argc, char const *argv[]) { + // parse input + FILE *input_data = fopen("/input/input.csv", "r"); + if (input_data == NULL) { + perror("could not open /input/input.csv"); + return -1; + } + FILE *output_data = fopen("/output/output.csv", "w+"); + if (output_data == NULL) { + perror("could not open /output/output.csv"); + return -1; + } + + char *workload = NULL; + size_t workload_size = 0; + if (__getdelim(&workload, &workload_size, ',', input_data) < 0) { + perror("failed to read workload"); + return -1; + } + workload[strlen(workload) - 1] = '\0'; + + char *function = NULL; + size_t function_size = 0; + if (__getdelim(&function, &function_size, ',', input_data) < 0) { + perror("failed to read function"); + return -1; + } + function[strlen(function) - 1] = '\0'; + + char *requestedCpu = NULL; + size_t requestedCpu_size = 0; + if (__getdelim(&requestedCpu, &requestedCpu_size, ',', input_data) < 0) { + perror("failed to read requestedCpu"); + return -1; + } + requestedCpu[strlen(requestedCpu) - 1] = '\0'; + + char *multiplier = NULL; + size_t multiplier_size = 0; + if (__getdelim(&multiplier, &multiplier_size, ',', input_data) < 0) { + perror("failed to read multiplier"); + return -1; + } + + char *trace_string = "trace"; + char *empyt_string = "empty"; + + if (strcmp(trace_string, function) == 0) { + long multiplier_num = strtol(multiplier, NULL, 10); + long requested_cpu_num = strtol(requestedCpu, NULL, 10); + + long total_iterations = multiplier_num * requested_cpu_num; + + // printf("%ld, %ld, %ld\n", multiplier_num, requested_cpu_num, + // total_iterations); + + volatile double result = 0.0; + volatile double input = 10.0; + volatile long iteration; + for (iteration = 0; iteration < total_iterations; iteration++) { + result = sqrt(input); + } + + fprintf(output_data, "\"OK\",\"%s\",\"dandelionServer\",%ld,%f", function, + iteration, result); + + return 0; + } else if (strcmp(empyt_string, function) == 0) { + fprintf(output_data, "\"OK - EMPTY\",\"dandelionServer\",\"%s\",0", + function); + + return 0; + } + + return -1; +} diff --git a/functions/example_app/CMakeLists.txt b/c_functions/example_app/CMakeLists.txt similarity index 100% rename from functions/example_app/CMakeLists.txt rename to c_functions/example_app/CMakeLists.txt diff --git a/functions/example_app/fan_out.c b/c_functions/example_app/fan_out.c similarity index 64% rename from functions/example_app/fan_out.c rename to c_functions/example_app/fan_out.c index e32c13e..d451f05 100644 --- a/functions/example_app/fan_out.c +++ b/c_functions/example_app/fan_out.c @@ -8,7 +8,7 @@ #include "unistd.h" -const unsigned int SERVER_NUMBER = 10; +const unsigned int SERVER_NUMBER = 10; int main(int argc, char const *argv[]) { @@ -18,15 +18,15 @@ int main(int argc, char const *argv[]) { perror("Failed to open file to get log server\n"); return -1; } - char* server_line = NULL; + char *server_line = NULL; size_t server_line_len = 0; - if(__getline(&server_line, &server_line_len, log_server) < 0) { + if (__getline(&server_line, &server_line_len, log_server) < 0) { perror("Fauled to read line from auth server file\n"); return -1; } - + // read auth response - FILE* auth_file = fopen("/responses/auth", "r"); + FILE *auth_file = fopen("/responses/auth", "r"); if (auth_file == NULL) { perror("Failed to open auth file\n"); return -1; @@ -34,20 +34,24 @@ int main(int argc, char const *argv[]) { // get the json with the athorization char username[21] = {0}; char token[257] = {0}; - if (fscanf(auth_file, "%*[^\"]\"authorized\":\"%20[^\"]\",\"token\":\"%256[^\"]\"}", username, token) < 0) { + if (fscanf(auth_file, + "%*[^\"]\"authorized\":\"%20[^\"]\",\"token\":\"%256[^\"]\"}", + username, token) < 0) { perror("Failed to parse line from token file"); return -1; } - for(unsigned int server_index = 0; server_index < SERVER_NUMBER; server_index++){ + for (unsigned int server_index = 0; server_index < SERVER_NUMBER; + server_index++) { char request_path[100] = {0}; - if (snprintf(request_path, 100, "/requests/server_%d", server_index) < 0){ - perror("Failed to format file path for log request"); - return -1; + if (snprintf(request_path, 100, "/requests/server_%d", server_index) < 0) { + perror("Failed to format file path for log request"); + return -1; } // write authorization request FILE *log_request = fopen(request_path, "w+"); - fprintf(log_request, "GET http://%s/logs/%02d HTTP/1.1\n\n", server_line, server_index); + fprintf(log_request, "GET http://%s/logs/%02d HTTP/1.1\n\n", server_line, + server_index); fprintf(log_request, "{\"username\": \"%s\"}", username); } diff --git a/functions/example_app/handle.c b/c_functions/example_app/handle.c similarity index 84% rename from functions/example_app/handle.c rename to c_functions/example_app/handle.c index f812947..6f77ba8 100644 --- a/functions/example_app/handle.c +++ b/c_functions/example_app/handle.c @@ -16,15 +16,15 @@ int main(int argc, char const *argv[]) { perror("Failed to open file to get auth server"); return -1; } - char* server_line = NULL; + char *server_line = NULL; size_t server_line_len = 0; - if(__getline(&server_line, &server_line_len, auth_server) < 0) { + if (__getline(&server_line, &server_line_len, auth_server) < 0) { perror("Fauled to read line from auth server file\n"); return -1; } - + // read token from incomming file - FILE* token_file = fopen("/responses/Authorization", "r"); + FILE *token_file = fopen("/responses/Authorization", "r"); if (token_file == NULL) { perror("Failed to open file with token"); return -1; diff --git a/c_functions/example_app/template.c b/c_functions/example_app/template.c new file mode 100644 index 0000000..75b0cd9 --- /dev/null +++ b/c_functions/example_app/template.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "unistd.h" + +typedef struct log_node { + struct log_node *next; + char timestamp[100]; + char server_id[100]; + char type[100]; + char details[100]; +} log_node; + +char event_template[] = "{" + "\"details\":\"%100[^\"]\"," + "\"event_type\":\"%100[^\"]\"," + "\"server_id\":\"%100[^\"]\"," + "\"timestamp\":\"%100[^\"]\"" + "%*[^{]"; + +int main(int argc, char const *argv[]) { + + log_node *log_root = NULL; + + // read all events and put the in list sorted by timestamps + DIR *log_dir = opendir("/responses"); + if (log_dir == NULL) { + perror("Could not open log directory"); + return -1; + } + + struct dirent *entry; + while ((entry = readdir(log_dir))) { + if (entry->d_type == DT_REG) { + char filepath[256] = {0}; + if (snprintf(filepath, 256, "/responses/%s", entry->d_name) < 0) { + perror("Failed to format log filepath\n"); + return -1; + } + FILE *log_file = fopen(filepath, "r"); + size_t line_size = 0; + char *line_buffer = NULL; + // read until and including initial [ + if (__getdelim(&line_buffer, &line_size, '[', log_file) < 0) { + perror("Failed to read initial json {"); + return -1; + } + free(line_buffer); + // keep reading until we find no more } + int items_found = 0; + log_node *current = malloc(sizeof(log_node)); + while ((items_found = fscanf(log_file, event_template, current->details, + current->type, current->server_id, + current->timestamp)) == 4) { + current->next = NULL; + if (log_root == NULL) { + log_root = current; + } else { + if (strcmp(current->timestamp, log_root->timestamp) <= 0) { + current->next = log_root; + log_root = current; + } else { + log_node *previous = log_root; + while (previous->next != NULL) { + log_node *next = previous->next; + if (strcmp(current->timestamp, next->timestamp) <= 0) { + current->next = next; + break; + } + previous = next; + } + previous->next = current; + } + } + current = malloc(sizeof(log_node)); + } + free(current); + } + } + + // open output file + FILE *log_file = fopen("/requests/log.txt", "w+"); + if (log_file == NULL) { + perror("Failed to open file to get auth server\n"); + return -1; + } + // write preamble + fprintf(log_file, "\n"); + fprintf(log_file, "\n"); + fprintf(log_file, "\n"); + fprintf(log_file, " \n"); + fprintf(log_file, " Logs\n"); + fprintf(log_file, "\n"); + fprintf(log_file, "\n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + + // write log information + for (log_node *current = log_root; current != NULL; current = current->next) { + fprintf(log_file, " \n"); + fprintf(log_file, " \n", current->timestamp); + fprintf(log_file, " \n", current->server_id); + fprintf(log_file, " \n", current->type); + fprintf(log_file, " \n", current->details); + fprintf(log_file, " \n"); + } + + // write ending + fprintf(log_file, "
TimestampServer IDEvent TypeDetails
%s%s%s%s
\n"); + fprintf(log_file, "\n"); + + return -1; +} diff --git a/functions/example_app_hybrid/CMakeLists.txt b/c_functions/example_app_hybrid/CMakeLists.txt similarity index 100% rename from functions/example_app_hybrid/CMakeLists.txt rename to c_functions/example_app_hybrid/CMakeLists.txt diff --git a/c_functions/example_app_hybrid/app_hybrid.c b/c_functions/example_app_hybrid/app_hybrid.c new file mode 100644 index 0000000..9860e38 --- /dev/null +++ b/c_functions/example_app_hybrid/app_hybrid.c @@ -0,0 +1,410 @@ +#include "unistd.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "syscall.h" + +#define BUFFER_SIZE 4096 +#define SERVER_NUMBER 10 +const uint16_t AUTH_SERVER_PORT = 8000; + +// Structure to store log event details +typedef struct log_node { + struct log_node *next; + char timestamp[100]; + char server_id[100]; + char type[100]; + char details[100]; +} log_node; + +char event_template[] = "%*[^{]{" + "\"details\":\"%100[^\"]\"," + "\"event_type\":\"%100[^\"]\"," + "\"server_id\":\"%100[^\"]\"," + "\"timestamp\":\"%100[^\"]\"" + "%}"; + +uint16_t my_htons(uint16_t port) { + return ((port >> 8) & 0x00FF) | ((port << 8) & 0xFF00); +} + +int my_inet_pton(int af, const char *src, void *dst) { + if (af == LINUX_AF_INET) { + uint8_t array[4] = {}; + for (int i = 0; i < 4; i++) { + char *end; + long value = strtol(src, &end, 10); + if (value < 0 || value > 255) { + return 0; + } + + if (*end != '\0' && *end != '.' && i < 3) { + return 0; + } + src = end + 1; + array[i] = (uint8_t)value; + } + + struct in_addr *addr = (struct in_addr *)dst; + memcpy(&addr->s_addr, array, 4); // Copy the 4 bytes into s_addr + + return 1; // Success + } + return -1; // Unsupported address family +} + +int connect_to_server(const char *ip, uint16_t port) { + struct sockaddr_in server_addr; + + int sockfd = linux_socket(LINUX_AF_INET, LINUX_SOCK_STREAM, 0); + if (sockfd < 0) { + perror("Socket creation failed"); + return -1; + } + + server_addr.sin_family = LINUX_AF_INET; + server_addr.sin_port = my_htons(port); + + if (my_inet_pton(LINUX_AF_INET, ip, &server_addr.sin_addr) != 1) { + perror("Invalid IP address"); + close(sockfd); + return -1; + } + + int err = linux_connect(sockfd, &server_addr, sizeof(server_addr)); + if (err < 0) { + printf("Error: %s\n", strerror(-err)); + printf("size of server_addr: %ld\n", sizeof(server_addr)); + perror("Socket connection failed"); + close(sockfd); + return -1; + } + + return sockfd; +} + +int send_http_request(int sockfd, const char *request, size_t request_size, + char *response, size_t response_size) { + if (linux_send(sockfd, request, request_size, 0) < 0) { + perror("Send failed"); + return -1; + } + + ssize_t total_received = 0, bytes; + // while ((bytes = linux_recv(sockfd, response + total_received, response_size + // - total_received - 1, 0)) > 0) { + // total_received += bytes; + // if (total_received >= response_size - 1) break; + // } + // blocks and waits forever if there is no next packate, could validate HTTP + // sice on the packages to see if we need more, this works for now + total_received = linux_recv(sockfd, response + total_received, + response_size - total_received - 1, 0); + if (total_received < 0) { + return -1; + } + response[total_received] = '\0'; + return 0; +} + +void parse_auth_response(const char *response, char *token) { + const char *start = strstr(response, "\"token\":"); + if (start) { + sscanf(start, "\"token\": \"%[^\"]", token); + } +} + +void parse_log_events(const char *response, log_node **log_root) { + log_node *current = NULL; + + const char *start = strstr(response, "\"events\":"); + if (start) { + start += 9; + while (1) { + current = malloc(sizeof(log_node)); + int found = sscanf(start, event_template, current->details, current->type, + current->server_id, current->timestamp); + if (found != 4) { + free(current); + break; + } + + current->next = *log_root; + *log_root = current; + + start = strstr(start, "},{"); + if (!start) + break; + start += 1; + } + } +} + +// Render logs to an HTML file +void render_logs_to_html(log_node *log_root) { + FILE *log_file = fopen("/requests/log.html", "w+"); + if (!log_file) { + perror("Failed to open HTML file"); + return; + } + + // sort by date + // get number + size_t num_entries = 0; + log_node *current = log_root; + while (current) { + num_entries++; + current = current->next; + } + log_node **log_heap = malloc(sizeof(log_node *) * num_entries); + current = log_root; + for (size_t index = 0; index < num_entries; index++) { + log_heap[index] = current; + current = current->next; + for (size_t insert = index; insert > 0; insert = insert / 2) { + if (strcmp(log_heap[insert / 2]->timestamp, + log_heap[insert]->timestamp) <= 0) { + break; + } else { + log_node *tmp = log_heap[insert]; + log_heap[insert] = log_heap[insert / 2]; + log_heap[insert / 2] = tmp; + } + } + } + + // write preamble + fprintf(log_file, "\n"); + fprintf(log_file, "\n"); + fprintf(log_file, "\n"); + fprintf(log_file, " \n"); + fprintf(log_file, " Logs\n"); + fprintf(log_file, "\n"); + fprintf(log_file, "\n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + fprintf(log_file, " \n"); + + // write log information + for (; num_entries > 0; num_entries--) { + current = log_heap[0]; + fprintf(log_file, " \n"); + fprintf(log_file, " \n", current->timestamp); + fprintf(log_file, " \n", current->server_id); + fprintf(log_file, " \n", current->type); + fprintf(log_file, " \n", current->details); + fprintf(log_file, " \n"); + // remove current root from heap + size_t index = 0; + log_heap[0] = log_heap[num_entries - 1]; + // trade down until no more children + while (index * 2 + 1 < num_entries) { + // compare with first child + int is_bigger = strcmp(log_heap[index]->timestamp, + log_heap[index * 2 + 1]->timestamp); + // check if second child exists + if (index * 2 + 2 >= num_entries) { + // second child does not exist, if is bigger than child swap + if (is_bigger > 0) { + log_node *tmp = log_heap[index]; + log_heap[index] = log_heap[index * 2 + 1]; + log_heap[index * 2 + 1] = tmp; + } + break; + } + // know fist child is smaller, if second is smaller than first swap with + // second otherwise with first + if (is_bigger > 0) { + size_t swap_index; + if (strcmp(log_heap[index * 2 + 1]->timestamp, + log_heap[index * 2 + 2]->timestamp) <= 0) { + // first is smaller + swap_index = index * 2 + 1; + } else { + swap_index = index * 2 + 2; + } + log_node *tmp = log_heap[index]; + log_heap[index] = log_heap[swap_index]; + log_heap[swap_index] = tmp; + index = swap_index; + } else { + // first is not smaller so need to check second if swap is neccessary + if (strcmp(log_heap[index]->timestamp, + log_heap[index * 2 + 2]->timestamp) > 0) { + // is bigger so need to swap + log_node *tmp = log_heap[index]; + log_heap[index] = log_heap[index * 2 + 2]; + log_heap[index * 2 + 2] = tmp; + index = index * 2 + 2; + } else { + break; + } + } + } + } + + // write ending + fprintf(log_file, "
TimestampServer IDEvent TypeDetails
%s%s%s%s
\n"); + fprintf(log_file, "\n"); + + fclose(log_file); +} + +// Main function to handle authorization, log fetching, and rendering +int main() { + // read authentification server ip from file + FILE *auth_server = fopen("/servers/server.txt", "r"); + if (auth_server == NULL) { + perror("Failed to open file to get auth server"); + return -1; + } + char *auth_server_ip = NULL; + size_t server_line_len = 0; + size_t read_chars = __getline(&auth_server_ip, &server_line_len, auth_server); + if (read_chars < 0) { + perror("Fauled to read line from auth server file\n"); + return -1; + } + // strip possible endln from the getline + if (auth_server_ip[read_chars - 1] == '\n') { + auth_server_ip[read_chars - 1] = '\0'; + } + + // read token from incomming file + FILE *token_file = fopen("/responses/Authorization", "r"); + if (token_file == NULL) { + perror("Failed to open file with token"); + return -1; + } + char auth_token[257] = {0}; + if (fscanf(token_file, "Bearer %256s", auth_token) < 0) { + perror("Failed to parse line from token file"); + return -1; + } + + // handle + int auth_sock = connect_to_server(auth_server_ip, AUTH_SERVER_PORT); + if (auth_sock < 0) + return 1; + + char auth_request[BUFFER_SIZE]; + int written = snprintf(auth_request, sizeof(auth_request), + "POST /authorize HTTP/1.1\n" + "Host: %s\n" + "Content-Type: application/json\n" + "Content-Length: %zu\n\n" + "{\"token\": \"%s\"}", + auth_server_ip, strlen(auth_token) + 13, auth_token); + if (written < 0 || written > BUFFER_SIZE) { + perror("Failed to format auth request"); + return -1; + } + char auth_response[BUFFER_SIZE]; + if (send_http_request(auth_sock, auth_request, written, auth_response, + BUFFER_SIZE) < 0) { + close(auth_sock); + return 1; + } + + close(auth_sock); + + char token[256]; + parse_auth_response(auth_response, token); + + // fan_out + log_node *log_root = NULL; + char log_request[SERVER_NUMBER][BUFFER_SIZE]; + int request_length[SERVER_NUMBER]; + char log_response[SERVER_NUMBER][BUFFER_SIZE]; + size_t response_read[SERVER_NUMBER]; + struct pollfd poll_fds[SERVER_NUMBER]; + + int log_sockets[SERVER_NUMBER]; + // open sockets + for (int server = 0; server < SERVER_NUMBER; server++) { + log_sockets[server] = connect_to_server(auth_server_ip, AUTH_SERVER_PORT); + if (log_sockets[server] < 0) { + perror("Failed to open log socket"); + return -1; + } + } + // format requests + for (int server = 0; server < SERVER_NUMBER; server++) { + request_length[server] = + snprintf(log_request[server], sizeof(log_request[server]), + "GET /logs/%d HTTP/1.1\n" + "Host: %s\n" + "Authorization: Bearer %s\n\n", + server, auth_server_ip, token); + if (request_length < 0) { + perror("Could not format server log request"); + return -1; + } + } + // issue all requests + for (int server = 0; server < SERVER_NUMBER; server++) { + if (linux_send(log_sockets[server], log_request[server], + request_length[server], 0) < 0) { + perror("Send failed"); + return -1; + } + response_read[server] = 0; + poll_fds[server].fd = log_sockets[server]; + poll_fds[server].events = POLLIN; + poll_fds[server].revents = 0; + } + unsigned int received_responses = 0; + while (received_responses < SERVER_NUMBER) { + int poll_result = linux_ppoll(poll_fds, SERVER_NUMBER); + if (poll_result < 0) { + perror("Polling failed"); + return -1; + } + // check all the file descriptors for arrived data + for (int server = 0; server < SERVER_NUMBER; server++) { + if (poll_fds[server].fd > 0 && + (poll_fds[server].revents & (POLLIN | POLLHUP))) { + received_responses++; + // disable further polling on this socket + poll_fds[server].fd = -1; + int new_data = linux_recv(log_sockets[server], + log_response[server] + response_read[server], + BUFFER_SIZE - response_read[server] - 1, 0); + if (new_data < 0) { + perror("Failed to read from socket"); + return -1; + } + response_read[server] += new_data; + log_response[server][new_data] = '\0'; + parse_log_events(log_response[server], &log_root); + } + } + } + // close sockets + for (int server = 0; server < SERVER_NUMBER; server++) { + close(log_sockets[server]); + } + + // render + render_logs_to_html(log_root); + + // Free log nodes + log_node *current = log_root; + while (current) { + log_node *next = current->next; + free(current); + current = next; + } + + return 0; +} \ No newline at end of file diff --git a/functions/example_app_hybrid/linux_syscalls/CMakeLists.txt b/c_functions/example_app_hybrid/linux_syscalls/CMakeLists.txt similarity index 100% rename from functions/example_app_hybrid/linux_syscalls/CMakeLists.txt rename to c_functions/example_app_hybrid/linux_syscalls/CMakeLists.txt diff --git a/functions/example_app_hybrid/linux_syscalls/arch/aarch64/syscall_arch.h b/c_functions/example_app_hybrid/linux_syscalls/arch/aarch64/syscall_arch.h similarity index 100% rename from functions/example_app_hybrid/linux_syscalls/arch/aarch64/syscall_arch.h rename to c_functions/example_app_hybrid/linux_syscalls/arch/aarch64/syscall_arch.h diff --git a/functions/example_app_hybrid/linux_syscalls/arch/x86_64/syscall_arch.h b/c_functions/example_app_hybrid/linux_syscalls/arch/x86_64/syscall_arch.h similarity index 100% rename from functions/example_app_hybrid/linux_syscalls/arch/x86_64/syscall_arch.h rename to c_functions/example_app_hybrid/linux_syscalls/arch/x86_64/syscall_arch.h diff --git a/functions/busy_hybrid/linux_syscalls/syscall.h b/c_functions/example_app_hybrid/linux_syscalls/syscall.h similarity index 74% rename from functions/busy_hybrid/linux_syscalls/syscall.h rename to c_functions/example_app_hybrid/linux_syscalls/syscall.h index 17c166a..723eceb 100644 --- a/functions/busy_hybrid/linux_syscalls/syscall.h +++ b/c_functions/example_app_hybrid/linux_syscalls/syscall.h @@ -18,12 +18,12 @@ #define LINUX_SOCK_STREAM 1 // poll defines -#define POLLIN 0x0001 -#define POLLPRI 0x0002 -#define POLLOUT 0x0004 -#define POLLERR 0x0008 -#define POLLHUP 0x0010 -#define POLLNVAL 0x0020 +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 #ifndef __scc #define __scc(X) ((long)(X)) @@ -63,34 +63,33 @@ struct sockaddr { }; struct in_addr { - uint32_t s_addr; + uint32_t s_addr; }; struct sockaddr_in { - unsigned short sin_family; - uint16_t sin_port; - struct in_addr sin_addr; - unsigned char sin_zero[sizeof (struct sockaddr) - - sizeof(unsigned short) - - sizeof(uint16_t) - - sizeof(struct in_addr)]; + unsigned short sin_family; + uint16_t sin_port; + struct in_addr sin_addr; + unsigned char sin_zero[sizeof(struct sockaddr) - sizeof(unsigned short) - + sizeof(uint16_t) - sizeof(struct in_addr)]; }; -static inline int linux_connect(int socketfd, const struct sockaddr_in* addr, int addrlen){ +static inline int linux_connect(int socketfd, const struct sockaddr_in *addr, + int addrlen) { return __syscall(SYS_connect, socketfd, addr, addrlen); } -static inline ptrdiff_t linux_send(int socketfd, const void* buf, size_t len, int flags){ +static inline ptrdiff_t linux_send(int socketfd, const void *buf, size_t len, + int flags) { return __syscall(SYS_sendto, socketfd, buf, len, flags, NULL, 0); } -static inline ptrdiff_t linux_recv(int socketfd, void* buf, size_t len, int flags){ +static inline ptrdiff_t linux_recv(int socketfd, void *buf, size_t len, + int flags) { return __syscall(SYS_recvfrom, socketfd, buf, len, flags, NULL, NULL); } -static inline int linux_close(int fd){ - return __syscall(SYS_close, fd); -} +static inline int linux_close(int fd) { return __syscall(SYS_close, fd); } struct pollfd { int fd; @@ -98,7 +97,7 @@ struct pollfd { short revents; }; -static inline int linux_ppoll(struct pollfd* fds, unsigned int nfds) { +static inline int linux_ppoll(struct pollfd *fds, unsigned int nfds) { return __syscall(SYS_ppoll, fds, nfds, NULL, NULL); } diff --git a/functions/example_app_nolibc/CMakeLists.txt b/c_functions/example_app_nolibc/CMakeLists.txt similarity index 100% rename from functions/example_app_nolibc/CMakeLists.txt rename to c_functions/example_app_nolibc/CMakeLists.txt diff --git a/functions/example_app_nolibc/commons.h b/c_functions/example_app_nolibc/commons.h similarity index 100% rename from functions/example_app_nolibc/commons.h rename to c_functions/example_app_nolibc/commons.h diff --git a/functions/example_app_nolibc/fan_out.c b/c_functions/example_app_nolibc/fan_out.c similarity index 100% rename from functions/example_app_nolibc/fan_out.c rename to c_functions/example_app_nolibc/fan_out.c diff --git a/functions/example_app_nolibc/handle.c b/c_functions/example_app_nolibc/handle.c similarity index 100% rename from functions/example_app_nolibc/handle.c rename to c_functions/example_app_nolibc/handle.c diff --git a/functions/example_app_nolibc/template.c b/c_functions/example_app_nolibc/template.c similarity index 100% rename from functions/example_app_nolibc/template.c rename to c_functions/example_app_nolibc/template.c diff --git a/functions/files/CMakeLists.txt b/c_functions/files/CMakeLists.txt similarity index 100% rename from functions/files/CMakeLists.txt rename to c_functions/files/CMakeLists.txt diff --git a/functions/files/fileio.c b/c_functions/files/fileio.c similarity index 91% rename from functions/files/fileio.c rename to c_functions/files/fileio.c index b74a382..5fefae4 100644 --- a/functions/files/fileio.c +++ b/c_functions/files/fileio.c @@ -41,13 +41,13 @@ int copy_folder(char *in_parent, char *out_parent) { char *new_in = new_path(in_parent, entry->d_name); if (entry->d_type == DT_DIR) { // check if folder exists - DIR* out_parent = opendir(new_out); - if(out_parent == NULL && errno == ENOENT){ + DIR *out_parent = opendir(new_out); + if (out_parent == NULL && errno == ENOENT) { if (mkdir(new_out, 0x777) < 0) { perror("Failed to create new directory"); return -1; } - } else if(out_parent == NULL) { + } else if (out_parent == NULL) { perror("Failed check for existing directory"); return -1; } else { diff --git a/functions/matmac/CMakeLists.txt b/c_functions/matmac/CMakeLists.txt similarity index 100% rename from functions/matmac/CMakeLists.txt rename to c_functions/matmac/CMakeLists.txt diff --git a/functions/matmac/matmac.c b/c_functions/matmac/matmac.c similarity index 100% rename from functions/matmac/matmac.c rename to c_functions/matmac/matmac.c diff --git a/functions/matmul/CMakeLists.txt b/c_functions/matmul/CMakeLists.txt similarity index 100% rename from functions/matmul/CMakeLists.txt rename to c_functions/matmul/CMakeLists.txt diff --git a/functions/matmul/matmul.c b/c_functions/matmul/matmul.c similarity index 100% rename from functions/matmul/matmul.c rename to c_functions/matmul/matmul.c diff --git a/functions/stdio/CMakeLists.txt b/c_functions/stdio/CMakeLists.txt similarity index 100% rename from functions/stdio/CMakeLists.txt rename to c_functions/stdio/CMakeLists.txt diff --git a/functions/stdio/stdio.c b/c_functions/stdio/stdio.c similarity index 83% rename from functions/stdio/stdio.c rename to c_functions/stdio/stdio.c index b0e2190..d5766b4 100644 --- a/functions/stdio/stdio.c +++ b/c_functions/stdio/stdio.c @@ -4,7 +4,7 @@ #include "unistd.h" -int main(int argc, char const* argv[]) { +int main(int argc, char const *argv[]) { // print to std out and std err with the usual mode int err; if ((err = printf("Test string to stdout\n")) < 0) { @@ -22,12 +22,13 @@ int main(int argc, char const* argv[]) { } printf("read %zi characters from stdin\n", read_chars); ssize_t written = fwrite(in_buffer, 1, read_chars, stdout); - if (written != read_chars) return -4; + if (written != read_chars) + return -4; // print string arguments for (int iter = 0; iter < argc; iter++) { printf("argument %d is %s\n", iter, argv[iter]); } - char* env_home = getenv("HOME"); + char *env_home = getenv("HOME"); printf("environmental variable HOME is %s\n", env_home); return 0; } diff --git a/functions/stdio_cxx/CMakeLists.txt b/c_functions/stdio_cxx/CMakeLists.txt similarity index 100% rename from functions/stdio_cxx/CMakeLists.txt rename to c_functions/stdio_cxx/CMakeLists.txt diff --git a/functions/stdio_cxx/stdio.cpp b/c_functions/stdio_cxx/stdio.cpp similarity index 100% rename from functions/stdio_cxx/stdio.cpp rename to c_functions/stdio_cxx/stdio.cpp diff --git a/functions/README.md b/functions/README.md deleted file mode 100644 index 32f422e..0000000 --- a/functions/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Functions - -This folder contains a series of functions used to test basic functionality. -The functions are split into multiple executables that can be built individually. -Some functions depend on the function interface directly while others use it -through mlibc. - -# Building the C functions -The cmake build process will build the functions based on the latest version of the dandelion SDK. -For this cmake meeds to know the platform to build for (cheri, mmu_freebsd, mmu_linux, debug) and architecture (x86_64, aarch64) -The build type can be Release or Debug. -From the top folder functions can be built with: -``` -mkdir build -cd build -cmake -DPLATFORM= -DARCHITECTURE= -DCMAKE_BUILD_TYPE- ../functions -``` diff --git a/functions/busy_hybrid/busy_hybrid.c b/functions/busy_hybrid/busy_hybrid.c deleted file mode 100644 index edcfa17..0000000 --- a/functions/busy_hybrid/busy_hybrid.c +++ /dev/null @@ -1,289 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "syscall.h" -#include "unistd.h" - -#define ARRAY_ITEMS 16 -#define BUFFER_SIZE 1 * 1024 * 1024 -#define FETCH_REQUEST_PATH "/data/get_request" -#define STORE_PREAMBLE_PATH "/preamble/post_request" -#define STORE_RESPONSE_PATH "/store_request/post_response" - -typedef struct data_item { - int8_t value[ARRAY_ITEMS]; -} data_item; - -uint16_t my_htons(uint16_t port) { - return ((port >> 8) & 0x00FF) | ((port << 8) & 0xFF00); -} - -int my_inet_pton(int af, const char *src, void *dst) { - if (af == LINUX_AF_INET) { - uint8_t array[4] = {}; - for (int i = 0; i < 4; i++) { - char *end; - long value = strtol(src, &end, 10); - if (value < 0 || value > 255) { - return 0; - } - - if (*end != '\0' && *end != '.' && i < 3) { - return 0; - } - src = end + 1; - array[i] = (uint8_t)value; - } - - struct in_addr *addr = (struct in_addr *)dst; - memcpy(&addr->s_addr, array, 4); // Copy the 4 bytes into s_addr - - return 1; // Success - } - return -1; // Unsupported address family -} - -int extract_ip_port(const char *http_request, char *ip, size_t ip_len, - uint16_t *port) { - const char *prefix = "http://"; - const char *start = strstr(http_request, prefix); - if (!start) - return -1; - - start += strlen(prefix); - const char *colon = strchr(start, ':'); - if (!colon) - return -2; - - const char *slash = strchr(colon, '/'); - if (!slash) - return -3; - - // Extract IP - size_t ip_size = colon - start; - if (ip_size >= ip_len) - return -4; - strncpy(ip, start, ip_size); - ip[ip_size] = '\0'; - - // Extract port - char port_str[6] = {0}; - size_t port_size = slash - colon - 1; - if (port_size >= sizeof(port_str)) - return -5; - strncpy(port_str, colon + 1, port_size); - *port = atoi(port_str); - - return 0; -} - -int connect_to_server(const char *ip, uint16_t port) { - struct sockaddr_in server_addr; - - int sockfd = linux_socket(LINUX_AF_INET, LINUX_SOCK_STREAM, 0); - if (sockfd < 0) { - perror("Socket creation failed"); - return -1; - } - - server_addr.sin_family = LINUX_AF_INET; - server_addr.sin_port = my_htons(port); - - if (my_inet_pton(LINUX_AF_INET, ip, &server_addr.sin_addr) != 1) { - perror("Invalid IP address"); - close(sockfd); - return -1; - } - - int err = linux_connect(sockfd, &server_addr, sizeof(server_addr)); - if (err < 0) { - printf("Error: %s\n", strerror(-err)); - printf("size of server_addr: %ld\n", sizeof(server_addr)); - perror("Socket connection failed"); - close(sockfd); - return -1; - } - - return sockfd; -} - -ssize_t send_http_request(int sockfd, const char *request, size_t request_size, - char *response, size_t response_size) { - if (linux_send(sockfd, request, request_size, 0) < 0) { - perror("Error sending request"); - return -1; - } - - // TODO: ensure the entire HTTP response has been received - ssize_t total_received = 0, bytes; - // while ((bytes = linux_recv(sockfd, response + total_received, - // response_size - total_received - 1, 0)) > 0) { - // total_received += bytes; - // if (total_received >= response_size - 1) break; - // } - // blocks and waits forever if there is no next packate, could validate HTTP - // sice on the packages to see if we need more, this works for now - total_received = linux_recv(sockfd, response + total_received, - response_size - total_received - 1, 0); - if (total_received < 0) { - perror("Error receiving response"); - return -1; - } - response[total_received] = '\0'; - return total_received; -} - -void parse_http_body(const char *response, size_t response_size, - char **response_body, size_t *response_body_size) { - *response_body = strstr(response, "\r\n\r\n"); - if (*response_body == NULL) - return; - *response_body += 4; - *response_body_size = response_size - (*response_body - response); -} - -size_t read_file_to_string(const char *filename, char **content) { - // Open file in read mode - FILE *file = fopen(filename, "r"); - if (file == NULL) { - perror("Error opening file"); - } - - // Get file size - fseek(file, 0, SEEK_END); - size_t file_size = ftell(file); - rewind(file); - - // Allocate memory for content (+1 for null terminator) - *content = (char *)malloc(file_size + 1); - if (*content == NULL) { - perror("Error allocating memory"); - } - - // Read file into the buffer - size_t bytes_read = fread(*content, 1, file_size, file); - (*content)[bytes_read] = '\0'; // Add null terminator - fclose(file); - - return bytes_read; // Return the length of the content -} - -int main() { - char *fetch_preamble = NULL; - size_t fetch_preamble_len = - read_file_to_string(FETCH_REQUEST_PATH, &fetch_preamble); - if (fetch_preamble == NULL) - return 1; - - char server_ip[16]; - uint16_t server_port; - if (extract_ip_port(fetch_preamble, server_ip, 16, &server_port) < 0) - return 2; - - // fetching data - int sock = connect_to_server(server_ip, server_port); - if (sock < 0) - return 3; - - size_t fetch_request_len = fetch_preamble_len + 4; - char *fetch_request = malloc(fetch_request_len); - memcpy(fetch_request, fetch_preamble, fetch_preamble_len); - memcpy(fetch_request + fetch_preamble_len, "\r\n\r\n", 4); - - char *fetch_response = (char *)malloc(BUFFER_SIZE); - if (fetch_response == NULL) { - perror("Error allocating memory"); - return 4; - } - ssize_t fetch_response_len = send_http_request( - sock, fetch_request, fetch_request_len, fetch_response, BUFFER_SIZE); - if (fetch_response_len < 0) - return 5; - close(sock); - - char *fetch_response_body; - size_t fetch_response_body_len; - parse_http_body(fetch_response, fetch_response_len, &fetch_response_body, - &fetch_response_body_len); - if (fetch_response_body == NULL) - return 6; - - uint64_t iterations = *(uint64_t *)fetch_response_body; - data_item *data_items = (data_item *)(fetch_response_body + 8); - - // the igreggated statistics we want to collect - // sum, average, variance, min, max - int64_t sum = 0; - int64_t min = 256; - int64_t max = -256; - - size_t struct_index = 0; - size_t value_index = 0; - size_t max_index = (fetch_response_body_len - 8) / sizeof(data_item); - for (size_t iteration = 0; iteration < iterations; iteration++) { - for (size_t array_index = 0; array_index < ARRAY_ITEMS; array_index++) { - int8_t value = data_items[struct_index].value[array_index]; - if (value > max) - max = value; - if (value < min) - min = value; - sum += value; - } - // using size_t which has no sign, guarantees that the index is not - // negative when computing the remainder - struct_index = - (struct_index + data_items[struct_index].value[value_index]) % - max_index; - value_index = (value_index + 1) % ARRAY_ITEMS; - } - - char *store_preamble = NULL; - size_t store_preamble_len = - read_file_to_string(STORE_PREAMBLE_PATH, &store_preamble); - if (store_preamble == NULL) - return 7; - store_preamble_len -= 2; // omit \n\n at the end - - char header[] = "\r\ncontent-length: 24\r\n\r\n"; // 3 * sizeof(int64_t) - size_t store_request_len = - store_preamble_len + (sizeof(header) - 1) + 3 * sizeof(int64_t); - char *store_request = malloc(store_request_len); - char *current_ptr = store_request; - memcpy(current_ptr, store_preamble, store_preamble_len); - current_ptr += store_preamble_len; - memcpy(current_ptr, header, sizeof(header) - 1); - current_ptr += sizeof(header) - 1; - memcpy(current_ptr, &sum, sizeof(int64_t)); - current_ptr += sizeof(int64_t); - memcpy(current_ptr, &min, sizeof(int64_t)); - current_ptr += sizeof(int64_t); - memcpy(current_ptr, &max, sizeof(int64_t)); - current_ptr += sizeof(int64_t); - - // posting data - char *store_response = fetch_response; - sock = connect_to_server(server_ip, server_port); - if (sock < 0) - return 3; - ssize_t store_response_len = send_http_request( - sock, store_request, store_request_len, store_response, BUFFER_SIZE); - if (store_response_len < 0) - return 8; - close(sock); - - FILE *store_response_file = fopen(STORE_RESPONSE_PATH, "wb"); - if (store_response_file == NULL) { - perror("Error opening file"); - return 9; - } - fwrite(store_response, 1, store_response_len, store_response_file); - fclose(store_response_file); - - return 0; -} diff --git a/functions/busy_libc/busy_libc.c b/functions/busy_libc/busy_libc.c deleted file mode 100644 index 4147084..0000000 --- a/functions/busy_libc/busy_libc.c +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dandelion/runtime.h" -#include "unistd.h" - -#define ARRAY_ITEMS 16 -#define FETCH_REQUEST_PATH "/data/get_request" -#define STORE_PREAMBLE_PATH "/preamble/post_request" -#define STORE_RESPONSE_PATH "/store_request/post_response" - -typedef struct data_item { - int8_t value[ARRAY_ITEMS]; -} data_item; - -size_t read_file_to_string(const char *filename, char **content) { - // Open file in read mode - FILE *file = fopen(filename, "r"); - if (file == NULL) { - perror("Error opening file"); - } - - // Get file size - fseek(file, 0, SEEK_END); - size_t file_size = ftell(file); - rewind(file); - - // Allocate memory for content (+1 for null terminator) - *content = (char *)malloc(file_size + 1); - if (*content == NULL) { - perror("Error allocating memory"); - } - - // Read file into the buffer - size_t bytes_read = fread(*content, 1, file_size, file); - (*content)[bytes_read] = '\0'; // Add null terminator - fclose(file); - - return bytes_read; // Return the length of the content -} - -int main() { - // use dandelion runtime interface directly to avoid data copy - IoBuffer *data_buffer = dandelion_get_input(0, 0); - char *data = (char *)data_buffer->data; - size_t data_len = data_buffer->data_len; - // char *data = NULL; - // size_t data_len = read_file_to_string(FETCH_REQUEST_PATH, &data); - // if (data == NULL) - // return 1; - - uint64_t iterations = *(uint64_t *)data; - data_item *data_items = (data_item *)(data + 8); - - // the igreggated statistics we want to collect - // sum, average, variance, min, max - int64_t sum = 0; - int64_t min = 256; - int64_t max = -256; - - size_t struct_index = 0; - size_t value_index = 0; - size_t max_index = (data_len - 8) / sizeof(data_item); - for (size_t iteration = 0; iteration < iterations; iteration++) { - for (size_t array_index = 0; array_index < ARRAY_ITEMS; array_index++) { - int8_t value = data_items[struct_index].value[array_index]; - if (value > max) - max = value; - if (value < min) - min = value; - sum += value; - } - // using size_t which has no sign, guarantees that the index is not - // negative when computing the remainder - struct_index = - (struct_index + data_items[struct_index].value[value_index]) % - max_index; - value_index = (value_index + 1) % ARRAY_ITEMS; - } - - char *store_preamble = NULL; - size_t store_preamble_len = - read_file_to_string(STORE_PREAMBLE_PATH, &store_preamble); - if (store_preamble == NULL) - return 7; - store_preamble_len -= 2; // omit \n\n at the end - - char header[] = "\nContent-Length: 24\n\n"; // 3 * sizeof(int64_t) - size_t store_request_len = - store_preamble_len + (sizeof(header) - 1) + 3 * sizeof(int64_t); - char *store_request = malloc(store_request_len); - char *current_ptr = store_request; - memcpy(current_ptr, store_preamble, store_preamble_len); - current_ptr += store_preamble_len; - memcpy(current_ptr, header, sizeof(header) - 1); - current_ptr += sizeof(header) - 1; - memcpy(current_ptr, &sum, sizeof(int64_t)); - current_ptr += sizeof(int64_t); - memcpy(current_ptr, &min, sizeof(int64_t)); - current_ptr += sizeof(int64_t); - memcpy(current_ptr, &max, sizeof(int64_t)); - current_ptr += sizeof(int64_t); - - FILE *store_response_file = fopen(STORE_RESPONSE_PATH, "wb"); - if (store_response_file == NULL) { - perror("Error opening file"); - return 9; - } - fwrite(store_request, 1, store_request_len, store_response_file); - fclose(store_response_file); - - return 0; -} diff --git a/functions/dirigent_busy/dirigent_busy.c b/functions/dirigent_busy/dirigent_busy.c deleted file mode 100644 index 551d81f..0000000 --- a/functions/dirigent_busy/dirigent_busy.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "unistd.h" - -int main(int argc, char const *argv[]) { - // parse input - FILE *input_data = fopen("/input/input.csv", "r"); - if (input_data == NULL) { - perror("could not open /input/input.csv"); - return -1; - } - FILE *output_data = fopen("/output/output.csv", "w+"); - if (output_data == NULL) { - perror("could not open /output/output.csv"); - return -1; - } - - char *workload = NULL; - size_t workload_size = 0; - if (__getdelim(&workload, &workload_size, ',', input_data) < 0) { - perror("failed to read workload"); - return -1; - } - workload[strlen(workload) - 1] = '\0'; - - char *function = NULL; - size_t function_size = 0; - if (__getdelim(&function, &function_size, ',', input_data) < 0) { - perror("failed to read function"); - return -1; - } - function[strlen(function) - 1] = '\0'; - - char *requestedCpu = NULL; - size_t requestedCpu_size = 0; - if (__getdelim(&requestedCpu, &requestedCpu_size, ',', input_data) < 0) { - perror("failed to read requestedCpu"); - return -1; - } - requestedCpu[strlen(requestedCpu) - 1] = '\0'; - - char *multiplier = NULL; - size_t multiplier_size = 0; - if (__getdelim(&multiplier, &multiplier_size, ',', input_data) < 0) { - perror("failed to read multiplier"); - return -1; - } - - char *trace_string = "trace"; - char *empyt_string = "empty"; - - if (strcmp(trace_string, function) == 0) { - long multiplier_num = strtol(multiplier, NULL, 10); - long requested_cpu_num = strtol(requestedCpu, NULL, 10); - - long total_iterations = multiplier_num * requested_cpu_num; - - //printf("%ld, %ld, %ld\n", multiplier_num, requested_cpu_num, total_iterations); - - volatile double result = 0.0; - volatile double input = 10.0; - volatile long iteration; - for (iteration = 0; iteration < total_iterations; iteration++) { - result = sqrt(input); - } - - fprintf(output_data, "\"OK\",\"%s\",\"dandelionServer\",%ld,%f", function, iteration, result); - - return 0; - } else if (strcmp(empyt_string, function) == 0) { - fprintf(output_data, "\"OK - EMPTY\",\"dandelionServer\",\"%s\",0", function); - - return 0; - } - - return -1; - -} diff --git a/functions/example_app/template.c b/functions/example_app/template.c deleted file mode 100644 index 1b495cd..0000000 --- a/functions/example_app/template.c +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "unistd.h" - -typedef struct log_node{ - struct log_node* next; - char timestamp[100]; - char server_id[100]; - char type[100]; - char details[100]; -} log_node; - -char event_template[] = -"{" -"\"details\":\"%100[^\"]\"," -"\"event_type\":\"%100[^\"]\"," -"\"server_id\":\"%100[^\"]\"," -"\"timestamp\":\"%100[^\"]\"" -"%*[^{]"; - -int main(int argc, char const *argv[]) { - - log_node* log_root = NULL; - - // read all events and put the in list sorted by timestamps - DIR* log_dir = opendir("/responses"); - if (log_dir == NULL) { - perror("Could not open log directory"); - return -1; - } - - struct dirent* entry; - while((entry = readdir(log_dir))) { - if(entry->d_type == DT_REG) { - char filepath[256] = {0}; - if(snprintf(filepath, 256, "/responses/%s", entry->d_name) < 0){ - perror("Failed to format log filepath\n"); - return -1; - } - FILE* log_file = fopen(filepath, "r"); - size_t line_size = 0; - char* line_buffer = NULL; - // read until and including initial [ - if(__getdelim(&line_buffer, &line_size,'[', log_file) < 0){ - perror("Failed to read initial json {"); - return -1; - } - free(line_buffer); - // keep reading until we find no more } - int items_found = 0; - log_node* current = malloc(sizeof(log_node)); - while( - (items_found = - fscanf(log_file, - event_template, - current->details, - current->type, - current->server_id, - current->timestamp)) == 4){ - current->next = NULL; - if(log_root == NULL) { - log_root = current; - } else { - if (strcmp(current->timestamp, log_root->timestamp) <= 0){ - current->next = log_root; - log_root = current; - } else { - log_node* previous = log_root; - while(previous->next != NULL) { - log_node* next = previous->next; - if(strcmp(current->timestamp, next->timestamp) <= 0){ - current->next = next; - break; - } - previous = next; - } - previous->next = current; - } - } - current = malloc(sizeof(log_node)); - } - free(current); - } - } - - // open output file - FILE *log_file = fopen("/requests/log.txt", "w+"); - if (log_file == NULL) { - perror("Failed to open file to get auth server\n"); - return -1; - } - // write preamble - fprintf(log_file, "\n"); - fprintf(log_file, "\n"); - fprintf(log_file, "\n"); - fprintf(log_file, " \n"); - fprintf(log_file, " Logs\n"); - fprintf(log_file, "\n"); - fprintf(log_file, "\n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - - // write log information - for(log_node* current = log_root; current != NULL; current = current->next){ - fprintf(log_file, " \n"); - fprintf(log_file, " \n", current->timestamp); - fprintf(log_file, " \n", current->server_id); - fprintf(log_file, " \n", current->type); - fprintf(log_file, " \n", current->details); - fprintf(log_file, " \n"); - } - - // write ending - fprintf(log_file, "
TimestampServer IDEvent TypeDetails
%s%s%s%s
\n"); - fprintf(log_file, "\n"); - - return -1; -} diff --git a/functions/example_app_hybrid/app_hybrid.c b/functions/example_app_hybrid/app_hybrid.c deleted file mode 100644 index 69279bc..0000000 --- a/functions/example_app_hybrid/app_hybrid.c +++ /dev/null @@ -1,399 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "unistd.h" - -#include "syscall.h" - -#define BUFFER_SIZE 4096 -#define SERVER_NUMBER 10 -const uint16_t AUTH_SERVER_PORT = 8000; - -// Structure to store log event details -typedef struct log_node { - struct log_node *next; - char timestamp[100]; - char server_id[100]; - char type[100]; - char details[100]; -} log_node; - -char event_template[] = -"%*[^{]{" -"\"details\":\"%100[^\"]\"," -"\"event_type\":\"%100[^\"]\"," -"\"server_id\":\"%100[^\"]\"," -"\"timestamp\":\"%100[^\"]\"" -"%}"; - - -uint16_t my_htons(uint16_t port) { - return ((port >> 8) & 0x00FF) | ((port << 8) & 0xFF00); -} - -int my_inet_pton(int af, const char *src, void *dst) { - if (af == LINUX_AF_INET) { - uint8_t array[4] = {}; - for (int i = 0; i < 4; i++) { - char *end; - long value = strtol(src, &end, 10); - if (value < 0 || value > 255) { - return 0; - } - - if (*end != '\0' && *end != '.' && i < 3) { - return 0; - } - src = end + 1; - array[i] = (uint8_t)value; - } - - struct in_addr *addr = (struct in_addr *)dst; - memcpy(&addr->s_addr, array, 4); // Copy the 4 bytes into s_addr - - return 1; // Success - } - return -1; // Unsupported address family -} - -int connect_to_server(const char *ip, uint16_t port) { - struct sockaddr_in server_addr; - - int sockfd = linux_socket(LINUX_AF_INET, LINUX_SOCK_STREAM, 0); - if (sockfd < 0) { - perror("Socket creation failed"); - return -1; - } - - server_addr.sin_family = LINUX_AF_INET; - server_addr.sin_port = my_htons(port); - - if (my_inet_pton(LINUX_AF_INET, ip, &server_addr.sin_addr) != 1) { - perror("Invalid IP address"); - close(sockfd); - return -1; - } - - int err = linux_connect(sockfd, &server_addr, sizeof(server_addr)); - if (err < 0) { - printf("Error: %s\n", strerror(-err)); - printf("size of server_addr: %ld\n", sizeof(server_addr)); - perror("Socket connection failed"); - close(sockfd); - return -1; - } - - return sockfd; -} - - -int send_http_request(int sockfd, const char *request, size_t request_size, char *response, size_t response_size) { - if (linux_send(sockfd, request, request_size, 0) < 0) { - perror("Send failed"); - return -1; - } - - ssize_t total_received = 0, bytes; - // while ((bytes = linux_recv(sockfd, response + total_received, response_size - total_received - 1, 0)) > 0) { - // total_received += bytes; - // if (total_received >= response_size - 1) break; - // } - // blocks and waits forever if there is no next packate, could validate HTTP sice on the packages to see if we need more, this works for now - total_received = linux_recv(sockfd, response + total_received, response_size - total_received - 1, 0); - if(total_received < 0){ - return -1; - } - response[total_received] = '\0'; - return 0; -} - - -void parse_auth_response(const char *response, char *token) { - const char *start = strstr(response, "\"token\":"); - if (start) { - sscanf(start, "\"token\": \"%[^\"]", token); - } -} - -void parse_log_events(const char *response, log_node **log_root) { - log_node *current = NULL; - - const char *start = strstr(response, "\"events\":"); - if (start) { - start += 9; - while (1) { - current = malloc(sizeof(log_node)); - int found = sscanf(start, - event_template, - current->details, - current->type, - current->server_id, - current->timestamp); - if (found != 4) { - free(current); - break; - } - - current->next = *log_root; - *log_root = current; - - start = strstr(start, "},{"); - if (!start) break; - start += 1; - } - } -} - -// Render logs to an HTML file -void render_logs_to_html(log_node *log_root) { - FILE *log_file = fopen("/requests/log.html", "w+"); - if (!log_file) { - perror("Failed to open HTML file"); - return; - } - - // sort by date - // get number - size_t num_entries = 0; - log_node* current = log_root; - while(current){ - num_entries++; - current = current->next; - } - log_node** log_heap = malloc(sizeof(log_node*) * num_entries); - current = log_root; - for(size_t index = 0; index < num_entries; index++){ - log_heap[index] = current; - current = current->next; - for(size_t insert = index; insert > 0; insert = insert / 2){ - if(strcmp(log_heap[insert/2]->timestamp, log_heap[insert]->timestamp) <= 0){ - break; - } else { - log_node* tmp = log_heap[insert]; - log_heap[insert] = log_heap[insert/2]; - log_heap[insert/2] = tmp; - } - } - } - - // write preamble - fprintf(log_file, "\n"); - fprintf(log_file, "\n"); - fprintf(log_file, "\n"); - fprintf(log_file, " \n"); - fprintf(log_file, " Logs\n"); - fprintf(log_file, "\n"); - fprintf(log_file, "\n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - fprintf(log_file, " \n"); - - // write log information - for(; num_entries > 0; num_entries--){ - current = log_heap[0]; - fprintf(log_file, " \n"); - fprintf(log_file, " \n", current->timestamp); - fprintf(log_file, " \n", current->server_id); - fprintf(log_file, " \n", current->type); - fprintf(log_file, " \n", current->details); - fprintf(log_file, " \n"); - // remove current root from heap - size_t index = 0; - log_heap[0] = log_heap[num_entries-1]; - // trade down until no more children - while(index*2 + 1 < num_entries){ - // compare with first child - int is_bigger = strcmp(log_heap[index]->timestamp, log_heap[index*2+1]->timestamp); - // check if second child exists - if(index*2 + 2 >= num_entries){ - // second child does not exist, if is bigger than child swap - if(is_bigger > 0){ - log_node* tmp = log_heap[index]; - log_heap[index] = log_heap[index*2+1]; - log_heap[index*2+1] = tmp; - } - break; - } - // know fist child is smaller, if second is smaller than first swap with second otherwise with first - if(is_bigger > 0){ - size_t swap_index; - if(strcmp(log_heap[index*2+1]->timestamp, log_heap[index*2+2]->timestamp) <= 0){ - // first is smaller - swap_index = index*2+1; - } else { - swap_index = index*2+2; - } - log_node* tmp = log_heap[index]; - log_heap[index] = log_heap[swap_index]; - log_heap[swap_index] = tmp; - index = swap_index; - } else { - // first is not smaller so need to check second if swap is neccessary - if(strcmp(log_heap[index]->timestamp, log_heap[index*2+2]->timestamp) > 0){ - // is bigger so need to swap - log_node* tmp = log_heap[index]; - log_heap[index] = log_heap[index*2+2]; - log_heap[index*2+2] = tmp; - index = index*2+2; - } else { - break; - } - } - } - } - - // write ending - fprintf(log_file, "
TimestampServer IDEvent TypeDetails
%s%s%s%s
\n"); - fprintf(log_file, "\n"); - - fclose(log_file); -} - -// Main function to handle authorization, log fetching, and rendering -int main() { - // read authentification server ip from file - FILE *auth_server = fopen("/servers/server.txt", "r"); - if (auth_server == NULL) { - perror("Failed to open file to get auth server"); - return -1; - } - char *auth_server_ip = NULL; - size_t server_line_len = 0; - size_t read_chars = __getline(&auth_server_ip, &server_line_len, auth_server); - if (read_chars < 0) { - perror("Fauled to read line from auth server file\n"); - return -1; - } - // strip possible endln from the getline - if(auth_server_ip[read_chars-1] == '\n'){ - auth_server_ip[read_chars-1] = '\0'; - } - - // read token from incomming file - FILE *token_file = fopen("/responses/Authorization", "r"); - if (token_file == NULL) { - perror("Failed to open file with token"); - return -1; - } - char auth_token[257] = {0}; - if (fscanf(token_file, "Bearer %256s", auth_token) < 0) { - perror("Failed to parse line from token file"); - return -1; - } - - // handle - int auth_sock = connect_to_server(auth_server_ip, AUTH_SERVER_PORT); - if (auth_sock < 0) return 1; - - char auth_request[BUFFER_SIZE]; - int written = snprintf(auth_request, sizeof(auth_request), - "POST /authorize HTTP/1.1\n" - "Host: %s\n" - "Content-Type: application/json\n" - "Content-Length: %zu\n\n" - "{\"token\": \"%s\"}", auth_server_ip, strlen(auth_token) + 13, auth_token); - if(written < 0 || written > BUFFER_SIZE){ - perror("Failed to format auth request"); - return -1; - } - char auth_response[BUFFER_SIZE]; - if (send_http_request(auth_sock, auth_request, written, auth_response, BUFFER_SIZE) < 0) { - close(auth_sock); - return 1; - } - - close(auth_sock); - - char token[256]; - parse_auth_response(auth_response, token); - - // fan_out - log_node *log_root = NULL; - char log_request[SERVER_NUMBER][BUFFER_SIZE]; - int request_length[SERVER_NUMBER]; - char log_response[SERVER_NUMBER][BUFFER_SIZE]; - size_t response_read[SERVER_NUMBER]; - struct pollfd poll_fds[SERVER_NUMBER]; - - int log_sockets[SERVER_NUMBER]; - // open sockets - for(int server = 0; server < SERVER_NUMBER; server++){ - log_sockets[server] = connect_to_server(auth_server_ip, AUTH_SERVER_PORT); - if(log_sockets[server] < 0){ - perror("Failed to open log socket"); - return -1; - } - } - // format requests - for (int server = 0; server < SERVER_NUMBER; server++) { - request_length[server] = snprintf(log_request[server], sizeof(log_request[server]), - "GET /logs/%d HTTP/1.1\n" - "Host: %s\n" - "Authorization: Bearer %s\n\n", server, auth_server_ip, token); - if(request_length < 0){ - perror("Could not format server log request"); - return -1; - } - } - // issue all requests - for (int server = 0; server < SERVER_NUMBER; server++){ - if (linux_send(log_sockets[server], log_request[server], request_length[server], 0) < 0) { - perror("Send failed"); - return -1; - } - response_read[server] = 0; - poll_fds[server].fd = log_sockets[server]; - poll_fds[server].events = POLLIN; - poll_fds[server].revents = 0; - } - unsigned int received_responses = 0; - while(received_responses < SERVER_NUMBER){ - int poll_result = linux_ppoll(poll_fds, SERVER_NUMBER); - if(poll_result < 0) { - perror("Polling failed"); - return -1; - } - // check all the file descriptors for arrived data - for(int server = 0; server < SERVER_NUMBER; server++){ - if(poll_fds[server].fd > 0 && (poll_fds[server].revents & (POLLIN | POLLHUP))){ - received_responses++; - // disable further polling on this socket - poll_fds[server].fd = -1; - int new_data = linux_recv(log_sockets[server], log_response[server] + response_read[server], BUFFER_SIZE - response_read[server] - 1, 0); - if(new_data < 0) { - perror("Failed to read from socket"); - return -1; - } - response_read[server] += new_data; - log_response[server][new_data] = '\0'; - parse_log_events(log_response[server], &log_root); - } - } - } - // close sockets - for(int server = 0; server < SERVER_NUMBER; server++){ - close(log_sockets[server]); - } - - // render - render_logs_to_html(log_root); - - // Free log nodes - log_node *current = log_root; - while (current) { - log_node *next = current->next; - free(current); - current = next; - } - - return 0; -} \ No newline at end of file diff --git a/rust_functions/Cargo.toml b/rust_functions/Cargo.toml deleted file mode 100644 index 4aedf38..0000000 --- a/rust_functions/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "rust_fass_functions" -version = "0.1.0" -authors = ["mgiardino"] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -ndarray = { version = "0.15.0", features = ["blas"] } -blas-src = { version = "0.8", features = ["openblas"] } -openblas-src = { version = "0.10", features = ["cblas", "system"] } - -ndarray-linalg = "0.16.0" -ndarray-rand = { version = "0.14.0" } -aes = "0.8.2" -generic-array = "0.14.6" diff --git a/rust_functions/src/aes_func.rs b/rust_functions/src/aes_func.rs deleted file mode 100644 index 1b80693..0000000 --- a/rust_functions/src/aes_func.rs +++ /dev/null @@ -1,27 +0,0 @@ -use aes::Aes128; -use aes::cipher::{ - BlockCipher, BlockEncrypt, BlockDecrypt, KeyInit, -}; -use generic_array::GenericArray; -use generic_array::ArrayLength; - -pub fn aes_encrypt(key: GenericArray>, - block: GenericArray>) - -> GenericArray> { - let cipher = Aes128::new(&key); - - cipher.encrypt_block(&mut block); - - block -} - -pub fn aes_decrypt(key: GenericArray>, - block: GenericArray>) - -> GenericArray> { - let cipher = Aes128::new(&key); - - cipher.decrypt_block(&mut block); - - block -} - diff --git a/rust_functions/src/float_operation.rs b/rust_functions/src/float_operation.rs deleted file mode 100644 index 0dc8ec6..0000000 --- a/rust_functions/src/float_operation.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub fn float_operations(n: i64) { - for i in 0..n { - let i: f64 = i as f64; - let sin_i = i.sin(); - let cos_i = i.cos(); - let sqrt_i = i.sqrt(); - - //println!("{n}: {sin_i} {cos_i} {sqrt_i}"); - println!("{0}: {1} {2} {3}", i, sin_i, cos_i, sqrt_i); - } -} - - diff --git a/rust_functions/src/linpack.rs b/rust_functions/src/linpack.rs deleted file mode 100644 index 0dc6713..0000000 --- a/rust_functions/src/linpack.rs +++ /dev/null @@ -1,7 +0,0 @@ -use ndarray::prelude::*; -use ndarray_linalg::Solve; - -pub fn linpack(a: Array2, b: Array1) -> Array1 { - let c = a.solve_into(b).unwrap(); - c -} diff --git a/rust_functions/src/main.rs b/rust_functions/src/main.rs deleted file mode 100644 index 6f20e30..0000000 --- a/rust_functions/src/main.rs +++ /dev/null @@ -1,47 +0,0 @@ -mod float_operation; -use float_operation::float_operations; -use ndarray::{Array1, Array2}; - -mod matmul; -use matmul::gen_matrix; -use matmul::gen_vector; -use matmul::matmul; - -mod linpack; -use linpack::linpack; - -mod aes_func; -use aes_func::aes_decrypt; -use aes_func::aes_encrypt; -use generic_array::GenericArray; - -extern crate blas_src; - -fn main() { - let i = 5; - println!("Running float operations"); - float_operations(i); - println!("Running matmul"); - let n: usize = 128; - let a = gen_matrix(n, n); - let b = gen_matrix(n, n); - let mut c = Array2::::zeros((n,n)); - c = matmul(a, b, c); - println!("{:?}", c); - - println!("Running linpack"); - let a = gen_matrix(n, n); - let b: Array1 = gen_vector(n); -// let b = arr1(b.slice(s![0..1, ..])); - let c = linpack(a, b); - println!("{:?}", c); -// AES doesn't work yet :( -// println!("Running AES"); -// let key = GenericArray::from([0u8; 16]); -// let mut block = GenericArray::from([42u8; 16]); -// block = aes_encrypt(key, &block); -// println!("{:?}", block); -// block = aes_decrypt(key, &block); -// println!("{:?}", block); -} - diff --git a/rust_functions/src/matmul.rs b/rust_functions/src/matmul.rs deleted file mode 100644 index 2651e03..0000000 --- a/rust_functions/src/matmul.rs +++ /dev/null @@ -1,34 +0,0 @@ -use ndarray::prelude::*; -use ndarray::{Array}; -use ndarray_rand::RandomExt; -use ndarray_rand::rand_distr::Uniform; - -pub fn matmul(a: Array2, b: Array2, mut c: Array2) -> Array2 { - let (n_a, m_a) = a.dim(); - let (n_b, m_b) = b.dim(); - let (n_c, m_c) = c.dim(); - if m_a != n_b { panic!{"matrix mismatch! m_a != n_b"}; } - if m_c != m_b { panic!{"matrix mismatch! m_c != m_b"}; } - if n_c != n_a { panic!{"matrix mismatch! n_c != n_a"}; } - - c = a.dot(&b); - c -} - -fn print_type_of(_: &T) { - println!("{}", std::any::type_name::()) -} - - -pub fn gen_matrix(n: usize, m: usize) -> Array> { - let a = Array::random((n, m), Uniform::new(-0.5, 0.5)); - print_type_of(&a); - // let a = Array::::zeros((n, n).f()); - a -} - -pub fn gen_vector(n: usize) -> Array1 { - let a = Array::random(n, Uniform::new(-0.5, 0.5)); - print_type_of(&a); - a -}