buffer.c - buffer con acceso directo

Útil para I/O que requiere mantener puntero de lectura y de escritura.
This commit is contained in:
Juan F. Codagnone 2017-09-17 00:02:18 -03:00 committed by Santiago Lo Coco
parent a9b3a97798
commit b9ce264b75
3 changed files with 368 additions and 0 deletions

103
src/buffer.c Normal file
View File

@ -0,0 +1,103 @@
/**
* buffer.c - buffer con acceso directo (útil para I/O) que mantiene
* mantiene puntero de lectura y de escritura.
*/
#include <string.h>
#include <stdint.h>
#include <assert.h>
#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;
}
}

149
src/buffer.h Normal file
View File

@ -0,0 +1,149 @@
#ifndef BUFFER_H_VelRDAxzvnuFmwEaR0ftrkIinkT
#define BUFFER_H_VelRDAxzvnuFmwEaR0ftrkIinkT
#include <stdbool.h>
#include <unistd.h> // 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

116
src/buffer_test.c Normal file
View File

@ -0,0 +1,116 @@
#include <stdlib.h>
#include <check.h>
// 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;
}