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