C용 모든 것을 포함하는 버퍼
뷔페는 4가지 모드가 있는 태그 조합입니다.
// 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
태그는 뷔페 모드를 설정합니다.
OWN
공동 소유 매장 일부SSO
내장 문자 배열SSV
(작은 문자열 보기) 보기VUE
비소유 보기OWN인 경우 Buffet.data는 할당된 힙 저장소를 가리킵니다.
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
광범위하기는 하지만 단위 테스트가 아직 모든 경우를 다루지는 않을 수 있습니다.
뷔페는 사용자를 포함한 메모리 오류를 방지하는 것을 목표로 합니다.
(물론 범위 상실 등은 제외합니다.)
// (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...
이를 위해 view() 또는 free() 와 같은 작업은 저장소의 헤더를 확인할 수 있습니다.
잘못된 경우 작업이 중단되고 빈 뷔페가 반환됩니다.
검사는 #define MEMCHECK
또는 다음을 사용하여 활성화됩니다.
MEMCHECK=1 make
#define DEBUG
또는 다음을 사용하여 빌드하면 경고가 활성화됩니다.
DEBUG=1 make
주의: 검사를 하더라도 일부 앨리어싱은 치명적일 수 있습니다.
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...
src/check.c 단위 테스트 및 경고 출력을 참조하세요.
make && make bench
( libbenchmark-dev 필요)
NB: lib는 그다지 최적화되지 않았고 벤치는 아마 아마추어 수준일 것입니다.
약한 Core i3의 경우:
MEMVIEW_cpp/8 0.609ns MEMVIEW_buffet/8 6.36ns MEMCOPY_c/8 16.7ns MEMCOPY_buffet/8 11.9ns MEMCOPY_c/32 15.3ns MEMCOPY_buffet/32 26.3ns MEMCOPY_c/128 16.8ns MEMCOPY_buffet/128 29.8ns MEMCOPY_c/512 24.9ns MEMCOPY_buffet/512 39.3ns MEMCOPY_c/2048 94.1ns MEMCOPY_buffet/2048 109ns MEMCOPY_c/8192 196ns MEMCOPY_buffet/8192 282ns APPEND_cpp/8/4 10.9ns APPEND_buffet/8/4 16.3ns APPEND_cpp/8/16 36.5ns APPEND_buffet/8/16 30.2ns APPEND_cpp/24/4 49.0ns APPEND_buffet/24/4 30.1ns APPEND_cpp/24/32 48.1ns APPEND_buffet/24/32 28.8ns SPLITJOIN_c 2782ns SPLITJOIN_cpp 3317ns SPLITJOIN_buffet 1397ns
bft_new
bft_memcopy
bft_memview
bft_copy
bft_copyall
bft_view
bft_dup( 뷔페에 별칭을 지정하지 말고 이것을 사용하세요)
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)
최소 용량 한도 의 빈 뷔페를 새로 만듭니다.
Buffet buf = bft_new ( 40 );
bft_dbg ( & buf );
// OWN 0 ""
Buffet bft_memcopy (const char *src, size_t len)
src 에서 len 바이트를 복사하여 새 Buffet을 만듭니다.
Buffet copy = bft_memcopy ( "Bonjour" , 3 );
// SSO 3 "Bon"
Buffet bft_memview (const char *src, size_t len)
src 에서 len 바이트를 보는 새로운 Buffet을 생성합니다.
복사나 할당 없이 src 에 창을 가져옵니다.
주의: 뷔페 데이터를 직접 memview해서는 안 됩니다. 보기() 사용
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)
Buffet src 의 오프셋 에서 len 바이트를 새 Buffet으로 복사합니다.
Buffet src = bft_memcopy ( "Bonjour" , 7 );
Buffet cpy = bft_copy ( & src , 3 , 4 );
// SSO 4 "jour"
Buffet bft_copyall (const Buffet *src)
Buffet src 의 모든 바이트를 새 Buffet에 복사합니다.
Buffet bft_view (Buffet *src, ptrdiff_t off, size_t len)
off 에서 시작하여 Buffet src 의 len 바이트를 봅니다.
복사나 할당 없이 src 에 창을 가져옵니다.
반환 내부 유형은 src 유형에 따라 다릅니다.
view(SSO) -> SSV
(재검토됨)view(SSV) -> SSV
src 대상의 SSVview(OWN) -> OWN
(재계산된 매장 공동 소유자로서)view(VUE) -> VUE
src 의 대상에 대한 VUE반품이 OWN인 경우 대상 매장은 이전에 출시되지 않습니다.
#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)
src 의 얕은 복사본을 만듭니다.
뷔페에 별칭을 지정하는 대신 이를 사용하세요.
Buffet src = bft_memcopy ( "Hello" , 5 );
Buffet cpy = src ; // BAD
Buffet cpy = bft_dup ( & src ); // GOOD
bft_dbg ( & cpy );
// SSO 5 "Hello"
Rem: 앨리어싱은 대부분 작동하지만 참조 계산을 엉망으로 만듭니다(저장소 보호가 활성화된 경우 충돌 없음).
Buffet alias = sso ; //ok if sso was not viewed
Buffet alias = own ; //not refcounted
Buffet alias = vue ; //ok
void bft_free (Buffet *buf)
buf 를 삭제합니다.
보안:
#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)
src 의 buf 및 len 바이트를 결과 dst 로 연결합니다.
전체 길이를 반환하거나 오류가 발생하면 0을 반환합니다.
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)
src 에서 dst 로 len 바이트를 추가합니다.
새로운 길이를 반환하거나 오류가 발생하면 0을 반환합니다.
Buffet buf = bft_memcopy ( "abc" , 3 );
size_t newlen = bft_append ( & buf , "def" , 3 );
bft_dbg ( & buf );
// SSO 6 "abcdef"
NB: buf 에 보기가 있고 용량을 늘리기 위해 SSO에서 OWN으로 변경하면 실패를 반환하고 보기를 무효화합니다.
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
이를 방지하려면 작은 뷔페에 추가하기 전에 뷰를 해제하세요.
Buffet* bft_split (const char* src, size_t srclen, const char* sep, size_t seplen,
int *outcnt)
구분 기호 sep를 따라 src를 *outcnt
길이의 Buffet Vue 목록으로 분할합니다.
뷰로 구성되어 있으므로 요소를 추가하여 소유자로 만들지 않은 경우 누수 없이 free(list)
할 수 있습니다.
Buffet* bft_splitstr (const char *src, const char *sep, int *outcnt);
내부적으로 strlen을 사용하여 편리하게 분할합니다 .
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);
구분 기호 sep 의 목록을 새 뷔페에 조인합니다.
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)
memcmp
사용하여 두 뷔페의 데이터를 비교합니다.
size_t bft_cap (Buffet *buf)
현재 용량을 가져옵니다.
size_t bft_len (Buffet *buf)`
현재 길이를 가져옵니다.
const char* bft_data (const Buffet *buf)`
현재 데이터 포인터를 가져옵니다.
buf.len
에서 null 종료를 보장하려면 bft_cstr 을 사용하세요.
const char* bft_cstr (const Buffet *buf, bool *mustfree)
현재 데이터를 최대 길이 buf.len
의 null 종료 C 문자열로 가져옵니다.
필요한 경우( buf가 뷰인 경우) mustfree가 설정된 경우 해제되어야 하는 새 C 문자열에 데이터가 복사됩니다.
char* bft_export (const Buffet *buf)
buf.len
까지의 데이터를 해제해야 하는 새 C 문자열에 복사합니다.
void bft_print (const Buffet *buf)`
buf.len
까지 데이터를 인쇄합니다.
void bft_dbg (Buffet *buf)
버퍼 상태를 인쇄합니다.
Buffet buf ;
bft_memcopy ( & buf , "foo" , 3 );
bft_dbg ( & buf );
// SSO 3 "foo"