commit
0d3316af79
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "cow.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "cow/slice.hpp"
|
||||
|
||||
struct Cow : public _cow_util::Span<unsigned char> {
|
||||
struct Fake;
|
||||
Cow() = delete;
|
||||
|
||||
explicit Cow(size_t size);
|
||||
Cow(Cow&& m);
|
||||
virtual ~Cow();
|
||||
|
||||
virtual Fake clone() const;
|
||||
|
||||
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 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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// XXX: Deprecated function for now. It seems `size()` offers better codegen on LTO and non-LTO enabled builds.
|
||||
[[deprecated("size() is safer and offers better codegen.")]] inline size_t size_unsafe() const { return _cow_size_unsafe(get_raw()); }
|
||||
|
||||
static Cow from_raw(cow_t* owned);
|
||||
|
||||
virtual cow_t* raw() const;
|
||||
|
||||
private:
|
||||
struct _inner;
|
||||
Cow(cow_t* raw);
|
||||
|
||||
protected:
|
||||
Cow(const Cow& c);
|
||||
const std::shared_ptr<_inner> super;
|
||||
virtual cow_t* get_raw() const;
|
||||
|
||||
};
|
||||
|
||||
struct Cow::Fake : public Cow {
|
||||
Fake() = delete;
|
||||
Fake(const Cow& real);
|
||||
Fake(const Fake& copy);
|
||||
Fake(Fake&& move);
|
||||
~Fake();
|
||||
|
||||
Fake clone() const override;
|
||||
|
||||
static Fake from_real(const Cow& real);
|
||||
|
||||
inline cow_t* raw() const override { return fake; }
|
||||
|
||||
protected:
|
||||
cow_t* get_raw() const override;
|
||||
private:
|
||||
|
||||
cow_t* const fake;
|
||||
};
|
@ -0,0 +1,37 @@
|
||||
// `Area` is a copyable type that opaquely represents either `Cow` or `Cow::Fake`.
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <cow.hpp>
|
||||
|
||||
namespace _cow_util {
|
||||
struct Area {
|
||||
Area() = delete;
|
||||
|
||||
explicit Area(size_t sz);
|
||||
Area(const Area& area);
|
||||
Area(Area&& area);
|
||||
|
||||
Area(Cow&& move);
|
||||
explicit Area(const Cow& copy);
|
||||
|
||||
inline const Cow* operator->() const { return _area.get(); }
|
||||
inline Cow* operator->() { return _area.get(); }
|
||||
|
||||
inline const Cow* operator*() const { return _area.get(); }
|
||||
inline Cow* operator*() { return _area.get(); }
|
||||
|
||||
inline operator const Cow&() const { return *_area.get(); }
|
||||
inline operator Cow&() { return *_area.get(); }
|
||||
|
||||
inline bool is_clone() const { return dynamic_cast<Cow::Fake*>(_area.get()) != nullptr; }
|
||||
|
||||
inline cow_t* raw() const { return _area->raw(); }
|
||||
|
||||
~Area();
|
||||
private:
|
||||
const std::unique_ptr<Cow> _area;
|
||||
};
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
namespace _cow_util {
|
||||
/// A type that spans a sized region of memory
|
||||
template<typename T>
|
||||
struct Span {
|
||||
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 "index out of range";
|
||||
return ptr()[index];
|
||||
}
|
||||
inline const T& operator[](size_t index) const {
|
||||
if(index >= size()) throw "index out of range";
|
||||
return ptr()[index];
|
||||
}
|
||||
|
||||
inline T* operator&() { return &(*this)[0]; }
|
||||
inline const T* operator&() const {return &(*this)[0]; }
|
||||
|
||||
inline T* operator->() { return &(*this)[0]; }
|
||||
inline const T* operator->() const {return &(*this)[0]; }
|
||||
|
||||
inline T& operator*() { return (*this)[0]; }
|
||||
inline const T& operator*() const { return (*this)[0]; }
|
||||
|
||||
inline operator const T*() const { return &(*this)[0]; }
|
||||
inline operator T*() { return &(*this)[0]; }
|
||||
|
||||
template<typename U>
|
||||
inline U* area_as() requires(sizeof(T) % sizeof(U) == 0) { return (U*)area(); }
|
||||
template<typename U>
|
||||
inline const U* area_as() const requires(sizeof(T) % sizeof(U) == 0) { return (U*)area(); }
|
||||
|
||||
template<typename U>
|
||||
size_t size_as() const requires(sizeof(T) % sizeof(U) == 0) { return size_bytes() / sizeof(U); }
|
||||
|
||||
struct Slice;
|
||||
|
||||
inline operator Slice() { return Slice(ptr(), size()); }
|
||||
inline operator const Slice() const { return Slice(const_cast<T*>(ptr()), size()); }
|
||||
|
||||
inline bool bounds_ok(size_t start) const
|
||||
{
|
||||
return start < size();
|
||||
}
|
||||
inline bool bounds_ok(size_t start, size_t len) const
|
||||
{
|
||||
return (start + len) <= size() && bounds_ok(start);
|
||||
}
|
||||
|
||||
inline ssize_t wrap_len(ssize_t len) const
|
||||
{
|
||||
if(size() ==0 ) return 0;
|
||||
return len < 0 ? wrap_len(size() + len) : ((size_t)len) % size();
|
||||
}
|
||||
|
||||
/// Slice (absolute). Specify start and end.
|
||||
inline const Slice slice_abs(size_t start, size_t end) const { auto len = end -start; if(bounds_ok(start,len)) return Slice(const_cast<T*>(ptr()+start), len); else throw "Out of bounds slice"; }
|
||||
inline Slice slice_abs(size_t start, size_t end) { auto len = end -start; if(bounds_ok(start,len)) return Slice(ptr()+start, len); else throw "Out of bounds slice"; }
|
||||
|
||||
/// Slice (relative). Specify start and length.
|
||||
inline const Slice slice(size_t start, size_t len) const { if(bounds_ok(start,len)) return Slice(const_cast<T*>(ptr()+start), len); else throw "Out of bounds slice"; }
|
||||
inline Slice slice(size_t start, size_t len) { if(bounds_ok(start,len)) return Slice(ptr()+start, len); else throw "Out of bounds slice"; }
|
||||
|
||||
|
||||
/// Slice from 0. Specify length.
|
||||
inline Slice slice(size_t len) { return slice(0, len); }
|
||||
inline const Slice slice(size_t len) const { return slice(0, len); }//slice(start, size()-start); }
|
||||
|
||||
/// Slice total.
|
||||
inline Slice slice() { return slice(0, size()); }
|
||||
inline const Slice slice() const { return slice(0, size()); }
|
||||
|
||||
/// Slice wrapping. Specify start and end that may wrap over and/or under the span's size.
|
||||
inline Slice slice_wrap(ssize_t start, ssize_t end) { return slice_abs((size_t)wrap_len(start), (size_t)wrap_len(end)); }
|
||||
inline const Slice slice_wrap(ssize_t start, ssize_t end) const { return slice_abs((size_t)wrap_len(start), (size_t)wrap_len(end)); }
|
||||
inline Slice slice_wrap(ssize_t len) { return slice_abs((size_t)wrap_len(len)); }
|
||||
inline const Slice slice_wrap(ssize_t len) const { return slice_abs((size_t)wrap_len(len)); }
|
||||
|
||||
template<typename U>
|
||||
inline Span<U>::Slice reinterpret() { return typename Span<U>::Slice((U*)area(), size_bytes() / sizeof(U)); }
|
||||
template<typename U>
|
||||
inline Span<const U>::Slice reinterpret() const { return typename Span<const U>::Slice((const U*)area(), size_bytes() / sizeof(U)); }
|
||||
};
|
||||
|
||||
/// A slice of memory with a backing pointer and size.
|
||||
template<typename T>
|
||||
struct Span<T>::Slice : public Span<T> {
|
||||
inline Slice(T* ptr, size_t sz) : _area((void*)ptr), _size(sz){}
|
||||
inline Slice(const Span<T>& slice) : _area(const_cast<void*>(slice.area())), _size(slice.size()){}
|
||||
inline Slice(const Slice& copy) = default;
|
||||
inline Slice(Slice&& copy) : _area(copy._area), _size(copy._size){ *const_cast<size_t*>(©._size) = 0; }
|
||||
Slice() = delete;
|
||||
|
||||
inline const void* area() const override { return _area; }
|
||||
inline void* area() override { return _area; }
|
||||
inline size_t size() const override { return _size; }
|
||||
|
||||
private:
|
||||
void* const _area;
|
||||
const size_t _size;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
using Slice = Span<T>::Slice;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#include <cow/area.hpp>
|
||||
|
||||
namespace _cow_util {
|
||||
Area::Area(size_t sz) : _area(std::make_unique<Cow>(sz)){}
|
||||
Area::Area(const Area& copy) :
|
||||
_area(std::make_unique<Cow::Fake>(*copy._area.get())){}
|
||||
Area::Area(Area&& move) :
|
||||
_area(std::move(*const_cast<std::unique_ptr<Cow>*>(&move._area))){}
|
||||
Area::~Area(){}
|
||||
|
||||
Area::Area(Cow&& r) :
|
||||
_area(std::make_unique<Cow>(std::move(r))){}
|
||||
Area::Area(const Cow& r) :
|
||||
_area(std::make_unique<Cow::Fake>(r.clone())){}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
#include <cow.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "cow_t.h"
|
||||
|
||||
struct Cow::_inner {
|
||||
cow_t cow;
|
||||
|
||||
inline const cow_t* ptr() const { return &cow; }
|
||||
inline cow_t* ptr() { return &cow; }
|
||||
|
||||
~_inner();
|
||||
_inner(size_t sz);
|
||||
_inner(cow_t* ptr);
|
||||
|
||||
_inner(const _inner& copy) = delete;
|
||||
_inner(_inner&& move) = delete;
|
||||
_inner() = delete;
|
||||
};
|
||||
Cow::_inner::~_inner() {
|
||||
_cow_free_unboxed(ptr());
|
||||
}
|
||||
Cow::_inner::_inner(size_t sz) : cow(_cow_create_unboxed(sz)){}
|
||||
Cow::_inner::_inner(cow_t* ptr) : cow(*ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
Cow::Cow(size_t size) : super(std::make_shared<_inner>(size)){}
|
||||
Cow::Cow(cow_t* raw) : super(std::make_shared<_inner>(raw)){}
|
||||
|
||||
Cow::Cow(Cow&& m) : super(std::move(*const_cast<std::shared_ptr<_inner>*>(&m.super))){}
|
||||
Cow::Cow(const Cow& c) : super(c.super){}
|
||||
Cow::~Cow(){}
|
||||
|
||||
Cow Cow::from_raw(cow_t* owned) { if(cow_is_fake(owned)) throw "Trying to create real from fake raw"; else return Cow(owned); }
|
||||
|
||||
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_t* Cow::raw() const { return &super->cow; }
|
||||
|
||||
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<const Cow*>(©)){}
|
||||
Cow::Fake::Fake(Fake&& move) : Cow(std::move(move)), fake(move.fake)
|
||||
{
|
||||
*const_cast<cow_t**>(&move.fake) = nullptr;
|
||||
}
|
||||
Cow::Fake::~Fake() { if(fake) cow_free(fake); }
|
||||
|
||||
Cow::Fake Cow::Fake::Fake::from_real(const Cow& real) { return Fake(real); }
|
||||
|
||||
Cow::Fake Cow::Fake::clone() const { return Fake(*static_cast<const Fake*>(this)); }
|
||||
cow_t* Cow::Fake::get_raw() const { return fake; }
|
||||
|
||||
|
@ -0,0 +1,47 @@
|
||||
// Internal header for defining the `cow_t` concrete type to be used with the C and C++ APIs.
|
||||
#ifndef _COW_T_H
|
||||
#define _COW_T_H
|
||||
|
||||
#define internal __attribute__((visibility("internal")))
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define restrict __restrict__
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cow.h>
|
||||
|
||||
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.
|
||||
}; // cow_t, *cow
|
||||
|
||||
#ifdef __cplusplus
|
||||
static_assert
|
||||
#else
|
||||
_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;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#undef restruct
|
||||
#endif
|
||||
|
||||
#endif /* _COW_T_H */
|
@ -0,0 +1,220 @@
|
||||
#include <cow.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <cow/area.hpp>
|
||||
|
||||
using namespace _cow_util;
|
||||
|
||||
/// UTF8 multibyte 4.
|
||||
struct utf8_t {
|
||||
const static constexpr size_t MULTIBYTE =4;
|
||||
typedef std::array<char, MULTIBYTE+1> Unicode;
|
||||
|
||||
constexpr inline utf8_t() { data[0] = 0; }
|
||||
|
||||
template<size_t N>
|
||||
constexpr inline utf8_t(const char (&buffer)[N])
|
||||
{
|
||||
static_assert(N<=MULTIBYTE, "Expected multibyte 4");
|
||||
data = {
|
||||
buffer[0],
|
||||
buffer[1],
|
||||
buffer[2],
|
||||
buffer[3],
|
||||
0,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr inline utf8_t(char ascii) {
|
||||
data[0] = ascii;
|
||||
data[1] = 0;
|
||||
}
|
||||
|
||||
constexpr inline utf8_t(const char* c) {
|
||||
for(size_t i=0;i<MULTIBYTE && (data[i] = c[i]); i++) (void)0;
|
||||
}
|
||||
|
||||
constexpr inline utf8_t(const Unicode& data) : data(data) {}
|
||||
|
||||
|
||||
constexpr inline operator const char*() const { return &data[0]; }
|
||||
constexpr inline operator char*() { return &data[0]; }
|
||||
|
||||
constexpr inline const char* c_str() const { return &data[0]; }
|
||||
constexpr inline char* c_str() { return &data[0]; }
|
||||
|
||||
constexpr inline const char* operator&() const { return c_str(); }
|
||||
constexpr inline char* operator&() { return c_str(); }
|
||||
|
||||
Unicode data;
|
||||
};
|
||||
|
||||
|
||||
namespace Tiling {
|
||||
struct Map;
|
||||
|
||||
struct Pixel {
|
||||
utf8_t ch;
|
||||
struct {
|
||||
uint8_t r, g, b, a;
|
||||
} c8;
|
||||
};
|
||||
|
||||
enum class Direction {
|
||||
Up, Right, Down, Left
|
||||
};
|
||||
|
||||
// Quad-linked list to upper, right, lower, left segments.
|
||||
struct Segment {
|
||||
friend class Map;
|
||||
Segment(size_t id) : id(id) {}
|
||||
//Segment(Map& owner) : owner(owner){}
|
||||
|
||||
~Segment() {
|
||||
// Unset pointers of neighbours.
|
||||
if(above) above->below = nullptr;
|
||||
if(right) right->left = nullptr;
|
||||
if(below) below->above = nullptr;
|
||||
if(left) left->right = nullptr;
|
||||
}
|
||||
private:
|
||||
size_t id;
|
||||
|
||||
//TODO
|
||||
//Cow raw_tiles;
|
||||
//Cow taw_graphics;
|
||||
|
||||
// Links will be NULL if there is no loaded segment in their position.
|
||||
// The objects themselves live in Map's segment registry (seg_reg).
|
||||
Segment* above;
|
||||
Segment* right;
|
||||
Segment* below;
|
||||
Segment* left;
|
||||
};
|
||||
|
||||
struct Map {
|
||||
// Remove a registered Segment. Resetting its neighbours' links if needed.
|
||||
// The reference will be invalid after calling this function.
|
||||
void unregister_segment(Segment& segment)
|
||||
{
|
||||
if(segment.id<SIZE_MAX) {
|
||||
seg_reg.erase(seg_reg.begin()+ segment.id);
|
||||
} else throw "dead segment unregistered";
|
||||
segment.id = SIZE_MAX;
|
||||
}
|
||||
// Register a new Segment and return it. The Segment will not be connected to any other.
|
||||
Segment& register_segment()
|
||||
{
|
||||
//seg_reg.push_back(Segment(seg_reg.size()));
|
||||
seg_reg.emplace_back(seg_reg.size());
|
||||
auto& seg = seg_reg.back();
|
||||
return seg;
|
||||
}
|
||||
// Register a new Segment and then attach it to the referenced one at the position specified by `to`.
|
||||
Segment& register_segment(Segment& attach, Direction to)
|
||||
{
|
||||
auto& seg = register_segment();
|
||||
|
||||
switch(to) {
|
||||
case Direction::Up:
|
||||
attach.above = &seg; break;
|
||||
case Direction::Right:
|
||||
attach.right = &seg; break;
|
||||
case Direction::Down:
|
||||
attach.below = &seg; break;
|
||||
case Direction::Left:
|
||||
attach.left = &seg; break;
|
||||
}
|
||||
|
||||
return seg;
|
||||
}
|
||||
private:
|
||||
// All segments live here.
|
||||
// All segments have a lifetime lower than this object.
|
||||
std::vector<Segment> seg_reg;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T = unsigned char>
|
||||
void print_slice(Slice<T> memory)
|
||||
{
|
||||
printf("slice: { %p, %lu (%lu bytes) }\n", memory.area(), memory.size(), memory.size_bytes());
|
||||
|
||||
}
|
||||
|
||||
void write_fake(Cow& clone, const char* string)
|
||||
{
|
||||
strncpy(clone.area_as<char>(), string, clone.size_as<char>()-1);
|
||||
}
|
||||
|
||||
void read_fake(const Cow& clone)
|
||||
{
|
||||
printf("read_fake: %s\n", clone.area_as<char>());
|
||||
}
|
||||
|
||||
void moving_cow(Cow moved)
|
||||
{
|
||||
auto moved_clone = moved.reinterpret<char>();
|
||||
strncpy(&moved_clone, "Ummmm....", moved_clone.size());
|
||||
|
||||
read_fake(moved);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Cow _area(4000);
|
||||
|
||||
Area area = std::move(_area);
|
||||
write_fake(area, "Hello???");
|
||||
Area area2 = area;
|
||||
write_fake(area2, "Hi");
|
||||
Area area3 = std::move(area2);
|
||||
Area area4 = std::move(area);
|
||||
read_fake(area3);
|
||||
read_fake(area4);
|
||||
|
||||
printf("Is clone: a1: %d, a2: %d\n", area4.is_clone(), area3.is_clone());
|
||||
|
||||
utf8_t ch = "あ";
|
||||
utf8_t ch2('a');
|
||||
utf8_t ch3 = ch.c_str();
|
||||
utf8_t ch4 = ch3.data;
|
||||
utf8_t ch5 = ch4;
|
||||
(void)ch5;
|
||||
printf("Test: %s, %s, %s\n", (const char*)ch, ch2.c_str(), ch3.c_str());
|
||||
|
||||
Cow real(4096);
|
||||
memset(real, 0, real.size_bytes());
|
||||
|
||||
printf("Created real: ");
|
||||
print_slice(real);
|
||||
print_slice(real.slice_wrap(-20, -10));
|
||||
|
||||
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());
|
||||
|
||||
read_fake(clone);
|
||||
write_fake(clone, "hello fake!");
|
||||
read_fake(clone);
|
||||
read_fake(real);
|
||||
|
||||
printf("First byte of: real = %x, fake = %x\n", real[0], clone[0]);
|
||||
moving_cow(std::move(real)); //moving real is fine
|
||||
// <-- real is now dropped. But `clone` still holds Rc to _inner (cow_t).
|
||||
printf("First byte of: fake = %x\n", clone[0]);
|
||||
read_fake(clone); //clone still functions because of refcount on origin.
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in new issue