lab 3.3 - add dynamic buffer

This commit is contained in:
Laila van Reenen 2024-03-08 10:07:08 +01:00
parent 6d8cc4f554
commit 3b23e37949
6 changed files with 339 additions and 0 deletions

7
buffer_dyn/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/*.o
/demo
/test
/Makefile
/CMakeFiles
/cmake_install.cmake
/CMakeCache.txt

View File

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.27)
project(buffer)
add_executable(demo demo.c buffer.c)
add_executable(test test.c buffer.c)
target_compile_options(demo PRIVATE -std=c18 -Wall -Wextra -Wpedantic -g3 -O0)
target_compile_options(test PRIVATE -std=c18 -Wall -Wextra -Wpedantic -g3 -O0)

150
buffer_dyn/buffer.c Normal file
View File

@ -0,0 +1,150 @@
#include <stdlib.h>
#include <stddef.h>
#include "buffer.h"
// implementation for a FIFO-buffer with ints
// this very simple FIFO-buffer can only hold one int
#define FIFO_SIZE 8
typedef struct FIFO_element_s {
int value;
void* nextElement;
} FIFO_element_t;
// shared variables within this file
static FIFO_element_t* FIFO_FirstElement_p = NULL;
static FIFO_element_t* FIFO_LastElement_p = NULL;
static unsigned int FIFO_count = 0;
bool FIFO_Full = false;
/** incrementPointer
*
* add one and rotates if it overflows the buffersize
*/
unsigned int incrementPointer(unsigned int p)
{
p++;
if (p == FIFO_SIZE)
{
p = 0;
}
return p;
}
bool buffer_put(int i)
{
bool ok = false;
if ((FIFO_LastElement_p == NULL) && (FIFO_FirstElement_p == NULL))
{
// buffer is empty. add first element
FIFO_FirstElement_p = malloc(sizeof(FIFO_element_t));
if (FIFO_FirstElement_p != NULL)
{
FIFO_FirstElement_p->value = i;
FIFO_FirstElement_p->nextElement = NULL;
FIFO_LastElement_p = FIFO_FirstElement_p;
FIFO_count++;
ok = true;
}
else
{
FIFO_Full = true;
}
}
else if ((FIFO_LastElement_p != NULL) && (FIFO_FirstElement_p != NULL))
{
// add element to exsiting buffer
FIFO_element_t* el = malloc(sizeof(FIFO_element_t));
if (el != NULL)
{
el->value = i;
el->nextElement = NULL;
FIFO_LastElement_p = el;
FIFO_count++;
ok = true;
}
else
{
FIFO_Full = true;
}
}
else
{
// buffer is unhealthy. try to clear it and reinit
//TODO: this while loop is unsave
while (FIFO_FirstElement_p != NULL)
{
buffer_get(NULL);
}
if (FIFO_LastElement_p != NULL)
{
free(FIFO_LastElement_p);
FIFO_LastElement_p = NULL;
}
buffer_put(i);
ok = false;
}
return ok;
}
bool buffer_get(int *p)
{
bool ok = false;
if (FIFO_FirstElement_p != NULL)
{
*p = FIFO_FirstElement_p->value;
FIFO_element_t* next = FIFO_FirstElement_p->nextElement;
free(FIFO_FirstElement_p);
FIFO_count--;
FIFO_Full = false;
if (next == NULL)
{
// this was the last element in the buffer
if (FIFO_LastElement_p == FIFO_FirstElement_p)
{
// clear last element becouse it's the same
FIFO_LastElement_p = NULL;
FIFO_count = 0;
ok = true;
}
else if (FIFO_LastElement_p != NULL)
{
// buffer chain is broken; skip to last element
FIFO_FirstElement_p = FIFO_LastElement_p;
}
else
{
// somehow the last element is NULL
FIFO_count = 0;
}
}
else
{
// set first element pointer to next element
FIFO_FirstElement_p = next;
ok = true;
}
}
else
{
ok = false;
}
return ok;
}
bool buffer_is_full(void)
{
return FIFO_Full;
}
bool buffer_is_empty(void)
{
return ((FIFO_LastElement_p == NULL) && (FIFO_FirstElement_p == NULL));
}
unsigned int number_of_elements_in_buffer()
{
return FIFO_count;
}

24
buffer_dyn/buffer.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _HR_BroJZ_buffer_
#define _HR_BroJZ_buffer_
#include <stdbool.h>
// interface for a buffer with int's
// put value i in buffer if buffer is not full
// returns true on success or false otherways
extern bool buffer_put(int i);
// get value from buffer and writes it to *p if buffer not empty
// returns true on success or false otherways
extern bool buffer_get(int *p);
// returns true when buffer is full or false otherways
extern bool buffer_is_full(void);
// returns true when buffer is empty or false otherways
extern bool buffer_is_empty(void);
extern unsigned int number_of_elements_in_buffer();
#endif

98
buffer_dyn/demo.c Normal file
View File

@ -0,0 +1,98 @@
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include "buffer.h"
void check_errno(int error)
{
if (error < 0)
{
perror("Error");
exit(EXIT_FAILURE);
}
}
void check(int error)
{
if (error != 0)
{
printf("Error: %s\n", strerror(error));
exit(EXIT_FAILURE);
}
}
int msleep(long msec) // millisecond sleep
{
struct timespec ts;
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
return nanosleep(&ts, NULL);
}
typedef struct {
pthread_mutex_t m;
} args;
void *producer(void *arg) // function for producer thread
{
args a = *(args *)arg;
int i = 0;
do
{
check( pthread_mutex_lock(&a.m) );
if (!buffer_is_full())
{
buffer_put(i);
}
check( pthread_mutex_unlock(&a.m) );
i++;
check_errno( msleep(150) );
} while(i != 10);
return NULL;
}
void *consumer(void *arg) // function for consumer thread
{
args a = *(args *)arg;
int i;
do
{
check( pthread_mutex_lock(&a.m) );
if (buffer_get(&i))
{
check_errno( printf("%d\n", i) );
}
check( pthread_mutex_unlock(&a.m) );
check_errno( msleep(1000) );
} while (i != 9);
return NULL;
}
int main(void)
{
args a;
pthread_mutexattr_t ma;
check( pthread_mutexattr_init(&ma) );
check( pthread_mutex_init(&a.m, &ma) );
pthread_t ptc, ptp;
check( pthread_create(&ptc, NULL, consumer, &a) );
check( pthread_create(&ptp, NULL, producer, &a) );
check( pthread_join(ptc, NULL) );
check( pthread_join(ptp, NULL) );
check( pthread_mutex_destroy(&a.m) );
check( pthread_mutexattr_destroy(&ma) );
return EXIT_SUCCESS;
}

54
buffer_dyn/test.c Normal file
View File

@ -0,0 +1,54 @@
#include <stdio.h>
#include "buffer.h"
static int tests = 0;
static int fails = 0;
#define TEST(condition, ...) \
tests++;\
if (!(condition))\
{\
fails++;\
printf("Error: ");\
printf(__VA_ARGS__);\
printf("\n");\
}
#define PRINT_TEST_REPORT\
printf("%d tests performed: %d succeded, %d failed.\n", tests, tests - fails, fails);
void test_put_and_get(int test_value)
{
// test if test_value can be written into the buffer
TEST(buffer_put(test_value), "value %d can not be written into the buffer", test_value)
TEST(!buffer_is_empty(), "buffer still empty after writing into the buffer")
// test if test_value can be retrieved from the buffer
int retrieved_value;
TEST(buffer_get(&retrieved_value), "retrieving %d from the buffer failed", test_value)
TEST(retrieved_value == test_value, "wrong value (%d) retrieved from the buffer, expected %d", retrieved_value, test_value)
TEST(buffer_is_empty(), "buffer not empty after writing and retrieving one int to and from the buffer")
}
int main(void)
{
// test if the buffer is empty at startup
TEST(buffer_is_empty(), "buffer not empty at startup")
// test if the buffer is not full at startup
TEST(!buffer_is_full(), "buffer full at startup")
// test if value 42 can be written into and retrieved from the buffer
test_put_and_get(42);
// test if value 0 can be written into and retrieved from the buffer
test_put_and_get(0);
// write to the buffer until it is full
while (buffer_put(13)) /* do nothing */;
TEST(buffer_is_full(), "buffer not full after put failed")
// retrieve from the buffer until it is empty
int retrieved_value;
while (buffer_get(&retrieved_value)) /* do nothing */;
TEST(buffer_is_empty(), "buffer not empty after get failed")
TEST(retrieved_value == 13, "retrieved value overridden after get failed, expected 13 but got %d", retrieved_value)
PRINT_TEST_REPORT
return 0;
}