diff --git a/include/cow.h b/include/cow.h index 6249fdb..fc9abe9 100644 --- a/include/cow.h +++ b/include/cow.h @@ -24,6 +24,14 @@ cow_t* cow_clone(const cow_t* cow); int cow_is_fake(const cow_t* cow); /// Get the size of this cow area. size_t cow_size(const cow_t* cow); +/// Get the size of this cow area by assuming layout. This should work assuming "cow_t.h"'s build assertions didn't fail and avoids an extra call. +#ifdef _COW_NO_ASSUME_ABI +#define cow_size_unsafe(v) cow_size(v) +#else +// XXX: This macro is *VERY* ABI sensitive. This shouldn't be used if the ABI has changed since the build of libcow's `cow_t.h` passed its static assertions in *both* the C and C++ implementations. +// The C++ API uses this by default for its `Cow::size()` function. +#define cow_size_unsafe(v) *(((size_t*)(v))+1) +#endif /// Get the `void*` pointer to the start of the area. #define cow_ptr(v) (*((void**)(v))) diff --git a/include/cow.hpp b/include/cow.hpp index 39588f2..53973e0 100644 --- a/include/cow.hpp +++ b/include/cow.hpp @@ -4,6 +4,7 @@ #include + struct Cow { struct Fake; Cow() = delete; @@ -16,13 +17,29 @@ struct Cow { inline void* area() { return cow_ptr(get_raw()); } inline const void* area() const { return cow_ptr_of(const void, get_raw()); } - inline size_t size() const { return cow_size(get_raw()); } + + /// Get the size of the mapped area. + /// + /// Note: This calls into `_inner`'s internals. To skip the call on non-LTO builds, use `size_unsafe()`. + size_t size() const; + /// Get the size by assuming the ABI layout of cow_t to be correct. Potentially faster but ABI sensitive. + /// This shouldn't be a problem if all build static assertions passed. + /// + /// Note: This behaviour can be diabled by building with `-DCOW_NO_ASSUME_ABI`. In this case, this function calls out to the C API to determine the size. + /// There is also likely no benefit using this over `size()` in LTO enabled builds. + inline size_t size_unsafe() const { return cow_size_unsafe(get_raw()); } inline unsigned char* as_bytes() { return (unsigned char*)area(); } inline const unsigned char* as_bytes() const { return (const unsigned char*)area(); } - unsigned char& operator[](size_t index); - const unsigned char& operator[](size_t index) const; + inline unsigned char& operator[](size_t index) { + if(index >= size()) throw "Size too large"; + return as_bytes()[index]; + } + inline const unsigned char& operator[](size_t index) const { + if(index >= size()) throw "Size too large"; + return as_bytes()[index]; + } static Cow from_raw(cow_t* owned); diff --git a/src/cow.cpp b/src/cow.cpp index f7faf80..41d5307 100644 --- a/src/cow.cpp +++ b/src/cow.cpp @@ -39,6 +39,8 @@ Cow Cow::from_raw(cow_t* owned) { if(cow_is_fake(owned)) throw "Trying to create Cow::Fake Cow::clone() const { return Fake::from_real(*this); } cow_t* Cow::get_raw() const { return super->ptr(); } +size_t Cow::size() const { return super->cow.size; } + Cow::Fake::Fake(const Cow& copy) : Cow(copy), fake(cow_clone(copy.super->ptr())){} Cow::Fake::Fake(const Fake& copy) : Cow(copy), fake(cow_clone(copy.fake)){}//Fake(*static_cast(©)){} Cow::Fake::Fake(Fake&& move) : Cow(std::move(move)), fake(move.fake) @@ -54,11 +56,3 @@ cow_t* Cow::Fake::get_raw() const { return fake; } // Operators -unsigned char& Cow::operator[](size_t index) { - if(index >= size()) throw "Size too large"; - return as_bytes()[index]; -} -const unsigned char& Cow::operator[](size_t index) const { - if(index >= size()) throw "Size too large"; - return as_bytes()[index]; -} diff --git a/src/cow_t.h b/src/cow_t.h index 2267c62..3719982 100644 --- a/src/cow_t.h +++ b/src/cow_t.h @@ -16,8 +16,8 @@ extern "C" { struct cow_mapped_slice { void* origin; // ptr to mapped memory. This *MUST* be the first field and have an offset of 0. + size_t size; // Should be at this offset. int fd; // Will be ORd with ~INT_MAX if it's a clone. Will be >0 if it's the original. - size_t size; }; // cow_t, *cow #ifdef __cplusplus @@ -27,6 +27,15 @@ _Static_assert #endif (offsetof(cow_t, origin) == 0, "`cow_t.origin` must have an offset of 0."); +#ifndef _COW_NO_ASSUME_ABI +#ifdef __cplusplus +static_assert +#else +_Static_assert +#endif + (offsetof(cow_t, size) == sizeof(void*), "`cow_t.size` should have an offset equal to `sizeof(void*)` or cow_size_unsafe() becomes UB."); +#endif + cow_t _cow_create_unboxed(size_t size) internal; void _cow_free_unboxed(const cow_t* cow) internal; diff --git a/src/test/main.cpp b/src/test/main.cpp index cb2a7a2..7f6ba64 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -1,9 +1,13 @@ #include +#include + int main() { Cow real(4096); + printf("Fast size: %lu, slow size: %lu\n", real.size_unsafe(), real.size()); + Cow::Fake clone = real; { Cow::Fake clone2 = clone;