From 4e39ae709f45684b045896a218b176f21e8521bf Mon Sep 17 00:00:00 2001 From: overflowerror Date: Fri, 17 Apr 2020 23:25:56 +0200 Subject: [PATCH] working version --- Makefile | 40 +++++ src/arrays.c | 65 ++++++++ src/arrays.h | 17 ++ src/demo.c | 33 ++++ src/do-test.c | 5 + src/functional.h | 76 +++++++++ src/linked-test.c | 127 +++++++++++++++ src/linked.c | 194 +++++++++++++++++++++++ src/linked.h | 29 ++++ src/streams-test.c | 283 +++++++++++++++++++++++++++++++++ src/streams.c | 378 +++++++++++++++++++++++++++++++++++++++++++++ src/streams.h | 108 +++++++++++++ src/strings-test.c | 72 +++++++++ src/strings.c | 20 +++ src/strings.h | 15 ++ 15 files changed, 1462 insertions(+) create mode 100644 Makefile create mode 100644 src/arrays.c create mode 100644 src/arrays.h create mode 100644 src/demo.c create mode 100644 src/do-test.c create mode 100644 src/functional.h create mode 100644 src/linked-test.c create mode 100644 src/linked.c create mode 100644 src/linked.h create mode 100644 src/streams-test.c create mode 100644 src/streams.c create mode 100644 src/streams.h create mode 100644 src/strings-test.c create mode 100644 src/strings.c create mode 100644 src/strings.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dd98f50 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +CC = gcc +CFLAGS = -std=c99 -Wall -D_POSIX_C_SOURCE=201112L -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -static +LD = gcc +LDFLAGS = -static + +LIBFLAGS = -Llibs -Ilibs + +OBJS = obj/arrays.o obj/linked.o obj/streams.o obj/strings.o +DEPS = $(OBJS:%.o=%.d) + +all: $(BIN_NAME) test + +demo: obj/demo.o $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +test: $(OBJS) obj/do-test.o obj/linked-test.o obj/streams-test.o obj/strings-test.o + $(LD) $(LDFLAGS) -o $@ $^ $(LIBFLAGS) -lviscocity + +obj/%-test.o: src/%-test.c obj + $(CC) $(CFLAGS) $(LIBFLAGS) -MMD -c -o $@ $< + + +valgrind: CFLAGS += -static -g +valgrind: clean test + valgrind --leak-check=yes ./test + +-include $(DEPS) + +obj/%.o: src/%.c obj + $(CC) $(CFLAGS) -MMD -c -o $@ $< + +obj: + @mkdir -p obj + +clean: + @echo "Cleaning up..." + @rm -f obj/*.o + @rm -f obj/*.d + @rm -f test + @rm -f demo diff --git a/src/arrays.c b/src/arrays.c new file mode 100644 index 0000000..102e768 --- /dev/null +++ b/src/arrays.c @@ -0,0 +1,65 @@ +#include "arrays.h" +#include "streams.h" + +typedef struct { + stream_t stream; + + raw_t* array; + size_t length; + size_t elementSize; +} arrayStream_t; + +static inline arrayStream_t* getCurrentArrayStream() { + return (arrayStream_t*) getCurrentStream(); +} + +void ptrArrayStreamIterate(predicate_t predicate) { + arrayStream_t* stream = getCurrentArrayStream(); + + for (size_t i = 0; i < stream->length; i++) { + if (predicate(stream->array[i])) { + break; + } + } +} + +void primitiveArrayStreamIterate(predicate_t predicate) { + arrayStream_t* stream = getCurrentArrayStream(); + + for (size_t i = 0; i < stream->length; i++) { + raw_t data = 0; + for (size_t j = 0; j < stream->elementSize; j++) { + data |= ((unsigned char*) stream->array)[i * stream->elementSize + j] << (j * 8); + } + + if (predicate(data)) { + break; + } + } +} + +stream_t arrayStream(void* array, size_t size, size_t elementSize) { + arrayStream_t* stream = malloc(sizeof(arrayStream_t)); + if (stream == NULL) { + fprintf(stderr, "Fuck\n"); + exit(1); + } + skeletonStream(stream); + + + stream->array = array; + stream->length = size; + stream->elementSize = elementSize; + + stream->stream.__iterate = (elementSize == sizeof(void*)) ? &ptrArrayStreamIterate : &primitiveArrayStreamIterate; + + return *((stream_t*) stream); +} + +stream_t ptrStream(raw_t* array, size_t length) { + return arrayStream(array, length, sizeof(void*)); +} + +stream_t primitiveStream(void* array, size_t length, size_t primitiveSize) { + return arrayStream(array, length, primitiveSize); +} diff --git a/src/arrays.h b/src/arrays.h new file mode 100644 index 0000000..4ca88ca --- /dev/null +++ b/src/arrays.h @@ -0,0 +1,17 @@ +#ifndef ARRAYS_H +#define ARRAYS_H + +#include "streams.h" + +typedef struct arrayWithLength { + void* array; + size_t length; +} arrayWithLength_t; + +stream_t arrayStream(void*, size_t, size_t); + +stream_t ptrStream(raw_t*, size_t); + +stream_t primitiveStream(void*, size_t, size_t); + +#endif diff --git a/src/demo.c b/src/demo.c new file mode 100644 index 0000000..a336856 --- /dev/null +++ b/src/demo.c @@ -0,0 +1,33 @@ +#include "functional.h" + +#include "streams.h" +#include "strings.h" +#include "arrays.h" + +#include +#include +#include +#include +#include + +#define ARRAY_LENGTH (5) + +int main(int argc, char** argv) { + const char* array[ARRAY_LENGTH] = { + "Hello", "World", "with", "no", "Streams" + }; + + ptrStream((void*) array, ARRAY_LENGTH) + .peek(qConsumer(obj, printf("%s\n", (char*) obj))) + .filter(stringNotEqualsPredicate("no")) + .map(qFunction(obj, (raw_t) charStream((char*) obj) + .map(qFunction(c, toupper((char) c))) + .collect(stringCollector()) + )).forEach(Consumer(obj, { + printf("%s\n", (char*) obj); + free((char*) obj); + })); + + + return 0; +} diff --git a/src/do-test.c b/src/do-test.c new file mode 100644 index 0000000..b57ba6e --- /dev/null +++ b/src/do-test.c @@ -0,0 +1,5 @@ +#include + +int main() { + return doTests(); +} diff --git a/src/functional.h b/src/functional.h new file mode 100644 index 0000000..87f11c9 --- /dev/null +++ b/src/functional.h @@ -0,0 +1,76 @@ +#ifndef FUNCTIONAL_H +#define FUNCTIONAL_H + +#include +#include + +#define lambda(r, f) ({r __fn__ f __fn__; }) + +typedef unsigned long long raw_t; + +typedef struct supply { + bool hasMore; + raw_t data; +} supply_t; + +typedef raw_t (*function_t)(raw_t); +typedef void (*consumer_t)(raw_t); +typedef void (*biconsumer_t)(raw_t, raw_t); +typedef bool (*predicate_t)(raw_t); +typedef raw_t (*supplier_t)(void); +typedef int (*comparator_t)(raw_t, raw_t); +typedef supply_t (*multisupplier_t)(void); + +#define Function(i, f) lambda(raw_t, (raw_t i) f) +#define Consumer(i, f) lambda(void, (raw_t i) f) +#define Predicate(i, f) lambda(bool, (raw_t i) f) +#define BiConsumer(i1, i2, f) lambda(void, (raw_t i1, raw_t i2) f) +#define Supplier(f) lambda(raw_t, () f) +#define Comparator(i1, i2, f) lambda(int, (raw_t i1, raw_t i2) f) + +#define qFunction(i, f) Function(i, { return f; }) +#define qConsumer(i, f) Consumer(i, { f; }) +#define qPredicate(i, f) Predicate(i, { return f; }) +#define qBiConsumer(i1, i2, f) BiConsumer(i1, i2, { f; }) +#define qSupplier(f) Supplier({ return f; }) +#define qComparator(i1, i2, f) Comparator(i1, i2, { return f; }) + +typedef struct collector { + supplier_t supplier; + biconsumer_t accumulator; + function_t finisher; +} collector_t; + +enum iteratorResultStepType { + CONTINUE, + BREAK +}; + +enum iteratorResultModifyType { + KEEP, + REPLACE, + DELETE +}; + +typedef struct iteratorResult { + enum iteratorResultStepType step; + enum iteratorResultModifyType modify; + raw_t data; +} iter_result_t; + +#define ITERATOR_CONTINUE ((iter_result_t) { .step = CONTINUE, .modify = KEEP }) +#define ITERATOR_BREAK ((iter_result_t) { .step = BREAK, .modify = KEEP }) +#define ITERATOR_DELETE ((iter_result_t) { .step = CONTINUE, .modify = DELETE }) +#define ITERATOR_REPLACE(d) ((iter_result_t) { .step = CONTINUE, .modify = REPLACE, .data = d }) + +typedef iter_result_t (*iterator_t)(size_t, raw_t); + +#define Iterator(i, d, f) lambda(iter_result_t, (size_t i, raw_t d) f) + + +typedef struct optional { + raw_t value; + bool present; +} optional_t; + +#endif diff --git a/src/linked-test.c b/src/linked-test.c new file mode 100644 index 0000000..5f25935 --- /dev/null +++ b/src/linked-test.c @@ -0,0 +1,127 @@ +#include + +#include "functional.h" +#include "linked.h" +#include "arrays.h" + +test(linked_empty, "linked_empty creates new linked_t with size 0") { + linked_t list = linked_empty(); + + assertTrue(linked_size(&list) == 0, "list has size 0"); +} + +test(linked_add_get_clear, "add, get and clear elements from list") { + linked_t list = linked_empty(); + assertTrue(linked_size(&list) == 0, "list has size 0"); + + linked_add(&list, 42); + assertTrue(linked_size(&list) == 1, "list has size 1"); + + assertTrue(linked_get(&list, 0) == 42, "get correct value from list"); + + linked_add(&list, 1337); + assertTrue(linked_size(&list) == 2, "list has size 1"); + + assertTrue(linked_get(&list, 1) == 1337, "get correct value from list"); + + linked_clear(&list); + assertTrue(linked_size(&list) == 0, "list has size 0"); +} + +test(linked_get_empty, "get non-existing entry returns 0") { + linked_t list = linked_empty(); + assertTrue(linked_size(&list) == 0, "list has size 0"); + + assertTrue(linked_get(&list, 0) == 0, "get correct value from list"); +} + +test(linked_fromArray_get, "create list from array and get elements") { + const char* array[] = {"Hello", "World", "foobar"}; + + linked_t list = linked_fromArray((void**) array, 3); + assertTrue(linked_size(&list) == 3, "list has size 3"); + + assertStringEqual((char*) linked_get(&list, 0), "Hello", "first element in list is \"Hello\""); + assertStringEqual((char*) linked_get(&list, 1), "World", "second element in list is \"World\""); + assertStringEqual((char*) linked_get(&list, 2), "foobar", "third element in list is \"foobar\""); + + linked_clear(&list); +} + +test(linked_fromSupplier_get, "create list from supplier and get elements") { + const char* array[] = {"Hello", "World", "foobar"}; + int i = 0; + + assertCallExact(4, "supplier call assertion"); + + linked_t list = linked_fromSupplier(lambda(supply_t, () { + assertCallExact(4, "supplier call assertion"); + + supply_t supply; + supply.data = (raw_t) array[i]; + supply.hasMore = ++i < 3; + + return supply; + })); + + assertTrue(linked_size(&list) == 3, "list has size 3"); + + assertStringEqual((char*) linked_get(&list, 0), "Hello", "first element in list is \"Hello\""); + assertStringEqual((char*) linked_get(&list, 1), "World", "second element in list is \"World\""); + assertStringEqual((char*) linked_get(&list, 2), "foobar", "third element in list is \"foobar\""); + + linked_clear(&list); +} + +test(linked_toArray, "get array from list") { + const char* originalArray[] = {"Hello", "World", "foobar"}; + + linked_t list = linked_fromArray((void**) originalArray, 3); + + size_t size; + const char** array = linked_toArray(&list, &size); + assertTrue(size == 3, "size of new array is correct"); + + for (int i = 0; i < size; i++) { + assertStringEqual(array[0], originalArray[0], "value in new array is correct"); + } + + linked_clear(&list); + free(array); +} + +test(linked_toPrimitiveArray, "get primitive array from list") { + linked_t list = linked_empty(); + + linked_add(&list, 42); + linked_add(&list, 1337); + linked_add(&list, 420); + + size_t size; + int* array = linked_toPrimitiveArray(&list, ofType(int), &size); + + assertTrue(size == 3, "size of new array is correct"); + + assertTrue(array[0] == 42, "value in array is correct"); + assertTrue(array[1] == 1337, "value in array is correct"); + assertTrue(array[2] == 420, "value in array is correct"); + + linked_clear(&list); + free(array); +} + +test(linked_iterate_continue, "iterator gets called for all elements in list on continue") { + const char* array[] = {"Hello", "World", "foobar"}; + + linked_t list = linked_fromArray((void**) array, 3); + + assertCallExact(4, "iterator call assertion"); + + linked_iterate(&list, lambda(iter_result_t, (size_t i, raw_t data) { + assertCallExact(4, "iterator call assertion"); + + assertStringEqual((const char*) data, array[i], "value in array is correct"); + + return ITERATOR_CONTINUE; + })); +} diff --git a/src/linked.c b/src/linked.c new file mode 100644 index 0000000..4e434e1 --- /dev/null +++ b/src/linked.c @@ -0,0 +1,194 @@ +#include "linked.h" + +#include +#include + +struct linkedElement* linked_createElement(raw_t data) { + struct linkedElement* element = malloc(sizeof(struct linkedElement)); + if (element == NULL) { + fprintf(stderr, "Fuck\n"); + exit(1); + } + + element->data = data; + element->next = NULL; + + return element; +} + +linked_t linked_empty() { + return (linked_t) { .head = NULL }; +} + + +linked_t linked_fromArray(void** array, size_t length) { + linked_t list = linked_empty(); + + struct linkedElement** nextRef = &list.head; + + for (size_t i = 0; i < length; i++) { + *nextRef = linked_createElement((raw_t) array[i]); + nextRef = &(*nextRef)->next; + } + + return list; +} + +linked_t linked_fromSupplier(multisupplier_t supplier) { + linked_t list = linked_empty(); + + struct linkedElement** nextRef = &list.head; + + supply_t supply = { .hasMore = true, .data = (raw_t) NULL }; + + while(supply.hasMore) { + supply = supplier(); + + *nextRef = linked_createElement(supply.data); + nextRef = &((*nextRef)->next); + } + + return list; +} + +size_t linked_add(linked_t* list, raw_t data) { + struct linkedElement** nextRef = &(list->head); + + size_t position = 0; + + while (*nextRef != NULL) { + nextRef = &((*nextRef)->next); + position++; + } + + *nextRef = linked_createElement(data); + + return position; +} + +size_t linked_size(linked_t* list) { + struct linkedElement* current = list->head; + + size_t size = 0; + + while (current != NULL) { + size++; + + current = current->next; + } + + return size; +} + +raw_t linked_get(linked_t* list, size_t index) { + raw_t result = 0; + + linked_iterate(list, Iterator(i, d, { + if (i == index) { + result = d; + return ITERATOR_BREAK; + } else { + return ITERATOR_CONTINUE; + } + })); + + return result; +} + +void linked_iterate(linked_t* list, iterator_t iterator) { + struct linkedElement** refToCurrent = &list->head; + struct linkedElement* current = list->head; + + size_t index = 0; + + while(current != NULL) { + iter_result_t result = iterator(index, current->data); + + bool skipContinue = false; + + switch(result.modify) { + case DELETE: + *refToCurrent = current->next; + + struct linkedElement* next = current->next; + free(current); + current = next; + + skipContinue = true; + break; + + case REPLACE: + current->data = result.data; + break; + case KEEP: + break; + } + + switch(result.step) { + case BREAK: + return; + case CONTINUE: + if (skipContinue) + break; + + refToCurrent = ¤t->next; + current = current->next; + break; + } + + index++; + } +} + +void linked_clear(linked_t* list) { + struct linkedElement* current = list->head; + while (current != NULL) { + struct linkedElement* next = current->next; + free(current); + current = next; + } + + list->head = NULL; +} + +void* linked_toArray(linked_t* list, size_t* length) { + size_t size = linked_size(list); + + if (length != NULL) + *length = size; + + raw_t* array = malloc(sizeof(raw_t) * size); + if (array == NULL) { + fprintf(stderr, "Fuck\n"); + exit(1); + } + + linked_iterate(list, Iterator(i, data, { + array[i] = data; + return ITERATOR_CONTINUE; + })); + + return array; +} + +void* linked_toPrimitiveArray(linked_t* list, size_t primitiveSize, size_t* length) { + size_t size = linked_size(list); + + if (length != NULL) + *length = size; + + unsigned char* array = malloc(primitiveSize * size); + if (array == NULL) { + fprintf(stderr, "Fuck\n"); + exit(1); + } + + linked_iterate(list, Iterator(i, data, { + for(size_t j = 0; j < primitiveSize; j++) { + array[i * primitiveSize + j] = (data >> (j * 8)) & 0xff; + } + return ITERATOR_CONTINUE; + })); + + return array; +} diff --git a/src/linked.h b/src/linked.h new file mode 100644 index 0000000..555a60d --- /dev/null +++ b/src/linked.h @@ -0,0 +1,29 @@ +#ifndef LINKED_H +#define LINKED_H + +#include "functional.h" + +struct linkedElement { + struct linkedElement* next; + raw_t data; +}; + +typedef struct linked { + struct linkedElement* head; +} linked_t; + +linked_t linked_empty(); +linked_t linked_fromArray(void**, size_t); +linked_t linked_fromSupplier(multisupplier_t); + +size_t linked_add(linked_t* list, raw_t data); +size_t linked_size(linked_t* list); +raw_t linked_get(linked_t*, size_t); + +void linked_iterate(linked_t* list, iterator_t iterator); +void linked_clear(linked_t* list); + +void* linked_toArray(linked_t*, size_t*); +void* linked_toPrimitiveArray(linked_t*, size_t, size_t*); + +#endif diff --git a/src/streams-test.c b/src/streams-test.c new file mode 100644 index 0000000..49acc9a --- /dev/null +++ b/src/streams-test.c @@ -0,0 +1,283 @@ +#include + +#include "streams.h" +#include "strings.h" +#include "arrays.h" + +#include +#include +#include + + +test(stream_forEach, "forEach get's called for every element in stream") { + const char* strings[] = {"Hello", "World"}; + + assertCallExact(3, "forEach call assertion"); + + ptrStream((void*) strings, 2) + .forEach(Consumer(s, { + assertCallExact(3, "forEach call assertion"); + + assertTrue(strcmp((char*) s, "Hello") == 0 || strcmp((char*) s, "World") == 0, "s is one of the elements in the stream"); + })); +} + +test(stream_peek, "peek get's called for every element in stream") { + const char* strings[] = {"Hello", "World"}; + + assertCallExact(3, "peek call assertion"); + + ptrStream((void*) strings, 2) + .peek(Consumer(s, { + assertCallExact(3, "peek call assertion"); + + assertTrue(strcmp((char*) s, "Hello") == 0 || strcmp((char*) s, "World") == 0, "s is one of the elements in the stream"); + })).forEach(Consumer(s, {})); +} + +test(stream_map, "map sees and replaces all elements in the stream") { + int numbers[] = {1, 2, 3}; + + assertCallExact(4, "forEach call assertion"); + + primitiveStream((void*) numbers, 3, ofType(int)) + .map(qFunction(i, i*i)) + .forEach(Consumer(i, { + assertCallExact(4, "forEach call assertion"); + assertTrue(i == 1 || i == 4 || i == 9, "i is one of the elements in the stream"); + })); +} + +test(stream_filter, "filter removes non-matching elements from stream") { + const char* strings[] = {"Hello", "World"}; + + assertCallExact(2, "forEach call assertion"); + + ptrStream((void*) strings, 2) + .filter(qPredicate(s, ((char*) s)[0] == 'W')) + .forEach(Consumer(s, { + assertStringEqual((char*) s, "World", NULL); + assertCallExact(2, "forEach call assertion"); + })); +} + +test(stream_limit, "limit aborts a stream after a certain number of elements") { + const char* strings[] = {"Hello", "World", "this", "is", "a", "test"}; + + assertCallExact(3, "forEach call assertion"); + + ptrStream((void*) strings, 6) + .limit(2) + .forEach(Consumer(s, { + assertCallExact(3, "forEach call assertion"); + assertTrue(strcmp((char*) s, "Hello") == 0 || strcmp((char*) s, "World") == 0, "s is one of the elements in the stream"); + })); +} + +test(stream_skip, "skip over a certain number of elements") { + const char* strings[] = {"Hello", "World", "this", "is", "a", "test"}; + + assertCallExact(3, "forEach call assertion"); + + ptrStream((void*) strings, 6) + .skip(4) + .forEach(Consumer(s, { + assertCallExact(3, "forEach call assertion"); + assertTrue(strcmp((char*) s, "a") == 0 || strcmp((char*) s, "test") == 0, "s is one of the elements in the stream"); + })); +} + + + + +test(stream_count, "count reports correct number") { + const char* strings[] = {"Hello", "World", "this", "is", "a", "test"}; + + size_t count = ptrStream((void*) strings, 6) + .count(); + + assertTrue(count == 6, "count is correct"); +} + +test(stream_count_zero, "count reports correct number on empty stream") { + const char* strings[] = {}; + + size_t count = ptrStream((void*) strings, 0) + .count(); + + assertTrue(count == 0, "count is correct"); +} + +test(stream_max, "get max value of stream") { + int numbers[] = {42, 1337, 420}; + + optional_t max = primitiveStream((void*) numbers, 3, ofType(int)) + .max(qComparator(i1, i2, i1 > i2)); + + assertTrue(max.present, "there is a max value"); + assertTrue(max.value == 1337, "max is correct"); +} + +test(stream_max_empty, "get max value of empty stream") { + int numbers[] = {}; + + optional_t max = primitiveStream((void*) numbers, 0, ofType(int)) + .max(qComparator(i1, i2, i1 > i2)); + + assertFalse(max.present, "there is no max value"); +} + +test(stream_min, "get min value of stream") { + int numbers[] = {42, 1337, 420}; + + optional_t min = primitiveStream((void*) numbers, 3, ofType(int)) + .min(qComparator(i1, i2, i1 > i2)); + + assertTrue(min.present, "there is a min value"); + assertTrue(min.value == 42, "max is correct"); +} + +test(stream_min_empty, "get min value of empty stream") { + int numbers[] = {}; + + optional_t min = primitiveStream((void*) numbers, 0, ofType(int)) + .min(qComparator(i1, i2, i1 > i2)); + + assertFalse(min.present, "there is no min value"); +} + +test(stream_allMatch_positive, "allMatch correctly reports positive matches") { + int numbers[] = {2, 2, 3}; + + assertTrue(primitiveStream((void*) numbers, 3, ofType(int)) + .allMatch(qPredicate(i, i > 1)), "are all numbers greater than 1"); +} + +test(stream_allMatch_negative, "allMatch correctly reports negative matches") { + int numbers[] = {2, 1, 3}; + + assertFalse(primitiveStream((void*) numbers, 3, ofType(int)) + .allMatch(qPredicate(i, i > 1)), "are not all numbers greater than 1"); +} + +test(stream_anyMatch_positive, "anyMatch correctly reports positive matches") { + int numbers[] = {2, 2, 3}; + + assertTrue(primitiveStream((void*) numbers, 3, ofType(int)) + .anyMatch(qPredicate(i, i > 2)), "one of the elements is > 2"); +} + +test(stream_anyMatch_negative, "anyMatch correctly reports negative matches") { + int numbers[] = {2, 1, 3}; + + assertFalse(primitiveStream((void*) numbers, 3, ofType(int)) + .allMatch(qPredicate(i, i > 3)), "none of the elements is > 3"); +} + +test(stream_collect_array, "arrayCollector correctly builds an array of all elements in the stream") { + int numbers[] = {42, 1337, 420}; + + arrayWithLength_t* array = (void*) primitiveStream((void*) numbers, 3, ofType(int)) + .collect(arrayCollector(ofType(int))); + + assertTrue(array->length == 3, "resulting array length is okay"); + + for (int i = 0; i < array->length; i++) { + assertTrue(((int*) (array->array))[i] == numbers[i], "checking values in array"); + } + + free(array->array); + free(array); +} + +test(stream_collect_string, "stringCollector correctly build a string of all elements in the stream") { + char characters[] = "Hello World"; + + char* string = (char*) charStream(characters) + .collect(stringCollector()); + + assertStringEqual(characters, string, "strings match"); + + free(string); +} + +test(stream_forEach_empty, "forEach doesn't get called on empty stream") { + const char* strings[] = {}; + + assertCallExact(1, "forEach call assertion"); + + ptrStream((void*) strings, 0) + .forEach(Consumer(s, { + assertCallExact(1, "forEach call assertion"); + })); +} + +test(stream_allMatch_empty, "allMatch on empty stream returns true") { + const char* strings[] = {}; + + assertTrue(ptrStream((void*) strings, 0) + .allMatch(qPredicate(s, true)), "allMatch returns true"); +} + +test(stream_collect_array_empty, "arrayCollector returns empty array on empty stream") { + int numbers[] = {}; + + arrayWithLength_t* array = (void*) primitiveStream((void*) numbers, 0, ofType(int)) + .collect(arrayCollector(ofType(int))); + + assertTrue(array->length == 0, "array length is 0"); + + free(array->array); + free(array); +} + +test(stream_collect_string_empty, "stringCollector returns empty string on empty stream") { + char characters[] = ""; + + char* string = (char*) charStream(characters) + .collect(stringCollector()); + + assertNotNull(string, "string is not null"); + assertStringEqual(string, "", "string is empty"); + + free(string); +} + + + + +test(stream_iterate, "stream from iterate works") { + assertCallExact(4, "forEach call assertion"); + + Stream.iterate(1, qFunction(last, 2*last)) + .limit(3) + .forEach(Consumer(i, { + assertCallExact(4, "forEach call assertion"); + + assertTrue(i == 1 || i == 2 || i == 4, "correct values in stream"); + })); +} + +test(stream_generate, "stream from generate works") { + assertCallExact(5, "supplier call assertion"); + assertCallExact(4, "forEach call assertion"); + + int i = 1; + Stream.generate(Supplier({ + assertCallExact(5, "supplier call assertion"); + + i *= 2; + return i; + })).limit(3) + .forEach(Consumer(i, { + assertCallExact(4, "forEach call assertion"); + + assertTrue(i == 2 || i == 4 || i == 8, "correct values in stream"); + })); +} + +test(stream_empty, "empty stream") { + size_t count = Stream.empty().count(); + + assertTrue(count == 0, "count is correct"); +} diff --git a/src/streams.c b/src/streams.c new file mode 100644 index 0000000..5e7a078 --- /dev/null +++ b/src/streams.c @@ -0,0 +1,378 @@ +#include "streams.h" + +#include "arrays.h" + +#include +#include + +#define MAX_STREAMS (1<<5) +stream_t* streams[MAX_STREAMS]; +int currentStream = -1; + +stream_t* getCurrentStream() { + return streams[currentStream]; +} + +// true for break; false for continue +bool streamApply(stream_t* stream, raw_t data, size_t elementIndex, size_t operationIndex, consumer_t finally) { + if (operationIndex >= stream->_operationsLength) { + finally(data); + return false; + } else { + operation_t operation = stream->_operations[operationIndex]; + switch(operation.type) { + case map: + data = ((function_t) operation.action)(data); + break; + case peek: + ((consumer_t) operation.action)(data); + break; + case filter: + if (!((predicate_t) operation.action)(data)) { + return false; // abort for this piece of data + } + break; + case limit: + if (elementIndex >= (size_t) operation.action) + return true; + break; + case skip: + if (elementIndex < (size_t) operation.action) + return false; + break; + } + + stream->_apply(stream, data, elementIndex, operationIndex + 1, finally); + } + + return false; +} + +#define OPERATION_CHUNK_SIZE (8) + +void streamAddOperation(enum operationType type, void* action) { + stream_t* stream = getCurrentStream(); + + if (stream->_operationsLength % OPERATION_CHUNK_SIZE == 0) { + operation_t* operations = realloc(stream->_operations, sizeof(operation_t) * ((stream->_operationsLength) / OPERATION_CHUNK_SIZE + 1) * OPERATION_CHUNK_SIZE); + if (operations == NULL) { + fprintf(stderr, "Fuck\n"); + exit(1); + } + stream->_operations = operations; + } + + stream->_operations[stream->_operationsLength++] = (operation_t) { + .action = action, + .type = type + }; +} + +stream_t streamMap(function_t function) { + stream_t* stream = getCurrentStream(); + + stream->_addOperation(map, function); + + return *stream; +} + +stream_t streamPeek(consumer_t consumer) { + stream_t* stream = getCurrentStream(); + + stream->_addOperation(peek, consumer); + + return *stream; +} + +stream_t streamFilter(predicate_t predicate) { + stream_t* stream = getCurrentStream(); + + stream->_addOperation(filter, predicate); + + return *stream; +} + +stream_t streamLimit(size_t maxSize) { + stream_t* stream = getCurrentStream(); + + stream->_addOperation(limit, (void*) maxSize); + + return *stream; +} + +stream_t streamSkip(size_t n) { + stream_t* stream = getCurrentStream(); + + stream->_addOperation(skip, (void*) n); + + return *stream; +} + +bool streamAllMatch(predicate_t predicate) { + stream_t* stream = getCurrentStream(); + + bool allMatch = true; + + size_t i = 0; + stream->__iterate(lambda(bool, (raw_t data) { + bool abort = stream->_apply(stream, data, i++, 0, lambda(void, (raw_t data) { + if (!predicate(data)) { + allMatch = false; + } + })); + if (!allMatch) + abort = true; + return abort; + })); + + stream->__destroy(); + + return allMatch; +} + +bool streamAnyMatch(predicate_t predicate) { + stream_t* stream = getCurrentStream(); + + bool anyMatch = false; + + size_t i = 0; + stream->__iterate(lambda(bool, (raw_t data) { + bool abort = stream->_apply(stream, data, i++, 0, lambda(void, (raw_t data) { + if (predicate(data)) { + anyMatch = true; + } + })); + if (anyMatch) + abort = true; + return abort; + })); + + stream->__destroy(); + + return anyMatch; +} + +void streamForEach(consumer_t consumer) { + stream_t* stream = getCurrentStream(); + + size_t i = 0; + stream->__iterate(lambda(bool, (raw_t data) { + return stream->_apply(stream, data, i++, 0, consumer); + })); + + stream->__destroy(); +} + +size_t streamCount() { + stream_t* stream = getCurrentStream(); + + size_t i = 0; + stream->__iterate(lambda(bool, (raw_t data) { + return stream->_apply(stream, data, i++, 0, Consumer(obj, {})); + })); + + stream->__destroy(); + + return i; +} + +optional_t streamMin(comparator_t comparator) { + stream_t* stream = getCurrentStream(); + + raw_t current; + bool present = false; + stream->__iterate(lambda(bool, (raw_t data) { + if (!present) { + current = data; + present = true; + } else if (comparator(data, current) < 0) { + current = data; + } + return false; + })); + + stream->__destroy(); + + return (optional_t) { + .value = current, + .present = present, + }; +} + +optional_t streamMax(comparator_t comparator) { + stream_t* stream = getCurrentStream(); + + raw_t current; + bool present = false; + stream->__iterate(lambda(bool, (raw_t data) { + if (!present) { + current = data; + present = true; + } else if (comparator(data, current) > 0) { + current = data; + } + return false; + })); + + stream->__destroy(); + + return (optional_t) { + .value = current, + .present = present, + }; +} + +raw_t streamCollect(collector_t collector) { + stream_t* stream = getCurrentStream(); + + raw_t result = collector.supplier(); + + size_t i = 0; + stream->__iterate(lambda(bool, (raw_t data) { + return stream->_apply(stream, data, i++, 0, lambda(void, (raw_t data) { + collector.accumulator(result, data); + })); + })); + + result = collector.finisher(result); + + stream->__destroy(); + + return result; +} + +void streamDestroy() { + stream_t* stream = getCurrentStream(); + + if (stream->_operations != NULL) { + free(stream->_operations); + } + + free(stream); + currentStream--; +} + +void skeletonStream(void* _stream) { + stream_t* stream = (stream_t*) _stream; + + stream->_operations = NULL; + stream->_operationsLength = 0; + + stream->_addOperation = &streamAddOperation; + stream->_destroy = &streamDestroy; + + stream->_apply = &streamApply; + + stream->__destroy = &streamDestroy; + + stream->map = &streamMap; + stream->peek = &streamPeek; + stream->filter = &streamFilter; + stream->limit = &streamLimit; + stream->skip = &streamSkip; + + stream->allMatch = &streamAllMatch; + stream->anyMatch = &streamAnyMatch; + stream->forEach = &streamForEach; + stream->count = &streamCount; + stream->min = &streamMin; + stream->max = &streamMax; + stream->collect = &streamCollect; + + streams[++currentStream] = stream; +} + +typedef struct { + stream_t stream; + raw_t seed; + function_t function; +} baseIterateStream_t; + +typedef struct { + stream_t stream; + supplier_t supplier; +} baseGenerateStream_t; + +typedef struct { + stream_t stream; + stream_t source1; + stream_t source2; +} baseConcatStream_t; + +void iterateStream_iterate(predicate_t predicate) { + baseIterateStream_t* stream = (baseIterateStream_t*) getCurrentStream(); + + raw_t current = stream->seed; + + while(true) { + if (predicate(current)) { + break; + } + current = stream->function(current); + } +} + +stream_t stream_iterate(raw_t seed, function_t function) { + baseIterateStream_t* stream = malloc(sizeof(baseIterateStream_t)); + if (stream == NULL) { + fprintf(stderr, "Fuck\n"); + exit(1); + } + skeletonStream(stream); + + stream->seed = seed; + stream->function = function; + + stream->stream.__iterate = &iterateStream_iterate; + + return *((stream_t*) stream); +} + +void generateStream_iterate(predicate_t predicate) { + baseGenerateStream_t* stream = (baseGenerateStream_t*) getCurrentStream(); + + while(true) { + if (predicate(stream->supplier())) { + break; + } + } +} + +stream_t stream_generate(supplier_t supplier) { + baseGenerateStream_t* stream = malloc(sizeof(baseGenerateStream_t)); + if (stream == NULL) { + fprintf(stderr, "Fuck\n"); + exit(1); + } + skeletonStream(stream); + + stream->supplier = supplier; + + stream->stream.__iterate = &generateStream_iterate; + + return *((stream_t*) stream); +} + +void emptyStream_iterate(predicate_t predicate) { + // do nothing +} + +stream_t stream_empty(supplier_t supplier) { + stream_t* stream = malloc(sizeof(stream_t)); + if (stream == NULL) { + fprintf(stderr, "Fuck\n"); + exit(1); + } + skeletonStream(stream); + + stream->__iterate = &emptyStream_iterate; + + return *stream; +} + +const struct globalStream Stream = { + .iterate = &stream_iterate, + .generate = &stream_generate, + .empty = &stream_empty, + //.concat = &stream_concat, +}; diff --git a/src/streams.h b/src/streams.h new file mode 100644 index 0000000..0f16d65 --- /dev/null +++ b/src/streams.h @@ -0,0 +1,108 @@ +#ifndef STREAMS_H +#define STREAMS_H + +#include "functional.h" + +#include +#include + +#define ARRAY_COLLECTOR_CHUNK_SIZE (8) + +#define ofType(t) (sizeof(t)) + +#define arrayCollector(s) ((collector_t) {\ + .supplier = Supplier({\ + arrayWithLength_t* internal = malloc(sizeof(struct arrayWithLength));\ + if (internal == NULL) {\ + fprintf(stderr, "Fuck\n");\ + exit(1);\ + }\ + internal->array = NULL;\ + internal->length = 0;\ + return (raw_t) internal;\ + }),\ + .accumulator = BiConsumer(_internal, data, {\ + arrayWithLength_t* internal = (void*) _internal;\ + if (internal->length % ARRAY_COLLECTOR_CHUNK_SIZE == 0) {\ + void* array = realloc(internal->array, s * ((internal->length / ARRAY_COLLECTOR_CHUNK_SIZE) + 1) * ARRAY_COLLECTOR_CHUNK_SIZE);\ + if (array == NULL) {\ + fprintf(stderr, "Fuck\n");\ + exit(1);\ + }\ + internal->array = array;\ + }\ + for (size_t i = 0; i < s; i++) {\ + ((char*) (internal->array))[internal->length * s + i] = (data >> (i * 8)) & 0xff;\ + }\ + internal->length++;\ + }),\ + .finisher = qFunction(_internal, _internal)\ +}) + +#define stringCollector() ({\ + collector_t collector = arrayCollector(ofType(char));\ + collector.finisher = Function(_internal, {\ + arrayWithLength_t* internal = (void*) _internal;\ + if (internal->length == 0 || ((char*) (internal->array))[internal->length - 1] != '\0') {\ + collector.accumulator(_internal, '\0');\ + }\ + raw_t result = (raw_t) internal->array;\ + free(internal);\ + return result;\ + });\ + collector;\ +}) + +#define sumCollector() ((collector_t) {\ + .supplier = qSupplier(0),\ + .accumulator = qBiConsumer(sum, element, sum + element),\ + .finisher = qFunction(sum, sum)\ +}) + +enum operationType { + peek, map, filter, limit, skip +}; + + +typedef struct operation { + enum operationType type; + void* action; +} operation_t; + +typedef struct stream { + operation_t* _operations; + size_t _operationsLength; + + void (*_addOperation)(enum operationType, void*); + bool (*_apply)(struct stream*, raw_t, size_t, size_t, consumer_t); + void (*_destroy)(); + + void (*__destroy)(); + void (*__iterate)(predicate_t); + + struct stream (*map)(function_t); + struct stream (*peek)(consumer_t); + struct stream (*filter)(predicate_t); + struct stream (*limit)(size_t); + struct stream (*skip)(size_t); + optional_t (*min)(comparator_t); + optional_t (*max)(comparator_t); + + bool (*allMatch)(predicate_t); + bool (*anyMatch)(predicate_t); + void (*forEach)(consumer_t); + size_t (*count)(); + raw_t (*collect)(collector_t); +} stream_t; + +const extern struct globalStream { + stream_t (*iterate)(raw_t, function_t); + stream_t (*generate)(supplier_t); + stream_t (*empty)(); +} Stream; + +stream_t* getCurrentStream(); + +void skeletonStream(void*); + +#endif diff --git a/src/strings-test.c b/src/strings-test.c new file mode 100644 index 0000000..4d40cdd --- /dev/null +++ b/src/strings-test.c @@ -0,0 +1,72 @@ +#include + +#include "streams.h" +#include "strings.h" +#include "arrays.h" + +test(string_strclone, "strclone creates copy of string") { + const char* string = "Hello World"; + + char* clone = strclone(string); + + assertNotNull(clone, "clone is not null"); + assertStringEqual(clone, string, "strings are equal"); + + free(clone); +} + +test(string_stringEqualsPredicate, "stringEqualsPredicate") { + const char* array[] = {"Hello", "World"}; + + assertCallExact(2, "forEach call assertion"); + + ptrStream((void*) array, 2) + .filter(stringEqualsPredicate("World")) + .forEach(Consumer(s, { + assertCallExact(2, "forEach call assertion"); + + assertStringEqual((char*) s, "World", "string is \"World\""); + })); +} + +test(string_stringNotEqualsPredicate, "stringNotEqualsPredicate") { + const char* array[] = {"Hello", "World"}; + + assertCallExact(2, "forEach call assertion"); + + ptrStream((void*) array, 2) + .filter(stringNotEqualsPredicate("World")) + .forEach(Consumer(s, { + assertCallExact(2, "forEach call assertion"); + + assertStringEqual((char*) s, "Hello", "string is \"Hello\""); + })); +} + +test(string_stringEmptyPredicate, "stringEmptyPredicate") { + const char* array[] = {"Hello", "", "World"}; + + assertCallExact(2, "forEach call assertion"); + + ptrStream((void*) array, 3) + .filter(stringEmptyPredicate()) + .forEach(Consumer(s, { + assertCallExact(2, "forEach call assertion"); + + assertStringEqual((char*) s, "", "string is empty"); + })); +} + +test(string_stringNotEmptyPredicate, "stringNotEmptyPredicate") { + const char* array[] = {"Hello", "", "World"}; + + assertCallExact(3, "forEach call assertion"); + + ptrStream((void*) array, 3) + .filter(stringNotEmptyPredicate()) + .forEach(Consumer(s, { + assertCallExact(3, "forEach call assertion"); + + assertTrue(strcmp((char*) s, "Hello") == 0 || strcmp((char*) s, "World") == 0, "s is one of the elements in the stream"); + })); +} diff --git a/src/strings.c b/src/strings.c new file mode 100644 index 0000000..8041667 --- /dev/null +++ b/src/strings.c @@ -0,0 +1,20 @@ +#include "strings.h" +#include "arrays.h" + +#include +#include +#include + +stream_t charStream(const char* string) { + return primitiveStream((char*) string, strlen(string), ofType(char)); +} + +char* strclone(const char* string) { + char* clone = malloc(strlen(string) + 1); + if (clone == NULL) { + fprintf(stderr, "Fuck!\n"); + exit(1); + } + strcpy(clone, string); + return clone; +} diff --git a/src/strings.h b/src/strings.h new file mode 100644 index 0000000..aec1515 --- /dev/null +++ b/src/strings.h @@ -0,0 +1,15 @@ +#ifndef STRINGS_H +#define STRINGS_H + +#include "streams.h" + +char* strclone(const char*); + +stream_t charStream(const char*); + +#define stringEqualsPredicate(s) qPredicate(obj, strcmp((char*) obj, s) == 0) +#define stringNotEqualsPredicate(s) qPredicate(obj, strcmp((char*) obj, s) != 0) +#define stringEmptyPredicate(s) qPredicate(obj, strcmp((char*) obj, "") == 0) +#define stringNotEmptyPredicate(s) qPredicate(obj, strcmp((char*) obj, "") != 0) + +#endif