lab 3.3 - add dynamic buffer
This commit is contained in:
parent
6d8cc4f554
commit
3b23e37949
7
buffer_dyn/.gitignore
vendored
Normal file
7
buffer_dyn/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/*.o
|
||||
/demo
|
||||
/test
|
||||
/Makefile
|
||||
/CMakeFiles
|
||||
/cmake_install.cmake
|
||||
/CMakeCache.txt
|
||||
6
buffer_dyn/CMakeLists.txt
Normal file
6
buffer_dyn/CMakeLists.txt
Normal 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
150
buffer_dyn/buffer.c
Normal 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
24
buffer_dyn/buffer.h
Normal 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
98
buffer_dyn/demo.c
Normal 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
54
buffer_dyn/test.c
Normal 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user