المخزن المؤقت الشامل لـ 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
(عرض سلسلة صغيرة) على SSOVUE
عدم امتلاك عرض على أية بياناتإذا كان 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 )
ملاحظة: لم يتم تحسين lib كثيرًا وربما يكون المقعد هواة.
على Core i3 الضعيف:
MEMVIEW_cpp/8 0.609 نانو ثانية MEMVIEW_buffet/8 6.36 ن.س MEMCOPY_c/8 16.7 نانو ثانية MEMCOPY_buffet/8 11.9 ن.س MEMCOPY_c/32 15.3 نانو ثانية MEMCOPY_buffet/32 26.3 ن.س MEMCOPY_c/128 16.8 نانو ثانية MEMCOPY_buffet/128 29.8 ns MEMCOPY_c/512 24.9 نانو ثانية MEMCOPY_buffet/512 39.3 ن.س MEMCOPY_c/2048 94.1 نانو ثانية MEMCOPY_buffet/2048 109 ن.س MEMCOPY_c/8192 196 نانو ثانية MEMCOPY_buffet/8192 282 ns APPEND_cpp/8/4 10.9 نانو ثانية APPEND_buffet/8/4 16.3 ن.س APPEND_cpp/8/16 36.5 نانو ثانية APPEND_buffet/8/16 30.2 ن.س APPEND_cpp/24/4 49.0 نانو ثانية APPEND_buffet/24/4 30.1 ن.س APPEND_cpp/24/32 48.1 نانو ثانية APPEND_buffet/24/32 28.8 ن.س SPLITJOIN_c 2782 نانو ثانية SPLITJOIN_cpp 3317 نانو ثانية SPLITJOIN_بوفيه 1397 ن.س
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)
قم بإنشاء بوفيه جديد عن طريق نسخ len bytes من src .
Buffet copy = bft_memcopy ( "Bonjour" , 3 );
// SSO 3 "Bon"
Buffet bft_memview (const char *src, size_t len)
قم بإنشاء بوفيه جديد لعرض وحدات البايت من src .
تحصل على نافذة في src بدون نسخ أو تخصيص.
ملاحظة: لا ينبغي عليك الاطلاع مباشرة على بيانات البوفيه. استخدم العرض ()
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)
انسخ بايتات len عند الإزاحة من Buffet src إلى 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)
عرض وحدات بايت len من Buffet src ، بدءًا من إيقاف التشغيل .
تحصل على نافذة في src بدون نسخ أو تخصيص.
يعتمد نوع الإرجاع الداخلي على نوع src :
view(SSO) -> SSV
(إعادة العد)view(SSV) -> SSV
على هدف srcview(OWN) -> OWN
(كمالك مشارك للمتجر المعاد حسابه)view(VUE) -> VUE
على هدف srcإذا كان الإرجاع مملوكًا، فلن يتم إصدار المتجر المستهدف قبل أي منهما
#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)
يسلسل buf و len بايت من src إلى 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)
إلحاق وحدات بايت len من src إلى dst .
إرجاع طول جديد أو 0 عند الخطأ.
Buffet buf = bft_memcopy ( "abc" , 3 );
size_t newlen = bft_append ( & buf , "def" , 3 );
bft_dbg ( & buf );
// SSO 6 "abcdef"
ملحوظة: يُرجع الفشل إذا كان 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)
يقسم src على طول الفاصل sep إلى قائمة Buffet Vue بطول *outcnt
.
نظرًا لكونك مكونًا من طرق عرض، يمكنك free(list)
دون تسرب بشرط عدم جعل أي عنصر مالكًا عن طريق الإلحاق به على سبيل المثال.
Buffet* bft_splitstr (const char *src, const char *sep, int *outcnt);
تقسيم مريح باستخدام سترلين داخليًا.
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);
انضم إلى القائمة الموجودة على الفاصل في بوفيه جديد.
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
، استخدم bft_cstr .
const char* bft_cstr (const Buffet *buf, bool *mustfree)
احصل على البيانات الحالية كسلسلة C منتهية بقيمة خالية وأقصى طول buf.len
.
إذا لزم الأمر (عندما يكون buf عرضًا)، فسيتم نسخ البيانات إلى سلسلة C جديدة يجب تحريرها إذا تم تعيين mustfree .
char* bft_export (const Buffet *buf)
نسخ البيانات حتى buf.len
إلى سلسلة C جديدة يجب تحريرها.
void bft_print (const Buffet *buf)`
طباعة البيانات حتى buf.len
.
void bft_dbg (Buffet *buf)
طباعة حالة buf .
Buffet buf ;
bft_memcopy ( & buf , "foo" , 3 );
bft_dbg ( & buf );
// SSO 3 "foo"