diff --git a/include/cow.hpp b/include/cow.hpp index 70c6e15..ab1364e 100644 --- a/include/cow.hpp +++ b/include/cow.hpp @@ -4,8 +4,9 @@ #include +#include "slice.hpp" -struct Cow { +struct Cow : public Slice { struct Fake; Cow() = delete; @@ -15,13 +16,13 @@ struct Cow { virtual Fake clone() const; - inline void* area() { return cow_ptr(get_raw()); } - inline const void* area() const { return cow_ptr_of(const void, get_raw()); } + inline void* area() override { return cow_ptr(get_raw()); } + inline const void* area() const override { return cow_ptr_of(const void, 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; + size_t size() const override; /// 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. /// @@ -31,18 +32,6 @@ struct Cow { /// XXX: Deprecated function for now. It seems `size()` offers better codegen on LTO and non-LTO enabled builds. [[deprecated]] 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(); } - - 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); private: @@ -56,7 +45,7 @@ struct Cow { }; -struct Cow::Fake : Cow { +struct Cow::Fake : public Cow { Fake() = delete; Fake(const Cow& real); Fake(const Fake& copy); diff --git a/include/slice.hpp b/include/slice.hpp new file mode 100644 index 0000000..29f343e --- /dev/null +++ b/include/slice.hpp @@ -0,0 +1,33 @@ +#pragma once + +template +struct Slice { + virtual const void* area() const =0; + virtual void* area() = 0; + virtual size_t size() const = 0; + + inline T* ptr() { return (T*)area(); } + inline const T* ptr() const { return (const T*)area(); } + + inline size_t size_bytes() const { return size() * sizeof(T); } + + inline unsigned char* as_bytes() { return (unsigned char*)area(); } + inline const unsigned char* as_bytes() const { return (const unsigned char*)area(); } + + inline T& operator[](size_t index) { + if(index >= size()) throw "Size too large"; + return ptr()[index]; + } + inline const T& operator[](size_t index) const { + if(index >= size()) throw "Size too large"; + return ptr()[index]; + } + + template + inline U* area_as() requires(sizeof(T) % sizeof(U) == 0) { return (U*)area(); } + template + inline const U* area_as() const requires(sizeof(T) % sizeof(U) == 0) { return (U*)area(); } + + template + size_t size_as() const requires(sizeof(T) % sizeof(U) == 0) { return size_bytes() / sizeof(U); } +}; diff --git a/src/test/main.cpp b/src/test/main.cpp index 6878d99..f40771e 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -1,18 +1,34 @@ #include #include +#include + +void write_fake(Cow& clone, const char* string) +{ + strncpy(clone.area_as(), string, clone.size_as()-1); +} + +void read_fake(const Cow& clone) +{ + printf("read_fake: %s\n", clone.area_as()); +} int main() { Cow real(4096); + write_fake(real, "Hello world"); + read_fake(real); + + Cow::Fake clone = real; + printf("Fake size: %lu\n", clone.size()); + printf("Fake ptr: %p\n", clone.area()); - printf("Fast size: %lu, slow size: %lu\n", real.size_unsafe(), real.size()); + read_fake(clone); + write_fake(clone, "hello fake!"); + read_fake(clone); + read_fake(real); - Cow::Fake clone = real; - { - Cow::Fake clone2 = clone; - } - if(0) cow_size_unsafe(NULL); + printf("First byte of: real = %x, fake = %x\n", real[0], clone[0]); return 0; }