All-Inclusive-Puffer für C
Buffet ist eine getaggte Union mit 4 Modi.
// 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
Das Tag legt den Buffet-Modus fest:
OWN
Miteigentümer eines LadensSSO
eingebettetes Char-ArraySSV
Ansicht (Small String View) auf einem SSOVUE
nicht besitzende Ansicht aller DatenWenn EIGEN, zeigt Buffet.data auf einen zugewiesenen Heap-Speicher:
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
Unit-Tests sind zwar umfangreich, decken jedoch möglicherweise noch nicht alle Fälle ab.
Buffet zielt darauf ab, Speicherfehler, auch seitens des Benutzers, zu verhindern.
(Außer natürlich der Verlust des Umfangs und dergleichen.)
// (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...
Zu diesem Zweck können Operationen wie view() oder free() den Header des Stores überprüfen.
Bei einem Fehler wird der Vorgang abgebrochen und ein leeres Buffet zurückgegeben.
Prüfungen werden durch #define MEMCHECK
oder durch Erstellen mit aktiviert
MEMCHECK=1 make
Warnungen werden durch #define DEBUG
oder durch Erstellen aktiviert
DEBUG=1 make
Hinweis: Selbst bei Überprüfungen kann Aliasing fatale Folgen haben.
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...
Siehe Ausgabe von Unit-Tests und Warnungen von src/check.c .
make && make bench
(erfordert libbenchmark-dev )
NB: Die Bibliothek ist nicht sehr optimiert und die Bank vielleicht amateurhaft.
Auf einem schwachen Core i3:
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_new
bft_memcopy
bft_memview
bft_copy
bft_copyall
bft_view
bft_dup ( keinen Alias für Buffets verwenden , sondern diesen verwenden)
bft_append
bft_split
bft_splitstr
bft_join
bft_free
bft_cmp
bft_cap
bft_len
bft_data
bft_cstr
bft_export
bft_print
bft_dbg
Buffet bft_new (size_t cap)
Erstellen Sie ein neues leeres Buffet mit einer Mindestkapazitätsgrenze .
Buffet buf = bft_new ( 40 );
bft_dbg ( & buf );
// OWN 0 ""
Buffet bft_memcopy (const char *src, size_t len)
Erstellen Sie ein neues Buffet, indem Sie len Bytes aus src kopieren.
Buffet copy = bft_memcopy ( "Bonjour" , 3 );
// SSO 3 "Bon"
Buffet bft_memview (const char *src, size_t len)
Erstellen Sie ein neues Buffet mit der Anzeige von len Bytes aus src .
Sie erhalten ein Fenster in src ohne Kopie oder Zuweisung.
Hinweis: Sie sollten sich die Daten eines Buffets nicht direkt ansehen . Verwenden Sie view()
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)
Kopieren Sie len Bytes mit Offset von Buffet src in ein neues Buffet.
Buffet src = bft_memcopy ( "Bonjour" , 7 );
Buffet cpy = bft_copy ( & src , 3 , 4 );
// SSO 4 "jour"
Buffet bft_copyall (const Buffet *src)
Kopieren Sie alle Bytes von Buffet src in ein neues Buffet.
Buffet bft_view (Buffet *src, ptrdiff_t off, size_t len)
Länge der Bytes von Buffet src anzeigen, beginnend bei off .
Sie erhalten ein Fenster in src ohne Kopie oder Zuweisung.
Der interne Rückgabetyp hängt vom Quelltyp ab:
view(SSO) -> SSV
(refcounted)view(SSV) -> SSV
auf dem Ziel von srcview(OWN) -> OWN
(als Refcounted-Store-Miteigentümer)view(VUE) -> VUE
auf dem Ziel von srcWenn die Rückgabe OWN ist, wird der Zielspeicher auch nicht vorher freigegeben
#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)
Erstellen Sie eine flache Kopie von src .
Verwenden Sie dies, anstatt ein Buffet zu aliasen.
Buffet src = bft_memcopy ( "Hello" , 5 );
Buffet cpy = src ; // BAD
Buffet cpy = bft_dup ( & src ); // GOOD
bft_dbg ( & cpy );
// SSO 5 "Hello"
Bem.: Aliasing würde meistens funktionieren, würde aber die Refzählung durcheinander bringen (ohne Absturz, wenn der Store-Schutz aktiviert ist):
Buffet alias = sso ; //ok if sso was not viewed
Buffet alias = own ; //not refcounted
Buffet alias = vue ; //ok
void bft_free (Buffet *buf)
Verwirft buf .
Sicherheit:
#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)
Verkettet buf- und len -Bytes von src zum resultierenden dst .
Gibt die Gesamtlänge oder 0 im Fehlerfall zurück.
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)
Hängt len Bytes von src an dst an .
Gibt bei einem Fehler eine neue Länge oder 0 zurück.
Buffet buf = bft_memcopy ( "abc" , 3 );
size_t newlen = bft_append ( & buf , "def" , 3 );
bft_dbg ( & buf );
// SSO 6 "abcdef"
Hinweis: Gibt einen Fehler zurück, wenn buf Ansichten hat und von SSO zu OWN wechseln würde, um die Kapazität zu erhöhen, wodurch die Ansichten ungültig werden:
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
Um dies zu verhindern, geben Sie Ansichten frei, bevor Sie sie an ein kleines Buffet anhängen.
Buffet* bft_split (const char* src, size_t srclen, const char* sep, size_t seplen,
int *outcnt)
Teilt src entlang des Trennzeichens sep in eine Buffet Vue-Liste der Länge *outcnt
.
Da es aus Ansichten besteht, können Sie es ohne Leck free(list)
, vorausgesetzt, dass kein Element zum Eigentümer gemacht wurde, indem Sie es beispielsweise anhängen.
Buffet* bft_splitstr (const char *src, const char *sep, int *outcnt);
Bequeme Aufteilung mithilfe von Strlen intern.
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);
Verbindet die Liste mit dem Trennzeichen und trennt sie in ein neues 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)
Vergleichen Sie die Daten zweier Buffets mit memcmp
.
size_t bft_cap (Buffet *buf)
Aktuelle Kapazität abrufen.
size_t bft_len (Buffet *buf)`
Aktuelle Länge abrufen.
const char* bft_data (const Buffet *buf)`
Aktuellen Datenzeiger abrufen.
Um die Nullterminierung bei buf.len
sicherzustellen, verwenden Sie bft_cstr .
const char* bft_cstr (const Buffet *buf, bool *mustfree)
Aktuelle Daten als nullterminierte C-Zeichenfolge mit maximaler Länge buf.len
abrufen.
Bei Bedarf (wenn buf eine Ansicht ist) werden die Daten in einen neuen C-String kopiert, der freigegeben werden muss, wenn „mustfree“ festgelegt ist.
char* bft_export (const Buffet *buf)
Kopiert Daten bis buf.len
in einen neuen C-String, der freigegeben werden muss.
void bft_print (const Buffet *buf)`
Druckt Daten bis zu buf.len
.
void bft_dbg (Buffet *buf)
Gibt den Pufferstatus aus.
Buffet buf ;
bft_memcopy ( & buf , "foo" , 3 );
bft_dbg ( & buf );
// SSO 3 "foo"