diff --git a/src/buffer.c b/src/buffer.c new file mode 100644 index 0000000..6dabdf3 --- /dev/null +++ b/src/buffer.c @@ -0,0 +1,103 @@ +/** + * buffer.c - buffer con acceso directo (útil para I/O) que mantiene + * mantiene puntero de lectura y de escritura. + */ +#include +#include +#include + +#include "buffer.h" + +inline void +buffer_reset(buffer *b) { + b->read = b->data; + b->write = b->data; +} + +void +buffer_init(buffer *b, const size_t n, uint8_t *data) { + b->data = data; + buffer_reset(b); + b->limit = b->data + n; +} + + +inline bool +buffer_can_write(buffer *b) { + return b->limit - b->write > 0; +} + +inline uint8_t * +buffer_write_ptr(buffer *b, size_t *nbyte) { + assert(b->write <= b->limit); + *nbyte = b->limit - b->write; + return b->write; +} + +inline bool +buffer_can_read(buffer *b) { + return b->write - b->read > 0; +} + +inline uint8_t * +buffer_read_ptr(buffer *b, size_t *nbyte) { + assert(b->read <= b->write); + *nbyte = b->write - b->read; + return b->read; +} + +inline void +buffer_write_adv(buffer *b, const ssize_t bytes) { + if(bytes > -1) { + b->write += (size_t) bytes; + assert(b->write <= b->limit); + } +} + +inline void +buffer_read_adv(buffer *b, const ssize_t bytes) { + if(bytes > -1) { + b->read += (size_t) bytes; + assert(b->read <= b->write); + + if(b->read == b->write) { + // compactacion poco costosa + buffer_compact(b); + } + } +} + +inline uint8_t +buffer_read(buffer *b) { + uint8_t ret; + if(buffer_can_read(b)) { + ret = *b->read; + buffer_read_adv(b, 1); + } else { + ret = 0; + } + return ret; +} + +inline void +buffer_write(buffer *b, uint8_t c) { + if(buffer_can_write(b)) { + *b->write = c; + buffer_write_adv(b, 1); + } +} + +void +buffer_compact(buffer *b) { + if(b->data == b->read) { + // nada por hacer + } else if(b->read == b->write) { + b->read = b->data; + b->write = b->data; + } else { + const size_t n = b->write - b->read; + memmove(b->data, b->read, n); + b->read = b->data; + b->write = b->data + n; + } +} diff --git a/src/buffer.h b/src/buffer.h new file mode 100644 index 0000000..18f852f --- /dev/null +++ b/src/buffer.h @@ -0,0 +1,149 @@ +#ifndef BUFFER_H_VelRDAxzvnuFmwEaR0ftrkIinkT +#define BUFFER_H_VelRDAxzvnuFmwEaR0ftrkIinkT + +#include +#include // size_t, ssize_t + +/** + * buffer.c - buffer con acceso directo (útil para I/O) que mantiene + * mantiene puntero de lectura y de escritura. + * + * + * Para esto se mantienen dos punteros, uno de lectura + * y otro de escritura, y se provee funciones para + * obtener puntero base y capacidad disponibles. + * + * R=0 + * ↓ + * +---+---+---+---+---+---+ + * | | | | | | | + * +---+---+---+---+---+---+ + * ↑ ↑ + * W=0 limit=6 + * + * Invariantes: + * R <= W <= limit + * + * Se quiere escribir en el bufer cuatro bytes. + * + * ptr + 0 <- buffer_write_ptr(b, &wbytes), wbytes=6 + * n = read(fd, ptr, wbytes) + * buffer_write_adv(b, n = 4) + * + * R=0 + * ↓ + * +---+---+---+---+---+---+ + * | H | O | L | A | | | + * +---+---+---+---+---+---+ + * ↑ ↑ + * W=4 limit=6 + * + * Quiero leer 3 del buffer + * ptr + 0 <- buffer_read_ptr, wbytes=4 + * buffer_read_adv(b, 3); + * + * R=3 + * ↓ + * +---+---+---+---+---+---+ + * | H | O | L | A | | | + * +---+---+---+---+---+---+ + * ↑ ↑ + * W=4 limit=6 + * + * Quiero escribir 2 bytes mas + * ptr + 4 <- buffer_write_ptr(b, &wbytes=2); + * buffer_write_adv(b, 2) + * + * R=3 + * ↓ + * +---+---+---+---+---+---+ + * | H | O | L | A | | M | + * +---+---+---+---+---+---+ + * ↑ + * limit=6 + * W=4 + * Compactación a demanda + * R=0 + * ↓ + * +---+---+---+---+---+---+ + * | A | | M | | | | + * +---+---+---+---+---+---+ + * ↑ ↑ + * W=3 limit=6 + * + * Leo los tres bytes, como R == W, se auto compacta. + * + * R=0 + * ↓ + * +---+---+---+---+---+---+ + * | | | | | | | + * +---+---+---+---+---+---+ + * ↑ ↑ + * W=0 limit=6 + */ +typedef struct buffer buffer; +struct buffer { + uint8_t *data; + + /** límite superior del buffer. inmutable */ + uint8_t *limit; + + /** puntero de lectura */ + uint8_t *read; + + /** puntero de escritura */ + uint8_t *write; +}; + +/** + * inicializa el buffer sin utilizar el heap + */ +void +buffer_init(buffer *b, const size_t n, uint8_t *data); + +/** + * Retorna un puntero donde se pueden escribir hasta `*nbytes`. + * Se debe notificar mediante la función `buffer_write_adv' + */ +uint8_t * +buffer_write_ptr(buffer *b, size_t *nbyte); +void +buffer_write_adv(buffer *b, const ssize_t bytes); + +uint8_t * +buffer_read_ptr(buffer *b, size_t *nbyte); +void +buffer_read_adv(buffer *b, const ssize_t bytes); + +/** + * obtiene un byte + */ +uint8_t +buffer_read(buffer *b); + +/** escribe un byte */ +void +buffer_write(buffer *b, uint8_t c); + +/** + * compacta el buffer + */ +void +buffer_compact(buffer *b); + +/** + * Reinicia todos los punteros + */ +void +buffer_reset(buffer *b); + +/** retorna true si hay bytes para leer del buffer */ +bool +buffer_can_read(buffer *b); + +/** retorna true si se pueden escribir bytes en el buffer */ +bool +buffer_can_write(buffer *b); + + +#endif diff --git a/src/buffer_test.c b/src/buffer_test.c new file mode 100644 index 0000000..a86ad83 --- /dev/null +++ b/src/buffer_test.c @@ -0,0 +1,116 @@ +#include +#include + +// asi se puede probar las funciones internas +#include "buffer.c" + +#define N(x) (sizeof(x)/sizeof((x)[0])) + + +START_TEST (test_buffer_misc) { + struct buffer buf; + buffer *b = &buf; + uint8_t direct_buff[6]; + buffer_init(&buf, N(direct_buff), direct_buff); + ck_assert_ptr_eq(&buf, b); + + ck_assert_int_eq(true, buffer_can_write(b)); + ck_assert_int_eq(false, buffer_can_read(b)); + + size_t wbytes = 0, rbytes = 0; + uint8_t *ptr = buffer_write_ptr(b, &wbytes); + ck_assert_uint_eq(6, wbytes); + // escribo 4 bytes + uint8_t first_write [] = { + 'H', 'O', 'L', 'A', + }; + memcpy(ptr, first_write, sizeof(first_write)); + buffer_write_adv(b, sizeof(first_write)); + + // quedan 2 libres para escribir + buffer_write_ptr(b, &wbytes); + ck_assert_uint_eq(2, wbytes); + + // tengo por leer + buffer_read_ptr(b, &rbytes); + ck_assert_uint_eq(4, rbytes); + + // leo 3 del buffer + ck_assert_uint_eq('H', buffer_read(b)); + ck_assert_uint_eq('O', buffer_read(b)); + ck_assert_uint_eq('L', buffer_read(b)); + + // queda 1 por leer + buffer_read_ptr(b, &rbytes); + ck_assert_uint_eq(1, rbytes); + + // quiero escribir..tendria que seguir habiendo 2 libres + ptr = buffer_write_ptr(b, &wbytes); + ck_assert_uint_eq(2, wbytes); + + uint8_t second_write [] = { + ' ', 'M', + }; + memcpy(ptr, second_write, sizeof(second_write)); + buffer_write_adv(b, sizeof(second_write)); + + ck_assert_int_eq(false, buffer_can_write(b)); + buffer_write_ptr(b, &wbytes); + ck_assert_uint_eq(0, wbytes); + + // tiene que haber 2 + 1 para leer + ptr = buffer_read_ptr(b, &rbytes); + ck_assert_uint_eq(3, rbytes); + ck_assert_ptr_ne(ptr, b->data); + + buffer_compact(b); + ck_assert_ptr_eq(b->data, buffer_read_ptr(b, &rbytes)); + ck_assert_uint_eq(3, rbytes); + ck_assert_ptr_eq(b->data + 3, buffer_write_ptr(b, &wbytes)); + ck_assert_uint_eq(3, wbytes); + + uint8_t third_write [] = { + 'U', 'N', 'D', + }; + memcpy(ptr, third_write, sizeof(third_write)); + buffer_write_adv(b, sizeof(third_write)); + + buffer_write_ptr(b, &wbytes); + ck_assert_uint_eq(0, wbytes); + ck_assert_ptr_eq(b->data, buffer_read_ptr(b, &rbytes)); + buffer_read_adv(b, rbytes); + buffer_read_ptr(b, &rbytes); + ck_assert_uint_eq(0, rbytes); + ck_assert_ptr_eq(b->data, buffer_write_ptr(b, &wbytes)); + ck_assert_uint_eq(6, wbytes); + + buffer_compact(b); + buffer_read_ptr(b, &rbytes); + ck_assert_uint_eq(0, rbytes); + buffer_write_ptr(b, &wbytes); + ck_assert_uint_eq(N(direct_buff), wbytes); + +} +END_TEST + +Suite * +suite(void) { + Suite *s = suite_create("buffer"); + TCase *tc = tcase_create("buffer"); + + tcase_add_test(tc, test_buffer_misc); + suite_add_tcase(s, tc); + + return s; +} + +int +main(void) { + SRunner *sr = srunner_create(suite()); + int number_failed; + + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +}