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