mirror of
https://github.com/sigmasternchen/libquid
synced 2025-03-15 06:58:55 +00:00
working version
This commit is contained in:
parent
e00c1b4a90
commit
4e39ae709f
15 changed files with 1462 additions and 0 deletions
40
Makefile
Normal file
40
Makefile
Normal file
|
@ -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
|
65
src/arrays.c
Normal file
65
src/arrays.c
Normal file
|
@ -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);
|
||||
}
|
17
src/arrays.h
Normal file
17
src/arrays.h
Normal file
|
@ -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
|
33
src/demo.c
Normal file
33
src/demo.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "functional.h"
|
||||
|
||||
#include "streams.h"
|
||||
#include "strings.h"
|
||||
#include "arrays.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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;
|
||||
}
|
5
src/do-test.c
Normal file
5
src/do-test.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include <viscocity.h>
|
||||
|
||||
int main() {
|
||||
return doTests();
|
||||
}
|
76
src/functional.h
Normal file
76
src/functional.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#ifndef FUNCTIONAL_H
|
||||
#define FUNCTIONAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
127
src/linked-test.c
Normal file
127
src/linked-test.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
#include <viscocity.h>
|
||||
|
||||
#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;
|
||||
}));
|
||||
}
|
194
src/linked.c
Normal file
194
src/linked.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
#include "linked.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
29
src/linked.h
Normal file
29
src/linked.h
Normal file
|
@ -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
|
283
src/streams-test.c
Normal file
283
src/streams-test.c
Normal file
|
@ -0,0 +1,283 @@
|
|||
#include <viscocity.h>
|
||||
|
||||
#include "streams.h"
|
||||
#include "strings.h"
|
||||
#include "arrays.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
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");
|
||||
}
|
378
src/streams.c
Normal file
378
src/streams.c
Normal file
|
@ -0,0 +1,378 @@
|
|||
#include "streams.h"
|
||||
|
||||
#include "arrays.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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,
|
||||
};
|
108
src/streams.h
Normal file
108
src/streams.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
#ifndef STREAMS_H
|
||||
#define STREAMS_H
|
||||
|
||||
#include "functional.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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
|
72
src/strings-test.c
Normal file
72
src/strings-test.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
#include <viscocity.h>
|
||||
|
||||
#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");
|
||||
}));
|
||||
}
|
20
src/strings.c
Normal file
20
src/strings.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "strings.h"
|
||||
#include "arrays.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
15
src/strings.h
Normal file
15
src/strings.h
Normal file
|
@ -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
|
Loading…
Reference in a new issue