├── autogen.sh ├── todo ├── src ├── reactor │ ├── decode.h │ ├── hash.h │ ├── encode.h │ ├── pool.h │ ├── timeout.h │ ├── signal.h │ ├── data.h │ ├── http.h │ ├── network.h │ ├── mapi.h │ ├── maps.h │ ├── mapd.h │ ├── pool.c │ ├── string.h │ ├── server.h │ ├── notify.h │ ├── map.h │ ├── stream.h │ ├── timeout.c │ ├── queue.h │ ├── list.h │ ├── vector.h │ ├── signal.c │ ├── buffer.h │ ├── mapi.c │ ├── mapd.c │ ├── maps.c │ ├── flow.h │ ├── data.c │ ├── vector.c │ ├── reactor.h │ ├── value.h │ ├── list.c │ ├── notify.c │ ├── map.c │ ├── stream.c │ ├── buffer.c │ ├── queue.c │ ├── server.c │ ├── encode.c │ └── hash.c ├── reactor.h └── picohttpparser │ └── picohttpparser.h ├── example ├── lookup.c ├── buffer.c ├── simpleread.c ├── timeout.c ├── module2.c ├── validate.sh ├── server.c ├── signal.c ├── benchmark.sh ├── flow.c ├── Makefile.am ├── stream.c ├── notify.c ├── validate.c ├── module1.c ├── queue.c ├── shufflecat.c ├── scan.c ├── mpmc.c ├── clients.c └── encode.c ├── test ├── conf │ ├── module2.c │ └── module1.c ├── valgrind.sh ├── include.c ├── coverage.sh ├── stack.c ├── pool.c ├── mapi.c ├── timeout.c ├── data.c ├── maps.c ├── signal.c ├── mapd.c ├── network_resolve.c ├── flow_async.c ├── hash.c ├── flow.c ├── vector.c ├── buffer.c ├── encode.c ├── notify.c ├── stream.c ├── list.c ├── map.c ├── queue.c ├── network.c ├── http.c ├── server.c └── string.c ├── libreactor.pc.in ├── .clang-format ├── configure.ac ├── .github └── workflows │ ├── c-cpp.yml │ └── codeql.yml ├── LICENSE └── Makefile.am /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p autotools m4 4 | autoreconf --force --install 5 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | * streams with graceful close, or flush event? 2 | * change char to utf8_t in string, and perhaps change to string_utf8_t type 3 | * reference and data values 4 | * ascii option 5 | -------------------------------------------------------------------------------- /src/reactor/decode.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUE_DECODE_H_INCLUDED 2 | #define VALUE_DECODE_H_INCLUDED 3 | 4 | #include 5 | 6 | value_t *value_decode(const char *, const char **); 7 | 8 | #endif /* VALUE_DECODE_H_INCLUDED */ 9 | -------------------------------------------------------------------------------- /example/lookup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | reactor_construct(); 8 | network_resolve(NULL, NULL, "www.sunet.se"); 9 | reactor_loop(); 10 | reactor_destruct(); 11 | } 12 | -------------------------------------------------------------------------------- /src/reactor/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUE_HASH_H_INCLUDED 2 | #define VALUE_HASH_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | uint64_t hash_data(const data_t); 8 | uint64_t hash_uint64(uint64_t); 9 | 10 | #endif /* VALUE_HASH_H_INCLUDED */ 11 | -------------------------------------------------------------------------------- /test/conf/module2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void receive(void *state, value_t *message) 5 | { 6 | (void) state; 7 | 8 | printf("[module2 instance pulse: %d\n", (int) value_number_get(message)); 9 | } 10 | 11 | flow_table_t module = {.receive = receive}; 12 | -------------------------------------------------------------------------------- /example/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | buffer_t b; 7 | 8 | buffer_construct(&b); 9 | buffer_loadz(&b, "/etc/services"); 10 | buffer_save(&b, "/tmp/services"); 11 | printf("%s", (char *) buffer_base(&b)); 12 | buffer_destruct(&b); 13 | } 14 | -------------------------------------------------------------------------------- /libreactor.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=${prefix}/include 5 | 6 | Name: libreactor 7 | Description: high performance, robust and secure, generic event-driven application framework 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -lreactor 10 | Cflags: -I${includedir} -flto 11 | -------------------------------------------------------------------------------- /example/simpleread.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void callback(reactor_event_t *event) 4 | { 5 | printf("read returned %d\n", (int) event->data); 6 | } 7 | 8 | int main() 9 | { 10 | char buffer[1024]; 11 | 12 | reactor_construct(); 13 | reactor_read(callback, NULL, 0, buffer, sizeof buffer, 0); 14 | reactor_loop(); 15 | reactor_destruct(); 16 | } 17 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Microsoft 4 | IndentWidth: 2 5 | SpaceAfterCStyleCast: true 6 | SpaceBeforeCpp11BracedList: true 7 | ColumnLimit: 120 8 | SortIncludes: false 9 | ... 10 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([libreactor],[0.9.2],[fredrik.widlund@gmail.com]) 2 | AC_CONFIG_AUX_DIR(autotools) 3 | AC_CONFIG_MACRO_DIR([m4]) 4 | AM_INIT_AUTOMAKE([-Wall -Werror foreign no-define]) 5 | 6 | : ${CFLAGS="-Wall -Wextra -Wpedantic"} 7 | 8 | AM_PROG_AR 9 | LT_INIT 10 | AM_PROG_CC_C_O 11 | 12 | AC_PREFIX_DEFAULT(/usr) 13 | AC_CONFIG_FILES([Makefile libreactor.pc example/Makefile]) 14 | AC_OUTPUT 15 | -------------------------------------------------------------------------------- /example/timeout.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void callback(__attribute__((unused)) reactor_event_t *event) 5 | { 6 | printf("timeout\n"); 7 | } 8 | 9 | int main() 10 | { 11 | timeout_t t; 12 | 13 | reactor_construct(); 14 | timeout_construct(&t, callback, &t); 15 | timeout_set(&t, reactor_now() + 1000000000, 0); 16 | reactor_loop(); 17 | reactor_destruct(); 18 | } 19 | -------------------------------------------------------------------------------- /example/module2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void *load(void) 5 | { 6 | printf("[module2 loading]\n"); 7 | return NULL; 8 | } 9 | 10 | static void receive(void *state, value_t *message) 11 | { 12 | (void) state; 13 | 14 | printf("[module2 instance pulse: %d\n", (int) value_number_get(message)); 15 | } 16 | 17 | flow_table_t module = {.load = load, .receive = receive}; 18 | -------------------------------------------------------------------------------- /example/validate.sh: -------------------------------------------------------------------------------- 1 | for file in /root/build/JSONTestSuite/test_parsing/y_* 2 | do 3 | ./validate $file > /dev/null || echo $file 4 | done 5 | 6 | for file in /root/build/JSONTestSuite/test_parsing/n_* 7 | do 8 | ./validate $file > /dev/null && echo $file 9 | done 10 | 11 | for file in /root/build/JSONTestSuite/test_parsing/i_* 12 | do 13 | out=$(./validate $file) && echo [$file] $out 14 | done 15 | -------------------------------------------------------------------------------- /test/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if command -v valgrind; then 4 | for file in include data hash buffer list vector map mapi maps mapd string value encode pool timeout notify network stream http server queue flow 5 | do 6 | echo [$file] 7 | if ! valgrind --track-fds=yes --error-exitcode=1 --leak-check=full --show-leak-kinds=all test/$file; then 8 | exit 1 9 | fi 10 | done 11 | fi 12 | -------------------------------------------------------------------------------- /test/include.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void test_include(__attribute__((unused)) void **arg) 8 | { 9 | assert_string_equal(REACTOR_VERSION, "0.9.2"); 10 | } 11 | 12 | int main() 13 | { 14 | const struct CMUnitTest tests[] = { 15 | cmocka_unit_test(test_include), 16 | }; 17 | 18 | return cmocka_run_group_tests(tests, NULL, NULL); 19 | } 20 | -------------------------------------------------------------------------------- /src/reactor/encode.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUE_ENCODE_H_INCLUDED 2 | #define VALUE_ENCODE_H_INCLUDED 3 | 4 | enum value_encode_options 5 | { 6 | VALUE_ENCODE_PRETTY = 1, 7 | VALUE_ENCODE_ASCII = 2, 8 | }; 9 | 10 | typedef enum value_encode_options value_encode_options_t; 11 | 12 | bool value_buffer_encode(buffer_t *, const value_t *, value_encode_options_t); 13 | string_t value_encode(const value_t *, value_encode_options_t); 14 | 15 | #endif /* VALUE_ENCODE_H_INCLUDED */ 16 | -------------------------------------------------------------------------------- /test/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | for file in data hash buffer list vector map mapi maps mapd string value encode decode pool reactor signal timeout notify network stream http server queue flow 4 | do 5 | echo [$file] 6 | test=`gcov -b src/reactor/libreactor_test_a-$file | grep -A4 File.*$file` 7 | echo "$test" 8 | echo "$test" | grep '% of' | grep '100.00%' >/dev/null || exit 1 9 | echo "$test" | grep '% of' | grep -v '100.00%' >/dev/null && exit 1 10 | done 11 | exit 0 12 | -------------------------------------------------------------------------------- /src/reactor/pool.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_POOL_H 2 | #define REACTOR_POOL_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct pool pool_t; 8 | 9 | struct pool 10 | { 11 | size_t size; 12 | size_t allocated; 13 | list_t chunks; 14 | vector_t free; 15 | }; 16 | 17 | void pool_construct(pool_t *, size_t); 18 | void pool_destruct(pool_t *); 19 | size_t pool_size(pool_t *); 20 | void *pool_malloc(pool_t *); 21 | void pool_free(pool_t *, void *); 22 | 23 | #endif /* REACTOR_POOL_H */ 24 | -------------------------------------------------------------------------------- /example/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void handle_request(reactor_event_t *event) 5 | { 6 | if (event->type == SERVER_ERROR) 7 | err(1, "server callback error"); 8 | server_plain((server_session_t *) event->data, string("hello"), NULL, 0); 9 | } 10 | 11 | int main() 12 | { 13 | server_t server; 14 | 15 | reactor_construct(); 16 | server_construct(&server, handle_request, NULL); 17 | server_open(&server, "127.0.0.1", 80); 18 | reactor_loop(); 19 | reactor_destruct(); 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: install cmocka 16 | run: sudo apt-get install -y libcmocka-dev libltdl-dev 17 | - uses: actions/checkout@v3 18 | - name: autogen 19 | run: ./autogen.sh 20 | - name: configure 21 | run: ./configure 22 | - name: make 23 | run: make 24 | - name: make check 25 | run: make check 26 | - name: make distcheck 27 | run: make distcheck 28 | -------------------------------------------------------------------------------- /example/signal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void callback(reactor_event_t *event) 5 | { 6 | signal_t *signal = event->state; 7 | 8 | printf("signal received\n"); 9 | signal_close(signal); 10 | } 11 | 12 | void producer(reactor_event_t *event) 13 | { 14 | signal_t *signal = event->state; 15 | 16 | if (event->type == REACTOR_CALL) 17 | signal_send_sync(signal); 18 | } 19 | 20 | int main() 21 | { 22 | signal_t signal; 23 | 24 | reactor_construct(); 25 | signal_construct(&signal, callback, &signal); 26 | signal_open(&signal); 27 | reactor_async(producer, &signal); 28 | reactor_loop(); 29 | signal_destruct(&signal); 30 | reactor_destruct(); 31 | } 32 | -------------------------------------------------------------------------------- /test/stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | static void test_stack(__attribute__((unused)) void **arg) 10 | { 11 | stack s; 12 | 13 | stack_construct(&s); 14 | assert_true(stack_empty(&s)); 15 | stack_push(&s, NULL); 16 | assert_false(stack_empty(&s)); 17 | assert_true(stack_peek(&s) == NULL); 18 | assert_true(stack_pop(&s) == NULL); 19 | stack_clear(&s, NULL); 20 | stack_destruct(&s, NULL); 21 | } 22 | 23 | int main() 24 | { 25 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_stack)}; 26 | 27 | return cmocka_run_group_tests(tests, NULL, NULL); 28 | } 29 | -------------------------------------------------------------------------------- /src/reactor/timeout.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_TIMEOUT_H_INCLUDED 2 | #define REACTOR_TIMEOUT_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | TIMEOUT_ALERT 10 | }; 11 | 12 | typedef struct timeout timeout_t; 13 | 14 | struct timeout 15 | { 16 | reactor_user_t user; 17 | reactor_time_t time; 18 | reactor_time_t delay; 19 | reactor_t timeout; 20 | struct timespec tv; 21 | }; 22 | 23 | void timeout_construct(timeout_t *, reactor_callback_t *, void *); 24 | void timeout_destruct(timeout_t *); 25 | void timeout_set(timeout_t *, reactor_time_t, reactor_time_t); 26 | void timeout_clear(timeout_t *); 27 | 28 | #endif /* REACTOR_TIMEOUT_H_INCLUDED */ 29 | -------------------------------------------------------------------------------- /example/benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | make -j8 -C .. && make -j8 server clients 4 | ulimit -n 10000 5 | 6 | 7 | echo start server 8 | taskset -c 0 ./server & 9 | 10 | echo start load 11 | taskset -c 1 ./clients 12 | 13 | kill $(jobs -p) 14 | sleep 2 15 | 16 | echo start saved server 17 | taskset -c 0 ./server.saved & 18 | 19 | echo start load 20 | taskset -c 1 ./clients 21 | 22 | kill $(jobs -p) 23 | sleep 2 24 | 25 | echo start server 26 | taskset -c 0 ./server & 27 | 28 | echo start load 29 | taskset -c 1 ./clients 30 | 31 | kill $(jobs -p) 32 | sleep 2 33 | 34 | echo start saved server 35 | taskset -c 0 ./server.saved & 36 | 37 | echo start load 38 | taskset -c 1 ./clients 39 | 40 | kill $(jobs -p) 41 | sleep 2 42 | -------------------------------------------------------------------------------- /src/reactor/signal.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_SIGNAL_H_INCLUDED 2 | #define REACTOR_SIGNAL_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | SIGNAL_RECEIVE 10 | }; 11 | 12 | typedef struct signal signal_t; 13 | 14 | struct signal 15 | { 16 | reactor_user_t user; 17 | reactor_t read; 18 | uint64_t counter; 19 | int fd; 20 | bool *abort; 21 | }; 22 | 23 | void signal_construct(signal_t *, reactor_callback_t *, void *); 24 | void signal_open(signal_t *); 25 | void signal_send(signal_t *); 26 | void signal_send_sync(signal_t *); 27 | void signal_close(signal_t *); 28 | void signal_destruct(signal_t *); 29 | 30 | #endif /* REACTOR_SIGNAL_H_INCLUDED */ 31 | 32 | -------------------------------------------------------------------------------- /example/flow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void callback(reactor_event_t *event) 6 | { 7 | value_t *message = (value_t *) event->data; 8 | 9 | fprintf(stderr, "[log] %s\n", value_string_base(message)); 10 | } 11 | 12 | int main() 13 | { 14 | flow_t flow; 15 | 16 | reactor_construct(); 17 | flow_construct(&flow, callback, NULL); 18 | flow_search(&flow, "."); 19 | 20 | assert(flow_node(&flow, "module1", NULL, "
", value_null()) == 0); 21 | assert(flow_node(&flow, "module2", NULL, "
", value_null()) == 0); 22 | flow_connect(&flow, "module1", "module2", value_null()); 23 | flow_start(&flow); 24 | 25 | reactor_loop(); 26 | 27 | flow_stop(&flow); 28 | flow_destruct(&flow); 29 | reactor_destruct(); 30 | } 31 | -------------------------------------------------------------------------------- /example/Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4L 2 | AM_CFLAGS = -std=gnu2x -g -O3 -flto=auto -rdynamic -march=native -I../src -fPIC 3 | AM_LDFLAGS = -static 4 | LDADD = ../libreactor.la 5 | 6 | bin_PROGRAMS = \ 7 | buffer \ 8 | encode \ 9 | validate \ 10 | shufflecat \ 11 | simpleread \ 12 | signal \ 13 | timeout \ 14 | notify \ 15 | lookup \ 16 | scan \ 17 | stream \ 18 | clients \ 19 | server \ 20 | mpmc \ 21 | queue \ 22 | flow 23 | 24 | flow_LDADD = -lltdl ../libreactor.la 25 | 26 | lib_LTLIBRARIES = \ 27 | module1.la \ 28 | module2.la 29 | 30 | module1_la_LDFLAGS = -module -avoid-version 31 | module1_la_LIBADD = ../libreactor.la 32 | module2_la_LDFLAGS = -module -avoid-version 33 | module2_la_LIBADD = ../libreactor.la 34 | 35 | MAINTAINERCLEANFILES = Makefile.in 36 | -------------------------------------------------------------------------------- /example/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static void input(reactor_event_t *event) 7 | { 8 | stream_t *stream = event->state; 9 | data_t data; 10 | 11 | switch (event->type) 12 | { 13 | case STREAM_ERROR: 14 | errx(1, "error: %s\n", strerror(event->data)); 15 | break; 16 | case STREAM_READ: 17 | data = stream_read(stream); 18 | stream_consume(stream, data_size(data)); 19 | fwrite(data_base(data), data_size(data), 1, stdout); 20 | break; 21 | case STREAM_CLOSE: 22 | stream_destruct(stream); 23 | break; 24 | } 25 | } 26 | 27 | int main() 28 | { 29 | stream_t s; 30 | 31 | reactor_construct(); 32 | stream_construct(&s, input, &s); 33 | stream_open(&s, 0, 0); 34 | reactor_loop(); 35 | reactor_destruct(); 36 | } 37 | -------------------------------------------------------------------------------- /example/notify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | static void callback(reactor_event_t *event) 8 | { 9 | notify_t *notify = event->state; 10 | notify_event_t *e = (notify_event_t *) event->data; 11 | 12 | (void) fprintf(stdout, "%d:%04x:%s:%s\n", e->id, e->mask, e->path, e->name); 13 | notify_remove_path(notify, e->path); 14 | } 15 | 16 | int main(int argc, char **argv) 17 | { 18 | notify_t notify; 19 | int i, e; 20 | 21 | reactor_construct(); 22 | notify_construct(¬ify, callback, ¬ify); 23 | notify_open(¬ify); 24 | for (i = 1; i < argc; i++) 25 | { 26 | e = notify_add(¬ify, argv[i], IN_ALL_EVENTS); 27 | if (e == -1) 28 | err(1, "notify_add"); 29 | } 30 | reactor_loop(); 31 | notify_destruct(¬ify); 32 | reactor_destruct(); 33 | } 34 | -------------------------------------------------------------------------------- /test/pool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "reactor.h" 11 | 12 | static void test_pool(__attribute__((unused)) void **arg) 13 | { 14 | pool_t pool; 15 | int i, n = 123; 16 | void *p[n]; 17 | 18 | pool_construct(&pool, sizeof (void *)); 19 | assert_int_equal(pool_size(&pool), 0); 20 | for (i = 0; i < n; i ++) 21 | p[i] = pool_malloc(&pool); 22 | assert_int_equal(pool_size(&pool), n); 23 | for (; i >= 0; i --) 24 | pool_free(&pool, p[i]); 25 | pool_destruct(&pool); 26 | } 27 | 28 | int main() 29 | { 30 | const struct CMUnitTest tests[] = 31 | { 32 | cmocka_unit_test(test_pool), 33 | }; 34 | 35 | return cmocka_run_group_tests(tests, NULL, NULL); 36 | } 37 | -------------------------------------------------------------------------------- /test/mapi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void test_mapi(__attribute__((unused)) void **state) 12 | { 13 | mapi_t mapi; 14 | 15 | mapi_construct(&mapi); 16 | 17 | mapi_insert(&mapi, 1, 42, NULL); 18 | mapi_insert(&mapi, 1, 42, NULL); 19 | assert_true(mapi_at(&mapi, 1) == 42); 20 | assert_true(mapi_size(&mapi) == 1); 21 | mapi_erase(&mapi, 2, NULL); 22 | assert_true(mapi_size(&mapi) == 1); 23 | mapi_erase(&mapi, 1, NULL); 24 | assert_true(mapi_size(&mapi) == 0); 25 | 26 | mapi_reserve(&mapi, 32); 27 | assert_true(mapi.map.elements_capacity == 64); 28 | mapi_clear(&mapi, NULL); 29 | mapi_destruct(&mapi, NULL); 30 | } 31 | 32 | int main() 33 | { 34 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_mapi)}; 35 | 36 | return cmocka_run_group_tests(tests, NULL, NULL); 37 | } 38 | -------------------------------------------------------------------------------- /example/validate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) 6 | { 7 | extern char *__progname; 8 | buffer_t buffer; 9 | value_t *value; 10 | const char *end; 11 | bool status; 12 | string_t string; 13 | 14 | if (argc != 2) 15 | { 16 | fprintf(stderr, "usage: %s FILE\n", __progname); 17 | exit(1); 18 | } 19 | 20 | buffer_construct(&buffer); 21 | status = buffer_loadz(&buffer, argv[1]); 22 | if (!status) 23 | err(1, "buffer_loadz"); 24 | 25 | value = value_decode(buffer_base(&buffer), &end); 26 | if (value_is_undefined(value) || end != (char *) buffer_base(&buffer) + buffer_size(&buffer)) 27 | { 28 | buffer_destruct(&buffer); 29 | value_release(value); 30 | printf("invalid\n"); 31 | exit(1); 32 | } 33 | 34 | buffer_destruct(&buffer); 35 | string = value_encode(value, VALUE_ENCODE_PRETTY); 36 | value_release(value); 37 | printf("%s\n", string_base(string)); 38 | string_release(string); 39 | } 40 | -------------------------------------------------------------------------------- /src/reactor/data.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_DATA_H_INCLUDED 2 | #define REACTOR_DATA_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct data data_t; 9 | 10 | struct data 11 | { 12 | struct iovec iov; 13 | }; 14 | 15 | /* constructor/destructor */ 16 | 17 | data_t data(const void *, size_t); 18 | data_t data_null(void); 19 | data_t data_string(const char *); 20 | data_t data_offset(const data_t, size_t); 21 | data_t data_select(const data_t, size_t); 22 | data_t data_copy(const data_t); 23 | data_t data_copyz(const data_t); 24 | data_t data_alloc(size_t); 25 | data_t data_realloc(data_t, size_t); 26 | void data_release(data_t); 27 | 28 | /* capacity */ 29 | 30 | size_t data_size(const data_t); 31 | bool data_empty(const data_t); 32 | 33 | /* element access */ 34 | 35 | void *data_base(const data_t); 36 | void *data_end(const data_t); 37 | 38 | /* operations */ 39 | 40 | bool data_equal(const data_t, const data_t); 41 | bool data_equal_case(const data_t, const data_t); 42 | 43 | #endif /* REACTOR_DATA_H_INCLUDED */ 44 | -------------------------------------------------------------------------------- /src/reactor/http.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_HTTP_H_INCLUDED 2 | #define REACTOR_HTTP_H_INCLUDED 3 | 4 | typedef struct http_request http_request_t; 5 | typedef struct http_response http_response_t; 6 | 7 | typedef struct http_field http_field_t; 8 | 9 | struct http_field 10 | { 11 | string_t name; 12 | string_t value; 13 | }; 14 | 15 | struct http_request 16 | { 17 | string_t method; 18 | string_t target; 19 | data_t body; 20 | http_field_t fields[16]; 21 | size_t fields_count; 22 | }; 23 | 24 | struct http_response 25 | { 26 | string_t status; 27 | string_t date; 28 | string_t type; 29 | data_t body; 30 | http_field_t fields[16]; 31 | size_t fields_count; 32 | }; 33 | 34 | http_field_t http_field_define(string_t, string_t); 35 | string_t http_field_lookup(http_field_t *, size_t, string_t); 36 | int http_read_request(stream_t *, string_t *, string_t *, data_t *, http_field_t *, size_t *); 37 | void http_write_response(stream_t *, string_t, string_t, string_t, data_t, http_field_t *, size_t); 38 | 39 | #endif /* REACTOR_HTTP_H_INCLUDED */ 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Fredrik Widlund 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/reactor/network.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_NETWORK_H_INCLUDED 2 | #define REACTOR_NETWORK_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | NETWORK_ERROR, 10 | NETWORK_RESOLVE, 11 | NETWORK_ACCEPT, 12 | NETWORK_ACCEPT_BIND, 13 | NETWORK_CONNECT 14 | }; 15 | 16 | enum 17 | { 18 | NETWORK_REUSEADDR = 0x01, 19 | NETWORK_REUSEPORT = 0x02 20 | }; 21 | 22 | typedef reactor_t network_t; 23 | 24 | void network_construct(void); 25 | void network_destruct(void); 26 | void network_cancel(network_t); 27 | void network_expire(uint64_t); 28 | 29 | network_t network_resolve(reactor_callback_t *, void *, const char *); 30 | 31 | network_t network_accept(reactor_callback_t *, void *, const char *, int, int); 32 | network_t network_accept_address(reactor_callback_t *, void *, struct sockaddr *, socklen_t, int); 33 | network_t network_accept_socket(reactor_callback_t *, void *, int); 34 | 35 | network_t network_connect(reactor_callback_t *, void *, const char *, int); 36 | network_t network_connect_address(reactor_callback_t *, void *, struct sockaddr *, socklen_t); 37 | 38 | #endif /* REACTOR_NETWORK_H_INCLUDED */ 39 | -------------------------------------------------------------------------------- /src/reactor/mapi.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_MAPI_H_INCLUDED 2 | #define REACTOR_MAPI_H_INCLUDED 3 | 4 | #define mapi_foreach(m, e) \ 5 | for ((e) = (m)->map.elements; (e) != ((mapi_entry *) (m)->map.elements) + (m)->map.elements_capacity; (e) ++) \ 6 | if ((e)->key) 7 | 8 | typedef struct mapi_entry mapi_entry_t; 9 | typedef struct mapi mapi_t; 10 | typedef void mapi_release_t(mapi_entry_t *); 11 | 12 | struct mapi_entry 13 | { 14 | uintptr_t key; 15 | uintptr_t value; 16 | }; 17 | 18 | struct mapi 19 | { 20 | map_t map; 21 | }; 22 | 23 | /* constructor/destructor */ 24 | 25 | void mapi_construct(mapi_t *); 26 | void mapi_destruct(mapi_t *, mapi_release_t *); 27 | 28 | /* capacity */ 29 | 30 | size_t mapi_size(const mapi_t *); 31 | void mapi_reserve(mapi_t *, size_t); 32 | 33 | /* element access */ 34 | 35 | uintptr_t mapi_at(const mapi_t *, uintptr_t); 36 | 37 | /* modifiers */ 38 | 39 | void mapi_insert(mapi_t *, uintptr_t, uintptr_t, mapi_release_t *); 40 | void mapi_erase(mapi_t *, uintptr_t, mapi_release_t *); 41 | void mapi_clear(mapi_t *, mapi_release_t *); 42 | 43 | #endif /* REACTOR_MAPI_H_INCLUDED */ 44 | -------------------------------------------------------------------------------- /src/reactor/maps.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_MAPS_H_INCLUDED 2 | #define REACTOR_MAPS_H_INCLUDED 3 | 4 | #define maps_foreach(m, e) \ 5 | for ((e) = (m)->map.elements; (e) != ((maps_entry *) (m)->map.elements) + (m)->map.elements_capacity; (e) ++) \ 6 | if ((e)->key) 7 | 8 | typedef struct maps_entry maps_entry_t; 9 | typedef struct maps maps_t; 10 | typedef void maps_release_t(maps_entry_t *); 11 | 12 | struct maps_entry 13 | { 14 | const char *key; 15 | uintptr_t value; 16 | }; 17 | 18 | struct maps 19 | { 20 | map_t map; 21 | }; 22 | 23 | /* constructor/destructor */ 24 | 25 | void maps_construct(maps_t *); 26 | void maps_destruct(maps_t *, maps_release_t *); 27 | 28 | /* capacity */ 29 | 30 | size_t maps_size(const maps_t *); 31 | void maps_reserve(maps_t *, size_t); 32 | 33 | /* element access */ 34 | 35 | uintptr_t maps_at(const maps_t *, const char *); 36 | 37 | /* modifiers */ 38 | 39 | void maps_insert(maps_t *, const char *, uintptr_t, maps_release_t *); 40 | void maps_erase(maps_t *, const char *, maps_release_t *); 41 | void maps_clear(maps_t *, maps_release_t *); 42 | 43 | #endif /* REACTOR_MAPS_H_INCLUDED */ 44 | -------------------------------------------------------------------------------- /src/reactor/mapd.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_MAPD_H_INCLUDED 2 | #define REACTOR_MAPD_H_INCLUDED 3 | 4 | #define mapd_foreach(m, e) \ 5 | for ((e) = (m)->map.elements; (e) != ((mapd_entry_t *) (m)->map.elements) + (m)->map.elements_capacity; (e)++) \ 6 | if (!data_nullp((e)->key)) 7 | 8 | typedef struct mapd_entry mapd_entry_t; 9 | typedef struct mapd mapd_t; 10 | typedef void mapd_release_t(mapd_entry_t *); 11 | 12 | struct mapd_entry 13 | { 14 | data_t key; 15 | uintptr_t value; 16 | }; 17 | 18 | struct mapd 19 | { 20 | map_t map; 21 | }; 22 | 23 | /* constructor/destructor */ 24 | 25 | void mapd_construct(mapd_t *); 26 | void mapd_destruct(mapd_t *, mapd_release_t *); 27 | 28 | /* capacity */ 29 | 30 | size_t mapd_size(const mapd_t *); 31 | void mapd_reserve(mapd_t *, size_t); 32 | 33 | /* element access */ 34 | 35 | uintptr_t mapd_at(const mapd_t *, const data_t); 36 | 37 | /* modifiers */ 38 | 39 | void mapd_insert(mapd_t *, const data_t, uintptr_t, mapd_release_t *); 40 | void mapd_erase(mapd_t *, const data_t, mapd_release_t *); 41 | void mapd_clear(mapd_t *, mapd_release_t *); 42 | 43 | #endif /* REACTOR_MAPD_H_INCLUDED */ 44 | -------------------------------------------------------------------------------- /src/reactor/pool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define POOL_CHUNK_SIZE 128 5 | 6 | void pool_construct(pool_t *pool, size_t size) 7 | { 8 | *pool = (struct pool) {.size = size}; 9 | 10 | list_construct(&pool->chunks); 11 | vector_construct(&pool->free, sizeof(void *)); 12 | } 13 | 14 | void pool_destruct(pool_t *pool) 15 | { 16 | list_destruct(&pool->chunks, NULL); 17 | vector_destruct(&pool->free, NULL); 18 | } 19 | 20 | size_t pool_size(pool_t *pool) 21 | { 22 | return pool->allocated; 23 | } 24 | 25 | void *pool_malloc(pool_t *pool) 26 | { 27 | void *chunk, *object; 28 | size_t i; 29 | 30 | if (vector_size(&pool->free) == 0) 31 | { 32 | chunk = list_push_back(&pool->chunks, NULL, pool->size * POOL_CHUNK_SIZE); 33 | for (i = 0; i < POOL_CHUNK_SIZE; i++) 34 | { 35 | object = (char *) chunk + (i * pool->size); 36 | vector_push_back(&pool->free, &object); 37 | } 38 | } 39 | 40 | object = *(void **) vector_back(&pool->free); 41 | vector_pop_back(&pool->free, NULL); 42 | pool->allocated++; 43 | return object; 44 | } 45 | 46 | void pool_free(pool_t *pool, void *object) 47 | { 48 | vector_push_back(&pool->free, &object); 49 | pool->allocated--; 50 | } 51 | -------------------------------------------------------------------------------- /test/timeout.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | struct state 15 | { 16 | uint64_t value; 17 | size_t calls; 18 | }; 19 | 20 | static void callback(reactor_event_t *event) 21 | { 22 | struct state *state = event->state; 23 | 24 | state->calls++; 25 | } 26 | 27 | static void test_timeout(__attribute__((unused)) void **arg) 28 | { 29 | struct state state = {0}; 30 | timeout_t t; 31 | 32 | reactor_construct(); 33 | timeout_construct(&t, callback, &state); 34 | 35 | timeout_set(&t, reactor_now(), 0); 36 | reactor_loop_once(); 37 | 38 | timeout_set(&t, reactor_now(), 1); 39 | reactor_loop_once(); 40 | 41 | timeout_set(&t, reactor_now(), 0); 42 | timeout_destruct(&t); 43 | reactor_loop_once(); 44 | 45 | reactor_destruct(); 46 | assert_int_equal(state.calls, 2); 47 | } 48 | 49 | int main() 50 | { 51 | const struct CMUnitTest tests[] = 52 | { 53 | cmocka_unit_test(test_timeout), 54 | }; 55 | 56 | return cmocka_run_group_tests(tests, NULL, NULL); 57 | } 58 | -------------------------------------------------------------------------------- /src/reactor/string.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_STRING_H_INCLUDED 2 | #define REACTOR_STRING_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | typedef data_t string_t; 8 | 9 | /* constructor/destructor */ 10 | 11 | string_t string(const char *); 12 | string_t string_data(const data_t); 13 | string_t string_null(void); 14 | string_t string_copy(const string_t); 15 | void string_release(string_t); 16 | 17 | /* capacity */ 18 | 19 | size_t string_size(const string_t); 20 | bool string_empty(string_t); 21 | 22 | /* element access */ 23 | 24 | char *string_base(const string_t); 25 | 26 | /* operations */ 27 | 28 | bool string_equal(const string_t, const string_t); 29 | bool string_equal_case(const string_t, const string_t); 30 | 31 | /* utf8 */ 32 | 33 | size_t string_utf8_length(const string_t); 34 | uint32_t string_utf8_get(const char *, const char *, const char **); 35 | uint32_t string_utf8_get_encoded(const char *, const char **); 36 | void string_utf8_put(buffer_t *, uint32_t); 37 | void string_utf8_put_encoded_basic(buffer_t *, uint16_t); 38 | void string_utf8_put_encoded(buffer_t *, uint32_t); 39 | bool string_utf8_encode(buffer_t *, const string_t, bool); 40 | string_t string_utf8_decode(const char *, const char **); 41 | 42 | #endif /* REACTOR_STRING_H_INCLUDED */ 43 | -------------------------------------------------------------------------------- /src/reactor/server.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_SERVER_H_INCLUDED 2 | #define REACTOR_SERVER_H_INCLUDED 3 | 4 | enum 5 | { 6 | SERVER_ERROR, 7 | SERVER_REQUEST 8 | }; 9 | 10 | enum 11 | { 12 | SERVER_SESSION_READY = 0x01, 13 | SERVER_SESSION_PROCESSING = 0x02 14 | }; 15 | 16 | typedef struct server server_t; 17 | typedef struct server_session server_session_t; 18 | 19 | struct server 20 | { 21 | reactor_user_t user; 22 | network_t accept; 23 | timeout_t timeout; 24 | list_t sessions; 25 | }; 26 | 27 | struct server_session 28 | { 29 | reactor_user_t user; 30 | http_request_t request; 31 | stream_t stream; 32 | int flags; 33 | bool *abort; 34 | reactor_t next; 35 | }; 36 | 37 | void server_construct(server_t *, reactor_callback_t *, void *); 38 | void server_destruct(server_t *); 39 | void server_open(server_t *, const char *, int); 40 | void server_open_socket(server_t *, int); 41 | void server_close(server_t *); 42 | void server_disconnect(server_session_t *); 43 | void server_respond(server_session_t *, string_t, string_t, data_t, http_field_t *, size_t); 44 | void server_ok(server_session_t *, string_t, data_t, http_field_t *, size_t); 45 | void server_plain(server_session_t *, data_t, http_field_t *, size_t); 46 | 47 | #endif /* REACTOR_SERVER_H_INCLUDED */ 48 | -------------------------------------------------------------------------------- /src/reactor/notify.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_NOTIFY_H_INCLUDED 2 | #define REACTOR_NOTIFY_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum 10 | { 11 | NOTIFY_EVENT 12 | }; 13 | 14 | typedef struct notify_event notify_event_t; 15 | typedef struct notify_entry notify_entry_t; 16 | typedef struct notify notify_t; 17 | 18 | struct notify_event 19 | { 20 | int id; 21 | char *path; 22 | char *name; 23 | uint32_t mask; 24 | }; 25 | 26 | struct notify_entry 27 | { 28 | int id; 29 | char *path; 30 | }; 31 | 32 | struct notify 33 | { 34 | reactor_user_t user; 35 | int fd; 36 | reactor_t read; 37 | reactor_t next; 38 | list_t entries; 39 | mapi_t ids; 40 | maps_t paths; 41 | bool *abort; 42 | char buffer[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); 43 | }; 44 | 45 | void notify_construct(notify_t *, reactor_callback_t *, void *); 46 | void notify_open(notify_t *); 47 | int notify_add(notify_t *, char *, uint32_t); 48 | bool notify_remove_path(notify_t *, char *); 49 | bool notify_remove_id(notify_t *, int); 50 | void notify_close(notify_t *); 51 | void notify_destruct(notify_t *); 52 | 53 | #endif /* REACTOR_NOTIFY_H_INCLUDED */ 54 | -------------------------------------------------------------------------------- /src/reactor/map.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_MAP_H_INCLUDED 2 | #define REACTOR_MAP_H_INCLUDED 3 | 4 | #ifndef MAP_ELEMENTS_CAPACITY_MIN 5 | #define MAP_ELEMENTS_CAPACITY_MIN 8 6 | #endif /* MAP_ELEMENTS_CAPACITY_MIN */ 7 | 8 | typedef size_t map_hash_t(const void *); 9 | typedef int map_equal_t(const void *, const void *); 10 | typedef void map_set_t(void *, const void *); 11 | typedef void map_release_t(void *); 12 | typedef struct map map_t; 13 | 14 | struct map 15 | { 16 | void *elements; 17 | size_t elements_count; 18 | size_t elements_capacity; 19 | size_t element_size; 20 | }; 21 | 22 | /* constructor/destructor */ 23 | 24 | void map_construct(map_t *, size_t, map_set_t *); 25 | void map_destruct(map_t *, map_equal_t *, map_release_t *); 26 | 27 | /* capacity */ 28 | 29 | size_t map_size(const map_t *); 30 | void map_reserve(map_t *, size_t, map_hash_t *, map_set_t *, map_equal_t *); 31 | 32 | /* element access */ 33 | 34 | void *map_at(const map_t *, const void *, map_hash_t *, map_equal_t *); 35 | 36 | /* modifiers */ 37 | 38 | void map_insert(map_t *, void *, map_hash_t *, map_set_t *, map_equal_t *, map_release_t *); 39 | void map_erase(map_t *, const void *, map_hash_t *, map_set_t *, map_equal_t *, map_release_t *); 40 | void map_clear(map_t *, map_set_t *, map_equal_t *, map_release_t *); 41 | 42 | #endif /* REACTOR_MAP_H_INCLUDED */ 43 | -------------------------------------------------------------------------------- /src/reactor/stream.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_STREAM_H_INCLUDED 2 | #define REACTOR_STREAM_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | STREAM_ERROR, 10 | STREAM_READ, 11 | STREAM_CLOSE 12 | }; 13 | 14 | enum 15 | { 16 | STREAM_WRITE_ONLY = 0x01 17 | }; 18 | 19 | typedef struct stream stream_t; 20 | 21 | struct stream 22 | { 23 | reactor_user_t user; 24 | int fd; 25 | int type; 26 | int flags; 27 | bool *abort; 28 | 29 | reactor_t read; 30 | size_t input_offset; 31 | buffer_t input; 32 | size_t input_consumed; 33 | 34 | reactor_t write; 35 | size_t output_offset; 36 | size_t output_partial; 37 | buffer_t output_writing; 38 | buffer_t output_waiting; 39 | size_t output_flushed; 40 | }; 41 | 42 | void stream_construct(stream_t *, reactor_callback_t *, void *); 43 | void stream_destruct(stream_t *); 44 | void stream_open(stream_t *, int, int); 45 | int stream_fd(stream_t *); 46 | bool stream_is_open(stream_t *); 47 | void stream_close(stream_t *); 48 | data_t stream_read(stream_t *); 49 | void stream_consume(stream_t *, size_t); 50 | void *stream_allocate(stream_t *, size_t); 51 | void stream_write(stream_t *, data_t); 52 | void stream_flush(stream_t *); 53 | 54 | #endif /* REACTOR_STREAM_H_INCLUDED */ 55 | -------------------------------------------------------------------------------- /test/data.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void test_data(__attribute__((unused)) void **arg) 9 | { 10 | data_t d, d2; 11 | 12 | d = data_null(); 13 | assert_true(data_empty(d)); 14 | 15 | d = data_string("test"); 16 | assert_true(strcmp(data_base(d), "test") == 0); 17 | assert_true(data_equal(d, d)); 18 | assert_true(data_equal(data_offset(d, 2), data_string("st"))); 19 | assert_true(data_equal(data_select(d, 2), data_string("te"))); 20 | 21 | assert_false(data_equal(data_string("a"), data_string("b"))); 22 | assert_false(data_equal(data_string("a"), data_string("a1"))); 23 | 24 | assert_true(data_equal_case(data_string("aA"), data_string("aA"))); 25 | assert_false(data_equal_case(data_string("a"), data_string("aA"))); 26 | assert_false(data_equal_case(data_string("a"), data_string("b"))); 27 | 28 | d2 = data_copy(d); 29 | assert_true(data_equal(d, d2)); 30 | data_release(d2); 31 | d2 = data_copyz(d); 32 | assert_true(data_equal(d, d2)); 33 | data_release(d2); 34 | 35 | d = data_alloc(42); 36 | d = data_realloc(d, 4711); 37 | assert_int_equal(data_size(d), 4711); 38 | data_release(d); 39 | } 40 | 41 | int main() 42 | { 43 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_data)}; 44 | 45 | return cmocka_run_group_tests(tests, NULL, NULL); 46 | } 47 | -------------------------------------------------------------------------------- /test/conf/module1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct state 4 | { 5 | flow_node_t *node; 6 | timeout_t timeout; 7 | size_t counter; 8 | }; 9 | 10 | static void *load(void) 11 | { 12 | printf("[module1 loading]\n"); 13 | return NULL; 14 | } 15 | 16 | static void timeout(reactor_event_t *event) 17 | { 18 | struct state *state = event->state; 19 | 20 | value_t *message; 21 | 22 | message = value_string(string("module1 sending pulse")); 23 | flow_log(state->node, message); 24 | value_release(message); 25 | 26 | flow_send_and_release(state->node, value_number(state->counter)); 27 | state->counter--; 28 | if (!state->counter) 29 | { 30 | timeout_clear(&state->timeout); 31 | flow_exit(state->node); 32 | } 33 | } 34 | 35 | static void *create(flow_node_t *node, value_t *value) 36 | { 37 | struct state *state = malloc(sizeof *state); 38 | 39 | (void) value; 40 | printf("[module1 instance created]\n"); 41 | state->node = node; 42 | state->counter = 3; 43 | timeout_construct(&state->timeout, timeout, state); 44 | timeout_set(&state->timeout, reactor_now(), 1000); 45 | return state; 46 | } 47 | 48 | static void destroy(void *user) 49 | { 50 | struct state *state = user; 51 | 52 | printf("[module1 instance destroyed]\n"); 53 | timeout_destruct(&state->timeout); 54 | free(state); 55 | } 56 | 57 | flow_table_t module = {.load = load, .create = create, .destroy = destroy}; 58 | -------------------------------------------------------------------------------- /example/module1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct state 4 | { 5 | flow_node_t *node; 6 | timeout_t timeout; 7 | size_t counter; 8 | }; 9 | 10 | static void *load(void) 11 | { 12 | printf("[module1 loading]\n"); 13 | return NULL; 14 | } 15 | 16 | static void timeout(reactor_event_t *event) 17 | { 18 | struct state *state = event->state; 19 | 20 | value_t *message; 21 | 22 | message = value_string(string("module1 sending pulse")); 23 | flow_log(state->node, message); 24 | value_release(message); 25 | 26 | flow_send_and_release(state->node, value_number(state->counter)); 27 | state->counter--; 28 | if (!state->counter) 29 | { 30 | timeout_clear(&state->timeout); 31 | flow_exit(state->node); 32 | } 33 | } 34 | 35 | static void *create(flow_node_t *node, value_t *value) 36 | { 37 | struct state *state = malloc(sizeof *state); 38 | 39 | (void) value; 40 | printf("[module1 instance created]\n"); 41 | state->node = node; 42 | state->counter = 3; 43 | timeout_construct(&state->timeout, timeout, state); 44 | timeout_set(&state->timeout, reactor_now(), 1000000000); 45 | return state; 46 | } 47 | 48 | static void destroy(void *user) 49 | { 50 | struct state *state = user; 51 | 52 | printf("[module1 instance destroyed]\n"); 53 | timeout_destruct(&state->timeout); 54 | free(state); 55 | } 56 | 57 | flow_table_t module = {.load = load, .create = create, .destroy = destroy}; 58 | -------------------------------------------------------------------------------- /test/maps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void release(maps_entry_t *e) 12 | { 13 | free((void *) e->key); 14 | } 15 | 16 | void test_maps(__attribute__((unused)) void **state) 17 | { 18 | maps_t maps; 19 | int i; 20 | void *p; 21 | char key[16]; 22 | 23 | maps_construct(&maps); 24 | 25 | for (i = 0; i < 2; i++) 26 | { 27 | p = malloc(5); 28 | strcpy(p, "test"); 29 | maps_insert(&maps, p, 42, release); 30 | } 31 | assert_true(maps_at(&maps, "test") == 42); 32 | assert_true(maps_size(&maps) == 1); 33 | maps_erase(&maps, "test2", release); 34 | assert_true(maps_size(&maps) == 1); 35 | maps_erase(&maps, "test", release); 36 | assert_true(maps_size(&maps) == 0); 37 | 38 | maps_reserve(&maps, 32); 39 | assert_true(maps.map.elements_capacity == 64); 40 | 41 | for (i = 0; i < 100; i++) 42 | { 43 | snprintf(key, sizeof key, "test%d", i); 44 | p = malloc(strlen(key) + 1); 45 | strcpy(p, key); 46 | maps_insert(&maps, p, i, release); 47 | } 48 | maps_clear(&maps, release); 49 | maps_destruct(&maps, release); 50 | } 51 | 52 | int main() 53 | { 54 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_maps)}; 55 | 56 | return cmocka_run_group_tests(tests, NULL, NULL); 57 | } 58 | -------------------------------------------------------------------------------- /test/signal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | struct state 15 | { 16 | signal_t signal; 17 | uint64_t value; 18 | size_t calls; 19 | bool close; 20 | }; 21 | 22 | static void callback(reactor_event_t *event) 23 | { 24 | struct state *state = event->state; 25 | 26 | state->calls++; 27 | if (state->close) 28 | signal_close(&state->signal); 29 | } 30 | 31 | static void test_signal(__attribute__((unused)) void **arg) 32 | { 33 | struct state state = {0}; 34 | 35 | reactor_construct(); 36 | signal_construct(&state.signal, callback, &state); 37 | signal_open(&state.signal); 38 | signal_send(&state.signal); 39 | reactor_loop_once(); 40 | assert_int_equal(state.calls, 1); 41 | signal_close(&state.signal); 42 | signal_open(&state.signal); 43 | signal_send_sync(&state.signal); 44 | state.close = true; 45 | reactor_loop_once(); 46 | assert_int_equal(state.calls, 2); 47 | signal_destruct(&state.signal); 48 | reactor_destruct(); 49 | } 50 | 51 | int main() 52 | { 53 | const struct CMUnitTest tests[] = 54 | { 55 | cmocka_unit_test(test_signal), 56 | }; 57 | 58 | return cmocka_run_group_tests(tests, NULL, NULL); 59 | } 60 | -------------------------------------------------------------------------------- /src/reactor.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_H_INCLUDED 2 | #define REACTOR_H_INCLUDED 3 | 4 | #ifdef UNIT_TESTING 5 | extern void mock_assert(const int, const char* const, const char * const, const int); 6 | #undef assert 7 | #define assert(expression) \ 8 | mock_assert((int)(expression), #expression, __FILE__, __LINE__); 9 | #endif 10 | 11 | #define REACTOR_VERSION "0.9.2" 12 | #define REACTOR_VERSION_MAJOR 0 13 | #define REACTOR_VERSION_MINOR 9 14 | #define REACTOR_VERSION_PATCH 2 15 | 16 | #define reactor_likely(x) __builtin_expect(!!(x), 1) 17 | #define reactor_unlikely(x) __builtin_expect(!!(x), 0) 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | #include "reactor/data.h" 24 | #include "reactor/hash.h" 25 | #include "reactor/buffer.h" 26 | #include "reactor/list.h" 27 | #include "reactor/vector.h" 28 | #include "reactor/map.h" 29 | #include "reactor/mapi.h" 30 | #include "reactor/maps.h" 31 | #include "reactor/mapd.h" 32 | #include "reactor/string.h" 33 | #include "reactor/value.h" 34 | #include "reactor/encode.h" 35 | #include "reactor/decode.h" 36 | #include "reactor/pool.h" 37 | #include "reactor/reactor.h" 38 | #include "reactor/signal.h" 39 | #include "reactor/timeout.h" 40 | #include "reactor/notify.h" 41 | #include "reactor/network.h" 42 | #include "reactor/stream.h" 43 | #include "reactor/http.h" 44 | #include "reactor/server.h" 45 | #include "reactor/queue.h" 46 | #include "reactor/flow.h" 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif /* REACTOR_H_INCLUDED */ 53 | -------------------------------------------------------------------------------- /test/mapd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void release(mapd_entry_t *e) 12 | { 13 | free(data_base(e->key)); 14 | } 15 | 16 | void test_mapd(__attribute__((unused)) void **state) 17 | { 18 | mapd_t mapd; 19 | int i; 20 | void *p; 21 | char key[16]; 22 | 23 | mapd_construct(&mapd); 24 | 25 | for (i = 0; i < 2; i++) 26 | { 27 | p = malloc(5); 28 | printf("-> %p\n", p); 29 | strcpy(p, "test"); 30 | mapd_insert(&mapd, data_string(p), 42, release); 31 | } 32 | assert_true(mapd_at(&mapd, data_string("test")) == 42); 33 | assert_true(mapd_size(&mapd) == 1); 34 | 35 | mapd_erase(&mapd, data_string("test2"), release); 36 | assert_true(mapd_size(&mapd) == 1); 37 | mapd_erase(&mapd, data_string("test"), release); 38 | assert_true(mapd_size(&mapd) == 0); 39 | 40 | mapd_reserve(&mapd, 32); 41 | assert_true(mapd.map.elements_capacity == 64); 42 | 43 | for (i = 0; i < 100; i++) 44 | { 45 | snprintf(key, sizeof key, "test%d", i); 46 | p = malloc(strlen(key) + 1); 47 | strcpy(p, key); 48 | mapd_insert(&mapd, data_string(p), i, release); 49 | } 50 | mapd_clear(&mapd, release); 51 | mapd_destruct(&mapd, release); 52 | } 53 | 54 | int main() 55 | { 56 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_mapd)}; 57 | 58 | return cmocka_run_group_tests(tests, NULL, NULL); 59 | } 60 | -------------------------------------------------------------------------------- /src/reactor/timeout.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void timeout_timeout(reactor_event_t *); 6 | 7 | static void timeout_submit(timeout_t *timeout) 8 | { 9 | timeout->tv.tv_sec = timeout->time / 1000000000ULL; 10 | timeout->tv.tv_nsec = timeout->time % 1000000000ULL; 11 | timeout->timeout = reactor_timeout(timeout_timeout, timeout, &timeout->tv, 0, IORING_TIMEOUT_ABS | IORING_TIMEOUT_REALTIME); 12 | } 13 | 14 | static void timeout_timeout(reactor_event_t *event) 15 | { 16 | timeout_t *timeout = event->state; 17 | reactor_time_t time; 18 | 19 | time = timeout->time; 20 | timeout->timeout = 0; 21 | if (timeout->delay) 22 | { 23 | timeout->time += timeout->delay; 24 | timeout_submit(timeout); 25 | } 26 | reactor_call(&timeout->user, TIMEOUT_ALERT, (uintptr_t) &time); 27 | } 28 | 29 | void timeout_construct(timeout_t *timeout, reactor_callback_t *callback, void *state) 30 | { 31 | *timeout = (timeout_t) {.user = reactor_user_define(callback, state)}; 32 | } 33 | 34 | void timeout_destruct(timeout_t *timeout) 35 | { 36 | timeout_clear(timeout); 37 | } 38 | 39 | void timeout_set(timeout_t *timeout, reactor_time_t time, reactor_time_t delay) 40 | { 41 | timeout_clear(timeout); 42 | timeout->time = time; 43 | timeout->delay = delay; 44 | timeout_submit(timeout); 45 | } 46 | 47 | void timeout_clear(timeout_t *timeout) 48 | { 49 | if (timeout->timeout) 50 | { 51 | reactor_cancel(timeout->timeout, NULL, NULL); 52 | timeout->timeout = 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/reactor/queue.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_QUEUE_H_INCLUDED 2 | #define REACTOR_QUEUE_H_INCLUDED 3 | 4 | #include 5 | 6 | enum 7 | { 8 | QUEUE_CONSUMER_POP 9 | }; 10 | 11 | typedef struct queue queue_t; 12 | typedef struct queue_consumer queue_consumer_t; 13 | typedef struct queue_producer queue_producer_t; 14 | 15 | struct queue 16 | { 17 | int fd[2]; 18 | size_t element_size; 19 | }; 20 | 21 | struct queue_producer 22 | { 23 | queue_t *queue; 24 | buffer_t output; 25 | size_t output_offset; 26 | buffer_t output_queued; 27 | reactor_t write; 28 | }; 29 | 30 | struct queue_consumer 31 | { 32 | reactor_user_t user; 33 | queue_t *queue; 34 | buffer_t input; 35 | reactor_t read; 36 | bool *abort; 37 | }; 38 | 39 | void queue_construct(queue_t *, size_t); 40 | void queue_destruct(queue_t *); 41 | 42 | void queue_producer_construct(queue_producer_t *); 43 | void queue_producer_destruct(queue_producer_t *); 44 | void queue_producer_open(queue_producer_t *, queue_t *); 45 | void queue_producer_push(queue_producer_t *, void *); 46 | void queue_producer_push_sync(queue_producer_t *, void *); 47 | void queue_producer_close(queue_producer_t *); 48 | 49 | void queue_consumer_construct(queue_consumer_t *, reactor_callback_t *, void *); 50 | void queue_consumer_destruct(queue_consumer_t *); 51 | void queue_consumer_open(queue_consumer_t *, queue_t *, size_t); 52 | void queue_consumer_pop(queue_consumer_t *); 53 | void *queue_consumer_pop_sync(queue_consumer_t *); 54 | void queue_consumer_close(queue_consumer_t *); 55 | 56 | #endif /* REACTOR_QUEUE_H_INCLUDED */ 57 | -------------------------------------------------------------------------------- /src/reactor/list.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_LIST_H_INCLUDED 2 | #define REACTOR_LIST_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #define list_foreach(l, o) for ((o) = list_front(l); (o) != list_end(l); (o) = list_next(o)) 8 | #define list_foreach_reverse(l, o) for ((o) = list_back(l); (o) != list_end(l); (o) = list_previous(o)) 9 | 10 | typedef void list_release_t(void *); 11 | typedef int list_compare_t(const void *, const void *); 12 | typedef struct list_item list_item_t; 13 | typedef struct list list_t; 14 | 15 | struct list 16 | { 17 | list_item_t *next; 18 | list_item_t *previous; 19 | }; 20 | 21 | struct list_item 22 | { 23 | list_t list; 24 | char object[]; 25 | }; 26 | 27 | /* constructor/destructor */ 28 | void list_construct(list_t *); 29 | void list_destruct(list_t *, list_release_t *); 30 | 31 | /* iterators */ 32 | void *list_next(const void *); 33 | void *list_previous(const void *); 34 | 35 | /* capacity */ 36 | int list_empty(const list_t *); 37 | 38 | /* object access */ 39 | void *list_front(const list_t *); 40 | void *list_back(const list_t *); 41 | void *list_end(const list_t *); 42 | 43 | /* modifiers */ 44 | void *list_push_front(list_t *, const void *, size_t); 45 | void *list_push_back(list_t *, const void *, size_t); 46 | void *list_insert(void *, const void *, size_t); 47 | void list_splice(void *, void *); 48 | void list_detach(void *); 49 | void list_erase(void *, list_release_t *); 50 | void list_clear(list_t *, list_release_t *); 51 | 52 | /* operations */ 53 | void *list_find(const list_t *, list_compare_t *, const void *); 54 | 55 | #endif /* REACTOR_LIST_H_INCLUDED */ 56 | -------------------------------------------------------------------------------- /test/network_resolve.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "reactor.h" 14 | 15 | struct state 16 | { 17 | uint64_t value; 18 | size_t calls; 19 | }; 20 | 21 | static void callback(reactor_event_t *event) 22 | { 23 | struct state *state = event->state; 24 | 25 | state->calls++; 26 | } 27 | 28 | static void test_resolve(__attribute__((unused)) void **arg) 29 | { 30 | struct state state = {0}; 31 | network_t id; 32 | 33 | reactor_construct(); 34 | network_expire(1000000ULL); 35 | 36 | id = network_resolve(callback, &state, "127.0.0.1"); 37 | network_cancel(id); 38 | id = network_resolve(callback, &state, "fail.localdomain"); 39 | network_cancel(id); 40 | reactor_loop(); 41 | 42 | network_resolve(callback, &state, "127.0.0.1"); 43 | network_resolve(callback, &state, "fail.localdomain"); 44 | network_resolve(callback, &state, "fail.localdomain"); 45 | reactor_loop(); 46 | 47 | network_resolve(callback, &state, NULL); 48 | reactor_loop(); 49 | 50 | /* reuse cached */ 51 | network_resolve(callback, &state, NULL); 52 | reactor_loop(); 53 | 54 | /* expired */ 55 | usleep(10000); 56 | network_resolve(callback, &state, NULL); 57 | reactor_loop(); 58 | 59 | reactor_destruct(); 60 | 61 | assert_int_equal(state.calls, 6); 62 | } 63 | 64 | int main() 65 | { 66 | const struct CMUnitTest tests[] = 67 | { 68 | cmocka_unit_test(test_resolve) 69 | }; 70 | 71 | return cmocka_run_group_tests(tests, NULL, NULL); 72 | } 73 | -------------------------------------------------------------------------------- /example/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | _Atomic int counter = 0; 7 | 8 | static void sender(reactor_event_t *event) 9 | { 10 | queue_t *queue = event->state; 11 | queue_producer_t producer; 12 | int i; 13 | 14 | if (event->type == REACTOR_RETURN) 15 | return; 16 | 17 | reactor_construct(); 18 | queue_producer_construct(&producer); 19 | queue_producer_open(&producer, queue); 20 | for (i = 0; i < 100; i ++) 21 | queue_producer_push(&producer, (int []){42}); 22 | reactor_loop(); 23 | queue_producer_destruct(&producer); 24 | reactor_destruct(); 25 | } 26 | 27 | static void receiver_event(reactor_event_t *event) 28 | { 29 | int *element = (int *) event->data; 30 | 31 | assert(*element == 42); 32 | if (__sync_add_and_fetch(&counter, 1) == 10000) 33 | exit(0); 34 | } 35 | 36 | static void receiver(reactor_event_t *event) 37 | { 38 | queue_t *queue = event->state; 39 | queue_consumer_t consumer; 40 | 41 | if (event->type == REACTOR_RETURN) 42 | return; 43 | 44 | reactor_construct(); 45 | queue_consumer_construct(&consumer, receiver_event, &consumer); 46 | queue_consumer_open(&consumer, queue, 10); 47 | queue_consumer_pop(&consumer); 48 | reactor_loop(); 49 | queue_consumer_destruct(&consumer); 50 | reactor_destruct(); 51 | } 52 | 53 | int main() 54 | { 55 | queue_t queue; 56 | int i; 57 | 58 | reactor_construct(); 59 | queue_construct(&queue, sizeof(int)); 60 | for (i = 0; i < 100; i++) 61 | reactor_async(sender, &queue); 62 | for (i = 0; i < 100; i++) 63 | reactor_async(receiver, &queue); 64 | reactor_loop(); 65 | queue_destruct(&queue); 66 | reactor_destruct(); 67 | } 68 | -------------------------------------------------------------------------------- /src/reactor/vector.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_VECTOR_H_INCLUDED 2 | #define REACTOR_VECTOR_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct vector vector_t; 9 | typedef void vector_release_t(void *); 10 | typedef int vector_compare_t(const void *, const void *); 11 | 12 | struct vector 13 | { 14 | buffer_t buffer; 15 | size_t object_size; 16 | }; 17 | 18 | /* constructor/destructor */ 19 | 20 | void vector_construct(vector_t *, size_t); 21 | void vector_destruct(vector_t *, vector_release_t); 22 | 23 | /* capacity */ 24 | 25 | size_t vector_size(const vector_t *); 26 | size_t vector_capacity(const vector_t *); 27 | bool vector_empty(const vector_t *); 28 | void vector_reserve(vector_t *, size_t); 29 | void vector_shrink_to_fit(vector_t *); 30 | 31 | /* element access */ 32 | 33 | void *vector_at(const vector_t *, size_t); 34 | void *vector_front(const vector_t *); 35 | void *vector_back(const vector_t *); 36 | void *vector_base(const vector_t *); 37 | 38 | /* modifiers */ 39 | 40 | void vector_insert(vector_t *, size_t, const void *); 41 | void vector_insert_range(vector_t *, size_t, const void *, const void *); 42 | void vector_insert_fill(vector_t *, size_t, size_t, const void *); 43 | void vector_erase(vector_t *, size_t, vector_release_t *); 44 | void vector_erase_range(vector_t *, size_t, size_t, vector_release_t *); 45 | void vector_push_back(vector_t *, const void *); 46 | void vector_pop_back(vector_t *, vector_release_t *); 47 | void vector_clear(vector_t *, vector_release_t *); 48 | 49 | /* operations */ 50 | 51 | void vector_sort(vector_t *, vector_compare_t *); 52 | 53 | #endif /* REACTOR_VECTOR_H_INCLUDED */ 54 | -------------------------------------------------------------------------------- /test/flow_async.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "reactor.h" 11 | 12 | static void test_start(__attribute__((unused)) void **arg) 13 | { 14 | flow_t flow; 15 | 16 | reactor_construct(); 17 | flow_construct(&flow, NULL, NULL); 18 | flow_start(&flow); 19 | 20 | reactor_loop(); 21 | 22 | flow_stop(&flow); 23 | flow_destruct(&flow); 24 | reactor_destruct(); 25 | } 26 | 27 | static void test_node(__attribute__((unused)) void **arg) 28 | { 29 | flow_t flow; 30 | 31 | reactor_construct(); 32 | flow_construct(&flow, NULL, NULL); 33 | flow_search(&flow, "test/conf"); 34 | 35 | assert_true(flow_node(&flow, "s1", "module1", "s1", value_null()) == 0); 36 | assert_true(flow_node(&flow, "s2", "module1", NULL, value_null()) == 0); 37 | assert_true(flow_node(&flow, "d1", "module2", NULL, value_null()) == 0); 38 | assert_true(flow_node(&flow, "d2", "module2", NULL, value_null()) == 0); 39 | flow_connect(&flow, "s1", "s1", value_null()); 40 | flow_connect(&flow, "s1", "d1", value_null()); 41 | flow_connect(&flow, "s2", "d1", value_null()); 42 | flow_connect(&flow, "s1", "d2", value_null()); 43 | flow_connect(&flow, "s2", "d2", value_null()); 44 | flow_connect(&flow, "d1", "d1", value_null()); 45 | flow_start(&flow); 46 | 47 | reactor_loop(); 48 | 49 | flow_stop(&flow); 50 | flow_destruct(&flow); 51 | reactor_destruct(); 52 | } 53 | 54 | int main() 55 | { 56 | const struct CMUnitTest tests[] = 57 | { 58 | cmocka_unit_test(test_start), 59 | cmocka_unit_test(test_node) 60 | }; 61 | 62 | return cmocka_run_group_tests(tests, NULL, NULL); 63 | } 64 | -------------------------------------------------------------------------------- /test/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void test_hash(__attribute__((unused)) void **state) 9 | { 10 | int i; 11 | struct 12 | { 13 | char *in; 14 | uint64_t out; 15 | } strings[] = {{"", 0x9ae16a3b2f90404f}, 16 | {"1", 0x811f023a122d0be1}, 17 | {"1234", 0xc3fa0b46e8adcae}, 18 | {"12345678", 0x2f99d2664a0fb6ea}, 19 | {"1234567890123456", 0xef3d9afd22778424}, 20 | {"12345678901234567890123456789012", 0xf8317d59683e31b1}, 21 | {"1234567890123456789012345678901234567890123456789012345678901234", 0x78a95a0d9788b255}, 22 | {"12345678901234567890123456789012345678901234567890123456789012345678901" 23 | "23456789012345678901234567890123456789012" 24 | "3456789012345678", 25 | 0x6085a2475352e7f9}, 26 | {"12345678901234567890123456789012345678901234567890123456789012345678901" 27 | "23456789012345678901234567890123456789012" 28 | "34567890123456789", 29 | 0xed4501398023b759}, 30 | {NULL, 0}}; 31 | struct 32 | { 33 | uint64_t in; 34 | uint64_t out; 35 | } integers[] = {{1ULL, 12994781566227106604ULL}, {12994781566227106604ULL, 9038243705893100514ULL}, {0, 0}}; 36 | 37 | for (i = 0; strings[i].in; i++) 38 | assert_true(hash_data(data_string(strings[i].in)) == strings[i].out); 39 | 40 | for (i = 0; integers[i].in; i++) 41 | assert_true(hash_uint64(integers[i].in) == integers[i].out); 42 | } 43 | 44 | int main() 45 | { 46 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_hash)}; 47 | 48 | return cmocka_run_group_tests(tests, NULL, NULL); 49 | } 50 | -------------------------------------------------------------------------------- /src/reactor/signal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void signal_read(reactor_event_t *reactor_event) 9 | { 10 | signal_t *signal = reactor_event->state; 11 | bool abort = false; 12 | 13 | signal->read = 0; 14 | assert(reactor_event->data == sizeof signal->counter); 15 | signal->abort = &abort; 16 | reactor_call(&signal->user, SIGNAL_RECEIVE, signal->counter); 17 | if (abort) 18 | return; 19 | signal->abort = NULL; 20 | signal->read = reactor_read(signal_read, signal, signal->fd, &signal->counter, sizeof signal->counter, 0); 21 | } 22 | 23 | void signal_construct(signal_t *signal, reactor_callback_t *callback, void *state) 24 | { 25 | *signal = (signal_t) {.user = reactor_user_define(callback, state), .fd = -1}; 26 | } 27 | 28 | void signal_open(signal_t *signal) 29 | { 30 | signal->fd = eventfd(0, signal->counter); 31 | signal->read = reactor_read(signal_read, signal, signal->fd, &signal->counter, sizeof signal->counter, 0); 32 | } 33 | 34 | void signal_send(signal_t *signal) 35 | { 36 | static const uint64_t flag = 1; 37 | 38 | (void) reactor_write(NULL, NULL, signal->fd, &flag, sizeof flag, 0); 39 | } 40 | 41 | void signal_send_sync(signal_t *signal) 42 | { 43 | ssize_t n; 44 | 45 | n = write(signal->fd, (uint64_t[]) {1}, sizeof (uint64_t)); 46 | assert(n == sizeof (uint64_t)); 47 | } 48 | 49 | void signal_close(signal_t *signal) 50 | { 51 | if (signal->fd == -1) 52 | return; 53 | 54 | if (signal->abort) 55 | *signal->abort = true; 56 | if (signal->read) 57 | reactor_cancel(signal->read, NULL, NULL); 58 | (void) reactor_close(NULL, NULL, signal->fd); 59 | signal->fd = -1; 60 | } 61 | 62 | void signal_destruct(signal_t *signal) 63 | { 64 | signal_close(signal); 65 | } 66 | -------------------------------------------------------------------------------- /src/reactor/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_BUFFER_H_INCLUDED 2 | #define REACTOR_BUFFER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct buffer buffer_t; 11 | 12 | struct buffer 13 | { 14 | data_t data; 15 | size_t capacity; 16 | }; 17 | 18 | /* constructor/destructor */ 19 | 20 | void buffer_construct(buffer_t *); 21 | void buffer_destruct(buffer_t *); 22 | void *buffer_deconstruct(buffer_t *); 23 | 24 | /* capacity */ 25 | 26 | size_t buffer_size(const buffer_t *); 27 | size_t buffer_capacity(const buffer_t *); 28 | bool buffer_empty(const buffer_t *); 29 | void buffer_reserve(buffer_t *, size_t); 30 | void buffer_resize(buffer_t *, size_t); 31 | void buffer_compact(buffer_t *); 32 | data_t buffer_allocate(buffer_t *, size_t); 33 | 34 | /* modifiers */ 35 | 36 | void buffer_insert(buffer_t *restrict, size_t, const data_t); 37 | void buffer_insert_fill(buffer_t *restrict, size_t, size_t, const data_t); 38 | void buffer_prepend(buffer_t *restrict, const data_t); 39 | void buffer_append(buffer_t *restrict, const data_t); 40 | void buffer_erase(buffer_t *restrict, size_t, size_t); 41 | void buffer_clear(buffer_t *); 42 | void buffer_read(buffer_t *restrict, FILE *restrict); 43 | bool buffer_load(buffer_t *, const char *); 44 | bool buffer_loadz(buffer_t *restrict, const char *); 45 | void buffer_switch(buffer_t *restrict, buffer_t *restrict); 46 | 47 | /* element access */ 48 | 49 | data_t buffer_data(const buffer_t *); 50 | void *buffer_base(const buffer_t *); 51 | void *buffer_at(const buffer_t *, size_t); 52 | void *buffer_end(const buffer_t *); 53 | 54 | /* operations */ 55 | 56 | void buffer_write(const buffer_t *restrict, FILE *restrict); 57 | bool buffer_save(const buffer_t *restrict, const char *); 58 | 59 | #endif /* REACTOR_BUFFER_H_INCLUDED */ 60 | -------------------------------------------------------------------------------- /src/reactor/mapi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void set(void *p1, const void *p2) 8 | { 9 | mapi_entry_t *a = p1; 10 | const mapi_entry_t *b = p2; 11 | 12 | *a = b ? *b : (mapi_entry_t) {0}; 13 | } 14 | 15 | static int equal(const void *p1, const void *p2) 16 | { 17 | const mapi_entry_t *a = p1, *b = p2; 18 | 19 | return b ? a->key == b->key : a->key == 0; 20 | } 21 | 22 | static size_t hash(const void *p) 23 | { 24 | const mapi_entry_t *a = p; 25 | 26 | return hash_uint64(a->key); 27 | } 28 | 29 | /* constructor/destructor */ 30 | 31 | void mapi_construct(mapi_t *mapi) 32 | { 33 | map_construct(&mapi->map, sizeof(mapi_entry_t), set); 34 | } 35 | 36 | void mapi_destruct(mapi_t *mapi, mapi_release_t *release) 37 | { 38 | map_destruct(&mapi->map, equal, (map_release_t *) release); 39 | } 40 | 41 | /* capacity */ 42 | 43 | size_t mapi_size(const mapi_t *mapi) 44 | { 45 | return map_size(&mapi->map); 46 | } 47 | 48 | void mapi_reserve(mapi_t *mapi, size_t size) 49 | { 50 | map_reserve(&mapi->map, size, hash, set, equal); 51 | } 52 | 53 | /* element access */ 54 | 55 | uintptr_t mapi_at(const mapi_t *mapi, uintptr_t key) 56 | { 57 | return ((mapi_entry_t *) map_at(&mapi->map, (mapi_entry_t[]) {{.key = key}}, hash, equal))->value; 58 | } 59 | 60 | /* modifiers */ 61 | 62 | void mapi_insert(mapi_t *mapi, uintptr_t key, uintptr_t value, mapi_release_t *release) 63 | { 64 | map_insert(&mapi->map, (mapi_entry_t[]) {{.key = key, .value = value}}, hash, set, equal, (map_release_t *) release); 65 | } 66 | 67 | void mapi_erase(mapi_t *mapi, uintptr_t key, mapi_release_t *release) 68 | { 69 | map_erase(&mapi->map, (mapi_entry_t[]) {{.key = key}}, hash, set, equal, (map_release_t *) release); 70 | } 71 | 72 | void mapi_clear(mapi_t *mapi, mapi_release_t *release) 73 | { 74 | map_clear(&mapi->map, set, equal, (map_release_t *) release); 75 | } 76 | -------------------------------------------------------------------------------- /src/reactor/mapd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void set(void *p1, const void *p2) 8 | { 9 | mapd_entry_t *a = p1; 10 | const mapd_entry_t *b = p2; 11 | 12 | *a = b ? *b : (mapd_entry_t) {0}; 13 | } 14 | 15 | static int equal(const void *p1, const void *p2) 16 | { 17 | const mapd_entry_t *a = p1, *b = p2; 18 | 19 | return b ? data_equal(a->key, b->key) : data_base(a->key) == NULL; 20 | } 21 | 22 | static size_t hash(const void *p) 23 | { 24 | const mapd_entry_t *a = p; 25 | 26 | return hash_data(a->key); 27 | } 28 | 29 | /* constructor/destructor */ 30 | 31 | void mapd_construct(mapd_t *mapd) 32 | { 33 | map_construct(&mapd->map, sizeof(mapd_entry_t), set); 34 | } 35 | 36 | void mapd_destruct(mapd_t *mapd, mapd_release_t *release) 37 | { 38 | map_destruct(&mapd->map, equal, (map_release_t *) release); 39 | } 40 | 41 | /* capacity */ 42 | 43 | size_t mapd_size(const mapd_t *mapd) 44 | { 45 | return map_size(&mapd->map); 46 | } 47 | 48 | void mapd_reserve(mapd_t *mapd, size_t size) 49 | { 50 | map_reserve(&mapd->map, size, hash, set, equal); 51 | } 52 | 53 | /* element access */ 54 | 55 | uintptr_t mapd_at(const mapd_t *mapd, const data_t key) 56 | { 57 | return ((mapd_entry_t *) map_at(&mapd->map, (mapd_entry_t[]) {{.key = key}}, hash, equal))->value; 58 | } 59 | 60 | /* modifiers */ 61 | 62 | void mapd_insert(mapd_t *mapd, const data_t key, uintptr_t value, mapd_release_t *release) 63 | { 64 | map_insert(&mapd->map, (mapd_entry_t[]) {{.key = key, .value = value}}, hash, set, equal, (map_release_t *) release); 65 | } 66 | 67 | void mapd_erase(mapd_t *mapd, const data_t key, mapd_release_t *release) 68 | { 69 | map_erase(&mapd->map, (mapd_entry_t[]) {{.key = key}}, hash, set, equal, (map_release_t *) release); 70 | } 71 | 72 | void mapd_clear(mapd_t *mapd, mapd_release_t *release) 73 | { 74 | map_clear(&mapd->map, set, equal, (map_release_t *) release); 75 | } 76 | -------------------------------------------------------------------------------- /src/reactor/maps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void set(void *p1, const void *p2) 8 | { 9 | maps_entry_t *a = p1; 10 | const maps_entry_t *b = p2; 11 | 12 | *a = b ? *b : (maps_entry_t) {0}; 13 | } 14 | 15 | static int equal(const void *p1, const void *p2) 16 | { 17 | const maps_entry_t *a = p1, *b = p2; 18 | 19 | return b ? strcmp(a->key, b->key) == 0 : a->key == NULL; 20 | } 21 | 22 | static size_t hash(const void *p) 23 | { 24 | const maps_entry_t *a = p; 25 | 26 | return hash_data(data_string(a->key)); 27 | } 28 | 29 | /* constructor/destructor */ 30 | 31 | void maps_construct(maps_t *maps) 32 | { 33 | map_construct(&maps->map, sizeof(maps_entry_t), set); 34 | } 35 | 36 | void maps_destruct(maps_t *maps, maps_release_t *release) 37 | { 38 | map_destruct(&maps->map, equal, (map_release_t *) release); 39 | } 40 | 41 | /* capacity */ 42 | 43 | size_t maps_size(const maps_t *maps) 44 | { 45 | return map_size(&maps->map); 46 | } 47 | 48 | void maps_reserve(maps_t *maps, size_t size) 49 | { 50 | map_reserve(&maps->map, size, hash, set, equal); 51 | } 52 | 53 | /* element access */ 54 | 55 | uintptr_t maps_at(const maps_t *maps, const char *key) 56 | { 57 | return ((maps_entry_t *) map_at(&maps->map, (maps_entry_t[]) {{.key = key}}, hash, equal))->value; 58 | } 59 | 60 | /* modifiers */ 61 | 62 | void maps_insert(maps_t *maps, const char *key, uintptr_t value, maps_release_t *release) 63 | { 64 | map_insert(&maps->map, (maps_entry_t[]) {{.key = key, .value = value}}, hash, set, equal, (map_release_t *) release); 65 | } 66 | 67 | void maps_erase(maps_t *maps, const char *key, maps_release_t *release) 68 | { 69 | map_erase(&maps->map, (maps_entry_t[]) {{.key = key}}, hash, set, equal, (map_release_t *) release); 70 | } 71 | 72 | void maps_clear(maps_t *maps, maps_release_t *release) 73 | { 74 | map_clear(&maps->map, set, equal, (map_release_t *) release); 75 | } 76 | -------------------------------------------------------------------------------- /example/shufflecat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | struct state 8 | { 9 | list_t files; 10 | size_t output_offset; 11 | }; 12 | 13 | struct file 14 | { 15 | struct state *state; 16 | const char *name; 17 | int fd; 18 | uint8_t byte; 19 | size_t input_offset; 20 | }; 21 | 22 | static void file_read(reactor_event_t *); 23 | 24 | static void file_write(reactor_event_t *event) 25 | { 26 | struct file *file = event->state; 27 | 28 | reactor_read(file_read, file, file->fd, &file->byte, 1, file->input_offset); 29 | } 30 | 31 | static void file_read(reactor_event_t *event) 32 | { 33 | struct file *file = event->state; 34 | int result = event->data; 35 | 36 | if (result <= 0) 37 | { 38 | reactor_close(NULL, NULL, file->fd); 39 | return; 40 | } 41 | 42 | file->input_offset++; 43 | reactor_write(file_write, file, STDOUT_FILENO, &file->byte, 1, file->state->output_offset); 44 | file->state->output_offset++; 45 | } 46 | 47 | static void file_open(reactor_event_t *event) 48 | { 49 | struct file *file = event->state; 50 | int result = event->data; 51 | 52 | if (result <= 0) 53 | { 54 | fprintf(stderr, "error: open %s: %s\n", file->name, strerror(-result)); 55 | return; 56 | } 57 | 58 | file->fd = result; 59 | reactor_read(file_read, file, file->fd, &file->byte, 1, file->input_offset); 60 | } 61 | 62 | void add_file(struct state *state, char *name) 63 | { 64 | struct file *file; 65 | 66 | file = list_push_back(&state->files, NULL, sizeof *file); 67 | file->state = state; 68 | file->name = name; 69 | file->input_offset = 0; 70 | reactor_openat(file_open, file, AT_FDCWD, file->name, O_RDONLY, 0); 71 | } 72 | 73 | int main(int argc, char **argv) 74 | { 75 | struct state state = {0}; 76 | int i; 77 | 78 | list_construct(&state.files); 79 | reactor_construct(); 80 | for (i = 1; i < argc; i++) 81 | add_file(&state, argv[i]); 82 | reactor_loop(); 83 | reactor_destruct(); 84 | list_destruct(&state.files, NULL); 85 | } 86 | -------------------------------------------------------------------------------- /example/scan.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct state 5 | { 6 | timeout_t timeout; 7 | list_t ports; 8 | int ref; 9 | }; 10 | 11 | struct port 12 | { 13 | struct state *state; 14 | int port; 15 | bool open; 16 | network_t connect; 17 | }; 18 | 19 | const int top[] = { 20 | 21,22,23,25,26,53,80,81,110,111,113,135,139,143,179,199,443,445,465,514,515, 21 | 548,554,587,646,993,995,1025,1026,1027,1433,1720,1723,2000,2001,3306,3389, 22 | 5060,5666,5900,6001,8000,8008,8080,8443,8888,10000,32768,49152,49154 23 | }; 24 | 25 | static void callback(reactor_event_t *event) 26 | { 27 | struct port *port = event->state; 28 | 29 | port->connect = 0; 30 | if (event->type == NETWORK_CONNECT) 31 | { 32 | port->open = true; 33 | reactor_close(NULL, NULL, event->data); 34 | } 35 | port->state->ref--; 36 | if (!port->state->ref) 37 | timeout_destruct(&port->state->timeout); 38 | } 39 | 40 | static void timeout(reactor_event_t *event) 41 | { 42 | struct state *state = event->state; 43 | struct port *port; 44 | 45 | list_foreach(&state->ports, port) 46 | if (port->connect) 47 | network_cancel(port->connect); 48 | timeout_destruct(&state->timeout); 49 | } 50 | 51 | void scan(char *host) 52 | { 53 | struct state state = {0}; 54 | struct port *port; 55 | size_t i; 56 | 57 | timeout_construct(&state.timeout, timeout, &state); 58 | timeout_set(&state.timeout, reactor_now() + 1000000000, 0); 59 | list_construct(&state.ports); 60 | for (i = 0; i < sizeof top / sizeof *top; i++) 61 | { 62 | port = list_push_back(&state.ports, NULL, sizeof (struct port)); 63 | port->port = top[i]; 64 | port->state = &state; 65 | port->connect = network_connect(callback, port, host, top[i]); 66 | state.ref++; 67 | } 68 | reactor_loop(); 69 | printf("[%s]", host); 70 | list_foreach(&state.ports, port) 71 | if (port->open) 72 | printf(" %d", port->port); 73 | printf("\n"); 74 | list_destruct(&state.ports, NULL); 75 | } 76 | 77 | int main(int argc, char **argv) 78 | { 79 | int i; 80 | 81 | reactor_construct(); 82 | for (i = 1; i < argc; i ++) 83 | scan(argv[i]); 84 | reactor_destruct(); 85 | } 86 | -------------------------------------------------------------------------------- /test/flow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "reactor.h" 11 | 12 | static void test_start(__attribute__((unused)) void **arg) 13 | { 14 | flow_t flow; 15 | 16 | reactor_construct(); 17 | flow_construct(&flow, NULL, NULL); 18 | flow_start(&flow); 19 | 20 | reactor_loop(); 21 | 22 | flow_stop(&flow); 23 | flow_destruct(&flow); 24 | reactor_destruct(); 25 | } 26 | 27 | static void test_node(__attribute__((unused)) void **arg) 28 | { 29 | flow_t flow; 30 | 31 | reactor_construct(); 32 | flow_construct(&flow, NULL, NULL); 33 | flow_search(&flow, "test/conf"); 34 | 35 | /* not found */ 36 | assert_false(flow_node(&flow, "s1", "xxxx", "
", value_null()) == 0); 37 | 38 | /* match */ 39 | assert_true(flow_module(&flow, "module1") == 0); 40 | assert_false(flow_module(&flow, "modulex") == 0); 41 | flow_module(&flow, "module2"); 42 | assert_true(flow_node(&flow, "module2a", NULL, "
", value_null()) == 0); 43 | assert_false(flow_node(&flow, "module", NULL, "
", value_null()) == 0); 44 | 45 | assert_true(flow_node(&flow, "s1", "module1", "
", value_null()) == 0); 46 | assert_true(flow_node(&flow, "s2", "module1", "
", value_null()) == 0); 47 | assert_true(flow_node(&flow, "d1", "module2", "
", value_null()) == 0); 48 | assert_true(flow_node(&flow, "d2", "module2", "
", value_null()) == 0); 49 | flow_connect(&flow, "s1", "s1", value_null()); 50 | flow_connect(&flow, "s1", "d1", value_null()); 51 | flow_connect(&flow, "s2", "d1", value_null()); 52 | flow_connect(&flow, "s1", "d2", value_null()); 53 | flow_connect(&flow, "s2", "d2", value_null()); 54 | flow_connect(&flow, "d1", "d1", value_null()); 55 | assert_false(flow_connect(&flow, "d1", "dx", value_null()) == 0); 56 | assert_false(flow_connect(&flow, "dx", "d2", value_null()) == 0); 57 | flow_start(&flow); 58 | 59 | reactor_loop(); 60 | 61 | flow_stop(&flow); 62 | flow_destruct(&flow); 63 | reactor_destruct(); 64 | } 65 | 66 | int main() 67 | { 68 | const struct CMUnitTest tests[] = 69 | { 70 | cmocka_unit_test(test_start), 71 | cmocka_unit_test(test_node) 72 | }; 73 | 74 | return cmocka_run_group_tests(tests, NULL, NULL); 75 | } 76 | -------------------------------------------------------------------------------- /test/vector.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static void release(void *object) 10 | { 11 | free(*(char **) object); 12 | } 13 | 14 | static int compare(const void *p1, const void *p2) 15 | { 16 | const char *s1 = *(char **) p1, *s2 = *(char **) p2; 17 | 18 | return strcmp(s1, s2); 19 | } 20 | 21 | void test_vector(__attribute__((unused)) void **state) 22 | { 23 | vector_t v; 24 | char *a[] = {"a", "list", "of", "string", "pointers"}, *s; 25 | size_t a_len, i; 26 | 27 | vector_construct(&v, sizeof(char *)); 28 | assert_true(vector_empty(&v)); 29 | assert_int_equal(vector_size(&v), 0); 30 | assert_int_equal(vector_capacity(&v), 0); 31 | 32 | vector_reserve(&v, 1024); 33 | assert_int_equal(vector_capacity(&v), 1024); 34 | vector_shrink_to_fit(&v); 35 | assert_int_equal(vector_capacity(&v), 0); 36 | 37 | vector_insert_fill(&v, 0, 5, (char *[]) {"foo"}); 38 | for (i = 0; i < 5; i++) 39 | assert_string_equal("foo", *(char **) vector_at(&v, i)); 40 | vector_erase_range(&v, 0, 5, NULL); 41 | 42 | a_len = sizeof a / sizeof a[0]; 43 | vector_insert_range(&v, 0, &a[0], &a[a_len]); 44 | for (i = 0; i < a_len; i++) 45 | assert_string_equal(a[i], *(char **) vector_at(&v, i)); 46 | 47 | assert_string_equal(a[0], *(char **) vector_front(&v)); 48 | assert_string_equal(a[a_len - 1], *(char **) vector_back(&v)); 49 | vector_erase(&v, 0, NULL); 50 | assert_string_equal(a[1], *(char **) vector_front(&v)); 51 | 52 | vector_push_back(&v, (char *[]) {"pushed"}); 53 | assert_string_equal("pushed", *(char **) vector_back(&v)); 54 | vector_pop_back(&v, NULL); 55 | assert_string_equal(a[a_len - 1], *(char **) vector_back(&v)); 56 | vector_clear(&v, NULL); 57 | 58 | for (i = 0; i < a_len; i++) 59 | { 60 | s = malloc(strlen(a[i]) + 1); 61 | strcpy(s, a[i]); 62 | vector_insert(&v, i, &s); 63 | } 64 | for (i = 0; i < a_len; i++) 65 | assert_string_equal(a[i], *(char **) vector_at(&v, i)); 66 | 67 | vector_sort(&v, compare); 68 | assert_string_equal(*(char **) vector_at(&v, 3), "pointers"); 69 | assert_string_equal(*(char **) vector_at(&v, 4), "string"); 70 | 71 | vector_erase(&v, 0, release); 72 | assert_string_equal(a[1], *(char **) vector_front(&v)); 73 | 74 | vector_destruct(&v, release); 75 | } 76 | 77 | int main() 78 | { 79 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_vector)}; 80 | 81 | return cmocka_run_group_tests(tests, NULL, NULL); 82 | } 83 | -------------------------------------------------------------------------------- /example/mpmc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct q 10 | { 11 | int fd[2]; 12 | int n; 13 | _Atomic int producer; 14 | _Atomic int consumer; 15 | _Atomic int count; 16 | }; 17 | 18 | int producer(void *arg) 19 | { 20 | struct q *q = arg; 21 | int i, t, id = __sync_add_and_fetch(&q->producer, 1); 22 | char data[q->n * 256]; 23 | ssize_t n; 24 | 25 | for (i = 0; i < q->n * 256; i++) 26 | data[i] = i % q->n; 27 | 28 | t = 1; 29 | for (i = 0; i < 1000;) 30 | { 31 | n = write(q->fd[1], data, q->n * t); 32 | //n = send(q->fd[1], data, q->n, 0); 33 | if (n != q->n * t) 34 | { 35 | printf("send %d -> %ld\n", q->n, n); 36 | err(1, "send"); 37 | } 38 | i += t; 39 | } 40 | 41 | (void) id; 42 | //printf("[p%d] done\n", id); 43 | return 0; 44 | } 45 | 46 | int consumer(void *arg) 47 | { 48 | static int calls = 0; 49 | struct q *q = arg; 50 | int i, id = __sync_add_and_fetch(&q->consumer, 1); 51 | char data[q->n * 1]; 52 | ssize_t n; 53 | 54 | while (1) 55 | { 56 | //n = recv(q->fd[0], data, sizeof data, 0); 57 | n = read(q->fd[0], data, sizeof data); 58 | calls++; 59 | if (n % q->n != 0) 60 | { 61 | printf("read %ld (align %d)\n", n, q->n); 62 | exit(1); 63 | } 64 | for (i = 0; i < n; i++) 65 | assert(data[i] == i % q->n); 66 | i = __sync_add_and_fetch(&q->count, n / q->n); 67 | if (i == 1000000) 68 | { 69 | printf("id %d done, %d calls\n", id, calls); 70 | exit(0); 71 | } 72 | //assert(n == q->n); 73 | } 74 | 75 | //printf("[c%d] done\n", id); 76 | return 0; 77 | } 78 | 79 | int main() 80 | { 81 | struct q q = {.n = 8}; 82 | thrd_t t; 83 | int i; 84 | 85 | assert(pipe(q.fd) == 0); 86 | //assert(socketpair(AF_UNIX, SOCK_STREAM, 0, q.fd) == 0); 87 | //assert(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, q.fd) == 0); 88 | //assert(socketpair(AF_UNIX, SOCK_DGRAM, 0, q.fd) == 0); 89 | //assert(socketpair(AF_UNIX, SOCK_STREAM, 0, q.fd) == 0); 90 | 91 | for (i = 0; i < 1000; i++) 92 | { 93 | assert(thrd_create(&t, producer, &q) == 0); 94 | assert(thrd_detach(t) == 0); 95 | } 96 | 97 | sleep(1); 98 | for (i = 0; i < 10; i++) 99 | { 100 | assert(thrd_create(&t, consumer, &q) == 0); 101 | assert(thrd_detach(t) == 0); 102 | } 103 | 104 | sleep(1000); 105 | } 106 | -------------------------------------------------------------------------------- /example/clients.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | const char request[] = 13 | "GET / HTTP/1.1\r\n" 14 | "Host: localhost\r\n" 15 | "\r\n"; 16 | 17 | struct state 18 | { 19 | int seconds; 20 | size_t requests; 21 | }; 22 | 23 | struct client 24 | { 25 | network_t connect; 26 | int socket; 27 | char input[1024]; 28 | struct state *state; 29 | }; 30 | 31 | static void client_request(struct client *); 32 | 33 | static void client_recv(reactor_event_t *event) 34 | { 35 | struct client *client = event->state; 36 | int result = event->data; 37 | 38 | assert(result > 0); 39 | client->state->requests++; 40 | client_request(client); 41 | } 42 | 43 | static void client_send(reactor_event_t *event) 44 | { 45 | struct client *client = event->state; 46 | int result = event->data; 47 | 48 | assert(result > 0); 49 | reactor_recv(client_recv, client, client->socket, client->input, sizeof client->input, 0); 50 | } 51 | 52 | static void client_request(struct client *client) 53 | { 54 | reactor_send(client_send, client, client->socket, request, strlen(request), 0); 55 | } 56 | 57 | static void client_connect(reactor_event_t *event) 58 | { 59 | struct client *client = event->state; 60 | int result = event->data; 61 | 62 | switch (event->type) 63 | { 64 | case NETWORK_CONNECT: 65 | client->socket = result; 66 | client_request(client); 67 | break; 68 | default: 69 | err(1, "unable to connect"); 70 | } 71 | } 72 | 73 | static struct client *client_create(struct state *state, const char *host, int port) 74 | { 75 | struct client *client; 76 | 77 | client = malloc(sizeof *client); 78 | client->state = state; 79 | client->connect = network_connect(client_connect, client, host, port); 80 | return client; 81 | } 82 | 83 | static void state_timeout(reactor_event_t *event) 84 | { 85 | struct state *state = event->state; 86 | 87 | printf("requests per seconds: %lu\n", state->requests / state->seconds); 88 | exit(0); 89 | } 90 | 91 | int main() 92 | { 93 | struct state state = {.seconds = 1}; 94 | struct timespec tv = {.tv_sec = state.seconds}; 95 | int i, n = 256; 96 | 97 | reactor_construct(); 98 | printf("running %d clients for %d seconds\n", n, state.seconds); 99 | for (i = 0; i < n; i ++) 100 | client_create(&state, "127.0.0.1", 80); 101 | 102 | reactor_timeout(state_timeout, &state, &tv, 0, 0); 103 | reactor_loop(); 104 | reactor_destruct(); 105 | } 106 | -------------------------------------------------------------------------------- /src/reactor/flow.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_FLOW_H_INCLUDED 2 | #define REACTOR_FLOW_H_INCLUDED 3 | 4 | enum 5 | { 6 | FLOW_ERROR, 7 | FLOW_LOG 8 | }; 9 | 10 | typedef struct flow_table flow_table_t; 11 | typedef struct flow_module flow_module_t; 12 | typedef struct flow_group flow_group_t; 13 | typedef struct flow_node flow_node_t; 14 | typedef struct flow_edge flow_edge_t; 15 | typedef struct flow flow_t; 16 | 17 | struct flow_table 18 | { 19 | void *(*load)(void); 20 | void *(*create)(flow_node_t *, value_t *); 21 | void (*receive)(void *, value_t *); 22 | void (*destroy)(void *); 23 | void (*unload)(void *); 24 | }; 25 | 26 | struct flow_module 27 | { 28 | const char *name; 29 | const void *handle; 30 | const flow_table_t *table; 31 | void *state; 32 | }; 33 | 34 | struct flow_group 35 | { 36 | const char *name; 37 | flow_t *flow; 38 | bool main_process; 39 | reactor_t async; 40 | list_t nodes; 41 | queue_producer_t log_producer; 42 | }; 43 | 44 | struct flow_node 45 | { 46 | const char *name; 47 | const flow_table_t *table; 48 | flow_group_t *group; 49 | value_t *conf; 50 | bool started; 51 | list_t edges; 52 | queue_t queue; 53 | size_t consumer_sources; 54 | queue_consumer_t consumer; 55 | void *state; 56 | }; 57 | 58 | struct flow_edge 59 | { 60 | bool direct; 61 | flow_node_t *target; 62 | value_t *conf; 63 | queue_producer_t producer; 64 | }; 65 | 66 | struct flow 67 | { 68 | reactor_user_t user; 69 | list_t modules; 70 | list_t groups; 71 | // value_t *globals; 72 | queue_t log_queue; 73 | size_t log_consumer_sources; 74 | queue_consumer_t log_consumer; 75 | }; 76 | 77 | /* flow functions */ 78 | 79 | void flow_construct(flow_t *, reactor_callback_t *, void *); 80 | void flow_destruct(flow_t *); 81 | void flow_search(flow_t *, const char *); 82 | int flow_module(flow_t *, const char *); 83 | int flow_node(flow_t *, const char *, const char *, const char *, value_t *); 84 | int flow_connect(flow_t *, const char *, const char *, value_t *); 85 | void flow_start(flow_t *); 86 | void flow_stop(flow_t *); 87 | 88 | /* flow node functions */ 89 | 90 | void flow_send(flow_node_t *, value_t *); 91 | void flow_send_and_release(flow_node_t *, value_t *); 92 | void flow_log(flow_node_t *, value_t *); 93 | void flow_exit(flow_node_t *); 94 | 95 | #endif /* REACTOR_FLOW_H_INCLUDED */ 96 | -------------------------------------------------------------------------------- /test/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void test_buffer(__attribute__((unused)) void **state) 8 | { 9 | buffer_t b; 10 | char *p; 11 | FILE *f; 12 | 13 | buffer_construct(&b); 14 | assert_int_equal(buffer_size(&b), 0); 15 | assert_int_equal(buffer_capacity(&b), 0); 16 | 17 | buffer_resize(&b, 100); 18 | assert_int_equal(buffer_size(&b), 100); 19 | buffer_resize(&b, 0); 20 | assert_int_equal(buffer_size(&b), 0); 21 | 22 | buffer_reserve(&b, 0); 23 | buffer_reserve(&b, 1024); 24 | assert_int_equal(buffer_capacity(&b), 1024); 25 | 26 | buffer_compact(&b); 27 | buffer_compact(&b); 28 | assert_int_equal(buffer_capacity(&b), 0); 29 | 30 | buffer_prepend(&b, data_string("last")); 31 | buffer_prepend(&b, data_string("first")); 32 | buffer_append(&b, data_string("end")); 33 | buffer_insert(&b, buffer_size(&b), data("", 1)); 34 | assert_string_equal(buffer_base(&b), "firstlastend"); 35 | 36 | buffer_erase(&b, 5, 7); 37 | assert_string_equal(buffer_base(&b), "first"); 38 | 39 | buffer_insert_fill(&b, 0, 5, data_string("x")); 40 | assert_string_equal(buffer_base(&b), "xxxxxfirst"); 41 | buffer_insert_fill(&b, buffer_size(&b), 5, data_string("x")); 42 | assert_string_equal(buffer_base(&b), "xxxxxfirst"); 43 | 44 | p = data_base(buffer_data(&b)); 45 | assert_string_equal(p, "xxxxxfirst"); 46 | buffer_destruct(&b); 47 | 48 | buffer_construct(&b); 49 | p = buffer_deconstruct(&b); 50 | free(p); 51 | 52 | buffer_construct(&b); 53 | assert_true(buffer_empty(&b)); 54 | assert_true(data_empty(buffer_data(&b))); 55 | assert_true(buffer_end(&b) == buffer_base(&b)); 56 | assert_true(data_base(buffer_allocate(&b, 256)) != NULL); 57 | buffer_clear(&b); 58 | buffer_destruct(&b); 59 | 60 | buffer_construct(&b); 61 | assert_false(buffer_load(&b, "/does/not/exist")); 62 | assert_true(buffer_load(&b, "/dev/null")); 63 | assert_true(data_equal(buffer_data(&b), data_null())); 64 | assert_false(buffer_loadz(&b, "/does/not/exist")); 65 | buffer_loadz(&b, "/dev/null"); 66 | assert_true(data_equal(buffer_data(&b), data_null())); 67 | assert_true(buffer_save(&b, "/dev/null")); 68 | assert_false(buffer_save(&b, "/does/not/exist")); 69 | buffer_destruct(&b); 70 | 71 | f = tmpfile(); 72 | buffer_construct(&b); 73 | buffer_insert_fill(&b, 0, 65536, data("", 1)); 74 | buffer_write(&b, f); 75 | buffer_clear(&b); 76 | fseek(f, 0, SEEK_SET); 77 | buffer_read(&b, f); 78 | assert_int_equal(buffer_size(&b), 65536); 79 | buffer_destruct(&b); 80 | fclose(f); 81 | } 82 | 83 | int main() 84 | { 85 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_buffer)}; 86 | 87 | return cmocka_run_group_tests(tests, NULL, NULL); 88 | } 89 | -------------------------------------------------------------------------------- /example/encode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static value_t *encapsulate(value_t *object) 7 | { 8 | value_t *parent = value_object(); 9 | 10 | value_object_set_release(parent, string(""), object); 11 | return parent; 12 | } 13 | 14 | static uint64_t ntime(void) 15 | { 16 | struct timespec tv; 17 | clock_gettime(CLOCK_MONOTONIC, &tv); 18 | return (uint64_t) tv.tv_sec * 1000000000ULL + (uint64_t) tv.tv_nsec; 19 | } 20 | 21 | int main() 22 | { 23 | value_t *v, *v2; 24 | string_t s; 25 | uint64_t t1, t2; 26 | 27 | v = value_number(42); 28 | for (int i = 0; i < 1000; i++) 29 | v = encapsulate(v); 30 | t1 = ntime(); 31 | s = value_encode(v, 0); 32 | t2 = ntime(); 33 | value_release(v); 34 | printf("%d '%s'\n", string_empty(s), string_base(s)); 35 | printf("time %f\n", ((double) t2 - t1) / 1000000000.); 36 | string_release(s); 37 | 38 | v = value_undefined(); 39 | s = value_encode(v, 0); 40 | value_release(v); 41 | printf("%d '%s'\n", string_empty(s), string_base(s)); 42 | string_release(s); 43 | 44 | const char p[] = "string, basic: \xE2\x98\x83, supp: \U0001F600, zero: \x00, control \t\t.end"; 45 | 46 | printf("string: "); 47 | fflush(stdout); 48 | write(1, p, sizeof p); 49 | printf("\n"); 50 | 51 | v2 = value_array(); 52 | value_array_append_release(v2, value_bool(false)); 53 | value_array_append_release(v2, value_bool(true)); 54 | value_array_append_release(v2, value_string(data(p, sizeof p - 1))); 55 | value_array_append_release(v2, value_number(42)); 56 | v = value_object(); 57 | value_object_set_release(v, string("test"), v2); 58 | t1 = ntime(); 59 | s = value_encode(v, 0); 60 | t2 = ntime(); 61 | value_release(v); 62 | printf("%s\n", string_base(s)); 63 | 64 | value_t *v3 = value_decode(string_base(s), NULL); 65 | string_release(s); 66 | s = value_encode(v3, 0); 67 | value_release(v3); 68 | printf("[decoded]\n%s\n", string_base(s)); 69 | string_release(s); 70 | 71 | v3 = value_decode("-234.1231249e90", NULL); 72 | s = value_encode(v3, 0); 73 | value_release(v3); 74 | printf("[decoded]\n%s\n", string_base(s)); 75 | string_release(s); 76 | 77 | v3 = value_decode("[\"a normal string, \\u2603 == \xE2\x98\x83, \\ud83d\\ude00 == \U0001F600\",1, 2, [{\"a\":null} ,{} ], true, 3]", NULL); 78 | s = value_encode(v3, VALUE_ENCODE_ASCII); 79 | value_release(v3); 80 | printf("[decoded]\n%s\n", string_base(s)); 81 | string_release(s); 82 | 83 | v3 = value_decode("[\"a normal string, \\u2603 == \xE2\x98\x83, \\ud83d\\ude00 == \U0001F600\",1, 2, [{\"a\":null} ,{} ], true, 3]", NULL); 84 | s = value_encode(v3, VALUE_ENCODE_PRETTY); 85 | value_release(v3); 86 | printf("[decoded]\n%s\n", string_base(s)); 87 | string_release(s); 88 | } 89 | -------------------------------------------------------------------------------- /test/encode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static char *accepts[] = {"42", "-1e2", 9 | "[-1e0]", "1.0E+2", 10 | "1.0E-9", "[null, \t\n\r true, false, 2.13, \"test\", [], [1], {}, {\"a\":1, \"b\":2}]"}; 11 | 12 | static char *rejects[] = { 13 | "-a", "01", "1.", "1e09", "1e99999999", "0x10", "1e", "nul", "tru", "fals", 14 | "[", "]", "+1", "\"", "{", "}", "{\"a\"", "{\"a\":}", "{\"a\":42,}", "{null:42}", 15 | "[42", "[1,,2]", "[1,]", "[}", "[null}", "{]", "{\"a\":null]", "[null", "[nullnull", 16 | }; 17 | 18 | static bool test(const char *input) 19 | { 20 | value_t *v; 21 | string_t s; 22 | const char *eof; 23 | bool result; 24 | 25 | v = value_decode(input, &eof); 26 | if (value_is_undefined(v) || *eof) 27 | { 28 | value_release(v); 29 | return false; 30 | } 31 | s = value_encode(v, 0); 32 | value_release(v); 33 | result = !string_empty(s); 34 | string_release(s); 35 | return result; 36 | } 37 | 38 | static void test_decode(__attribute__((unused)) void **arg) 39 | { 40 | size_t i; 41 | value_t *v; 42 | 43 | for (i = 0; i < sizeof accepts / sizeof *accepts; i++) 44 | assert_true(test(accepts[i])); 45 | for (i = 0; i < sizeof rejects / sizeof *rejects; i++) 46 | assert_false(test(rejects[i])); 47 | 48 | v = value_decode("42", NULL); 49 | assert_false(value_is_undefined(v)); 50 | value_release(v); 51 | } 52 | 53 | static void test_encode(__attribute__((unused)) void **arg) 54 | { 55 | assert_true(string_empty(value_encode(value_undefined(), 0))); 56 | } 57 | 58 | static void test_encode_pretty(__attribute__((unused)) void **arg) 59 | { 60 | value_t *v; 61 | string_t s; 62 | char *in = "{\"a\":[[[], [0], [1, 2]]], \"b\":{}}"; 63 | char *out = "{\n" 64 | " \"a\": [\n" 65 | " [\n" 66 | " [],\n" 67 | " [\n" 68 | " 0\n" 69 | " ],\n" 70 | " [\n" 71 | " 1,\n" 72 | " 2\n" 73 | " ]\n" 74 | " ]\n" 75 | " ],\n" 76 | " \"b\": {}\n" 77 | "}"; 78 | 79 | v = value_decode(in, NULL); 80 | s = value_encode(v, VALUE_ENCODE_PRETTY); 81 | assert_string_equal(string_base(s), out); 82 | value_release(v); 83 | string_release(s); 84 | } 85 | 86 | int main() 87 | { 88 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_decode), cmocka_unit_test(test_encode), 89 | cmocka_unit_test(test_encode_pretty)}; 90 | 91 | return cmocka_run_group_tests(tests, NULL, NULL); 92 | } 93 | -------------------------------------------------------------------------------- /src/reactor/data.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* memcasecmp from gnulib */ 10 | 11 | static int memcasecmp(const void *vs1, const void *vs2, size_t n) 12 | { 13 | size_t i; 14 | char const *s1 = vs1; 15 | char const *s2 = vs2; 16 | 17 | for (i = 0; i < n; i++) 18 | { 19 | unsigned char u1 = s1[i]; 20 | unsigned char u2 = s2[i]; 21 | int U1 = toupper(u1); 22 | int U2 = toupper(u2); 23 | int diff = (UCHAR_MAX <= INT_MAX ? U1 - U2 : U1 < U2 ? -1 : U2 < U1); 24 | if (diff) 25 | return diff; 26 | } 27 | return 0; 28 | } 29 | 30 | data_t data(const void *base, size_t size) 31 | { 32 | return (data_t) {.iov.iov_base = (void *) base, .iov.iov_len = size}; 33 | } 34 | 35 | data_t data_null(void) 36 | { 37 | return (data_t) {0}; 38 | } 39 | 40 | data_t data_string(const char *chars) 41 | { 42 | return data(chars, strlen(chars)); 43 | } 44 | 45 | data_t data_offset(const data_t d, size_t offset) 46 | { 47 | return data((char *) data_base(d) + offset, data_size(d) - offset); 48 | } 49 | 50 | data_t data_select(const data_t d, size_t size) 51 | { 52 | return data(data_base(d), size); 53 | } 54 | 55 | data_t data_copy(const data_t from) 56 | { 57 | data_t to; 58 | 59 | to = data(malloc(data_size(from)), data_size(from)); 60 | memcpy(data_base(to), data_base(from), data_size(from)); 61 | return to; 62 | } 63 | 64 | data_t data_copyz(const data_t from) 65 | { 66 | data_t to; 67 | 68 | to = data(malloc(data_size(from) + 1), data_size(from)); 69 | memcpy(data_base(to), data_base(from), data_size(from)); 70 | *(char *) data_end(to) = 0; 71 | return to; 72 | } 73 | 74 | data_t data_alloc(size_t size) 75 | { 76 | return data(calloc(1, size), size); 77 | } 78 | 79 | data_t data_realloc(data_t d, size_t size) 80 | { 81 | return data(realloc(data_base(d), size), size); 82 | } 83 | 84 | void data_release(data_t d) 85 | { 86 | free(data_base(d)); 87 | } 88 | 89 | /* capacity */ 90 | 91 | size_t data_size(const data_t d) 92 | { 93 | return d.iov.iov_len; 94 | } 95 | 96 | bool data_empty(const data_t d) 97 | { 98 | return data_size(d) == 0; 99 | } 100 | 101 | /* element access */ 102 | 103 | void *data_base(const data_t d) 104 | { 105 | return d.iov.iov_base; 106 | } 107 | 108 | void *data_end(const data_t d) 109 | { 110 | return (char *) data_base(d) + data_size(d); 111 | } 112 | 113 | /* operations */ 114 | 115 | bool data_equal(const data_t d1, const data_t d2) 116 | { 117 | return data_size(d1) == data_size(d2) && memcmp(data_base(d1), data_base(d2), data_size(d1)) == 0; 118 | } 119 | 120 | bool data_equal_case(const data_t d1, const data_t d2) 121 | { 122 | return data_size(d1) == data_size(d2) && memcasecmp(data_base(d1), data_base(d2), data_size(d1)) == 0; 123 | } 124 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '38 6 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 27 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 28 | permissions: 29 | actions: read 30 | contents: read 31 | security-events: write 32 | 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | language: [ 'cpp' ] 37 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] 38 | # Use only 'java' to analyze code written in Java, Kotlin or both 39 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 40 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 41 | 42 | steps: 43 | - name: Checkout repository 44 | uses: actions/checkout@v3 45 | 46 | # Initializes the CodeQL tools for scanning. 47 | - name: Initialize CodeQL 48 | uses: github/codeql-action/init@v2 49 | with: 50 | languages: ${{ matrix.language }} 51 | # If you wish to specify custom queries, you can do so here or in a config file. 52 | # By default, queries listed here will override any specified in a config file. 53 | # Prefix the list here with "+" to use these queries and those in the config file. 54 | 55 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 56 | # queries: security-extended,security-and-quality 57 | 58 | 59 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). 60 | # If this step fails, then you should remove it and run the build manually (see below) 61 | - name: Autobuild 62 | uses: github/codeql-action/autobuild@v2 63 | 64 | # ℹ️ Command-line programs to run using the OS shell. 65 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 66 | 67 | # If the Autobuild fails above, remove it and uncomment the following three lines. 68 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 69 | 70 | # - run: | 71 | # echo "Run, Build Application using script" 72 | # ./location_of_script_within_repo/buildscript.sh 73 | 74 | - name: Perform CodeQL Analysis 75 | uses: github/codeql-action/analyze@v2 76 | with: 77 | category: "/language:${{matrix.language}}" 78 | -------------------------------------------------------------------------------- /test/notify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | static void callback_remove(reactor_event_t *event) 15 | { 16 | notify_t *n = event->state; 17 | notify_event_t *e = (notify_event_t *) event->data; 18 | 19 | notify_remove_id(n, e->id); 20 | } 21 | 22 | static void callback(reactor_event_t *event) 23 | { 24 | notify_t *n = event->state; 25 | 26 | notify_destruct(n); 27 | free(n); 28 | } 29 | 30 | static void test_notify_basic(__attribute__((unused)) void **arg) 31 | { 32 | notify_t n; 33 | 34 | /* receive one and remove */ 35 | reactor_construct(); 36 | notify_construct(&n, callback_remove, &n); 37 | notify_open(&n); 38 | assert_true(notify_add(&n, "/", IN_ALL_EVENTS) > 0); 39 | assert_int_equal(closedir(opendir("/")), 0); 40 | reactor_loop(); 41 | notify_close(&n); 42 | notify_destruct(&n); 43 | reactor_destruct(); 44 | 45 | /* receive events for file */ 46 | reactor_construct(); 47 | notify_construct(&n, NULL, NULL); 48 | notify_open(&n); 49 | assert_true(notify_add(&n, "/bin", IN_ALL_EVENTS) > 0); 50 | assert_true(notify_add(&n, "/", IN_ALL_EVENTS) > 0); 51 | assert_int_equal(closedir(opendir("/bin")), 0); 52 | assert_int_equal(closedir(opendir("/")), 0); 53 | assert_int_equal(close(open("/bin/sh", O_RDONLY)), 0); 54 | reactor_loop_once(); 55 | notify_close(&n); 56 | notify_destruct(&n); 57 | reactor_destruct(); 58 | 59 | /* watch file that does not exist */ 60 | reactor_construct(); 61 | notify_construct(&n, NULL, NULL); 62 | notify_open(&n); 63 | assert_true(notify_add(&n, "/invalid/file", IN_ALL_EVENTS) == -1); 64 | reactor_loop(); 65 | notify_destruct(&n); 66 | reactor_destruct(); 67 | } 68 | 69 | static void test_notify_remove(__attribute__((unused)) void **arg) 70 | { 71 | notify_t n; 72 | int id; 73 | 74 | /* remove files */ 75 | reactor_construct(); 76 | notify_construct(&n, NULL, NULL); 77 | notify_open(&n); 78 | assert_true(notify_add(&n, "/", IN_ALL_EVENTS) > 0); 79 | assert_true(notify_remove_path(&n, "/")); 80 | id = notify_add(&n, "/bin", IN_ALL_EVENTS); 81 | assert_true(notify_remove_id(&n, id)); 82 | 83 | /* does not exist */ 84 | assert_false(notify_remove_id(&n, -1)); 85 | assert_false(notify_remove_path(&n, "no")); 86 | reactor_loop(); 87 | notify_destruct(&n); 88 | reactor_destruct(); 89 | 90 | /* remove and close */ 91 | reactor_construct(); 92 | notify_construct(&n, NULL, NULL); 93 | notify_open(&n); 94 | assert_true(notify_add(&n, "/", IN_ALL_EVENTS) > 0); 95 | assert_true(notify_remove_path(&n, "/")); 96 | notify_close(&n); 97 | reactor_loop(); 98 | notify_destruct(&n); 99 | reactor_destruct(); 100 | } 101 | 102 | static void test_notify_abort(void **state) 103 | { 104 | notify_t *n = malloc(sizeof *n);; 105 | 106 | (void) state; 107 | /* abort while receiving events */ 108 | reactor_construct(); 109 | notify_construct(n, callback, n); 110 | notify_open(n); 111 | notify_add(n, "/", IN_ALL_EVENTS); 112 | notify_add(n, "/", IN_ALL_EVENTS); 113 | assert_int_equal(closedir(opendir("/")), 0); 114 | reactor_loop_once(); 115 | reactor_destruct(); 116 | } 117 | 118 | int main() 119 | { 120 | const struct CMUnitTest tests[] = 121 | { 122 | cmocka_unit_test(test_notify_remove), 123 | cmocka_unit_test(test_notify_basic), 124 | cmocka_unit_test(test_notify_abort) 125 | }; 126 | 127 | return cmocka_run_group_tests(tests, NULL, NULL); 128 | } 129 | -------------------------------------------------------------------------------- /src/picohttpparser/picohttpparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, 3 | * Shigeo Mitsunari 4 | * 5 | * The software is licensed under either the MIT License (below) or the Perl 6 | * license. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef picohttpparser_h 28 | #define picohttpparser_h 29 | 30 | #include 31 | 32 | #ifdef _MSC_VER 33 | #define ssize_t intptr_t 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /* contains name and value of a header (name == NULL if is a continuing line 41 | * of a multiline header */ 42 | struct phr_header { 43 | const char *name; 44 | size_t name_len; 45 | const char *value; 46 | size_t value_len; 47 | }; 48 | 49 | /* returns number of bytes consumed if successful, -2 if request is partial, 50 | * -1 if failed */ 51 | int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 52 | int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); 53 | 54 | /* ditto */ 55 | int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, 56 | struct phr_header *headers, size_t *num_headers, size_t last_len); 57 | 58 | /* ditto */ 59 | int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); 60 | 61 | /* should be zero-filled before start */ 62 | struct phr_chunked_decoder { 63 | size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ 64 | char consume_trailer; /* if trailing headers should be consumed */ 65 | char _hex_count; 66 | char _state; 67 | }; 68 | 69 | /* the function rewrites the buffer given as (buf, bufsz) removing the chunked- 70 | * encoding headers. When the function returns without an error, bufsz is 71 | * updated to the length of the decoded data available. Applications should 72 | * repeatedly call the function while it returns -2 (incomplete) every time 73 | * supplying newly arrived data. If the end of the chunked-encoded data is 74 | * found, the function returns a non-negative number indicating the number of 75 | * octets left undecoded at the tail of the supplied buffer. Returns -1 on 76 | * error. 77 | */ 78 | ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz); 79 | 80 | /* returns if the chunked decoder is in middle of chunked data */ 81 | int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/reactor/vector.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* constructor/destructor */ 7 | 8 | void vector_construct(vector_t *vector, size_t object_size) 9 | { 10 | buffer_construct(&vector->buffer); 11 | vector->object_size = object_size; 12 | } 13 | 14 | void vector_destruct(vector_t *vector, vector_release_t *release) 15 | { 16 | vector_clear(vector, release); 17 | buffer_destruct(&vector->buffer); 18 | } 19 | 20 | /* capacity */ 21 | 22 | size_t vector_size(const vector_t *vector) 23 | { 24 | return buffer_size(&vector->buffer) / vector->object_size; 25 | } 26 | 27 | size_t vector_capacity(const vector_t *vector) 28 | { 29 | return buffer_capacity(&vector->buffer) / vector->object_size; 30 | } 31 | 32 | bool vector_empty(const vector_t *vector) 33 | { 34 | return vector_size(vector) == 0; 35 | } 36 | 37 | void vector_reserve(vector_t *vector, size_t capacity) 38 | { 39 | buffer_reserve(&vector->buffer, capacity * vector->object_size); 40 | } 41 | 42 | void vector_shrink_to_fit(vector_t *vector) 43 | { 44 | buffer_compact(&vector->buffer); 45 | } 46 | 47 | /* element access */ 48 | 49 | void *vector_at(const vector_t *vector, size_t position) 50 | { 51 | return buffer_at(&vector->buffer, position * vector->object_size); 52 | } 53 | 54 | void *vector_front(const vector_t *vector) 55 | { 56 | return vector_base(vector); 57 | } 58 | 59 | void *vector_back(const vector_t *vector) 60 | { 61 | return buffer_at(&vector->buffer, buffer_size(&vector->buffer) - vector->object_size); 62 | } 63 | 64 | void *vector_base(const vector_t *vector) 65 | { 66 | return buffer_base(&vector->buffer); 67 | } 68 | 69 | /* modifiers */ 70 | 71 | void vector_insert(vector_t *vector, size_t position, const void *object) 72 | { 73 | buffer_insert(&vector->buffer, position * vector->object_size, data(object, vector->object_size)); 74 | } 75 | 76 | void vector_insert_range(vector_t *vector, size_t position, const void *first, const void *last) 77 | { 78 | buffer_insert(&vector->buffer, position * vector->object_size, data(first, (char *) last - (char *) first)); 79 | } 80 | 81 | void vector_insert_fill(vector_t *vector, size_t position, size_t count, const void *object) 82 | { 83 | buffer_insert_fill(&vector->buffer, position * vector->object_size, count, data(object, vector->object_size)); 84 | } 85 | 86 | void vector_erase(vector_t *vector, size_t position, vector_release_t *release) 87 | { 88 | if (release) 89 | release(vector_at(vector, position)); 90 | 91 | buffer_erase(&vector->buffer, position * vector->object_size, vector->object_size); 92 | } 93 | 94 | void vector_erase_range(vector_t *vector, size_t first, size_t last, vector_release_t *release) 95 | { 96 | size_t i; 97 | 98 | if (release) 99 | for (i = first; i < last; i++) 100 | release(vector_at(vector, i)); 101 | 102 | buffer_erase(&vector->buffer, first * vector->object_size, (last - first) * vector->object_size); 103 | } 104 | 105 | void vector_clear(vector_t *vector, vector_release_t *release) 106 | { 107 | vector_erase_range(vector, 0, vector_size(vector), release); 108 | buffer_clear(&vector->buffer); 109 | } 110 | 111 | void vector_push_back(vector_t *vector, const void *object) 112 | { 113 | buffer_insert(&vector->buffer, buffer_size(&vector->buffer), data(object, vector->object_size)); 114 | } 115 | 116 | void vector_pop_back(vector_t *vector, vector_release_t *release) 117 | { 118 | vector_erase(vector, vector_size(vector) - 1, release); 119 | } 120 | 121 | /* operations */ 122 | 123 | void vector_sort(vector_t *vector, vector_compare_t *compare) 124 | { 125 | qsort(vector_base(vector), vector_size(vector), vector->object_size, compare); 126 | } 127 | -------------------------------------------------------------------------------- /src/reactor/reactor.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_REACTOR_H_INCLUDED 2 | #define REACTOR_REACTOR_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define REACTOR_RING_SIZE 4096 13 | 14 | enum 15 | { 16 | REACTOR_CALL, 17 | REACTOR_RETURN 18 | }; 19 | 20 | typedef struct reactor_event reactor_event_t; 21 | typedef struct reactor_user reactor_user_t; // 22 | typedef uint64_t reactor_time_t; 23 | typedef uint64_t reactor_t; 24 | typedef void (reactor_callback_t)(reactor_event_t *); 25 | 26 | struct reactor_event 27 | { 28 | void *state; 29 | int type; 30 | uint64_t data; 31 | }; 32 | 33 | struct reactor_user 34 | { 35 | reactor_callback_t *callback; 36 | void *state; 37 | }; 38 | 39 | reactor_event_t reactor_event_define(void *, int, uint64_t); 40 | reactor_user_t reactor_user_define(reactor_callback_t *, void *); 41 | void reactor_user_construct(reactor_user_t *, reactor_callback_t *, void *); 42 | 43 | void reactor_construct(void); 44 | void reactor_destruct(void); 45 | reactor_time_t reactor_now(void); 46 | void reactor_loop(void); 47 | void reactor_loop_once(void); 48 | void reactor_call(reactor_user_t *, int, uint64_t); 49 | void reactor_cancel(reactor_t, reactor_callback_t *, void *); 50 | 51 | reactor_t reactor_async(reactor_callback_t *, void *); 52 | reactor_t reactor_next(reactor_callback_t *, void *); 53 | reactor_t reactor_async_cancel(reactor_callback_t *, void *, uint64_t); 54 | reactor_t reactor_nop(reactor_callback_t *, void *); 55 | reactor_t reactor_readv(reactor_callback_t *, void *, int, const struct iovec *, int, size_t); 56 | reactor_t reactor_writev(reactor_callback_t *, void *, int, const struct iovec *, int, size_t); 57 | reactor_t reactor_fsync(reactor_callback_t *, void *, int); 58 | reactor_t reactor_poll_add(reactor_callback_t *, void *, int, short int); 59 | reactor_t reactor_poll_add_multi(reactor_callback_t *, void *, int, short int); 60 | reactor_t reactor_poll_update(reactor_callback_t *, void *, reactor_t, short int); 61 | reactor_t reactor_poll_remove(reactor_callback_t *, void *, reactor_t); 62 | reactor_t reactor_epoll_ctl(reactor_callback_t *, void *, int, int, int, struct epoll_event *); 63 | reactor_t reactor_sync_file_range(reactor_callback_t *, void *, int, uint64_t, uint64_t, int); 64 | reactor_t reactor_sendmsg(reactor_callback_t *, void *, int, const struct msghdr *, int); 65 | reactor_t reactor_recvmsg(reactor_callback_t *, void *, int, struct msghdr *, int); 66 | reactor_t reactor_send(reactor_callback_t *, void *, int, const void *, size_t, int); 67 | reactor_t reactor_recv(reactor_callback_t *, void *, int, void *, size_t, int); 68 | reactor_t reactor_timeout(reactor_callback_t *, void *, struct timespec *, int, int); 69 | reactor_t reactor_accept(reactor_callback_t *, void *, int, struct sockaddr *, socklen_t *, int); 70 | reactor_t reactor_read(reactor_callback_t *, void *, int, void *, size_t, size_t); 71 | reactor_t reactor_write(reactor_callback_t *, void *, int, const void *, size_t, size_t); 72 | reactor_t reactor_connect(reactor_callback_t *, void *, int, struct sockaddr *, socklen_t); 73 | reactor_t reactor_fallocate(reactor_callback_t *, void *, int, int, uint64_t, uint64_t); 74 | reactor_t reactor_openat(reactor_callback_t *, void *, int, const char *, int, mode_t); 75 | reactor_t reactor_close(reactor_callback_t *, void *, int); 76 | 77 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,0,0) 78 | reactor_t reactor_send_zerocopy(reactor_callback_t *, void *, int, const void *, size_t, int); 79 | #endif 80 | 81 | #endif /* REACTOR_REACTOR_H_INCLUDED */ 82 | -------------------------------------------------------------------------------- /src/reactor/value.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_VALUE_H_INCLUDED 2 | #define REACTOR_VALUE_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | typedef void value_t; 8 | typedef void value_destroy_t(value_t *); 9 | typedef struct value_table value_table_t; 10 | 11 | struct value_table 12 | { 13 | uintptr_t user; 14 | value_destroy_t *destroy; 15 | }; 16 | 17 | value_t *value_create(const void *, size_t, const value_table_t *); 18 | uintptr_t value_user(const value_t *); 19 | size_t value_ref(const value_t *); 20 | void value_hold(value_t *); 21 | void value_release(value_t *); 22 | uintptr_t value_data(const value_t *); 23 | const char *value_type(const value_t *); 24 | 25 | /* undefined */ 26 | 27 | extern const char VALUE_UNDEFINED[]; 28 | 29 | value_t *value_undefined(void); 30 | bool value_is_undefined(const value_t *); 31 | 32 | /* null */ 33 | 34 | extern const char VALUE_NULL[]; 35 | 36 | value_t *value_null(void); 37 | bool value_is_null(const value_t *); 38 | 39 | /* bool */ 40 | 41 | extern const char VALUE_BOOL[]; 42 | 43 | value_t *value_bool(bool); 44 | bool value_is_bool(const value_t *); 45 | bool value_bool_get(const value_t *); 46 | 47 | /* number */ 48 | 49 | extern const char VALUE_NUMBER[]; 50 | 51 | value_t *value_number(long double); 52 | bool value_is_number(const value_t *); 53 | long double value_number_get(const value_t *); 54 | 55 | /* string */ 56 | 57 | extern const char VALUE_STRING[]; 58 | 59 | value_t *value_string(const string_t); 60 | value_t *value_string_constant(const string_t); 61 | value_t *value_string_release(const string_t); 62 | bool value_is_string(const value_t *); 63 | string_t value_string_get(const value_t *); 64 | char *value_string_base(const value_t *); 65 | size_t value_string_size(const value_t *); 66 | 67 | /* array */ 68 | 69 | extern const char VALUE_ARRAY[]; 70 | 71 | #define value_array_foreach(array, element) \ 72 | if (value_is_array(array)) \ 73 | for (size_t __value_iter = 0; __value_iter < vector_size(array) && \ 74 | (element = *(void **) vector_at(array, __value_iter)); \ 75 | __value_iter++) 76 | 77 | value_t *value_array(void); 78 | bool value_is_array(const value_t *); 79 | size_t value_array_length(const value_t *); 80 | void value_array_append(value_t *, value_t *); 81 | void value_array_append_release(value_t *, value_t *); 82 | value_t *value_array_get(const value_t *, size_t); 83 | void value_array_remove(value_t *, size_t); 84 | 85 | /* object */ 86 | 87 | extern const char VALUE_OBJECT[]; 88 | 89 | #define value_object_foreach(object, key, value) \ 90 | if (value_is_object(object)) \ 91 | for (size_t __value_iter = 0; __value_iter < ((mapd_t *) object)->map.elements_capacity; __value_iter++) \ 92 | if ((value) = (value_t *) ((mapd_entry_t *)((mapd_t *) (object))->map.elements)[__value_iter].value, \ 93 | data_base((key) = ((mapd_entry_t *)((mapd_t *) (object))->map.elements)[__value_iter].key) != NULL) 94 | 95 | value_t *value_object(void); 96 | bool value_is_object(const value_t *); 97 | size_t value_object_size(const value_t *); 98 | void value_object_set(value_t *, const string_t, value_t *); 99 | void value_object_set_release(value_t *, string_t, value_t *); 100 | value_t *value_object_get(const value_t *, const string_t); 101 | void value_object_delete(value_t *, const string_t); 102 | value_t *value_object_keys(const value_t *); 103 | 104 | #endif /* REACTOR_VALUE_H_INCLUDED */ 105 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = subdir-objects serial-tests 2 | ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 3 | DIST_SUBDIRS = example 4 | 5 | SOURCE_FILES = \ 6 | src/picohttpparser/picohttpparser.c \ 7 | src/reactor/data.c \ 8 | src/reactor/hash.c \ 9 | src/reactor/buffer.c \ 10 | src/reactor/list.c \ 11 | src/reactor/vector.c \ 12 | src/reactor/map.c \ 13 | src/reactor/mapi.c \ 14 | src/reactor/maps.c \ 15 | src/reactor/mapd.c \ 16 | src/reactor/string.c \ 17 | src/reactor/value.c \ 18 | src/reactor/encode.c \ 19 | src/reactor/decode.c \ 20 | src/reactor/pool.c \ 21 | src/reactor/reactor.c \ 22 | src/reactor/signal.c \ 23 | src/reactor/timeout.c \ 24 | src/reactor/notify.c \ 25 | src/reactor/network.c \ 26 | src/reactor/stream.c \ 27 | src/reactor/http.c \ 28 | src/reactor/server.c \ 29 | src/reactor/queue.c \ 30 | src/reactor/flow.c 31 | 32 | HEADER_FILES = \ 33 | src/picohttpparser/picohttpparser.h \ 34 | src/reactor/data.h \ 35 | src/reactor/hash.h \ 36 | src/reactor/buffer.h \ 37 | src/reactor/list.h \ 38 | src/reactor/vector.h \ 39 | src/reactor/map.h \ 40 | src/reactor/mapi.h \ 41 | src/reactor/maps.h \ 42 | src/reactor/mapd.h \ 43 | src/reactor/string.h \ 44 | src/reactor/value.h \ 45 | src/reactor/encode.h \ 46 | src/reactor/decode.h \ 47 | src/reactor/pool.h \ 48 | src/reactor/reactor.h \ 49 | src/reactor/signal.h \ 50 | src/reactor/timeout.h \ 51 | src/reactor/notify.h \ 52 | src/reactor/network.h \ 53 | src/reactor/stream.h \ 54 | src/reactor/http.h \ 55 | src/reactor/server.h \ 56 | src/reactor/queue.h \ 57 | src/reactor/flow.h 58 | 59 | lib_LTLIBRARIES = libreactor.la 60 | libreactor_la_SOURCES = $(SOURCE_FILES) $(HEADER_FILES) 61 | libreactor_la_CFLAGS = -std=gnu2x -g -O3 -flto=auto -rdynamic -march=native -I$(srcdir)/src -fPIC 62 | libreactor_la_LDFLAGS = -static 63 | 64 | headerfilesdir = $(includedir)/reactor 65 | headerfiles_HEADERS = $(HEADER_FILES) 66 | 67 | mainheaderdir = $(includedir) 68 | mainheader_HEADERS = src/reactor.h 69 | 70 | pkgconfigdir = $(libdir)/pkgconfig 71 | pkgconfig_DATA = libreactor.pc 72 | 73 | MAINTAINERCLEANFILES = aclocal.m4 config.h.in configure Makefile.in libreactor-?.?.?.tar.gz 74 | 75 | CLEANFILES = {.,test/,test/conf,src/reactor/,src/picohttpparser}/*.{gcno,gcda,gcov} 76 | 77 | maintainer-clean-local: 78 | rm -rf autotools m4 libreactor-?.?.? 79 | 80 | indent: 81 | clang-format -i src/reactor/*.c test/*.c 82 | 83 | ### unit testing ### 84 | 85 | AM_CFLAGS = -std=gnu2x -g -O0 -rdynamic -fPIC -ftest-coverage -fprofile-arcs -I$(srcdir)/src -DGCOV_BUILD -DUNIT_TESTING 86 | LDADD = -L. libreactor_test.a -lcmocka 87 | 88 | check_LIBRARIES = libreactor_test.a 89 | libreactor_test_a_CFLAGS = $(AM_CFLAGS) 90 | libreactor_test_a_SOURCES = $(SOURCE_FILES) $(HEADER_FILES) 91 | 92 | check_PROGRAMS = \ 93 | test/reactor \ 94 | test/include \ 95 | test/data \ 96 | test/hash \ 97 | test/buffer \ 98 | test/list \ 99 | test/vector \ 100 | test/map \ 101 | test/mapi \ 102 | test/maps \ 103 | test/mapd \ 104 | test/string \ 105 | test/value \ 106 | test/encode \ 107 | test/pool \ 108 | test/signal \ 109 | test/timeout \ 110 | test/notify \ 111 | test/network_resolve \ 112 | test/network \ 113 | test/stream \ 114 | test/http \ 115 | test/server \ 116 | test/queue \ 117 | test/flow_async \ 118 | test/flow 119 | 120 | test_flow_LDADD = $(LDADD) -lltdl 121 | test_flow_async_LDADD = $(LDADD) -lltdl 122 | 123 | check_LTLIBRARIES = \ 124 | test/conf/module1.la \ 125 | test/conf/module2.la 126 | 127 | test_conf_module1_la_LDFLAGS = -rpath /foo -module -avoid-version 128 | test_conf_module1_la_LIBADD = -L. -lreactor_test 129 | test_conf_module2_la_LDFLAGS = -rpath /foo -module -avoid-version 130 | test_conf_module2_la_LIBADD = -L. -lreactor_test 131 | 132 | dist_noinst_SCRIPTS = test/valgrind.sh test/coverage.sh 133 | 134 | TESTS = $(check_PROGRAMS) test/coverage.sh test/valgrind.sh 135 | -------------------------------------------------------------------------------- /src/reactor/list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* internals */ 9 | 10 | static list_item_t *list_object_item(const void *object) 11 | { 12 | return (list_item_t *) ((uintptr_t) object - offsetof(list_item_t, object)); 13 | } 14 | 15 | static list_item_t *list_item_new(const void *object, size_t size) 16 | { 17 | list_item_t *item; 18 | 19 | item = calloc(1, sizeof(list_item_t) + size); 20 | assert(item != NULL); 21 | if (object) 22 | memcpy(item->object, object, size); 23 | 24 | return item; 25 | } 26 | 27 | /* constructor/destructor */ 28 | 29 | void list_construct(list_t *list) 30 | { 31 | list->next = (list_item_t *) list; 32 | list->previous = (list_item_t *) list; 33 | } 34 | 35 | void list_destruct(list_t *list, list_release_t *release) 36 | { 37 | list_clear(list, release); 38 | } 39 | 40 | /* iterators */ 41 | 42 | void *list_next(const void *object) 43 | { 44 | return list_object_item(object)->list.next->object; 45 | } 46 | 47 | void *list_previous(const void *object) 48 | { 49 | return list_object_item(object)->list.previous->object; 50 | } 51 | 52 | /* capacity */ 53 | 54 | int list_empty(const list_t *list) 55 | { 56 | return list->next == (list_item_t *) list; 57 | } 58 | 59 | /* element access */ 60 | 61 | void *list_front(const list_t *list) 62 | { 63 | return list->next->object; 64 | } 65 | 66 | void *list_back(const list_t *list) 67 | { 68 | return list->previous->object; 69 | } 70 | 71 | void *list_end(const list_t *list) 72 | { 73 | return ((list_item_t *) list)->object; 74 | } 75 | 76 | /* modifiers */ 77 | 78 | void *list_push_front(list_t *list, const void *object, size_t size) 79 | { 80 | return list_insert(list_front(list), object, size); 81 | } 82 | 83 | void *list_push_back(list_t *list, const void *object, size_t size) 84 | { 85 | return list_insert(list_end(list), object, size); 86 | } 87 | 88 | void *list_insert(void *list_object, const void *object, size_t size) 89 | { 90 | list_item_t *after, *item; 91 | 92 | after = list_object_item(list_object); 93 | item = list_item_new(object, size); 94 | item->list.previous = after->list.previous; 95 | item->list.next = after; 96 | item->list.previous->list.next = item; 97 | item->list.next->list.previous = item; 98 | return item->object; 99 | } 100 | 101 | void list_splice(void *object1, void *object2) 102 | { 103 | list_item_t *to, *from; 104 | 105 | if (object1 == object2) 106 | return; 107 | to = list_object_item(object1); 108 | from = list_object_item(object2); 109 | from->list.previous->list.next = from->list.next; 110 | from->list.next->list.previous = from->list.previous; 111 | from->list.previous = to->list.previous; 112 | from->list.next = to; 113 | from->list.previous->list.next = from; 114 | from->list.next->list.previous = from; 115 | } 116 | 117 | void list_detach(void *object) 118 | { 119 | list_item_t *item = list_object_item(object); 120 | 121 | item->list.previous->list.next = item->list.next; 122 | item->list.next->list.previous = item->list.previous; 123 | item->list.next = item; 124 | item->list.previous = item; 125 | } 126 | 127 | void list_erase(void *object, list_release_t *release) 128 | { 129 | list_item_t *item = list_object_item(object); 130 | 131 | item->list.previous->list.next = item->list.next; 132 | item->list.next->list.previous = item->list.previous; 133 | if (release) 134 | release(object); 135 | free(item); 136 | } 137 | 138 | void list_clear(list_t *list, list_release_t *release) 139 | { 140 | while (!list_empty(list)) 141 | list_erase(list_front(list), release); 142 | } 143 | 144 | /* operations */ 145 | 146 | void *list_find(const list_t *list, list_compare_t *compare, const void *object) 147 | { 148 | void *list_object; 149 | 150 | list_foreach(list, list_object) if (compare(object, list_object) == 0) return list_object; 151 | 152 | return NULL; 153 | } 154 | -------------------------------------------------------------------------------- /test/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "reactor.h" 13 | 14 | struct state 15 | { 16 | uint64_t value; 17 | size_t calls; 18 | size_t read; 19 | stream_t in; 20 | stream_t out; 21 | size_t errors; 22 | }; 23 | 24 | static void callback(reactor_event_t *event) 25 | { 26 | struct state *state = event->state; 27 | data_t data; 28 | 29 | state->calls++; 30 | 31 | switch (event->type) 32 | { 33 | case STREAM_READ: 34 | data = stream_read(&state->in); 35 | state->read += data_size(data); 36 | if (state->read >= 65536) 37 | stream_consume(&state->in, data_size(data)); 38 | if (state->read >= 65536 * 2) 39 | stream_close(&state->in); 40 | break; 41 | case STREAM_ERROR: 42 | state->errors++; 43 | break; 44 | default: 45 | break; 46 | } 47 | } 48 | 49 | static void test_domain_socket(__attribute__((unused)) void **arg) 50 | { 51 | struct state state = {0}; 52 | int fd[2]; 53 | 54 | assert_true(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == 0); 55 | 56 | reactor_construct(); 57 | stream_construct(&state.in, callback, &state); 58 | stream_open(&state.in, fd[0], 0); 59 | stream_construct(&state.out, callback, &state); 60 | stream_open(&state.out, fd[1], 0); 61 | stream_write(&state.out, data_string("test")); 62 | stream_flush(&state.out); 63 | stream_flush(&state.out); 64 | stream_write(&state.out, data_string("test")); 65 | stream_flush(&state.out); 66 | reactor_loop_once(); 67 | stream_close(&state.in); 68 | assert_int_equal(stream_fd(&state.in), -1); 69 | reactor_loop(); 70 | stream_destruct(&state.in); 71 | stream_destruct(&state.out); 72 | reactor_destruct(); 73 | assert_int_equal(state.calls, 2); 74 | assert_int_equal(state.errors, 0); 75 | close(fd[0]); 76 | close(fd[1]); 77 | } 78 | 79 | static void test_pipe(__attribute__((unused)) void **arg) 80 | { 81 | struct state state = {0}; 82 | char chunk[1024 * 1024] = {0}; 83 | int fd[2]; 84 | 85 | assert_true(pipe(fd) == 0); 86 | 87 | reactor_construct(); 88 | stream_construct(&state.in, callback, &state); 89 | stream_open(&state.in, fd[0], 0); 90 | stream_construct(&state.out, callback, &state); 91 | stream_open(&state.out, fd[1], STREAM_WRITE_ONLY); 92 | stream_write(&state.out, data(chunk, sizeof chunk)); 93 | stream_flush(&state.out); 94 | stream_flush(&state.out); 95 | reactor_loop(); 96 | assert_int_equal(state.errors, 1); 97 | 98 | stream_write(&state.out, data(chunk, sizeof chunk)); 99 | stream_flush(&state.out); 100 | stream_close(&state.out); 101 | reactor_loop(); 102 | assert_int_equal(state.errors, 1); 103 | 104 | stream_destruct(&state.in); 105 | stream_destruct(&state.out); 106 | reactor_destruct(); 107 | assert_true(state.calls >= 2); 108 | close(fd[0]); 109 | close(fd[1]); 110 | } 111 | 112 | static void test_error(__attribute__((unused)) void **arg) 113 | { 114 | struct state state = {0}; 115 | int fd[2]; 116 | 117 | assert_true(pipe(fd) == 0); 118 | 119 | /* read from write only fd, and write to read only fd */ 120 | reactor_construct(); 121 | stream_construct(&state.in, callback, &state); 122 | stream_construct(&state.out, callback, &state); 123 | stream_open(&state.in, fd[0], 0); 124 | stream_write(&state.in, data("test", 4)); 125 | stream_flush(&state.in); 126 | reactor_loop_once(); 127 | stream_close(&state.in); 128 | stream_open(&state.out, fd[1], 0); 129 | reactor_loop(); 130 | stream_destruct(&state.in); 131 | stream_destruct(&state.out); 132 | reactor_destruct(); 133 | assert_int_equal(state.errors, 2); 134 | close(fd[0]); 135 | close(fd[1]); 136 | } 137 | 138 | int main() 139 | { 140 | const struct CMUnitTest tests[] = 141 | { 142 | cmocka_unit_test(test_domain_socket), 143 | cmocka_unit_test(test_pipe), 144 | cmocka_unit_test(test_error) 145 | }; 146 | 147 | return cmocka_run_group_tests(tests, NULL, NULL); 148 | } 149 | -------------------------------------------------------------------------------- /test/list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static void release(void *object) 10 | { 11 | free(*(void **) object); 12 | } 13 | 14 | static int compare(const void *a, const void *b) 15 | { 16 | const char *sa = *(const char **) a, *sb = *(const char **) b; 17 | 18 | return strcmp(sa, sb); 19 | } 20 | 21 | void test_core(__attribute__((unused)) void **state) 22 | { 23 | list_t l; 24 | char *a[] = {"a", "list", "of", "string", "pointers"}, **s; 25 | size_t i; 26 | 27 | list_construct(&l); 28 | for (i = 0; i < sizeof a / sizeof *a; i++) 29 | list_push_back(&l, &a[i], sizeof(char *)); 30 | 31 | s = list_front(&l); 32 | assert_string_equal(*s, "a"); 33 | 34 | list_push_front(&l, (char *[]) {"test"}, sizeof(char *)); 35 | s = list_front(&l); 36 | assert_string_equal(*s, "test"); 37 | list_erase(s, NULL); 38 | 39 | i = 0; 40 | list_foreach(&l, s) assert_string_equal(*s, a[i++]); 41 | 42 | i = sizeof a / sizeof *a; 43 | list_foreach_reverse(&l, s) assert_string_equal(*s, a[--i]); 44 | 45 | s = list_find(&l, compare, (char *[]) {"pointers"}); 46 | assert_string_equal(*s, "pointers"); 47 | 48 | s = list_find(&l, compare, (char *[]) {"foo"}); 49 | assert_true(!s); 50 | 51 | list_destruct(&l, NULL); 52 | } 53 | 54 | void test_list_release(__attribute__((unused)) void **state) 55 | { 56 | list_t l; 57 | void *p; 58 | int i; 59 | 60 | list_construct(&l); 61 | for (i = 0; i < 16; i++) 62 | { 63 | p = malloc(4096); 64 | list_push_back(&l, &p, sizeof p); 65 | } 66 | list_destruct(&l, release); 67 | } 68 | 69 | void test_unit(__attribute__((unused)) void **state) 70 | { 71 | list_t l, l2; 72 | int *p; 73 | 74 | list_construct(&l); 75 | 76 | list_insert(list_front(&l), (int[]) {1}, sizeof(int)); 77 | assert_int_equal(*(int *) list_front(&l), 1); 78 | list_clear(&l, NULL); 79 | 80 | p = list_insert(list_front(&l), NULL, sizeof(int)); 81 | *p = 42; 82 | assert_int_equal(*(int *) list_front(&l), 42); 83 | list_clear(&l, NULL); 84 | 85 | list_insert(list_previous(list_front(&l)), (int[]) {1}, sizeof(int)); 86 | assert_int_equal(*(int *) list_front(&l), 1); 87 | list_erase(list_back(&l), NULL); 88 | 89 | list_push_front(&l, (int[]) {1}, sizeof(int)); 90 | assert_int_equal(*(int *) list_front(&l), 1); 91 | list_erase(list_front(&l), NULL); 92 | 93 | list_push_back(&l, (int[]) {1}, sizeof(int)); 94 | assert_int_equal(*(int *) list_front(&l), 1); 95 | list_clear(&l, NULL); 96 | 97 | list_push_back(&l, (int[]) {1}, sizeof(int)); 98 | list_push_back(&l, (int[]) {2}, sizeof(int)); 99 | list_push_back(&l, (int[]) {3}, sizeof(int)); 100 | p = list_next(list_front(&l)); 101 | assert_int_equal(*p, 2); 102 | list_erase(p, NULL); 103 | p = list_next(list_front(&l)); 104 | assert_int_equal(*p, 3); 105 | 106 | list_clear(&l, NULL); 107 | list_construct(&l2); 108 | list_push_back(&l, (int[]) {1}, sizeof(int)); 109 | list_push_back(&l, (int[]) {2}, sizeof(int)); 110 | list_push_back(&l, (int[]) {3}, sizeof(int)); 111 | assert_true(list_empty(&l2)); 112 | list_splice(list_front(&l2), list_next(list_front(&l))); 113 | assert_int_equal(*(int *) list_front(&l2), 2); 114 | assert_int_equal(*(int *) list_next(list_front(&l)), 3); 115 | list_destruct(&l2, NULL); 116 | 117 | list_clear(&l, NULL); 118 | p = list_push_back(&l, (int[]) {1}, sizeof(int)); 119 | list_detach(p); 120 | list_erase(p, NULL); 121 | 122 | list_destruct(&l, NULL); 123 | } 124 | 125 | void test_edge(__attribute__((unused)) void **state) 126 | { 127 | list_t l1; 128 | int *i; 129 | 130 | list_construct(&l1); 131 | 132 | i = list_push_back(&l1, (int[]) {1}, sizeof(int)); 133 | list_splice(i, i); 134 | list_destruct(&l1, NULL); 135 | } 136 | 137 | int main() 138 | { 139 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_list_release), cmocka_unit_test(test_core), 140 | cmocka_unit_test(test_unit), cmocka_unit_test(test_edge)}; 141 | 142 | return cmocka_run_group_tests(tests, NULL, NULL); 143 | } 144 | -------------------------------------------------------------------------------- /test/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void set(void *p1, const void *p2) 12 | { 13 | uint32_t *a = p1; 14 | const uint32_t *b = p2; 15 | 16 | *a = b ? *b : 0; 17 | } 18 | 19 | static int equal(const void *p1, const void *p2) 20 | { 21 | const uint32_t *a = p1, *b = p2; 22 | 23 | return b ? *a == *b : *a == 0; 24 | } 25 | 26 | static size_t hash(const void *p) 27 | { 28 | return *(uint32_t *) p; 29 | } 30 | 31 | void test_map(__attribute__((unused)) void **state) 32 | { 33 | map_t m; 34 | uint32_t last = MAP_ELEMENTS_CAPACITY_MIN - 1, coll1 = 1 << 16, coll2 = 1 << 17; 35 | 36 | map_construct(&m, sizeof(uint32_t), set); 37 | 38 | /* erase non-existing object */ 39 | map_erase(&m, (uint32_t[]) {42}, hash, set, equal, NULL); 40 | assert_int_equal(map_size(&m), 0); 41 | 42 | /* erase object with duplicate */ 43 | map_insert(&m, (uint32_t[]) {1}, hash, set, equal, NULL); 44 | map_insert(&m, (uint32_t[]) {1 + coll1}, hash, set, equal, NULL); 45 | map_erase(&m, (uint32_t[]) {1}, hash, set, equal, NULL); 46 | assert_int_equal(map_size(&m), 1); 47 | 48 | /* erase object with empty succ */ 49 | map_clear(&m, set, NULL, NULL); 50 | map_insert(&m, (uint32_t[]) {1}, hash, set, equal, NULL); 51 | map_erase(&m, (uint32_t[]) {5}, hash, set, equal, NULL); 52 | assert_false(equal(map_at(&m, (uint32_t[]) {1}, hash, equal), NULL)); 53 | 54 | /* erase when w wraps */ 55 | map_clear(&m, set, NULL, NULL); 56 | map_insert(&m, (uint32_t[]) {last + coll1}, hash, set, equal, NULL); 57 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 58 | map_insert(&m, (uint32_t[]) {last + coll2}, hash, set, equal, NULL); 59 | map_erase(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 60 | assert_false(equal(map_at(&m, (uint32_t[]) {last + coll1}, hash, equal), NULL)); 61 | assert_false(equal(map_at(&m, (uint32_t[]) {last + coll2}, hash, equal), NULL)); 62 | 63 | /* erase when i wraps */ 64 | map_clear(&m, set, NULL, NULL); 65 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 66 | map_erase(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 67 | assert_true(equal(map_at(&m, (uint32_t[]) {last}, hash, equal), NULL)); 68 | 69 | /* erase when i wraps and w < i */ 70 | map_clear(&m, set, NULL, NULL); 71 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 72 | map_insert(&m, (uint32_t[]) {coll1}, hash, set, equal, NULL); 73 | map_insert(&m, (uint32_t[]) {last + coll1}, hash, set, equal, NULL); 74 | map_erase(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 75 | assert_false(equal(map_at(&m, (uint32_t[]) {coll1}, hash, equal), NULL)); 76 | assert_false(equal(map_at(&m, (uint32_t[]) {last + coll1}, hash, equal), NULL)); 77 | assert_true(equal(map_at(&m, (uint32_t[]) {last}, hash, equal), NULL)); 78 | 79 | /* erase when i wraps and w > o */ 80 | map_clear(&m, set, NULL, NULL); 81 | map_insert(&m, (uint32_t[]) {last - 1}, hash, set, equal, NULL); 82 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 83 | map_insert(&m, (uint32_t[]) {last + coll1}, hash, set, equal, NULL); 84 | map_erase(&m, (uint32_t[]) {last - 1}, hash, set, equal, NULL); 85 | assert_false(equal(map_at(&m, (uint32_t[]) {last}, hash, equal), NULL)); 86 | assert_false(equal(map_at(&m, (uint32_t[]) {last + coll1}, hash, equal), NULL)); 87 | assert_true(equal(map_at(&m, (uint32_t[]) {last - 1}, hash, equal), NULL)); 88 | 89 | /* erase when j wraps */ 90 | map_clear(&m, set, NULL, NULL); 91 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 92 | map_insert(&m, (uint32_t[]) {coll1}, hash, set, equal, NULL); 93 | map_insert(&m, (uint32_t[]) {coll2}, hash, set, equal, NULL); 94 | map_erase(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 95 | assert_false(equal(map_at(&m, (uint32_t[]) {coll1}, hash, equal), NULL)); 96 | assert_false(equal(map_at(&m, (uint32_t[]) {coll2}, hash, equal), NULL)); 97 | assert_true(equal(map_at(&m, (uint32_t[]) {last}, hash, equal), NULL)); 98 | 99 | map_destruct(&m, NULL, NULL); 100 | } 101 | 102 | int main() 103 | { 104 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_map)}; 105 | 106 | return cmocka_run_group_tests(tests, NULL, NULL); 107 | } 108 | -------------------------------------------------------------------------------- /src/reactor/notify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void notify_read(reactor_event_t *); 9 | 10 | static void notify_update(notify_t *notify) 11 | { 12 | if (notify->read) 13 | { 14 | if (list_empty(¬ify->entries)) 15 | { 16 | reactor_cancel(notify->read, NULL, NULL); 17 | notify->read = 0; 18 | } 19 | } 20 | else 21 | { 22 | if (!list_empty(¬ify->entries)) 23 | { 24 | notify->read = reactor_read(notify_read, notify, notify->fd, notify->buffer, sizeof notify->buffer, 0); 25 | } 26 | } 27 | } 28 | 29 | static void notify_next(reactor_event_t *event) 30 | { 31 | notify_t *notify = event->state; 32 | 33 | notify->next = 0; 34 | notify_update(notify); 35 | } 36 | 37 | static void notify_read(reactor_event_t *event) 38 | { 39 | notify_t *notify = event->state; 40 | struct inotify_event *i; 41 | notify_entry_t *e; 42 | char *p; 43 | int result = event->data; 44 | bool abort = false; 45 | 46 | notify->read = 0; 47 | assert(result >= 0); 48 | notify->abort = &abort; 49 | for (p = notify->buffer; p < notify->buffer + result; p += sizeof(struct inotify_event) + i->len) 50 | { 51 | i = (struct inotify_event *) p; 52 | e = (notify_entry_t *) mapi_at(¬ify->ids, i->wd); 53 | if (e) 54 | reactor_call(¬ify->user, NOTIFY_EVENT, (uint64_t) (notify_event_t[]) 55 | {{.mask = i->mask, .id = e->id, .name = i->len ? i->name : "", .path = e->path}}); 56 | if (abort) 57 | return; 58 | } 59 | notify->abort = NULL; 60 | notify_update(notify); 61 | } 62 | 63 | static void notify_remove_entry(notify_t *notify, notify_entry_t *entry) 64 | { 65 | int e; 66 | 67 | e = inotify_rm_watch(notify->fd, entry->id); 68 | assert(e == 0); 69 | mapi_erase(¬ify->ids, entry->id, NULL); 70 | maps_erase(¬ify->paths, entry->path, NULL); 71 | free(entry->path); 72 | list_erase(entry, NULL); 73 | if (!notify->next) 74 | notify->next = reactor_next(notify_next, notify); 75 | } 76 | 77 | void notify_construct(notify_t *notify, reactor_callback_t *callback, void *state) 78 | { 79 | *notify = (notify_t) {.user = reactor_user_define(callback, state), .fd = -1}; 80 | list_construct(¬ify->entries); 81 | mapi_construct(¬ify->ids); 82 | maps_construct(¬ify->paths); 83 | } 84 | 85 | int notify_add(notify_t *notify, char *path, uint32_t mask) 86 | { 87 | notify_entry_t *entry; 88 | int id; 89 | 90 | entry = (notify_entry_t *) maps_at(¬ify->paths, path); 91 | if (entry) 92 | return entry->id; 93 | 94 | id = inotify_add_watch(notify->fd, path, mask); 95 | if (id == -1) 96 | return id; 97 | 98 | entry = list_push_back(¬ify->entries, NULL, sizeof *entry); 99 | entry->id = id; 100 | entry->path = strdup(path); 101 | 102 | mapi_insert(¬ify->ids, entry->id, (uint64_t) entry, NULL); 103 | maps_insert(¬ify->paths, entry->path, (uint64_t) entry, NULL); 104 | notify_update(notify); 105 | return id; 106 | } 107 | 108 | bool notify_remove_path(notify_t *notify, char *path) 109 | { 110 | notify_entry_t *e; 111 | 112 | e = (notify_entry_t *) maps_at(¬ify->paths, path); 113 | if (!e) 114 | return false; 115 | notify_remove_entry(notify, e); 116 | return true; 117 | } 118 | 119 | bool notify_remove_id(notify_t *notify, int id) 120 | { 121 | notify_entry_t *e; 122 | 123 | e = (notify_entry_t *) mapi_at(¬ify->ids, id); 124 | if (!e) 125 | return false; 126 | notify_remove_entry(notify, e); 127 | return true; 128 | } 129 | 130 | void notify_open(notify_t *notify) 131 | { 132 | assert(notify->fd == -1); 133 | notify->fd = inotify_init1(IN_CLOEXEC); 134 | assert(notify->fd >= 0); 135 | } 136 | 137 | void notify_close(notify_t *notify) 138 | { 139 | if (notify->fd == -1) 140 | return; 141 | 142 | if (notify->abort) 143 | *notify->abort = true; 144 | 145 | if (notify->next) 146 | { 147 | reactor_cancel(notify->next, NULL, NULL); 148 | notify->next = 0; 149 | } 150 | 151 | while (!list_empty(¬ify->entries)) 152 | notify_remove_entry(notify, list_front(¬ify->entries)); 153 | (void) reactor_close(NULL, NULL, notify->fd); 154 | notify->fd = -1; 155 | } 156 | 157 | void notify_destruct(notify_t *notify) 158 | { 159 | notify_close(notify); 160 | list_destruct(¬ify->entries, NULL); 161 | mapi_destruct(¬ify->ids, NULL); 162 | maps_destruct(¬ify->paths, NULL); 163 | } 164 | -------------------------------------------------------------------------------- /test/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "reactor.h" 14 | 15 | static void test_queue_sync(__attribute__((unused)) void **arg) 16 | { 17 | queue_t q; 18 | queue_producer_t p; 19 | queue_consumer_t c; 20 | int *x; 21 | 22 | queue_construct(&q, sizeof *x); 23 | queue_producer_construct(&p); 24 | queue_producer_open(&p, &q); 25 | queue_producer_push_sync(&p, (int[]) {1}); 26 | queue_producer_push_sync(&p, (int[]) {2}); 27 | queue_producer_push_sync(&p, (int[]) {3}); 28 | queue_producer_destruct(&p); 29 | 30 | queue_consumer_construct(&c, NULL, NULL); 31 | queue_consumer_open(&c, &q, 1); 32 | assert_int_equal(*(int *) queue_consumer_pop_sync(&c), 1); 33 | assert_int_equal(*(int *) queue_consumer_pop_sync(&c), 2); 34 | assert_int_equal(*(int *) queue_consumer_pop_sync(&c), 3); 35 | queue_consumer_destruct(&c); 36 | queue_destruct(&q); 37 | } 38 | 39 | struct state 40 | { 41 | queue_t queue; 42 | queue_producer_t producer; 43 | queue_consumer_t consumer; 44 | uint64_t value; 45 | size_t calls; 46 | }; 47 | 48 | static void callback(reactor_event_t *event) 49 | { 50 | struct state *s = event->state; 51 | 52 | s->calls++; 53 | if (s->calls == s->value) 54 | { 55 | queue_producer_push(&s->producer, (int[]){0}); 56 | queue_producer_close(&s->producer); 57 | queue_consumer_close(&s->consumer); 58 | } 59 | } 60 | 61 | extern void queue_producer_write(reactor_event_t *); 62 | extern void queue_consumer_read(reactor_event_t *); 63 | 64 | static void test_queue(__attribute__((unused)) void **arg) 65 | { 66 | struct state s = {.value = 1000}; 67 | int i; 68 | 69 | reactor_construct(); 70 | queue_construct(&s.queue, sizeof (int)); 71 | queue_consumer_construct(&s.consumer, callback, &s); 72 | queue_consumer_open(&s.consumer, &s.queue, 1); 73 | queue_consumer_pop(&s.consumer); 74 | queue_consumer_pop(&s.consumer); 75 | queue_producer_construct(&s.producer); 76 | queue_producer_open(&s.producer, &s.queue); 77 | for (i = 0; i < (int) s.value; i ++) 78 | queue_producer_push(&s.producer, &i); 79 | reactor_loop(); 80 | queue_producer_destruct(&s.producer); 81 | queue_consumer_destruct(&s.consumer); 82 | queue_destruct(&s.queue); 83 | reactor_destruct(); 84 | assert_int_equal(s.calls, 1000); 85 | 86 | /* cancel read */ 87 | reactor_construct(); 88 | queue_construct(&s.queue, sizeof (int)); 89 | queue_consumer_construct(&s.consumer, callback, &s); 90 | queue_consumer_open(&s.consumer, &s.queue, 1); 91 | queue_consumer_pop(&s.consumer); 92 | queue_consumer_close(&s.consumer); 93 | reactor_loop(); 94 | queue_consumer_destruct(&s.consumer); 95 | queue_destruct(&s.queue); 96 | reactor_destruct(); 97 | 98 | /* producer errors */ 99 | reactor_construct(); 100 | queue_construct(&s.queue, sizeof (int)); 101 | queue_producer_construct(&s.producer); 102 | queue_producer_open(&s.producer, &s.queue); 103 | queue_producer_write((reactor_event_t[]){reactor_event_define(&s.producer, REACTOR_CALL, -EINTR)}); 104 | expect_assert_failure(queue_producer_write((reactor_event_t[]){reactor_event_define(&s.producer, REACTOR_CALL, -EBADF)})); 105 | expect_assert_failure(queue_producer_write((reactor_event_t[]){reactor_event_define(&s.producer, REACTOR_CALL, 1)})); 106 | queue_producer_destruct(&s.producer); 107 | queue_destruct(&s.queue); 108 | reactor_destruct(); 109 | 110 | /* consumer errors */ 111 | reactor_construct(); 112 | queue_construct(&s.queue, sizeof (int)); 113 | queue_consumer_construct(&s.consumer, NULL, NULL); 114 | queue_consumer_open(&s.consumer, &s.queue, 1); 115 | queue_consumer_read((reactor_event_t[]){reactor_event_define(&s.consumer, REACTOR_CALL, -EINTR)}); 116 | expect_assert_failure(queue_consumer_read((reactor_event_t[]){reactor_event_define(&s.consumer, REACTOR_CALL, -EBADF)})); 117 | expect_assert_failure(queue_consumer_read((reactor_event_t[]){reactor_event_define(&s.consumer, REACTOR_CALL, 1)})); 118 | queue_consumer_destruct(&s.consumer); 119 | queue_destruct(&s.queue); 120 | reactor_destruct(); 121 | } 122 | 123 | int main() 124 | { 125 | const struct CMUnitTest tests[] = 126 | { 127 | cmocka_unit_test(test_queue_sync), 128 | cmocka_unit_test(test_queue) 129 | }; 130 | 131 | return cmocka_run_group_tests(tests, NULL, NULL); 132 | } 133 | -------------------------------------------------------------------------------- /src/reactor/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static size_t map_roundup(size_t s) 8 | { 9 | s--; 10 | s |= s >> 1; 11 | s |= s >> 2; 12 | s |= s >> 4; 13 | s |= s >> 8; 14 | s |= s >> 16; 15 | s |= s >> 32; 16 | s++; 17 | 18 | return s; 19 | } 20 | 21 | static void *map_element(const map_t *map, size_t position) 22 | { 23 | return (char *) map->elements + (position * map->element_size); 24 | } 25 | 26 | static void map_release_all(map_t *map, map_equal_t *equal, map_release_t *release) 27 | { 28 | size_t i; 29 | 30 | if (release) 31 | for (i = 0; i < map->elements_capacity; i++) 32 | if (!equal(map_element(map, i), NULL)) 33 | release(map_element(map, i)); 34 | } 35 | 36 | static void map_rehash(map_t *map, size_t size, map_hash_t *hash, map_set_t *set, map_equal_t *equal) 37 | { 38 | map_t new; 39 | size_t i; 40 | 41 | size = map_roundup(size); 42 | new = *map; 43 | new.elements_count = 0; 44 | new.elements_capacity = size; 45 | new.elements = malloc(new.elements_capacity *new.element_size); 46 | 47 | for (i = 0; i < new.elements_capacity; i++) 48 | set(map_element(&new, i), NULL); 49 | 50 | if (map->elements) 51 | { 52 | for (i = 0; i < map->elements_capacity; i++) 53 | if (!equal(map_element(map, i), NULL)) 54 | map_insert(&new, map_element(map, i), hash, set, equal, NULL); 55 | free(map->elements); 56 | } 57 | 58 | *map = new; 59 | } 60 | 61 | /* constructor/destructor */ 62 | 63 | void map_construct(map_t *map, size_t element_size, map_set_t *set) 64 | { 65 | map->elements = NULL; 66 | map->elements_count = 0; 67 | map->elements_capacity = 0; 68 | map->element_size = element_size; 69 | map_rehash(map, MAP_ELEMENTS_CAPACITY_MIN, NULL, set, NULL); 70 | } 71 | 72 | void map_destruct(map_t *map, map_equal_t *equal, map_release_t *release) 73 | { 74 | map_release_all(map, equal, release); 75 | free(map->elements); 76 | } 77 | 78 | /* capacity */ 79 | 80 | size_t map_size(const map_t *map) 81 | { 82 | return map->elements_count; 83 | } 84 | 85 | void map_reserve(map_t *map, size_t size, map_hash_t *hash, map_set_t *set, map_equal_t *equal) 86 | { 87 | size *= 2; 88 | if (size > map->elements_capacity) 89 | map_rehash(map, size, hash, set, equal); 90 | } 91 | 92 | /* element access */ 93 | 94 | void *map_at(const map_t *map, const void *element, map_hash_t *hash, map_equal_t *equal) 95 | { 96 | size_t i; 97 | void *test; 98 | 99 | i = hash(element); 100 | while (1) 101 | { 102 | i &= map->elements_capacity - 1; 103 | test = map_element(map, i); 104 | if (equal(test, NULL) || equal(test, element)) 105 | return test; 106 | i++; 107 | } 108 | } 109 | 110 | /* modifiers */ 111 | 112 | void map_insert(map_t *map, void *element, map_hash_t *hash, map_set_t *set, map_equal_t *equal, map_release_t *release) 113 | { 114 | void *test; 115 | 116 | map_reserve(map, map->elements_count + 1, hash, set, equal); 117 | test = map_at(map, element, hash, equal); 118 | if (equal(test, NULL)) 119 | { 120 | set(test, element); 121 | map->elements_count++; 122 | } 123 | else if (release) 124 | release(element); 125 | } 126 | 127 | void map_erase(map_t *map, const void *element, map_hash_t *hash, map_set_t *set, map_equal_t *equal, 128 | map_release_t *release) 129 | { 130 | void *test; 131 | size_t i, j, k; 132 | 133 | i = hash(element); 134 | while (1) 135 | { 136 | i &= map->elements_capacity - 1; 137 | test = map_element(map, i); 138 | if (equal(test, NULL)) 139 | return; 140 | if (equal(test, element)) 141 | break; 142 | i++; 143 | } 144 | 145 | if (release) 146 | release(test); 147 | map->elements_count--; 148 | 149 | j = i; 150 | while (1) 151 | { 152 | j = (j + 1) & (map->elements_capacity - 1); 153 | if (equal(map_element(map, j), NULL)) 154 | break; 155 | 156 | k = hash(map_element(map, j)) & (map->elements_capacity - 1); 157 | if ((i < j && (k <= i || k > j)) || (i > j && (k <= i && k > j))) 158 | { 159 | set(map_element(map, i), map_element(map, j)); 160 | i = j; 161 | } 162 | } 163 | 164 | set(map_element(map, i), NULL); 165 | } 166 | 167 | void map_clear(map_t *map, map_set_t *set, map_equal_t *equal, map_release_t *release) 168 | { 169 | map_release_all(map, equal, release); 170 | free(map->elements); 171 | map->elements = NULL; 172 | map->elements_count = 0; 173 | map->elements_capacity = 0; 174 | map_rehash(map, MAP_ELEMENTS_CAPACITY_MIN, NULL, set, NULL); 175 | } 176 | -------------------------------------------------------------------------------- /test/network.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "reactor.h" 14 | 15 | struct state 16 | { 17 | network_t id; 18 | bool cancel; 19 | int port; 20 | }; 21 | 22 | static void test_accept_callback(reactor_event_t *event) 23 | { 24 | struct state *state = event->state; 25 | int e, fd = event->data; 26 | struct sockaddr_in sin; 27 | socklen_t len = sizeof sin; 28 | 29 | switch (event->type) 30 | { 31 | case NETWORK_ACCEPT_BIND: 32 | assert_true(fd >= 0); 33 | if (state->cancel) 34 | { 35 | network_cancel(state->id); 36 | break; 37 | } 38 | e = getsockname(fd, (struct sockaddr *) &sin, &len); 39 | assert_int_equal(e, 0); 40 | state->port = ntohs(sin.sin_port); 41 | network_connect(test_accept_callback, NULL, "127.0.0.1", state->port); 42 | break; 43 | case NETWORK_ACCEPT: 44 | assert_true(fd >= 0); 45 | (void) close(fd); 46 | network_cancel(state->id); 47 | break; 48 | case NETWORK_CONNECT: 49 | assert_true(fd >= 0); 50 | (void) close(fd); 51 | break; 52 | default: 53 | assert_false(true); 54 | } 55 | } 56 | 57 | static void test_accept(__attribute__((unused)) void **arg) 58 | { 59 | struct state state = {0}; 60 | 61 | /* specify no port and connect */ 62 | reactor_construct(); 63 | state.id = network_accept(test_accept_callback, &state, "127.0.0.1", 0, NETWORK_REUSEADDR | NETWORK_REUSEPORT); 64 | reactor_loop(); 65 | reactor_destruct(); 66 | 67 | /* specify ipv4 port */ 68 | reactor_construct(); 69 | state.cancel = true; 70 | state.id = network_accept(test_accept_callback, &state, "127.0.0.1", state.port, NETWORK_REUSEADDR); 71 | reactor_loop(); 72 | reactor_destruct(); 73 | 74 | /* specify ipv6 port */ 75 | reactor_construct(); 76 | state.id = network_accept(test_accept_callback, &state, "::1", state.port, NETWORK_REUSEADDR); 77 | reactor_loop(); 78 | reactor_destruct(); 79 | } 80 | 81 | static void test_connect_next(reactor_event_t *event) 82 | { 83 | network_t *id = event->state; 84 | 85 | network_cancel(*id); 86 | } 87 | 88 | static void test_connect(__attribute__((unused)) void **arg) 89 | { 90 | network_t id; 91 | 92 | /* connect and cancel while connecting */ 93 | reactor_construct(); 94 | id = network_connect(NULL, NULL, "127.0.0.1", 0); 95 | reactor_next(test_connect_next, &id); 96 | reactor_loop(); 97 | reactor_destruct(); 98 | } 99 | 100 | static void test_cancel(__attribute__((unused)) void **arg) 101 | { 102 | network_t id; 103 | 104 | reactor_construct(); 105 | id = network_accept(NULL, NULL, "1.2.3.4", 80, 0); 106 | network_cancel(id); 107 | id = network_connect(NULL, NULL, "1.2.3.4", 80); 108 | network_cancel(id); 109 | reactor_loop(); 110 | reactor_destruct(); 111 | } 112 | 113 | static void test_error_callback(reactor_event_t *event) 114 | { 115 | int fd = event->data, *errors = event->state; 116 | 117 | switch (event->type) 118 | { 119 | case NETWORK_ACCEPT_BIND: 120 | close(fd); 121 | break; 122 | case NETWORK_ERROR: 123 | (*errors)++; 124 | break; 125 | } 126 | } 127 | 128 | static void test_error(__attribute__((unused)) void **arg) 129 | { 130 | struct sockaddr_in sin = {.sin_family = 99}; 131 | int errors = 0; 132 | 133 | // error on socket() 134 | reactor_construct(); 135 | network_connect_address(test_error_callback, &errors, (struct sockaddr *) &sin, sizeof sin); 136 | network_accept_address(test_error_callback, &errors, (struct sockaddr *) &sin, sizeof sin, 0); 137 | reactor_loop(); 138 | assert_int_equal(errors, 2); 139 | 140 | // error on bind(); 141 | errors = 0; 142 | sin.sin_addr.s_addr = ntohl(0x12345678); 143 | sin.sin_family = AF_INET; 144 | network_accept_address(test_error_callback, &errors, (struct sockaddr *) &sin, sizeof sin, 0); 145 | reactor_loop(); 146 | assert_int_equal(errors, 1); 147 | 148 | // error on accept since socket is closed; 149 | errors = 0; 150 | network_accept(test_error_callback, &errors, "127.0.0.1", 0, NETWORK_REUSEADDR); 151 | reactor_loop(); 152 | assert_int_equal(errors, 1); 153 | 154 | // error on accept resolve; 155 | errors = 0; 156 | network_accept(test_error_callback, &errors, "999.999", 0, 0); 157 | reactor_loop(); 158 | assert_int_equal(errors, 1); 159 | 160 | // error on accept bind; 161 | errors = 0; 162 | network_accept(test_error_callback, &errors, "12.34.56.78", 0, 0); 163 | reactor_loop(); 164 | assert_int_equal(errors, 1); 165 | 166 | /* specify socket */ 167 | errors = 0; 168 | network_accept_socket(test_error_callback, &errors, -1); 169 | reactor_loop(); 170 | assert_int_equal(errors, 1); 171 | 172 | /* connect error */ 173 | errors = 0; 174 | network_connect(test_error_callback, &errors, "127.0.0.123", 4567); 175 | reactor_loop(); 176 | assert_int_equal(errors, 1); 177 | 178 | /* connect error */ 179 | errors = 0; 180 | network_connect(test_error_callback, &errors, "::2", 4567); 181 | reactor_loop(); 182 | assert_int_equal(errors, 1); 183 | 184 | // error on connect resolve; 185 | errors = 0; 186 | network_connect(test_error_callback, &errors, "999.999", 0); 187 | reactor_loop(); 188 | assert_int_equal(errors, 1); 189 | 190 | reactor_destruct(); 191 | } 192 | 193 | int main() 194 | { 195 | const struct CMUnitTest tests[] = 196 | { 197 | cmocka_unit_test(test_accept), 198 | cmocka_unit_test(test_connect), 199 | cmocka_unit_test(test_cancel), 200 | cmocka_unit_test(test_error) 201 | }; 202 | 203 | return cmocka_run_group_tests(tests, NULL, NULL); 204 | } 205 | -------------------------------------------------------------------------------- /test/http.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "reactor.h" 13 | 14 | struct test 15 | { 16 | const char *request; 17 | int result; 18 | int remaining; 19 | }; 20 | 21 | struct test request_tests[] = 22 | { 23 | {"", 0, 0}, 24 | {"GET / HTTP/1.0\r\nA: B\r\n\r\n", 1, 0}, 25 | {"GET / HTTP/1.0\r\n", 0, 16}, 26 | {"GET / XXXX/1.0\r\n", -1, 16}, 27 | {"POST / HTTP/1.1\r\n" 28 | "Host: localhost\r\n" 29 | "\r\n", 1, 0}, 30 | {"POST / HTTP/1.1\r\n" 31 | "Host: localhost\r\n" 32 | "Transfer-Encoding: chunked\r\n" 33 | "\r\n" 34 | "0\r\n" 35 | "\r\n", 1, 0}, 36 | {"POST / HTTP/1.1\r\n" 37 | "Host: localhost\r\n" 38 | "Transfer-Encoding: chunked\r\n" 39 | "\r\n" 40 | "1\r\n" 41 | "a\r\n" 42 | "0\r\n" 43 | "\r\n", 1, 0}, 44 | {"POST / HTTP/1.1\r\n" 45 | "Host: localhost\r\n" 46 | "Transfer-Encoding: chunked\r\n" 47 | "\r\n" 48 | "\t 0 \r\n" 49 | "\r\n", 1, 0}, 50 | {"POST / HTTP/1.1\r\n" 51 | "Host: localhost\r\n" 52 | "Transfer-Encoding: chunked\r\n" 53 | "\r\n" 54 | "\t 0; xxxxx xxxx \r\n" 55 | "\r\n", 1, 0}, 56 | {"POST / HTTP/1.1\r\n" 57 | "Host: localhost\r\n" 58 | "Transfer-Encoding: chunked\r\n" 59 | "\r\n" 60 | "\t 0; xxxxx xxxx \n" 61 | "\r\n", -1, 85}, 62 | {"POST / HTTP/1.1\r\n" 63 | "Host: localhost\r\n" 64 | "Transfer-Encoding: chunked\r\n" 65 | "\r\n" 66 | "\t 0 xxxxx xxxx \r\n" 67 | "\r\n", -1, 86}, 68 | {"POST / HTTP/1.1\r\n" 69 | "Host: localhost\r\n" 70 | "Transfer-Encoding: chunked\r\n" 71 | "\r\n" 72 | "FFFFFFFFFFFFFFFFFFFFF\r\n" 73 | "\r\n", -1, 89}, 74 | {"POST / HTTP/1.1\r\n" 75 | "Host: localhost\r\n" 76 | "Transfer-Encoding: chunked\r\n" 77 | "\r\n" 78 | "\t0\r \n" 79 | "\r\n", -1, 71}, 80 | {"POST / HTTP/1.1\r\n" 81 | "Host: localhost\r\n" 82 | "Transfer-Encoding: chunked\r\n" 83 | "\r\n" 84 | "0", 0, 65}, 85 | {"POST / HTTP/1.1\r\n" 86 | "Host: localhost\r\n" 87 | "Transfer-Encoding: chunked\r\n" 88 | "\r\n" 89 | "0\r\n", 0, 67}, 90 | {"POST / HTTP/1.1\r\n" 91 | "Host: localhost\r\n" 92 | "Transfer-Encoding: chunked\r\n" 93 | "\r\n" 94 | "FF\r\n" 95 | "\r\n" 96 | , 0, 70}, 97 | {"POST / HTTP/1.1\r\n" 98 | "Host: localhost\r\n" 99 | "Transfer-Encoding: chunked\r\n" 100 | "\r\n" 101 | "\r\n", -1, 66}, 102 | {"POST / HTTP/1.1\r\n" 103 | "Host: localhost\r\n" 104 | "Content-Length: 0\r\n" 105 | "\r\n", 1, 0}, 106 | {"POST / HTTP/1.1\r\n" 107 | "Host: localhost\r\n" 108 | "Content-Length: 999\r\n" 109 | "\r\n", 0, 57}, 110 | {"POST / HTTP/1.1\r\n" 111 | "Host: localhost\r\n" 112 | "Transfer-Encoding: chunked\r\n" 113 | "Content-Length: 0\r\n" 114 | "\r\n", -1, 83}, 115 | {"POST / HTTP/1.1\r\n" 116 | "Host: localhost\r\n" 117 | "Transfer-Encoding: XXXX\r\n" 118 | "\r\n", -1, 61}, 119 | }; 120 | 121 | static void test_read_request(__attribute__((unused)) void **arg) 122 | { 123 | stream_t s; 124 | string_t method, target; 125 | data_t body; 126 | http_field_t fields[16]; 127 | size_t fields_count; 128 | size_t i; 129 | int n; 130 | 131 | for (i = 0; i < sizeof request_tests / sizeof *request_tests; i++) 132 | { 133 | stream_construct(&s, NULL, NULL); 134 | buffer_append(&s.input, string(request_tests[i].request)); 135 | fields_count = 16; 136 | n = http_read_request(&s, &method, &target, &body, fields, &fields_count); 137 | assert_int_equal(n, request_tests[i].result); 138 | assert_int_equal(data_size(stream_read(&s)), request_tests[i].remaining); 139 | stream_destruct(&s); 140 | } 141 | } 142 | 143 | static void test_write_response(__attribute__((unused)) void **arg) 144 | { 145 | stream_t s; 146 | char blob[1024] = {0}; 147 | 148 | stream_construct(&s, NULL, NULL); 149 | 150 | http_write_response(&s, string("200 OK"), string("Wed, 16 Aug 2023 10:29:23 GMT"), string("text/plain"), string("Hello"), NULL, 0); 151 | assert_true(string_equal(string( 152 | "HTTP/1.1 200 OK\r\n" 153 | "Server: *\r\n" 154 | "Date: Wed, 16 Aug 2023 10:29:23 GMT\r\n" 155 | "Content-Type: text/plain\r\n" 156 | "Content-Length: 5\r\n" 157 | "\r\n" 158 | "Hello" 159 | ), buffer_data(&s.output_waiting))); 160 | buffer_clear(&s.output_waiting); 161 | 162 | http_write_response(&s, string("200 OK"), string("Wed, 16 Aug 2023 10:29:23 GMT"), string("text/plain"), string("Hello, again"), 163 | (http_field_t []) {http_field_define(string("Cookie"), string("Test"))}, 1); 164 | assert_true(string_equal(string( 165 | "HTTP/1.1 200 OK\r\n" 166 | "Server: *\r\n" 167 | "Date: Wed, 16 Aug 2023 10:29:23 GMT\r\n" 168 | "Content-Type: text/plain\r\n" 169 | "Content-Length: 12\r\n" 170 | "Cookie: Test\r\n" 171 | "\r\n" 172 | "Hello, again" 173 | ), buffer_data(&s.output_waiting))); 174 | buffer_clear(&s.output_waiting); 175 | 176 | http_write_response(&s, string("200 OK"), string("Wed, 16 Aug 2023 10:29:23 GMT"), string("text/plain"), data(blob, sizeof blob), NULL, 0); 177 | assert_int_equal(buffer_size(&s.output_waiting), 1139); 178 | buffer_clear(&s.output_waiting); 179 | 180 | stream_destruct(&s); 181 | } 182 | 183 | int main() 184 | { 185 | const struct CMUnitTest tests[] = 186 | { 187 | cmocka_unit_test(test_read_request), 188 | cmocka_unit_test(test_write_response) 189 | }; 190 | 191 | return cmocka_run_group_tests(tests, NULL, NULL); 192 | } 193 | -------------------------------------------------------------------------------- /src/reactor/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const size_t STREAM_BLOCK_SIZE = 16384; 9 | 10 | enum 11 | { 12 | STREAM_TYPE_SOCKET, 13 | STREAM_TYPE_FILE 14 | }; 15 | 16 | static void stream_update(stream_t *); 17 | 18 | static void stream_release_buffer(reactor_event_t *event) 19 | { 20 | free(event->state); 21 | } 22 | 23 | static void stream_recv(reactor_event_t *event) 24 | { 25 | stream_t *stream = event->state; 26 | int result = event->data; 27 | bool abort = false; 28 | 29 | stream->read = 0; 30 | if (reactor_unlikely(result <= 0)) 31 | { 32 | reactor_call(&stream->user, result == 0 ? STREAM_CLOSE : STREAM_ERROR, -result); 33 | return; 34 | } 35 | 36 | stream->input_offset += result; 37 | buffer_resize(&stream->input, buffer_size(&stream->input) + result); 38 | stream->abort = &abort; 39 | reactor_call(&stream->user, STREAM_READ, 0); 40 | if (reactor_unlikely(abort)) 41 | return; 42 | stream->abort = NULL; 43 | stream_update(stream); 44 | } 45 | 46 | static void stream_send(reactor_event_t *event) 47 | { 48 | stream_t *stream = event->state; 49 | int result = event->data; 50 | 51 | stream->write = 0; 52 | if (result <= 0) 53 | { 54 | reactor_call(&stream->user, STREAM_ERROR, -result); 55 | return; 56 | } 57 | 58 | stream->output_offset += result; 59 | stream->output_partial += result; 60 | if (stream->output_partial == buffer_size(&stream->output_writing)) 61 | stream->output_partial = 0; 62 | stream_update(stream); 63 | } 64 | 65 | static void stream_input(stream_t *stream) 66 | { 67 | const size_t size = STREAM_BLOCK_SIZE; 68 | 69 | if (reactor_likely(stream->input_consumed)) 70 | { 71 | buffer_erase(&stream->input, 0, stream->input_consumed); 72 | stream->input_consumed = 0; 73 | } 74 | 75 | buffer_reserve(&stream->input, buffer_size(&stream->input) + size); 76 | 77 | #ifdef UNIT_TESTING 78 | memset(buffer_end(&stream->input), 0, size); 79 | #endif 80 | 81 | stream->read = reactor_likely(stream->type == STREAM_TYPE_SOCKET) ? 82 | reactor_recv(stream_recv, stream, stream->fd, buffer_end(&stream->input), size, 0) : 83 | reactor_read(stream_recv, stream, stream->fd, buffer_end(&stream->input), size, stream->input_offset); 84 | } 85 | 86 | 87 | static void stream_output(stream_t *stream) 88 | { 89 | data_t data; 90 | 91 | data = data_offset(buffer_data(&stream->output_writing), stream->output_partial); 92 | stream->write = reactor_likely(stream->type == STREAM_TYPE_SOCKET) ? 93 | reactor_send(stream_send, stream, stream->fd, data_base(data), data_size(data), 0) : 94 | reactor_write(stream_send, stream, stream->fd, data_base(data), data_size(data), stream->output_offset); 95 | } 96 | 97 | static void stream_update(stream_t *stream) 98 | { 99 | if (reactor_unlikely(!stream_is_open(stream)) || stream->write) 100 | return; 101 | 102 | if (stream->output_partial) 103 | { 104 | stream_output(stream); 105 | } 106 | else if (stream->output_flushed) 107 | { 108 | stream->output_partial = 0; 109 | buffer_clear(&stream->output_writing); 110 | buffer_append(&stream->output_writing, data_offset(buffer_data(&stream->output_waiting), stream->output_flushed)); 111 | buffer_resize(&stream->output_waiting, stream->output_flushed); 112 | buffer_switch(&stream->output_waiting, &stream->output_writing); 113 | stream->output_flushed = 0; 114 | stream_output(stream); 115 | } 116 | else if (!stream->read && (!stream->flags & STREAM_WRITE_ONLY)) 117 | { 118 | stream_input(stream); 119 | } 120 | } 121 | 122 | void stream_construct(stream_t *stream, reactor_callback_t *callback, void *state) 123 | { 124 | *stream = (stream_t) {.user = reactor_user_define(callback, state), .fd = -1}; 125 | buffer_construct(&stream->input); 126 | buffer_construct(&stream->output_writing); 127 | buffer_construct(&stream->output_waiting); 128 | } 129 | 130 | void stream_destruct(stream_t *stream) 131 | { 132 | stream_close(stream); 133 | if (stream->abort) 134 | *stream->abort = true; 135 | buffer_destruct(&stream->input); 136 | buffer_destruct(&stream->output_writing); 137 | buffer_destruct(&stream->output_waiting); 138 | } 139 | 140 | void stream_open(stream_t *stream, int fd, int flags) 141 | { 142 | struct stat st; 143 | int e; 144 | 145 | stream->fd = fd; 146 | stream->flags = flags; 147 | e = fstat(fd, &st); 148 | assert(e == 0); 149 | stream->type = S_ISSOCK(st.st_mode) ? STREAM_TYPE_SOCKET : STREAM_TYPE_FILE; 150 | stream_update(stream); 151 | } 152 | 153 | int stream_fd(stream_t *stream) 154 | { 155 | return stream->fd; 156 | } 157 | 158 | bool stream_is_open(stream_t *stream) 159 | { 160 | return stream->fd >= 0; 161 | } 162 | 163 | void stream_close(stream_t *stream) 164 | { 165 | if (stream->read) 166 | { 167 | reactor_cancel(stream->read, stream_release_buffer, buffer_deconstruct(&stream->input)); 168 | stream->read = 0; 169 | } 170 | if (stream->write) 171 | { 172 | reactor_cancel(stream->write, stream_release_buffer, buffer_deconstruct(&stream->output_writing)); 173 | stream->write = 0; 174 | } 175 | if (stream->fd >= 0) 176 | { 177 | reactor_close(NULL, NULL, stream->fd); 178 | stream->fd = -1; 179 | } 180 | } 181 | 182 | inline __attribute__((always_inline, flatten)) 183 | data_t stream_read(stream_t *stream) 184 | { 185 | return data_offset(buffer_data(&stream->input), stream->input_consumed); 186 | } 187 | 188 | void stream_consume(stream_t *stream, size_t size) 189 | { 190 | stream->input_consumed += size; 191 | } 192 | 193 | void *stream_allocate(stream_t *stream, size_t size) 194 | { 195 | return data_base(buffer_allocate(&stream->output_waiting, size)); 196 | } 197 | 198 | void stream_write(stream_t *stream, data_t data) 199 | { 200 | memcpy(stream_allocate(stream, data_size(data)), data_base(data), data_size(data)); 201 | } 202 | 203 | void stream_flush(stream_t *stream) 204 | { 205 | stream->output_flushed = buffer_size(&stream->output_waiting); 206 | stream_update(stream); 207 | } 208 | -------------------------------------------------------------------------------- /test/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "reactor.h" 15 | 16 | static int server_socket(int *port) 17 | { 18 | struct sockaddr_in sin = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(0x7f000001)}; 19 | socklen_t len = sizeof sin; 20 | int s; 21 | 22 | s = socket(AF_INET, SOCK_STREAM, 0); 23 | assert(s >= 0); 24 | assert(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (int[]) {1}, sizeof(int)) == 0); 25 | assert(bind(s, (struct sockaddr *) &sin, sizeof sin) == 0); 26 | assert(listen(s, INT_MAX) == 0); 27 | assert(getsockname(s, (struct sockaddr *) &sin, &len) == 0); 28 | *port = ntohs(sin.sin_port); 29 | return s; 30 | } 31 | 32 | static int client_socket(int port) 33 | { 34 | struct sockaddr_in sin = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(0x7f000001), .sin_port = htons(port)}; 35 | socklen_t len = sizeof sin; 36 | int c; 37 | 38 | c = socket(AF_INET, SOCK_STREAM, 0); 39 | assert(c >= 0); 40 | assert(connect(c, (struct sockaddr *) &sin, len) == 0); 41 | return c; 42 | } 43 | 44 | struct state 45 | { 46 | server_t server; 47 | stream_t stream; 48 | int result; 49 | uint64_t value; 50 | size_t calls; 51 | }; 52 | 53 | static void server_next1(reactor_event_t *event) 54 | { 55 | server_session_t *session = (server_session_t *) event->state; 56 | 57 | server_respond(session, string("200 OK"), string("text/plain"), string("ok"), NULL, 0); 58 | } 59 | 60 | static void server_next2(reactor_event_t *event) 61 | { 62 | server_session_t *session = (server_session_t *) event->state; 63 | 64 | server_ok(session, string("text/plain"), string("ok"), NULL, 0); 65 | server_disconnect(session); 66 | } 67 | 68 | static void server_callback(reactor_event_t *event) 69 | { 70 | struct state *state = event->state; 71 | server_session_t *session = (server_session_t *) event->data; 72 | 73 | state->calls++; 74 | assert(event->type == SERVER_REQUEST); 75 | if (string_equal(session->request.target, string("/abort"))) 76 | { 77 | server_plain(session, string("no"), NULL, 0); 78 | server_disconnect(session); 79 | } 80 | else if (string_equal(session->request.target, string("/next1"))) 81 | reactor_next(server_next1, session); 82 | else if (string_equal(session->request.target, string("/next2"))) 83 | reactor_next(server_next2, session); 84 | else 85 | server_plain(session, string("ok"), NULL, 0); 86 | } 87 | 88 | static void stream_callback(reactor_event_t *event) 89 | { 90 | struct state *state = event->state; 91 | data_t data = stream_read(&state->stream); 92 | int code; 93 | 94 | state->calls++; 95 | switch (event->type) 96 | { 97 | case STREAM_READ: 98 | assert_true(memchr(data_base(data), '\n', data_size(data)) != NULL); 99 | assert_true(strncmp(data_base(data), "HTTP/1.1 ", 9) == 0); 100 | data = data_offset(data, 9); 101 | code = strtoul(data_base(data), NULL, 0); 102 | assert_int_equal(code, state->result); 103 | server_destruct(&state->server); 104 | stream_close(&state->stream); 105 | break; 106 | case STREAM_CLOSE: 107 | assert_int_equal(state->result, 0); 108 | server_close(&state->server); 109 | break; 110 | } 111 | } 112 | 113 | static void run(char *request, int result, int calls) 114 | { 115 | struct state state = {.result = result}; 116 | int c, s, port; 117 | 118 | reactor_construct(); 119 | server_construct(&state.server, server_callback, &state); 120 | 121 | s = server_socket(&port); 122 | server_open_socket(&state.server, s); 123 | 124 | stream_construct(&state.stream, stream_callback, &state); 125 | c = client_socket(port); 126 | stream_open(&state.stream, c, 0); 127 | 128 | if (request) 129 | { 130 | stream_write(&state.stream, data_string(request)); 131 | stream_flush(&state.stream); 132 | } 133 | else 134 | { 135 | reactor_loop_once(); 136 | shutdown(stream_fd(&state.stream), SHUT_RDWR); 137 | stream_close(&state.stream); 138 | reactor_loop_once(); 139 | server_close(&state.server); 140 | } 141 | reactor_loop(); 142 | stream_destruct(&state.stream); 143 | server_destruct(&state.server); 144 | reactor_destruct(); 145 | close(c); 146 | close(s); 147 | assert_int_equal(state.calls, calls); 148 | } 149 | 150 | static void test_server(__attribute__((unused)) void **arg) 151 | { 152 | run("GET / HTTP/1.0\r\n\r\n", 200, 2); 153 | run("GET / HTTP/1.0\r\n\r\nGET", 200, 2); 154 | run("GET /abort HTTP/1.0\r\n\r\n", 0, 2); 155 | run("GET /next1 HTTP/1.0\r\n\r\n", 200, 2); 156 | run("GET /next2 HTTP/1.0\r\n\r\n", 200, 2); 157 | run("GET /next1 HTTP/1.0\r\n\r\nGET /next1 HTTP/1.0\r\n\r\n", 200, 3); 158 | run("i n v a l i d", 0, 1); 159 | run(NULL, 0, 0); 160 | } 161 | 162 | static void test_error(__attribute__((unused)) void **arg) 163 | { 164 | server_t s1, s2; 165 | 166 | reactor_construct(); 167 | server_construct(&s1, NULL, NULL); 168 | server_open_socket(&s1, -1); 169 | server_destruct(&s1); 170 | reactor_destruct(); 171 | 172 | reactor_construct(); 173 | server_construct(&s1, NULL, NULL); 174 | server_construct(&s2, NULL, NULL); 175 | server_open(&s1, "127.0.0.1", 12345); 176 | server_open(&s2, "127.0.0.1", 12345); 177 | reactor_loop_once(); 178 | server_destruct(&s1); 179 | server_destruct(&s2); 180 | reactor_destruct(); 181 | } 182 | 183 | static void test_basic(__attribute__((unused)) void **arg) 184 | { 185 | server_t server; 186 | 187 | reactor_construct(); 188 | server_construct(&server, NULL, NULL); 189 | server_open(&server, "127.0.0.1", 0); 190 | reactor_loop_once(); 191 | server_destruct(&server); 192 | reactor_destruct(); 193 | } 194 | 195 | int main() 196 | { 197 | const struct CMUnitTest tests[] = 198 | { 199 | cmocka_unit_test(test_server), 200 | cmocka_unit_test(test_error), 201 | cmocka_unit_test(test_basic) 202 | }; 203 | 204 | return cmocka_run_group_tests(tests, NULL, NULL); 205 | } 206 | -------------------------------------------------------------------------------- /src/reactor/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static size_t buffer_roundup(size_t size) 7 | { 8 | size--; 9 | size |= size >> 1; 10 | size |= size >> 2; 11 | size |= size >> 4; 12 | size |= size >> 8; 13 | size |= size >> 16; 14 | size |= size >> 32; 15 | size++; 16 | return size; 17 | } 18 | 19 | /* constructor/destructor */ 20 | 21 | void buffer_construct(buffer_t *buffer) 22 | { 23 | *buffer = (buffer_t) {0}; 24 | } 25 | 26 | void buffer_destruct(buffer_t *buffer) 27 | { 28 | data_release(buffer->data); 29 | } 30 | 31 | void *buffer_deconstruct(buffer_t *buffer) 32 | { 33 | void *base = buffer_base(buffer); 34 | 35 | buffer_construct(buffer); 36 | return base; 37 | } 38 | 39 | /* capacity */ 40 | 41 | size_t buffer_size(const buffer_t *buffer) 42 | { 43 | return data_size(buffer->data); 44 | } 45 | 46 | size_t buffer_capacity(const buffer_t *buffer) 47 | { 48 | return buffer->capacity; 49 | } 50 | 51 | bool buffer_empty(const buffer_t *buffer) 52 | { 53 | return buffer_size(buffer) == 0; 54 | } 55 | 56 | void buffer_reserve(buffer_t *buffer, size_t capacity) 57 | { 58 | if (capacity > buffer->capacity) 59 | { 60 | capacity = buffer_roundup(capacity); 61 | buffer->data = data(realloc(buffer_base(buffer), capacity), buffer_size(buffer)); 62 | buffer->capacity = capacity; 63 | } 64 | } 65 | 66 | void buffer_resize(buffer_t *buffer, size_t size) 67 | { 68 | if (size > buffer->capacity) 69 | buffer_reserve(buffer, size); 70 | buffer->data = data_select(buffer->data, size); 71 | } 72 | 73 | void buffer_compact(buffer_t *buffer) 74 | { 75 | size_t size; 76 | 77 | size = buffer_size(buffer); 78 | if (buffer->capacity > size) 79 | { 80 | if (size) 81 | { 82 | buffer->data = data(realloc(buffer_base(buffer), size), size); 83 | } 84 | else 85 | { 86 | free(buffer_base(buffer)); 87 | buffer->data = data_null(); 88 | } 89 | buffer->capacity = size; 90 | } 91 | } 92 | 93 | data_t buffer_allocate(buffer_t *buffer, size_t data_size) 94 | { 95 | size_t size; 96 | 97 | size = buffer_size(buffer); 98 | buffer_resize(buffer, size + data_size); 99 | return data_offset(buffer->data, size); 100 | } 101 | 102 | /* modifiers */ 103 | 104 | void buffer_insert(buffer_t *restrict buffer, size_t pos, const data_t data) 105 | { 106 | buffer_reserve(buffer, buffer_size(buffer) + data_size(data)); 107 | if (pos < buffer_size(buffer)) 108 | memmove(buffer_at(buffer, pos + data_size(data)), buffer_at(buffer, pos), buffer_size(buffer) - pos); 109 | memcpy(buffer_at(buffer, pos), data_base(data), data_size(data)); 110 | buffer_resize(buffer, buffer_size(buffer) + data_size(data)); 111 | } 112 | 113 | void buffer_insert_fill(buffer_t *restrict buffer, size_t pos, size_t count, const data_t data) 114 | { 115 | size_t i; 116 | 117 | buffer_reserve(buffer, buffer_size(buffer) + (count * data_size(data))); 118 | if (pos < buffer_size(buffer)) 119 | memmove(buffer_at(buffer, pos + (count * data_size(data))), buffer_at(buffer, pos), buffer_size(buffer) - pos); 120 | for (i = 0; i < count; i++) 121 | memcpy(buffer_at(buffer, pos + (i * data_size(data))), data_base(data), data_size(data)); 122 | buffer_resize(buffer, buffer_size(buffer) + (count * data_size(data))); 123 | } 124 | 125 | void buffer_prepend(buffer_t *restrict buffer, const data_t data) 126 | { 127 | buffer_insert(buffer, 0, data); 128 | } 129 | 130 | void buffer_append(buffer_t *restrict buffer, const data_t data) 131 | { 132 | buffer_insert(buffer, buffer_size(buffer), data); 133 | } 134 | 135 | void buffer_erase(buffer_t *restrict buffer, size_t position, size_t size) 136 | { 137 | memmove(buffer_at(buffer, position), buffer_at(buffer, position + size), buffer_size(buffer) - position - size); 138 | buffer_resize(buffer, buffer_size(buffer) - size); 139 | } 140 | 141 | void buffer_clear(buffer_t *restrict buffer) 142 | { 143 | buffer_resize(buffer, 0); 144 | } 145 | 146 | void buffer_read(buffer_t *restrict buffer, FILE *restrict file) 147 | { 148 | size_t n; 149 | 150 | do 151 | { 152 | n = fread(data_base(buffer_allocate(buffer, 4096)), 1, 4096, file); 153 | buffer_resize(buffer, buffer_size(buffer) - 4096 + n); 154 | } while (n == 4096); 155 | } 156 | 157 | bool buffer_load(buffer_t *restrict buffer, const char *path) 158 | { 159 | FILE *file; 160 | 161 | file = fopen(path, "r"); 162 | if (!file) 163 | return false; 164 | buffer_clear(buffer); 165 | buffer_read(buffer, file); 166 | buffer_compact(buffer); 167 | fclose(file); 168 | return true; 169 | } 170 | 171 | bool buffer_loadz(buffer_t *restrict buffer, const char *path) 172 | { 173 | FILE *file; 174 | 175 | file = fopen(path, "r"); 176 | if (!file) 177 | return false; 178 | buffer_clear(buffer); 179 | buffer_read(buffer, file); 180 | buffer_append(buffer, data("", 1)); 181 | buffer_compact(buffer); 182 | buffer_resize(buffer, buffer_size(buffer) - 1); 183 | fclose(file); 184 | return true; 185 | } 186 | 187 | void buffer_switch(buffer_t *restrict b1, buffer_t *restrict b2) 188 | { 189 | buffer_t tmp; 190 | 191 | tmp = *b1; 192 | *b1 = *b2; 193 | *b2 = tmp; 194 | } 195 | 196 | /* element access */ 197 | 198 | data_t buffer_data(const buffer_t *buffer) 199 | { 200 | return buffer->data; 201 | } 202 | 203 | void *buffer_base(const buffer_t *buffer) 204 | { 205 | return data_base(buffer->data); 206 | } 207 | 208 | void *buffer_at(const buffer_t *buffer, size_t pos) 209 | { 210 | return (char *) buffer_base(buffer) + pos; 211 | } 212 | 213 | void *buffer_end(const buffer_t *buffer) 214 | { 215 | return buffer_at(buffer, buffer_size(buffer)); 216 | } 217 | 218 | /* operations */ 219 | 220 | void buffer_write(const buffer_t *restrict buffer, FILE *restrict file) 221 | { 222 | (void) fwrite(buffer_base(buffer), buffer_size(buffer), 1, file); 223 | } 224 | 225 | bool buffer_save(const buffer_t *restrict buffer, const char *path) 226 | { 227 | FILE *file; 228 | 229 | file = fopen(path, "w"); 230 | if (!file) 231 | return false; 232 | buffer_write(buffer, file); 233 | fclose(file); 234 | return true; 235 | } 236 | -------------------------------------------------------------------------------- /src/reactor/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../reactor.h" 9 | 10 | /* queue */ 11 | 12 | static void queue_cancel(reactor_event_t *event) 13 | { 14 | free(event->state); 15 | } 16 | 17 | void queue_construct(queue_t *queue, size_t element_size) 18 | { 19 | *queue = (struct queue) {.element_size = element_size}; 20 | assert(pipe(queue->fd) == 0); 21 | } 22 | 23 | void queue_destruct(queue_t *queue) 24 | { 25 | (void) close(queue->fd[0]); 26 | (void) close(queue->fd[1]); 27 | } 28 | 29 | /* queue producer */ 30 | 31 | static void queue_producer_update(queue_producer_t *); 32 | 33 | #ifndef UNIT_TESTING 34 | static 35 | #endif 36 | void queue_producer_write(reactor_event_t *event) 37 | { 38 | queue_producer_t *producer = event->state; 39 | int result = event->data; 40 | 41 | producer->write = 0; 42 | if (result == -EINTR) 43 | { 44 | queue_producer_update(producer); 45 | return; 46 | } 47 | assert(result > 0 && result % producer->queue->element_size == 0); 48 | producer->output_offset += result; 49 | queue_producer_update(producer); 50 | } 51 | 52 | static void queue_producer_update(queue_producer_t *producer) 53 | { 54 | buffer_t buffer; 55 | size_t size, size_max; 56 | 57 | if (producer->write) 58 | return; 59 | 60 | if (producer->output_offset == buffer_size(&producer->output)) 61 | { 62 | buffer_clear(&producer->output); 63 | producer->output_offset = 0; 64 | buffer = producer->output; 65 | producer->output = producer->output_queued; 66 | producer->output_queued = buffer; 67 | } 68 | 69 | if (!buffer_size(&producer->output)) 70 | return; 71 | 72 | assert(producer->queue->fd[1] >= 0); 73 | size_max = producer->queue->element_size * 128; 74 | size = buffer_size(&producer->output) - producer->output_offset; 75 | if (size > size_max) 76 | size = size_max; 77 | producer->write = reactor_write(queue_producer_write, producer, producer->queue->fd[1], 78 | (char *) buffer_base(&producer->output) + producer->output_offset, size, 0); 79 | } 80 | 81 | void queue_producer_construct(queue_producer_t *producer) 82 | { 83 | *producer = (queue_producer_t) {0}; 84 | 85 | buffer_construct(&producer->output); 86 | buffer_construct(&producer->output_queued); 87 | } 88 | 89 | void queue_producer_destruct(queue_producer_t *producer) 90 | { 91 | queue_producer_close(producer); 92 | } 93 | 94 | void queue_producer_open(queue_producer_t *producer, queue_t *queue) 95 | { 96 | producer->queue = queue; 97 | } 98 | 99 | void queue_producer_push(queue_producer_t *producer, void *element) 100 | { 101 | buffer_insert(&producer->output_queued, buffer_size(&producer->output_queued), data(element, producer->queue->element_size)); 102 | queue_producer_update(producer); 103 | } 104 | 105 | void queue_producer_push_sync(queue_producer_t *producer, void *element) 106 | { 107 | assert(write(producer->queue->fd[1], element, producer->queue->element_size) == (ssize_t) producer->queue->element_size); 108 | } 109 | 110 | void queue_producer_close(queue_producer_t *producer) 111 | { 112 | if (!producer->queue) 113 | return; 114 | producer->queue = NULL; 115 | if (producer->write) 116 | { 117 | reactor_cancel(producer->write, queue_cancel, buffer_deconstruct(&producer->output)); 118 | producer->write = 0; 119 | } 120 | buffer_destruct(&producer->output); 121 | buffer_destruct(&producer->output_queued); 122 | } 123 | 124 | /* queue consumer */ 125 | 126 | static void queue_consumer_update(queue_consumer_t *); 127 | 128 | #ifndef UNIT_TESTING 129 | static 130 | #endif 131 | void queue_consumer_read(reactor_event_t *event) 132 | { 133 | queue_consumer_t *consumer = event->state; 134 | int offset, result = event->data; 135 | bool abort = false; 136 | 137 | consumer->read = 0; 138 | if (result == -EINTR) 139 | { 140 | queue_consumer_update(consumer); 141 | return; 142 | } 143 | assert(result > 0 && result % consumer->queue->element_size == 0); 144 | for (offset = 0; offset < result; offset += consumer->queue->element_size) 145 | { 146 | consumer->abort = &abort; 147 | reactor_call(&consumer->user, QUEUE_CONSUMER_POP, (uint64_t) buffer_base(&consumer->input) + offset); 148 | if (abort) 149 | return; 150 | consumer->abort = NULL; 151 | } 152 | queue_consumer_update(consumer); 153 | } 154 | 155 | static void queue_consumer_update(queue_consumer_t *consumer) 156 | { 157 | if (consumer->read) 158 | return; 159 | consumer->read = reactor_read(queue_consumer_read, consumer, consumer->queue->fd[0], 160 | buffer_base(&consumer->input), buffer_size(&consumer->input), 0); 161 | } 162 | 163 | void queue_consumer_construct(queue_consumer_t *consumer, reactor_callback_t *callback, void *state) 164 | { 165 | *consumer = (queue_consumer_t) {.user = reactor_user_define(callback, state)}; 166 | buffer_construct(&consumer->input); 167 | } 168 | 169 | void queue_consumer_destruct(queue_consumer_t *consumer) 170 | { 171 | queue_consumer_close(consumer); 172 | buffer_destruct(&consumer->input); 173 | } 174 | 175 | void queue_consumer_open(queue_consumer_t *consumer, queue_t *queue, size_t batch_size) 176 | { 177 | consumer->queue = queue; 178 | buffer_resize(&consumer->input, queue->element_size * batch_size); 179 | } 180 | 181 | void queue_consumer_pop(queue_consumer_t *consumer) 182 | { 183 | queue_consumer_update(consumer); 184 | } 185 | 186 | void *queue_consumer_pop_sync(queue_consumer_t *consumer) 187 | { 188 | void *element = buffer_base(&consumer->input); 189 | 190 | assert(read(consumer->queue->fd[0], element, consumer->queue->element_size) == 191 | (ssize_t) consumer->queue->element_size); 192 | return element; 193 | } 194 | 195 | void queue_consumer_close(queue_consumer_t *consumer) 196 | { 197 | if (!consumer->queue) 198 | return; 199 | if (consumer->abort) 200 | *consumer->abort = true; 201 | consumer->queue = NULL; 202 | if (consumer->read) 203 | { 204 | reactor_cancel(consumer->read, queue_cancel, buffer_deconstruct(&consumer->input)); 205 | consumer->read = 0; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/reactor/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static __thread char server_date[30] = {0}; 5 | 6 | static void server_date_update(void) 7 | { 8 | time_t t; 9 | struct tm tm; 10 | static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 11 | static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; 12 | 13 | t = (reactor_now() + 10000000) / 1000000000; 14 | (void) gmtime_r(&t, &tm); 15 | (void) strftime(server_date, sizeof server_date, "---, %d --- %Y %H:%M:%S GMT", &tm); 16 | memcpy(server_date, days[tm.tm_wday], 3); 17 | memcpy(server_date + 8, months[tm.tm_mon], 3); 18 | } 19 | 20 | static void server_session_close(server_session_t *session) 21 | { 22 | stream_close(&session->stream); 23 | if (session->abort) 24 | *session->abort = true; 25 | if (session->next) 26 | { 27 | reactor_cancel(session->next, NULL, NULL); 28 | session->next = 0; 29 | } 30 | if (session->flags & SERVER_SESSION_READY) 31 | { 32 | stream_destruct(&session->stream); 33 | list_erase(session, NULL); 34 | } 35 | } 36 | 37 | static void server_session_read(server_session_t *session) 38 | { 39 | int n; 40 | bool abort = false; 41 | 42 | while (session->flags & SERVER_SESSION_READY) 43 | { 44 | session->request.fields_count = 16; 45 | n = http_read_request(&session->stream, &session->request.method, &session->request.target, &session->request.body, 46 | session->request.fields, &session->request.fields_count); 47 | if (reactor_unlikely(n == -1)) 48 | { 49 | server_session_close(session); 50 | return; 51 | } 52 | if (reactor_unlikely(n == 0)) 53 | break; 54 | 55 | session->flags &= ~SERVER_SESSION_READY; 56 | session->flags |= SERVER_SESSION_PROCESSING; 57 | session->abort = &abort; 58 | reactor_call(&session->user, SERVER_REQUEST, (uintptr_t) session); 59 | if (reactor_unlikely(abort)) 60 | return; 61 | session->abort = NULL; 62 | session->flags &= ~SERVER_SESSION_PROCESSING; 63 | } 64 | stream_flush(&session->stream); 65 | } 66 | 67 | static void server_session_next(reactor_event_t *event) 68 | { 69 | server_session_t *session = event->state; 70 | 71 | session->next = 0; 72 | server_session_read(session); 73 | } 74 | 75 | inline __attribute__((always_inline, flatten)) 76 | static void server_session_stream(reactor_event_t *event) 77 | { 78 | server_session_t *session = event->state; 79 | 80 | if (reactor_likely(event->type == STREAM_READ)) 81 | server_session_read(session); 82 | else 83 | server_session_close(session); 84 | } 85 | 86 | static void server_create_session(server_t *server, int fd) 87 | { 88 | server_session_t *session; 89 | 90 | session = list_push_back(&server->sessions, NULL, sizeof *session); 91 | session->user = server->user; 92 | session->flags = SERVER_SESSION_READY; 93 | stream_construct(&session->stream, server_session_stream, session); 94 | stream_open(&session->stream, fd, 0); 95 | } 96 | 97 | static void server_accept(reactor_event_t *event) 98 | { 99 | server_t *server = event->state; 100 | 101 | switch (event->type) 102 | { 103 | case NETWORK_ACCEPT: 104 | server_create_session(server, event->data); 105 | break; 106 | case NETWORK_ACCEPT_BIND: 107 | break; 108 | default: 109 | server->accept = 0; 110 | reactor_call(&server->user, SERVER_ERROR, 0); 111 | break; 112 | } 113 | } 114 | 115 | static void server_timeout(reactor_event_t *event) 116 | { 117 | (void) event; 118 | server_date_update(); 119 | } 120 | 121 | void server_construct(server_t *server, reactor_callback_t *callback, void *state) 122 | { 123 | *server = (server_t) {.user = reactor_user_define(callback, state)}; 124 | list_construct(&server->sessions); 125 | timeout_construct(&server->timeout, server_timeout, NULL); 126 | } 127 | 128 | void server_destruct(server_t *server) 129 | { 130 | server_close(server); 131 | timeout_destruct(&server->timeout); 132 | 133 | while (!list_empty(&server->sessions)) 134 | list_detach(list_front(&server->sessions)); 135 | list_destruct(&server->sessions, NULL); 136 | } 137 | 138 | void server_open(server_t *server, const char *host, int port) 139 | { 140 | server->accept = network_accept(server_accept, server, host, port, NETWORK_REUSEADDR); 141 | timeout_set(&server->timeout, ((reactor_now() / 1000000000) * 1000000000), 1000000000); 142 | server_date_update(); 143 | } 144 | 145 | void server_open_socket(server_t *server, int socket) 146 | { 147 | server->accept = network_accept_socket(server_accept, server, socket); 148 | timeout_set(&server->timeout, ((reactor_now() / 1000000000) * 1000000000), 1000000000); 149 | server_date_update(); 150 | } 151 | 152 | void server_close(server_t *server) 153 | { 154 | if (server->accept) 155 | { 156 | network_cancel(server->accept); 157 | server->accept = 0; 158 | } 159 | timeout_clear(&server->timeout); 160 | } 161 | 162 | void server_disconnect(server_session_t *session) 163 | { 164 | session->flags |= SERVER_SESSION_READY; 165 | server_session_close(session); 166 | } 167 | 168 | void server_respond(server_session_t *session, string_t status, string_t type, data_t body, 169 | http_field_t *fields, size_t fields_count) 170 | { 171 | session->flags |= SERVER_SESSION_READY; 172 | if (reactor_likely(stream_is_open(&session->stream))) 173 | { 174 | http_write_response(&session->stream, status, data(server_date, 29), type, body, fields, fields_count); 175 | if (!(session->flags & SERVER_SESSION_PROCESSING)) 176 | { 177 | stream_flush(&session->stream); 178 | session->next = reactor_next(server_session_next, session); 179 | } 180 | } 181 | else 182 | { 183 | server_session_close(session); 184 | } 185 | } 186 | 187 | void server_ok(server_session_t *session, string_t type, string_t body, http_field_t *fields, size_t fields_count) 188 | { 189 | server_respond(session, string("200 OK"), type, body, fields, fields_count); 190 | } 191 | 192 | void server_plain(server_session_t *session, data_t body, http_field_t *fields, size_t fields_count) 193 | { 194 | server_respond(session, string("200 OK"), string("text/plain"), body, fields, fields_count); 195 | } 196 | -------------------------------------------------------------------------------- /test/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void test_string(__attribute__((unused)) void **arg) 9 | { 10 | const char *end, *s; 11 | string_t s1, s2; 12 | 13 | /* basic */ 14 | s = "test"; 15 | s1 = string_data(data(s, strlen(s))); 16 | assert_true(string_equal(s1, string("test"))); 17 | 18 | s1 = string_null(); 19 | assert_int_equal(string_utf8_length(s1), 0); 20 | 21 | /* basic utf8 */ 22 | s1 = string_utf8_decode("\xE2\x98\x83", &end); 23 | assert_string_equal(end, ""); 24 | assert_int_equal(string_size(s1), 3); 25 | assert_int_equal(string_utf8_length(s1), 1); 26 | string_release(s1); 27 | 28 | /* encoding and decoding */ 29 | s = ""; 30 | assert_int_equal(string_utf8_get(s, s + strlen(s), NULL), 0); 31 | 32 | s = "\xc2"; 33 | assert_int_equal(string_utf8_get(s, s + strlen(s), NULL), 0xFFFD); 34 | 35 | s = "\xc2\xa3"; 36 | assert_int_equal(string_utf8_get(s, s + strlen(s), NULL), 0x00a3); 37 | s = "\xE0\xA0\xB3"; 38 | assert_int_equal(string_utf8_get(s, s + strlen(s), NULL), 0x0833); 39 | assert_int_equal(string_utf8_get_encoded("\\u1234", NULL), 0x1234); 40 | assert_int_equal(string_utf8_get_encoded("\\uabcd", NULL), 0xABCD); 41 | assert_int_equal(string_utf8_get_encoded("\\uABCD", NULL), 0xABCD); 42 | assert_int_equal(string_utf8_get_encoded("\\ud800\\udc00", NULL), 0x10000); 43 | assert_int_equal(string_utf8_get_encoded("\\ue000", NULL), 0xe000); 44 | 45 | /* invalid encodings */ 46 | assert_int_equal(string_utf8_get_encoded("xu1234", NULL), 0); 47 | assert_int_equal(string_utf8_get_encoded("\\x1234", NULL), 0); 48 | assert_int_equal(string_utf8_get_encoded("\\ux234", NULL), 0); 49 | assert_int_equal(string_utf8_get_encoded("\\u1x34", NULL), 0); 50 | assert_int_equal(string_utf8_get_encoded("\\u12x4", NULL), 0); 51 | assert_int_equal(string_utf8_get_encoded("\\u123x", NULL), 0); 52 | assert_int_equal(string_utf8_get_encoded("x", NULL), 0); 53 | assert_int_equal(string_utf8_get_encoded("\\udc00", NULL), 0); 54 | assert_int_equal(string_utf8_get_encoded("\\ud800\\u0000", NULL), 0); 55 | assert_int_equal(string_utf8_get_encoded("\\ud800\\ue000", NULL), 0); 56 | 57 | /* valid */ 58 | s1 = string_utf8_decode("\\u0061\\u00a3\\u0833", NULL); 59 | assert_string_equal(string_base(s1), "a\xc2\xa3\xE0\xA0\xB3"); 60 | string_release(s1); 61 | 62 | /* ascii with utf8 */ 63 | s1 = string_utf8_decode("snowman \xE2\x98\x83", &end); 64 | assert_string_equal(end, ""); 65 | assert_int_equal(string_size(s1), 11); 66 | assert_int_equal(string_utf8_length(s1), 9); 67 | string_release(s1); 68 | 69 | /* ascii with ending with " */ 70 | s1 = string_utf8_decode("snowman \xE2\x98\x83\"", &end); 71 | assert_string_equal(end, "\""); 72 | assert_int_equal(string_size(s1), 11); 73 | assert_int_equal(string_utf8_length(s1), 9); 74 | string_release(s1); 75 | 76 | /* ascii with invalid escape */ 77 | s1 = string_utf8_decode("test\\x", &end); 78 | assert_string_equal(end, "\\x"); 79 | assert_int_equal(string_size(s1), 4); 80 | string_release(s1); 81 | 82 | /* whitespace ending with " */ 83 | s1 = string_utf8_decode(" \"", &end); 84 | assert_string_equal(end, "\""); 85 | assert_int_equal(string_size(s1), 8); 86 | string_release(s1); 87 | 88 | /* ascii with utf8 */ 89 | s1 = string_utf8_decode("control \\\" \\\\ \\/ \\b \\f \\n \\r \\t", &end); 90 | assert_string_equal(end, ""); 91 | assert_string_equal(string_base(s1), "control \" \\ / \b \f \n \r \t"); 92 | string_release(s1); 93 | 94 | /* invalid utf8 */ 95 | s1 = string_utf8_decode("\xFF\xFF\xFF", NULL); 96 | assert_false(string_empty(s1)); 97 | assert_int_equal(string_utf8_length(s1), 3); 98 | string_release(s1); 99 | 100 | /* supplementary plane */ 101 | s1 = string_utf8_decode("aaa\U0001F600bbb", &end); 102 | assert_string_equal(end, ""); 103 | assert_int_equal(string_size(s1), 10); 104 | string_release(s1); 105 | 106 | /* escaped supplementary plane */ 107 | s1 = string_utf8_decode("aaa\\ud83d\\ude03bbb", &end); 108 | assert_string_equal(end, ""); 109 | assert_int_equal(string_size(s1), 10); 110 | s2 = string_utf8_decode("aaa\U0001f603bbb", &end); 111 | assert_string_equal(end, ""); 112 | assert_true(string_equal(s1, s2)); 113 | string_release(s1); 114 | string_release(s2); 115 | 116 | /* equality */ 117 | s1 = string_utf8_decode("testa", NULL); 118 | s2 = string_utf8_decode("test", NULL); 119 | assert_false(string_equal(s1, s2)); 120 | string_release(s1); 121 | string_release(s2); 122 | 123 | s1 = string_utf8_decode("test1", NULL); 124 | s2 = string_utf8_decode("test2", NULL); 125 | assert_false(string_equal(s1, s2)); 126 | string_release(s1); 127 | string_release(s2); 128 | 129 | s1 = string_utf8_decode("tesT1", NULL); 130 | s2 = string_utf8_decode("tEst1", NULL); 131 | assert_true(string_equal_case(s1, s2)); 132 | string_release(s1); 133 | string_release(s2); 134 | 135 | s1 = string_utf8_decode("", NULL); 136 | assert_true(string_empty(s1)); 137 | string_release(s1); 138 | 139 | /* string_utf8_encode */ 140 | buffer_t b; 141 | buffer_construct(&b); 142 | assert_true(string_utf8_encode(&b, string("abc\xE2\x98\x83\"\\\f\n\r\t\x01"), true)); 143 | buffer_append(&b, data((char[]) {0}, 1)); 144 | assert_string_equal("abc\\u2603\\\"\\\\\\f\\n\\r\\t\\u0001", (char *) buffer_base(&b)); 145 | 146 | buffer_clear(&b); 147 | 148 | assert_true(string_utf8_encode(&b, string("abc\xE2\x98\x83\"\\\f\n\r\t\x01"), false)); 149 | buffer_append(&b, data((char[]) {0}, 1)); 150 | assert_string_equal("abc\xE2\x98\x83\\\"\\\\\\f\\n\\r\\t\\u0001", (char *) buffer_base(&b)); 151 | 152 | buffer_clear(&b); 153 | assert_true(string_utf8_encode(&b, string("\U0001F600"), true)); 154 | 155 | buffer_clear(&b); 156 | assert_true(string_utf8_encode(&b, string("test"), true)); 157 | 158 | buffer_destruct(&b); 159 | 160 | /* clear null */ 161 | s1 = string_null(); 162 | assert_int_equal(string_utf8_length(s1), 0); 163 | 164 | /* copy */ 165 | s1 = string_null(); 166 | s2 = string_copy(s1); 167 | assert_true(string_empty(s2)); 168 | string_release(s2); 169 | 170 | s1 = string("test"); 171 | s2 = string_copy(s1); 172 | assert_string_equal(string_base(s2), "test"); 173 | string_release(s2); 174 | } 175 | 176 | int main() 177 | { 178 | const struct CMUnitTest tests[] = {cmocka_unit_test(test_string)}; 179 | 180 | return cmocka_run_group_tests(tests, NULL, NULL); 181 | } 182 | -------------------------------------------------------------------------------- /src/reactor/encode.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct context context_t; 4 | 5 | struct context 6 | { 7 | const value_t *value; 8 | value_t *keys; 9 | size_t index; 10 | }; 11 | 12 | enum state 13 | { 14 | STATE_VALUE_START, 15 | STATE_VALUE_END, 16 | STATE_NULL, 17 | STATE_BOOL, 18 | STATE_NUMBER, 19 | STATE_STRING, 20 | STATE_ARRAY_FIRST, 21 | STATE_ARRAY_REST, 22 | STATE_OBJECT_FIRST, 23 | STATE_OBJECT_REST, 24 | STATE_ACCEPT, 25 | STATE_REJECT 26 | }; 27 | 28 | static void value_encode_release_context(void *arg) 29 | { 30 | context_t *context = arg; 31 | 32 | value_release(context->keys); 33 | } 34 | 35 | static void value_buffer_encode_number(buffer_t *buffer, long double number) 36 | { 37 | char str[64]; 38 | 39 | snprintf(str, sizeof str, "%.32Lg", number); 40 | buffer_append(buffer, data_string(str)); 41 | } 42 | 43 | static void value_buffer_encode_string(buffer_t *buffer, const string_t s, bool ascii) 44 | { 45 | buffer_append(buffer, string("\"")); 46 | string_utf8_encode(buffer, s, ascii); 47 | buffer_append(buffer, string("\"")); 48 | } 49 | 50 | __attribute__((flatten)) bool value_buffer_encode(buffer_t *buffer, const value_t *value, 51 | value_encode_options_t options) 52 | { 53 | context_t *context; 54 | enum state state, state_old; 55 | vector_t stack; 56 | string_t key; 57 | bool pretty = options & VALUE_ENCODE_PRETTY; 58 | bool ascii = options & VALUE_ENCODE_ASCII; 59 | 60 | vector_construct(&stack, sizeof *context); 61 | state = STATE_VALUE_START; 62 | while (1) 63 | { 64 | state_old = state; 65 | state = STATE_REJECT; 66 | switch (state_old) 67 | { 68 | case STATE_VALUE_START: 69 | if (value_is_null(value)) 70 | state = STATE_NULL; 71 | else if (value_is_bool(value)) 72 | state = STATE_BOOL; 73 | else if (value_is_number(value)) 74 | state = STATE_NUMBER; 75 | else if (value_is_string(value)) 76 | state = STATE_STRING; 77 | else if (value_is_object(value)) 78 | state = STATE_OBJECT_FIRST; 79 | else if (value_is_array(value)) 80 | state = STATE_ARRAY_FIRST; 81 | break; 82 | case STATE_VALUE_END: 83 | if (vector_empty(&stack)) 84 | state = STATE_ACCEPT; 85 | else 86 | { 87 | context = vector_back(&stack); 88 | state = context->keys ? STATE_OBJECT_REST : STATE_ARRAY_REST; 89 | } 90 | break; 91 | case STATE_NULL: 92 | buffer_append(buffer, data_string("null")); 93 | state = STATE_VALUE_END; 94 | break; 95 | case STATE_BOOL: 96 | buffer_append(buffer, value_bool_get(value) ? data_string("true") : data_string("false")); 97 | state = STATE_VALUE_END; 98 | break; 99 | case STATE_NUMBER: 100 | value_buffer_encode_number(buffer, value_number_get(value)); 101 | state = STATE_VALUE_END; 102 | break; 103 | case STATE_STRING: 104 | value_buffer_encode_string(buffer, value_string_get(value), ascii); 105 | state = STATE_VALUE_END; 106 | break; 107 | case STATE_ARRAY_FIRST: 108 | vector_push_back(&stack, (context_t[]) {{.value = value}}); 109 | buffer_append(buffer, string("[")); 110 | state = STATE_ARRAY_REST; 111 | break; 112 | case STATE_ARRAY_REST: 113 | context = vector_back(&stack); 114 | if (context->index == value_array_length(context->value)) 115 | { 116 | vector_pop_back(&stack, value_encode_release_context); 117 | if (pretty && context->index) 118 | { 119 | buffer_append(buffer, data_string("\n")); 120 | buffer_insert_fill(buffer, buffer_size(buffer), vector_size(&stack) * 2, data_string(" ")); 121 | buffer_append(buffer, data_string("]")); 122 | } 123 | else 124 | buffer_append(buffer, string("]")); 125 | state = STATE_VALUE_END; 126 | break; 127 | } 128 | if (context->index) 129 | buffer_append(buffer, data_string(",")); 130 | if (pretty) 131 | { 132 | buffer_append(buffer, data_string("\n")); 133 | buffer_insert_fill(buffer, buffer_size(buffer), vector_size(&stack) * 2, data_string(" ")); 134 | } 135 | value = value_array_get(context->value, context->index); 136 | context->index++; 137 | state = STATE_VALUE_START; 138 | break; 139 | case STATE_OBJECT_FIRST: 140 | vector_push_back(&stack, (context_t[]) {{.value = value, .keys = value_object_keys(value)}}); 141 | buffer_append(buffer, string("{")); 142 | state = STATE_OBJECT_REST; 143 | break; 144 | case STATE_OBJECT_REST: 145 | context = vector_back(&stack); 146 | if (context->index == value_array_length(context->keys)) 147 | { 148 | vector_pop_back(&stack, value_encode_release_context); 149 | if (pretty && context->index) 150 | { 151 | buffer_append(buffer, data_string("\n")); 152 | buffer_insert_fill(buffer, buffer_size(buffer), vector_size(&stack) * 2, data_string(" ")); 153 | buffer_append(buffer, data_string("}")); 154 | } 155 | else 156 | buffer_append(buffer, string("}")); 157 | state = STATE_VALUE_END; 158 | break; 159 | } 160 | 161 | if (context->index) 162 | buffer_append(buffer, string(",")); 163 | 164 | if (pretty) 165 | { 166 | buffer_append(buffer, data_string("\n")); 167 | buffer_insert_fill(buffer, buffer_size(buffer), vector_size(&stack) * 2, data_string(" ")); 168 | key = value_string_get(value_array_get(context->keys, context->index)); 169 | value_buffer_encode_string(buffer, key, ascii); 170 | buffer_append(buffer, string(": ")); 171 | } 172 | else 173 | { 174 | key = value_string_get(value_array_get(context->keys, context->index)); 175 | value_buffer_encode_string(buffer, key, ascii); 176 | buffer_append(buffer, string(":")); 177 | } 178 | context->index++; 179 | value = value_object_get(context->value, key); 180 | state = STATE_VALUE_START; 181 | break; 182 | case STATE_ACCEPT: 183 | vector_destruct(&stack, value_encode_release_context); 184 | return true; 185 | default: 186 | case STATE_REJECT: 187 | vector_destruct(&stack, value_encode_release_context); 188 | return false; 189 | } 190 | } 191 | } 192 | 193 | string_t value_encode(const value_t *value, value_encode_options_t options) 194 | { 195 | buffer_t buffer; 196 | bool status; 197 | 198 | buffer_construct(&buffer); 199 | status = value_buffer_encode(&buffer, value, options); 200 | if (!status) 201 | { 202 | buffer_destruct(&buffer); 203 | return string_null(); 204 | } 205 | buffer_append(&buffer, data((char[]) {0}, 1)); 206 | buffer_compact(&buffer); 207 | buffer_resize(&buffer, buffer_size(&buffer) - 1); 208 | return buffer_data(&buffer); 209 | } 210 | -------------------------------------------------------------------------------- /src/reactor/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define bswap32(x) __builtin_bswap32(x) 7 | #define bswap64(x) __builtin_bswap64(x) 8 | 9 | #ifdef FARMHASH_BIG_ENDIAN 10 | #define uint32_in_expected_order(x) bswap32(x) 11 | #define uint64_in_expected_order(x) bswap64(x) 12 | #else 13 | #define uint32_in_expected_order(x) (x) 14 | #define uint64_in_expected_order(x) (x) 15 | #endif 16 | 17 | // Some primes between 2^63 and 2^64 for various uses. 18 | 19 | static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; 20 | static const uint64_t k1 = 0xb492b66fbe98f273ULL; 21 | static const uint64_t k2 = 0x9ae16a3b2f90404fULL; 22 | 23 | static inline uint32_t fetch32(const char *p) 24 | { 25 | uint32_t result; 26 | 27 | memcpy(&result, p, sizeof(result)); 28 | 29 | return uint32_in_expected_order(result); 30 | } 31 | 32 | static inline uint64_t fetch64(const char *p) 33 | { 34 | uint64_t result; 35 | 36 | memcpy(&result, p, sizeof(result)); 37 | 38 | return uint64_in_expected_order(result); 39 | } 40 | 41 | static inline uint64_t shift_mix(uint64_t v) 42 | { 43 | return v ^ (v >> 47); 44 | } 45 | 46 | static inline uint64_t rotate64(uint64_t v, int shift) 47 | { 48 | return ((v >> shift) | (v << (64 - shift))); 49 | } 50 | 51 | static inline uint64_t hash_len_16(uint64_t u, uint64_t v, uint64_t mul) 52 | { 53 | uint64_t a, b; 54 | 55 | a = (u ^ v) * mul; 56 | a ^= (a >> 47); 57 | b = (v ^ a) * mul; 58 | b ^= (b >> 47); 59 | b *= mul; 60 | 61 | return b; 62 | } 63 | 64 | static inline uint64_t hash_len_0_to_16(const char *s, size_t len) 65 | { 66 | if (len >= 8) 67 | { 68 | uint64_t mul = k2 + len * 2; 69 | uint64_t a = fetch64(s) + k2; 70 | uint64_t b = fetch64(s + len - 8); 71 | uint64_t c = rotate64(b, 37) * mul + a; 72 | uint64_t d = (rotate64(a, 25) + b) * mul; 73 | return hash_len_16(c, d, mul); 74 | } 75 | 76 | if (len >= 4) 77 | { 78 | uint64_t mul = k2 + len * 2; 79 | uint64_t a = fetch32(s); 80 | return hash_len_16(len + (a << 3), fetch32(s + len - 4), mul); 81 | } 82 | 83 | if (len > 0) 84 | { 85 | uint8_t a = s[0]; 86 | uint8_t b = s[len >> 1]; 87 | uint8_t c = s[len - 1]; 88 | uint32_t y = (uint32_t) a + ((uint32_t) b << 8); 89 | uint32_t z = len + ((uint32_t) c << 2); 90 | return shift_mix(y * k2 ^ z * k0) * k2; 91 | } 92 | 93 | return k2; 94 | } 95 | 96 | static inline uint64_t hash_len_17_to_32(const char *s, size_t len) 97 | { 98 | uint64_t mul = k2 + len * 2; 99 | uint64_t a = fetch64(s) * k1; 100 | uint64_t b = fetch64(s + 8); 101 | uint64_t c = fetch64(s + len - 8) * mul; 102 | uint64_t d = fetch64(s + len - 16) * k2; 103 | 104 | return hash_len_16(rotate64(a + b, 43) + rotate64(c, 30) + d, a + rotate64(b + k2, 18) + c, mul); 105 | } 106 | 107 | static inline uint64_t hash_len_33_to_64(const char *s, size_t len) 108 | { 109 | uint64_t mul = k2 + len * 2; 110 | uint64_t a = fetch64(s) * k2; 111 | uint64_t b = fetch64(s + 8); 112 | uint64_t c = fetch64(s + len - 8) * mul; 113 | uint64_t d = fetch64(s + len - 16) * k2; 114 | uint64_t y = rotate64(a + b, 43) + rotate64(c, 30) + d; 115 | uint64_t z = hash_len_16(y, a + rotate64(b + k2, 18) + c, mul); 116 | uint64_t e = fetch64(s + 16) * mul; 117 | uint64_t f = fetch64(s + 24); 118 | uint64_t g = (y + fetch64(s + len - 32)) * mul; 119 | uint64_t h = (z + fetch64(s + len - 24)) * mul; 120 | 121 | return hash_len_16(rotate64(e + f, 43) + rotate64(g, 30) + h, e + rotate64(f + a, 18) + g, mul); 122 | } 123 | 124 | #define swap(x, y) \ 125 | do \ 126 | { \ 127 | (x) = (x) ^ (y); \ 128 | (y) = (x) ^ (y); \ 129 | (x) = (x) ^ y; \ 130 | } while (0); 131 | 132 | typedef struct pair64 pair64; 133 | 134 | struct pair64 135 | { 136 | uint64_t first; 137 | uint64_t second; 138 | }; 139 | 140 | static inline pair64 weak_hash_len_32_with_seeds2(uint64_t w, uint64_t x, uint64_t y, uint64_t z, uint64_t a, 141 | uint64_t b) 142 | { 143 | uint64_t c; 144 | pair64 result; 145 | 146 | a += w; 147 | b = rotate64(b + a + z, 21); 148 | c = a; 149 | a += x; 150 | a += y; 151 | b += rotate64(a, 44); 152 | result.first = a + z; 153 | result.second = b + c; 154 | 155 | return result; 156 | } 157 | 158 | static inline pair64 weak_hash_len_32_with_seeds(const char *s, uint64_t a, uint64_t b) 159 | { 160 | return weak_hash_len_32_with_seeds2(fetch64(s), fetch64(s + 8), fetch64(s + 16), fetch64(s + 24), a, b); 161 | } 162 | 163 | static uint64_t cfarmhash(const char *s, size_t len) 164 | { 165 | uint64_t mul; 166 | const uint64_t seed = 81; 167 | 168 | if (len <= 16) 169 | return hash_len_0_to_16(s, len); 170 | 171 | if (len <= 32) 172 | return hash_len_17_to_32(s, len); 173 | 174 | if (len <= 64) 175 | return hash_len_33_to_64(s, len); 176 | 177 | uint64_t x = seed, y = seed * k1 + 113, z = shift_mix(y * k2 + 113) * k2; 178 | pair64 v = {0, 0}, w = {0, 0}; 179 | 180 | x = x * k2 + fetch64(s); 181 | 182 | const char *end = s + ((len - 1) / 64) * 64; 183 | const char *last64 = end + ((len - 1) & 63) - 63; 184 | 185 | do 186 | { 187 | x = rotate64(x + y + v.first + fetch64(s + 8), 37) * k1; 188 | y = rotate64(y + v.second + fetch64(s + 48), 42) * k1; 189 | x ^= w.second; 190 | y += v.first + fetch64(s + 40); 191 | z = rotate64(z + w.first, 33) * k1; 192 | v = weak_hash_len_32_with_seeds(s, v.second * k1, x + w.first); 193 | w = weak_hash_len_32_with_seeds(s + 32, z + w.second, y + fetch64(s + 16)); 194 | swap(z, x); 195 | s += 64; 196 | } while (s != end); 197 | 198 | mul = k1 + ((z & 0xff) << 1); 199 | s = last64; 200 | w.first += ((len - 1) & 63); 201 | v.first += w.first; 202 | w.first += v.first; 203 | x = rotate64(x + y + v.first + fetch64(s + 8), 37) * mul; 204 | y = rotate64(y + v.second + fetch64(s + 48), 42) * mul; 205 | x ^= w.second * 9; 206 | y += v.first * 9 + fetch64(s + 40); 207 | z = rotate64(z + w.first, 33) * mul; 208 | v = weak_hash_len_32_with_seeds(s, v.second * mul, x + w.first); 209 | w = weak_hash_len_32_with_seeds(s + 32, z + w.second, y + fetch64(s + 16)); 210 | swap(z, x); 211 | 212 | return hash_len_16(hash_len_16(v.first, w.first, mul) + shift_mix(y) * k0 + z, 213 | hash_len_16(v.second, w.second, mul) + x, mul); 214 | } 215 | 216 | uint64_t hash_data(const data_t data) 217 | { 218 | return cfarmhash(data_base(data), data_size(data)); 219 | } 220 | 221 | uint64_t hash_uint64(uint64_t v) 222 | { 223 | v ^= v >> 33; 224 | v *= 0xff51afd7ed558ccdULL; 225 | v ^= v >> 33; 226 | v *= 0xc4ceb9fe1a85ec53ULL; 227 | v ^= v >> 33; 228 | return v; 229 | } 230 | --------------------------------------------------------------------------------