Búfer todo incluido para C
Buffet es una unión etiquetada con 4 modos.
// Hard values show 64-bit
union Buffet {
struct ptr {
char * data
size_t len
size_t off : 62 , tag : 2 // tag = OWN|SSV|VUE
}
struct sso {
char data [ 22 ]
uint8_t refcnt
uint8_t len : 6 , tag : 2 // tag = SSO
}
}
sizeof ( Buffet ) == 24
La etiqueta establece el modo Buffet:
OWN
porción copropietaria de una tiendaSSO
SSV
(vista de cadena pequeña) en un SSOVUE
sobre cualquier datoSi es PROPIO, Buffet.data apunta a un almacén de montón asignado:
struct Store {
size_t cap // store capacity
size_t len // store length
uint32_t refcnt // number of views on store
uint32_t canary // invalidates store if modified
char data [] // buffer data, shared by owning views
}
#include "../buffet.h"
int main () {
// SHARED OWN =================
char large [] = "DATA STORE IS HEAP ALLOCATION." ;
Buffet own1 = bft_memcopy ( large , sizeof ( large ) - 1 );
// Now own1 owns a store housing a copy of `large`
bft_dbg ( & own1 );
//-> OWN 30 "DATA STORE ..."
// View "STORE" in own1 :
Buffet own2 = bft_view ( & own1 , 5 , 5 );
// Now own1 and own2 share the store, whose refcount is 2
bft_dbg ( & own2 );
//-> OWN 5 "STORE"
// SSO & SSV =================
char small [] = "SMALL STRING" ;
Buffet sso1 = bft_memcopy ( small , sizeof ( small ) - 1 );
bft_dbg ( & sso1 );
//-> SSO 12 "SMALL STRING"
// View "STRING" in sso1 :
Buffet ssv1 = bft_view ( & sso1 , 6 , 6 );
bft_dbg ( & ssv1 );
//-> SSV 6 "STRING"
// VUE =======================
char any [] = "SOME BYTES" ;
// View "BYTES" in `any` :
Buffet vue1 = bft_memview ( any + 5 , 5 );
bft_dbg ( & vue1 );
//-> VUE 5 "BYTES"
return 0 ;
}
make && make check
Si bien son extensas, es posible que las pruebas unitarias aún no cubran todos los casos.
Buffet tiene como objetivo prevenir fallos de memoria, incluso por parte del usuario.
(Excepto, por supuesto, perder el alcance y demás).
// (pseudo code)
// overflow
buf = new ( 8 )
append ( buf , large_str ) // Done
// invalid ref
buf = memcopy ( short_str ) // SSO
view = view ( buf )
append ( buf , large_str ) // would mutate SSO to OWN
// => abort & warn "Append would invalidate views on SSO"
// double-free
bft_free ( buf )
bft_free ( buf ) // OK
// use-after-free
bft_free ( buf )
append ( buf , "foo" ) // Done. Now buf is "foo".
// aliasing
alias = buf // should be `alias = bft_dup(buf)`
bft_free ( buf )
bft_free ( alias ) // OK. Possible warning "Bad canary. Double free ?"
// Etc...
Para ello, operaciones como view() o free() pueden comprobar el encabezado de la tienda.
Si es incorrecta, la operación se cancela y devuelve un buffet vacío.
Las comprobaciones se habilitan con #define MEMCHECK
o compilando con
MEMCHECK=1 make
Las advertencias se habilitan mediante #define DEBUG
o compilando con
DEBUG=1 make
NB: Incluso con controles, algunos alias pueden ser fatales.
own = memcopy ( large_str )
view = view ( own )
alias = view
bft_free ( view )
bft_free ( own ) // refcnt == 0, free(store) !
// alias now points into freed memory...
Consulte la salida de advertencias y pruebas unitarias de src/check.c .
make && make bench
(requiere libbenchmark-dev )
NB: La biblioteca no está muy optimizada y el banco puede ser amateur.
En un Core i3 débil:
MEMVIEW_cpp/8 0,609 ns MEMVIEW_buffet/8 6,36 ns MEMCOPY_c/8 16,7 ns MEMCOPY_buffet/8 11,9 ns MEMCOPY_c/32 15,3 ns MEMCOPY_buffet/32 26,3 ns MEMCOPY_c/128 16,8 ns MEMCOPY_buffet/128 29,8 ns MEMCOPY_c/512 24,9 ns MEMCOPY_buffet/512 39,3 ns MEMCOPY_c/2048 94,1 ns MEMCOPY_buffet/2048 109 ns MEMCOPY_c/8192 196 ns MEMCOPY_buffet/8192 282 ns APPEND_cpp/8/4 10,9 ns APPEND_buffet/8/4 16,3 ns APPEND_cpp/8/16 36,5 ns APPEND_buffet/8/16 30,2 ns APPEND_cpp/24/4 49,0 ns APPEND_buffet/24/4 30,1 ns APPEND_cpp/24/32 48,1 ns APPEND_buffet/24/32 28,8 ns SPLITJOIN_c 2782 ns SPLITJOIN_cpp 3317 ns SPLITJOIN_buffet 1397 ns
bft_nuevo
bft_memcopy
bft_memview
bft_copia
bft_copyall
bft_vista
bft_dup ( no utilices el alias buffets , usa esto)
bft_append
bft_split
bft_splitstr
bft_join
bft_libre
bft_cmp
bft_cap
bft_len
bft_data
bft_cstr
bft_exportación
bft_imprimir
bft_dbg
Buffet bft_new (size_t cap)
Cree un nuevo Buffet vacío con límite de capacidad mínima.
Buffet buf = bft_new ( 40 );
bft_dbg ( & buf );
// OWN 0 ""
Buffet bft_memcopy (const char *src, size_t len)
Cree un nuevo Buffet copiando len bytes de src .
Buffet copy = bft_memcopy ( "Bonjour" , 3 );
// SSO 3 "Bon"
Buffet bft_memview (const char *src, size_t len)
Cree un nuevo Buffet que visualice len bytes desde src .
Obtiene una ventana en src sin copia ni asignación.
NB: No deberías ver directamente los datos de un Buffet. Usar vista()
char src [] = "Eat Buffet!" ;
Buffet view = bft_memview ( src + 4 , 6 );
// VUE 6 "Buffet"
Buffet bft_copy (const Buffet *src, ptrdiff_t off, size_t len)
Copie len bytes en el desplazamiento de Buffet src en un nuevo Buffet.
Buffet src = bft_memcopy ( "Bonjour" , 7 );
Buffet cpy = bft_copy ( & src , 3 , 4 );
// SSO 4 "jour"
Buffet bft_copyall (const Buffet *src)
Copie todos los bytes de Buffet src en un nuevo Buffet.
Buffet bft_view (Buffet *src, ptrdiff_t off, size_t len)
Ver len bytes de Buffet src , comenzando en off .
Obtiene una ventana en src sin copia ni asignación.
El tipo interno de retorno depende del tipo de src :
view(SSO) -> SSV
(recontado)view(SSV) -> SSV
en el objetivo de srcview(OWN) -> OWN
(como copropietario de la tienda recontado)view(VUE) -> VUE
en el objetivo de srcSi la devolución es PROPIA, la tienda de destino no se liberará antes de ninguna de las dos
#include "../buffet.h"
int main () {
char text [] = "Bonjour monsieur Buddy. Already speaks french!" ;
// view sso
Buffet sso = bft_memcopy ( text , 16 ); // "Bonjour monsieur"
Buffet ssv = bft_view ( & sso , 0 , 7 );
bft_dbg ( & ssv );
// view ssv
Buffet Bon = bft_view ( & ssv , 0 , 3 );
bft_dbg ( & Bon );
// view own
Buffet own = bft_memcopy ( text , sizeof ( text ));
Buffet ownview = bft_view ( & own , 0 , 7 );
bft_dbg ( & ownview );
// detach view
bft_append ( & ownview , "!" , 1 );
// bft_free(&ownview);
bft_free ( & own ); // Done
// view vue
Buffet vue = bft_memview ( text + 8 , 8 ); // "Good"
Buffet mon = bft_view ( & vue , 0 , 3 );
bft_dbg ( & mon );
return 0 ;
}
$ cc view.c libbuffet.a -o view && ./view
SSV 7 data:"Bonjour"
SSV 3 data:"Bon"
OWN 7 data:"Bonjour"
VUE 3 data:"mon"
Buffet bft_dup (const Buffet *src)
Crea una copia superficial de src .
Utilice esto en lugar de asignar un alias a un Buffet.
Buffet src = bft_memcopy ( "Hello" , 5 );
Buffet cpy = src ; // BAD
Buffet cpy = bft_dup ( & src ); // GOOD
bft_dbg ( & cpy );
// SSO 5 "Hello"
Rem: el alias funcionaría principalmente, pero arruinaría el recuento (sin fallar si las protecciones de la tienda están habilitadas):
Buffet alias = sso ; //ok if sso was not viewed
Buffet alias = own ; //not refcounted
Buffet alias = vue ; //ok
void bft_free (Buffet *buf)
Descarta buf .
Seguridad:
#include "../buffet.h"
int main () {
char text [] = "Le grand orchestre de Patato Valdez" ;
Buffet own = bft_memcopy ( text , sizeof ( text ));
Buffet ref = bft_view ( & own , 9 , 9 ); // "orchestre"
bft_free ( & own ); // A bit soon but ok, --refcnt
bft_dbg ( & own ); // SSO 0 ""
bft_free ( & ref ); // Was last co-owner, store is released
Buffet sso = bft_memcopy ( text , 8 ); // "Le grand"
Buffet ref2 = bft_view ( & sso , 3 , 5 ); // "grand"
bft_free ( & sso ); // WARN line:328 bft_free: SSO has views on it
bft_free ( & ref2 );
bft_free ( & sso ); // OK now
bft_dbg ( & sso ); // SSO 0 ""
return 0 ;
}
$ valgrind --leak-check=full ./bin/ex/free
All heap blocks were freed -- no leaks are possible
size_t bft_cat (Buffet *dst, const Buffet *buf, const char *src, size_t len)
Concatena bytes buf y len de src en el dst resultante.
Devuelve la longitud total o 0 en caso de error.
Buffet buf = bft_memcopy ( "abc" , 3 );
Buffet dst ;
size_t totlen = bft_cat ( & dst , & buf , "def" , 3 );
bft_dbg ( & dst );
// SSO 6 "abcdef"
size_t bft_append (Buffet *dst, const char *src, size_t len)
Agrega len bytes de src a dst .
Devuelve una nueva longitud o 0 en caso de error.
Buffet buf = bft_memcopy ( "abc" , 3 );
size_t newlen = bft_append ( & buf , "def" , 3 );
bft_dbg ( & buf );
// SSO 6 "abcdef"
NB: devuelve un error si buf tiene vistas y mutaría de SSO a OWN para aumentar la capacidad, invalidando las vistas:
Buffet foo = bft_memcopy ( "short foo " , 10 );
Buffet view = bft_view ( & foo , 0 , 5 );
// would mutate to OWN :
size_t rc = bft_append ( & foo , "now too long for SSO" );
assert ( rc == 0 ); // meaning aborted
Para evitar esto, libere las vistas antes de agregarlas a un pequeño buffet.
Buffet* bft_split (const char* src, size_t srclen, const char* sep, size_t seplen,
int *outcnt)
Divide src a lo largo del separador sep en una lista de Buffet Vue de longitud *outcnt
.
Al estar formado por vistas, puede free(list)
sin fugas, siempre que ningún elemento se haya convertido en propietario, por ejemplo, agregándolo.
Buffet* bft_splitstr (const char *src, const char *sep, int *outcnt);
División conveniente usando strlen internamente.
int cnt ;
Buffet * parts = bft_splitstr ( "Split me" , " " , & cnt );
for ( int i = 0 ; i < cnt ; ++ i )
bft_print ( & parts [ i ]);
// VUE 5 "Split"
// VUE 2 "me"
free ( parts );
Buffet bft_join (Buffet *list, int cnt, const char* sep, size_t seplen);
Se une a la lista en el separador en un nuevo Buffet.
int cnt ;
Buffet * parts = bft_splitstr ( "Split me" , " " , & cnt );
Buffet back = bft_join ( parts , cnt , " " , 1 );
bft_dbg ( & back );
// SSO 8 'Split me'
int bft_cmp (const Buffet *a, const Buffet *b)
Compare los datos de dos buffets usando memcmp
.
size_t bft_cap (Buffet *buf)
Obtenga la capacidad actual.
size_t bft_len (Buffet *buf)`
Obtener la longitud actual.
const char* bft_data (const Buffet *buf)`
Obtenga el puntero de datos actual.
Para garantizar la terminación nula en buf.len
, utilice bft_cstr .
const char* bft_cstr (const Buffet *buf, bool *mustfree)
Obtenga los datos actuales como una cadena C terminada en nulo de longitud máxima buf.len
.
Si es necesario (cuando buf es una vista), los datos se copian en una nueva cadena C que debe liberarse si se establece mustfree .
char* bft_export (const Buffet *buf)
Copia datos hasta buf.len
en una nueva cadena C que debe liberarse.
void bft_print (const Buffet *buf)`
Imprime datos hasta buf.len
.
void bft_dbg (Buffet *buf)
Imprime el estado buf .
Buffet buf ;
bft_memcopy ( & buf , "foo" , 3 );
bft_dbg ( & buf );
// SSO 3 "foo"