added C++ interface

error_handling
Avril 4 years ago
commit 0d3316af79
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -21,21 +21,29 @@ OPT_FLAGS?= $(addprefix -march=,$(TARGET_CPU)) -fgraphite -fopenmp -floop-parall
-floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \ -floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \
-fno-stack-check -fno-stack-check
CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors CXX_OPT_FLAGS?= $(OPT_FLAGS)
CFLAGS += $(COMMON_FLAGS) --std=gnu11 CFLAGS += $(COMMON_FLAGS) --std=gnu11
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++20 #-fno-exceptions CXXFLAGS += $(COMMON_FLAGS) --std=gnu++20 -felide-constructors
LDFLAGS += LDFLAGS +=
STRIP=strip STRIP=strip
RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS) ifneq ($(TARGET_SPEC_FLAGS),no)
RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS) RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS)
RELEASE_LDFLAGS?= -O3 -flto RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS)
RELEASE_LDFLAGS?= -O3 -flto
DEBUG_CFLAGS?= -O0 -g -DDEBUG DEBUG_CFLAGS?= -O0 -g
DEBUG_CXXFLAGS?=-O0 -g -DDEBUG DEBUG_CXXFLAGS?=-O0 -g
DEBUG_LDFLAGS?= DEBUG_LDFLAGS?=
endif
DEBUG_CFLAGS+=-DDEBUG
DEBUG_CXXFLAGS+=-DDEBUG
RELEASE_CFLAGS+=-DRELEASE
RELEASE_CXXFLAGS+=-DRELEASE
# Objects # Objects
@ -69,6 +77,11 @@ all: | clean
.PHONY: install .PHONY: install
.PHONY: uninstall .PHONY: uninstall
.PHONY: test
test:
@rm -f $(PROJECT)-cpp-test
@$(MAKE) $(PROJECT)-cpp-test
# Targets # Targets
dirs: dirs:
@ -130,3 +143,7 @@ uninstall:
-rm $(DESTDIR)$(PREFIX)/lib/lib$(PROJECT).{a,so} -rm $(DESTDIR)$(PREFIX)/lib/lib$(PROJECT).{a,so}
cd $(INCLUDE) && find . -type f | xargs -I {} rm "$(DESTDIR)$(PREFIX)/include/{}" cd $(INCLUDE) && find . -type f | xargs -I {} rm "$(DESTDIR)$(PREFIX)/include/{}"
-rmdir $(DESTDIR)$(PREFIX)/include/$(PROJECT) -rmdir $(DESTDIR)$(PREFIX)/include/$(PROJECT)
$(PROJECT)-cpp-test: lib$(PROJECT).a
g++ -O3 --std=gnu++20 -Iinclude/ -g -Wall -Wextra src/test/*.cpp -o $@ -l:$<
valgrind ./$@

@ -3,7 +3,16 @@ Automatic copy-on-write semantic memory slices for use in C (and C++)
# Usage # Usage
See `include/cow.h` for documentation on each function. See `include/cow.h` for documentation on each function.
Each function, macro, and type definition in the header will be prefixed with `cow_` or `COW_`.
## C API
Each function, macro, and type definition in the header will be prefixed with `cow_` or `COW_`. Internal non-prototpyed items use the namespace `_cow_` or `_COW_`.
### C++ wrapper API
The C++ interface defines the type `Cow`, a reference-counted wrapper over `cow_t` instances that supports cloning through its subtype, `Cow::Fake`, and automatically ensures the originally created `cow_t` is not destroyed until all its clones are, as well as the namespace `_cow_util` which contains memory accessor helpers `Span<T>` and `Slice<T>` (aka `Span<T>::Slice`).
There are also the following:
* `cow/area.hpp` (namespace `_cow_util`) - The `Area` type is a copy-constructable wrapper around *both* `Cow` and `Cow::Fake`, allowing for implicit cloning.
* `cow/slice.hpp` (namespace `_cow_util`) - Contains the definitions for `Span<T>` and `Slice<T>`. Included automatically by `cow.hpp` (*see above*).
## Building ## Building
Run `make` to build to build the `release` (optimised) target of the library. Run `make` to build to build the `release` (optimised) target of the library.
@ -16,8 +25,27 @@ It will create two files: `libcow-debug.a` and `libcow-debug.so`.
Each target compiles both a static and dynamic library. You may need to run `make clean` before switching build targets. Each target compiles both a static and dynamic library. You may need to run `make clean` before switching build targets.
To build both targets, run `make all`. To build both targets, run `make all`.
To disable default target-specific (e.g. optimisation) flags, set `TARGET_SPEC_FLAGS=no` when running `make`.
Run `sudo make install` to install the libraries (static and dynamic) and header files (C and C++).
Run `sudo make uninstall` to remove the libraries and header files.
By default, the install target is `/usr/local/`. Set the `PREFIX` variable when running `make install` / `make uninstall` to specify a different path.
### Full build and installation
```shell
$ make && sudo make install
```
Will build with the default optimisation configuration and install the following files/directories:
* /usr/local/lib/libcow.a
* /usr/local/lib/libcow.so
* /usr/local/include/cow.h
* /usr/local/include/cow.hpp
* /usr/local/include/cow/
### Notes ### Notes
* The `release` target specifies `-march=native` by default. This may be undesirable, if so, run `make MARCH="" release` instead. * The `release` target specifies `-march=native` by default. This may be undesirable, if so, set `TARGET_CPU=""` when running `make`.
* Many optimisation flags for the `release` configuration are specific to GCC (with graphite enabled by default), if builds on other compilers (or non-graphite enabled GCC builds) complain, either set the `OPT_FLAGS` env var or remove the problem flags from the Makefile. * Many optimisation flags for the `release` configuration are specific to GCC (with graphite enabled by default), if builds on other compilers (or non-graphite enabled GCC builds) complain, either set the `OPT_FLAGS` env var or remove the problem flags from the Makefile.
* `release` builds are stripped by default. run `make STRIP=: release` to prevent stripping. * `release` builds are stripped by default. run `make STRIP=: release` to prevent stripping.
* The targets are all built with `-fno-strict-aliasing`, but functions in the header file are still annotated with `restrict` needed. This is just to inform users that the function will assume the pointer is not aliased. (When included in C++, where `restrict` is not a keyword, we temporarily define it to be `__restrict__`, which is the GCC equivalent for C++). * The targets are all built with `-fno-strict-aliasing`, but functions in the header file are still annotated with `restrict` needed. This is just to inform users that the function will assume the pointer is not aliased. (When included in C++, where `restrict` is not a keyword, we temporarily define it to be `__restrict__`, which is the GCC equivalent for C++).

@ -9,7 +9,7 @@ extern "C" {
#include <stdlib.h> #include <stdlib.h>
// Copy-on-write mapped memory. // Copy-on-write mapped memory.
typedef struct cow cow_t; typedef struct cow_mapped_slice cow_t;
/// Create a new copy-on-write area of `size` bytes. /// Create a new copy-on-write area of `size` bytes.
/// Writes to this instance pointer (`cow_ptr()`) are written to the allocated memory. /// Writes to this instance pointer (`cow_ptr()`) are written to the allocated memory.
@ -25,6 +25,19 @@ int cow_is_fake(const cow_t* cow);
/// Get the size of this cow area. /// Get the size of this cow area.
size_t cow_size(const cow_t* cow); 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.
/// XXX: Deprecated function unpon seeing preferable codegen for using `cow_size()` over this. Use `cow_size()` instead.
static inline
#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))
__attribute__((deprecated("size() is safer and offers better codegen.")))
#endif
size_t cow_size_unsafe(const cow_t* v) { return _cow_size_unsafe(v); }
/// Get the `void*` pointer to the start of the area. /// Get the `void*` pointer to the start of the area.
#define cow_ptr(v) (*((void**)(v))) #define cow_ptr(v) (*((void**)(v)))
/// Get the `T*` pointer to the start of the area. /// Get the `T*` pointer to the start of the area.

@ -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*>(&copy._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())){}
}

@ -31,14 +31,8 @@
#define LASSERT(expr, msg) ASSERT(LIKELY(expr), "(unexpected) " msg) #define LASSERT(expr, msg) ASSERT(LIKELY(expr), "(unexpected) " msg)
#define UASSERT(expr, msg) ASSERT(UNLIKELY(expr), "(expected) " msg) #define UASSERT(expr, msg) ASSERT(UNLIKELY(expr), "(expected) " msg)
struct cow { // struct cow { ... }
void* origin; // ptr to mapped memory. This *MUST* be the first field and have an offset of 0. #include "cow_t.h"
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
_Static_assert(offsetof(cow_t, origin) == 0, "`cow_t.origin` must have an offset of 0.");
static __attribute__((noreturn)) __attribute__((noinline)) __attribute__((cold)) void die(const char* error) static __attribute__((noreturn)) __attribute__((noinline)) __attribute__((cold)) void die(const char* error)
{ {
@ -92,7 +86,7 @@ size_t cow_size(const cow_t* cow)
return cow->size; return cow->size;
} }
cow_t* cow_create(size_t size) inline internal cow_t _cow_create_unboxed(size_t size)
{ {
cow_t ret; cow_t ret;
ret.size = size; ret.size = size;
@ -101,15 +95,24 @@ cow_t* cow_create(size_t size)
if(ret.origin == MAP_FAILED) die("cow_create:mmap"); if(ret.origin == MAP_FAILED) die("cow_create:mmap");
TRACE("mapped new origin cow page of %lu size at %p (memfd %d)", size, ret.origin, ret.fd); TRACE("mapped new origin cow page of %lu size at %p (memfd %d)", size, ret.origin, ret.fd);
return box_value(ret); return ret;
}
cow_t* cow_create(size_t size)
{
return box_value(_cow_create_unboxed(size));
} }
void cow_free(cow_t* restrict cow) inline internal void _cow_free_unboxed(const cow_t* cow)
{ {
TRACE("unmapping %s cow of %lu size from %p (fd %d, real fd %d)", cow_is_fake(cow) ? "fake" : "and closing fd of origin", cow->size, cow->origin, cow->fd, cow_real_fd(cow)); TRACE("unmapping %s cow of %lu size from %p (fd %d, real fd %d)", cow_is_fake(cow) ? "fake" : "and closing fd of origin", cow->size, cow->origin, cow->fd, cow_real_fd(cow));
munmap(cow->origin, cow->size); munmap(cow->origin, cow->size);
if(!cow_is_fake(cow)) if(!cow_is_fake(cow))
close(cow->fd); close(cow->fd);
}
void cow_free(cow_t* restrict cow)
{
_cow_free_unboxed(cow);
free(cow); free(cow);
} }

@ -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*>(&copy)){}
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…
Cancel
Save