l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
requires: l4sys l4util
provides: cxx_atexit cxx_atexit_e cxx_base cxx_base_e cxx_io
cxx_io_e cxx_io_kdebug cxx_io_kdebug_e cxx_util
cxx_util_e
Maintainer: warg@os.inf.tu-dresden.de

View File

@@ -0,0 +1,8 @@
PKGDIR = .
L4DIR ?= $(PKGDIR)/../../..
TARGET = lib
include $(L4DIR)/mk/subdir.mk
include:
$(foreach DIR, $(SUBDIR), $(MAKE) -C $(DIR) include)

View File

@@ -0,0 +1,8 @@
/* vim:set ft=c: */
/**
* \defgroup cxx_api Small C++ Template Library
*/
/**
* Small Low-Level C++ Library
*/
namespace cxx {}

View File

@@ -0,0 +1,2 @@
INPUT += l4/cxx
INPUT += %PKGDIR%/doc/cxx.dox

View File

@@ -0,0 +1,12 @@
PKGDIR = ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = io base be start util tl ipc
include $(L4DIR)/mk/subdir.mk
base: io
be: io
start: base util
util: io
ipc: base tl

View File

@@ -0,0 +1,32 @@
# vim:set ft=make:
TARGET_BASENAME := $(TARGET)
TARGET_A := $(TARGET_BASENAME).a
TARGET_E_A := $(TARGET_BASENAME)_e.a
PC_FILENAMES := $(PC_FILENAME) $(PC_FILENAME)_e
PC_LIBS_$(PC_FILENAME) := -l$(patsubst lib%,%,$(TARGET_BASENAME))
PC_LIBS_$(PC_FILENAME)_e := -l$(patsubst lib%,%,$(TARGET_BASENAME))_e
$(foreach src,$(SRC_CC),$(eval CXXFLAGS_$$(src) += -DL4_NO_RTTI -fno-exceptions -fno-rtti))
# Prevent the removal of the %.e.cc files. The build system shall not rebuild
# an unchanged source tree.
.SECONDARY: $(patsubst %.cc,%.e.cc,$(SRC_CC))
SRC_CC_$(TARGET_BASENAME).a := $(SRC_CC)
SRC_CC_$(TARGET_BASENAME).p.a := $(SRC_CC)
SRC_CC_$(TARGET_BASENAME)_e.a := $(patsubst %.cc,%.e.cc,$(SRC_CC))
SRC_CC_$(TARGET_BASENAME)_e.p.a := $(patsubst %.cc,%.e.cc,$(SRC_CC))
SRC_CC :=
PRIVATE_INCDIR += $(SRC_DIR)/
TARGET := $(TARGET_BASENAME).a $(TARGET_BASENAME).p.a \
$(TARGET_BASENAME)_e.a $(TARGET_BASENAME)_e.p.a
all::
%.e.cc: %.cc
$(VERBOSE)ln -s $< $@

View File

@@ -0,0 +1,5 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := include src
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,5 @@
PKGDIR = ../../..
L4DIR ?= $(PKGDIR)/../../..
EXTRA_TARGET := exceptions string
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,312 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* \brief Base exceptions
* \ingroup l4cxx_exceptions
*/
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/l4types.h>
#include <l4/cxx/basic_ostream>
#include <l4/sys/err.h>
#include <l4/sys/capability>
/**
* \defgroup l4cxx_exceptions C++ Exceptions
* \ingroup api_l4re
*/
/**@{*/
#ifndef L4_CXX_NO_EXCEPTION_BACKTRACE
# define L4_CXX_EXCEPTION_BACKTRACE 20 ///< Number of instruction pointers in backtrace
#endif
#if defined(L4_CXX_EXCEPTION_BACKTRACE)
#include <l4/util/backtrace.h>
#endif
/**@}*/
namespace L4
{
/**
* \addtogroup l4cxx_exceptions
*/
/**@{*/
/**
* \brief Back-trace support for exceptions.
* \headerfile l4/cxx/exceptions
*
* This class holds an array of at most #L4_CXX_EXCEPTION_BACKTRACE
* instruction pointers containing the call trace at the instant when an
* exception was thrown.
*/
class Exception_tracer
{
#if defined(L4_CXX_EXCEPTION_BACKTRACE)
private:
void *_pc_array[L4_CXX_EXCEPTION_BACKTRACE];
int _frame_cnt;
protected:
/**
* \brief Create a back trace.
*/
#if defined(__PIC__)
Exception_tracer() noexcept : _frame_cnt(0) {}
#else
Exception_tracer() noexcept
: _frame_cnt(l4util_backtrace(_pc_array, L4_CXX_EXCEPTION_BACKTRACE)) {}
#endif
public:
/**
* \brief Get the array containing the call trace.
*/
void const *const *pc_array() const noexcept { return _pc_array; }
/**
* \brief Get the number of entries that are valid in the call trace.
*/
int frame_count() const noexcept { return _frame_cnt; }
#else
protected:
/**
* \brief Create a back trace.
*/
Exception_tracer() noexcept {}
public:
/**
* \brief Get the array containing the call trace.
*/
void const *const *pc_array() const noexcept { return 0; }
/**
* \brief Get the number of entries that are valid in the call trace.
*/
int frame_count() const noexcept { return 0; }
#endif
};
/**
* \brief Base class for all exceptions, thrown by the L4Re framework.
* \headerfile l4/cxx/exceptions
*
* This is the abstract base of all exceptions thrown within the
* L4Re framework. It is basically also a good idea to use it as base of
* all user defined exceptions.
*/
class Base_exception : public Exception_tracer
{
protected:
/// Create a base exception.
Base_exception() noexcept {}
public:
/**
* Return a human readable string for the exception.
*/
virtual char const *str() const noexcept = 0;
/// Destruction
virtual ~Base_exception() noexcept {}
};
/**
* \brief Exception for an abstract runtime error.
* \headerfile l4/cxx/exceptions
*
* This is the base class for a set of exceptions that cover all errors
* that have a C error value (see #l4_error_code_t).
*/
class Runtime_error : public Base_exception
{
private:
long _errno;
char _extra[80];
public:
/**
* Create a new Runtime_error.
*
* \param err_no Error value for this runtime error.
* \param extra Description of what happened when the error occurred.
*/
explicit Runtime_error(long err_no, char const *extra = 0) noexcept
: _errno(err_no)
{
if (!extra)
_extra[0] = 0;
else
{
unsigned i = 0;
for (; i < sizeof(_extra) && extra[i]; ++i)
_extra[i] = extra[i];
_extra[i < sizeof(_extra) ? i : sizeof(_extra) - 1] = 0;
}
}
char const *str() const noexcept override
{ return l4sys_errtostr(_errno); }
/**
* Get the description text for this runtime error.
*
* \return Pointer to the description string.
*/
char const *extra_str() const { return _extra; }
~Runtime_error() noexcept {}
/**
* Get the error value for this runtime error.
*
* \return Error value.
*/
long err_no() const noexcept { return _errno; }
};
/**
* \brief Exception signalling insufficient memory.
* \headerfile l4/cxx/exceptions
*/
class Out_of_memory : public Runtime_error
{
public:
/// Create an out-of-memory exception.
explicit Out_of_memory(char const *extra = "") noexcept
: Runtime_error(-L4_ENOMEM, extra) {}
/// Destruction
~Out_of_memory() noexcept {}
};
/**
* \brief Exception for duplicate element insertions.
* \headerfile l4/cxx/exceptions
*/
class Element_already_exists : public Runtime_error
{
public:
explicit Element_already_exists(char const *e = "") noexcept
: Runtime_error(-L4_EEXIST, e) {}
~Element_already_exists() noexcept {}
};
/**
* \brief Exception for an unknown condition.
* \headerfile l4/cxx/exceptions
*
* This error is usually used when a server returns an unknown return state
* to the client, this may indicate incompatible messages used by the client
* and the server.
*/
class Unknown_error : public Base_exception
{
public:
Unknown_error() noexcept {}
char const *str() const noexcept override { return "unknown error"; }
~Unknown_error() noexcept {}
};
/**
* \brief Exception for a failed lookup (element not found).
* \headerfile l4/cxx/exceptions
*/
class Element_not_found : public Runtime_error
{
public:
explicit Element_not_found(char const *e = "") noexcept
: Runtime_error(-L4_ENOENT, e) {}
};
/**
* \brief Indicates that an invalid object was invoked.
* \headerfile l4/cxx/exceptions
*
* An Object is invalid if it has L4_INVALID_ID as server L4 UID,
* or if the server does not know the object ID.
*/
class Invalid_capability : public Base_exception
{
private:
Cap<void> const _o;
public:
/**
* \brief Create an Invalid_object exception for the Object o.
* \param o The object that caused the server side error.
*/
explicit Invalid_capability(Cap<void> const &o) noexcept : _o(o) {}
template< typename T>
explicit Invalid_capability(Cap<T> const &o) noexcept : _o(o.cap()) {}
char const *str() const noexcept override { return "invalid object"; }
/**
* \brief Get the object that caused the error.
* \return The object that caused the error on invocation.
*/
Cap<void> const &cap() const noexcept { return _o; }
~Invalid_capability() noexcept {}
};
/**
* \brief Error conditions during IPC.
* \headerfile l4/cxx/exceptions
*
* This exception encapsulates all IPC error conditions of L4 IPC.
*/
class Com_error : public Runtime_error
{
public:
/**
* \brief Create a Com_error for the given L4 IPC error code.
* \param err The L4 IPC error code (l4_ipc... return value).
*/
explicit Com_error(long err) noexcept : Runtime_error(err) {}
~Com_error() noexcept {}
};
/**
* \brief Access out of bounds.
*/
class Bounds_error : public Runtime_error
{
public:
explicit Bounds_error(char const *e = "") noexcept
: Runtime_error(-L4_ERANGE, e) {}
~Bounds_error() noexcept {}
};
/**@}*/
};
inline
L4::BasicOStream &
operator << (L4::BasicOStream &o, L4::Base_exception const &e)
{
o << "Exception: " << e.str() << ".\n";
o << "Backtrace:\n";
for (int i = 0; i < e.frame_count(); ++i)
o << " " << L4::n_hex(l4_addr_t(e.pc_array()[i])) << '\n';
return o;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &o, L4::Runtime_error const &e)
{
o << "Exception: " << e.str();
if (e.extra_str())
o << ": " << e.extra_str();
o << "\n" << "Backtrace:\n";
for (int i = 0; i < e.frame_count(); ++i)
o << " " << L4::n_hex(l4_addr_t(e.pc_array()[i])) << '\n';
return o;
}

View File

@@ -0,0 +1,313 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* Strings.
*/
#pragma once
#include <l4/cxx/minmax>
#include <l4/cxx/basic_ostream>
namespace cxx {
/**
* Allocation free string class with explicit length field.
*
* This class is used to group characters of a string which belong
* to one syntactical token types number, identifier, string,
* whitespace or another single character.
*
* Stings in this class can contain null bytes and may denote parts of
* other strings.
*/
class String
{
public:
/// Character index type.
typedef char const *Index;
/// Initialize from a zero-terminated string.
String(char const *s) noexcept : _start(s), _len(__builtin_strlen(s)) {}
/// Initialize from a pointer to first character and a length.
String(char const *s, unsigned long len) noexcept : _start(s), _len(len) {}
/**
* Initialize with start and end pointer.
*
* \param s first character of the string
* \param e pointer to first byte behind the string
*/
String(char const *s, char const *e) noexcept : _start(s), _len(e - s) {}
/// Zero-initialize. Create an invalid string.
String() : _start(0), _len(0) {}
/// Pointer to first character.
Index start() const { return _start; }
/// Pointer to first byte behind the string.
Index end() const { return _start + _len; }
/// Length.
int len() const { return _len; }
/// Set start.
void start(char const *s) { _start = s; }
/// Set length.
void len(unsigned long len) { _len = len; }
/// Check if the string has length zero.
bool empty() const { return !_len; }
/// Return prefix up to index.
String head(Index end) const
{
if (end < _start)
return String();
if (eof(end))
return *this;
return String(_start, end - _start);
}
/// Prefix of length `end`.
String head(unsigned long end) const
{ return head(start() + end); }
/// Substring of length `len` starting at `idx`.
String substr(unsigned long idx, unsigned long len = ~0UL) const
{
if (idx >= _len)
return String(end(), 0UL);
return String(_start + idx, cxx::min(len, _len - idx));
}
/// Substring of length `len` starting at `start`.
String substr(char const *start, unsigned long len = 0) const
{
if (start >= _start && !eof(start))
{
unsigned long nlen = _start + _len - start;
if (len != 0)
nlen = cxx::min(nlen, len);
return String(start, nlen);
}
return String(end(), 0UL);
}
/// Find matching character. `match` should be a function such as `isspace`.
template< typename F >
char const *find_match(F &&match) const
{
String::Index s = _start;
while (1)
{
if (eof(s))
return s;
if (match(*s))
return s;
++s;
}
}
/// Find character. Return end() if not found.
char const *find(char const *c) const
{ return find(c, start()); }
/// Find character. Return end() if not found.
char const *find(int c) const
{ return find(c, start()); }
/// Find right-most character. Return end() if not found.
char const *rfind(char const *c) const
{
if (!_len)
return end();
char const *p = end();
--p;
while (p >= _start)
{
if (*p == *c)
return p;
--p;
}
return end();
}
/**
* Check if `c` is a prefix of string.
*
* \return 0 if `c` is not a prefix, if it is a prefix, return
* first position not in `c` (which might be end()).
*/
Index starts_with(cxx::String const &c) const
{
unsigned long i;
for (i = 0; i < c._len && i < _len; ++i)
if (_start[i] != c[i])
return 0;
return i == c._len ? start() + i : 0;
}
/// Find character `c` starting at position `s`. Return end() if not found.
char const *find(int c, char const *s) const
{
if (s < _start)
return end();
while (1)
{
if (eof(s))
return s;
if (*s == c)
return s;
++s;
}
}
/**
* Find character set at position.
*
* \param c zero-terminated string of characters to search for
* \param s start position of search in string
*
* \retval end() if no char in `c` is contained in string at or behind `s`.
* \retval position in string of some character in `c`.
*/
char const *find(char const *c, char const *s) const
{
if (s < _start)
return end();
while (1)
{
if (eof(s))
return s;
for (char const *x = c; *x; ++x)
if (*s == *x)
return s;
++s;
}
}
/// Get character at `idx`.
char const &operator [] (unsigned long idx) const { return _start[idx]; }
/// Get character at `idx`.
char const &operator [] (int idx) const { return _start[idx]; }
/// Get character at `idx`.
char const &operator [] (Index idx) const { return *idx; }
/// Check if pointer `s` points behind string.
bool eof(char const *s) const { return s >= _start + _len || !*s; }
/**
* Convert decimal string to integer.
*
* \tparam INT result integer type
* \param[out] v conversion result
*
* \return position of first character not converted.
*/
template<typename INT>
int from_dec(INT *v) const
{
*v = 0;
Index c;
for (c = start(); !eof(c); ++c)
{
unsigned char n;
if (*c >= '0' && *c <= '9')
n = *c - '0';
else
return c - start();
*v *= 10;
*v += n;
}
return c - start();
}
/**
* Convert hex string to integer.
*
* \tparam INT result integer type
* \param[out] v conversion result
*
* \retval -1 if the maximal amount of digits fitting into `INT` have
* been read,
* \retval position of first character not converted otherwise.
*/
template<typename INT>
int from_hex(INT *v) const
{
*v = 0;
unsigned shift = 0;
Index c;
for (c = start(); !eof(c); ++c)
{
shift += 4;
if (shift > sizeof(INT) * 8)
return -1;
unsigned char n;
if (*c >= '0' && *c <= '9')
n = *c - '0';
else if (*c >= 'A' && *c <= 'F')
n = *c - 'A' + 10;
else if (*c >= 'a' && *c <= 'f')
n = *c - 'a' + 10;
else
return c - start();
*v <<= 4;
*v |= n;
}
return c - start();
}
/// Equality.
bool operator == (String const &o) const
{
if (len() != o.len())
return false;
for (unsigned long i = 0; i < _len; ++i)
if (_start[i] != o._start[i])
return false;
return true;
}
/// Inequality.
bool operator != (String const &o) const
{ return ! (operator == (o)); }
private:
char const *_start;
unsigned long _len;
};
}
/// Write `str` on `s`.
inline
L4::BasicOStream &operator << (L4::BasicOStream &s, cxx::String const &str)
{
s.write(str.start(), str.len());
return s;
}

View File

@@ -0,0 +1,15 @@
PKGDIR ?= ../../..
L4DIR ?= $(PKGDIR)/../../..
TARGET = libcxx_base
SYSTEMS = $(SYSTEMS_PLAIN)
PC_FILENAME = cxx_base
SRC_CC = cxa_pure_virtual.cc cxa_pure_delete.cc
SRC_CC_arm = dso_handle.cc
REQUIRES_LIBS := cxx_io
CXX_PKG_DIR=$(PKGDIR)
include $(PKGDIR)/lib/Makefile.inc
include $(L4DIR)/mk/lib.mk
$(GENERAL_D_LOC): $(CXX_PKG_DIR)/lib/Makefile.inc

View File

@@ -0,0 +1,25 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <stddef.h>
#include <l4/cxx/iostream>
void operator delete (void *obj) noexcept
{
L4::cerr << "cxa pure delete operator called for object @"
<< L4::hex << obj << L4::dec << "\n";
}
#if __cplusplus >= 201400
void operator delete (void *obj, size_t size) noexcept;
void operator delete (void *obj, size_t size) noexcept
{
L4::cerr << "cxa pure delete operator called for object @"
<< L4::hex << obj << L4::dec
<< " size " << size << "\n";
}
#endif

View File

@@ -0,0 +1,23 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/cxx/iostream>
extern "C" void __cxa_pure_virtual(void);
void __cxa_pure_virtual()
{
L4::cerr << "cxa pure virtual function called\n";
}
extern "C" void __pure_virtual(void);
void __pure_virtual()
{
L4::cerr << "cxa pure virtual function called\n";
}

View File

@@ -0,0 +1,9 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/* ARM C++ ABI requirement */
void *__dso_handle __attribute__ ((__visibility__ ("hidden"))) = &__dso_handle;

View File

@@ -0,0 +1,5 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := kdebug
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,12 @@
PKGDIR ?= ../../..
L4DIR ?= $(PKGDIR)/../../..
TARGET = libcxx_io_kdebug
PC_FILENAME = cxx_io_kdebug
SRC_CC = iostream.cc
CXX_PKG_DIR=$(PKGDIR)
include $(PKGDIR)/lib/Makefile.inc
include $(L4DIR)/mk/lib.mk
$(GENERAL_D_LOC): $(CXX_PKG_DIR)/lib/Makefile.inc

View File

@@ -0,0 +1,52 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/cxx/basic_ostream>
#include <l4/sys/kdebug.h>
#include <l4/sys/l4int.h>
#include <l4/util/atomic.h>
#include <stddef.h>
inline void *operator new (size_t, void *p) { return p; }
namespace L4 {
class KdbgIOBackend : public IOBackend
{
protected:
void write(char const *str, unsigned len) override;
};
void KdbgIOBackend::write(char const *str, unsigned len)
{
outnstring(str,len);
}
typedef char Fake_iobackend[sizeof(KdbgIOBackend)]
__attribute__((aligned(__alignof__(KdbgIOBackend))));
typedef char Fake_ostream[sizeof(BasicOStream)]
__attribute__((aligned(__alignof__(BasicOStream))));
Fake_ostream cout;
Fake_ostream cerr;
static Fake_iobackend _iob;
void iostream_init();
void iostream_init()
{
static l4_umword_t _initialized;
if (l4util_xchg(&_initialized, 1) == 0)
{
KdbgIOBackend *iob = new (&_iob) KdbgIOBackend();
new (&cerr) BasicOStream(iob);
new (&cout) BasicOStream(iob);
}
}
};

View File

@@ -0,0 +1,8 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := include src
include $(L4DIR)/mk/subdir.mk
src: include

View File

@@ -0,0 +1,6 @@
PKGDIR = ../../..
L4DIR ?= $(PKGDIR)/../../..
EXTRA_TARGET := basic_ostream iostream l4iostream
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,281 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* \brief Basic IO stream
*/
/*
* (c) 2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace L4 {
/**
* \brief Modifier class for the IO stream.
*
* An IO Modifier can be used to change properties of an IO stream
* for example the number format.
*/
class IOModifier
{
public:
IOModifier(int x) : mod(x) {}
bool operator == (IOModifier o) { return mod == o.mod; }
bool operator != (IOModifier o) { return mod != o.mod; }
int mod;
};
/**
* \internal
* \brief Backend to write or read stream data.
*/
class IOBackend
{
public:
typedef int Mode;
protected:
friend class BasicOStream;
IOBackend()
: int_mode(10)
{}
virtual ~IOBackend() {}
virtual void write(char const *str, unsigned len) = 0;
private:
void write(IOModifier m);
void write(long long int c, int len);
void write(long long unsigned c, int len);
void write(long long unsigned c, unsigned char base = 10,
unsigned char len = 0, char pad = ' ');
Mode mode() const
{ return int_mode; }
void mode(Mode m)
{ int_mode = m; }
int int_mode;
};
/**
* \internal
* \brief Write-only backend for stream data.
*/
class BasicOStream
{
public:
BasicOStream(IOBackend *b)
: iob(b)
{}
void write(char const *str, unsigned len)
{
if (iob)
iob->write(str, len);
}
void write(long long int c, int len)
{
if (iob)
iob->write(c, len);
}
void write(long long unsigned c, unsigned char base = 10,
unsigned char len = 0, char pad = ' ')
{
if (iob)
iob->write(c, base, len, pad);
}
void write(long long unsigned c, int len)
{
if (iob)
iob->write(c, len);
}
void write(IOModifier m)
{
if (iob)
iob->write(m);
}
IOBackend::Mode be_mode() const
{
if (iob)
return iob->mode();
return 0;
}
void be_mode(IOBackend::Mode m)
{
if (iob)
iob->mode(m);
}
private:
IOBackend *iob;
};
/**
* \internal
* \brief Container class describing a the number format.
*/
class IONumFmt
{
public:
IONumFmt(unsigned long long n, unsigned char base = 10,
unsigned char len = 0, char pad = ' ')
: n(n), base(base), len(len), pad(pad)
{}
BasicOStream &print(BasicOStream &o) const;
private:
unsigned long long n;
unsigned char base, len;
char pad;
};
inline IONumFmt n_hex(unsigned long long n) { return IONumFmt(n, 16); }
/**
* \brief Modifies the stream to print numbers as hexadecimal values.
*/
extern IOModifier const hex;
/**
* \brief Modifies the stream to print numbers as decimal values.
*/
extern IOModifier const dec;
inline
BasicOStream &IONumFmt::print(BasicOStream &o) const
{
o.write(n, base, len, pad);
return o;
}
}
// Implementation
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, char const * const str)
{
if (!str)
{
s.write("(NULL)", 6);
return s;
}
unsigned l = 0;
for (; str[l] != 0; l++)
;
s.write(str, l);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, signed short u)
{
s.write(static_cast<long long signed>(u), -1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, signed u)
{
s.write(static_cast<long long signed>(u), -1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, signed long u)
{
s.write(static_cast<long long signed>(u), -1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, signed long long u)
{
s.write(u, -1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, unsigned short u)
{
s.write(static_cast<long long unsigned>(u), -1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, unsigned u)
{
s.write(static_cast<long long unsigned>(u), -1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, unsigned long u)
{
s.write(static_cast<long long unsigned>(u), -1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, unsigned long long u)
{
s.write(u, -1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, void const *u)
{
long unsigned x = reinterpret_cast<long unsigned>(u);
L4::IOBackend::Mode mode = s.be_mode();
s.write(L4::hex);
s.write(static_cast<long long unsigned>(x), -1);
s.be_mode(mode);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, L4::IOModifier m)
{
s.write(m);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &s, char c)
{
s.write(&c, 1);
return s;
}
inline
L4::BasicOStream &
operator << (L4::BasicOStream &o, L4::IONumFmt const &n)
{ return n.print(o); }

View File

@@ -0,0 +1,34 @@
// -*- Mode: C++ -*-
// vim:ft=cpp
/**
* \file
* \brief IO Stream
*/
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/basic_ostream>
namespace L4 {
/**
* \brief Standard output stream.
*/
extern BasicOStream cout;
/**
* \brief Standard error stream.
*/
extern BasicOStream cerr;
extern void iostream_init();
static void __attribute__((used, constructor)) __iostream_init()
{ iostream_init(); }
};

View File

@@ -0,0 +1,35 @@
// -*- Mode: C++ -*-
// vim:ft=cpp
/**
* \file
* \brief L4 IO stream
*/
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/basic_ostream>
#include <l4/sys/types.h>
#include <l4/sys/capability>
inline
L4::BasicOStream &operator << (L4::BasicOStream &o, l4_msgtag_t const &tag)
{
L4::IOBackend::Mode m = o.be_mode();
o << "[l=" << L4::dec << tag.label() << "; w=" << tag.words() << "; i="
<< tag.items() << "]";
o.be_mode(m);
return o;
}
template<typename T>
inline
L4::BasicOStream &operator << (L4::BasicOStream &o, L4::Cap<T> const &cap)
{
o << "[C:" << L4::n_hex(cap.cap()) << "]";
return o;
}

View File

@@ -0,0 +1,13 @@
PKGDIR ?= ../../..
L4DIR ?= $(PKGDIR)/../../..
TARGET = libcxx_io
SRC_CC = iob_write.cc
PC_FILENAME = cxx_io
SYSTEMS = $(SYSTEMS_PLAIN)
CXX_PKG_DIR=$(PKGDIR)
include $(PKGDIR)/lib/Makefile.inc
include $(L4DIR)/mk/lib.mk
$(GENERAL_D_LOC): $(CXX_PKG_DIR)/lib/Makefile.inc

View File

@@ -0,0 +1,87 @@
/*
* (c) 2004-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* Frank Mehnert <fm3@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/cxx/basic_ostream>
namespace L4
{
IOModifier const hex(16);
IOModifier const dec(10);
static char const hex_chars[] = "0123456789abcdef";
void IOBackend::write(IOModifier m)
{
if (m == dec)
int_mode = 10;
else if (m == hex)
int_mode = 16;
}
void IOBackend::write(long long int u, int /*len*/)
{
char buffer[20];
int pos = 20;
bool sign = u < 0;
if (sign)
u = -u;
buffer[19] = '0';
while (u)
{
buffer[--pos] = hex_chars[u % int_mode];
u /= int_mode;
}
if (pos == 20)
pos = 19;
if (sign)
buffer[--pos] = '-';
write(buffer + pos, 20 - pos);
}
void IOBackend::write(long long unsigned u, int /*len*/)
{
char buffer[20];
int pos = 20;
buffer[19] = '0';
while (u)
{
buffer[--pos] = hex_chars[u % int_mode];
u /= int_mode;
}
if (pos == 20)
pos = 19;
write(buffer + pos, 20 - pos);
}
void IOBackend::write(long long unsigned u, unsigned char base,
unsigned char len, char pad)
{
char buffer[30];
unsigned pos = sizeof(buffer) - !u;
buffer[sizeof(buffer) - 1] = '0';
while (pos > 0 && u)
{
buffer[--pos] = hex_chars[u % base];
u /= base;
}
if (len > sizeof(buffer))
len = sizeof(buffer);
if (len && sizeof(buffer) - pos > len)
pos = sizeof(buffer) - len;
while (sizeof(buffer) - pos < len)
buffer[--pos] = pad;
write(buffer + pos, sizeof(buffer) - pos);
}
};

View File

@@ -0,0 +1,6 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := include
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,5 @@
PKGDIR = ../../..
L4DIR ?= $(PKGDIR)/../../..
EXTRA_TARGET := ipc_helper ipc_stream ipc_server ipc_timeout_queue
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,55 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* \brief IPC helper
*/
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/exceptions>
#include <l4/sys/utcb.h>
/**
* \defgroup helper IPC Helpers
*/
namespace L4
{
#ifdef __EXCEPTIONS
/**
* \brief Throw an L4 IPC error as exception.
*
* \ingroup helper
* \param o The client side object, for which the IPC was invoked.
* \param err The IPC result code (error code).
* \utcb{utcb}
*/
inline void
throw_ipc_exception([[maybe_unused]] L4::Cap<void> const &o,
l4_msgtag_t const &err, l4_utcb_t *utcb)
{
if (err.has_error())
throw (L4::Com_error(l4_error_u(err, utcb)));
}
/**
* \brief Throw an L4 IPC error as exception.
*
* \ingroup helper
* \param o The client side object, for which the IPC was invoked.
* \param err The IPC result code (error code).
* \utcb{utcb}
*/
inline void
throw_ipc_exception(void const *o, l4_msgtag_t const &err,
l4_utcb_t *utcb)
{ throw_ipc_exception(L4::Cap<void>(o), err, utcb); }
#endif
}

View File

@@ -0,0 +1,163 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* \brief IPC server loop
*/
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/capability>
#include <l4/sys/typeinfo_svr>
#include <l4/sys/err.h>
#include <l4/cxx/ipc_stream>
#include <l4/sys/cxx/ipc_epiface>
#include <l4/sys/cxx/ipc_server_loop>
#include <l4/cxx/type_traits>
#include <l4/cxx/exceptions>
namespace L4 {
/**
* \ingroup cxx_ipc_server
* \brief Abstract server object to be used with L4::Server and
* L4::Basic_registry.
* \note Usually L4::Server_object_t is used as a base class when writing
* server objects.
*
* This server object provides an abstract interface that is used by the
* L4::Registry_dispatcher model. You can derive subclasses from this
* interface and implement application specific server objects.
*/
class Server_object : public Epiface
{
public:
/**
* \brief The abstract handler for client requests to the object.
* \param rights The rights bits in the invoked capability.
* \param ios The Ipc::Iostream for reading the request and writing the reply.
* \retval -L4_ENOREPLY Instructs the server loop to not send a reply.
* \retval <0 Error, reply with error code.
* \retval >=0 Success, reply with return value.
*
* This function must be implemented by application specific server
* objects. The implementation must unmarshall data from the stream (`ios`)
* and create a reply by marshalling to the stream (`ios`). For details
* about the IPC stream see [IPC stream operators](\ref ipc_stream).
*
* \note You need to extract the complete message from the \a ios stream
* before inserting any reply data or before doing any function call
* that may use the UTCB. Otherwise, the incoming message may get lost.
*/
virtual int dispatch(unsigned long rights, Ipc::Iostream &ios) = 0;
l4_msgtag_t dispatch(l4_msgtag_t tag, unsigned rights, l4_utcb_t *utcb) override
{
L4::Ipc::Iostream ios(utcb);
ios.tag() = tag;
int r = dispatch(rights, ios);
return ios.prepare_ipc(r);
}
Cap<Kobject> obj_cap() const
{ return cap_cast<Kobject>(Epiface::obj_cap()); }
};
/**
* \ingroup cxx_ipc_server
* Base class (template) for server implementing server objects.
* \tparam IFACE The IPC interface class that defines the interface that shall
* be implemented.
* \tparam BASE The server object base class (usually L4::Server_object).
*/
template<typename IFACE, typename BASE = L4::Server_object>
struct Server_object_t : BASE
{
/// Data type of the IPC interface definition
typedef IFACE Interface;
/// \return the server-side buffer demand based in \a IFACE.
typename BASE::Demand get_buffer_demand() const override
{ return typename L4::Kobject_typeid<IFACE>::Demand(); }
/**
* Implementation of the meta protocol based on \a IFACE.
* \param ios The IO stream used for receiving the message.
*
* This function can be used to handle incoming #L4_PROTO_META protocol
* requests. The implementation uses the L4::Type_info of \a IFACE
* to handle the requests. Call this function in the implementation of
* Server_object::dispatch() when the received message tag has protocol
* #L4_PROTO_META (L4::Meta::Protocol).
*/
int dispatch_meta_request(L4::Ipc::Iostream &ios)
{ return L4::Util::handle_meta_request<IFACE>(ios); }
/**
* Implementation of protocol-based dispatch for this server object.
* \param self The this pointer for the object (inherits from
* Server_object_t).
* \param rights The rights from the received IPC (forwarded to p_dispatch()).
* \param ios The message stream for the incoming and the reply message.
*
* Server objects may call this function from their dispatch() function.
* This function reads the protocol ID from the message tag and uses the
* p_dispatch code to dispatch to overloaded p_dispatch functions of self.
*/
template<typename THIS>
static int proto_dispatch(THIS *self, l4_umword_t rights, L4::Ipc::Iostream &ios)
{
l4_msgtag_t t;
ios >> t;
return Kobject_typeid<IFACE>::proto_dispatch(self, t.label(), rights, ios);
}
};
/**
* \ingroup cxx_ipc_server
* Helper class to implement p_dispatch based server objects.
* \tparam Derived The data type of your server object class.
* \tparam IFACE The data type providing the interface definition
* for the object.
* \tparam BASE Optional data-type of the base server object (usually
* L4::Server_object)
* \implements L4::Server_object
*
* This class implements the standard dispatch() function of L4::Server_object
* and forwards incoming messages to a set of overloaded p_dispatch() functions.
* There must be a p_dispatch() function in Derived for each interface provided
* by IFACE with the signature
* \code int p_dispatch(Iface *, unsigned rights, L4::Ipc::Iostream &) \endcode
* that is called for messages with protocol == Iface::Protocol.
*
* Example signature for L4Re::Dataspace is:
* \code int p_dispatch(L4Re::Dataspace *, unsigned, L4::Ipc::Iostream &) \endcode
*/
template<typename Derived, typename IFACE, typename BASE = L4::Server_object>
struct Server_object_x : Server_object_t<IFACE, BASE>
{
/// Implementation forwarding to p_dispatch().
int dispatch(l4_umword_t r, L4::Ipc::Iostream &ios)
{
return Server_object_t<IFACE, BASE>::proto_dispatch(static_cast<Derived *>(this),
r, ios);
}
};
/**
* \ingroup cxx_ipc_server
* Server object base class for handling IRQ messages.
*
* This server object base class implements the empty interface
* (L4::Kobject). The implementation of Server_object::dispatch() must
* return -#L4_ENOREPLY, because IRQ messages do not handle replies.
*/
struct Irq_handler_object : Server_object_t<Kobject> {};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,224 @@
// vim:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/hlist>
#include <l4/sys/cxx/ipc_server_loop>
namespace L4 { namespace Ipc_svr {
/**
* \brief Callback interface for Timeout_queue
* \ingroup cxx_ipc_server
*/
class Timeout : public cxx::H_list_item
{
friend class Timeout_queue;
public:
/// Make a timeout
Timeout() : _timeout(0) {}
/// Destroy a timeout
virtual ~Timeout() = 0;
/**
* \brief callback function to be called when timeout happened
* \note The timeout object is already dequeued when this function is called,
* this means the timeout may be safely queued again within the expired()
* function.
*/
virtual void expired() = 0;
/**
* \brief return absolute timeout of this callback.
* \return absolute timeout for this instance of the timeout.
* \pre The timeout object must have been in a queue before, otherwise the
* timeout is not set.
*/
l4_kernel_clock_t timeout() const
{ return _timeout; }
private:
l4_kernel_clock_t _timeout;
};
inline Timeout::~Timeout() {}
/**
* \brief Timeout queue to be used in l4re server loop
* \ingroup cxx_ipc_server
*/
class Timeout_queue
{
public:
/// Provide a local definition of Timeout for backward compatibility.
typedef L4::Ipc_svr::Timeout Timeout;
/**
* \brief Get the time for the next timeout.
* \return the time for the next timeout or 0 if there is none
*/
l4_kernel_clock_t next_timeout() const
{
if (auto e = _timeouts.front())
return e->timeout();
return 0;
}
/**
* \brief Determine if a timeout has happened.
*
* \param now The current time.
*
* \retval true There is at least one expired timeout in the queue.
* false No expired timeout in the queue.
*/
bool timeout_expired(l4_kernel_clock_t now) const
{
l4_kernel_clock_t next = next_timeout();
return (next != 0) && (next <= now);
}
/**
* \brief run the callbacks of expired timeouts
* \param now the current time.
*/
void handle_expired_timeouts(l4_kernel_clock_t now)
{
while (!_timeouts.empty())
{
Queue::Iterator top = _timeouts.begin();
if ((*top)->_timeout > now)
return;
Timeout *t = *top;
top = _timeouts.erase(top);
t->expired();
}
}
/**
* \brief Add a timeout to the queue
* \param timeout timeout object to add
* \param time the time when the timeout expires
* \pre \a timeout must not be in any queue already
*/
void add(Timeout *timeout, l4_kernel_clock_t time)
{
timeout->_timeout = time;
Queue::Iterator i = _timeouts.begin();
while (i != _timeouts.end() && (*i)->timeout() < time)
++i;
_timeouts.insert_before(timeout, i);
}
/**
* \brief Remove \a timeout from the queue.
* \param timeout timeout to remove from timeout queue
* \pre \a timeout must be in this queue
*/
void remove(Timeout *timeout)
{
_timeouts.remove(timeout);
}
private:
typedef cxx::H_list<Timeout> Queue;
Queue _timeouts;
};
/**
* \ingroup cxx_ipc_server
* \brief Loop hooks mixin for integrating a timeout queue into the server
* loop.
* \tparam HOOKS has to inherit from Timeout_queue_hooks<> and provide
* the functions now() that has to return the current time.
* \tparam BR_MAN This used as a base class for and provides the API for
* selecting the buffer register (BR) that is used to store the
* timeout value. This is usually L4Re::Util::Br_manager or
* L4::Ipc_svr::Br_manager_no_buffers.
*
* \implements L4::Ipc_svr::Server_iface
*/
template< typename HOOKS, typename BR_MAN = Br_manager_no_buffers >
class Timeout_queue_hooks : public BR_MAN
{
l4_kernel_clock_t _now()
{ return static_cast<HOOKS*>(this)->now(); }
unsigned _timeout_br()
{ return this->first_free_br(); }
public:
/// get the time for the next timeout
l4_timeout_t timeout()
{
l4_kernel_clock_t t = queue.next_timeout();
if (t)
return l4_timeout(L4_IPC_TIMEOUT_0, l4_timeout_abs(t, _timeout_br()));
return L4_IPC_SEND_TIMEOUT_0;
}
/// setup_wait() for the server loop
void setup_wait(l4_utcb_t *utcb, L4::Ipc_svr::Reply_mode mode)
{
// we must handle the timer only when called after a possible reply
// otherwise we probably destroy the reply message.
if (mode == L4::Ipc_svr::Reply_separate)
{
l4_kernel_clock_t now = _now();
if (queue.timeout_expired(now))
queue.handle_expired_timeouts(now);
}
BR_MAN::setup_wait(utcb, mode);
}
/// server loop hook
L4::Ipc_svr::Reply_mode before_reply(l4_msgtag_t, l4_utcb_t *)
{
// split up reply and wait when a timeout has expired
if (queue.timeout_expired(_now()))
return L4::Ipc_svr::Reply_separate;
return L4::Ipc_svr::Reply_compound;
}
/**
* Add a timeout to the queue for time \a time.
* \param timeout The timeout object to add into the queue (must not be in
* any queue currently).
* \param time The time when the timeout shall expire.
* \pre timeout must not be in any queue.
*
* \note The timeout is automatically dequeued before the Timeout::expired()
* function is called
*/
int add_timeout(Timeout *timeout, l4_kernel_clock_t time) override
{
queue.add(timeout, time);
return 0;
}
/**
* Remove timeout from the queue.
* \param timeout The timeout object to be removed from the queue.
* \note This function may be safely called even if the timeout is not
* currently enqueued.
* \note in Timeout::expired() the timeout is already dequeued!
*/
int remove_timeout(Timeout *timeout) override
{
queue.remove(timeout);
return 0;
}
Timeout_queue queue; ///< Use this timeout queue
};
}}

View File

@@ -0,0 +1,5 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := include src
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,4 @@
PKGDIR = ../../..
L4DIR ?= $(PKGDIR)/../../..
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,13 @@
PKGDIR ?= ../../..
L4DIR ?= $(PKGDIR)/../../..
TARGET = libcxx_atexit
SYSTEMS = $(SYSTEMS_PLAIN)
SRC_CC = cxx_atexit.cc
PC_FILENAME = cxx_atexit
CXX_PKG_DIR=$(PKGDIR)
include $(PKGDIR)/lib/Makefile.inc
include $(L4DIR)/mk/lib.mk
$(GENERAL_D_LOC): $(CXX_PKG_DIR)/lib/Makefile.inc

View File

@@ -0,0 +1,57 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "cxx_atexit.h"
#define NUM_ATEXIT 64
struct __exit_handler
{
void (*f)(void *);
void *arg;
void *dso_handle;
};
static __exit_handler __atexitlist[NUM_ATEXIT];
static volatile unsigned atexit_counter;
int __cxa_atexit(void (*f)(void*), void *arg, void *dso_handle)
{
unsigned c = atexit_counter++;
if (c >= NUM_ATEXIT)
return -1;
__atexitlist[c].f = f;
__atexitlist[c].arg = arg;
__atexitlist[c].dso_handle = dso_handle;
return 0;
}
extern void *__dso_handle __attribute__((weak));
int atexit(void (*f)(void))
{
void (*fct)(void *) = reinterpret_cast<void (*)(void*)>(f);
return __cxa_atexit(fct, 0, (!&__dso_handle) ? nullptr : __dso_handle);
}
void __cxa_finalize(void *dso_handle)
{
unsigned co = atexit_counter;
if (co > NUM_ATEXIT)
co = NUM_ATEXIT;
while(co)
{
__exit_handler *h = &__atexitlist[--co];
if (h->f && (dso_handle == 0 || h->dso_handle == dso_handle))
{
h->f(h->arg);
h->f = 0;
}
}
}

View File

@@ -0,0 +1,15 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#ifndef L4_CXX_ATEXIT_H__
#define L4_CXX_ATEXIT_H__
extern "C" void __cxa_finalize(void *dso_handle);
extern "C" int __cxa_atexit(void (*f)(void*), void *arg, void *dso_handle);
extern "C" int atexit(void (*f)(void));
#endif

View File

@@ -0,0 +1,5 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := src
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,12 @@
PKGDIR ?= ../../..
L4DIR ?= $(PKGDIR)/../../..
TARGET = libsupc++-support
SRC_C = memset.c
SRC_CC = abort.cc
CXX_PKG_DIR=$(PKGDIR)
include $(PKGDIR)/lib/Makefile.inc
include $(L4DIR)/mk/lib.mk
$(GENERAL_D_LOC): $(CXX_PKG_DIR)/lib/Makefile.inc

View File

@@ -0,0 +1,20 @@
/*
* (c) 2004-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/sys/ipc.h>
#include <l4/cxx/iostream>
extern "C"
void abort(void) __attribute((noreturn));
void abort(void)
{
L4::cerr << "Aborted\n";
for (;;)
l4_ipc_sleep(L4_IPC_NEVER, l4sys_utcb());
}

View File

@@ -0,0 +1,20 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <stddef.h>
void *memset(void *s, int c, size_t n);
void *memset(void *s, int c, size_t n)
{
size_t x;
char *p = s;
for (x=0; x<n; ++x)
*p++ = c;
return s;
}

View File

@@ -0,0 +1,5 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := include
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,33 @@
PKGDIR = ../../..
L4DIR ?= $(PKGDIR)/../../..
EXTRA_TARGET := \
arith \
avl_tree \
avl_set \
avl_map \
bitmap \
dlist \
hlist \
slist \
list \
list_alloc \
minmax \
numeric \
observer \
pair \
ref_ptr \
slab_alloc \
static_container \
static_vector \
std_alloc \
std_ops \
type_traits \
type_list \
bitfield \
unique_ptr \
utils \
weak_ref \
unique_ptr_list \
ref_ptr_list
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,107 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace cxx { namespace arith {
/**
* Divides two integers, and rounds the result to the next largest integer if
* the division yields a remainder.
*
* Examples:
* 6 / 3 = 2
* 7 / 3 = 3
* -7 / 3 = -2
*
* \param n Numerator
* \param d Denominator
*
* \return ceil(n / d)
*/
template<typename N, typename D>
constexpr N
div_ceil(N const &n, D const &d)
{
// Since C++11 the "quotient is truncated towards zero (fractional part is
// discarded)". Thus a negative quotient is already ceiled, whereas a
// positive quotient is floored. Furthermore, since C++11 the sign of the
// % operator is no longer implementation defined, thus we can use n % d to
// detect if the quotient is positive (n % d >= 0) and was truncated (n % d !=
// 0). In that case, we add one to round to the next largest integer.
return n / d + (n % d > 0);
}
/**
* Computes the binary logarithm of the given number at compile time.
*
* \param val Number whose logarithm to compute, must be greater than zero.
*
* \return The binary logarithm of `val`.
*/
template< unsigned long V >
struct Ld
{
enum { value = Ld<V / 2>::value + 1 };
};
template<>
struct Ld<0>
{
enum { value = ~0UL };
};
template<>
struct Ld<1>
{
enum { value = 0 };
};
/**
* Computes the binary logarithm of the given number.
*
* \param val Number whose logarithm to compute, must be greater than zero.
*
* \return The binary logarithm of `val`.
*/
constexpr unsigned
log2u(unsigned val)
{
return 8 * sizeof(val) - __builtin_clz(val) - 1;
}
/// \copydoc log2u(unsigned)
constexpr unsigned
log2u(unsigned long val)
{
return 8 * sizeof(val) - __builtin_clzl(val) - 1;
}
/// \copydoc log2u(unsigned)
constexpr unsigned
log2u(unsigned long long val)
{
return 8 * sizeof(val) - __builtin_clzll(val) - 1;
}
/**
* Computes the ceiling of the binary logarithm of the given number.
*
* \param val Number whose ceiling of the logarithm to compute, must be
* greater than zero.
*
* \return The ceiling of the binary logarithm of `val`.
*/
template<typename T>
constexpr unsigned
log2u_ceil(T val)
{
return val == 1 ? 0 : log2u(val - 1) + 1;
}
}}

View File

@@ -0,0 +1,134 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* \brief AVL map
*/
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/std_alloc>
#include <l4/cxx/std_ops>
#include <l4/cxx/pair>
#include <l4/cxx/avl_set>
namespace cxx {
namespace Bits {
/// Key-getter for Avl_map
template<typename KEY_TYPE>
struct Avl_map_get_key
{
typedef KEY_TYPE Key_type;
template<typename NODE>
static Key_type const &key_of(NODE const *n)
{ return n->item.first; }
};
}
/**
* AVL tree based associative container.
*
* \tparam KEY_TYPE Type of the key values.
* \tparam DATA_TYPE Type of the data values.
* \tparam COMPARE Type comparison functor for the key values.
* \tparam ALLOC Type of the allocator used for the nodes.
*/
template< typename KEY_TYPE, typename DATA_TYPE,
template<typename A> class COMPARE = Lt_functor,
template<typename B> class ALLOC = New_allocator >
class Avl_map :
public Bits::Base_avl_set<Pair<KEY_TYPE, DATA_TYPE>,
COMPARE<KEY_TYPE>, ALLOC,
Bits::Avl_map_get_key<KEY_TYPE> >
{
private:
typedef Pair<KEY_TYPE, DATA_TYPE> Local_item_type;
typedef Bits::Base_avl_set<Local_item_type, COMPARE<KEY_TYPE>, ALLOC,
Bits::Avl_map_get_key<KEY_TYPE> > Base_type;
public:
/// Type of the comparison functor.
typedef COMPARE<KEY_TYPE> Key_compare;
/// Type of the key values.
typedef KEY_TYPE Key_type;
/// Type of the data values.
typedef DATA_TYPE Data_type;
/// Return type for find.
typedef typename Base_type::Node Node;
/// Type of the allocator
typedef typename Base_type::Node_allocator Node_allocator;
typedef typename Base_type::Iterator Iterator;
typedef typename Base_type::Iterator iterator;
typedef typename Base_type::Const_iterator Const_iterator;
typedef typename Base_type::Const_iterator const_iterator;
typedef typename Base_type::Rev_iterator Rev_iterator;
typedef typename Base_type::Rev_iterator reverse_iterator;
typedef typename Base_type::Const_rev_iterator Const_rev_iterator;
typedef typename Base_type::Const_rev_iterator const_reverse_iterator;
/**
* \brief Create an empty AVL-tree based map.
* \param alloc The node allocator.
*/
Avl_map(Node_allocator const &alloc = Node_allocator())
: Base_type(alloc)
{}
/**
* Insert a <key, data> pair into the map.
*
* \param key The key value.
* \param data The data value to insert.
*
* \return A pair of iterator (`first`) and return value (`second`).
* `second` will be 0 if the element was inserted into the set
* and `-#E_exist` if the key was already in the set and the
* set was therefore not updated.
* In both cases, `first` contains an iterator that points to
* the element.
* `second` may also be `-#E_nomem` when memory for the new node
* could not be allocated. `first` is then invalid.
*/
cxx::Pair<Iterator, int> insert(Key_type const &key, Data_type const &data)
{ return Base_type::insert(Pair<Key_type, Data_type>(key, data)); }
template<typename... Args>
cxx::Pair<Iterator, int> emplace(Args &&...args)
{ return Base_type::emplace(cxx::forward<Args>(args)...); }
/**
* \brief Get the data for the given key.
* \param key The key value to use for lookup.
* \pre A <key, data> pair for the given key value must exist.
*/
Data_type const &operator [] (Key_type const &key) const
{ return this->find_node(key)->second; }
/**
* Get or insert data for the given key.
*
* \param key The key value to use for lookup.
*
* \return If the item already exists, a reference to the data item.
* Otherwise a new data item is default-constructed and inserted
* under the given key before a reference is returned.
*/
Data_type &operator [] (Key_type const &key)
{
Node n = this->find_node(key);
if (n)
return const_cast<Data_type&>(n->second);
else
return insert(key, Data_type()).first->second;
}
};
}

View File

@@ -0,0 +1,481 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* \brief AVL set
*/
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Carsten Weinhold <weinhold@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/std_alloc>
#include <l4/cxx/std_ops>
#include <l4/cxx/type_traits>
#include <l4/cxx/avl_tree>
struct Avl_set_tester;
namespace cxx {
namespace Bits {
/**
* \internal
* \brief Generic iterator for the AVL-tree based set.
* \param Cmp the type of the comparison functor.
* \param Node the type of a node.
* \param Key the type of the item stored in a node.
* \param Node_op the type used to determine the direction of the iterator.
*/
template< typename Node, typename Key, typename Node_op >
class Avl_set_iter : public __Bst_iter_b<Node, Node_op>
{
private:
/// Super-class type
typedef __Bst_iter_b<Node, Node_op> Base;
/// our non-const key type
typedef typename Type_traits<Key>::Non_const_type Non_const_key;
/// our non-const iterator type
typedef Avl_set_iter<Node, Non_const_key, Node_op> Non_const_iter;
using Base::_n;
using Base::_r;
using Base::inc;
public:
/// Create an invalid iterator (end marker)
Avl_set_iter() = default;
/**
* Create an iterator for the given tree.
* \param t the root node of the tree to iterate.
*/
Avl_set_iter(Node const *t) : Base(t) {}
/**
* Create an iterator from a BST iterator.
* \prarm o The BST iterator that shall be copied.
*/
Avl_set_iter(Base const &o) : Base(o) {}
/// Allow copy of non-const iterator to const iterator versions.
Avl_set_iter(Non_const_iter const &o)
: Base(o) {}
/// Allow assignment of non-const iterator to const iterator versions.
Avl_set_iter &operator = (Non_const_iter const &o)
{ Base::operator = (o); return *this; }
/**
* \brief Dereference the iterator and get the item out of the tree.
* \return A reference to the data stored in the AVL tree.
*/
Key &operator * () const { return const_cast<Node*>(_n)->item; }
/**
* \brief Member access to the item the iterator points to.
* \return A pointer to the item in the node.
*/
Key *operator -> () const { return &const_cast<Node*>(_n)->item; }
/**
* \brief Set the iterator to the next element (pre increment).
*/
Avl_set_iter &operator ++ () { inc(); return *this; }
/**
* \brief Set the iterator to the next element (post increment).
*/
Avl_set_iter operator ++ (int)
{ Avl_set_iter tmp = *this; inc(); return tmp; }
};
/// Internal, key-getter for Avl_set nodes.
template<typename KEY_TYPE>
struct Avl_set_get_key
{
typedef KEY_TYPE Key_type;
template<typename NODE>
static Key_type const &key_of(NODE const *n)
{ return n->item; }
};
/**
* Internal: AVL set with internally managed nodes.
*
* Use Avl_set, Avl_map, or Avl_tree in applications.
*
* \tparam ITEM_TYPE The type of the items to be stored in the set.
* \tparam COMPARE The relation to define the partial order, default is
* to use operator '<'.
* \tparam ALLOC The allocator to use for the nodes of the AVL set.
* \tparam GET_KEY Sort-key getter (must provide the `Key_type` and
* sort-key for an item (of `ITEM_TYPE`).
*/
template< typename ITEM_TYPE, class COMPARE,
template<typename A> class ALLOC,
typename GET_KEY>
class Base_avl_set
{
friend struct ::Avl_set_tester;
public:
/**
* Return status constants.
*
* These constants are compatible with the L4 error codes, see
* #l4_error_code_t.
*/
enum
{
E_noent = 2, ///< Item does not exist.
E_exist = 17, ///< Item exists already.
E_nomem = 12, ///< Memory allocation failed.
E_inval = 22 ///< Internal error.
};
/// Type for the items store in the set
typedef ITEM_TYPE Item_type;
/// Key-getter type to derive the sort key of an internal node.
typedef GET_KEY Get_key;
/// Type of the sort key used for the items
typedef typename GET_KEY::Key_type Key_type;
/// Type used for const items within the set
typedef typename Type_traits<Item_type>::Const_type Const_item_type;
/// Type for the comparison functor.
typedef COMPARE Item_compare;
private:
/// Internal representation of a tree node.
class _Node : public Avl_tree_node
{
public:
/// The actual item stored in the node.
Item_type item;
_Node() = default;
_Node(Item_type const &item) : Avl_tree_node(), item(item) {}
template<typename ...ARGS>
_Node(ARGS &&...args) : Avl_tree_node(), item(cxx::forward<ARGS>(args)...)
{}
};
public:
/**
* \brief A smart pointer to a tree item.
*/
class Node
{
private:
struct No_type;
friend class Base_avl_set<ITEM_TYPE, COMPARE, ALLOC, GET_KEY>;
_Node const *_n;
explicit Node(_Node const *n) : _n(n) {}
public:
/// Default construction for NIL pointer.
Node() : _n(0) {}
/**
* Dereference the pointer.
*
* \pre Node is valid.
*/
Item_type const &operator * () { return _n->item; }
/**
* Dereferenced member access.
*
* \pre Node is valid.
*/
Item_type const *operator -> () { return &_n->item; }
/**
* \brief Validity check.
* \return false if the pointer is NIL, true if valid.
*/
bool valid() const { return _n; }
/// Cast to a real item pointer.
operator Item_type const * () { return _n ? &_n->item : 0; }
};
/// Type for the node allocator.
typedef ALLOC<_Node> Node_allocator;
private:
typedef Avl_tree<_Node, GET_KEY, COMPARE> Tree;
Tree _tree;
/// The allocator for new nodes
Node_allocator _alloc;
Base_avl_set &operator = (Base_avl_set const &) = delete;
typedef typename Tree::Fwd_iter_ops Fwd;
typedef typename Tree::Rev_iter_ops Rev;
public:
typedef typename Type_traits<Item_type>::Param_type Item_param_type;
/// Forward iterator for the set.
typedef Avl_set_iter<_Node, Item_type, Fwd> Iterator;
typedef Iterator iterator;
/// Constant forward iterator for the set.
typedef Avl_set_iter<_Node, Const_item_type, Fwd> Const_iterator;
typedef Const_iterator const_iterator;
/// Backward iterator for the set.
typedef Avl_set_iter<_Node, Item_type, Rev> Rev_iterator;
typedef Rev_iterator reverse_iterator;
/// Constant backward iterator for the set.
typedef Avl_set_iter<_Node, Const_item_type, Rev> Const_rev_iterator;
typedef Const_rev_iterator const_reverse_iterator;
/**
* \brief Create a AVL-tree based set.
* \param alloc Node allocator.
*
* Create an empty set (AVL-tree based).
*/
explicit Base_avl_set(Node_allocator const &alloc = Node_allocator())
: _tree(), _alloc(alloc)
{}
~Base_avl_set()
{
_tree.remove_all([this](_Node *n)
{
n->~_Node();
_alloc.free(n);
});
}
/**
* Create a copy of an AVL-tree based set.
*
* \param o The set to copy.
*
* Creates a deep copy of the set with all its items.
*/
inline Base_avl_set(Base_avl_set const &o);
/**
* Insert an item into the set.
*
* \param item The item to insert.
*
* \return A pair of iterator (`first`) and return value (`second`).
* `second` will be 0 if the element was inserted into the set
* and `-#E_exist` if the element was already in the set and
* the set was therefore not updated.
* In both cases, `first` contains an iterator that points to
* the element.
* `second` may also be `-#E_nomem` when memory for the node
* could not be allocated. `first` is then invalid.
*
* Insert a new item into the set, each item can only be once in
* the set.
*/
cxx::Pair<Iterator, int> insert(Item_type const &item);
template<typename... Args>
cxx::Pair<Iterator, int> emplace(Args&&... args);
/**
* Remove an item from the set.
*
* \param item The item to remove.
*
* \retval 0 Success
* \retval -E_noent Item does not exist
*/
int remove(Key_type const &item)
{
_Node *n = _tree.remove(item);
if (n)
{
n->~_Node();
_alloc.free(n);
return 0;
}
return -E_noent;
}
/**
* Erase the item with the given key.
* \param item The key of the item to remove.
*/
int erase(Key_type const &item)
{ return remove(item); }
/**
* Lookup a node equal to `item`.
*
* \param item The value to search for.
*
* \return A smart pointer to the element found.
* If no element was found the smart pointer will be invalid.
*/
Node find_node(Key_type const &item) const
{ return Node(_tree.find_node(item)); }
/**
* Find the first node greater or equal to `key`.
*
* \param key Minimum key to look for.
*
* \return Smart pointer to the first node greater or equal to `key`.
* Will be invalid if no such element was found.
*/
Node lower_bound_node(Key_type const &key) const
{ return Node(_tree.lower_bound_node(key)); }
Node lower_bound_node(Key_type &&key) const
{ return Node(_tree.lower_bound_node(key)); }
/**
* \brief Get the constant forward iterator for the first element in the set.
* \return Constant forward iterator for the first element in the set.
*/
Const_iterator begin() const { return _tree.begin(); }
/**
* \brief Get the end marker for the constant forward iterator.
* \return The end marker for the constant forward iterator.
*/
Const_iterator end() const { return _tree.end(); }
/**
* \brief Get the mutable forward iterator for the first element of the set.
* \return The mutable forward iterator for the first element of the set.
*/
Iterator begin() { return _tree.begin(); }
/**
* \brief Get the end marker for the mutable forward iterator.
* \return The end marker for mutable forward iterator.
*/
Iterator end() { return _tree.end(); }
/**
* \brief Get the constant backward iterator for the last element in the set.
* \return The constant backward iterator for the last element in the set.
*/
Const_rev_iterator rbegin() const { return _tree.rbegin(); }
/**
* \brief Get the end marker for the constant backward iterator.
* \return The end marker for the constant backward iterator.
*/
Const_rev_iterator rend() const { return _tree.rend(); }
/**
* \brief Get the mutable backward iterator for the last element of the set.
* \return The mutable backward iterator for the last element of the set.
*/
Rev_iterator rbegin() { return _tree.rbegin(); }
/**
* \brief Get the end marker for the mutable backward iterator.
* \return The end marker for mutable backward iterator.
*/
Rev_iterator rend() { return _tree.rend(); }
Const_iterator find(Key_type const &item) const
{ return _tree.find(item); }
#ifdef __DEBUG_L4_AVL
bool rec_dump(bool print)
{
return _tree.rec_dump(print);
}
#endif
};
//----------------------------------------------------------------------------
/* Implementation of AVL Tree */
/* Create a copy */
template< typename Item, class Compare, template<typename A> class Alloc, typename KEY_TYPE>
Base_avl_set<Item,Compare,Alloc,KEY_TYPE>::Base_avl_set(Base_avl_set const &o)
: _tree(), _alloc(o._alloc)
{
for (Const_iterator i = o.begin(); i != o.end(); ++i)
insert(*i);
}
/* Insert new _Node. */
template< typename Item, class Compare, template< typename A > class Alloc, typename KEY_TYPE>
Pair<typename Base_avl_set<Item,Compare,Alloc,KEY_TYPE>::Iterator, int>
Base_avl_set<Item,Compare,Alloc,KEY_TYPE>::insert(Item const &item)
{
_Node *n = _alloc.alloc();
if (!n)
return cxx::pair(end(), -E_nomem);
new (n, Nothrow()) _Node(item);
Pair<_Node *, bool> err = _tree.insert(n);
if (!err.second)
{
n->~_Node();
_alloc.free(n);
}
return cxx::pair(Iterator(typename Tree::Iterator(err.first, err.first)), err.second ? 0 : -E_exist);
}
/* In-place insert new _Node. */
template< typename Item, class Compare, template< typename A > class Alloc, typename KEY_TYPE>
template<typename... Args>
Pair<typename Base_avl_set<Item,Compare,Alloc,KEY_TYPE>::Iterator, int>
Base_avl_set<Item,Compare,Alloc,KEY_TYPE>::emplace(Args&&... args)
{
_Node *n = _alloc.alloc();
if (!n)
return cxx::pair(end(), -E_nomem);
new (n, Nothrow()) _Node(cxx::forward<Args>(args)...);
Pair<_Node *, bool> err = _tree.insert(n);
if (!err.second)
{
n->~_Node();
_alloc.free(n);
}
return cxx::pair(Iterator(typename Tree::Iterator(err.first, err.first)), err.second ? 0 : -E_exist);
}
} // namespace Bits
/**
* \brief AVL set for simple comparable items.
*
* The AVL set can store any kind of items where a partial order is defined.
* The default relation is defined by the '<' operator.
*
* \tparam ITEM_TYPE The type of the items to be stored in the set.
* \tparam COMPARE The relation to define the partial order, default is
* to use operator '<'.
* \tparam ALLOC The allocator to use for the nodes of the AVL set.
*/
template< typename ITEM_TYPE, class COMPARE = Lt_functor<ITEM_TYPE>,
template<typename A> class ALLOC = New_allocator>
class Avl_set :
public Bits::Base_avl_set<ITEM_TYPE, COMPARE, ALLOC,
Bits::Avl_set_get_key<ITEM_TYPE> >
{
private:
typedef Bits::Base_avl_set<ITEM_TYPE, COMPARE, ALLOC,
Bits::Avl_set_get_key<ITEM_TYPE> > Base;
public:
typedef typename Base::Node_allocator Node_allocator;
Avl_set() = default;
Avl_set(Node_allocator const &alloc)
: Base(alloc)
{}
};
} // namespace cxx

View File

@@ -0,0 +1,409 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* \brief AVL tree
*/
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Carsten Weinhold <weinhold@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "std_ops"
#include "pair"
#include "bits/bst.h"
#include "bits/bst_iter.h"
struct Avl_set_tester;
namespace cxx {
/**
* \brief Node of an AVL tree.
*/
class Avl_tree_node : public Bits::Bst_node
{
friend struct ::Avl_set_tester;
private:
template< typename Node, typename Get_key, typename Compare >
friend class Avl_tree;
/// Shortcut for Balance values (we use Direction for that).
typedef Bits::Direction Bal;
/// Alias for Direction.
typedef Bits::Direction Dir;
// We are a final BST node, hide interior.
/**@{*/
using Bits::Bst_node::next;
using Bits::Bst_node::next_p;
using Bits::Bst_node::rotate;
/**@}*/
/// The balance value (#Direction::N is balanced).
Bal _balance;
protected:
/// Create an uninitialized node, this is what you should do.
Avl_tree_node() = default;
private:
Avl_tree_node(Avl_tree_node const &o) = delete;
Avl_tree_node(Avl_tree_node &&o) = delete;
/// default copy for friend Avl_tree
Avl_tree_node &operator = (Avl_tree_node const &o) = default;
/// default move for friend Avl_tree
Avl_tree_node &operator = (Avl_tree_node &&o) = default;
/// Create an initialized node (for internal stuff).
explicit Avl_tree_node(bool) : Bits::Bst_node(true), _balance(Dir::N) {}
/// Double rotation of \a t.
static Bits::Bst_node *rotate2(Bst_node **t, Bal idir, Bal pre);
/// Is this subtree balanced?
bool balanced() const { return _balance == Bal::N; }
/// What is the balance of this subtree?
Bal balance() const { return _balance; }
/// Set the balance of this subtree to \a b.
void balance(Bal b) { _balance = b; }
};
/**
* \brief A generic AVL tree.
* \tparam Node The data type of the nodes (must inherit from Avl_tree_node).
* \tparam Get_key The meta function to get the key value from a node.
* The implementation uses `Get_key::key_of(ptr_to_node)`. The
* type of the key values must be defined in `Get_key::Key_type`.
* \tparam Compare Binary relation to establish a total order for the
* nodes of the tree. `Compare()(l, r)` must return true if
* the key \a l is smaller than the key \a r.
*
* This implementation does not provide any memory management. It is the
* responsibility of the caller to allocate nodes before inserting them and
* to free them when they are removed or when the tree is destroyed.
* Conversely, the caller must also ensure that nodes are removed
* from the tree before they are destroyed.
*/
template< typename Node, typename Get_key,
typename Compare = Lt_functor<typename Get_key::Key_type> >
class Avl_tree : public Bits::Bst<Node, Get_key, Compare>
{
private:
typedef Bits::Bst<Node, Get_key, Compare> Bst;
/// Hide this from possible descendants.
using Bst::_head;
/// Provide access to keys of nodes.
using Bst::k;
/// Alias type for balance values.
typedef typename Avl_tree_node::Bal Bal;
/// Alias type for Direction values.
typedef typename Avl_tree_node::Bal Dir;
Avl_tree(Avl_tree const &o) = delete;
Avl_tree &operator = (Avl_tree const &o) = delete;
Avl_tree(Avl_tree &&o) = delete;
Avl_tree &operator = (Avl_tree &&o) = delete;
public:
///@{
typedef typename Bst::Key_type Key_type;
typedef typename Bst::Key_param_type Key_param_type;
///@}
// Grab iterator types from Bst
///@{
/// Forward iterator for the tree.
typedef typename Bst::Iterator Iterator;
/// Constant forward iterator for the tree.
typedef typename Bst::Const_iterator Const_iterator;
/// Backward iterator for the tree.
typedef typename Bst::Rev_iterator Rev_iterator;
/// Constant backward iterator for the tree.
typedef typename Bst::Const_rev_iterator Const_rev_iterator;
///@}
/**
* \brief Insert a new node into this AVL tree.
* \param new_node A pointer to the new node.
* \return A pair, with \a second set to `true` and \a first pointing to
* \a new_node, on success. If there is already a node with the
* same key then \a first points to this node and \a second is 'false'.
*/
Pair<Node *, bool> insert(Node *new_node);
/**
* \brief Remove the node with \a key from the tree.
* \param key The key to the node to remove.
* \return The pointer to the removed node on success,
* or 0 if no node with the \a key exists.
*/
Node *remove(Key_param_type key);
/**
* \brief An alias for remove().
*/
Node *erase(Key_param_type key) { return remove(key); }
/// Create an empty AVL tree.
Avl_tree() = default;
/// Destroy the tree.
~Avl_tree() noexcept
{
this->remove_all([](Node *){});
}
#ifdef __DEBUG_L4_AVL
bool rec_dump(Avl_tree_node *n, int depth, int *dp, bool print, char pfx);
bool rec_dump(bool print)
{
int dp=0;
return rec_dump(static_cast<Avl_tree_node *>(_head), 0, &dp, print, '+');
}
#endif
};
//----------------------------------------------------------------------------
/* IMPLEMENTATION: Bits::__Bst_iter_b */
inline
Bits::Bst_node *
Avl_tree_node::rotate2(Bst_node **t, Bal idir, Bal pre)
{
typedef Bits::Bst_node N;
typedef Avl_tree_node A;
N *tmp[2] = { *t, N::next(*t, idir) };
*t = N::next(tmp[1], !idir);
A *n = static_cast<A*>(*t);
N::next(tmp[0], idir, N::next(n, !idir));
N::next(tmp[1], !idir, N::next(n, idir));
N::next(n, !idir, tmp[0]);
N::next(n, idir, tmp[1]);
n->balance(Bal::N);
if (pre == Bal::N)
{
static_cast<A*>(tmp[0])->balance(Bal::N);
static_cast<A*>(tmp[1])->balance(Bal::N);
return 0;
}
static_cast<A*>(tmp[pre != idir])->balance(!pre);
static_cast<A*>(tmp[pre == idir])->balance(Bal::N);
return N::next(tmp[pre == idir], !pre);
}
//----------------------------------------------------------------------------
/* Implementation of AVL Tree */
/* Insert new _Node. */
template< typename Node, typename Get_key, class Compare>
Pair<Node *, bool>
Avl_tree<Node, Get_key, Compare>::insert(Node *new_node)
{
typedef Avl_tree_node A;
typedef Bits::Bst_node N;
N **t = &_head; /* search variable */
N **s = &_head; /* node where rebalancing may occur */
Key_param_type new_key = Get_key::key_of(new_node);
// search insertion point
for (N *p; (p = *t);)
{
Dir b = this->dir(new_key, p);
if (b == Dir::N)
return pair(static_cast<Node*>(p), false);
if (!static_cast<A const *>(p)->balanced())
s = t;
t = A::next_p(p, b);
}
*static_cast<A*>(new_node) = A(true);
*t = new_node;
N *n = *s;
A *a = static_cast<A*>(n);
if (!a->balanced())
{
A::Bal b(this->greater(new_key, n));
if (a->balance() != b)
{
// ok we got in balance the shorter subtree go higher
a->balance(Bal::N);
// propagate the new balance down to the new node
n = A::next(n, b);
}
else if (b == Bal(this->greater(new_key, A::next(n, b))))
{
// left-left or right-right case -> single rotation
A::rotate(s, b);
a->balance(Bal::N);
static_cast<A*>(*s)->balance(Bal::N);
n = A::next(*s, b);
}
else
{
// need a double rotation
n = A::next(A::next(n, b), !b);
n = A::rotate2(s, b, n == new_node ? Bal::N : Bal(this->greater(new_key, n)));
}
}
for (A::Bal b; n && n != new_node; static_cast<A*>(n)->balance(b), n = A::next(n, b))
b = Bal(this->greater(new_key, n));
return pair(new_node, true);
}
/* remove an element */
template< typename Node, typename Get_key, class Compare>
inline
Node *Avl_tree<Node, Get_key, Compare>::remove(Key_param_type key)
{
typedef Avl_tree_node A;
typedef Bits::Bst_node N;
N **q = &_head; /* search variable */
N **s = &_head; /* last ('deepest') node on the search path to q
* with balance 0, at this place the rebalancing
* stops in any case */
N **t = 0;
Dir dir;
// find target node and rebalancing entry
for (N *n; (n = *q); q = A::next_p(n, dir))
{
dir = Dir(this->greater(key, n));
if (dir == Dir::L && !this->greater(k(n), key))
/* found node */
t = q;
if (!A::next(n, dir))
break;
A const *a = static_cast<A const *>(n);
if (a->balanced() || (a->balance() == !dir && A::next<A>(n, !dir)->balanced()))
s = q;
}
// nothing found
if (!t)
return 0;
A *i = static_cast<A*>(*t);
for (N *n; (n = *s); s = A::next_p(n, dir))
{
dir = Dir(this->greater(key, n));
if (!A::next(n, dir))
break;
A *a = static_cast<A*>(n);
// got one out of balance
if (a->balanced())
a->balance(!dir);
else if (a->balance() == dir)
a->balance(Bal::N);
else
{
// we need rotations to get in balance
Bal b = A::next<A>(n, !dir)->balance();
if (b == dir)
A::rotate2(s, !dir, A::next<A>(A::next(n, !dir), dir)->balance());
else
{
A::rotate(s, !dir);
if (b != Bal::N)
{
a->balance(Bal::N);
static_cast<A*>(*s)->balance(Bal::N);
}
else
{
a->balance(!dir);
static_cast<A*>(*s)->balance(dir);
}
}
if (n == i)
t = A::next_p(*s, dir);
}
}
A *n = static_cast<A*>(*q);
*t = n;
*q = A::next(n, !dir);
*n = *i;
return static_cast<Node*>(i);
}
#ifdef __DEBUG_L4_AVL
template< typename Node, typename Get_key, class Compare>
bool Avl_tree<Node, Get_key, Compare>::rec_dump(Avl_tree_node *n, int depth, int *dp, bool print, char pfx)
{
typedef Avl_tree_node A;
if (!n)
return true;
int dpx[2] = {depth,depth};
bool res = true;
res = rec_dump(A::next<A>(n, Dir::R), depth + 1, dpx + 1, print, '/');
if (print)
{
fprintf(stderr, "%2d: [%8p] b=%1d: ", depth, n, (int)n->balance().d);
for (int i = 0; i < depth; ++i)
std::cerr << " ";
std::cerr << pfx << (static_cast<Node*>(n)->item) << std::endl;
}
res = res & rec_dump(A::next<A>(n, Dir::L), depth + 1, dpx, print, '\\');
int b = dpx[1] - dpx[0];
if (b < 0)
*dp = dpx[0];
else
*dp = dpx[1];
Bal x = n->balance();
if ((b < -1 || b > 1) ||
(b == 0 && x != Bal::N) ||
(b == -1 && x != Bal::L) ||
(b == 1 && x != Bal::R))
{
if (print)
fprintf(stderr, "%2d: [%8p] b=%1d: balance error %d\n", depth, n, (int)n->balance().d, b);
return false;
}
return res;
}
#endif
}

View File

@@ -0,0 +1,33 @@
/**
* \file
* \brief Basic vector
*/
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/std_alloc>
namespace cxx {
template< typename T >
class Basic_vector
{
public:
Basic_vector(T *array, unsigned long capacity)
: _array(array), _capacity(capacity)
{
for (unsigned long i = 0; i < capacity; ++i)
new (&_array[i]) T();
}
private:
T *_array;
unsigned long _capacity;
};
};

View File

@@ -0,0 +1,308 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2012 Alexander Warg <warg@os.inf.tu-dresden.de>,
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "type_list"
/** Our C++ library. */
namespace cxx {
/**
* Definition for a member (part) of a bit field.
*
* \param T The underlying type of the bit field.
* \param LSB The least significant bit of our bits.
* \param MSB The most significant bit of our bits.
*/
template<typename T, unsigned LSB, unsigned MSB>
class Bitfield
{
private:
typedef remove_reference_t<T> Base_type;
static_assert(MSB >= LSB, "boundary mismatch in bit-field definition");
static_assert(MSB < sizeof(Base_type) * 8, "MSB outside of bit-field type");
static_assert(LSB < sizeof(Base_type) * 8, "LSB outside of bit-field type");
/**
* Get the best unsigned type for `bits`.
*
* \param BITS Number of bits to cover.
*/
template<unsigned BITS> struct Best_type
{
template< typename TY > struct Cmp { enum { value = (BITS <= sizeof(TY)*8) }; };
typedef cxx::type_list<
unsigned char,
unsigned short,
unsigned int,
unsigned long,
unsigned long long
> Unsigned_types;
typedef cxx::find_type_t<Unsigned_types, Cmp> Type;
};
public:
enum
{
Bits = MSB + 1 - LSB, ///< Number of bits
Lsb = LSB, ///< index of the LSB
Msb = MSB, ///< index of the MSB
};
/// Masks for bitswise operation on internal parts of a bitfield
enum Masks : Base_type
{
/** Mask value to get #Bits bits. */
Low_mask = static_cast<Base_type>(~0ULL) >> (sizeof(Base_type)*8 - Bits),
/** Mask value to the bits out of a `T`. */
Mask = Low_mask << Lsb,
};
/**
* Type to hold at least #Bits bits.
*
* This type can handle all values that can be stored in this part of the bit
* field.
*/
typedef typename Best_type<Bits>::Type Bits_type;
/**
* Type to hold at least #Bits + #Lsb bits.
*
* This type can handle all values that can be stored in this part of the bit
* field when they are at the target location (#Lsb bits shifted to the left).
*/
typedef typename Best_type<Bits + Lsb>::Type Shift_type;
private:
static_assert(sizeof(Bits_type)*8 >= Bits, "error finding the type to store the bits");
static_assert(sizeof(Shift_type)*8 >= Bits + Lsb, "error finding the type to keep the shifted bits");
static_assert(sizeof(Bits_type) <= sizeof(Base_type), "size mismatch for Bits_type");
static_assert(sizeof(Shift_type) <= sizeof(Base_type), "size mismatch for Shift_type");
static_assert(sizeof(Bits_type) <= sizeof(Shift_type), "size mismatch for Shift_type and Bits_type");
public:
/**
* Get the bits out of `val`.
*
* \param val The raw value of the whole bit field.
*
* \return The bits form #Lsb to #Msb shifted to the right.
*/
static constexpr Bits_type get(Shift_type val)
{ return (val >> Lsb) & Low_mask; }
/**
* Get the bits in place out of `val`.
*
* \param val The raw value of the whole bit field.
*
* \return The bits from #Lsb to #Msb (unshifted).
*
* This means other bits are masked out, however the result is not shifted to
* the right.
*/
static constexpr Base_type get_unshifted(Shift_type val)
{ return val & Mask; }
/**
* Set the bits corresponding to `val`.
*
* \param dest The current value of the whole bit field.
* \param val The value to set into the bits.
*
* \return The new value of the whole bit field.
*
* \pre `val` must not contain more than #Bits bits.
*
* \note This function does not mask `val` to the right number of bits.
*/
static constexpr Base_type set_dirty(Base_type dest, Shift_type val)
{
//assert (!(val & ~Low_mask));
return (dest & ~Mask) | (val << Lsb);
}
/**
* Set the bits corresponding to `val`.
*
* \param dest The current value of the whole bit field.
* \param val The value shifted #Lsb bits to the left that shall be set into
* the bits.
*
* \return The new value of the whole bit field.
*
* \pre `val` must not contain more than #Bits bits shifted #Lsb bits to the
* left.
*
* \note This function does not mask `val` to the right number of bits.
*/
static constexpr Base_type set_unshifted_dirty(Base_type dest, Shift_type val)
{
//assert (!(val & ~Mask));
return (dest & ~Mask) | val;
}
/**
* Set the bits corresponding to `val`.
*
* \param dest The current value of the whole bit field.
* \param val The value to set into the bits.
*
* \return The new value of the whole bit field.
*/
static Base_type set(Base_type dest, Bits_type val)
{ return set_dirty(dest, val & Low_mask); }
/**
* Set the bits corresponding to `val`.
*
* \param dest The current value of the whole bit field.
* \param val The value shifted #Lsb bits to the left that shall be set into
* the bit field.
*
* \return the new value of the whole bit field.
*/
static Base_type set_unshifted(Base_type dest, Shift_type val)
{ return set_unshifted_dirty(dest, val & Mask); }
/**
* Get the shifted bits for `val`.
*
* \param val The value to set into the bits.
*
* \return The raw bit field value.
*
* \pre `val` must not contain more than #Bits bits.
*
* \note This function does not mask `val` to the right number of bits.
*/
static constexpr Base_type val_dirty(Shift_type val) { return val << Lsb; }
/**
* Get the shifted bits for `val`.
*
* \param val The value to set into the bits.
*
* \return The raw bit field value.
*/
static constexpr Base_type val(Bits_type val) { return val_dirty(val & Low_mask); }
/**
* Get the shifted bits for `val`.
*
* \param val The value shifted #Lsb bits to the left that shall be set into
* the bits.
*
* \return The raw bit field value.
*/
static constexpr Base_type val_unshifted(Shift_type val) { return val & Mask; }
/** Internal helper type */
template< typename TT >
class Value_base
{
private:
TT v;
public:
constexpr Value_base(TT t) : v(t) {}
constexpr Bits_type get() const { return Bitfield::get(v); }
constexpr Base_type get_unshifted() const { return Bitfield::get_unshifted(v); }
void set(Bits_type val) { v = Bitfield::set(v, val); }
void set_dirty(Bits_type val) { v = Bitfield::set_dirty(v, val); }
void set_unshifted(Shift_type val) { v = Bitfield::set_unshifted(v, val); }
void set_unshifted_dirty(Shift_type val) { v = Bitfield::set_unshifted_dirty(v, val); }
};
/** Internal helper type */
template< typename TT >
class Value : public Value_base<TT>
{
public:
constexpr Value(TT t) : Value_base<TT>(t) {}
constexpr operator Bits_type () const { return this->get(); }
constexpr Value &operator = (Bits_type val) { this->set(val); return *this; }
constexpr Value &operator = (Value const &val)
{ this->set(val.get()); return *this; }
Value(Value const &) = default;
};
/** Internal helper type */
template< typename TT >
class Value_unshifted : public Value_base<TT>
{
public:
constexpr Value_unshifted(TT t) : Value_base<TT>(t) {}
constexpr operator Shift_type () const { return this->get_unshifted(); }
constexpr Value_unshifted &operator = (Shift_type val) { this->set_unshifted(val); return *this; }
constexpr Value_unshifted &operator = (Value_unshifted const &val)
{ this->set_unshifted(val.get_unshifted()); return *this; }
Value_unshifted(Value_unshifted const &) = default;
};
/** Reference type to access the bits inside a raw bit field. */
typedef Value<Base_type &> Ref;
/** Volatile reference type to access the bits inside a raw bit field. */
typedef Value<Base_type volatile &> Ref_volatile;
/** Value type to access the bits inside a raw bit field. */
typedef Value<Base_type const> Val;
/** Reference type to access the bits inside a raw bit field (in place). */
typedef Value_unshifted<Base_type &> Ref_unshifted;
/** Volatile reference type to access the bits inside a raw bit field (in place). */
typedef Value_unshifted<Base_type volatile &> Ref_unshifted_volatile;
/** Value type to access the bits inside a raw bit field (in place). */
typedef Value_unshifted<Base_type const> Val_unshifted;
};
#define CXX_BITFIELD_MEMBER(LSB, MSB, name, data_member) \
/** @{ */ \
/** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
/** Get the `name` bits (LSB to MSB) of `data_member`. */ \
constexpr typename name ## _bfm_t::Val name() const { return data_member; } \
typename name ## _bfm_t::Val name() const volatile { return data_member; } \
/** Get a reference to the `name` bits (LSB to MSB) of `data_member`. */ \
constexpr typename name ## _bfm_t::Ref name() { return data_member; } \
typename name ## _bfm_t::Ref_volatile name() volatile { return data_member; } \
/** @} */
#define CXX_BITFIELD_MEMBER_RO(LSB, MSB, name, data_member) \
/** @{ */ \
/** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
/** Get the `name` bits (LSB to MSB) of `data_member`. */ \
constexpr typename name ## _bfm_t::Val name() const { return data_member; } \
typename name ## _bfm_t::Val name() const volatile { return data_member; } \
/** @} */
#define CXX_BITFIELD_MEMBER_UNSHIFTED(LSB, MSB, name, data_member) \
/** @{ */ \
/** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
/** Get the `name` bits (LSB to MSB) of `data_member`. */ \
constexpr typename name ## _bfm_t::Val_unshifted name() const { return data_member; } \
typename name ## _bfm_t::Val_unshifted name() const volatile { return data_member; } \
/** Get a reference to the `name` bits (LSB to MSB) of `data_member`. */ \
constexpr typename name ## _bfm_t::Ref_unshifted name() { return data_member; } \
typename name ## _bfm_t::Ref_unshifted_volatile name() volatile { return data_member; } \
/** @} */
#define CXX_BITFIELD_MEMBER_UNSHIFTED_RO(LSB, MSB, name, data_member) \
/** @{ */ \
/** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
/** Get the `name` bits (LSB to MSB) of `data_member`. */ \
constexpr typename name ## _bfm_t::Val_unshifted name() const { return data_member; } \
typename name ## _bfm_t::Val_unshifted name() const volatile { return data_member; } \
/** @} */
}

View File

@@ -0,0 +1,370 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2014 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace cxx {
/**
* Basic bitmap abstraction.
*
* This abstraction keeps a pointer to a memory area that is used as bitmap.
*/
class Bitmap_base
{
protected:
/**
* Data type for each element of the bit buffer.
*/
typedef unsigned long word_type;
enum
{
W_bits = sizeof(word_type) * 8, ///< number of bits in word_type
C_bits = 8, ///< number of bits in char
};
/**
* Pointer to the buffer storing the bits.
*/
word_type *_bits;
/**
* Get the word index for the given bit.
*
* \param bit The index of the bit in question.
*
* \return the index in Bitmap_base::_bits for the given bit (bit / W_bits).
*/
static unsigned word_index(unsigned bit) { return bit / W_bits; }
/**
* Get the bit index within word_type for the given bit.
*
* \param bit The bit index in the bitmap.
*
* \return the bit index within word_type (bit % W_bits).
*/
static unsigned bit_index(unsigned bit) { return bit % W_bits; }
/**
* A writable bit in a bitmap.
*/
class Bit
{
Bitmap_base *_bm;
long _bit;
public:
Bit(Bitmap_base *bm, long bit) : _bm(bm), _bit(bit) {}
Bit &operator = (bool val) { _bm->bit(_bit, val); return *this; }
operator bool () const { return _bm->bit(_bit); }
};
public:
explicit Bitmap_base(void *bits) noexcept : _bits(reinterpret_cast<word_type *>(bits)) {}
/** Get the number of `Words` that are used for the bitmap. */
static long words(long bits) noexcept { return (bits + W_bits -1) / W_bits; }
static long bit_buffer_bytes(long bits) noexcept
{ return words(bits) * W_bits / 8; }
/** Helper abstraction for a word contained in the bitmap. */
template< long BITS >
class Word
{
public:
typedef unsigned long Type;
enum
{
Size = (BITS + W_bits - 1) / W_bits
};
};
/** Get the number of chars that are used for the bitmap. */
static long chars(long bits) noexcept
{ return (bits + C_bits -1) / C_bits; }
/** Helper abstraction for a byte contained in the bitmap. */
template< long BITS >
class Char
{
public:
typedef unsigned char Type;
enum
{
Size = (BITS + C_bits - 1) / C_bits
};
};
/**
* Set the value of bit `bit` to `on`.
*
* \param bit The number of the bit.
* \param on The boolean value that shall be assigned to the bit.
*/
void bit(long bit, bool on) noexcept;
/**
* Clear bit `bit`.
*
* \param bit The number of the bit to clear.
*/
void clear_bit(long bit) noexcept;
/**
* Clear bit `bit` atomically.
*
* Use this function for multi-threaded access to the bitmap.
*
* \param bit The number of the bit to clear.
*/
void atomic_clear_bit(long bit) noexcept;
/**
* Clear bit `bit` atomically and return old state.
*
* Use this function for multi-threaded access to the bitmap.
*
* \param bit The number of the bit to clear.
*/
word_type atomic_get_and_clear(long bit) noexcept;
/**
* Set bit `bit`.
*
* \param bit The number of the bit to set.
*/
void set_bit(long bit) noexcept;
/**
* Set bit `bit` atomically.
*
* Use this function for multi-threaded access to the bitmap.
*
* \param bit The number of the bit to set.
*/
void atomic_set_bit(long bit) noexcept;
/**
* Set bit `bit` atomically and return old state.
*
* Use this function for multi-threaded access to the bitmap.
*
* \param bit The number of the bit to set.
*/
word_type atomic_get_and_set(long bit) noexcept;
/**
* Get the truth value of a bit.
*
* \param bit The number of the bit to read.
*
* \retval 0 Bit is not set.
* \retval != 0 Bit is set.
*/
word_type bit(long bit) const noexcept;
/**
* Get the bit at index `bit`.
*
* \param bit The number of the bit to read.
*
* \retval 0 Bit is not set.
* \retval != 0 Bit is set.
*/
word_type operator [] (long bit) const noexcept
{ return this->bit(bit); }
/**
* Get the lvalue for the bit at index `bit`.
*
* \param bit The number.
*
* \return lvalue for `bit`
*/
Bit operator [] (long bit) noexcept
{ return Bit(this, bit); }
/**
* Scan for the first zero bit.
*
* \param max_bit Upper bound (exclusive) for the scanning operation.
* \param start_bit Hint at the number of the first bit to look at.
* Zero bits below `start_bit` may or may not be
* taken into account by the implementation.
*
* \retval >= 0 Number of first zero bit found.
* \retval -1 All bits between `start_bit` and `max_bit` are set.
*/
long scan_zero(long max_bit, long start_bit = 0) const noexcept;
void *bit_buffer() const noexcept { return _bits; }
protected:
static int _bzl(unsigned long w) noexcept;
};
/**
* A static bitmap.
*
* \tparam BITS The number of bits that shall be in the bitmap.
*/
template<int BITS>
class Bitmap : public Bitmap_base
{
private:
char _bits[Bitmap_base::Char<BITS>::Size];
public:
/** Create a bitmap with `BITS` bits. */
Bitmap() noexcept : Bitmap_base(_bits) {}
Bitmap(Bitmap<BITS> const &o) noexcept : Bitmap_base(_bits)
{ __builtin_memcpy(_bits, o._bits, sizeof(_bits)); }
/**
* Scan for the first zero bit.
*
* \param start_bit Hint at the number of the first bit to look at.
* Zero bits below `start_bit` may or may not be
* taken into account by the implementation.
*
* \retval >= 0 Number of first zero bit found.
* \retval -1 All bits at `start_bit` or higher are set.
*
* Compared to Bitmap_base::scan_zero(), the upper bound is set to BITS.
*/
long scan_zero(long start_bit = 0) const noexcept;
void clear_all()
{ __builtin_memset(_bits, 0, sizeof(_bits)); }
};
inline
void
Bitmap_base::bit(long bit, bool on) noexcept
{
long idx = word_index(bit);
long b = bit_index(bit);
_bits[idx] = (_bits[idx] & ~(1UL << b)) | (static_cast<unsigned long>(on) << b);
}
inline
void
Bitmap_base::clear_bit(long bit) noexcept
{
long idx = word_index(bit);
long b = bit_index(bit);
_bits[idx] &= ~(1UL << b);
}
inline
void
Bitmap_base::atomic_clear_bit(long bit) noexcept
{
long idx = word_index(bit);
long b = bit_index(bit);
word_type mask = 1UL << b;
__atomic_and_fetch(&_bits[idx], ~mask, __ATOMIC_RELAXED);
}
inline
Bitmap_base::word_type
Bitmap_base::atomic_get_and_clear(long bit) noexcept
{
long idx = word_index(bit);
long b = bit_index(bit);
word_type mask = 1UL << b;
return __atomic_fetch_and(&_bits[idx], ~mask, __ATOMIC_RELAXED) & mask;
}
inline
void
Bitmap_base::set_bit(long bit) noexcept
{
long idx = word_index(bit);
long b = bit_index(bit);
_bits[idx] |= (1UL << b);
}
inline
void
Bitmap_base::atomic_set_bit(long bit) noexcept
{
long idx = word_index(bit);
long b = bit_index(bit);
word_type mask = 1UL << b;
__atomic_or_fetch(&_bits[idx], mask, __ATOMIC_RELAXED);
}
inline
Bitmap_base::word_type
Bitmap_base::atomic_get_and_set(long bit) noexcept
{
long idx = word_index(bit);
long b = bit_index(bit);
word_type mask = 1UL << b;
return __atomic_fetch_or(&_bits[idx], mask, __ATOMIC_RELAXED) & mask;
}
inline
Bitmap_base::word_type
Bitmap_base::bit(long bit) const noexcept
{
long idx = word_index(bit);
long b = bit_index(bit);
return _bits[idx] & (1UL << b);
}
inline
int
Bitmap_base::_bzl(unsigned long w) noexcept
{
for (int i = 0; i < W_bits; ++i, w >>= 1)
{
if ((w & 1) == 0)
return i;
}
return -1;
}
inline
long
Bitmap_base::scan_zero(long max_bit, long start_bit) const noexcept
{
if (!(operator [] (start_bit)))
return start_bit;
long idx = word_index(start_bit);
max_bit -= start_bit & ~(W_bits - 1);
for (; max_bit > 0; max_bit -= W_bits, ++idx)
{
if (_bits[idx] == 0)
return idx * W_bits;
if (_bits[idx] != ~0UL)
{
long zbit = _bzl(_bits[idx]);
return zbit < max_bit ? idx * W_bits + zbit : -1;
}
}
return -1;
}
template<int BITS> inline
long
Bitmap<BITS>::scan_zero(long start_bit) const noexcept
{
return Bitmap_base::scan_zero(BITS, start_bit);
}
};

View File

@@ -0,0 +1,322 @@
// vi:ft=cpp
/**
* \file
* \brief AVL tree
*/
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Carsten Weinhold <weinhold@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "../type_traits"
#include "bst_base.h"
#include "bst_iter.h"
struct Avl_set_tester;
namespace cxx { namespace Bits {
/**
* \brief Basic binary search tree (BST).
*
* This class is intended as a base class for concrete binary search trees,
* such as an AVL tree. This class already provides the basic lookup methods
* and iterator definitions for a BST.
*/
template< typename Node, typename Get_key, typename Compare >
class Bst
{
friend struct ::Avl_set_tester;
private:
typedef Direction Dir;
/// Ops for forward iterators
struct Fwd
{
static Node *child(Node const *n, Direction d)
{ return Bst_node::next<Node>(n, d); }
static bool cmp(Node const *l, Node const *r)
{ return Compare()(Get_key::key_of(l), Get_key::key_of(r)); }
};
/// Ops for Reverse iterators
struct Rev
{
static Node *child(Node const *n, Direction d)
{ return Bst_node::next<Node>(n, !d); }
static bool cmp(Node const *l, Node const *r)
{ return Compare()(Get_key::key_of(r), Get_key::key_of(l)); }
};
public:
/// The type of key values used to generate the total order of the elements.
typedef typename Get_key::Key_type Key_type;
/// The type for key parameters.
typedef typename Type_traits<Key_type>::Param_type Key_param_type;
/// Helper for building forward iterators for different wrapper classes.
typedef Fwd Fwd_iter_ops;
/// Helper for building reverse iterators for different wrapper classes.
typedef Rev Rev_iter_ops;
/// \name Iterators
/**@{*/
/// Forward iterator.
typedef __Bst_iter<Node, Node, Fwd> Iterator;
/// Constant forward iterator.
typedef __Bst_iter<Node, Node const, Fwd> Const_iterator;
/// Backward iterator.
typedef __Bst_iter<Node, Node, Rev> Rev_iterator;
/// Constant backward.
typedef __Bst_iter<Node, Node const, Rev> Const_rev_iterator;
/**@}*/
protected:
/**
* \name Interior access for descendants.
*
* As this class is an intended base class we provide protected access
* to our interior, use 'using' to make this private in concrete
* implementations.
*/
/**@{*/
/// The head pointer of the tree.
Bst_node *_head;
/// Create an empty tree.
Bst() : _head(0) {}
/// Access the head node as object of type \a Node.
Node *head() const { return static_cast<Node*>(_head); }
/// Get the key value of \a n.
static Key_type k(Bst_node const *n)
{ return Get_key::key_of(static_cast<Node const *>(n)); }
/**
* \brief Get the direction to go from `l` to search for `r`.
* \param l is the key to look for.
* \param r is the key at the current position.
* \retval Direction::L for left
* \retval Direction::R for right
* \retval Direction::N if `l` is equal to `r`.
*/
static Dir dir(Key_param_type l, Key_param_type r)
{
Compare cmp;
Dir d(cmp(r, l));
if (d == Direction::L && !cmp(l, r))
return Direction::N;
return d;
}
/**
* \brief Get the direction to go from `l` to search for `r`.
* \param l is the key to look for.
* \param r is the node at the current position.
* \retval Direction::L For left.
* \retval Direction::R For right.
* \retval Direction::N If `l` is equal to `r`.
*/
static Dir dir(Key_param_type l, Bst_node const *r)
{ return dir(l, k(r)); }
/// Is \a l greater than \a r.
static bool greater(Key_param_type l, Key_param_type r)
{ return Compare()(r, l); }
/// Is \a l greater than \a r.
static bool greater(Key_param_type l, Bst_node const *r)
{ return greater(l, k(r)); }
/// Is \a l greater than \a r.
static bool greater(Bst_node const *l, Bst_node const *r)
{ return greater(k(l), k(r)); }
/**@}*/
/**
* Remove all elements in the subtree of head.
*
* \param head Head of the the subtree to remove
* \param callback Optional function called on each removed element.
*/
template<typename FUNC>
static void remove_tree(Bst_node *head, FUNC &&callback)
{
if (Bst_node *n = Bst_node::next(head, Dir::L))
remove_tree(n, callback);
if (Bst_node *n = Bst_node::next(head, Dir::R))
remove_tree(n, callback);
callback(static_cast<Node *>(head));
}
public:
/**
* \name Get default iterators for the ordered tree.
*/
/**@{*/
/**
* \brief Get the constant forward iterator for the first element in the set.
* \return Constant forward iterator for the first element in the set.
*/
Const_iterator begin() const { return Const_iterator(head()); }
/**
* \brief Get the end marker for the constant forward iterator.
* \return The end marker for the constant forward iterator.
*/
Const_iterator end() const { return Const_iterator(); }
/**
* \brief Get the mutable forward iterator for the first element of the set.
* \return The mutable forward iterator for the first element of the set.
*/
Iterator begin() { return Iterator(head()); }
/**
* \brief Get the end marker for the mutable forward iterator.
* \return The end marker for mutable forward iterator.
*/
Iterator end() { return Iterator(); }
/**
* \brief Get the constant backward iterator for the last element in the set.
* \return The constant backward iterator for the last element in the set.
*/
Const_rev_iterator rbegin() const { return Const_rev_iterator(head()); }
/**
* \brief Get the end marker for the constant backward iterator.
* \return The end marker for the constant backward iterator.
*/
Const_rev_iterator rend() const { return Const_rev_iterator(); }
/**
* \brief Get the mutable backward iterator for the last element of the set.
* \return The mutable backward iterator for the last element of the set.
*/
Rev_iterator rbegin() { return Rev_iterator(head()); }
/**
* \brief Get the end marker for the mutable backward iterator.
* \return The end marker for mutable backward iterator.
*/
Rev_iterator rend() { return Rev_iterator(); }
/**@}*/
/**
* \name Lookup functions.
*/
///@{
/**
* \brief find the node with the given \a key.
* \param key The key value of the element to search.
* \return A pointer to the node with the given \a key, or
* NULL if \a key was not found.
*/
Node *find_node(Key_param_type key) const;
/**
* Find the first node with a key not less than the given `key`.
*
* \param key The key used for the search.
* \return A pointer to the found node, or `NULL` if no node was found.
*/
Node *lower_bound_node(Key_param_type key) const;
/**
* \brief find the node with the given \a key.
* \param key The key value of the element to search.
* \return A valid iterator for the node with the given \a key,
* or an invalid iterator if \a key was not found.
*/
Const_iterator find(Key_param_type key) const;
/**
* Clear the tree.
*
* \param callback Optional function to be called on each removed element.
*
* The callback may delete the elements. The function guarantees that
* the elements are no longer used after the callback has been called.
*/
template<typename FUNC>
void remove_all(FUNC &&callback)
{
if (!_head)
return;
Bst_node *head = _head;
_head = 0;
remove_tree(head, cxx::forward<FUNC>(callback));
}
///@}
};
/* find an element */
template< typename Node, typename Get_key, class Compare>
inline
Node *
Bst<Node, Get_key, Compare>::find_node(Key_param_type key) const
{
Dir d;
for (Bst_node *q = _head; q; q = Bst_node::next(q, d))
{
d = dir(key, q);
if (d == Dir::N)
return static_cast<Node*>(q);
}
return 0;
}
template< typename Node, typename Get_key, class Compare>
inline
Node *
Bst<Node, Get_key, Compare>::lower_bound_node(Key_param_type key) const
{
Dir d;
Bst_node *r = 0;
for (Bst_node *q = _head; q; q = Bst_node::next(q, d))
{
d = dir(key, q);
if (d == Dir::L)
r = q; // found a node greater than key
else if (d == Dir::N)
return static_cast<Node*>(q);
}
return static_cast<Node*>(r);
}
/* find an element */
template< typename Node, typename Get_key, class Compare>
inline
typename Bst<Node, Get_key, Compare>::Const_iterator
Bst<Node, Get_key, Compare>::find(Key_param_type key) const
{
Bst_node *q = _head;
Bst_node *r = q;
for (Dir d; q; q = Bst_node::next(q, d))
{
d = dir(key, q);
if (d == Dir::N)
return Iterator(static_cast<Node*>(q), static_cast<Node *>(r));
if (d != Dir::L && q == r)
r = Bst_node::next(q, d);
}
return Iterator();
}
}}

View File

@@ -0,0 +1,128 @@
// vi:ft=cpp
/**
* \file
* \brief AVL tree
*/
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Carsten Weinhold <weinhold@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
/*
* This file contains very basic bits for implementing binary search trees
*/
namespace cxx {
/**
* \brief Internal helpers for the cxx package.
*/
namespace Bits {
/**
* \brief The direction to go in a binary search tree.
*/
struct Direction
{
/// The literal direction values.
enum Direction_e
{
L = 0, ///< Go to the left child
R = 1, ///< Go to the right child
N = 2 ///< Stop
};
unsigned char d;
/// Uninitialized direction
Direction() = default;
/// Convert a literal direction (#L, #R, #N) to an object
Direction(Direction_e d) : d(d) {}
/// Convert a boolean to a direction (false == #L, true == #R)
explicit Direction(bool b) : d(Direction_e(b)) /*d(b ? R : L)*/ {}
/**
* \brief Negate the direction.
* \note This is only defined for a current value of #L or #R
*/
Direction operator ! () const { return Direction(!d); }
/// \name Comparison operators (equality and inequality)
/**@{*/
/// Compare for equality.
bool operator == (Direction_e o) const { return d == o; }
/// Compare for inequality.
bool operator != (Direction_e o) const { return d != o; }
/// Compare for equality.
bool operator == (Direction o) const { return d == o.d; }
/// Compare for inequality.
bool operator != (Direction o) const { return d != o.d; }
/**@}*/
};
/**
* \brief Basic type of a node in a binary search tree (BST).
*/
class Bst_node
{
// all BSTs are friends
template< typename Node, typename Get_key, typename Compare >
friend class Bst;
protected:
/**
* \name Access to BST linkage.
*
* Provide access to the tree linkage to inherited classes
* Inherited nodes, such as AVL nodes should make these methods
* private via 'using'
*/
/**@{*/
/// Get next node in direction \a d.
static Bst_node *next(Bst_node const *p, Direction d)
{ return p->_c[d.d]; }
/// Set next node of \a p in direction \a d to \a n.
static void next(Bst_node *p, Direction d, Bst_node *n)
{ p->_c[d.d] = n; }
/// Get pointer to link in direction \a d.
static Bst_node **next_p(Bst_node *p, Direction d)
{ return &p->_c[d.d]; }
/// Get next node in direction \a d as type \a Node.
template< typename Node > static
Node *next(Bst_node const *p, Direction d)
{ return static_cast<Node *>(p->_c[d.d]); }
/// Rotate subtree \a t in the opposite direction of \a idir.
static void rotate(Bst_node **t, Direction idir);
/**@}*/
private:
Bst_node *_c[2];
protected:
/// Create uninitialized node
Bst_node() {}
/// Create initialized node
explicit Bst_node(bool) { _c[0] = _c[1] = 0; }
};
inline
void
Bst_node::rotate(Bst_node **t, Direction idir)
{
Bst_node *tmp = *t;
*t = next(tmp, idir);
next(tmp, idir, next(*t, !idir));
next(*t, !idir, tmp);
}
}}

View File

@@ -0,0 +1,177 @@
// vi:ft=cpp
/**
* \file
* \brief AVL tree
*/
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Carsten Weinhold <weinhold@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "bst_base.h"
namespace cxx { namespace Bits {
/**
* \internal
* \ingroup cxx_api
* \brief Generic iterator for the AVL-tree based set.
* \param Cmp the type of the comparison functor.
* \param Node the type of a node.
* \param Node_op the type used to determine the direction of the iterator.
*/
template< typename Node, typename Node_op >
class __Bst_iter_b
{
protected:
typedef Direction Dir;
Node const *_n; ///< Current node
Node const *_r; ///< Root node of current subtree
/// Create an invalid iterator, used as end marker.
__Bst_iter_b() : _n(0), _r(0) {}
/**
* \brief Create an iterator for the given AVL tree.
* \param t the root node of the tree to iterate.
* \param cmp the comparison functor for tree elements.
*/
__Bst_iter_b(Node const *t)
: _n(t), _r(_n)
{ _downmost(); }
__Bst_iter_b(Node const *t, Node const *r)
: _n(t), _r(r)
{}
/// traverse the subtree down to the leftmost/rightmost leave.
inline void _downmost();
/// Increment to the next element.
inline void inc();
public:
/// Check two iterators for equality.
bool operator == (__Bst_iter_b const &o) const { return _n == o._n; }
/// Check two iterators for non equality.
bool operator != (__Bst_iter_b const &o) const { return _n != o._n; }
};
/**
* \internal
* \ingroup cxx_api
* \brief Generic iterator for the AVL-tree based set.
* \param Node the type of a node.
* \param Node_type the type of the node to return stored in a node.
* \param Node_op the type used to determine the direction of the iterator.
*/
template< typename Node, typename Node_type, typename Node_op >
class __Bst_iter : public __Bst_iter_b<Node, Node_op>
{
private:
/// Super-class type
typedef __Bst_iter_b<Node, Node_op> Base;
using Base::_n;
using Base::_r;
using Base::inc;
public:
/// Create an invalid iterator (end marker)
__Bst_iter() {}
/**
* \brief Create an iterator for the given tree.
* \param t the root node of the tree to iterate.
* \param cmp the comparison functor for tree elements.
*/
__Bst_iter(Node const *t) : Base(t) {}
__Bst_iter(Node const *t, Node const *r) : Base(t, r) {}
// template<typename Key2>
__Bst_iter(Base const &o) : Base(o) {}
/**
* \brief Dereference the iterator and get the item out of the tree.
* \return A reference to the data stored in the AVL tree.
*/
Node_type &operator * () const { return *const_cast<Node *>(_n); }
/**
* \brief Member access to the item the iterator points to.
* \return A pointer to the item in the node.
*/
Node_type *operator -> () const { return const_cast<Node *>(_n); }
/**
* \brief Set the iterator to the next element (pre increment).
*/
__Bst_iter &operator ++ () { inc(); return *this; }
/**
* \brief Set the iterator to the next element (post increment).
*/
__Bst_iter &operator ++ (int)
{ __Bst_iter tmp = *this; inc(); return tmp; }
};
//----------------------------------------------------------------------------
/* IMPLEMENTATION: __Bst_iter_b */
template< typename Node, typename Node_op>
inline
void __Bst_iter_b<Node, Node_op>::_downmost()
{
while (_n)
{
Node *n = Node_op::child(_n, Dir::L);
if (n)
_n = n;
else
return;
}
}
template< typename Node, typename Node_op>
void __Bst_iter_b<Node, Node_op>::inc()
{
if (!_n)
return;
if (_n == _r)
{
_r = _n = Node_op::child(_r, Dir::R);
_downmost();
return;
}
if (Node_op::child(_n, Dir::R))
{
_n = Node_op::child(_n, Dir::R);
_downmost();
return;
}
Node const *q = _r;
Node const *p = _r;
while (1)
{
if (Node_op::cmp(_n, q))
{
p = q;
q = Node_op::child(q, Dir::L);
}
else if (_n == q || Node_op::child(q, Dir::R) == _n)
{
_n = p;
return;
}
else
q = Node_op::child(q, Dir::R);
}
}
}}

View File

@@ -0,0 +1,163 @@
/*
* (c) 2011 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace cxx { namespace Bits {
template< typename T >
class List_iterator_end_ptr
{
private:
template< typename U > friend class Basic_list;
static void *_end;
};
template< typename T >
void *List_iterator_end_ptr<T>::_end;
template< typename VALUE_T, typename TYPE >
struct Basic_list_policy
{
typedef VALUE_T *Value_type;
typedef VALUE_T const *Const_value_type;
typedef TYPE **Type;
typedef TYPE *Const_type;
typedef TYPE *Head_type;
typedef TYPE Item_type;
static Type next(Type c) { return &(*c)->_n; }
static Const_type next(Const_type c) { return c->_n; }
};
/// Internal: Common functions for all head-based list implementations.
template< typename POLICY >
class Basic_list
{
Basic_list(Basic_list const &) = delete;
void operator = (Basic_list const &) = delete;
public:
typedef typename POLICY::Value_type Value_type;
typedef typename POLICY::Const_value_type Const_value_type;
class Iterator
{
private:
typedef typename POLICY::Type Internal_type;
public:
typedef typename POLICY::Value_type value_type;
typedef typename POLICY::Value_type Value_type;
Value_type operator * () const { return static_cast<Value_type>(*_c); }
Value_type operator -> () const { return static_cast<Value_type>(*_c); }
Iterator operator ++ () { _c = POLICY::next(_c); return *this; }
bool operator == (Iterator const &o) const { return *_c == *o._c; }
bool operator != (Iterator const &o) const { return !operator == (o); }
Iterator() : _c(__end()) {}
private:
friend class Basic_list;
static Internal_type __end()
{
union X { Internal_type l; void **v; } z;
z.v = &Bits::List_iterator_end_ptr<void>::_end;
return z.l;
}
explicit Iterator(Internal_type i) : _c(i) {}
Internal_type _c;
};
class Const_iterator
{
private:
typedef typename POLICY::Const_type Internal_type;
public:
typedef typename POLICY::Value_type value_type;
typedef typename POLICY::Value_type Value_type;
Value_type operator * () const { return static_cast<Value_type>(_c); }
Value_type operator -> () const { return static_cast<Value_type>(_c); }
Const_iterator operator ++ () { _c = POLICY::next(_c); return *this; }
friend bool operator == (Const_iterator const &lhs, Const_iterator const &rhs)
{ return lhs._c == rhs._c; }
friend bool operator != (Const_iterator const &lhs, Const_iterator const &rhs)
{ return lhs._c != rhs._c; }
Const_iterator() {}
Const_iterator(Iterator const &o) : _c(*o) {}
private:
friend class Basic_list;
explicit Const_iterator(Internal_type i) : _c(i) {}
Internal_type _c;
};
// BSS allocation
explicit Basic_list(bool) {}
Basic_list() : _f(0) {}
Basic_list(Basic_list &&o) : _f(o._f)
{
o.clear();
}
Basic_list &operator = (Basic_list &&o)
{
_f = o._f;
o.clear();
return *this;
}
/// Check if the list is empty.
bool empty() const { return !_f; }
/// Return the first element in the list.
Value_type front() const { return static_cast<Value_type>(_f); }
/**
* Remove all elements from the list.
*
* After the operation the state of the elements is undefined.
*/
void clear() { _f = 0; }
/// Return an iterator to the beginning of the list.
Iterator begin() { return Iterator(&_f); }
/// Return a const iterator to the beginning of the list.
Const_iterator begin() const { return Const_iterator(_f); }
/**
* Return a const iterator that begins at the given element.
*
* \param c Element where the iterator should start.
*
* \pre The element `c` must already be in a list.
*/
static Const_iterator iter(Const_value_type c) { return Const_iterator(c); }
/// Return a const iterator to the end of the list.
Const_iterator end() const { return Const_iterator(nullptr); }
/// Return an iterator to the end of the list.
Iterator end() { return Iterator(); }
protected:
static typename POLICY::Type __get_internal(Iterator const &i) { return i._c; }
static Iterator __iter(typename POLICY::Type c) { return Iterator(c); }
/// Pointer to front of the list.
typename POLICY::Head_type _f;
};
}}

View File

@@ -0,0 +1,180 @@
/**
* \file
* Implementation of a list of smart-pointer-managed objects.
*/
/*
* Copyright (C) 2018, 2022, 2024 Kernkonzept GmbH.
* Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "../type_traits"
namespace cxx { namespace Bits {
/**
* List item for an arbitrary item in a Smart_ptr_list.
*
* \tparam T Type of object to be stored in the list.
* \tparam STORE_T Storage type for pointer to next item. The class must
* implement a get() function that returns a pointer to the
* stored object and destroy the stored object when the item
* goes out of scope.
*/
template <typename T, typename STORE_T>
class Smart_ptr_list_item
{
using Value_type = T;
using Storage_type = STORE_T;
template<typename U> friend class Smart_ptr_list;
Storage_type _n;
};
/**
* List of smart-pointer-managed objects.
*
* \tparam ITEM Type of the list items.
*
* The list is implemented as a single-linked list connected via smart pointers,
* so that they are automatically cleaned up when they are removed from the
* list.
*/
template <typename ITEM>
class Smart_ptr_list
{
using Value_type = typename ITEM::Value_type;
using Next_type = typename ITEM::Storage_type;
public:
class Iterator
{
public:
Iterator() : _c(nullptr) {}
Value_type *operator * () const { return _c; }
Value_type *operator -> () const { return _c; }
Iterator operator ++ ()
{
_c = _c->_n.get();
return *this;
}
bool operator == (Iterator const &o) const { return _c == o._c; }
bool operator != (Iterator const &o) const { return !operator == (o); }
private:
friend class Smart_ptr_list;
explicit Iterator(Value_type *i) : _c(i) {}
Value_type *_c;
};
class Const_iterator
{
public:
Const_iterator() : _c(nullptr) {}
Value_type const *operator * () const { return _c; }
Value_type const *operator -> () const { return _c; }
Const_iterator operator ++ ()
{
_c = _c->_n.get();
return *this;
}
bool operator == (Const_iterator const &o) const { return _c == o._c; }
bool operator != (Const_iterator const &o) const { return !operator == (o); }
private:
friend class Smart_ptr_list;
explicit Const_iterator(Value_type const *i) : _c(i) {}
Value_type const *_c;
};
Smart_ptr_list() : _b(&_f) {}
/// Add an element to the front of the list.
void push_front(Next_type &&e)
{
e->_n = cxx::move(this->_f);
this->_f = cxx::move(e);
if (_b == &_f)
_b = &(_f->_n);
}
/// Add an element to the front of the list.
void push_front(Next_type const &e)
{
e->_n = cxx::move(this->_f);
this->_f = e;
if (_b == &_f)
_b = &(_f->_n);
}
/// Add an element at the end of the list.
void push_back(Next_type &&e)
{
*_b = cxx::move(e);
_b = &((*_b)->_n);
}
/// Add an element at the end of the list.
void push_back(Next_type const &e)
{
*_b = e;
_b = &((*_b)->_n);
}
/// Return a pointer to the first element in the list.
Value_type *front() const
{ return _f.get(); }
/**
* Remove the element in front of the list and return it.
*
* \return The element that was previously in front of the list
* as a managed pointer or a nullptr-equivalent when the
* list was already empty.
*/
Next_type pop_front()
{
Next_type ret = cxx::move(_f);
if (ret)
_f = cxx::move(ret->_n);
if (!_f)
_b = &_f;
return ret;
}
/// Check if the list is empty.
bool empty() const
{ return !_f; }
Iterator begin() { return Iterator(_f.get()); }
Iterator end() { return Iterator(); }
Const_iterator begin() const { return Const_iterator(_f.get()); }
Const_iterator end() const { return Const_iterator(); }
Const_iterator cbegin() const { return const_iterator(_f.get()); }
Const_iterator cend() const { return Const_iterator(); }
private:
Next_type _f;
Next_type *_b;
};
} }

View File

@@ -0,0 +1,220 @@
// vi:ft=cpp
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace cxx {
class Null_type;
template< bool flag, typename T, typename F >
class Select
{
public:
typedef T Type;
};
template< typename T, typename F >
class Select< false, T, F >
{
public:
typedef F Type;
};
template< typename T, typename U >
class Conversion
{
typedef char S;
class B { char dummy[2]; };
static S test(U);
static B test(...);
static T make_T();
public:
enum
{
exists = sizeof(test(make_T())) == sizeof(S),
two_way = exists && Conversion<U,T>::exists,
exists_2_way = two_way,
same_type = false
};
};
template< >
class Conversion<void, void>
{
public:
enum { exists = 1, two_way = 1, exists_2_way = two_way, same_type = 1 };
};
template< typename T >
class Conversion<T, T>
{
public:
enum { exists = 1, two_way = 1, exists_2_way = two_way, same_type = 1 };
};
template< typename T >
class Conversion<void, T>
{
public:
enum { exists = 0, two_way = 0, exists_2_way = two_way, same_type = 0 };
};
template< typename T >
class Conversion<T, void>
{
public:
enum { exists = 0, two_way = 0, exists_2_way = two_way, same_type = 0 };
};
template< int I >
class Int_to_type
{
public:
enum { i = I };
};
namespace TT
{
template< typename U > class Pointer_traits
{
public:
typedef Null_type Pointee;
enum { value = false };
};
template< typename U > class Pointer_traits< U* >
{
public:
typedef U Pointee;
enum { value = true };
};
template< typename U > struct Ref_traits
{
enum { value = false };
typedef U Referee;
};
template< typename U > struct Ref_traits<U&>
{
enum { value = true };
typedef U Referee;
};
template< typename U > struct Add_ref { typedef U &Type; };
template< typename U > struct Add_ref<U&> { typedef U Type; };
template< typename U > struct PMF_traits { enum { value = false }; };
template< typename U, typename F > struct PMF_traits<U F::*>
{ enum { value = true }; };
template< typename U > class Is_unsigned { public: enum { value = false }; };
template<> class Is_unsigned<unsigned> { public: enum { value = true }; };
template<> class Is_unsigned<unsigned char> {
public: enum { value = true };
};
template<> class Is_unsigned<unsigned short> {
public: enum { value = true };
};
template<> class Is_unsigned<unsigned long> {
public: enum { value = true };
};
template<> class Is_unsigned<unsigned long long> {
public: enum { value = true };
};
template< typename U > class Is_signed { public: enum { value = false }; };
template<> class Is_signed<signed char> { public: enum { value = true }; };
template<> class Is_signed<signed short> { public: enum { value = true }; };
template<> class Is_signed<signed> { public: enum { value = true }; };
template<> class Is_signed<signed long> { public: enum { value = true }; };
template<> class Is_signed<signed long long> {
public: enum { value = true };
};
template< typename U > class Is_int { public: enum { value = false }; };
template<> class Is_int< char > { public: enum { value = true }; };
template<> class Is_int< bool > { public: enum { value = true }; };
template<> class Is_int< wchar_t > { public: enum { value = true }; };
template< typename U > class Is_float { public: enum { value = false }; };
template<> class Is_float< float > { public: enum { value = true }; };
template<> class Is_float< double > { public: enum { value = true }; };
template<> class Is_float< long double > { public: enum { value = true }; };
template<typename T> class Const_traits
{
public:
enum { value = false };
typedef T Type;
typedef const T Const_type;
};
template<typename T> class Const_traits<const T>
{
public:
enum { value = true };
typedef T Type;
typedef const T Const_type;
};
};
template< typename T >
class Type_traits
{
public:
enum
{
is_unsigned = TT::Is_unsigned<T>::value,
is_signed = TT::Is_signed<T>::value,
is_int = TT::Is_int<T>::value,
is_float = TT::Is_float<T>::value,
is_pointer = TT::Pointer_traits<T>::value,
is_pointer_to_member = TT::PMF_traits<T>::value,
is_reference = TT::Ref_traits<T>::value,
is_scalar = is_unsigned || is_signed || is_int || is_pointer
|| is_pointer_to_member || is_reference,
is_fundamental = is_unsigned || is_signed || is_float
|| Conversion<T, void>::same_type,
is_const = TT::Const_traits<T>::value,
alignment =
(sizeof(T) >= sizeof(unsigned long)
? sizeof(unsigned long)
: (sizeof(T) >= sizeof(unsigned)
? sizeof(unsigned)
: (sizeof(T) >= sizeof(short)
? sizeof(short)
: 1)))
};
typedef typename Select<is_scalar, T, typename TT::Add_ref<typename TT::Const_traits<T>::Const_type>::Type>::Type Param_type;
typedef typename TT::Pointer_traits<T>::Pointee Pointee_type;
typedef typename TT::Ref_traits<T>::Referee Referee_type;
typedef typename TT::Const_traits<T>::Type Non_const_type;
typedef typename TT::Const_traits<T>::Const_type Const_type;
static unsigned long align(unsigned long a)
{
return (a + static_cast<unsigned long>(alignment) - 1UL)
& ~(static_cast<unsigned long>(alignment) - 1UL);
}
};
};

View File

@@ -0,0 +1,439 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2011 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace cxx {
class D_list_item
{
public:
constexpr D_list_item() : _dli_next(nullptr), _dli_prev(nullptr) {}
D_list_item(D_list_item const &) = delete;
void operator = (D_list_item const &) = delete;
private:
friend struct D_list_item_policy;
D_list_item *_dli_next, *_dli_prev;
};
struct D_list_item_policy
{
typedef D_list_item Item;
static D_list_item *&prev(D_list_item *e) { return e->_dli_prev; }
static D_list_item *&next(D_list_item *e) { return e->_dli_next; }
static D_list_item *prev(D_list_item const *e) { return e->_dli_prev; }
static D_list_item *next(D_list_item const *e) { return e->_dli_next; }
};
template< typename T >
struct Sd_list_head_policy
{
typedef T *Head_type;
static T *head(Head_type h) { return h; }
static void set_head(Head_type &h, T *v) { h = v; }
};
template<
typename T,
typename C = D_list_item_policy
>
class D_list_cyclic
{
protected:
template< typename VALUE, typename ITEM >
class __Iterator
{
public:
typedef VALUE *Value_type;
typedef VALUE *value_type;
__Iterator() {}
bool operator == (__Iterator const &o) const
{ return _c == o._c; }
bool operator != (__Iterator const &o) const
{ return _c != o._c; }
__Iterator &operator ++ ()
{
_c = C::next(_c);
return *this;
}
__Iterator &operator -- ()
{
_c = C::prev(_c);
return *this;
}
Value_type operator * () const { return static_cast<Value_type>(_c); }
Value_type operator -> () const { return static_cast<Value_type>(_c); }
protected:
friend class D_list_cyclic;
explicit __Iterator(ITEM *s) : _c(s) {}
ITEM *_c;
};
public:
typedef T *Value_type;
typedef T *value_type;
typedef __Iterator<T, typename C::Item> Iterator;
typedef Iterator Const_iterator;
static void remove(T *e)
{
C::next(C::prev(e)) = C::next(e);
C::prev(C::next(e)) = C::prev(e);
C::next(e) = nullptr;
}
static Iterator erase(Iterator const &e)
{
typename C::Item *n = C::next(*e);
remove(*e);
return __iter(n);
}
static Iterator iter(T const *e) { return Iterator(const_cast<T*>(e)); }
static bool in_list(T const *e) { return C::next(const_cast<T*>(e)); }
static bool has_sibling(T const *e) { return C::next(const_cast<T*>(e)) != e; }
static Iterator insert_after(T *e, Iterator const &pos)
{
C::prev(e) = pos._c;
C::next(e) = C::next(pos._c);
C::prev(C::next(pos._c)) = e;
C::next(pos._c) = e;
return pos;
}
static Iterator insert_before(T *e, Iterator const &pos)
{
C::next(e) = pos._c;
C::prev(e) = C::prev(pos._c);
C::next(C::prev(pos._c)) = e;
C::prev(pos._c) = e;
return pos;
}
protected:
static void self_insert(typename C::Item *e)
{ C::next(e) = C::prev(e) = e; }
static void remove_last(T *e)
{ C::next(e) = nullptr; }
/**
* Splice the elements of `other_list` into the list before `pos`.
*
* \pre Must not be called for an empty `other_list`!
* \post Leaves the `other_list` in an invalid state.
*/
static void splice_heads(Const_iterator pos, typename C::Item *other_list)
{
typename C::Item *ins_next = pos._c;
typename C::Item *ins_prev = C::prev(pos._c);
typename C::Item *other_head = C::next(other_list);
typename C::Item *other_tail = C::prev(other_list);
C::next(ins_prev) = other_head;
C::prev(other_head) = ins_prev;
C::prev(ins_next) = other_tail;
C::next(other_tail) = ins_next;
}
static Iterator __iter(typename C::Item *e) { return Iterator(e); }
};
template<
typename T,
typename C = D_list_item_policy,
typename H = Sd_list_head_policy<T>,
bool BSS = false
>
class Sd_list : public D_list_cyclic<T, C>
{
private:
typedef D_list_cyclic<T, C> Base;
public:
class Iterator : public Base::Iterator
{
public:
Iterator &operator ++ ()
{
if (this->_c)
Base::Iterator::operator ++ ();
if (this->_c == _h)
this->_c = nullptr;
return *this;
}
Iterator &operator -- () = delete;
private:
friend class Sd_list;
explicit Iterator(T *h) : Base::Iterator(h), _h(h) {}
typename C::Item *_h;
};
class R_iterator : public Base::Iterator
{
public:
R_iterator &operator ++ ()
{
if (this->_c)
Base::Iterator::operator -- ();
if (this->_c == _h)
this->_c = nullptr;
return *this;
}
R_iterator &operator -- () = delete;
private:
friend class Sd_list;
explicit R_iterator(T *h) : Base::Iterator(h), _h(h) {}
typename C::Item *_h;
};
//typedef typename Base::Iterator Iterator;
enum Pos
{ Back, Front };
Sd_list()
{
if (!BSS)
H::set_head(_f, nullptr);
}
bool empty() const { return !H::head(_f); }
T *front() const { return H::head(_f); }
void remove(T *e)
{
T *h = H::head(_f);
if (e == C::next(e)) // must be the last
{
Base::remove_last(e);
H::set_head(_f, nullptr);
return;
}
if (e == H::head(_f))
H::set_head(_f, static_cast<T*>(C::next(h)));
Base::remove(e);
}
Iterator erase(Iterator const &e)
{
Iterator next = e;
++next;
remove(*e);
return next;
}
void push(T *e, Pos pos)
{
T *h = H::head(_f);
if (!h)
{
Base::self_insert(e);
H::set_head(_f, e);
}
else
{
Base::insert_before(e, this->iter(h));
if (pos == Front)
H::set_head(_f, e);
}
}
void push_back(T *e) { push(e, Back); }
void push_front(T *e) { push(e, Front); }
void rotate_to(T *h) { H::set_head(_f, h); }
typename H::Head_type const &head() const { return _f; }
typename H::Head_type &head() { return _f; }
Iterator begin() { return Iterator(H::head(_f)); }
Iterator end() { return Iterator(nullptr); }
R_iterator rbegin()
{
if (head())
return R_iterator(static_cast<T*>(C::prev(H::head(_f))));
return R_iterator(nullptr);
}
R_iterator rend() { return R_iterator(nullptr); }
private:
Sd_list(Sd_list const &);
void operator = (Sd_list const &);
typename H::Head_type _f;
};
template<
typename T,
typename C = D_list_item_policy,
bool BSS = false
>
class D_list : public D_list_cyclic<T, C>
{
private:
typedef D_list_cyclic<T, C> Base;
typedef typename C::Item Internal_type;
public:
enum Pos
{ Back, Front };
typedef typename Base::Iterator Iterator;
typedef typename Base::Const_iterator Const_iterator;
typedef T* value_type;
typedef T* Value_type;
D_list() { this->self_insert(&_h); }
~D_list() { clear(); }
D_list(D_list &&o)
{
if (o.empty())
{
this->self_insert(&_h);
}
else
{
Internal_type *p = C::prev(&o._h);
Internal_type *n = C::next(&o._h);
C::prev(&_h) = p;
C::next(&_h) = n;
C::next(p) = &_h;
C::prev(n) = &_h;
o.self_insert(&o._h);
}
}
D_list &operator=(D_list &&o)
{
if (&o == this)
return *this;
clear();
if (!o.empty())
{
Internal_type *p = C::prev(&o._h);
Internal_type *n = C::next(&o._h);
C::prev(&_h) = p;
C::next(&_h) = n;
C::next(p) = &_h;
C::prev(n) = &_h;
o.self_insert(&o._h);
}
}
D_list(D_list const &) = delete;
void operator = (D_list const &) = delete;
void splice(Const_iterator pos, D_list &&other)
{
if (other.empty())
return;
Base::splice_heads(pos, &other._h);
other.self_insert(&other._h);
}
bool empty() const { return C::next(&_h) == &_h; }
static void remove(T *e) { Base::remove(e); }
Iterator erase(Iterator const &e) { return Base::erase(e); }
void clear()
{
// Just clear the _dli_next pointers of all elements. It is the indicator
// that an element is not on a list.
Internal_type *i = C::next(&_h);
while (i != &_h)
{
Internal_type *d = i;
i = C::next(i);
C::next(d) = nullptr;
}
this->self_insert(&_h);
}
void push(T *e, Pos pos)
{
if (pos == Front)
Base::insert_after(e, end());
else
Base::insert_before(e, end());
}
void push_back(T *e) { push(e, Back); }
void push_front(T *e) { push(e, Front); }
/**
* Remove element from the end of the list and return it.
*
* \pre The list is not empty.
*/
T *pop_back()
{
T *ret = *(end()--);
remove(ret);
return ret;
}
/**
* Remove element from the beginning of the list and return it.
*
* \pre The list is not empty.
*/
T *pop_front()
{
T *ret = *begin();
remove(ret);
return ret;
}
Iterator begin() const { return this->__iter(C::next(const_cast<Internal_type *>(&_h))); }
Iterator end() const { return this->__iter(const_cast<Internal_type *>(&_h)); }
bool has_sibling(T const *e)
{
return C::next(const_cast<T*>(e)) != &_h
|| C::prev(const_cast<T*>(e)) != &_h;
}
private:
Internal_type _h;
};
}

View File

@@ -0,0 +1,271 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2011 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "bits/list_basics.h"
#include "type_traits"
namespace cxx {
/**
* Basic element type for a double-linked H_list.
*
* \tparam ELEM_TYPE Base class of the list element.
*/
template<typename ELEM_TYPE>
class H_list_item_t
{
public:
/**
* Constructor.
*
* Creates an element that is not in any list.
*/
H_list_item_t() : _n(0), _pn(0) {}
/**
* Destructor.
*
* Automatically removes the element from any list it still might be
* enchained in.
*/
~H_list_item_t() noexcept { l_remove(); }
private:
H_list_item_t(H_list_item_t const &) = delete;
template<typename T, typename P> friend class H_list;
template<typename T, typename X> friend struct Bits::Basic_list_policy;
void l_remove() noexcept
{
if (!_pn)
return;
*_pn = _n;
if (_n)
_n->_pn = _pn;
_pn = 0;
}
H_list_item_t *_n, **_pn;
};
/** Untyped list item. */
typedef H_list_item_t<void> H_list_item;
/**
* General double-linked list of unspecified cxx::H_list_item elements.
*
* Most of the time, you want to use H_list_t.
*/
template< typename T, typename POLICY = Bits::Basic_list_policy< T, H_list_item> >
class H_list : public Bits::Basic_list<POLICY>
{
private:
typedef typename POLICY::Item_type Item;
typedef Bits::Basic_list<POLICY> Base;
H_list(H_list const &);
void operator = (H_list const &);
public:
typedef typename Base::Iterator Iterator;
// BSS allocation
explicit H_list(bool x) : Base(x) {}
H_list() : Base() {}
/**
* Return an iterator for an arbitrary list element
*
* \param c List element to start the iteration.
*
* \return A mutable forward iterator.
*
* \pre The element must be in a list.
*/
static Iterator iter(T *c) { return Base::__iter(c->Item::_pn); }
/// Check if the given element is currently part of a list.
static bool in_list(T const *e) { return e->Item::_pn; }
/// Add element to the front of the list.
void add(T *e)
{
if (this->_f)
this->_f->_pn = &e->Item::_n;
e->Item::_n = this->_f;
e->Item::_pn = &this->_f;
this->_f = static_cast<Item*>(e);
}
/// Add element to the front of the list.
void push_front(T *e) { add(e); }
/**
* Remove and return the head element of the list.
*
* \pre The list must not be empty or the behaviour will be undefined.
*/
T *pop_front()
{
T *r = this->front();
remove(r);
return r;
}
/**
* Insert an element at the iterator position.
*
* \param e New Element to be inserted
* \param pred Iterator pointing to the element after which the
* element will be inserted. If end() is given, the
* element will be inserted at the beginning of the queue.
*
* \return Iterator pointing to the newly inserted element.
*/
Iterator insert(T *e, Iterator const &pred)
{
Item **x = &this->_f;
if (pred != Base::end())
x = &(*pred)->_n;
e->Item::_n = *x;
if (*x)
(*x)->_pn = &(e->Item::_n);
e->Item::_pn = x;
*x = static_cast<Item*>(e);
return iter(e);
}
/**
* Insert an element after the iterator position.
*
* \param e New element to be inserted.
* \param pred Iterator pointing to the element after which the
* element will be inserted. Must not be end().
*
* \return Iterator pointing to the newly inserted element.
*
* \pre The list must not be empty.
*/
static Iterator insert_after(T *e, Iterator const &pred)
{
Item **x = &(*pred)->_n;
e->Item::_n = *x;
if (*x)
(*x)->_pn = &(e->Item::_n);
e->Item::_pn = x;
*x = static_cast<Item*>(e);
return iter(e);
}
/**
* Insert an element before the iterator position.
*
* \param e New element to be inserted.
* \param succ Iterator pointing to the element before which the
* element will be inserted. Must not be end().
*/
static void insert_before(T *e, Iterator const &succ)
{
Item **x = Base::__get_internal(succ);
e->Item::_n = *x;
e->Item::_pn = x;
if (*x)
(*x)->_pn = &e->Item::_n;
*x = static_cast<Item*>(e);
}
/**
* Replace an element in a list with a new element.
*
* \param p Element in list to be replaced.
* \param e Replacement element, must not yet be in a list.
*
* \pre `p` and `e` must not be NULL.
*
* After the operation the p element is no longer in the
* list and may be reused.
*/
static void replace(T *p, T *e)
{
e->Item::_n = p->Item::_n;
e->Item::_pn = p->Item::_pn;
*(p->Item::_pn) = static_cast<Item*>(e);
if (e->Item::_n)
e->Item::_n->_pn = &(e->Item::_n);
p->Item::_pn = 0;
}
/**
* Remove the given element from its list.
*
* \param e Element to be removed. Must be in a list.
*/
static void remove(T *e)
{ e->Item::l_remove(); }
/**
* Remove the element at the given iterator position.
*
* \param e Iterator pointing to the element to be removed. Must not
* point to end().
*
* \return New iterator pointing to the element after the removed one.
*
* \note The hlist implementation guarantees that the original iterator
* is still valid after the element has been removed. In fact, the
* iterator returned is the same as the one supplied in the `e`
* parameter.
*/
static Iterator erase(Iterator const &e)
{ e->Item::l_remove(); return e; }
};
/**
* Double-linked list of typed H_list_item_t elements.
*
* \note H_lists are not self-cleaning. Elements that are still chained
* during destruction are not removed and will therefore be in an
* undefined state after the destruction.
*/
template< typename T >
struct H_list_t : H_list<T, Bits::Basic_list_policy< T, H_list_item_t<T> > >
{
H_list_t() = default;
H_list_t(bool b)
: H_list<T, Bits::Basic_list_policy< T, H_list_item_t<T> > >(b)
{};
};
template< typename T >
class H_list_bss : public H_list<T>
{
public:
H_list_bss() : H_list<T>(true) {}
};
template< typename T >
class H_list_t_bss : public H_list_t<T>
{
public:
H_list_t_bss() : H_list_t<T>(true) {}
};
}

View File

@@ -0,0 +1,411 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/type_traits>
#include <l4/cxx/std_alloc>
#include <l4/cxx/std_ops>
namespace cxx {
/*
* Classes: List_item, List<D, Alloc>
*/
/**
* \ingroup cxx_api
* \brief Basic list item.
*
* Basic item that can be member of a doubly linked, cyclic list.
*/
class List_item
{
public:
/**
* \brief Iterator for a list of ListItem-s.
*
* The Iterator iterates till it finds the first element again.
*/
class Iter
{
public:
Iter(List_item *c, List_item *f) noexcept : _c(c), _f(f) {}
Iter(List_item *f = 0) noexcept : _c(f), _f(f) {}
List_item *operator * () const noexcept { return _c; }
List_item *operator -> () const noexcept { return _c; }
Iter &operator ++ () noexcept
{
if (!_f)
_c = 0;
else
_c = _c->get_next_item();
if (_c == _f)
_c = 0;
return *this;
}
Iter operator ++ (int) noexcept
{ Iter o = *this; operator ++ (); return o; }
Iter &operator -- () noexcept
{
if (!_f)
_c = 0;
else
_c = _c->get_prev_item();
if (_c == _f)
_c = 0;
return *this;
}
Iter operator -- (int) noexcept
{ Iter o = *this; operator -- (); return o; }
/** Remove item pointed to by iterator, and return pointer to element. */
List_item *remove_me() noexcept
{
if (!_c)
return 0;
List_item *l = _c;
operator ++ ();
l->remove_me();
if (_f == l)
_f = _c;
return l;
}
private:
List_item *_c, *_f;
};
/**
* \brief Iterator for derived classes from ListItem.
*
* Allows direct access to derived classes by * operator.
*
* Example:
* class Foo : public ListItem
* {
* public:
* typedef T_iter<Foo> Iter;
* ...
* };
*/
template< typename T, bool Poly = false>
class T_iter : public Iter
{
private:
static bool const P = !Conversion<const T*, const List_item *>::exists
|| Poly;
static List_item *cast_to_li(T *i, Int_to_type<true>) noexcept
{ return dynamic_cast<List_item*>(i); }
static List_item *cast_to_li(T *i, Int_to_type<false>) noexcept
{ return i; }
static T *cast_to_type(List_item *i, Int_to_type<true>) noexcept
{ return dynamic_cast<T*>(i); }
static T *cast_to_type(List_item *i, Int_to_type<false>) noexcept
{ return static_cast<T*>(i); }
public:
template< typename O >
explicit T_iter(T_iter<O> const &o) noexcept
: Iter(o) { dynamic_cast<T*>(*o); }
//TIter(CListItem *f) : Iter(f) {}
T_iter(T *f = 0) noexcept : Iter(cast_to_li(f, Int_to_type<P>())) {}
T_iter(T *c, T *f) noexcept
: Iter(cast_to_li(c, Int_to_type<P>()),
cast_to_li(f, Int_to_type<P>()))
{}
inline T *operator * () const noexcept
{ return cast_to_type(Iter::operator * (),Int_to_type<P>()); }
inline T *operator -> () const noexcept
{ return operator * (); }
T_iter<T, Poly> operator ++ (int) noexcept
{ T_iter<T, Poly> o = *this; Iter::operator ++ (); return o; }
T_iter<T, Poly> operator -- (int) noexcept
{ T_iter<T, Poly> o = *this; Iter::operator -- (); return o; }
T_iter<T, Poly> &operator ++ () noexcept
{ Iter::operator ++ (); return *this; }
T_iter<T, Poly> &operator -- () noexcept
{ Iter::operator -- (); return *this; }
inline T *remove_me() noexcept;
};
List_item() noexcept : _n(this), _p(this) {}
protected:
List_item(List_item const &) noexcept : _n(this), _p(this) {}
public:
/** Get previous item. */
List_item *get_prev_item() const noexcept { return _p; }
/** Get next item. */
List_item *get_next_item() const noexcept { return _n; }
/** Insert item p before this item. */
void insert_prev_item(List_item *p) noexcept
{
p->_p->_n = this;
List_item *pr = p->_p;
p->_p = _p;
_p->_n = p;
_p = pr;
}
/** Insert item p after this item. */
void insert_next_item(List_item *p) noexcept
{
p->_p->_n = _n;
p->_p = this;
_n->_p = p;
_n = p;
}
/** Remove this item from the list. */
void remove_me() noexcept
{
if (_p != this)
{
_p->_n = _n;
_n->_p = _p;
}
_p = _n = this;
}
/**
* \brief Append item to a list.
*
* Convenience function for empty-head corner case.
* \param head Pointer to the current list head.
* \param p Pointer to new item.
* \return the pointer to the new head.
*/
template< typename C, typename N >
static inline C *push_back(C *head, N *p) noexcept;
/**
* \brief Prepend item to a list.
*
* Convenience function for empty-head corner case.
* \param head pointer to the current list head.
* \param p pointer to new item.
* \return the pointer to the new head.
*/
template< typename C, typename N >
static inline C *push_front(C *head, N *p) noexcept;
/**
* \brief Remove item from a list.
*
* Convenience function for remove-head corner case.
* \param head pointer to the current list head.
* \param p pointer to the item to remove.
* \return the pointer to the new head.
*/
template< typename C, typename N >
static inline C *remove(C *head, N *p) noexcept;
private:
List_item *_n, *_p;
};
/* IMPLEMENTATION -----------------------------------------------------------*/
template< typename C, typename N >
C *List_item::push_back(C *h, N *p) noexcept
{
if (!p)
return h;
if (!h)
return p;
h->insert_prev_item(p);
return h;
}
template< typename C, typename N >
C *List_item::push_front(C *h, N *p) noexcept
{
if (!p)
return h;
if (h)
h->insert_prev_item(p);
return p;
}
template< typename C, typename N >
C *List_item::remove(C *h, N *p) noexcept
{
if (!p)
return h;
if (!h)
return 0;
if (h == p)
{
if (p == p->_n)
h = 0;
else
h = static_cast<C*>(p->_n);
}
p->remove_me();
return h;
}
template< typename T, bool Poly >
inline
T *List_item::T_iter<T, Poly>::remove_me() noexcept
{ return cast_to_type(Iter::remove_me(), Int_to_type<P>()); }
template< typename T >
class T_list_item : public List_item
{
public:
T *next() const { return static_cast<T*>(List_item::get_next_item()); }
T *prev() const { return static_cast<T*>(List_item::get_prev_item()); }
};
template< typename LI >
class L_list
{
private:
LI *_h;
public:
L_list() : _h(0) {}
void push_front(LI *e) { _h = LI::push_front(_h, e); }
void push_back(LI *e) { _h = LI::push_back(_h, e); }
void insert_before(LI *e, LI *p)
{
p->insert_prev_item(e);
if (_h == p)
_h = e;
}
void insert_after(LI *e, LI *p) { p->insert_next_item(e); }
void remove(LI *e)
{ _h = LI::remove(_h, e); }
LI *head() const { return _h; }
};
/**
* Doubly linked list, with internal allocation.
* Container for items of type D, implemented by a doubly linked list.
* Alloc defines the allocator policy.
*/
template< typename D, template<typename A> class Alloc = New_allocator >
class List
{
private:
class E : public List_item
{
public:
E(D const &d) noexcept : data(d) {}
D data;
};
public:
class Node : private E
{};
typedef Alloc<Node> Node_alloc;
/**
* Iterator.
* Forward and backward iteratable.
*/
class Iter
{
private:
List_item::T_iter<E> _i;
public:
Iter(E *e) noexcept : _i(e) {}
D &operator * () const noexcept { return (*_i)->data; }
D &operator -> () const noexcept { return (*_i)->data; }
Iter operator ++ (int) noexcept
{ Iter o = *this; operator ++ (); return o; }
Iter operator -- (int) noexcept
{ Iter o = *this; operator -- (); return o; }
Iter &operator ++ () noexcept { ++_i; return *this; }
Iter &operator -- () noexcept { --_i; return *this; }
/** operator for testing validity (syntactically equal to pointers) */
operator E* () const noexcept { return *_i; }
};
List(Alloc<Node> const &a = Alloc<Node>()) noexcept : _h(0), _l(0), _a(a) {}
/** Add element at the end of the list. */
void push_back(D const &d) noexcept
{
void *n = _a.alloc();
if (!n) return;
_h = E::push_back(_h, new (n) E(d));
++_l;
}
/** Add element at the beginning of the list. */
void push_front(D const &d) noexcept
{
void *n = _a.alloc();
if (!n) return;
_h = E::push_front(_h, new (n) E(d));
++_l;
}
/** Remove element pointed to by the iterator. */
void remove(Iter const &i) noexcept
{ E *e = i; _h = E::remove(_h, e); --_l; _a.free(e); }
/** Get the length of the list. */
unsigned long size() const noexcept { return _l; }
/** Random access. Complexity is O(n). */
D const &operator [] (unsigned long idx) const noexcept
{ Iter i = _h; for (; idx && *i; ++i, --idx) { } return *i; }
/** Random access. Complexity is O(n). */
D &operator [] (unsigned long idx) noexcept
{ Iter i = _h; for (; idx && *i; ++i, --idx) { } return *i; }
/** Get iterator for the list elements. */
Iter items() noexcept { return Iter(_h); }
private:
E *_h;
unsigned _l;
Alloc<Node> _a;
};
};

View File

@@ -0,0 +1,524 @@
// vim:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/arith>
#include <l4/cxx/minmax>
#include <l4/sys/consts.h>
namespace cxx {
/**
* Standard list-based allocator.
*/
class List_alloc
{
private:
friend class List_alloc_sanity_guard;
struct Mem_block
{
Mem_block *next;
unsigned long size;
};
Mem_block *_first;
inline void check_overlap(void *, unsigned long );
inline void sanity_check_list(char const *, char const *);
inline void merge();
public:
/**
* Initializes an empty list allocator.
*
* \note To initialize the allocator with available memory
* use the #free() function.
*/
List_alloc() : _first(0) {}
/**
* Return a free memory block to the allocator.
*
* \param block Pointer to memory block.
* \param size Size of memory block.
* \param initial_free Set to true for putting fresh memory
* to the allocator. This will enforce alignment on that
* memory.
*
* \pre `block` must not be NULL.
* \pre `2 * sizeof(void *)` <= `size` <= `~0UL - 32`.
*/
inline void free(void *block, unsigned long size, bool initial_free = false);
/**
* Allocate a memory block.
*
* \param size Size of the memory block.
* \param align Alignment constraint.
* \param lower Lower bound of the physical region the memory block should be
* allocated from.
* \param upper Upper bound of the physical region the memory block should be
* allocated from, value is inclusive.
*
* \return Pointer to memory block
*
* \pre 0 < `size` <= `~0UL - 32`.
*/
inline void *alloc(unsigned long size, unsigned long align,
unsigned long lower = 0, unsigned long upper = ~0UL);
/**
* Allocate a memory block of `min` <= size <= `max`.
*
* \param min Minimal size to allocate (in bytes).
* \param[in,out] max Maximum size to allocate (in bytes). The actual
* allocated size is returned here.
* \param align Alignment constraint.
* \param granularity Granularity to use for the allocation (power
* of 2).
* \param lower Lower bound of the physical region the memory
* block should be allocated from.
* \param upper Upper bound of the physical region the memory
* block should be allocated from, value is
* inclusive.
*
* \return Pointer to memory block
*
* \pre 0 < `min` <= `~0UL - 32`.
* \pre 0 < `max`.
*/
inline void *alloc_max(unsigned long min, unsigned long *max,
unsigned long align, unsigned granularity,
unsigned long lower = 0, unsigned long upper = ~0UL);
/**
* Get the amount of available memory.
*
* \return Available memory in bytes
*/
inline unsigned long avail();
template <typename DBG>
void dump_free_list(DBG &out);
};
#if !defined (CXX_LIST_ALLOC_SANITY)
class List_alloc_sanity_guard
{
public:
List_alloc_sanity_guard(List_alloc *, char const *)
{}
};
void
List_alloc::check_overlap(void *, unsigned long )
{}
void
List_alloc::sanity_check_list(char const *, char const *)
{}
#else
class List_alloc_sanity_guard
{
private:
List_alloc *a;
char const *func;
public:
List_alloc_sanity_guard(List_alloc *a, char const *func)
: a(a), func(func)
{ a->sanity_check_list(func, "entry"); }
~List_alloc_sanity_guard()
{ a->sanity_check_list(func, "exit"); }
};
void
List_alloc::check_overlap(void *b, unsigned long s)
{
unsigned long const mb_align = (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1;
if ((unsigned long)b & mb_align)
{
L4::cerr << "List_alloc(FATAL): trying to free unaligned memory: "
<< b << " align=" << arith::Ld<sizeof(Mem_block)>::value << "\n";
}
Mem_block *c = _first;
for (;c ; c = c->next)
{
unsigned long x_s = (unsigned long)b;
unsigned long x_e = x_s + s;
unsigned long b_s = (unsigned long)c;
unsigned long b_e = b_s + c->size;
if ((x_s >= b_s && x_s < b_e)
|| (x_e > b_s && x_e <= b_e)
|| (b_s >= x_s && b_s < x_e)
|| (b_e > x_s && b_e <= x_e))
{
L4::cerr << "List_alloc(FATAL): trying to free memory that "
"is already free: \n ["
<< (void*)x_s << '-' << (void*)x_e << ") overlaps ["
<< (void*)b_s << '-' << (void*)b_e << ")\n";
}
}
}
void
List_alloc::sanity_check_list(char const *func, char const *info)
{
Mem_block *c = _first;
for (;c ; c = c->next)
{
if (c->next)
{
if (c >= c->next)
{
L4::cerr << "List_alloc(FATAL): " << func << '(' << info
<< "): list order violation\n";
}
if (((unsigned long)c) + c->size > (unsigned long)c->next)
{
L4::cerr << "List_alloc(FATAL): " << func << '(' << info
<< "): list order violation\n";
}
}
}
}
#endif
void
List_alloc::merge()
{
List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__);
Mem_block *c = _first;
while (c && c->next)
{
unsigned long f_start = reinterpret_cast<unsigned long>(c);
unsigned long f_end = f_start + c->size;
unsigned long n_start = reinterpret_cast<unsigned long>(c->next);
if (f_end == n_start)
{
c->size += c->next->size;
c->next = c->next->next;
continue;
}
c = c->next;
}
}
void
List_alloc::free(void *block, unsigned long size, bool initial_free)
{
List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__);
unsigned long const mb_align = (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1;
if (initial_free)
{
// enforce alignment constraint on initial memory
unsigned long nblock = (reinterpret_cast<unsigned long>(block) + mb_align)
& ~mb_align;
size = (size - (nblock - reinterpret_cast<unsigned long>(block)))
& ~mb_align;
block = reinterpret_cast<void*>(nblock);
}
else
// blow up size to the minimum aligned size
size = (size + mb_align) & ~mb_align;
check_overlap(block, size);
Mem_block **c = &_first;
Mem_block *next = 0;
if (*c)
{
while (*c && *c < block)
c = &(*c)->next;
next = *c;
}
*c = reinterpret_cast<Mem_block*>(block);
(*c)->next = next;
(*c)->size = size;
merge();
}
void *
List_alloc::alloc_max(unsigned long min, unsigned long *max, unsigned long align,
unsigned granularity, unsigned long lower,
unsigned long upper)
{
List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__);
unsigned char const mb_bits = arith::Ld<sizeof(Mem_block)>::value;
unsigned long const mb_align = (1UL << mb_bits) - 1;
// blow minimum up to at least the minimum aligned size of a Mem_block
min = l4_round_size(min, mb_bits);
// truncate maximum to at least the size of a Mem_block
*max = l4_trunc_size(*max, mb_bits);
// truncate maximum size according to granularity
*max = *max & ~(granularity - 1UL);
if (min > *max)
return 0;
unsigned long almask = align ? (align - 1UL) : 0;
// minimum alignment is given by the size of a Mem_block
if (almask < mb_align)
almask = mb_align;
Mem_block **c = &_first;
Mem_block **fit = 0;
unsigned long max_fit = 0;
unsigned long a_lower = (lower + almask) & ~almask;
for (; *c; c = &(*c)->next)
{
// address of free memory block
unsigned long n_start = reinterpret_cast<unsigned long>(*c);
// block too small, next
// XXX: maybe we can skip this and just do the test below
if ((*c)->size < min)
continue;
// block outside region, next
if (upper < n_start || a_lower > n_start + (*c)->size)
continue;
// aligned start address within the free block
unsigned long a_start = (n_start + almask) & ~almask;
// check if aligned start address is behind the block, next
if (a_start - n_start >= (*c)->size)
continue;
a_start = a_start < a_lower ? a_lower : a_start;
// end address would overflow, next
if (min > ~0UL - a_start)
continue;
// block outside region, next
if (a_start + min - 1UL > upper)
continue;
// remaining size after subtracting the padding for the alignment
unsigned long r_size = (*c)->size - a_start + n_start;
// upper limit can limit maximum size
if (a_start + r_size - 1UL > upper)
r_size = upper - a_start + 1UL;
// round down according to granularity
r_size &= ~(granularity - 1UL);
// block too small
if (r_size < min)
continue;
if (r_size >= *max)
{
fit = c;
max_fit = *max;
break;
}
if (r_size > max_fit)
{
max_fit = r_size;
fit = c;
}
}
if (fit)
{
unsigned long n_start = reinterpret_cast<unsigned long>(*fit);
unsigned long a_lower = (lower + almask) & ~almask;
unsigned long a_start = (n_start + almask) & ~almask;
a_start = a_start < a_lower ? a_lower : a_start;
unsigned long r_size = (*fit)->size - a_start + n_start;
if (a_start > n_start)
{
(*fit)->size -= r_size;
fit = &(*fit)->next;
}
else
*fit = (*fit)->next;
*max = max_fit;
if (r_size == max_fit)
return reinterpret_cast<void *>(a_start);
Mem_block *m = reinterpret_cast<Mem_block*>(a_start + max_fit);
m->next = *fit;
m->size = r_size - max_fit;
*fit = m;
return reinterpret_cast<void *>(a_start);
}
return 0;
}
void *
List_alloc::alloc(unsigned long size, unsigned long align, unsigned long lower,
unsigned long upper)
{
List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__);
unsigned long const mb_align
= (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1;
// blow up size to the minimum aligned size
size = (size + mb_align) & ~mb_align;
unsigned long almask = align ? (align - 1UL) : 0;
// minimum alignment is given by the size of a Mem_block
if (almask < mb_align)
almask = mb_align;
Mem_block **c = &_first;
unsigned long a_lower = (lower + almask) & ~almask;
for (; *c; c=&(*c)->next)
{
// address of free memory block
unsigned long n_start = reinterpret_cast<unsigned long>(*c);
// block too small, next
// XXX: maybe we can skip this and just do the test below
if ((*c)->size < size)
continue;
// block outside region, next
if (upper < n_start || a_lower > n_start + (*c)->size)
continue;
// aligned start address within the free block
unsigned long a_start = (n_start + almask) & ~almask;
// block too small after alignment, next
if (a_start - n_start >= (*c)->size)
continue;
a_start = a_start < a_lower ? a_lower : a_start;
// end address would overflow, next
if (size > ~0UL - a_start)
continue;
// block outside region, next
if (a_start + size - 1UL > upper)
continue;
// remaining size after subtracting the padding
// for the alignment
unsigned long r_size = (*c)->size - a_start + n_start;
// block too small
if (r_size < size)
continue;
if (a_start > n_start)
{
// have free space before the allocated block
// shrink the block and set c to the next pointer of that
// block
(*c)->size -= r_size;
c = &(*c)->next;
}
else
// drop the block, c remains the next pointer of the
// previous block
*c = (*c)->next;
// allocated the whole remaining space
if (r_size == size)
return reinterpret_cast<void*>(a_start);
// add a new free block behind the allocated block
Mem_block *m = reinterpret_cast<Mem_block*>(a_start + size);
m->next = *c;
m->size = r_size - size;
*c = m;
return reinterpret_cast<void *>(a_start);
}
return 0;
}
unsigned long
List_alloc::avail()
{
List_alloc_sanity_guard __attribute__((unused)) guard(this, __FUNCTION__);
Mem_block *c = _first;
unsigned long a = 0;
while (c)
{
a += c->size;
c = c->next;
}
return a;
}
template <typename DBG>
void
List_alloc::dump_free_list(DBG &out)
{
Mem_block *c = _first;
while (c)
{
unsigned sz;
const char *unit;
if (c->size < 1024)
{
sz = c->size;
unit = "Byte";
}
else if (c->size < 1 << 20)
{
sz = c->size >> 10;
unit = "KB";
}
else
{
sz = c->size >> 20;
unit = "MB";
}
out.printf("%12p - %12p (%u %s)\n", c,
reinterpret_cast<char *>(c) + c->size - 1, sz, unit);
c = c->next;
}
}
}

View File

@@ -0,0 +1,111 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "type_traits"
/**
* \ingroup cxx_api
* \brief Various kinds of C++ utilities.
*/
namespace cxx
{
// trivial, used to terminate the variadic recursion
template<typename A>
constexpr A const &
min(A const &a)
{ return a; }
/**
* \ingroup cxx_api
* Get the minimum of `a1` and `a2` upt to `aN`.
*
* \param a1 The first value.
* \param a2 The second value.
* \param ...a Arbitrary number of additional parameters.
*
* Matches with automatic argument type deduction.
*/
template<typename A, typename ...ARGS>
constexpr A const &
min(A const &a1, A const &a2, ARGS const &...a)
{
return min((a1 <= a2) ? a1 : a2, a...);
}
/**
* \ingroup cxx_api
* Get the minimum of `a1` and `a2` upt to `aN`.
*
* \param a1 The first value.
* \param a2 The second value.
* \param ...a Arbitrary number of additional parameters.
*
* Matches with explicit template type A.
*/
template<typename A, typename ...ARGS>
constexpr A const &
min(cxx::identity_t<A> const &a1,
cxx::identity_t<A> const &a2,
ARGS const &...a)
{
return min<A>((a1 <= a2) ? a1 : a2, a...);
}
// trivial, used to terminate the variadic recursion
template<typename A>
constexpr A const &
max(A const &a)
{ return a; }
/**
* \ingroup cxx_api
* Get the maximum of `a1` and `a2` upt to `aN`.
*
* \param a1 The first value.
* \param a2 The second value.
* \param ...a Arbitrary number of additional parameters.
*
* Matches with automatic argument type deduction.
*/
template<typename A, typename ...ARGS>
constexpr A const &
max(A const &a1, A const &a2, ARGS const &...a)
{ return max((a1 >= a2) ? a1 : a2, a...); }
/**
* \ingroup cxx_api
* Get the maximum of `a1` and `a2` upt to `aN`.
*
* \param a1 The first value.
* \param a2 The second value.
* \param ...a Arbitrary number of additional parameters.
*
* Matches with explicit template type A.
*/
template<typename A, typename ...ARGS>
constexpr A const &
max(cxx::identity_t<A> const &a1,
cxx::identity_t<A> const &a2,
ARGS const &...a)
{
return max<A>((a1 >= a2) ? a1 : a2, a...);
}
/**
* \ingroup cxx_api
* \brief Limit \a v to the range given by \a lo and \a hi.
* \param v The value to clamp.
* \param lo The lower boundary to clamp \a v to.
* \param hi The upper boundary to clamp \a v to.
*/
template< typename T1 >
inline
T1 clamp(T1 v, T1 lo, T1 hi)
{ return min(hi, max(lo, v)); }
};

View File

@@ -0,0 +1,78 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Martin Decky <martin.decky@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/type_traits>
namespace cxx {
/**
* Compute the greatest common divisor of two unsigned values.
*
* This uses the Euclidean modulo algorithm.
*
* \note Contrary to the C++17 specification, this implementation requires the
* same unsigned type of both arguments and returns the same type (not
* a common type). This should be fine for most practical use cases.
*
* \note Contrary to the C++17 specification, this implementation does not
* accept signed arguments and negative literals.
*
* \tparam T Unsigned integer type of the values.
* \param a First input.
* \param b Second input.
*
* \return Greatest common divisor of the input values.
*/
template <typename T>
constexpr T gcd(T a, T b)
{
static_assert(Type_traits<T>::is_unsigned, "Type must be unsigned");
while (b != 0)
{
T remainder = a % b;
a = b;
b = remainder;
}
return a;
}
/**
* Compute the least common multiple of two unsigned values.
*
* This uses the greatest common divisor to compute the least common
* multiple.
*
* \note Contrary to the C++17 specification, this implementation requires the
* same unsigned type of both arguments and returns the same type (not
* a common type). This should be fine for most practical use cases.
*
* \note Contrary to the C++17 specification, this implementation does not
* accept signed arguments and negative literals.
*
* \tparam T Unsigned integer type of the values.
* \param a First input.
* \param b Second input.
*
* \return Least common multiple of the input values.
*/
template <typename T>
constexpr T lcm(T a, T b)
{
static_assert(Type_traits<T>::is_unsigned, "Type must be unsigned");
if (a == 0 || b == 0)
return 0;
return (a / gcd(a, b)) * b;
}
}

View File

@@ -0,0 +1,32 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/hlist>
namespace cxx {
class Observer : public H_list_item
{
public:
virtual void notify() = 0;
};
class Notifier : public H_list<Observer>
{
public:
void notify()
{
for (Iterator i = begin(); i != end(); ++i)
i->notify();
}
};
}

View File

@@ -0,0 +1,104 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* \brief Pair implementation
*/
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/type_traits>
namespace cxx {
/**
* \ingroup cxx_api
* \brief Pair of two values.
*
* Standard container for a pair of values.
* \param First Type of the first value.
* \param Second Type of the second value.
*/
template< typename First, typename Second >
struct Pair
{
/// Type of first value.
typedef First First_type;
/// Type of second value.
typedef Second Second_type;
/// First value.
First first;
/// Second value.
Second second;
/**
* \brief Create a pair from the two values.
* \param first The first value.
* \param second The second value.
*/
template<typename A1, typename A2>
Pair(A1 &&first, A2 &&second)
: first(cxx::forward<A1>(first)), second(cxx::forward<A2>(second)) {}
/**
* \brief Create a pair, default constructing the second value.
* \param first The first value.
*/
template<typename A1>
Pair(A1 &&first)
: first(cxx::forward<A1>(first)), second() {}
/// Default construction.
Pair() = default;
};
template< typename F, typename S >
Pair<F,S> pair(F const &f, S const &s)
{ return cxx::Pair<F,S>(f,s); }
/**
* \brief Comparison functor for Pair.
* \param Cmp Comparison functor for the first value of the pair.
* \param Typ The pair type.
*
* This functor can be used to compare Pair values with respect to the
* first value.
*/
template< typename Cmp, typename Typ >
class Pair_first_compare
{
private:
Cmp const &_cmp;
public:
/**
* \brief Construction.
* \param cmp The comparison functor used for the first value.
*/
Pair_first_compare(Cmp const &cmp = Cmp()) : _cmp(cmp) {}
/**
* \brief Do the comparison based on the first value.
* \param l The lefthand value.
* \param r The righthand value.
*/
bool operator () (Typ const &l, Typ const &r) const
{ return _cmp(l.first,r.first); }
};
}
template< typename OS, typename A, typename B >
inline
OS &operator << (OS &os, cxx::Pair<A,B> const &p)
{
os << p.first << ';' << p.second;
return os;
}

View File

@@ -0,0 +1,345 @@
// vim:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "type_traits"
#include <stddef.h>
#include <l4/sys/compiler.h>
namespace cxx {
template< typename T >
struct Default_ref_counter
{
void h_drop_ref(T *p) noexcept
{
if (p->remove_ref() == 0)
delete p;
}
void h_take_ref(T *p) noexcept
{
p->add_ref();
}
};
struct Ref_ptr_base
{
enum Default_value
{ Nil = 0 };
};
template<typename T, template< typename X > class CNT = Default_ref_counter>
class Weak_ptr;
/**
* A reference-counting pointer with automatic cleanup.
*
* \tparam T Type of object the pointer points to.
* \tparam CNT Type of management class that manages the life time of
* the object.
*
* This pointer is similar to the standard C++-11 shared_ptr but it does
* the reference counting directly in the object being pointed to, so that
* no additional management structures need to be allocated from the heap.
*
* Classes that use this pointer type must implement two functions:
*
* int remove_ref()
*
* is called when a reference is removed and must return 0 when there are no
* further references to the object.
*
* void add_ref()
*
* is called when another ref_ptr to the object is created.
*
* Ref_obj provides a simple implementation of this interface from which
* classes may inherit.
*/
template <
typename T = void,
template< typename X > class CNT = Default_ref_counter
>
class Ref_ptr : public Ref_ptr_base, private CNT<T>
{
private:
typedef decltype(nullptr) Null_type;
typedef Weak_ptr<T, CNT> Wp;
public:
/// Default constructor creates a pointer with no managed object.
Ref_ptr() noexcept : _p(0) {}
Ref_ptr(Ref_ptr_base::Default_value v)
: _p(reinterpret_cast<T*>(static_cast<unsigned long>(v))) {}
/**
* Create a shared pointer from a weak pointer.
*
* Increases references.
*/
Ref_ptr(Wp const &o) noexcept : _p(o.ptr())
{ __take_ref(); }
/// allow creation from `nullptr`
Ref_ptr(decltype(nullptr) n) noexcept : _p(n) {}
/**
* Create a shared pointer from a raw pointer.
*
* In contrast to C++11 shared_ptr it is safe to use this constructor
* multiple times and have the same reference counter.
*/
template<typename X>
explicit Ref_ptr(X *o) noexcept : _p(o)
{ __take_ref(); }
/**
* Create a shared pointer from a raw pointer without creating a new
* reference.
*
* \param o Pointer to the object.
* \param d Dummy parameter to select this constructor at compile time.
* The value may be true or false.
*
* This is the counterpart to release().
*/
Ref_ptr(T *o, [[maybe_unused]] bool d) noexcept : _p(o) { }
/**
* Return a raw pointer to the object this shared pointer points to.
*
* This does not release the pointer or decrease the reference count.
*/
T *get() const noexcept
{
return _p;
}
/** \copydoc get() */
T *ptr() const noexcept
{
return _p;
}
/**
* Release the shared pointer without removing the reference.
*
* \return A raw pointer to the managed object.
*
*/
T *release() noexcept
{
T *p = _p;
_p = 0;
return p;
}
~Ref_ptr() noexcept
{ __drop_ref(); }
template<typename OT>
Ref_ptr(Ref_ptr<OT, CNT> const &o) noexcept
{
_p = o.ptr();
__take_ref();
}
Ref_ptr(Ref_ptr<T> const &o) noexcept
{
_p = o._p;
__take_ref();
}
template< typename OT >
void operator = (Ref_ptr<OT> const &o) noexcept
{
__drop_ref();
_p = o.ptr();
__take_ref();
}
void operator = (Ref_ptr<T> const &o) noexcept
{
if (&o == this)
return;
__drop_ref();
_p = o._p;
__take_ref();
}
void operator = (Null_type) noexcept
{
__drop_ref();
_p = 0;
}
template<typename OT>
Ref_ptr(Ref_ptr<OT, CNT> &&o) noexcept
{ _p = o.release(); }
Ref_ptr(Ref_ptr<T> &&o) noexcept
{ _p = o.release(); }
template< typename OT >
void operator = (Ref_ptr<OT> &&o) noexcept
{
__drop_ref();
_p = o.release();
}
void operator = (Ref_ptr<T> &&o) noexcept
{
if (&o == this)
return;
__drop_ref();
_p = o.release();
}
[[nodiscard]] explicit operator bool () const noexcept { return _p; }
T *operator -> () const noexcept
{ return _p; }
[[nodiscard]] bool operator == (Ref_ptr const &o) const noexcept
{ return _p == o._p; }
[[nodiscard]] bool operator != (Ref_ptr const &o) const noexcept
{ return _p != o._p; }
[[nodiscard]] bool operator < (Ref_ptr const &o) const noexcept
{ return _p < o._p; }
[[nodiscard]] bool operator <= (Ref_ptr const &o) const noexcept
{ return _p <= o._p; }
[[nodiscard]] bool operator > (Ref_ptr const &o) const noexcept
{ return _p > o._p; }
[[nodiscard]] bool operator >= (Ref_ptr const &o) const noexcept
{ return _p >= o._p; }
[[nodiscard]] bool operator == (T const *o) const noexcept
{ return _p == o; }
[[nodiscard]] bool operator < (T const *o) const noexcept
{ return _p < o; }
[[nodiscard]] bool operator <= (T const *o) const noexcept
{ return _p <= o; }
[[nodiscard]] bool operator > (T const *o) const noexcept
{ return _p > o; }
[[nodiscard]] bool operator >= (T const *o) const noexcept
{ return _p >= o; }
private:
void __drop_ref() noexcept
{
if (_p)
static_cast<CNT<T>*>(this)->h_drop_ref(_p);
}
void __take_ref() noexcept
{
if (_p)
static_cast<CNT<T>*>(this)->h_take_ref(_p);
}
T *_p;
};
template<typename T, template< typename X > class CNT>
class Weak_ptr
{
private:
struct Null_type;
typedef Ref_ptr<T, CNT> Rp;
public:
Weak_ptr() = default;
Weak_ptr(decltype(nullptr)) : _p(nullptr) {}
// backwards 0 ctor
explicit Weak_ptr(int x) noexcept
L4_DEPRECATED("Use initialization from 'nullptr'")
: _p(nullptr)
{ if (x != 0) __builtin_trap(); }
Weak_ptr(Rp const &o) noexcept : _p(o.ptr()) {}
explicit Weak_ptr(T *o) noexcept : _p(o) {}
template<typename OT>
Weak_ptr(Weak_ptr<OT, CNT> const &o) noexcept : _p(o.ptr()) {}
Weak_ptr(Weak_ptr<T, CNT> const &o) noexcept : _p(o._p) {}
Weak_ptr<T, CNT> &operator = (const Weak_ptr<T, CNT> &o) = default;
T *get() const noexcept { return _p; }
T *ptr() const noexcept { return _p; }
T *operator -> () const noexcept { return _p; }
operator Null_type const * () const noexcept
{ return reinterpret_cast<Null_type const*>(_p); }
private:
T *_p;
};
template<typename OT, typename T> inline
Ref_ptr<OT> ref_ptr_static_cast(Ref_ptr<T> const &o)
{ return ref_ptr(static_cast<OT*>(o.ptr())); }
template< typename T >
inline Ref_ptr<T> ref_ptr(T *t)
{ return Ref_ptr<T>(t); }
template< typename T >
inline Weak_ptr<T> weak_ptr(T *t)
{ return Weak_ptr<T>(t); }
class Ref_obj
{
private:
mutable int _ref_cnt;
public:
Ref_obj() : _ref_cnt(0) {}
void add_ref() const noexcept { ++_ref_cnt; }
int remove_ref() const noexcept { return --_ref_cnt; }
};
template< typename T, typename... Args >
Ref_ptr<T>
make_ref_obj(Args &&... args)
{ return cxx::Ref_ptr<T>(new T(cxx::forward<Args>(args)...)); }
template<typename T, typename U>
Ref_ptr<T>
dynamic_pointer_cast(Ref_ptr<U> const &p) noexcept
{
// our constructor from a naked pointer increments the counter
return Ref_ptr<T>(dynamic_cast<T *>(p.get()));
}
template<typename T, typename U>
Ref_ptr<T>
static_pointer_cast(Ref_ptr<U> const &p) noexcept
{
// our constructor from a naked pointer increments the counter
return Ref_ptr<T>(static_cast<T *>(p.get()));
}
}

View File

@@ -0,0 +1,34 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* Implementation of a list of ref-ptr-managed objects.
*/
/*
* Copyright (C) 2018, 2022, 2024 Kernkonzept GmbH.
* Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/ref_ptr>
#include "bits/smart_ptr_list.h"
namespace cxx {
/// Item for list linked with cxx::Ref_ptr.
template <typename T>
using Ref_ptr_list_item = Bits::Smart_ptr_list_item<T, cxx::Ref_ptr<T> >;
/// Item for list linked via cxx::Ref_ptr with default refence counting.
template <typename T>
struct Ref_obj_list_item : public Ref_ptr_list_item<T>, public cxx::Ref_obj {};
/**
* Single-linked list where elements are connected via a cxx::Ref_ptr.
*/
template <typename T>
using Ref_ptr_list = Bits::Smart_ptr_list<Ref_ptr_list_item<T> >;
}

View File

@@ -0,0 +1,485 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/std_alloc>
#include <l4/cxx/hlist>
#include <l4/sys/consts.h>
namespace cxx {
/**
* \ingroup cxx_api
* Basic slab allocator.
*
* \tparam Obj_size The size of the objects managed by the allocator (in bytes).
* \tparam Slab_size The size of a slab (in bytes).
* \tparam Max_free The maximum number of free slabs. When this limit is reached
* slabs are freed, provided that the backend allocator
* supports allocated memory to be freed.
* \tparam Alloc The backend allocator used to allocate slabs.
*/
template< int Obj_size, int Slab_size = L4_PAGESIZE,
int Max_free = 2, template<typename A> class Alloc = New_allocator >
class Base_slab
{
private:
struct Free_o
{
Free_o *next;
};
protected:
struct Slab_i;
private:
struct Slab_head : public H_list_item
{
/// Number of free objects in the slab.
unsigned num_free;
/// Pointer to the first free object in the slab.
Free_o *free;
/// Pointer to the slab cache (instance of the slab allocator).
Base_slab<Obj_size, Slab_size, Max_free, Alloc> *cache;
inline Slab_head() noexcept : num_free(0), free(0), cache(0)
{}
};
// In an empty or partially filled slab, each free object stores a pointer to
// the next free object. Thus, the size of an object needs to be at least the
// size of a pointer.
static_assert(Obj_size >= sizeof(void *),
"Object size must be at least the size of a pointer.");
static_assert(Obj_size <= Slab_size - sizeof(Slab_head),
"Object_size exceeds slab capability.");
public:
enum
{
/// Size of an object.
object_size = Obj_size,
/// Size of a slab.
slab_size = Slab_size,
/// Objects per slab.
objects_per_slab = (Slab_size - sizeof(Slab_head)) / object_size,
/// Maximum number of free slabs.
max_free_slabs = Max_free,
};
protected:
struct Slab_store
{
char _o[slab_size - sizeof(Slab_head)];
Free_o *object(unsigned obj) noexcept
{ return reinterpret_cast<Free_o*>(_o + object_size * obj); }
};
/// Type of a slab
struct Slab_i : public Slab_store, public Slab_head
{};
public:
/// Type of the backend allocator.
typedef Alloc<Slab_i> Slab_alloc;
typedef void Obj_type;
private:
/// Allocator used for slabs.
Slab_alloc _alloc;
/// Number of empty slabs.
unsigned _num_free;
/// Total number of slabs.
unsigned _num_slabs;
/// List of full slabs.
H_list<Slab_i> _full_slabs;
/// List of partial slabs.
H_list<Slab_i> _partial_slabs;
/// List of empty slabs.
H_list<Slab_i> _empty_slabs;
/// Add a new slab.
void add_slab(Slab_i *s) noexcept
{
s->num_free = objects_per_slab;
s->cache = this;
//L4::cerr << "Slab: " << this << "->add_slab(" << s << ", size="
// << slab_size << "):" << " f=" << s->object(0) << '\n';
// initialize free list
Free_o *f = s->free = s->object(0);
for (unsigned i = 1; i < objects_per_slab; ++i)
{
f->next = s->object(i);
f = f->next;
}
f->next = 0;
// insert slab into cache's list
_empty_slabs.push_front(s);
++_num_slabs;
++_num_free;
}
/// Grow the slab cache, by adding a new slab.
bool grow() noexcept
{
Slab_i *s = _alloc.alloc();
if (!s)
return false;
new (s, cxx::Nothrow()) Slab_i();
add_slab(s);
return true;
}
/**
* Shrink the slab cache by freeing empty slabs.
*
* The flow of memory from the slab cache back to the system is regulated via
* the backend allocator's flag `can_free`. If this flag is set to true, the
* slab cache retains at maximum Max_free empty slabs; empty slabs exceeding
* this limit are freed. If `can_free` is set to false, the shrink operation
* does nothing.
*/
void shrink() noexcept
{
if (!_alloc.can_free)
return;
while (!_empty_slabs.empty() && _num_free > max_free_slabs)
{
Slab_i *s = _empty_slabs.front();
_empty_slabs.remove(s);
--_num_free;
--_num_slabs;
_alloc.free(s);
}
}
public:
Base_slab(Slab_alloc const &alloc = Slab_alloc()) noexcept
: _alloc(alloc), _num_free(0), _num_slabs(0), _full_slabs(),
_partial_slabs(), _empty_slabs()
{}
~Base_slab() noexcept
{
while (!_empty_slabs.empty())
{
Slab_i *o = _empty_slabs.front();
_empty_slabs.remove(o);
_alloc.free(o);
}
while (!_partial_slabs.empty())
{
Slab_i *o = _partial_slabs.front();
_partial_slabs.remove(o);
_alloc.free(o);
}
while (!_full_slabs.empty())
{
Slab_i *o = _full_slabs.front();
_full_slabs.remove(o);
_alloc.free(o);
}
}
/**
* Allocate a new object.
*
* \return A pointer to the new object if the allocation succeeds, or 0 on
* failure to acquire memory from the backend allocator when the slab
* cache memory is already exhausted.
*
* \note The user is responsible for initializing the object.
*/
void *alloc() noexcept
{
H_list<Slab_i> *free = &_partial_slabs;
if (free->empty())
free = &_empty_slabs;
if (free->empty() && !grow())
return 0;
Slab_i *s = free->front();
Free_o *o = s->free;
s->free = o->next;
if (free == &_empty_slabs)
{
_empty_slabs.remove(s);
--_num_free;
}
--(s->num_free);
if (!s->free)
{
_partial_slabs.remove(s);
_full_slabs.push_front(s);
}
else if (free == &_empty_slabs)
_partial_slabs.push_front(s);
//L4::cerr << this << "->alloc(): " << o << ", of " << s << '\n';
return o;
}
/**
* Free the given object (`_o`).
*
* \pre The object must have been allocated with this allocator.
*/
void free(void *_o) noexcept
{
if (!_o)
return;
unsigned long addr = reinterpret_cast<unsigned long>(_o);
// find out the slab the object is in
addr = (addr / slab_size) * slab_size;
Slab_i *s = reinterpret_cast<Slab_i*>(addr);
if (s->cache != this)
return;
Free_o *o = reinterpret_cast<Free_o*>(_o);
o->next = s->free;
s->free = o;
bool was_full = false;
if (!s->num_free)
{
_full_slabs.remove(s);
was_full = true;
}
++(s->num_free);
if (s->num_free == objects_per_slab)
{
if (!was_full)
_partial_slabs.remove(s);
_empty_slabs.push_front(s);
++_num_free;
if (_num_free > max_free_slabs)
shrink();
was_full = false;
}
else if (was_full)
_partial_slabs.push_front(s);
//L4::cerr << this << "->free(" << _o << "): of " << s << '\n';
}
/**
* Get the total number of objects managed by the slab allocator.
*
* \return The number of objects managed by the allocator (including the
* free objects).
*/
unsigned total_objects() const noexcept
{ return _num_slabs * objects_per_slab; }
/**
* Get the number of objects which can be allocated before a new empty slab
* needs to be added to the slab allocator.
*
* \return The number of free objects in the slab allocator.
*/
unsigned free_objects() const noexcept
{
unsigned count = 0;
/* count partial slabs first */
for (typename H_list<Slab_i>::Const_iterator s = _partial_slabs.begin();
s != _partial_slabs.end(); ++s)
count += s->num_free;
/* add empty slabs */
count += _num_free * objects_per_slab;
return count;
}
};
/**
* \ingroup cxx_api
* Slab allocator for object of type `Type`.
*
* \tparam Type The type of the objects to manage.
* \tparam Slab_size Size of a slab.
* \tparam Max_free The maximum number of free slabs.
* \tparam Alloc The allocator for the slabs.
*/
template<typename Type, int Slab_size = L4_PAGESIZE,
int Max_free = 2, template<typename A> class Alloc = New_allocator >
class Slab : public Base_slab<sizeof(Type), Slab_size, Max_free, Alloc>
{
private:
typedef Base_slab<sizeof(Type), Slab_size, Max_free, Alloc> Base_type;
public:
typedef Type Obj_type;
Slab(typename Base_type::Slab_alloc const &alloc
= typename Base_type::Slab_alloc()) noexcept
: Base_slab<sizeof(Type), Slab_size, Max_free, Alloc>(alloc) {}
/**
* Allocate an object of type `Type`.
*
* \return A pointer to the object just allocated, or 0 on failure.
*
* \note The user is responsible for initializing the object.
*/
Type *alloc() noexcept
{
return reinterpret_cast<Type *>(Base_type::alloc());
}
/**
* Free the object addressed by `o`.
*
* \param o The pointer to the object to free.
* \pre The object must have been allocated with this allocator.
*/
void free(Type *o) noexcept
{ Base_slab<sizeof(Type), Slab_size, Max_free, Alloc>::free(o); }
};
/**
* \ingroup cxx_api
* Merged slab allocator (allocators for objects of the same size are merged
* together).
*
* \tparam Obj_size The size of an object managed by the slab allocator.
* \tparam Slab_size The size of a slab.
* \tparam Max_free The maximum number of free slabs.
* \tparam Alloc The allocator for the slabs.
*
* This slab allocator class is useful for merging slab allocators with the
* same parameters (equal `Obj_size`, `Slab_size`, `Max_free`, and
* `Alloc` parameters) together and share the overhead for the slab caches
* among all equal-sized objects.
*/
template< int Obj_size, int Slab_size = L4_PAGESIZE,
int Max_free = 2, template<typename A> class Alloc = New_allocator >
class Base_slab_static
{
private:
typedef Base_slab<Obj_size, Slab_size, Max_free, Alloc> _A;
static _A _a;
public:
typedef void Obj_type;
enum
{
/// Size of an object.
object_size = Obj_size,
/// Size of a slab.
slab_size = Slab_size,
/// Number of objects per slab.
objects_per_slab = _A::objects_per_slab,
/// Maximum number of free slabs.
max_free_slabs = Max_free,
};
/**
* Allocate an object.
*
* \note The user is responsible for initializing the object.
*/
void *alloc() noexcept { return _a.alloc(); }
/**
* Free the given object (`p`).
*
* \param p The pointer to the object to free.
* \pre `p` must be a pointer to an object allocated by this allocator.
*/
void free(void *p) noexcept { _a.free(p); }
/**
* Get the total number of objects managed by the slab allocator.
*
* \return The number of objects managed by the allocator (including the
* free objects).
* \note The value is the merged value for all equal parameterized
* Base_slab_static instances.
*/
unsigned total_objects() const noexcept { return _a.total_objects(); }
/**
* Get the number of free objects in the slab allocator.
*
* \return The number of free objects in all free and partially used
* slabs managed by this allocator.
* \note The value is the merged value for all equal parameterized
* Base_slab_static instances.
*/
unsigned free_objects() const noexcept { return _a.free_objects(); }
};
template< int _O, int _S, int _M, template<typename A> class Alloc >
typename Base_slab_static<_O,_S,_M,Alloc>::_A
Base_slab_static<_O,_S,_M,Alloc>::_a;
/**
* \ingroup cxx_api
* Merged slab allocator (allocators for objects of the same size are merged
* together).
*
* \tparam Type The type of the objects to manage.
* \tparam Slab_size The size of a slab.
* \tparam Max_free The maximum number of free slabs.
* \tparam Alloc The allocator for the slabs.
*
* This slab allocator class is useful for merging slab allocators with the
* same parameters (equal `sizeof(Type)`, `Slab_size`, `Max_free`, and
* `Alloc` parameters) together and share the overhead for the slab caches
* among all equal-sized objects.
*/
template<typename Type, int Slab_size = L4_PAGESIZE,
int Max_free = 2, template<typename A> class Alloc = New_allocator >
class Slab_static
: public Base_slab_static<sizeof(Type), Slab_size, Max_free, Alloc>
{
public:
typedef Type Obj_type;
/**
* Allocate an object of type `Type`.
*
* \return A pointer to the just allocated object, or 0 on failure.
*
* \note The object is not zeroed out by the slab allocator.
*/
Type *alloc() noexcept
{
return reinterpret_cast<Type *>(
Base_slab_static<sizeof(Type), Slab_size, Max_free, Alloc>::alloc());
}
};
}

View File

@@ -0,0 +1,212 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2011 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "bits/list_basics.h"
namespace cxx {
class S_list_item
{
public:
S_list_item() : _n(0) {}
// BSS allocation
explicit S_list_item(bool) {}
private:
template<typename T, typename P> friend class S_list;
template<typename T, typename P> friend class S_list_tail;
template<typename T, typename X> friend struct Bits::Basic_list_policy;
S_list_item(S_list_item const &);
void operator = (S_list_item const &);
S_list_item *_n;
};
/**
* Simple single-linked list.
*
* \tparam T Type of elements saved in the list. Must inherit from
* cxx::S_list_item
*/
template< typename T, typename POLICY = Bits::Basic_list_policy< T, S_list_item > >
class S_list : public Bits::Basic_list<POLICY>
{
S_list(S_list const &) = delete;
void operator = (S_list const &) = delete;
private:
typedef typename Bits::Basic_list<POLICY> Base;
public:
typedef typename Base::Iterator Iterator;
S_list(S_list &&o) : Base(static_cast<Base&&>(o)) {}
S_list &operator = (S_list &&o)
{
Base::operator = (static_cast<Base&&>(o));
return *this;
}
// BSS allocation
explicit S_list(bool x) : Base(x) {}
S_list() : Base() {}
/// Add an element to the front of the list.
void add(T *e)
{
e->_n = this->_f;
this->_f = e;
}
template< typename CAS >
void add(T *e, CAS const &c)
{
do
{
e->_n = this->_f;
}
while (!c(&this->_f, e->_n, e));
}
/// Add an element to the front of the list.
void push_front(T *e) { add(e); }
/**
* Remove and return the head element of the list.
*
* \pre The list must not be empty or the behaviour will be undefined.
*/
T *pop_front()
{
T *r = this->front();
if (this->_f)
this->_f = this->_f->_n;
return r;
}
void insert(T *e, Iterator const &pred)
{
S_list_item *p = *pred;
e->_n = p->_n;
p->_n = e;
}
static void insert_before(T *e, Iterator const &succ)
{
S_list_item **x = Base::__get_internal(succ);
e->_n = *x;
*x = e;
}
static void replace(Iterator const &p, T*e)
{
S_list_item **x = Base::__get_internal(p);
e->_n = (*x)->_n;
*x = e;
}
static Iterator erase(Iterator const &e)
{
S_list_item **x = Base::__get_internal(e);
*x = (*x)->_n;
return e;
}
};
template< typename T >
class S_list_bss : public S_list<T>
{
public:
S_list_bss() : S_list<T>(true) {}
};
template< typename T, typename POLICY = Bits::Basic_list_policy< T, S_list_item > >
class S_list_tail : public S_list<T, POLICY>
{
private:
typedef S_list<T, POLICY> Base;
void add(T *e) = delete;
public:
using Iterator = typename Base::Iterator;
S_list_tail() : Base(), _tail(&this->_f) {}
S_list_tail(S_list_tail &&t)
: Base(static_cast<Base&&>(t)), _tail(t.empty() ? &this->_f : t._tail)
{
t._tail = &t._f;
}
S_list_tail &operator = (S_list_tail &&t)
{
if (&t == this)
return *this;
Base::operator = (static_cast<Base &&>(t));
_tail = t.empty() ? &this->_f : t._tail;
t._tail = &t._f;
return *this;
}
void push_front(T *e)
{
if (Base::empty())
_tail = &e->_n;
Base::push_front(e);
}
void push_back(T *e)
{
e->_n = 0;
*_tail = e;
_tail = &e->_n;
}
void clear()
{
Base::clear();
_tail = &this->_f;
}
void append(S_list_tail &o)
{
T *x = o.front();
*_tail = x;
if (x)
_tail = o._tail;
o.clear();
}
T *pop_front()
{
T *t = Base::pop_front();
if (t && Base::empty())
_tail = &this->_f;
return t;
}
private:
static void insert(T *e, Iterator const &pred);
static void insert_before(T *e, Iterator const &succ);
static void replace(Iterator const &p, T*e);
static Iterator erase(Iterator const &e);
private:
S_list_item **_tail;
};
}

View File

@@ -0,0 +1,52 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2012-2013 Technische Universität Dresden.
* Copyright (C) 2016-2017, 2020, 2023-2024 Kernkonzept GmbH.
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/type_traits>
#include <stddef.h>
namespace cxx {
template< typename T >
class Static_container
{
private:
struct X : T
{
void *operator new (size_t, void *p) noexcept { return p; }
void operator delete (void *) {}
X() = default;
template<typename ...Args>
X(Args && ...a) : T(cxx::forward<Args>(a)...) {}
};
public:
void operator = (Static_container const &) = delete;
Static_container(Static_container const &) = delete;
Static_container() = default;
T *get() { return reinterpret_cast<X*>(_s); }
T *operator -> () { return get(); }
T &operator * () { return *get(); }
operator T* () { return get(); }
void construct()
{ new (reinterpret_cast<void*>(_s)) X; }
template< typename ...Args >
void construct(Args && ...args)
{ new (reinterpret_cast<void*>(_s)) X(cxx::forward<Args>(args)...); }
private:
char _s[sizeof(X)] __attribute__((aligned(__alignof(X))));
};
}

View File

@@ -0,0 +1,58 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
#pragma once
#include "type_traits"
namespace cxx {
/**
* Simple encapsulation for a dynamically allocated array.
*
* The main purpose of this class is to support C++11 range for
* for simple dynamically allocated array with static size.
*/
template<typename T, typename IDX = unsigned>
class static_vector
{
private:
template<typename X, typename IDX2> friend class static_vector;
T *_v;
IDX _l;
public:
typedef T value_type;
typedef IDX index_type;
static_vector() = default;
static_vector(value_type *v, index_type length) : _v(v), _l(length) {}
template<typename Z,
typename = enable_if_t<is_same<remove_extent_t<Z>, T>::value>>
constexpr static_vector(Z &v) : _v(v), _l(array_size(v))
{}
/// Conversion from compatible arrays
template<typename X,
typename = enable_if_t<is_convertible<X, T>::value>>
static_vector(static_vector<X, IDX> const &o) : _v(o._v), _l(o._l) {}
index_type size() const { return _l; }
bool empty() const { return _l == 0; }
value_type &operator [] (index_type idx) { return _v[idx]; }
value_type const &operator [] (index_type idx) const { return _v[idx]; }
value_type *begin() { return _v; }
value_type *end() { return _v + _l; }
value_type const *begin() const { return _v; }
value_type const *end() const { return _v + _l; }
value_type const *cbegin() const { return _v; }
value_type const *cend() const { return _v + _l; }
/// Get the index of the given element of the array
index_type index(value_type const *o) const { return o - _v; }
index_type index(value_type const &o) const { return &o - _v; }
};
}

View File

@@ -0,0 +1,74 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <stddef.h>
namespace cxx {
/**
* \ingroup cxx_api
* \brief Helper type to distinguish the <c> operator new </c> version
* that does not throw exceptions.
*/
class Nothrow {};
}
/**
* \ingroup cxx_api
* \brief Simple placement new operator.
* \param mem the address of the memory block to place the new object.
* \return the address given by \a mem.
*/
inline void *operator new (size_t, void *mem, cxx::Nothrow const &) noexcept
{ return mem; }
/**
* \ingroup cxx_api
* \brief New operator that does not throw exceptions.
*/
void *operator new (size_t, cxx::Nothrow const &) noexcept;
/**
* \ingroup cxx_api
* \brief Delete operator complementing the new operator not throwing
* exceptions.
*/
void operator delete (void *, cxx::Nothrow const &) noexcept;
namespace cxx {
/**
* \ingroup cxx_api
* \brief Standard allocator based on <c>operator new () </c>.
*
* This allocator is the default allocator used for the \em cxx
* \em Containers, such as cxx::Avl_set and cxx::Avl_map, to allocate
* the internal data structures.
*/
template< typename _Type >
class New_allocator
{
public:
enum { can_free = true };
New_allocator() noexcept {}
New_allocator(New_allocator const &) noexcept {}
~New_allocator() noexcept {}
_Type *alloc() noexcept
{ return static_cast<_Type*>(::operator new(sizeof (_Type), cxx::Nothrow())); }
void free(_Type *t) noexcept
{ ::operator delete(t, cxx::Nothrow()); }
};
}

View File

@@ -0,0 +1,25 @@
// vim:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace cxx {
/**
* \brief Generic comparator class that defaults to the less-than operator.
*/
template< typename Obj >
struct Lt_functor
{
bool operator () (Obj const &l, Obj const &r) const
{ return l < r; }
};
};

View File

@@ -0,0 +1,54 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
#pragma once
/*
* (c) 2012 Alexander Warg <warg@os.inf.tu-dresden.de>,
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "type_traits"
namespace cxx {
template< typename ...T >
struct type_list;
template<>
struct type_list<>
{
typedef false_type head;
typedef false_type tail;
};
template<typename HEAD, typename ...TAIL>
struct type_list<HEAD, TAIL...>
{
typedef HEAD head;
typedef type_list<TAIL...> tail;
};
template<typename TYPELIST, template <typename T> class PREDICATE>
struct find_type;
template<template <typename T> class PREDICATE>
struct find_type<type_list<>, PREDICATE>
{
typedef false_type type;
};
template<typename TYPELIST, template <typename T> class PREDICATE>
struct find_type
{
typedef typename conditional<PREDICATE<typename TYPELIST::head>::value,
typename TYPELIST::head,
typename find_type<typename TYPELIST::tail, PREDICATE>::type>::type type;
};
template<typename TYPELIST, template <typename T> class PREDICATE>
using find_type_t = typename find_type<TYPELIST, PREDICATE>::type;
}

View File

@@ -0,0 +1,378 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#pragma GCC system_header
#include <l4/sys/compiler.h>
#include "bits/type_traits.h"
namespace cxx {
template< typename T, T V >
struct integral_constant
{
static T const value = V;
typedef T value_type;
typedef integral_constant<T, V> type;
};
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
template< typename T > struct remove_reference;
template< typename T > struct identity { typedef T type; };
template< typename T > using identity_t = typename identity<T>::type;
template< typename T1, typename T2 > struct is_same;
template< typename T > struct remove_const;
template< typename T > struct remove_volatile;
template< typename T > struct remove_cv;
template< typename T > struct remove_pointer;
template< typename T > struct remove_extent;
template< typename T > struct remove_all_extents;
template< typename, typename >
struct is_same : false_type {};
template< typename T >
struct is_same<T, T> : true_type {};
template< typename T1, typename T2 >
inline constexpr bool is_same_v = is_same<T1, T2>::value;
template< typename T >
struct remove_reference { typedef T type; };
template< typename T >
struct remove_reference<T &> { typedef T type; };
template< typename T >
struct remove_reference<T &&> { typedef T type; };
template< typename T >
using remove_reference_t = typename remove_reference<T>::type;
template< typename T > struct remove_const { typedef T type; };
template< typename T > struct remove_const<T const> { typedef T type; };
template< typename T > using remove_const_t = typename remove_const<T>::type;
template< typename T > struct remove_volatile { typedef T type; };
template< typename T > struct remove_volatile<T volatile> { typedef T type; };
template< typename T > using remove_volatile_t = typename remove_volatile<T>::type;
template< typename T >
struct remove_cv { typedef remove_const_t<remove_volatile_t<T>> type; };
template< typename T >
using remove_cv_t = typename remove_cv<T>::type;
template<class T>
struct remove_cvref { using type = remove_cv_t<remove_reference_t<T>>; };
template< typename T >
using remove_cvref_t = typename remove_cvref<T>::type;
template< typename T, typename >
struct __remove_pointer_h { typedef T type; };
template< typename T, typename I >
struct __remove_pointer_h<T, I*> { typedef I type; };
template< typename T >
struct remove_pointer : __remove_pointer_h<T, remove_cv_t<T>> {};
template< typename T >
using remove_pointer_t = typename remove_pointer<T>::type;
template< typename T >
struct remove_extent { typedef T type; };
template< typename T >
struct remove_extent<T[]> { typedef T type; };
template< typename T, unsigned long N >
struct remove_extent<T[N]> { typedef T type; };
template< typename T >
using remove_extent_t = typename remove_extent<T>::type;
template< typename T >
struct remove_all_extents { typedef T type; };
template< typename T >
struct remove_all_extents<T[]> { typedef typename remove_all_extents<T>::type type; };
template< typename T, unsigned long N >
struct remove_all_extents<T[N]> { typedef typename remove_all_extents<T>::type type; };
template< typename T >
using remove_all_extents_t = typename remove_all_extents<T>::type;
template< typename T >
constexpr T &&
forward(cxx::remove_reference_t<T> &t)
{ return static_cast<T &&>(t); }
template< typename T >
constexpr T &&
forward(cxx::remove_reference_t<T> &&t)
{ return static_cast<T &&>(t); }
template< typename T >
constexpr cxx::remove_reference_t<T> &&
move(T &&t) { return static_cast<cxx::remove_reference_t<T> &&>(t); }
template< bool, typename T = void >
struct enable_if {};
template< typename T >
struct enable_if<true, T> { typedef T type; };
template< bool C, typename T = void >
using enable_if_t = typename enable_if<C, T>::type;
template< typename T >
struct is_const : false_type {};
template< typename T >
struct is_const<T const> : true_type {};
template< typename T >
inline constexpr bool is_const_v = is_const<T>::value;
template< typename T >
struct is_volatile : false_type {};
template< typename T >
struct is_volatile<T volatile> : true_type {};
template< typename T >
inline constexpr bool is_volatile_v = is_volatile<T>::value;
template< typename T >
struct is_pointer : false_type {};
template< typename T >
struct is_pointer<T *> : true_type {};
template< typename T >
inline constexpr bool is_pointer_v = is_pointer<T>::value;
template<class T>
inline constexpr bool is_null_pointer_v = is_same_v<decltype(nullptr), remove_cv_t<T>>;
template< typename T >
struct is_reference : false_type {};
template< typename T >
struct is_reference<T &> : true_type {};
template< typename T >
struct is_reference<T &&> : true_type {};
template< typename T >
inline constexpr bool is_reference_v = is_reference<T>::value;
template< bool, typename, typename >
struct conditional;
template< bool C, typename T_TRUE, typename T_FALSE >
struct conditional { typedef T_TRUE type; };
template< typename T_TRUE, typename T_FALSE >
struct conditional< false, T_TRUE, T_FALSE > { typedef T_FALSE type; };
template< bool C, typename T_TRUE, typename T_FALSE >
using conditional_t = typename conditional<C, T_TRUE, T_FALSE>::type;
template<typename T>
struct is_enum : integral_constant<bool, __is_enum(T)> {};
template< typename T >
inline constexpr bool is_enum_v = is_enum<T>::value;
template<typename T>
struct is_polymorphic : cxx::integral_constant<bool, __is_polymorphic(T)> {};
template< typename T > struct is_integral : false_type {};
template<> struct is_integral<bool> : true_type {};
template<> struct is_integral<char> : true_type {};
template<> struct is_integral<signed char> : true_type {};
template<> struct is_integral<unsigned char> : true_type {};
template<> struct is_integral<short> : true_type {};
template<> struct is_integral<unsigned short> : true_type {};
template<> struct is_integral<int> : true_type {};
template<> struct is_integral<unsigned int> : true_type {};
template<> struct is_integral<long> : true_type {};
template<> struct is_integral<unsigned long> : true_type {};
template<> struct is_integral<long long> : true_type {};
template<> struct is_integral<unsigned long long> : true_type {};
template< typename T >
inline constexpr bool is_integral_v = is_integral<T>::value;
template< typename T, bool = is_integral_v<T> || is_enum_v<T> >
struct __is_signed_helper : integral_constant<bool, static_cast<bool>(T(-1) < T(0))> {};
template< typename T >
struct __is_signed_helper<T, false> : integral_constant<bool, false> {};
template< typename T >
struct is_signed : __is_signed_helper<T> {};
template< typename T >
inline constexpr bool is_signed_v = is_signed<T>::value;
template< typename >
struct is_array : false_type {};
template< typename T >
struct is_array<T[]> : true_type {};
template< typename T, unsigned long N >
struct is_array<T[N]> : true_type {};
template< typename T >
inline constexpr bool is_array_v = is_array<T>::value;
template< typename T, unsigned N >
constexpr unsigned array_size(T const (&)[N]) { return N; }
template< int SIZE, bool SIGN = false, bool = true > struct int_type_for_size;
template<> struct int_type_for_size<sizeof(char), true, true>
{ typedef signed char type; };
template<> struct int_type_for_size<sizeof(char), false, true>
{ typedef unsigned char type; };
template<> struct int_type_for_size<sizeof(short), true, (sizeof(short) > sizeof(char))>
{ typedef short type; };
template<> struct int_type_for_size<sizeof(short), false, (sizeof(short) > sizeof(char))>
{ typedef unsigned short type; };
template<> struct int_type_for_size<sizeof(int), true, (sizeof(int) > sizeof(short))>
{ typedef int type; };
template<> struct int_type_for_size<sizeof(int), false, (sizeof(int) > sizeof(short))>
{ typedef unsigned int type; };
template<> struct int_type_for_size<sizeof(long), true, (sizeof(long) > sizeof(int))>
{ typedef long type; };
template<> struct int_type_for_size<sizeof(long), false, (sizeof(long) > sizeof(int))>
{ typedef unsigned long type; };
template<> struct int_type_for_size<sizeof(long long), true, (sizeof(long long) > sizeof(long))>
{ typedef long long type; };
template<> struct int_type_for_size<sizeof(long long), false, (sizeof(long long) > sizeof(long))>
{ typedef unsigned long long type; };
template< int SIZE, bool SIGN = false>
using int_type_for_size_t = typename int_type_for_size<SIZE, SIGN>::type;
template< typename T, class Enable = void > struct underlying_type {};
template< typename T >
struct underlying_type<T, typename enable_if<is_enum_v<T>>::type >
{
typedef int_type_for_size_t<sizeof(T), is_signed_v<T>> type;
};
template< typename T >
using underlying_type_t = typename underlying_type<T>::type;
template< typename T > struct make_signed;
template<> struct make_signed<char> { typedef signed char type; };
template<> struct make_signed<unsigned char> { typedef signed char type; };
template<> struct make_signed<signed char> { typedef signed char type; };
template<> struct make_signed<unsigned int> { typedef signed int type; };
template<> struct make_signed<signed int> { typedef signed int type; };
template<> struct make_signed<unsigned long int> { typedef signed long int type; };
template<> struct make_signed<signed long int> { typedef signed long int type; };
template<> struct make_signed<unsigned long long int> { typedef signed long long int type; };
template<> struct make_signed<signed long long int> { typedef signed long long int type; };
template< typename T > using make_signed_t = typename make_signed<T>::type;
template< typename T > struct make_unsigned;
template<> struct make_unsigned<char> { typedef unsigned char type; };
template<> struct make_unsigned<unsigned char> { typedef unsigned char type; };
template<> struct make_unsigned<signed char> { typedef unsigned char type; };
template<> struct make_unsigned<unsigned int> { typedef unsigned int type; };
template<> struct make_unsigned<signed int> { typedef unsigned int type; };
template<> struct make_unsigned<unsigned long int> { typedef unsigned long int type; };
template<> struct make_unsigned<signed long int> { typedef unsigned long int type; };
template<> struct make_unsigned<unsigned long long int> { typedef unsigned long long int type; };
template<> struct make_unsigned<signed long long int> { typedef unsigned long long int type; };
template< typename T > using make_unsigned_t = typename make_unsigned<T>::type;
template<typename From, typename To>
struct is_convertible
{
private:
struct _true { char x[2]; };
struct _false {};
static _true _helper(To const *);
static _false _helper(...);
public:
enum
{
value = sizeof(_true) == sizeof(_helper(static_cast<From*>(0)))
? true : false
};
typedef bool value_type;
};
template<typename From, typename To>
inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
template< typename T >
struct is_empty : integral_constant<bool, __is_empty(T)> {};
template< typename T >
inline constexpr bool is_empty_v = is_empty<T>::value;
#if L4_HAS_BUILTIN(__is_function)
template < typename T >
struct is_function : integral_constant<bool, __is_function(T)> {};
#else
template < typename T >
struct is_function : integral_constant<bool, !is_reference_v<T>
&& !is_const_v<const T>> {};
#endif
template< typename T >
inline constexpr bool is_function_v = is_function<T>::value;
}

View File

@@ -0,0 +1,127 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2013 Technische Universität Dresden.
* Copyright (C) 2014-2017, 2020, 2023-2024 Kernkonzept GmbH.
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "type_traits"
namespace cxx
{
template< typename T >
struct default_delete
{
default_delete() {}
template< typename U >
default_delete(default_delete<U> const &) {}
void operator () (T *p) const
{ delete p; }
};
template< typename T >
struct default_delete<T[]>
{
default_delete() {}
void operator () (T *p)
{ delete [] p; }
};
template< typename T, typename C >
struct unique_ptr_index_op {};
template< typename T, typename C >
struct unique_ptr_index_op<T[], C>
{
typedef T &reference;
reference operator [] (int idx) const
{ return static_cast<C const *>(this)->get()[idx]; }
};
template< typename T, typename T_Del = default_delete<T> >
class unique_ptr : public unique_ptr_index_op<T, unique_ptr<T, T_Del> >
{
private:
struct _unspec;
typedef _unspec* _unspec_ptr_type;
public:
typedef cxx::remove_extent_t<T> element_type;
typedef element_type *pointer;
typedef element_type &reference;
typedef T_Del deleter_type;
unique_ptr() : _ptr(pointer()) {}
explicit unique_ptr(pointer p) : _ptr(p) {}
unique_ptr(unique_ptr &&o) : _ptr(o.release()) {}
~unique_ptr() { reset(); }
unique_ptr &operator = (unique_ptr &&o)
{
reset(o.release());
return *this;
}
unique_ptr &operator = (_unspec_ptr_type)
{
reset();
return *this;
}
element_type &operator * () const { return *get(); }
pointer operator -> () const { return get(); }
pointer get() const { return _ptr; }
operator _unspec_ptr_type () const
{ return reinterpret_cast<_unspec_ptr_type>(get()); }
pointer release()
{
pointer r = _ptr;
_ptr = 0;
return r;
}
void reset(pointer p = pointer())
{
if (p != get())
{
deleter_type()(get());
_ptr = p;
}
}
unique_ptr(unique_ptr const &) = delete;
unique_ptr &operator = (unique_ptr const &) = delete;
private:
pointer _ptr;
};
template< typename T >
unique_ptr<T>
make_unique_ptr(T *p)
{ return unique_ptr<T>(p); }
template< typename T >
cxx::enable_if_t<cxx::is_array<T>::value, unique_ptr<T>>
make_unique(unsigned long size)
{ return cxx::unique_ptr<T>(new cxx::remove_extent_t<T>[size]()); }
template< typename T, typename... Args >
cxx::enable_if_t<!cxx::is_array<T>::value, unique_ptr<T>>
make_unique(Args &&... args)
{ return cxx::unique_ptr<T>(new T(cxx::forward<Args>(args)...)); }
}

View File

@@ -0,0 +1,30 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/**
* \file
* Implementation of a list of unique-ptr-managed objects.
*/
/*
* Copyright (C) 2018-2019, 2022, 2024 Kernkonzept GmbH.
* Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/unique_ptr>
#include "bits/smart_ptr_list.h"
namespace cxx {
/// Item for list linked with cxx::unique_ptr.
template <typename T>
using Unique_ptr_list_item = Bits::Smart_ptr_list_item<T, cxx::unique_ptr<T> >;
/**
* Single-linked list where elements are connected with a cxx::unique_ptr.
*/
template <typename T>
using Unique_ptr_list = Bits::Smart_ptr_list<Unique_ptr_list_item<T> >;
}

View File

@@ -0,0 +1,80 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2013 Technische Universität Dresden.
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace cxx {
/**
* Read the value at an address at most once.
*
* The read might be omitted if the result is not used by any code unless
* `typename` contains `volatile`. If the read operation has side effects and
* must not be omitted, use different means like L4drivers::Mmio_register_block
* or similar.
*
* The compiler is disallowed to reuse a previous read at the same address, for
* example:
* ```
* val1 = *a;
* val2 = access_once(a); // compiler may not replace this by val2 = val1;
* ```
*
* The compiler is also disallowed to repeat the read, for example:
* ```
* val1 = access_once(a);
* val2 = val1; // compiler may not replace this by val2 = *a;
* ```
*
* The above implies that the compiler is also disallowed to move the read out
* of or into loops.
*
* \note The read might still be moved relative to other code.
* \note The value might be read from a hardware cache, not from RAM.
*/
template< typename T > inline
T access_once(T const *a)
{
#if 1
__asm__ __volatile__ ( "" : "=m"(*const_cast<T*>(a)));
T tmp = *a;
__asm__ __volatile__ ( "" : "=m"(*const_cast<T*>(a)));
return tmp;
#else
return *static_cast<T const volatile *>(a);
#endif
}
/**
* Write a value at an address exactly once.
*
* The compiler is disallowed to skip the write, for example:
* ```
* *a = val;
* write_now(a, val); // compiler may not skip this line
* ```
*
* The compiler is also disallowed to repeat the write.
*
* The above implies that the compiler is also disallowed to move the write out
* of or into loops.
*
* \note The write might still be moved relative to other code.
* \note The value might be written just to a hardware cache for the moment, not
* immediately to RAM.
*/
template< typename T, typename VAL > inline
void write_now(T *a, VAL &&val)
{
__asm__ __volatile__ ( "" : "=m"(*a));
*a = val;
__asm__ __volatile__ ( "" : : "m"(*a));
}
}

View File

@@ -0,0 +1,157 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2015, 2017, 2024 Kernkonzept GmbH.
* Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
* Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "hlist"
namespace cxx {
/**
* Generic (base) weak reference to some object.
*
* A weak reference is a reference that gets reset to NULL when the object
* shall be deleted. All weak references to the same object are kept in a
* linked list of weak references.
*
* For typed weak references see `cxx::Weak_ref`.
*/
class Weak_ref_base : public H_list_item_t<Weak_ref_base>
{
protected:
Weak_ref_base(void const *ptr = nullptr) : _obj(ptr) {}
void reset_hard() { _obj = nullptr; }
void const *_obj;
public:
/**
* The list type for keeping all weak references to an object.
*
* On destruction of a list, all weak references to the respective object are
* set to `nullptr`.
*/
struct List : H_list_t<Weak_ref_base>
{
void reset()
{
while (!empty())
pop_front()->reset_hard();
}
~List()
{ reset(); }
};
explicit operator bool () const
{ return _obj ? true : false; }
};
/**
* Typed weak reference to an object of type `T`.
*
* \tparam T The type of the referenced object.
*
* A weak reference is a reference that is invalidated when the referenced
* object is about to be deleted. All weak references to an object are kept in
* a linked list (see Weak_ref_base::List) and all the weak references are
* iterated and reset by the Weak_ref_base::List destructor or
* Weak_ref_base::List::reset().
*
* The type `T` must provide two methods that handle the housekeeping of weak
* references: `remove_weak_ref(Weak_ref_base *)` and
* `add_weak_ref(Weak_ref_base *)`. These functions must handle the insertion
* and removal of the weak reference into the respective Weak_ref_base::List
* object. For convenience one can use the cxx::Weak_ref_obj as a base class
* that handles weak references for you.
*
* For example:
* ```{.cpp}
* class C : public cxx::Weak_ref_obj {};
*
* int main()
* {
* cxx::Weak_ref<C> r; // r is nullptr
* {
* C c;
* r = &c; // now r points to c
* } // c is destructed, which implies resetting all weak references to c
* // now r is nullptr
* return 0;
* }
* ```
*
* \note Weak references have no effect on the lifetime of the referenced
* object. Hence, a referenced object is *not* deleted when all weak
* references for it are gone. If automatic deletion is needed, see
* cxx::Ref_ptr.
*/
template <typename T>
class Weak_ref : public Weak_ref_base
{
public:
T *get() const
{ return reinterpret_cast<T*>(const_cast<void *>(_obj)); }
T *reset(T *n)
{
T *r = get();
if (r)
r->remove_weak_ref(this);
_obj = n;
if (n)
n->add_weak_ref(this);
return r;
}
Weak_ref(T *s = nullptr) : Weak_ref_base(s)
{
if (s)
s->add_weak_ref(this);
}
~Weak_ref() { reset(0); }
void operator = (T *n)
{ reset(n); }
Weak_ref(Weak_ref const &o) : Weak_ref_base(o._obj)
{
if (T *x = get())
x->add_weak_ref(this);
}
Weak_ref &operator = (Weak_ref const &o)
{
if (&o == this)
return *this;
reset(o.get());
return *this;
}
T &operator * () const { return get(); }
T *operator -> () const { return get(); }
};
class Weak_ref_obj
{
protected:
template <typename T> friend class Weak_ref;
mutable Weak_ref_base::List weak_references;
void add_weak_ref(Weak_ref_base *ref) const
{ weak_references.push_front(ref); }
void remove_weak_ref(Weak_ref_base *ref) const
{ weak_references.remove(ref); }
};
}

View File

@@ -0,0 +1,8 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := include src
include $(L4DIR)/mk/subdir.mk
src: include

View File

@@ -0,0 +1,4 @@
PKGDIR = ../../..
L4DIR ?= $(PKGDIR)/../../..
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,39 @@
/**
* \file
* \brief Alloc list
*/
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace L4 {
/**
* \brief A simple list-based allocator.
* \ingroup cxx_api
*/
class Alloc_list
{
public:
Alloc_list() : _free(0) {}
Alloc_list(void *blk, unsigned long size) : _free(0)
{ free( blk, size); }
void free(void *blk, unsigned long size);
void *alloc(unsigned long size);
private:
struct Elem
{
Elem *next;
unsigned long size;
};
Elem *_free;
};
}

View File

@@ -0,0 +1,38 @@
/**
* \file
* \brief Atomic template
*/
/*
* (c) 2004-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/util/atomic.h>
extern "C" void ____error_compare_and_swap_does_not_support_3_bytes____();
extern "C" void ____error_compare_and_swap_does_not_support_more_than_4_bytes____();
namespace L4
{
template< typename X >
inline int compare_and_swap(X volatile *dst, X old_val, X new_val)
{
switch (sizeof(X))
{
case 1:
return l4util_cmpxchg8((l4_uint8_t volatile*)dst, old_val, new_val);
case 2:
return l4util_cmpxchg16((l4_uint16_t volatile *)dst, old_val, new_val);
case 3: ____error_compare_and_swap_does_not_support_3_bytes____();
case 4:
return l4util_cmpxchg32((l4_uint32_t volatile*)dst, old_val, new_val);
default:
____error_compare_and_swap_does_not_support_more_than_4_bytes____();
}
return 0;
}
}

View File

@@ -0,0 +1,14 @@
/**
* \file
* \brief L4 Types
*/
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/types.h>
#include <l4/cxx/basic_ostream>

View File

@@ -0,0 +1,165 @@
// vim:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Martin Decky <martin.decky@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* \file
* Lock guard implementation.
*/
#pragma once
#include <pthread.h>
#include <stdlib.h>
namespace L4 {
/**
* Basic lock guard implementation that prevents forgotten unlocks on exit
* paths from a method or a block of code. Targeting `pthread_mutex_t`.
*
* An instance of lock guard cannot be copied, but it can be moved.
*
* The typical usage pattern of the lock guard is:
*
* \code{.cpp}
* pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
*
* {
* auto guard = L4Re::Lock_guard(mtx);
*
* // Correctness check.
* assert(guard.status() == 0);
*
* // Critical section protected by mtx.
*
* // The mtx is automatically unlocked when guard goes out of scope.
* }
* \endcode
*/
class Lock_guard
{
public:
Lock_guard() = delete;
Lock_guard(const Lock_guard &) = delete;
Lock_guard &operator=(const Lock_guard &) = delete;
/**
* Construct the lock guard and lock the associated mutex.
*
* The error condition of the locking operation can be checked by the
* #status() method.
*
* \param lock Associated mutex to be locked.
*/
explicit Lock_guard(pthread_mutex_t &lock) : _lock(&lock)
{
_status = pthread_mutex_lock(_lock);
}
/**
* Move constructor from other lock guard.
*
* The mutex associated with the other lock guard is kept locked.
*
* \param guard Lock guard to be moved.
*/
Lock_guard(Lock_guard &&guard) : _lock(guard._lock), _status(guard._status)
{
guard.release();
}
/**
* Move assignment from other lock guard.
*
* The mutex currently associated with this lock guard is unlocked. The mutex
* associated with the other lock guard is kept locked.
*
* There is no mechanism for indicating any error conditions of the unlocking
* operation. However, if the mutex has been previously locked successfully by
* this class and if the implementation of the mutex behaves according to the
* POSIX specification, the construction of this class guarantees that the
* unlock operation does not fail.
*
* \param guard Lock guard to be moved.
*/
Lock_guard &operator=(Lock_guard &&guard)
{
// Unlock the currently associated mutex (if any).
reset();
// Move the state from the other guard.
_lock = guard._lock;
_status = guard._status;
// Release the mutex from the other guard.
guard.release();
return *this;
}
/**
* Return last lock/unlock operation error status.
*
* \return Zero indicating no errors, any other value indicating an error.
*/
int status() const
{
return _status;
}
/**
* Lock guard destructor.
*
* The associated mutex (if any) is unlocked.
*
* There is no mechanism for indicating any error conditions. However, if
* the mutex has been previously locked successfully by this class and if the
* implementation of the mutex behaves according to the POSIX specification,
* the construction of this class guarantees that the unlock operation does
* not fail.
*/
~Lock_guard()
{
reset();
}
private:
/**
* Deassociate the mutex from the guard.
*
* Used internally to implement the move semantics only.
*/
void release()
{
_lock = nullptr;
}
/**
* Unlock and deassociate the mutex from the guard.
*
* Used internally to implement the move semantics and destruction only.
*
* The error condition of the unlocking operation can be checked by the
* #status() method.
*/
void reset()
{
// No mutex might be associated with this lock guard only if the mutex has
// been moved to a different lock guard.
if (_lock)
{
_status = pthread_mutex_unlock(_lock);
release();
}
}
pthread_mutex_t *_lock;
int _status;
};
} // namespace L4

View File

@@ -0,0 +1,48 @@
/**
* \file
* \brief String
*/
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/basic_ostream>
namespace L4 {
/**
* \brief A null-terminated string container class.
* \ingroup cxx_api
*/
class String
{
public:
String(char const *str = "") : _str(str)
{}
unsigned length() const
{
unsigned l;
for (l = 0; _str[l]; l++)
;
return l;
}
char const *p_str() const { return _str; }
private:
char const *_str;
};
}
inline
L4::BasicOStream &operator << (L4::BasicOStream &o, L4::String const &s)
{
o << s.p_str();
return o;
}

View File

@@ -0,0 +1,13 @@
PKGDIR ?= ../../..
L4DIR ?= $(PKGDIR)/../../..
TARGET = libcxx_util
PC_FILENAME = cxx_util
SRC_CC = ipc_error_str.cc alloc_list.cc
SYSTEMS = $(SYSTEMS_PLAIN)
CXX_PKG_DIR=$(PKGDIR)
include $(PKGDIR)/lib/Makefile.inc
include $(L4DIR)/mk/lib.mk
$(GENERAL_D_LOC): $(CXX_PKG_DIR)/lib/Makefile.inc

View File

@@ -0,0 +1,82 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/cxx/alloc.h>
namespace L4 {
void Alloc_list::free(void *blk, unsigned long size)
{
Elem *n = reinterpret_cast<Elem*>(blk);
Elem **c = &_free;
while (*c)
{
if (reinterpret_cast<char*>(*c) + (*c)->size == blk)
{
(*c)->size += size;
blk = 0;
break;
}
if (reinterpret_cast<char*>(*c) > blk)
break;
c = &((*c)->next);
}
if (blk)
{
n->next = *c;
n->size = size;
*c = n;
}
while (*c
&& (*c)->next
&& reinterpret_cast<char *>(*c) + (*c)->size
== reinterpret_cast<char *>((*c)->next))
{
(*c)->size += (*c)->next->size;
(*c)->next = (*c)->next->next;
}
}
void *Alloc_list::alloc(unsigned long size)
{
if (!_free)
return 0;
// best fit;
Elem **min = 0;
Elem **c = &_free;
while(*c)
{
if ((*c)->size >= size && (!min || (*min)->size > (*c)->size))
min = c;
c = &((*c)->next);
}
if (!min)
return 0;
void *r;
if ((*min)->size > size)
{
r = reinterpret_cast<char*>(*min) + ((*min)->size - size);
(*min)->size -= size;
}
else
{
r = *min;
*min = (*min)->next;
}
return r;
}
}

View File

@@ -0,0 +1,20 @@
/*
* (c) 2004-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
namespace L4 {
char const *ipc_error_str[] =
{ "ok",
"timeout",
"phase canceled",
"mapping failed",
"send page fault timeout",
"receive page fault timeout",
"aborted",
"message cut"
};
}