Browse Source

Implement vectored I/O.

coderain 5 months ago
parent
commit
126dd9162c
4 changed files with 396 additions and 0 deletions
  1. 1 0
      sdk/monolithium.h
  2. 167 0
      sdk/vector.h
  3. 2 0
      tests/unit_tests/src/main.c
  4. 226 0
      tests/unit_tests/src/vector_io.c

+ 1 - 0
sdk/monolithium.h

@@ -33,3 +33,4 @@
 #include "thread.h"
 #include "timer.h"
 #include "user.h"
+#include "vector.h"

+ 167 - 0
sdk/vector.h

@@ -0,0 +1,167 @@
+/*
+ * vector.h
+ *
+ * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MONOLITHIUM_VECTOR_H__
+#define __MONOLITHIUM_VECTOR_H__
+
+#include "defs.h"
+#include "list.h"
+
+#ifndef VECTOR_REALLOC
+extern void *realloc(void*, size_t);
+#define VECTOR_REALLOC(p, s) realloc((p), (s))
+#endif
+
+typedef struct
+{
+    void *address;
+    size_t length;
+} vector_buffer_t;
+
+typedef struct
+{
+    list_entry_t link;
+    vector_buffer_t buf;
+} vector_buffer_entry_t;
+
+typedef struct
+{
+    size_t position;
+    size_t size;
+    list_entry_t buffer_list;
+} vector_t;
+
+static bool_t vector_insert_buffer(vector_t *vector, size_t position, const vector_buffer_t *buffer)
+{
+    if (!buffer->length) return TRUE;
+    list_entry_t *ptr = &vector->buffer_list;
+
+    while (position)
+    {
+        list_entry_t *next = ptr->next;
+        if (next == &vector->buffer_list) return FALSE;
+
+        vector_buffer_entry_t *entry = CONTAINER_OF(next, vector_buffer_entry_t, link);
+        if (entry->buf.length > position)
+        {
+            vector_buffer_entry_t *remaining = VECTOR_REALLOC(NULL, sizeof(vector_buffer_entry_t));
+            if (!remaining) return FALSE;
+
+            remaining->buf.address = (void*)((uintptr_t)entry->buf.address + position);
+            remaining->buf.length = entry->buf.length - position;
+            entry->buf.length = position;
+
+            list_put_after(next, &remaining->link);
+        }
+
+        position -= entry->buf.length;
+        ptr = next;
+    }
+
+    vector_buffer_entry_t *entry = VECTOR_REALLOC(NULL, sizeof(vector_buffer_entry_t));
+    entry->buf = *buffer;
+    list_put_after(ptr, &entry->link);
+    vector->size += buffer->length;
+    return TRUE;
+}
+
+static inline void vector_clear(vector_t *vector)
+{
+    vector->position = vector->size = 0;
+
+    while (vector->buffer_list.next != &vector->buffer_list)
+    {
+        list_entry_t *ptr = vector->buffer_list.next;
+        list_remove(ptr);
+        ptr = VECTOR_REALLOC(ptr, 0);
+    }
+}
+
+static inline bool_t vector_init(vector_t *vector, vector_buffer_t *buffers, size_t num_buffers)
+{
+    vector->position = vector->size = 0;
+    list_init(&vector->buffer_list);
+
+    int i;
+    for (i = 0; i < num_buffers; i++)
+    {
+        if (!vector_insert_buffer(vector, vector->size, &buffers[i]))
+        {
+            vector_clear(vector);
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static inline void vector_read_gather(vector_t *vector, void *data, size_t size)
+{
+    size_t position = 0;
+    list_entry_t *ptr;
+
+    for (ptr = vector->buffer_list.next; ptr != &vector->buffer_list && position < vector->position + size; ptr = ptr->next)
+    {
+        vector_buffer_t *buffer = &CONTAINER_OF(ptr, vector_buffer_entry_t, link)->buf;
+        uintptr_t start = position > vector->position ? position : vector->position;
+        uintptr_t end = position + buffer->length < vector->position + size ? position + buffer->length : vector->position + size;
+
+        if (start < end)
+        {
+            __builtin_memcpy((void*)((uintptr_t)data + start - vector->position),
+                             (void*)((uintptr_t)buffer->address + start - position),
+                             end - start);
+        }
+
+        position += buffer->length;
+    }
+
+    vector->position += size;
+}
+
+static inline void vector_write_scatter(vector_t *vector, const void *data, size_t size)
+{
+    size_t position = 0;
+    list_entry_t *ptr;
+
+    for (ptr = vector->buffer_list.next; ptr != &vector->buffer_list && position < vector->position + size; ptr = ptr->next)
+    {
+        vector_buffer_t *buffer = &CONTAINER_OF(ptr, vector_buffer_entry_t, link)->buf;
+        uintptr_t start = position > vector->position ? position : vector->position;
+        uintptr_t end = position + buffer->length < vector->position + size ? position + buffer->length : vector->position + size;
+
+        if (start < end)
+        {
+            __builtin_memcpy((void*)((uintptr_t)buffer->address + start - position),
+                             (void*)((uintptr_t)data + start - vector->position),
+                             end - start);
+        }
+
+        position += buffer->length;
+    }
+
+    vector->position += size;
+}
+
+static inline void vector_seek(vector_t *vector, size_t position)
+{
+    vector->position = position < vector->size ? position : vector->size;
+}
+
+#endif

+ 2 - 0
tests/unit_tests/src/main.c

@@ -21,10 +21,12 @@
 #include <check.h>
 
 Suite *suite_avl_tree_create(void);
+Suite *suite_vector_io_create(void);
 
 int main(int argc, char *argv[])
 {
     SRunner *suite_runner = srunner_create(suite_avl_tree_create());
+    srunner_add_suite(suite_runner, suite_vector_io_create());
     srunner_run_all(suite_runner, CK_NORMAL);
     srunner_free(suite_runner);
     return 0;

+ 226 - 0
tests/unit_tests/src/vector_io.c

@@ -0,0 +1,226 @@
+/*
+ * Monolithium Unit Tests
+ * vector_io.c
+ *
+ * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <check.h>
+#include <stdlib.h>
+#include <sdk/vector.h>
+
+static void setup(void)
+{
+}
+
+static void teardown(void)
+{
+}
+
+START_TEST(test_vector_io_simple_scatter)
+{
+    const char text[] = "The destiny of this string is to break apart into pieces.";
+    const char correct_piece1[] = "The destiny";
+    const char correct_piece2[] = " of this string";
+    const char correct_piece3[] = " is to break apart into pieces.";
+    char piece1[sizeof(correct_piece1)] = "";
+    char piece2[sizeof(correct_piece2)] = "";
+    char piece3[sizeof(correct_piece3)] = "";
+
+    vector_buffer_t buffers[] = {
+        { piece1, sizeof(piece1) - 1 },
+        { piece2, sizeof(piece2) - 1 },
+        { piece3, sizeof(piece3) - 1 },
+    };
+
+    vector_t vector;
+    vector_init(&vector, buffers, sizeof(buffers) / sizeof(*buffers));
+    vector_write_scatter(&vector, text, sizeof(text) - 1);
+    vector_clear(&vector);
+
+    ck_assert_str_eq(piece1, correct_piece1);
+    ck_assert_str_eq(piece2, correct_piece2);
+    ck_assert_str_eq(piece3, correct_piece3);
+}
+END_TEST
+
+START_TEST(test_vector_io_simple_gather)
+{
+    char piece1[] = "And then ";
+    char piece2[] = "come back together,";
+    char piece3[] = " like nothing ever happened.";
+    const char correct_text[] = "And then come back together, like nothing ever happened.";
+    char text[sizeof(correct_text)] = "";
+
+    vector_buffer_t buffers[] = {
+        { piece1, sizeof(piece1) - 1 },
+        { piece2, sizeof(piece2) - 1 },
+        { piece3, sizeof(piece3) - 1 },
+    };
+
+    vector_t vector;
+    vector_init(&vector, buffers, sizeof(buffers) / sizeof(*buffers));
+    vector_read_gather(&vector, text, sizeof(text) - 1);
+    vector_clear(&vector);
+
+    ck_assert_str_eq(text, correct_text);
+}
+END_TEST
+
+START_TEST(test_vector_io_multi_write_scatter)
+{
+    const char text[] = "The destiny of this string is to break apart into pieces.";
+    const char correct_piece1[] = "The destiny";
+    const char correct_piece2[] = " of this string";
+    const char correct_piece3[] = " is to break apart into pieces.";
+    char piece1[sizeof(correct_piece1)] = "";
+    char piece2[sizeof(correct_piece2)] = "";
+    char piece3[sizeof(correct_piece3)] = "";
+
+    vector_buffer_t buffers[] = {
+        { piece1, sizeof(piece1) - 1 },
+        { piece2, sizeof(piece2) - 1 },
+        { piece3, sizeof(piece3) - 1 },
+    };
+
+    vector_t vector;
+    vector_init(&vector, buffers, sizeof(buffers) / sizeof(*buffers));
+    int i;
+    for (i = 0; i < sizeof(text) - 1; i++) vector_write_scatter(&vector, &text[i], 1);
+    vector_clear(&vector);
+
+    ck_assert_str_eq(piece1, correct_piece1);
+    ck_assert_str_eq(piece2, correct_piece2);
+    ck_assert_str_eq(piece3, correct_piece3);
+}
+END_TEST
+
+START_TEST(test_vector_io_multi_read_gather)
+{
+    char piece1[] = "And then ";
+    char piece2[] = "come back together,";
+    char piece3[] = " like nothing ever happened.";
+    const char correct_text[] = "And then come back together, like nothing ever happened.";
+    char text[sizeof(correct_text)] = "";
+
+    vector_buffer_t buffers[] = {
+        { piece1, sizeof(piece1) - 1 },
+        { piece2, sizeof(piece2) - 1 },
+        { piece3, sizeof(piece3) - 1 },
+    };
+
+    vector_t vector;
+    vector_init(&vector, buffers, sizeof(buffers) / sizeof(*buffers));
+    int i;
+    for (i = 0; i < sizeof(text) - 1; i++) vector_read_gather(&vector, &text[i], 1);
+    vector_clear(&vector);
+
+    ck_assert_str_eq(text, correct_text);
+}
+END_TEST
+
+START_TEST(test_vector_io_dynamic_scatter)
+{
+    const char text[] = "Scattering strings in an unnecessarily complicated way is fun.";
+    const char correct_piece1[] = "Scattering strings is fun.";
+    const char correct_piece2[] = "in a complicated way ";
+    const char correct_piece3[] = "n unnecessarily";
+    char piece1[sizeof(correct_piece1)] = "";
+    char piece2[sizeof(correct_piece2)] = "";
+    char piece3[sizeof(correct_piece3)] = "";
+
+    vector_buffer_t buffers[] = {
+        { piece1, sizeof(piece1) - 1 },
+        { piece2, sizeof(piece2) - 1 },
+        { piece3, sizeof(piece3) - 1 },
+    };
+
+    vector_t vector;
+    vector_init(&vector, NULL, 0);
+    vector_insert_buffer(&vector, 0, &buffers[0]);
+    vector_insert_buffer(&vector, 19, &buffers[1]);
+    vector_insert_buffer(&vector, 23, &buffers[2]);
+    vector_write_scatter(&vector, text, sizeof(text) - 1);
+    vector_clear(&vector);
+
+    ck_assert_str_eq(piece1, correct_piece1);
+    ck_assert_str_eq(piece2, correct_piece2);
+    ck_assert_str_eq(piece3, correct_piece3);
+}
+END_TEST
+
+START_TEST(test_vector_io_dynamic_gather)
+{
+    char piece1[] = "Building is fun.";
+    char piece2[] = "a string in a non-linear way ";
+    char piece3[] = "from pieces ";
+    const char correct_text[] = "Building a string from pieces in a non-linear way is fun.";
+    char text[sizeof(correct_text)] = "";
+
+    vector_buffer_t buffers[] = {
+        { piece1, sizeof(piece1) - 1 },
+        { piece2, sizeof(piece2) - 1 },
+        { piece3, sizeof(piece3) - 1 },
+    };
+
+    vector_t vector;
+    vector_init(&vector, NULL, 0);
+    vector_insert_buffer(&vector, 0, &buffers[0]);
+    vector_insert_buffer(&vector, 9, &buffers[1]);
+    vector_insert_buffer(&vector, 18, &buffers[2]);
+    vector_read_gather(&vector, text, sizeof(text) - 1);
+    vector_clear(&vector);
+
+    ck_assert_str_eq(text, correct_text);
+}
+END_TEST
+
+TCase *tcase_vector_io_simple_create(void)
+{
+    TCase *tc = tcase_create("Simple");
+    tcase_add_checked_fixture(tc, setup, teardown);
+    tcase_add_test(tc, test_vector_io_simple_scatter);
+    tcase_add_test(tc, test_vector_io_simple_gather);
+    return tc;
+}
+
+TCase *tcase_vector_io_multi_create(void)
+{
+    TCase *tc = tcase_create("Multiple Reads/Writes");
+    tcase_add_checked_fixture(tc, setup, teardown);
+    tcase_add_test(tc, test_vector_io_multi_write_scatter);
+    tcase_add_test(tc, test_vector_io_multi_read_gather);
+    return tc;
+}
+
+TCase *tcase_vector_io_dynamic_create(void)
+{
+    TCase *tc = tcase_create("Dynamic");
+    tcase_add_checked_fixture(tc, setup, teardown);
+    tcase_add_test(tc, test_vector_io_dynamic_scatter);
+    tcase_add_test(tc, test_vector_io_dynamic_gather);
+    return tc;
+}
+
+Suite *suite_vector_io_create(void)
+{
+    Suite *suite = suite_create("Vectored I/O");
+    suite_add_tcase(suite, tcase_vector_io_simple_create());
+    suite_add_tcase(suite, tcase_vector_io_multi_create());
+    suite_add_tcase(suite, tcase_vector_io_dynamic_create());
+    return suite;
+}
+