aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--CMakeLists.txt11
-rw-r--r--LICENSE30
-rw-r--r--README.md3
-rw-r--r--ext_internal.c46
-rw-r--r--ext_internal.h13
-rw-r--r--extensions/CMakeLists.txt4
-rw-r--r--extensions/ext.c10
-rw-r--r--extensions/ext.h18
-rw-r--r--extensions/ext_hello.c14
-rw-r--r--main.c366
11 files changed, 521 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5135a76
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.idea
+cmake-build-*
+build-*
+*.so
+*.dylib
+*.orig
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..54a43f5
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.28)
+project(asdfapi C)
+
+set(CMAKE_C_STANDARD 99)
+
+link_libraries(dl)
+add_subdirectory(extensions)
+add_executable(asdfapi main.c
+ ext_internal.c
+ ext_internal.h
+)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3ac99da
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+BSD 3-Clause License
+
+Copyright (c) 2023-2024, Joseph Hunkeler,
+Association of Universities for Research in Astronomy (AURA)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..34e299d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# ASDFAPI
+
+
diff --git a/ext_internal.c b/ext_internal.c
new file mode 100644
index 0000000..3199eb7
--- /dev/null
+++ b/ext_internal.c
@@ -0,0 +1,46 @@
+//
+// Created by jhunk on 7/2/24.
+//
+#include "ext_internal.h"
+#include "extensions/ext.h"
+
+void asdfapi_ext_show_descriptor(struct ASDFExtension *info) {
+ printf("Extension name: %s\n", info->name);
+ printf("Extension description: %s\n", info->desc);
+}
+
+void *asdfapi_ext_load(struct ASDFExtension **info, const char *filename) {
+ fnptr_ext_init ext_init;
+
+ printf("Loading extension: %s\n", filename);
+ void *handle_ext = dlopen(filename, RTLD_LAZY);
+ if (handle_ext) {
+ dlerror();
+ ext_init = dlsym(handle_ext, "asdfapi_ext_init");
+ if (ext_init) {
+ (*ext_init)();
+ dlerror();
+ *info = dlsym(handle_ext, "asdfapi_ext_descriptor");
+ if (!*info) {
+ fprintf(stderr, "error reading extension descriptor: %s\n", dlerror());
+ goto fail;
+ }
+ } else {
+ fprintf(stderr, "error reading extension function: %s\n", dlerror());
+ goto fail;
+ }
+ } else {
+ fprintf(stderr, "error opening extension: %s\n", dlerror());
+ goto fail;
+ }
+ return handle_ext;
+ fail:
+ return NULL;
+}
+
+void *asdfapi_ext_call(void *handle, const char *sym) {
+ const char *prefix = "asdfapi_ext_";
+ char name[255] = {0};
+ snprintf(name, sizeof(name) - 1, "%s%s", prefix, sym);
+ return dlsym(handle, name);
+}
diff --git a/ext_internal.h b/ext_internal.h
new file mode 100644
index 0000000..a8c7393
--- /dev/null
+++ b/ext_internal.h
@@ -0,0 +1,13 @@
+#ifndef ASDFAPI_EXT_INTERNAL_H
+#define ASDFAPI_EXT_INTERNAL_H
+
+#include <dlfcn.h>
+#include "extensions/ext.h"
+
+typedef int (*fnptr_ext_init)();
+
+void *asdfapi_ext_load(struct ASDFExtension **info, const char *filename);
+void *asdfapi_ext_call(void *handle, const char *sym);
+void asdfapi_ext_show_descriptor(struct ASDFExtension *info);
+#endif //ASDFAPI_EXT_INTERNAL_H
+
diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt
new file mode 100644
index 0000000..ed1a5e6
--- /dev/null
+++ b/extensions/CMakeLists.txt
@@ -0,0 +1,4 @@
+project(asdfapi_ext)
+add_library(asdfapi_ext SHARED ext.c ext.h)
+add_library(asdfapi_ext_hello SHARED ext_hello.c)
+target_link_libraries(asdfapi_ext_hello asdfapi_ext) \ No newline at end of file
diff --git a/extensions/ext.c b/extensions/ext.c
new file mode 100644
index 0000000..c46c9f9
--- /dev/null
+++ b/extensions/ext.c
@@ -0,0 +1,10 @@
+//
+// Created by jhunk on 7/2/24.
+//
+#include "ext.h"
+
+int asdfapi_ext_new(struct ASDFExtension *ext, const char *name, const char *desc) {
+ ext->name = name;
+ ext->desc = desc;
+ return 0;
+}
diff --git a/extensions/ext.h b/extensions/ext.h
new file mode 100644
index 0000000..8a52b90
--- /dev/null
+++ b/extensions/ext.h
@@ -0,0 +1,18 @@
+//
+// Created by jhunk on 7/2/24.
+//
+
+#ifndef ASDFAPI_EXT_H
+#define ASDFAPI_EXT_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct ASDFExtension {
+ const char *name; // Name of extension
+ const char *desc; // Description of extension
+};
+
+int asdfapi_ext_new(struct ASDFExtension *ext, const char *name, const char *desc);
+#endif //ASDFAPI_EXT_H
diff --git a/extensions/ext_hello.c b/extensions/ext_hello.c
new file mode 100644
index 0000000..d071c4f
--- /dev/null
+++ b/extensions/ext_hello.c
@@ -0,0 +1,14 @@
+#include "ext.h"
+
+struct ASDFExtension asdfapi_ext_descriptor;
+
+int asdfapi_ext_say_hello(const char *msg) {
+ printf("HELLO FROM %s:%s!\nMessage: %s\n", __FILE_NAME__, __FUNCTION__, msg);
+ return 0;
+}
+
+int asdfapi_ext_init() {
+ fprintf(stdout, "Initializing %s:%s\n", __FILE_NAME__ ,__FUNCTION__);
+ asdfapi_ext_new(&asdfapi_ext_descriptor, "Hello", "An extension that prints a hello message");
+ return 0;
+} \ No newline at end of file
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..52e7615
--- /dev/null
+++ b/main.c
@@ -0,0 +1,366 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <byteswap.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include "ext_internal.h"
+
+#define ASDF_MAGIC_TOKEN "#ASDF "
+#define ASDF_MAGIC_BLOCK_DATA_TOKEN "\323BLK"
+#define ASDF_MAGIC_BLOCK_INDEX_TOKEN "#ASDF BLOCK INDEX\n"
+#define ASDF_START_OF_DOCUMENT_TOKEN "---\n"
+#define ASDF_END_OF_DOCUMENT_TOKEN "...\n"
+
+enum {
+ ASDF_E_SUCCESS=0,
+ ASDF_E_READ,
+ ASDF_E_WRITE,
+ ASDF_E_INVALID,
+ ASDF_E_MEMORY,
+ ASDF_E_UNKNOWN,
+};
+
+enum {
+ ASDF_FAILED=-1,
+ ASDF_SUCCESS=0,
+ ASDF_STREAM,
+};
+
+struct ASDFVersion {
+ unsigned major;
+ unsigned minor;
+ unsigned patch;
+ char *suffix;
+ char *raw;
+};
+
+struct ASDFBlock {
+ int mfd; // memmap file descriptor
+ const char *mem_region; // Mapped region (for munmap)
+ char *mem; // Mapped address of size ASDFBlockHeader.used_size
+ off_t offset; // Data offset
+ size_t size; // future
+ size_t nelem; // future
+};
+
+struct ASDFBlockHeader {
+ unsigned short size;
+ unsigned int flags;
+ unsigned long allocated_size;
+ unsigned long used_size;
+ unsigned long data_size;
+ char magic_token[4];
+ char compression[4];
+ char checksum[16];
+ struct ASDFBlock data;
+};
+
+struct ASDFHandle {
+ FILE *fp; // File handle
+ const char *fp_origin; // Path to file
+ unsigned last_error; // Last ASDF error recorded (default: ASDF_E_SUCCESS)
+ off_t pos; // Current position in file
+ off_t block_index_offset; // Block header position in file
+ size_t *block_offsets; // Array of block offsets in file
+ unsigned block_stream; // Streaming detected in file
+ struct ASDFVersion version; // ASDF file version
+};
+
+size_t *asdfapi_get_block_index_array(struct ASDFHandle *handle) {
+ off_t pos_prev = ftell(handle->fp);
+ size_t size_default = 1024;
+ size_t *block_list_temp = calloc(size_default, sizeof(*block_list_temp));
+ if (!block_list_temp) {
+ handle->last_error = ASDF_E_MEMORY;
+ return NULL;
+ }
+
+ fseek(handle->fp, handle->block_index_offset, SEEK_SET);
+ if (handle->block_stream) {
+ block_list_temp[0] = handle->block_index_offset;
+ fseek(handle->fp, pos_prev, SEEK_SET);
+ return block_list_temp;
+ }
+
+ char buf[100] = {0};
+ size_t i = 0;
+ while (fgets(buf, sizeof(buf) - 1, handle->fp) != NULL) {
+ if (!strncmp(buf, "%YAML", 5) || !strncmp(buf, ASDF_START_OF_DOCUMENT_TOKEN, strlen(ASDF_START_OF_DOCUMENT_TOKEN))) {
+ continue;
+ } else if (!strncmp(buf, ASDF_END_OF_DOCUMENT_TOKEN, strlen(ASDF_END_OF_DOCUMENT_TOKEN))) {
+ break;
+ }
+ char *data = strstr(buf, "- ");
+ if (data) {
+ data += 2;
+ block_list_temp[i] = strtoul(data, NULL, 10);
+ i++;
+ }
+ }
+
+ fseek(handle->fp, pos_prev, SEEK_SET);
+ return block_list_temp;
+}
+
+size_t asdfapi_poll_block_index_offset(struct ASDFHandle *handle) {
+ fseek(handle->fp, 0, SEEK_END);
+ off_t cur = ftell(handle->fp);
+ const size_t magic_size = strlen(ASDF_MAGIC_BLOCK_INDEX_TOKEN);
+ char *buf = calloc(magic_size + 1, sizeof(*buf));
+
+ fseek(handle->fp, (ssize_t) -strlen(ASDF_END_OF_DOCUMENT_TOKEN), SEEK_CUR);
+ fread(buf, sizeof(*buf), strlen(ASDF_END_OF_DOCUMENT_TOKEN), handle->fp);
+ if (strncmp(buf, ASDF_END_OF_DOCUMENT_TOKEN, strlen(ASDF_END_OF_DOCUMENT_TOKEN)) != 0) {
+ fprintf(stderr, "STREAM DETECTED\n");
+ handle->block_stream = 1;
+ cur = ftell(handle->fp);
+ while (cur != 0) {
+ if (fread(buf, sizeof(*buf), 4, handle->fp) != 4 && ferror(handle->fp)) {
+ fprintf(stderr, "read failure\n");
+ return -1;
+ } else {
+ if (!memcmp(buf, ASDF_MAGIC_BLOCK_DATA_TOKEN, 4)) {
+ fseek(handle->fp, -4, SEEK_CUR);
+ cur = ftell(handle->fp);
+ free(buf);
+ return cur;
+ }
+ }
+ fseek(handle->fp, cur, SEEK_SET);
+ cur--;
+ }
+ }
+
+ while (cur != 0) {
+ if (fread(buf, sizeof(*buf), magic_size, handle->fp) != magic_size && ferror(handle->fp)) {
+ fprintf(stderr, "read failure\n");
+ return -1;
+ }
+ if (!strncmp(buf, ASDF_MAGIC_BLOCK_INDEX_TOKEN, magic_size)) {
+ cur = ftell(handle->fp);
+ break;
+ }
+ fseek(handle->fp, cur, SEEK_SET);
+ cur--;
+ }
+ fseek(handle->fp, handle->pos, SEEK_SET);
+ handle->pos = ftell(handle->fp);
+ free(buf);
+ return cur;
+}
+
+int asdfapi_poll_version(struct ASDFHandle *handle) {
+ char buf[100] = {0};
+ if (fgets(buf, sizeof(buf) - 1, handle->fp)) {
+ handle->pos = ftell(handle->fp);
+ if (!strncmp(buf, ASDF_MAGIC_TOKEN, strlen(ASDF_MAGIC_TOKEN))) {
+ char *version_p = strstr(buf, ASDF_MAGIC_TOKEN);
+ if (version_p) {
+ version_p += strlen(ASDF_MAGIC_TOKEN);
+ handle->version.raw = strndup(version_p, strlen(version_p) - 1);
+ char *errptr = NULL;
+ handle->version.major = strtoul(handle->version.raw, &errptr, 10);
+ if (errptr) {
+ handle->version.minor = strtoul(errptr + 1, &errptr, 10);
+ }
+ if (errptr) {
+ handle->version.patch = strtoul(errptr + 1, &errptr, 10);
+ }
+ if (errptr && strlen(errptr) && *errptr != '.') {
+ handle->version.suffix = strdup(errptr);
+ }
+ }
+ } else {
+ handle->last_error = ASDF_E_INVALID;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void asdfapi_close(struct ASDFHandle **handle) {
+ if ((*handle)->version.raw) {
+ free((char *) (*handle)->version.raw);
+ if ((*handle)->version.suffix) {
+ free((*handle)->version.suffix);
+ }
+ }
+ if ((*handle)->block_offsets) {
+ free((*handle)->block_offsets);
+ }
+ if ((*handle)->fp) {
+ fclose((*handle)->fp);
+ }
+ free((*handle));
+}
+
+
+struct ASDFHandle *asdfapi_open(struct ASDFHandle **handle, const char *filename) {
+ *handle = calloc(1, sizeof(**handle));
+ if (!*handle) {
+ return NULL;
+ }
+
+ (*handle)->fp = fopen(filename, "rb+");
+ if (!(*handle)->fp) {
+ return NULL;
+ }
+
+ (*handle)->fp_origin = filename;
+ if (asdfapi_poll_version(*handle)) {
+ fprintf(stderr, "failed to determine ASDF file version\n");
+ return NULL;
+ }
+
+ (*handle)->block_index_offset = (off_t) asdfapi_poll_block_index_offset(*handle);
+ (*handle)->block_offsets = asdfapi_get_block_index_array(*handle);
+ if (!(*handle)->block_offsets) {
+ perror("wtf?");
+ return NULL;
+ }
+ for (size_t i = 0; (*handle)->block_offsets[i] != 0; i++) {
+ printf("block[%zu] @ %zu\n", i, (*handle)->block_offsets[i]);
+ }
+ return *handle;
+}
+
+void asdfapi_free_block_header(struct ASDFBlockHeader **hdr) {
+ if ((*hdr)->data.mem_region) {
+ munmap((char *) (*hdr)->data.mem_region, (*hdr)->used_size);
+ (*hdr)->data.mem_region = NULL;
+ (*hdr)->data.mem = NULL;
+ }
+ memset((*hdr), 0, sizeof(*(*hdr)));
+ free((*hdr));
+}
+
+struct ASDFBlockHeader *asdfapi_read_block_header(struct ASDFHandle *handle, size_t block) {
+ struct ASDFBlockHeader *result = calloc(1, sizeof(*result));
+ fseek(handle->fp, (off_t) handle->block_offsets[block], SEEK_SET);
+ fread(result->magic_token, sizeof(result->magic_token), sizeof(*result->magic_token), handle->fp);
+ fread(&result->size, sizeof(result->size), 1, handle->fp);
+ result->size = bswap_16(result->size);
+ fread(&result->flags, sizeof(result->flags), 1, handle->fp);
+ result->flags = bswap_32(result->flags);
+ fread(result->compression, sizeof(result->compression), sizeof(*result->compression), handle->fp);
+ fread(&result->allocated_size, sizeof(result->allocated_size), 1, handle->fp);
+ result->allocated_size = bswap_64(result->allocated_size);
+ fread(&result->used_size, sizeof(result->used_size), 1, handle->fp);
+ result->used_size = bswap_64(result->used_size);
+ fread(&result->data_size, sizeof(result->data_size), 1, handle->fp);
+ result->data_size = bswap_64(result->data_size);
+ fread(result->checksum, sizeof(result->checksum), 1, handle->fp);
+ result->data.offset = ftell(handle->fp);
+ result->data.mfd = fileno(handle->fp);
+ off_t pa_offset = result->data.offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
+ size_t map_size = result->used_size;
+ if (result->flags & ASDF_STREAM) {
+ map_size = result->size;
+ }
+ result->data.mem_region = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, result->data.mfd, pa_offset);
+ if (result->data.mem_region == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+ result->data.mem = (char *) &result->data.mem_region[result->data.offset];
+
+ return result;
+}
+
+void asdfapi_show_block_header(struct ASDFBlockHeader *hdr) {
+ printf("ASDF HEADER BLOCK\n");
+ printf("Raw size: %u\n", hdr->size);
+ printf("Allocated size: %zu\n", hdr->allocated_size);
+ printf("Used size: %zu\n", hdr->used_size);
+ printf("Data size: %zu\n", hdr->data_size);
+ printf("Data checksum: ");
+ // Quick check: first, middle, and last byte of the MD5 hash are non-zero
+ if (hdr->checksum[0] && hdr->checksum[7] && hdr->checksum[15]) {
+ // read checksum
+ for (size_t i = 0; i < sizeof(hdr->checksum); i++) {
+ unsigned char b = (unsigned char) hdr->checksum[i];
+ printf("%02X", b);
+ }
+ } else {
+ printf("None");
+ }
+ printf("\n");
+
+ printf("Compressed: ");
+ if (*hdr->compression) {
+ char compression[sizeof(hdr->compression) + 1] = {0};
+ memcpy(compression, hdr->compression, sizeof(hdr->compression));
+ printf("Yes (%s)", compression);
+ } else {
+ printf("No");
+ }
+ printf("\n");
+
+ printf("Flags: ");
+ if (hdr->flags) {
+ unsigned tmp = hdr->flags;
+ for (size_t i = 0; i < sizeof(hdr->flags) * 8; i++) {
+ unsigned have_flag = tmp & 1;
+ if (have_flag) {
+ switch (i) {
+ case 0:
+ printf("STREAM ");
+ break;
+ default:
+ printf("UNKNOWN(bit %zu) ", i + 1);
+ break;
+ }
+ }
+ tmp = tmp >> 1;
+ }
+ } else {
+ printf("None");
+ }
+ printf("\n");
+}
+
+int main(int argc, char *argv[]) {
+ const char *filename = "../ascii.asdf";
+ printf("Opening %s\n", filename);
+ struct ASDFHandle *handle = NULL;
+ asdfapi_open(&handle, filename);
+ if (!handle) {
+ perror(filename);
+ exit(1);
+ }
+
+ printf("ASDF file version: %s\n", handle->version.raw);
+
+ if (handle->block_offsets) {
+ for (size_t i = 0; handle->block_offsets[i] != 0; i++) {
+ struct ASDFBlockHeader *block_hdr = asdfapi_read_block_header(handle, i);
+ asdfapi_show_block_header(block_hdr);
+ puts("");
+ for (size_t b = 0; b < block_hdr->used_size / sizeof(char); b++) {
+ printf("%c ", (char) block_hdr->data.mem[b]);
+ }
+ puts("\n");
+ asdfapi_free_block_header(&block_hdr);
+ }
+ }
+ asdfapi_close(&handle);
+
+ // Extension framework test
+ const char *libfilename = "extensions/libasdfapi_ext_hello.so";
+ struct ASDFExtension *ext_info;
+
+ void *ext = asdfapi_ext_load(&ext_info, libfilename);
+ asdfapi_ext_show_descriptor(ext_info);
+
+ typedef int (*fn_sig_say_hello)(const char *);
+ fn_sig_say_hello say_hello = asdfapi_ext_call(ext, "say_hello");
+ if (say_hello) {
+ say_hello("BEEP BEEP");
+ } else {
+ fprintf(stderr, "%s", dlerror());
+ exit(1);
+ }
+ dlclose(ext);
+}