Compare commits

...

13 Commits

Author SHA1 Message Date
Avril 2458a2b969
Added skeleton for new RNG type: Lorenz Attractor.
1 year ago
Avril 295da7df02
Started branch "progress", for working on timed-out progress indicator. Current build succeeds but does not use progress indicators at all.
1 year ago
Avril dd54a4823d
Started progress bar
1 year ago
Avril 0a0fb4555a
Improved test.sh
1 year ago
Avril ef7f06e8e0
Makefile: Added better control over used intrinsics and target arch
1 year ago
Avril 16ae82f05d
Changed `debug` target: Now uses -Og instead of -O0.
1 year ago
Avril 454378acfe
Added prototype for `unmap_and_close_s()` for non-inlining?
1 year ago
Avril ecf72a7526
Improved PGO profiling
1 year ago
Avril 3e3b5d1cf6
Fixed issue with `constexpr` debug statements.
1 year ago
Avril f648646317
fix make failing on clean builds
3 years ago
Avril b882bfa8ea
added _FS_SPILL_BUFFER=MAP
3 years ago
Avril 9270fe49ab
lazy init of spill buffer
3 years ago
Avril d1c5484ada
memusage on arm
3 years ago

@ -6,30 +6,51 @@ INCLUDE = include
PROJECT=shuffle3
# Link to these libraries dynamicalls
SHARED_LIBS=fmt
# Link to these libraries statically
STATIC_LIBS=
# Currently supported:
# _FS_SPILL_BUFFER: Use file backed buffer instead of memory backed one for unshuffling. See `shuffle3.h`.
# Setting the value to `DYN` enables the dynamic buffer, setting it to `MAP` enabled memory-mapped buffer. `MAP` is usually the fastest of the 3 modes.
# DEBUG: Pretend we're building a debug release even though we're not. Will enable additional output messages and may interfere with some optimisations
FEATURE_FLAGS?=
COMMON_FLAGS = $(addprefix -D,$(FEATURE_FLAGS)) -Wall -pedantic $(addprefix -I,$(INCLUDE)) -fno-strict-aliasing
COMMON_FLAGS+= $(addprefix -D,$(FEATURE_FLAGS)) -Wall -Wstrict-aliasing $(addprefix -I,$(INCLUDE)) -fno-strict-aliasing
ARCH?=native
OPT_FLAGS?= -march=native -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
INTRINSICS=avx sse3
OPT_FLAGS?=-fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
-floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \
-fno-stack-check
OPT_FLAGS+=$(addprefix -m,$(INTRINSICS))
ifneq ($(ARCH),)
OPT_FLAGS+=$(addprefix -march=,$(ARCH))
endif
CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors
CFLAGS += $(COMMON_FLAGS) --std=gnu11
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++20 -fno-exceptions
LDFLAGS +=
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++23 -fno-exceptions
# XXX: We might need exceptions soon, for OOP usage, because we try multiple approaches from most efficient to least.
LDFLAGS += $(addsuffix .a,$(addprefix -l:lib,$(STATIC_LIBS))) $(addprefix -l,$(SHARED_LIBS))
STRIP=strip
RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS)
RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS)
RELEASE_LDFLAGS?= -O3 -flto
RELEASE_COMMON_FLAGS?= -DNOTRACE
RELEASE_COMMON_FLAGS+= -DRELEASE
RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS) $(RELEASE_COMMON_FLAGS)
RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS) $(RELEASE_COMMON_FLAGS)
RELEASE_LDFLAGS?= -Wl,-O3 -Wl,-flto
DEBUG_CFLAGS?= -O0 -g -DDEBUG
DEBUG_CFLAGS?= -Og -g -DDEBUG
DEBUG_CXXFLAGS?= $(DEBUG_CFLAGS)
DEBUG_LDFLAGS?=
@ -46,6 +67,8 @@ PGO_OBJ_CXX = $(addprefix obj/pgo/cxx/,$(SRC_CXX:.cpp=.o))
PGO_OBJ = $(PGO_OBJ_C) $(PGO_OBJ_CXX)
PGO_ITERATIONS=5
PGO_BLOCKS={1..4}
PGO_SIZE={1024..4096}
PGO_SET_LOC?=/tmp/$(PROJECT)-pgo
PGO_FLAGS = -fprofile-generate
@ -103,21 +126,29 @@ pgo-generate: $(PGO_OBJ)
$(CXX) $^ $(CXXFLAGS) $(PGO_FLAGS) -o $@ $(LDFLAGS) $(PGO_LDFLAGS)
pgo-reset:
find ./obj/pgo -name \*.gcda -exec rm {} +
-find ./obj/pgo -name \*.gcda -exec rm {} +
pgo-profile: | pgo-reset pgo-generate
mkdir -p $(PGO_SET_LOC)
for i in {1..$(PGO_ITERATIONS)}; do \
dd if=/dev/urandom of=$(PGO_SET_LOC)/small bs=1024 count=1 >> /dev/null 2>&1; \
printf "Iteration $$i / $(PGO_ITERATIONS)\r"; \
./pgo-generate -s $(PGO_SET_LOC)/small >>/dev/null; \
./pgo-generate -u $(PGO_SET_LOC)/small >>/dev/null; \
./pgo-generate -h >> /dev/null; \
FCNT=1 ./test.sh ./pgo-generate >>/dev/null; \
FCNT=2 ./test.sh ./pgo-generate >>/dev/null; \
FCNT=3 ./test.sh ./pgo-generate >>/dev/null; \
FCNT=4 ./test.sh ./pgo-generate >>/dev/null; \
block=$$(rng --of $(PGO_SIZE)); \
block_count=$$(rng --of $(PGO_BLOCKS)); \
dd if=/dev/urandom of=$(PGO_SET_LOC)/small bs=$$block count=$$block_count >> /dev/null 2>&1; \
printf "Iteration $$i / $(PGO_ITERATIONS) ($$block * $$block_count)\r"; \
( echo ">> $$i" >&2; \
echo ">> $$i"; \
./pgo-generate -s $(PGO_SET_LOC)/small && \
./pgo-generate -u $(PGO_SET_LOC)/small && \
./pgo-generate -h && \
FCNT=1 ./test.sh ./pgo-generate && \
FCNT=2 ./test.sh ./pgo-generate && \
FCNT=3 ./test.sh ./pgo-generate && \
FCNT=4 ./test.sh ./pgo-generate && \
: \
) >>$(PGO_SET_LOC)/stdout.log 2>>$(PGO_SET_LOC)/stderr.log || \exit $$?; \
done
$(shell command -v bat >/dev/null && echo "bat --pager=none" || echo cat) $(PGO_SET_LOC)/stdout.log; \
$(shell command -v bat >/dev/null && echo "bat --pager=none" || echo cat) $(PGO_SET_LOC)/stderr.log >&2
rm -rf $(PGO_SET_LOC)
rm pgo-generate

@ -85,6 +85,7 @@ There are some build-time flags you can switch while building by appending to th
| `DEBUG` | Pretend we're building a debug release even though we're not. |
| `_FS_SPILL_BUFFER` | Spill buffers into a file if they grow over a threshold. Can cause massive slowdowns but prevent OOMs while unshuffling on systems with low available memory. See [shuffle3.h](./include/shuffle3.h) for more details |
| `_FS_SPILL_BUFFER=DYN` | Same as above except allocates memory dynamically. Might be faster. |
| `_FS_SPILL_BUFFER=MAP` | Same as above except it calls `fallocate()` and `mmap()` to prodive a buffer of the full size needed. Is usually the fastest of the options for `_FS_SPILL_BUFFER` and is preferrable if possible. |
## Gentoo ebuild

@ -1,6 +1,14 @@
#ifndef _DEBUG_H
#define _DEBUG_H
// If `TRACE` is defined, or if `NOTRACE` is defined, use simplified debug info
#if defined(TRACE) || !defined(NOTRACE)
#define D_TRACE
#define _D__FUNC __PRETTY_FUNCTION__
#else
#define _D__FUNC __func__
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -8,12 +16,14 @@ extern "C" {
struct debuginfo {
const char* file;
const char* function;
//const char* pfunction; //TODO: for `#if defined(TRACE)`, __PRETTY_FUNCTION__ goes here, __func__ goes above.
int line;
};
void _do_dprintf(struct debuginfo di, const char* fmt, ...);
#ifdef __cplusplus
#define _D_COMPTIME constexpr
extern "C++" {
#include <utility>
template<typename... Args>
@ -24,14 +34,15 @@ extern "C++" {
_do_dprintf(i, fmt, std::forward<Args>(args)...);
#endif
}
#define D_dprintf(fmt, ...) _real_dprintf(__FILE__, __func__, __LINE__, fmt __VA_OPT__(,) __VA_ARGS__)
#define D_dprintf(fmt, ...) do { if consteval { (void)0; } else { _real_dprintf(__FILE__, _D__FUNC, __LINE__, fmt __VA_OPT__(,) __VA_ARGS__); } } while(0)
}
#else
#define _D_COMPTIME
#ifdef DEBUG
#define D_dprintf(fmt, ...) _do_dprintf( (struct debuginfo){.file = __FILE__, .function = __func__, .line = __LINE__}, fmt __VA_OPT__(,) __VA_ARGS__)
#define D_dprintf(fmt, ...) _do_dprintf( (struct debuginfo){.file = __FILE__, .function = _D__FUNC, .line = __LINE__}, fmt __VA_OPT__(,) __VA_ARGS__)
#else
static inline void _do__nothing(const char* fmt, ...) {}
static _D_COMPTIME inline void _do__nothing(const char* fmt, ...) {}
#define D_dprintf(fmt, ...) _do__nothing(fmt __VA_OPT__(,) __VA_ARGS__) //(fmt __VA_OPT__(,) __VA_ARGS__, (void)0)
#endif
#endif
@ -39,5 +50,6 @@ static inline void _do__nothing(const char* fmt, ...) {}
#ifdef __cplusplus
}
#endif
#undef _D_COMPTIME
#endif /* _DEBUG_H */

@ -0,0 +1,36 @@
#ifndef _ERROR_H
#define _ERROR_H
#ifndef $PASTE
# define $_PASTE(x,y) x ## y
# define $PASTE(x,y) $_PASTE(x,y)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
constexpr inline bool is_noexcept=
#if __cpp_exceptions
#define EXCEPT 1
//#define try try
//#define catch(...) catch(__VA_ARGS__)
false
#else
#define EXCEPT 0
#define NOEXCEPT
//#define catch(...) __try {} catch(__VA_ARGS__)
//#define try if constexpr(!is_noexcept)
//#define throw (void)0
true
#endif
;
#endif
#endif /* _ERROR_H */

@ -6,9 +6,12 @@
#include <utility>
#include <vector>
#include <map.h>
#include <tempfile.hpp>
#include <panic.h>
#include <debug.h>
#include <shuffle3.h>
template<typename T>
@ -25,6 +28,12 @@ struct i_back_inserter
virtual inline ~i_back_inserter() =default;
};
struct i_shunt
{
virtual bool is_full() const =0;
virtual inline ~i_shunt() =default;
};
struct file_back_buffer
{
const static constexpr std::size_t DEFAULT_CAP = 1024;
@ -165,48 +174,151 @@ private:
};
template<typename T>
struct dynamic_spill_vector : public i_back_inserter<T>
struct mapped_vector : public i_back_inserter<T>, public i_shunt
{
inline static mapped_vector<T> from_temp(std::size_t sz)
{
D_dprintf("generating with %lu size", sz);
temp_file file;
mapped_vector<T> mvec(file.full_path().c_str(), sz);
D_dprintf("generated?");
mvec.temp = std::make_unique<temp_file>(std::move(file));
return mvec;
}
inline mapped_vector(const char* file, std::size_t sz)
: sz(sz),
temp(nullptr),
map(mm::mmap::allocate(file, sz * sizeof(T))){}
inline mapped_vector(const mapped_vector<T>& c) = delete;
inline mapped_vector(mapped_vector<T>&& m)
: sz(m.sz),
fill_ptr(m.fill_ptr),
temp(std::move(m.temp)),
map(std::move(m.map)){}
inline mapped_vector() : mapped_vector(nullptr, 0)
{
panic("unsupported");
}
inline void push_back(T&& value) override
{
if(is_full()) panic("Tried to push past end of map");
else memory()[++fill_ptr] = value;
}
inline void pop_back() override
{
if(fill_ptr>=0) fill_ptr-=1;
}
inline T& back() override
{
if(fill_ptr>=0)
{
return memory()[fill_ptr];
} else panic("back() called with no elements");
}
inline const T& back() const override
{
if(fill_ptr>=0)
{
return memory()[fill_ptr];
} else panic("back() const called with no elements");
}
inline const std::size_t size() const override { return ((std::size_t)fill_ptr)+1; }
inline std::size_t cap() const { return sz; }
inline bool is_full() const { return fill_ptr >= (ssize_t)(sz-1); }
protected:
inline const span<T> memory() const { return map.as_span().reinterpret<T>(); }
inline span<T> memory() { return map.as_span().reinterpret<T>(); }
private:
std::size_t sz;
ssize_t fill_ptr=-1;
std::unique_ptr<temp_file> temp;
mm::mmap map;
};
template<typename T, typename Shunt>
requires(std::is_base_of<i_back_inserter<T>, Shunt >::value)
struct shunt : public i_back_inserter<T>, protected i_shunt
{
inline dynamic_spill_vector() : dynamic_spill_vector(FSV_DEFAULT_SPILL_AT){}
inline dynamic_spill_vector(std::size_t cap) : dynamic_spill_vector(cap, cap){}
inline dynamic_spill_vector(std::size_t cap, std::size_t spill) : _spill_at(spill), mem(std::vector<T>()), fil(file_vector<T>(cap)) {
typedef Shunt spill_type;
inline shunt() : shunt(FSV_DEFAULT_SPILL_AT){}
inline shunt(spill_type&& into) : shunt(FSV_DEFAULT_SPILL_AT, std::move(into)){}
inline shunt(std::size_t cap) : shunt(cap, cap){}
inline shunt(std::size_t cap, spill_type&& into) : shunt(cap, cap, std::move(into)){}
inline shunt(std::size_t cap, std::size_t spill, spill_type&& into)
: _spill_at(spill), mem(std::vector<T>()), fil(std::make_unique<spill_type>(std::move(into))) {
mem.reserve(cap);
D_dprintf("alloc (explicit) cap %lu (sz %lu == 0?), spill %lu", cap, mem.size(), spill_at());
}
inline shunt(std::size_t cap, std::size_t spill) : _spill_at(spill), mem(std::vector<T>()), fil(nullptr) {
mem.reserve(cap);
D_dprintf("alloc cap %lu (sz %lu == 0?), spill %lu", cap, mem.size(), spill_at());
}
inline dynamic_spill_vector(const dynamic_spill_vector<T>& c) = delete;
inline dynamic_spill_vector(dynamic_spill_vector<T>&& m) :
inline shunt(const shunt<T, Shunt>& c) = delete;
inline shunt(shunt<T, Shunt>&& m) :
_spill_at(m._spill_at),
mem(std::move(m.mem)),
fil(std::move(m.fil)){}
inline void push_back(T&& value) override
{
if(size()>=spill_at()) {
D_dprintf("Spilling: sz %lu, spl: %lu", size(), spill_at());
fil.push_back(std::move(value));
if(is_full()) {
spl()->push_back(std::move(value));
}
else mem.push_back(std::move(value));
}
inline void pop_back() override
{
if(fil.size()) fil.pop_back();
else mem.pop_back();
if(is_full() && spl()->size()) spl()->pop_back();
else mem.pop_back();
}
inline const T& back() const override
{
if(fil.size()) return fil.back();
else return mem.back();
if(is_full() && spl()->size()) return spl()->back();
else return mem.back();
}
inline T& back() override
{
if(fil.size()) return fil.back();
else return mem.back();
if(is_full() && spl()->size()) return spl()->back();
else return mem.back();
}
inline const std::size_t size() const override { return mem.size() + fil.size(); }
inline const std::size_t size() const override { return mem.size() + (fil ? fil->size() : 0); }
inline const std::size_t spill_at() const { return _spill_at; }
inline bool is_spilling() const { return is_full(); }
protected:
inline bool is_full() const override { return size()>=spill_at(); }
private:
inline const spill_type* spl() const
{
if(is_full()) {
if(!fil) fil = std::make_unique<spill_type>();
return fil.get();
} else {
return fil.get();
}
}
inline spill_type* spl()
{
if(is_full()) {
if(!fil) fil = std::make_unique<spill_type>();
return fil.get();
} else {
return fil.get();
}
}
std::size_t _spill_at;
std::vector<T> mem;
file_vector<T> fil;
mutable std::unique_ptr<spill_type> fil;
};
template<typename T>
using dynamic_spill_vector = shunt<T, file_vector<T> >;
template<typename T>
using mapped_spill_vector = shunt<T, mapped_vector<T> >;

@ -2,8 +2,14 @@
#define _MAP_H
#ifdef __cplusplus
#include <stdexcept>
extern "C" {
#define restrict __restrict__
#define MIFCXX(y, n) y
#define MIFC(y, n) n
#else
#define MIFCXX(y, n) n
#define MIFC(y, n) y
#endif
#include <stddef.h>
@ -15,11 +21,23 @@ typedef struct mmap {
size_t len;
} mmap_t;
enum map_flags {
MMF_SHARED,
MMF_PRIVATE,
MMF_READONLY,
};
int map_raw_fd(int fd, mmap_t* restrict ptr, const size_t MIFCXX(* sz, sz[static 1]));
int open_and_alloc(const char* file, mmap_t* restrict ptr, size_t sz);
int open_and_map(const char* file, mmap_t* restrict ptr);
int unmap_and_close(mmap_t map);
int dup_map(const mmap_t *in, mmap_t* restrict out, const size_t *new_size, enum map_flags flags) __attribute__((nonnull(1)));
int map_advise_rand(mmap_t* restrict ptr, int need);
typedef void* (*map_cb)(mmap_t map, void* user);
void* map_and_then(const char* file, map_cb callback, void* user);
void* map_fd_and_then(int fd, map_cb callback, void* user);
int unmap_and_close_s(mmap_t map, int flags);
#ifdef __cplusplus
}
@ -27,15 +45,48 @@ void* map_and_then(const char* file, map_cb callback, void* user);
#include "reinterpret.h"
#include <cstdint>
namespace mm {
enum class Access {
Random,
};
struct mmap;
template<typename T>
concept MemMap = std::derived_from<T, mmap>
and std::is_constructible_v<T, mmap_t>;
struct mmap {
template<MemMap M = mmap>
inline static M allocate(const char* file, std::size_t sz)
{
mmap_t map;
if(!open_and_alloc(file, &map, sz)) panic("Failed to allocmap file");
return M{map};
}
inline static mmap_t create_raw(const char* file)
{
mmap_t map;
if (!open_and_map(file, &map)) panic("Failed to map file");
if (!::open_and_map(file, &map)) panic("Failed to map file");
return map;
}
inline static mmap_t create_raw_fd(int fd, size_t sz)
{
mmap_t map;
if (!::map_raw_fd(fd, &map, &sz)) panic("Failed to allocmap fd");
return map;
}
inline static mmap_t create_raw_fd(int fd)
{
mmap_t map;
if (!::map_raw_fd(fd, &map, nullptr)) panic("Failed to map fd");
return map;
}
template<MemMap M = mmap>
inline static M map_raw_fd(int fd, std::size_t sz = 0)
{
if(sz)
return M{fd, sz};
else return M{fd};
}
inline mmap(mmap_t raw) :inner(raw){}
inline explicit mmap(mmap_t raw) noexcept :inner(raw){}
inline mmap(const char* file)
: inner(create_raw(file)) {}
@ -46,29 +97,106 @@ namespace mm {
}
inline mmap(const mmap& copt) = delete;
inline ~mmap()
inline virtual ~mmap()
{
if (inner.ptr) {
::unmap_and_close(inner);
}
}
inline const span<const unsigned char> as_span() const { return span(as_ptr(), size()); }
inline span<unsigned char> as_span() { return span(as_ptr(), size()); }
inline virtual mmap&& access(Access a, bool need=false) && noexcept
{
if(inner.ptr)
static_cast<mmap&>(*this).access(a, need);
return std::move(*this);
}
inline virtual mmap& access(Access a, bool need=false) & noexcept
{
switch(a) {
case Access::Random: ::map_advise_rand(const_cast<mmap_t*>(&inner), int(need));
default: break;
}
return *this;
}
inline const span<const unsigned char> as_span() const noexcept { return span(as_ptr(), size()); }
inline span<unsigned char> as_span() noexcept { return span(as_ptr(), size()); }
inline const std::uint8_t* as_ptr() const { return (const std::uint8_t*)inner.ptr; }
inline std::uint8_t* as_ptr() { return (std::uint8_t*)inner.ptr; }
inline virtual const std::uint8_t* as_ptr() const noexcept { return static_cast<const std::uint8_t*>(inner.ptr); }
inline virtual std::uint8_t* as_ptr() noexcept { return static_cast<std::uint8_t*>(inner.ptr); }
inline std::size_t size() const { return inner.len; }
inline virtual std::size_t size() const noexcept { return inner.len; }
inline int as_fd() const { return inner.fd; }
inline const mmap_t& as_raw() const { return inner; }
inline virtual int as_fd() const { return inner.fd; }
inline virtual const mmap_t& as_raw() const noexcept { return inner; }
protected:
inline mmap(int fd, size_t sz)
: inner(create_raw_fd(fd, sz)) {}
inline explicit mmap(int fd)
: inner(create_raw_fd(fd)) {}
inline virtual mmap_t& as_raw() noexcept { return const_cast<mmap_t&>( inner ); }
private:
const mmap_t inner;
};
//template mmap::map_raw_fd<mmap>(int, size_t); // Probably not needed?
//template mmap::allocate<mmap>(int, size_t);
struct vmap : public mmap {
inline explicit vmap(mmap_t m) noexcept : mmap(m) {}
vmap();
vmap(size_t);
inline explicit vmap(int fd) noexcept : mmap(fd) {}
//vmap(const char* file);
//int as_fd() const override; unneeded
inline virtual ~vmap() { /* unmap_and_close() is called by super */ }
//TODO: Implement this ^ with `memfd_create()`, etc.
};
template<MemMap M, MemMap D = M>
inline D dup_map(const M& map, size_t resize, map_flags flags =0)
{
const mmap& m = static_cast<const mmap&>(map);
mmap_t out;
if(! ::dup_map(&m.as_raw(), &out, &resize, flags)) panic("Failed to duplicate mapping");
return D{out};
}
template<MemMap M, MemMap D = M>
inline D dup_map(const M& map, map_flags flags =0)
{
const mmap& m = static_cast<const mmap&>(map);
mmap_t out;
if(! ::dup_map(&m.as_raw(), &out, nullptr, flags)) panic("Failed to duplicate mapping");
return D{out};
}
#if 0
// dup_map() deduction guids (XXX: Are these needed, or even valid?)
template<MemMap D>
dup_map(const mmap& m, size_t s, map_flags f) -> dup_map<mmap, D>;
template<>
dup_map(const mmap& m, size_t s, map_flags f) -> dup_map<mmap, mmap>;
template<MemMap M>
dup_map(const M& m, size_t s, map_flags f) -> dup_map<M, M>;
template<MemMap D>
dup_map(const mmap& m, map_flags f) -> dup_map<mmap, D>;
template<>
dup_map(const mmap& m, map_flags f) -> dup_map<mmap, mmap>;
template<MemMap M>
dup_map(const M& m, map_flags f) -> dup_map<M, M>;
#endif
}
#undef restrict
#undef MIFCXX
#undef MIFC
#endif
#endif /* _MAP_H */

@ -20,7 +20,7 @@ extern "C++" {
__attribute__((noreturn)) inline void _real_panic(const char* file, const char* function, int line, const char* fmt, Args&&... args)
{
panicinfo i = { file, function, line };
_do_panic(i, fmt, std::forward<Args>(args)...);
_do_panic(i, fmt, std::move(args)...);
}
#define panic(fmt, ...) _real_panic(__FILE__, __func__, __LINE__, fmt __VA_OPT__(,) __VA_ARGS__)
}

@ -0,0 +1,406 @@
#pragma once
#include <string>
#include <memory>
#include <utility>
#include <array>
#include <utility>
#include <cstdio>
#ifndef __cplusplus
#error "C++ header only"
#endif
namespace pr {
namespace details [[gnu::visibility("internal")]] {
/*
template<template<typename...> typename P>
struct generic_valid { template<typename... Args> constexpr static inline bool value = requires{ typename P<Args...>; }; };
template<template<typename...> class P>
struct generic : std::conditional_t<generic_valid<P>::value,
std::true_type,
std::false_type>
{ template<typename... Args> using type = P<Args...>; };
template<template<typename> typename T, typename... Args>
concept Generic = generic_valid<T>::template value<Args...>;
template<Generic T, typename... Args>
using generic_t = generic<T>::template type<Args...>;
template<template<typename> template T>
concept AnyGeneric = requires{ typename generic_valid<T>; };
*/
template<typename T>
T* take(T*&& ptr) noexcept { return std::exchange(ptr, nullptr); }
/*
template<template<typename U> Inst, typename T>
struct is_inst_of { constexpr static inline bool value = std::is_same_v<T, Inst<U>>; };
template<typename T, typename I>
constexpr inline bool is_inst_of_v = is_inst_of<I, T>::value;
*/
template<typename U, typename T>
constexpr std::unique_ptr<U> static_ucast(std::unique_ptr<T>&& from) noexcept
{ return std::unique_ptr<U>{static_cast<U*>(std::move(from).release())}; }
template<typename U, typename T>
constexpr std::shared_ptr<U> static_ucast(std::shared_ptr<T>&& from) noexcept
{ return std::static_pointer_cast<U>(std::move(from)); }
template<typename Ptr, typename From> requires(requires(From&& p) {
{ p.release() };
requires(std::is_constructible_v<Ptr, decltype(p.release())>);
})
constexpr Ptr static_pcast(From&& p) noexcept { return Ptr{p.release()}; }
}
using increment_t = long double;
template<typename T>
concept Bar = requires(std::remove_const_t<std::remove_reference_t<T>>& v) {
{ v.spin(increment_t(0)) };
{ std::as_const(v).aux() } -> std::convertible_to<std::string_view>;
//{ v.aux() } -> std::assignable_from<std::string_view>;
{ (v.aux() = std::string{}) } -> std::same_as<std::add_lvalue_reference_t<decltype(v.aux())>>;
//requires(std::assignable_from<std::add_lvalue_reference_t<decltype(v.aux())>, std::string_view>);
{ v.aux() += std::string_view{} } -> std::convertible_to<decltype(v.aux())>;
{ v.aux(std::declval<std::string&&>()) } -> std::convertible_to<std::string_view>;
};
template<Bar>
struct Carrier;
struct Dynamic {
template<Bar> friend class Carrier;
constexpr auto make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v<std::remove_reference_t<decltype(bar)>>);
constexpr virtual void spin(increment_t) =0;
constexpr virtual std::string& aux() =0;
constexpr virtual std::string const& aux() const noexcept =0;
constexpr virtual std::string aux(std::string&& s) { return std::exchange(aux(), std::move(s)); }
constexpr virtual ~Dynamic() {}
private:
constexpr Dynamic() noexcept {}
};
template<Bar B>
struct Carrier : public virtual Dynamic {
using held_type = B;
template<typename... Args> requires(std::constructible_from<B, Args...>)
constexpr Carrier(Args&&... args) noexcept(std::is_nothrow_constructible_v<B, Args...>)
: Dynamic(), member_(std::forward<decltype(args)>(args)...) {}
constexpr Carrier(Carrier&& m) noexcept(std::is_nothrow_move_constructible_v<B>)
requires(std::is_move_constructible_v<B>)
: Dynamic(), member_(std::move(m.member_)) {}
constexpr Carrier(Carrier const& c) noexcept(std::is_nothrow_copy_constructible_v<B>)
requires(std::is_copy_constructible_v<B>)
: Dynamic(), member_(c.member_) {}
constexpr Carrier& operator=(Carrier&& m) noexcept(std::is_nothrow_move_assignable_v<B>) requires(std::is_move_assignable_v<B>)
{ if(this != &m) member_ = std::move(m.member_); return *this; }
constexpr Carrier& operator=(Carrier const& c) noexcept(std::is_nothrow_copy_assignable_v<B>) requires(std::is_copy_assignable_v<B>)
{ if(this != &c) member_ = c.member_; return *this; }
constexpr void spin(increment_t a) override { member_.spin(a); }
constexpr std::string& aux() override { return member_.aux(); }
constexpr std::string const& aux() const noexcept override { return member_.aux(); }
constexpr std::string aux(std::string&& s) override { return member_.aux(std::move(s)); }
template<typename... Args> requires(std::is_invocable_v<B::spin, B&, increment_t, Args...>)
constexpr void spin(increment_t a, Args&&... args) noexcept(std::is_nothrow_invocable_v<B::spin, B&, increment_t, decltype(args)...>)
{ return member_.spin(a, std::forward<decltype(args)>(args)...); }
constexpr virtual ~Carrier() {}
private:
B member_;
};
template<Bar T>
Carrier(T) -> Carrier<T>;
template<Bar T>
Carrier(T const&) -> Carrier<T>;
template<Bar T>
Carrier(T&&) -> Carrier<T>;
constexpr auto Dynamic::make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v<std::remove_reference_t<decltype(bar)>>) { return Carrier(std::move(bar)); }
template<Bar T, typename Ptr = std::unique_ptr<Dynamic>>
constexpr Ptr make_dyn(T&& bar) noexcept(std::is_nothrow_move_constructible_v<T>)
{
using namespace details;
if constexpr(requires{
typename T::held_type;
requires(std::is_same_v<Carrier<typename T::held_type>, T>);
}) return static_pcast<Ptr>(static_ucast<Dynamic>(std::make_unique<T>(std::move(bar))));
else return static_pcast<Ptr>(static_ucast<Dynamic>(std::make_unique<Carrier<T>>(std::move(bar))));
}
template<Bar T, typename Ptr = std::unique_ptr<Dynamic>>
constexpr Ptr make_dyn(T const& bar) noexcept(std::is_nothrow_move_constructible_v<T>)
{
if constexpr(std::is_copy_constructible_v<T>) {
T nbar{bar};
return make_dyn<T, Ptr>(std::move(nbar));
} else {
struct unsafe_ref {
constexpr unsafe_ref(const T& ba) noexcept : b(std::addressof(ba)) {}
constexpr unsafe_ref(const unsafe_ref&) noexcept = default;
constexpr ~unsafe_ref() noexcept = default;
constexpr unsafe_ref(unsafe_ref&& b) noexcept : b(std::exchange(b.b, nullptr)) {}
constexpr unsafe_ref& operator=(unsafe_ref const&) noexcept = default;
constexpr unsafe_ref& operator=(unsafe_ref&& m) noexcept {
if(this != &m)
b = std::exchange(m.b, nullptr);
return *this;
}
const T* b;
constexpr operator T const&() const noexcept { return *b; }
};
unsafe_ref re{bar};
return make_dyn<unsafe_ref, Ptr>(std::move(re));
}
}
template<typename Ptr> requires(requires(std::unique_ptr<Dynamic>&& p) { details::static_pcast<Ptr>(std::move(p)); })
constexpr Ptr make_dyn_for(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v<std::remove_reference_t<decltype(bar)>>)
{ return make_dyn<decltype(bar), Ptr>(std::move(bar)); }
constexpr void spin(Bar auto& bar, std::convertible_to<increment_t> auto&& a) { return bar.spin(increment_t(a)); }
struct None {
constexpr None() noexcept = default;
constexpr ~None() noexcept = default;
constexpr void spin(increment_t) const noexcept {}
constexpr auto aux() noexcept { return not_string(); }
inline std::string const& aux() const noexcept { return none_; }
constexpr std::string aux(std::convertible_to<std::string> auto&& s) const noexcept { return {}; } //{ return std::exchange(none_, std::string(std::move(s))); }
private:
struct not_string {
constexpr not_string() noexcept= default;
constexpr ~not_string() noexcept= default;
[[gnu::const]]
constexpr not_string& operator+=(std::convertible_to<std::string_view> auto&&) { return *this; }
[[gnu::const]]
constexpr not_string& operator=(std::convertible_to<std::string> auto&&) { return *this; }
inline operator std::string&() noexcept { return none_; }
inline operator std::string const&() const noexcept { return none_; }
};
/*constinit*/ thread_local static inline std::string none_;
};
constexpr inline None disable{};
// A bounded progress-bar
struct Progress {
using fract_t = increment_t;
constexpr static inline bool DEFAULT_WIDTH = 50;
constexpr static inline fract_t DEFAULT_TERM_FRACT=1.0l/3.0l;
[[gnu::nonnull(1)]]
Progress(FILE* p = stdout) noexcept;
Progress(const Progress& p) noexcept;
Progress(Progress&& p) noexcept;
Progress& operator=(Progress const& c) noexcept;
Progress& operator=(Progress&& m) noexcept;
virtual ~Progress();
inline std::string& aux() noexcept { return tag(); }
inline std::string const& aux() const noexcept { return tag(); }
inline std::string aux(std::convertible_to<std::string> auto&& s)
{ return std::exchange(tag(), std::string(std::move(s))); }
protected:
std::string& tag() noexcept;
const std::string& tag() const noexcept;
public:
FILE* output() const noexcept;
FILE*& output() noexcept;
size_t& width() noexcept;
size_t width() const noexcept;
fract_t& fraction() noexcept;
fract_t fraction() const noexcept;
fract_t percentage() const noexcept;
inline auto percentage() noexcept {
struct v final {
Progress* p_;
v(Progress* p) noexcept : p_(p) {}
v(v&&) noexcept = default;
v(v const&) noexcept = default;
v& operator=(v const&) noexcept = default;
v& operator=(v&&) noexcept = default;
~v() noexcept = default;
const v& operator=(fract_t f) const noexcept { p_->percentage(f); return *this; }
operator fract_t() const noexcept { return static_cast<const Progress*>(p_)->percentage(); }
};
return v(this);
}
void spin(increment_t by, bool render=true, bool flush=true) noexcept;
void render(bool flush = true) const noexcept;
private:
void percentage(fract_t fract) noexcept;
struct _impl;
std::shared_ptr<_impl> inner_;
};
#ifdef SPINNER
class Spinner {
consteval static auto _GENERATE_MAP(auto const& map, size_t size) noexcept
-> std::array<size_t, 256>
{
static_assert(sizeof(map) == size, "Bad map size");
std::array<size_t, 256> out{};
while( size --> 0 ) out[int(map[size])] = size;
return out;
}
constexpr static increment_t range(increment_t by)
{
if(by < -1.0l) return -1.0l;
else if(by > 1.0l) return 1.0l;
return by;
}
public:
constexpr static inline auto ROUTINE = "|/-\\|/-\\|";
constexpr static inline auto ROUTINE_SIZE = sizeof(ROUTINE);
constexpr static inline auto REVERSE_MAP = _GENERATE_MAP(ROUTINE, ROUTINE_SIZE);
static_assert(ROUTINE_SIZE != sizeof(char*), "Invalid routine size");
constexpr ssize_t range(int sz) noexcept
{
/*if(__builtin_expect(sz < 0, false)) {
std::terminate(); // TODO: How to handle wrapping negatives?? Ugh.
}*/
return ssize_t(sz) % ssize_t(ROUTINE_SIZE);
}
constexpr Spinner(size_t n) noexcept
: cur_(ROUTINE[range(n)]) {}
constexpr Spinner(char c = ROUTINE[0]) noexcept
: cur_(range(REVERSE_MAP[int(c)])) {}
constexpr Spinner(Spinner const&) noexcept = default;
constexpr Spinner(Spinner &&) noexcept = default;
~Spinner();
inline void spin(int by) noexcept
{
operator+=(by);
render();
}
inline void spin(increment_by by) noexcept
{
spin(int(range(by) * increment_t(ROUTINE_SIZE)));
}
inline Spinner& operator+=(int by) noexcept
{
cur_ = ROUTINE[size_t(ssize_t(REVERSE_MAP[int(cur_)]) + range(by))];
return *this;
}
inline Spinner& operator++() noexcept { spin(1); return *this; }
inline Spinner operator++(int) noexcept { Spinner s = *this; ++s; return *this; }
constexpr Spinner& operator=(Spinner&&) noexcept = default;
constexpr Spinner& operator=(Spinner const&) noexcept = default;
constexpr char& character() noexcept { return cur_; }
constexpr char character() const noexcept { return cur_; }
void render(bool flush=true);
private:
char cur_ = '|';
};
//TODO: Spinny bar thing (unbounded)
#endif
/*
struct Bar {
Bar() noexcept = default;
Bar(const Bar&) noexcept = default;
Bar(Bar&&) noexcept = default;
Bar& operator=(const Bar&) noexcept = default;
Bar& operator=(Bar&&) noexcept = default;
virtual ~Bar() = default;
virtual void spin(int) =0;
};
enum bar_kind {
BRK_UNTRACKED,
BRK_TRACKED,
};
template<bar_kind = BRK_UNTRACKED>
struct progress;
template<>
struct progress<BRK_UNTRACKED>
: public virtual Bar {
progress(FILE* to);
progress(progress const&);
inline progress(progress&& mv) noexcept
: Bar()
, _perc(mv._perc)
, _output(details::take(mv._output)) {}
virtual ~progress();
void spin(int) override;
//TODO: this
protected:
union {
double _perc;
size_t _num; // not used here
};
FILE* _output;
};
template<>
class progress<BRK_TRACKED>
: public progress<BRK_UNTRACKED> {
using base_t = progress<BRK_UNTRACKED>;
public:
inline progress(FILE* to, size_t max) : base_t(to), _num(0), _max(max) {}
progress(const progress&) = default;
progress(progress&& m) : base_t(std::move(m)), _num(std::exchange(m._num, 0)), _max(m._max) {}
void spin(int) override;
//TODO: this
virtual ~progress() {}
protected:
using base_t::_output;
private:
using base_t::_num;
size_t _max;
};
*/
}

@ -4,6 +4,7 @@
#include "shuffle3.h"
#ifdef __cplusplus
#include "rng/impl.hpp"
extern "C" {
#endif
@ -11,8 +12,12 @@ enum rng_kind {
RNG_KIND_FRNG,
RNG_KIND_DRNG,
RNG_KIND_XORNG,
RNG_KIND_LORENZ,
};
typedef long double rng_st_lorenz_t;
typedef _Complex long double rng_lorenz_t;
typedef struct rng_init
{
enum rng_kind kind;
@ -25,7 +30,12 @@ typedef struct rng_init
} drng;
struct {
uint64_t state[2];
} xorng;
} xorng;
struct {
rng_lorenz_t point;
const rng_st_lorenz_t (* _UNIQUE state)[5];
uint64_t iter;
} lorenz;
} init;
} rng_init_opt;
@ -38,14 +48,15 @@ void rng_free(rng_t ptr);
// Tests
extern void rng_test();
extern void rng_test_spec(rng_t rng);
extern void rng_test_spec(rng_t rng) __attribute__((nonnull(1)));
#ifdef __cplusplus
}
// RNG interfaces
#include <rng/frng.hpp>
#include <rng/drng.hpp>
#include <rng/xoroshiro128plus.hpp>
#include "rng/xoroshiro128plus.hpp"
#include "rng/frng.hpp"
#include "rng/drng.hpp"
#include "rng/lorenz.hpp"
namespace rng {
void test_algo(RNG&& rng);

@ -34,6 +34,7 @@ namespace rng
inline constexpr frng(const std::array<double, 2>& ar) : state(ar){P}
inline constexpr frng(std::array<double, 2>&& ar) : state(ar){P}
inline constexpr frng(const double (&ar)[2]) : state({ar[0], ar[1]}) {P}
constexpr virtual ~frng() = default;
#undef P
inline constexpr double next_double() override { return sample(); }
inline constexpr float next_float() override { return (float)sample(); }

@ -6,6 +6,8 @@
/// Base class for RNG impls
struct RNG {
constexpr RNG() noexcept = default;
virtual unsigned char byte();
virtual void bytes(unsigned char* ptr, std::size_t len);
@ -29,7 +31,12 @@ struct RNG {
inline virtual float next_float() { return (float)sample(); }
inline virtual double next_double() { return sample(); }
virtual ~RNG() = default;
constexpr virtual ~RNG() = default;
//explicit operator rng_t() const noexcept;
//friend operator RNG*(rng_t) noexcept;
protected:
virtual double sample() = 0;
//private:
//struct rng_impl* _UNIQUE _held = nullptr;
};

@ -0,0 +1,2 @@
#pragma once

@ -9,10 +9,11 @@ namespace rng
{
using State = std::array<std::uint64_t, 2>;
#define P D_dprintf("xorng: seeded with (%lu, %lu)", state[0], state[1]);
inline constexpr xoroshiro128plus(std::uint64_t s0, std::uint64_t s1) : state({s0, s1}){P}
inline constexpr xoroshiro128plus(std::array<std::uint64_t, 2>&& ar) : state(ar){P}
inline constexpr xoroshiro128plus(const std::array<std::uint64_t, 2>& ar) : state(ar){P}
inline constexpr xoroshiro128plus(const std::uint64_t (&ar)[2]) : state({ar[0], ar[1]}){P}
inline constexpr xoroshiro128plus(std::uint64_t s0, std::uint64_t s1) : RNG(), state({s0, s1}){P}
inline constexpr xoroshiro128plus(std::array<std::uint64_t, 2>&& ar) : RNG(), state(ar){P}
inline constexpr xoroshiro128plus(const std::array<std::uint64_t, 2>& ar) : RNG(), state(ar){P}
inline constexpr xoroshiro128plus(const std::uint64_t (&ar)[2]) : RNG(), state({ar[0], ar[1]}){P}
inline virtual ~xoroshiro128plus() {}
#undef P
std::uint64_t next_ulong();
using RNG::next_long;
@ -25,5 +26,6 @@ namespace rng
private:
State state;
};
static_assert(std::derived_from<xoroshiro128plus, RNG>, "Wtf???");
}

@ -14,7 +14,7 @@ namespace rng {
inline void shuffle(R& rng, span<T> span)
{
if(!span.size()) return;
std::cout << " -> shuffling " << span.size() << " objects...";
std::cout << " -> shuffling " << span.size() << " objects..." << std::flush;
for(std::size_t i=span.size()-1;i>0;i--)
{
auto j = rng.next_long(i);
@ -37,20 +37,33 @@ namespace rng {
{
if(!span.size()) return;
#if defined(_FS_SPILL_BUFFER) && _FS_SPILL_BUFFER == DYN
#define DYN 2
#define MAP 3
#if defined(_FS_SPILL_BUFFER) && (_FS_SPILL_BUFFER == DYN)
D_dprintf("spill=dyn");
dynamic_spill_vector<std::size_t> rng_values =
can_allocate<std::size_t>(span.size()) //Is there any way we can not waste this malloc() when it's valid?
? dynamic_spill_vector<std::size_t> (span.size())
//can_allocate<std::size_t>(span.size()) //Is there any way we can not waste this malloc() when it's valid?
span.size() <= FSV_DEFAULT_SPILL_AT
? dynamic_spill_vector<std::size_t> (span.size(), FSV_DEFAULT_SPILL_AT)
: dynamic_spill_vector<std::size_t> (FSV_DEFAULT_SPILL_AT);
#elif defined(_FS_SPILL_BUFFER) && (_FS_SPILL_BUFFER == MAP)
D_dprintf("spill=map");
mapped_spill_vector<std::size_t> rng_values =
span.size() <= FSV_DEFAULT_SPILL_AT
? mapped_spill_vector<std::size_t> (span.size(), FSV_DEFAULT_SPILL_AT)
: mapped_spill_vector<std::size_t> (FSV_DEFAULT_SPILL_AT, mapped_vector<std::size_t>::from_temp(span.size() - FSV_DEFAULT_SPILL_AT));
#elif defined(_FS_SPILL_BUFFER)
D_dprintf("spill=static");
fixed_spill_vector<std::size_t> rng_values;
#else
D_dprintf("spill=none");
std::vector<std::size_t> rng_values;
rng_values.reserve(span.size());
#endif
#undef MAP
#undef DYN
std::cout << " -> unshuffling " << span.size() << " objects...";
std::cout << " -> unshuffling " << span.size() << " objects..." << std::flush;
for(std::size_t i=span.size()-1;i>0;i--)
rng_values.push_back(rng.next_long(i));

@ -0,0 +1,49 @@
#pragma once
#include <filesystem>
#include <string>
#include <utility>
#include "uuid.hpp"
#include <debug.h>
using std::size_t;
namespace fs = std::filesystem;
/// A temporary file name
struct temp_file
{
inline temp_file(const temp_file& c) = delete;
inline temp_file(temp_file&& m) : name(std::move(m.name)), _full_path(std::move(m._full_path)) {}
inline temp_file() : name(uuid::generate().to_string()+"-s3"){}
inline temp_file(const char* name) : name(name) {}
inline temp_file(std::string&& name) : name(name) {}
inline ~temp_file()
{
if(name.empty() && _full_path.empty()) return;
D_dprintf("~tempfile(): %s", _full_path.c_str());
if(!_full_path.empty() && fs::exists(_full_path) ) {
D_dprintf("tfile removing: %s", _full_path.c_str());
fs::remove(_full_path);
}
}
inline const fs::path& full_path() const
{
if(_full_path.empty()) {
_full_path = fs::absolute( fs::temp_directory_path() / name );
D_dprintf("tfile path: %s", _full_path.c_str());
}
return _full_path;
}
inline const std::string& base_name() const { return name; }
inline const fs::path* operator->() const { return &full_path(); }
private:
std::string name;
mutable fs::path _full_path;
};

@ -1,5 +1,6 @@
#pragma once
#include <array>
#include <random>
#include <string>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

@ -1,17 +1,85 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <debug.h>
#include <colours.h>
enum jt_comp {
JT_COMP_UNIX,
JT_COMP_DATE,
JT_COMP_TIME,
JT_COMP_DATETIME,
};
#define CLONE(p) ({ __auto_type $__p = (p); \
__auto_type $__r = __builtin_choose_expr(__builtin_classify_type($__p) == 5, *$__p, $__p); \
/* ((__typeof__($__r) []) { $__r }); }) */ \
__typeof__($__r) $__rp[1]; \
$__rp[0] = $__r; \
&$__rp[0]; })
__attribute__((returns_nonnull, nonnull(1)))
static inline
const char* jtime(const struct tm* restrict lt, enum jt_comp kind)
{
_Thread_local static char datebuf[1
+ 4 + 2 + 4 + 1 // `dd/mm/yyyy`
+ 6 + 3 // ` hh:MM:ss`
];
_Static_assert(sizeof(datebuf) != sizeof(char*), "Unexpected decay");
//memset(datebuf, '0', sizeof(datebuf)-1);
if(!kind)
snprintf(datebuf, sizeof(datebuf), "%lu", mktime(CLONE(lt)));
else {
size_t w = 0, sz = sizeof(datebuf);
if(kind & JT_COMP_DATE)
w += snprintf(datebuf, sz, "%02u/%02u/%4.4u"
, (unsigned)lt->tm_mday
, (unsigned)lt->tm_mon
, (unsigned)(1900 + lt->tm_year));
if(kind & JT_COMP_TIME)
w += snprintf(datebuf+w, sz - w, " %02u:%02u:%02u"
, (unsigned)lt->tm_hour
, (unsigned)lt->tm_min
, (unsigned)lt->tm_sec);
}
return datebuf;
}
void _do_dprintf(struct debuginfo info, const char* fmt, ...)
{
#ifdef DEBUG
va_list li;
time_t utime;
struct tm ltime;
time(&utime);
localtime_r(&utime, &ltime);
va_start(li, fmt);
fprintf(stderr, "["FGRN("dbg")" " FYEL("@") BOLD("%s") "->" FWHT("%s") ":" FYEL("%d") "]: ", info.file, info.function,info.line);
#ifdef D_TRACE
#define FMT_FUNC "%s"
#else
#define FMT_FUNC "%s()"
#endif
#define TTY_OUT "["FGRN("dbg")" (%s %s) " FYEL("@") BOLD("%s") "->" FWHT(FMT_FUNC) ":" FYEL("%d") "]: "
#define NCOL(x) x
#define FIL_OUT "["NCOL("dbg")" (%s %s) " NCOL("@") NCOL("%s") "->" NCOL(FMT_FUNC) ":" NCOL("%d") "]: "
fprintf(stderr, isatty(fileno(stderr)) ? (TTY_OUT) : (FIL_OUT)
, jtime(&ltime, JT_COMP_DATETIME), ltime.tm_zone
, info.file
, info.function
, info.line);
vfprintf(stderr, fmt, li);
fprintf(stderr, "\n");
va_end(li);

@ -5,43 +5,10 @@
#include <fsvec.h>
#include <debug.h>
#define FB file_back_buffer
using std::size_t;
namespace fs = std::filesystem;
/// A temporary file name
struct temp_file
{
inline temp_file(const temp_file& c) = delete;
#include <tempfile.hpp>
inline temp_file(temp_file&& m) : name(std::move(m.name)) { m._full_path.clear(); }
inline temp_file() : name(uuid::generate().to_string()+"-s3"){}
inline temp_file(const char* name) : name(name) {}
inline temp_file(std::string&& name) : name(name) {}
inline ~temp_file()
{
if(!_full_path.empty() && fs::exists(_full_path) ) {
D_dprintf("tfile removing: %s", _full_path.c_str());
fs::remove(_full_path);
}
}
inline const fs::path& full_path() const
{
if(_full_path.empty()) {
_full_path = fs::absolute( fs::temp_directory_path() / name );
D_dprintf("tfile path: %s", _full_path.c_str());
}
return _full_path;
}
inline const std::string& base_name() const { return name; }
#define FB file_back_buffer
inline const fs::path* operator->() const { return &full_path(); }
private:
std::string name;
mutable fs::path _full_path;
};
struct FB::impl
{

@ -12,6 +12,51 @@
#include <map.h>
__attribute__((pure, nonnull(1)))
static inline int _map(mmap_t* restrict map)
{
//TODO: Based on map->len, choose (or calculate) appropriate `MAP_HUGE_*` flag
return (map->ptr = mmap(NULL, map->len, PROT_READ | PROT_WRITE, MAP_SHARED, map->fd, 0)) != MAP_FAILED;
}
//#define _map(...) _map(__VA_ARGS__, 0)
int map_advise_rand(mmap_t* restrict ptr, int need)
{
int flags = MADV_RANDOM | (need ? MADV_WILLNEED : 0);
if(madvise(ptr->ptr, ptr->len, flags)) {
perror("madvise() failed");
return 0;
}
return 1;
}
int map_raw_fd(int fd, mmap_t* restrict ptr, const size_t sz[static 1])
{
if(fd < 0) return 0;
struct stat st;
if(sz && ftruncate(fd, *sz)) {
perror("Failed to allocate");
return 0;
} else if(fstat(fd, &st) < 0) {
perror("Failed to stat file");
return 0;
}
struct mmap map = { .fd = fd, .ptr = NULL, .len = sz ? *sz : st.st_size };
if (!_map(&map)) {
perror("mmap() failed");
return 0;
}
*ptr = map;
return 1;
}
int open_and_map(const char* file, mmap_t* restrict ptr)
{
int fd;
@ -27,9 +72,9 @@ int open_and_map(const char* file, mmap_t* restrict ptr)
return 0;
}
register struct mmap map = { .fd = fd, .ptr = NULL, .len = st.st_size };
struct mmap map = { .fd = fd, .ptr = NULL, .len = st.st_size };
if ((map.ptr = mmap(NULL, map.len, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0)) == MAP_FAILED) {
if (!_map(&map)) {
perror("mmap() failed");
close(fd);
return 0;
@ -40,9 +85,41 @@ int open_and_map(const char* file, mmap_t* restrict ptr)
return 1;
}
int unmap_and_close(mmap_t map)
int open_and_alloc(const char* file, mmap_t* restrict ptr, size_t sz)
{
int fd;
if ((fd = open(file, O_CREAT | O_RDWR, FILEMODE)) < 0) {
perror("Failed to open file");
return 0;
}
if(ftruncate(fd, sz))
{
perror("Failed to allocate");
close(fd);
return 0;
}
struct mmap map = { .fd = fd, .ptr = NULL, .len = sz };
if (!_map(&map)) {
perror("mmap() failed");
close(fd);
return 0;
}
*ptr = map;
return 1;
}
inline
int unmap_and_close_s(mmap_t map, int flags)
{
register int rval=1;
if (map.fd && msync(map.ptr, map.len, flags))
perror("msync() failed");
if (munmap(map.ptr, map.len) < 0) {
perror("munmap() failed");
rval=0;
@ -54,3 +131,41 @@ int unmap_and_close(mmap_t map)
return rval;
}
int unmap_and_close(mmap_t map)
{
return unmap_and_close_s(map, MS_SYNC /* | MS_INVALIDATE XXX: I don't think this is needed, we son't be using the mapping anymore*/);
}
__attribute__((nonnull(1)))
int dup_map(const mmap_t *in, mmap_t* restrict ptr, const size_t *new_size, enum map_flags flags)
{
int fd;
if ( (fd = dup(in->fd)) < 0 )
{
perror("dup() failed");
return 0;
}
size_t sz;
if(new_size) {
if(ftruncate(fd, sz = *new_size))
{
perror("Failed to set cloned map size");
close(fd);
return 0;
}
} else sz = in->len;
struct mmap map = { .fd = fd, .ptr = NULL, .len = sz };
if ((map.ptr = mmap(NULL, map.len, PROT_READ | (flags & MMF_READONLY ? 0 : PROT_WRITE), (flags & MMF_PRIVATE) ? MAP_PRIVATE : MAP_SHARED,fd, 0)) == MAP_FAILED) {
perror("mmap() failed");
close(fd);
return 0;
}
*ptr = map;
return 1;
}

@ -0,0 +1,31 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <panic.h>
#include <map.h>
namespace {
constexpr inline auto MEMFD_DEFAULT_FLAGS = MFD_CLOEXEC;
int memfd_alloc(const char* name, size_t size = 0, int flags = MEMFD_DEFAULT_FLAGS)
{
int fd = memfd_create(name, flags);
if(fd < 0) panic("memfd_create() failed");
if(size)
if(ftruncate(fd, size)) { close(fd); panic("ftruncate() failed"); }
return fd;
}
}
namespace mm {
vmap::vmap()
: mmap(create_raw_fd(memfd_alloc(__PRETTY_FUNCTION__))) {}
vmap::vmap(size_t sz)
: mmap(create_raw_fd(memfd_alloc(__PRETTY_FUNCTION__, sz))) {}
}

@ -7,5 +7,11 @@
extern "C" void* map_and_then(const char* file, map_cb callback, void* user)
{
mm::mmap map(file);
return callback(map.as_raw(), user);
return callback(std::as_const(map).as_raw(), user);
}
extern "C" void* map_fd_and_then(int fd, map_cb callback, void* user)
{
auto map = mm::mmap::map_raw_fd(fd);
return callback(std::as_const(map).as_raw(), user);
}

@ -1,18 +1,41 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <panic.h>
#include <colours.h>
__attribute__((noreturn)) void _do_panic(struct panicinfo info, const char* fmt, ...)
#ifndef DEFAULT_PANIC_HANDLER
#if __cpp_exceptions || 1 /* XXX: Cannot detect this here */
# define DEFAULT_PANIC_HANDLER "_panic__start_unwind_cxx"
#else
# define DEFAULT_PANIC_HANDLER "_panic__start_unwind_abort"
#endif
#endif
extern void _panic__start_unwind_cxx(void);
static void _panic__start_unwind_abort(void*)
__attribute__((weakref("abort"), noreturn));
static void _panic__start_unwind(void* payload)
__attribute__((noreturn, weakref(DEFAULT_PANIC_HANDLER)));
__attribute__((noreturn, weak)) void _do_panic(struct panicinfo info, const char* fmt, ...)
{
va_list li;
va_start(li, fmt);
fprintf(stderr, BOLD(UNDL(FRED("[!]"))) " (" BOLD("%s") "->" BOLD(FRED("%s")) ":" FYEL("%d") ") " BOLD(FRED("fatal error")) ": ", info.file, info.function, info.line);
#define FMT_TTY BOLD(UNDL(FRED("[!]"))) " (" BOLD("%s") "->" BOLD(FRED("%s")) ":" FYEL("%d") ") " BOLD(FRED("fatal error")) ": "
#define FMT_FIL "[!]" " (" "%s" "->" "%s" ":" "%d" ") " "fatal error" ": "
fprintf(stderr, isatty(fileno(stderr))
? FMT_TTY
: FMT_FIL , info.file, info.function, info.line);
vfprintf(stderr, fmt, li);
fprintf(stderr, "\n");
va_end(li);
abort();
_panic__start_unwind(&info);
__builtin_unreachable();
}

@ -0,0 +1,47 @@
#include <utility>
#include <stdexcept>
#include <cstdlib>
extern "C" {
struct ErrorPayload {};
}
struct FatalError {
FatalError(void* payload)
: payload(payload) {}
FatalError(FatalError&& m)
: payload(std::exchange(m.payload, nullptr)) {}
FatalError& operator=(FatalError&& m)
{
if(this != &m) {
if(payload) destroy();
payload = std::exchange(m.payload, nullptr);
} return *this;
}
virtual ~FatalError() noexcept {
destroy();
}
protected:
virtual void destroy() noexcept
{
/*if(auto* p = dynamic_cast<ErrorPayload*>(payload))
delete p;*/
}
public:
void* payload;
};
extern "C" {
[[noreturn, gnu::weak]]
void _panic__start_unwind_cxx(void* payload)
{
#if __EXCEPTIONS
throw FatalError(payload);
#else
::abort();
#endif
}
}

@ -0,0 +1,135 @@
#include <string>
#include <vector>
#include <fmt/format.h>
#include <sys/ioctl.h>
#include <perc.h>
using fract_t = pr::Progress::fract_t;
namespace {
using namespace pr;
size_t twidth(int fd = STDIN_FILENO, size_t orr = Progress::DEFAULT_WIDTH) noexcept
{
struct winsize w;
if(ioctl(fd, TIOCGWINSZ, &w) == -1) return orr;
return size_t(w.ws_col);
}
size_t def_bar_width(size_t tw) noexcept
{
return size_t(std::round(Progress::DEFAULT_TERM_FRACT * fract_t(tw)));
}
}
namespace pr {
struct Progress::_impl {
Progress::fract_t fract; // 0..=1
FILE* output;
size_t width;
std::string aux;
};
size_t& Progress::width() noexcept { return inner_->width; }
size_t Progress::width() const noexcept { return inner_->width; }
fract_t& Progress::fraction() noexcept { return inner_->fract; }
fract_t Progress::fraction() const noexcept { return inner_->fract; }
fract_t Progress::percentage() const noexcept { return inner_->fract * 100.0l; }
void Progress::percentage(fract_t fract) noexcept { inner_->fract = fract / 100.0l; }
std::string& Progress::tag() noexcept { return inner_->aux; }
const std::string& Progress::tag() const noexcept { return inner_->aux; }
FILE* Progress::output() const noexcept { return inner_->output; }
FILE*& Progress::output() noexcept { return inner_->output; }
void Progress::render(bool flush) const noexcept
{
constinit thread_local static std::vector<char> buffer{};
const auto& inner = *inner_;
FILE* out = inner.output;
auto wf = fract_t(inner.width) * inner.fract;
auto perc = percentage();
size_t cf = size_t(std::floor(wf));
//auto print = []<typename F> (F f, auto&&... args) {
#define print(...) fmt::format_to(std::back_inserter(buffer), __VA_ARGS__);
//};
buffer.clear();
// Render bar
buffer.push_back('[');
for(size_t i=0;i<inner.width;i++)
if(i<=cf) buffer.push_back( '#' );
else buffer.push_back( ' ' );
buffer.push_back( ']' );
print(": {:00.2}%", perc);
if(inner.aux.size())
print(" ({})", std::string_view(inner.aux));
// Print bar
fprintf(out, "\r%.*s", int(buffer.size() & INT_MAX), static_cast<const char*>(buffer.data()));
// Flush output stream
if(flush) fflush(out);
#undef print
}
void Progress::spin(increment_t by, bool r, bool f) noexcept
{
auto& inner = *inner_;
inner.fract=by;
if(inner.fract > 1.0l)
inner.fract=1.0l;
if(r) this->render(f);
}
[[gnu::nonnull(1)]]
Progress::Progress(FILE* p) noexcept
: inner_(std::make_unique<_impl>(0, p, def_bar_width(twidth(fileno(p))) )) {}
Progress::Progress(const Progress& p) noexcept
: inner_(p.inner_ ? std::make_unique<_impl>(*p.inner_) : nullptr) {}
Progress::Progress(Progress&& p) noexcept
: inner_(std::move(p.inner_)) {}
Progress& Progress::operator=(Progress&& p) noexcept
{
if(this != &p) inner_ = std::move(p.inner_);
return *this;
}
Progress& Progress::operator=(Progress const& p) noexcept
{
if(this != &p) inner_ = p.inner_;
return *this;
}
Progress::~Progress() {
if(inner_)
fputc('\n', inner_->output);
}
#ifdef SPINNER
Spinner::~Spinner() {
//TODO: Add output: if(cur_) fputc('\n', output);
}
void Spinner::render(bool flush) {
/*char arr[] = {
'\r',
cur_,
0,
};
fputs(arr, output);
if(flush) fflush(output);*/
}
#endif
}

@ -1,8 +1,53 @@
#include <stdexcept>
#include <iostream>
#include <cmath>
#include <sys/types.h>
#include <rng/impl.hpp>
#include <error.h>
#include <panic.h>
//#define IMPORT_ONE(NS, NAME) using NAME = NS :: NAME
//IMPORT_ONE(std, ssize_t);
template<typename To, typename T, typename Unit=unsigned char>
static inline To* offset_ptr(T* ptr, ssize_t by) noexcept
{
return reinterpret_cast<To*>(reinterpret_cast<Unit*>(ptr)+by);
}
/*
template<typename To, typename Unit>
offset_ptr(auto* p, std::ssize_t by) -> offset_ptr<To, decltype(p), Unit>;
template<typename To>
offset_ptr(auto* p, std::ssize_t by) -> offset_ptr<To, decltype(p)>;
*/
#ifdef FT_PT_OPERATORS
inline operator RNG*(rng_t ptr) noexcept
{
if(__builtin_expect(!ptr, false)) return nullptr;
#ifdef DEBUG
RNG* op =
#else
return
#endif
reinterpret_cast<RNG*>(offset_ptr<RNG>(ptr, -static_cast<ssize_t>(offsetof(RNG, _held))));
#ifdef DEBUG
if(__builtin_expect(!op, false)) return nullptr;
if(__builtin_expect(op->_held, false)) panic("Invalid rng_t -> RNG conversion");
return ptr;
#endif
}
RNG::operator rng_t() const noexcept
{
return reinterpret_cast<rng_t>(static_cast<RNG*>(this)->_held);
}
#endif
inline unsigned char RNG::byte()
{
return (unsigned char)next_int(255);
@ -48,14 +93,21 @@ std::int64_t RNG::next_long()
}
#include <rng.h>
#include <panic.h>
namespace { // C interface
using namespace std;
#define extract_ptr(ptr) ((RNG*)(ptr))
[[gnu::always_inline, gnu::gnu_inline]]
static inline RNG* extract_ptr(rng_t ptr)
{ return reinterpret_cast<RNG*> (ptr); }
static inline RNG& extract_ref(rng_t rng)
{
return *extract_ptr(rng);
}
template<std::derived_from<RNG> T>
static inline rng_t wrap_ptr(T* ptr) noexcept
{
if(__builtin_expect(!ptr, false)) return nullptr;
return reinterpret_cast<rng_t>(static_cast<RNG*>(ptr));
}
template<typename T>
static inline T* extract_downcast_ptr(rng_t rng)
{
@ -64,29 +116,50 @@ namespace { // C interface
extern "C"
{
rng_t rng_new(rng_init_opt opt)
{
switch(opt.kind)
{
case RNG_KIND_FRNG: return (rng_t) new rng::frng(opt.init.frng.state);
case RNG_KIND_DRNG: return (rng_t) new rng::drng(opt.init.drng.state);
case RNG_KIND_XORNG: return (rng_t) new rng::xoroshiro128plus(opt.init.xorng.state);
case RNG_KIND_FRNG: return wrap_ptr( new rng::frng(opt.init.frng.state) );
case RNG_KIND_DRNG: return wrap_ptr( new rng::drng(opt.init.drng.state) );
case RNG_KIND_XORNG: return wrap_ptr( new rng::xoroshiro128plus(opt.init.xorng.state) );
//case RNG_KIND_LORENX: return static_cast<rng_t>( new rng::lorenzAttractor(opt.init.lorenz); );
default: panic("Unknown RNG init opt: %d", opt.kind);
__builtin_unreachable();
}
return nullptr;
}
void rng_free(rng_t rng)
{
RNG* ptr = (RNG*)rng;
RNG* ptr = extract_ptr(rng);
delete ptr;
}
[[gnu::nonnull(1)]]
void rng_test_spec(rng_t rng)
{
cout << "rng_test_spec:" << endl;
rng::test_algo(std::move(extract_ref(rng)));
auto& ref = extract_ref(rng);
cout << "rng_test_spec (" << typeid(ref).name() << ")..." << std::flush;
if constexpr(!is_noexcept) {
#if EXCEPT
try {
rng::test_algo(std::move(ref));
}
catch(const std::exception& except) {
cout << "\r\r\r\tERROR: " << (except.what() ?: typeid(except).name()) << endl;
rng_free(rng);
throw;
} catch(...) {
cout << "\r\r\r\tERROR" << endl;
rng_free(rng);
throw;
}
#endif
} else rng::test_algo(std::move(ref));
cout << "\r\r\r\tOK" << endl;
rng_free(rng);
}
}
}

@ -1,5 +1,6 @@
#include <tuple>
#include <functional>
#include <bit>
#include <cfloat>
#include <shuffle3.h>
@ -14,8 +15,16 @@
template<typename T, typename Fn>
std::tuple<T, T> minmax_t(const span<T>& array, Fn keep)
{
T highest;
T lowest;
T highest
#ifdef DEBUG
{}
#endif
;
T lowest
#ifdef DEBUG
{}
#endif
;
bool init=false;
D_dprintf("minmax_t: %p (%lu)", array.as_ptr(), array.size());
for(std::size_t i=0;i<array.size();i++)
@ -47,6 +56,7 @@ namespace work
int xshuffle_ip(const char* file)
{
mm::mmap map(file);
map.access(mm::Access::Random, true);
if constexpr(unshuffle)
{
@ -62,12 +72,12 @@ namespace work
auto [long_l, long_h] = minmax_t(map.as_span().reinterpret<std::int64_t>());
D_dprintf("MMX res (u64): %ld -- %ld", long_l, long_h);
rng::xoroshiro128plus xorng(*(const std::uint64_t*)&long_l, *(const std::uint64_t*)&long_h);
rng::xoroshiro128plus xorng(std::bit_cast<std::uint64_t>(long_l), std::bit_cast<std::uint64_t>(long_h));
rng::unshuffle(xorng, map.as_span().reinterpret<std::int64_t>());
} else {
auto [long_l, long_h] = minmax_t(map.as_span().reinterpret<std::int64_t>());
D_dprintf("MMX res (u64): %ld -- %ld", long_l, long_h);
rng::xoroshiro128plus xorng(*(const std::uint64_t*)&long_l, *(const std::uint64_t*)&long_h);
rng::xoroshiro128plus xorng(std::bit_cast<std::uint64_t>(long_l), std::bit_cast<std::uint64_t>(long_h));
rng::shuffle(xorng, map.as_span().reinterpret<std::int64_t>());
auto [float_l, float_h] = minmax_t(map.as_span().reinterpret<float>(), [](float f) -> bool { return !( (f!=f) || f < -FLT_MAX || f > FLT_MAX); });
@ -88,10 +98,15 @@ namespace work
template<bool unshuffle>
int xshuffle_op(const char* ifile, const char* ofile, bool is_buffered)
{
//TODO: Use libcow's `cow_create_fd()`/`Cow::Cow(fd, size)` to try to map `ifile`
// Then, clone it, and use the `Fake` to do the shuffling on, and write the data to `ofile`.
// If `ifile` cannot be mapped, create a shim `Cow(size(ifile))`, `sendfile()/copy_file_range()/splice()` `ifile` into the shim memory fd, and use that (do *not* clone it to a Fake,)
// Then, perform the shuffling on the original `Cow`, and `sendfile()/copy_file_range()/splice()` the `ifile` file memory descriptor used for the `Cow` into `ofile`, and then destroy the `Cow`, **make sure to `msync()` the Cow'd range before the copy**.
mm::vmap imap; //{ifile};
if constexpr(unshuffle)
{
} else {
}

@ -42,7 +42,7 @@ dd if=/dev/urandom of=$TFILE bs=$FSIZE count=$FCNT >> /dev/null 2>&1 || exit -1
cp $TFILE $TFILE2 || exit -1
for ex in "$@"; do
if [[ -f "$ex" ]]; then
if [[ -f "${ex%% *}" ]] || command -v "${ex%% *}" >/dev/null 2>&1; then
echo ">>> Testing $ex"
stest "$ex" || exit 1
fi

Loading…
Cancel
Save