buffer.c - buffer con acceso directo
Útil para I/O que requiere mantener puntero de lectura y de escritura.
This commit is contained in:
parent
a9b3a97798
commit
b9ce264b75
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue