This commit is contained in:
Niklas Gollenstede
2025-04-14 11:20:52 +02:00
commit 5a2e32aaeb
126 changed files with 16742 additions and 0 deletions

62
object/bbuffer.h Normal file
View File

@@ -0,0 +1,62 @@
// vim: set noet ts=4 sw=4:
/*! \file
* \brief Contains a \ref BBuffer "bounded buffer"
*/
#pragma once
#include "../types.h"
/*! \brief The class BBuffer implements a bounded buffer, that is a circular
* buffer with a fixed capacity.
*
* \tparam T the type of data to be stored
* \tparam CAP the buffers capacity (must be greater than 1)
*/
template <typename T, unsigned CAP>
class BBuffer {
static_assert(CAP > 1, "BBuffer of size 1 is unsupported.");
// Prevent copies and assignments
BBuffer(const BBuffer&) = delete;
BBuffer& operator=(const BBuffer&) = delete;
private:
T data[CAP];
volatile unsigned in;
volatile unsigned out;
public:
/*! \brief Constructor that initialized an empty buffer.
*/
BBuffer() : in(0), out(0) {}
/*! \brief Add an element to the buffer.
* \param val The element to be added.
* \return `false` if the buffer is full and no element can be added; `true`
* otherwise.
*/
bool produce(T val) {
unsigned nextin = (in + 1) % CAP;
if (nextin != out) {
data[in] = val;
in = nextin;
return true;
}
return false;
}
/*! \brief Remove an element from the buffer.
* \param val Output parameter that receives the next element. If there is
* (currently) no next element, `val` will not be modified.
* \return `false` if the buffer was empty; `true` if the buffer was
* not empty and an element was written to val.
*/
bool consume(T& val) {
if (in != out) {
val = data[out];
out = (out + 1) % CAP;
return true;
}
return false;
}
};

118
object/key.cc Normal file
View File

@@ -0,0 +1,118 @@
#include "key.h"
// Character table for scan codes for US keyboards
static struct {
const unsigned char normal, // Character without modifiers
shift, // Character with pressed Shift, Capslock, or in Numpad
alt; // Character with pressed Alt key
} ascii_tab[Key::Scancode::KEYS] = {
{0, 0, 0}, // KEY_INVALID
{0, 0, 0}, // KEY_ESCAPE
{'1', '!', 0}, // KEY_1
{'2', '"', 253}, // KEY_2
{'3', 21, 0}, // KEY_3
{'4', '$', 0}, // KEY_4
{'5', '%', 0}, // KEY_5
{'6', '&', 0}, // KEY_6
{'7', '/', '{'}, // KEY_7
{'8', '(', '['}, // KEY_8
{'9', ')', ']'}, // KEY_9
{'0', '=', '}'}, // KEY_0
{225, '?', '\\'}, // KEY_DASH
{39, 96, 0}, // KEY_EQUAL
{'\b', 0, 0}, // KEY_BACKSPACE
{0, 0, 0}, // KEY_TAB
{'q', 'Q', '@'}, // KEY_Q
{'w', 'W', 0}, // KEY_W
{'e', 'E', 0}, // KEY_E
{'r', 'R', 0}, // KEY_R
{'t', 'T', 0}, // KEY_T
{'z', 'Z', 0}, // KEY_Y
{'u', 'U', 0}, // KEY_U
{'i', 'I', 0}, // KEY_I
{'o', 'O', 0}, // KEY_O
{'p', 'P', 0}, // KEY_P
{129, 154, 0}, // KEY_OPEN_BRACKET
{'+', '*', '~'}, // KEY_CLOSE_BRACKET
{'\n', 0, 0}, // KEY_ENTER
{0, 0, 0}, // KEY_LEFT_CTRL
{'a', 'A', 0}, // KEY_A
{'s', 'S', 0}, // KEY_S
{'d', 'D', 0}, // KEY_D
{'f', 'F', 0}, // KEY_F
{'g', 'G', 0}, // KEY_G
{'h', 'H', 0}, // KEY_H
{'j', 'J', 0}, // KEY_J
{'k', 'K', 0}, // KEY_K
{'l', 'L', 0}, // KEY_L
{148, 153, 0}, // KEY_SEMICOLON
{132, 142, 0}, // KEY_APOSTROPH
{'^', 248, 0}, // KEY_GRAVE_ACCENT
{0, 0, 0}, // KEY_LEFT_SHIFT
{'#', 39, 0}, // KEY_BACKSLASH
{'y', 'Y', 0}, // KEY_Z
{'x', 'X', 0}, // KEY_X
{'c', 'C', 0}, // KEY_C
{'v', 'V', 0}, // KEY_V
{'b', 'B', 0}, // KEY_B
{'n', 'N', 0}, // KEY_N
{'m', 'M', 230}, // KEY_M
{',', ';', 0}, // KEY_COMMA
{'.', ':', 0}, // KEY_PERIOD
{'-', '_', 0}, // KEY_SLASH
{0, 0, 0}, // KEY_RIGHT_SHIFT
{'*', '*', 0}, // KEY_KP_STAR
{0, 0, 0}, // KEY_LEFT_ALT
{' ', ' ', 0}, // KEY_SPACEBAR
{0, 0, 0}, // KEY_CAPS_LOCK
{0, 0, 0}, // KEY_F1
{0, 0, 0}, // KEY_F2
{0, 0, 0}, // KEY_F3
{0, 0, 0}, // KEY_F4
{0, 0, 0}, // KEY_F5
{0, 0, 0}, // KEY_F6
{0, 0, 0}, // KEY_F7
{0, 0, 0}, // KEY_F8
{0, 0, 0}, // KEY_F9
{0, 0, 0}, // KEY_F10
{0, 0, 0}, // KEY_NUM_LOCK
{0, 0, 0}, // KEY_SCROLL_LOCK
{0, '7', 0}, // KEY_KP_7
{0, '8', 0}, // KEY_KP_8
{0, '9', 0}, // KEY_KP_9
{'-', '-', 0}, // KEY_KP_DASH
{0, '4', 0}, // KEY_KP_4
{0, '5', 0}, // KEY_KP_5
{0, '6', 0}, // KEY_KP_6
{'+', '+', 0}, // KEY_KP_PLUS
{0, '1', 0}, // KEY_KP_1
{0, '2', 0}, // KEY_KP_2
{0, '3', 0}, // KEY_KP_3
{0, '0', 0}, // KEY_KP_0
{127, ',', 0}, // KEY_KP_PERIOD
{0, 0, 0}, // KEY_SYSREQ
{0, 0, 0}, // KEY_EUROPE_2
{'<', '>', '|'}, // KEY_F11
{0, 0, 0}, // KEY_F12
{0, 0, 0}, // KEY_KP_EQUAL
};
unsigned char Key::ascii() const {
// Select the correct table depending on the modifier bits.
// For the sake of simplicity, Shift and NumLock have precedence over Alt.
// The Ctrl modifier does not have a distinct table.
if (!valid()) {
return '\0';
} else if (shift ||
(caps_lock && ((scancode >= KEY_Q && scancode <= KEY_P) ||
(scancode >= KEY_A && scancode <= KEY_L) ||
(scancode >= KEY_Z && scancode <= KEY_M))) ||
(num_lock && scancode >= KEY_KP_7 && scancode <= KEY_KP_PERIOD)) {
return ascii_tab[scancode].shift;
} else if (alt()) {
return ascii_tab[scancode].alt;
} else {
return ascii_tab[scancode].normal;
}
}

165
object/key.h Normal file
View File

@@ -0,0 +1,165 @@
/*! \file
* \brief \ref Key, an abstraction for handling pressed keys and their
* modifiers
*/
#pragma once
#include "../types.h"
/*! \brief Class that abstracts a key, made up of the scan code and the modifier
* bits.
*/
struct Key {
/*! \brief The keys' scan codes (code 1)
*/
enum Scancode : uint8_t {
// Invalid scan code
KEY_INVALID = 0,
// "real" valid scan codes
KEY_ESCAPE,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_0,
KEY_DASH,
KEY_EQUAL,
KEY_BACKSPACE,
KEY_TAB,
KEY_Q,
KEY_W,
KEY_E,
KEY_R,
KEY_T,
KEY_Y,
KEY_U,
KEY_I,
KEY_O,
KEY_P,
KEY_OPEN_BRACKET,
KEY_CLOSE_BRACKET,
KEY_ENTER,
KEY_LEFT_CTRL,
KEY_A,
KEY_S,
KEY_D,
KEY_F,
KEY_G,
KEY_H,
KEY_J,
KEY_K,
KEY_L,
KEY_SEMICOLON,
KEY_APOSTROPH,
KEY_GRAVE_ACCENT,
KEY_LEFT_SHIFT,
KEY_BACKSLASH,
KEY_Z,
KEY_X,
KEY_C,
KEY_V,
KEY_B,
KEY_N,
KEY_M,
KEY_COMMA,
KEY_PERIOD,
KEY_SLASH,
KEY_RIGHT_SHIFT,
KEY_KP_STAR,
KEY_LEFT_ALT,
KEY_SPACEBAR,
KEY_CAPS_LOCK,
KEY_F1,
KEY_F2,
KEY_F3,
KEY_F4,
KEY_F5,
KEY_F6,
KEY_F7,
KEY_F8,
KEY_F9,
KEY_F10,
KEY_NUM_LOCK,
KEY_SCROLL_LOCK,
KEY_KP_7,
KEY_KP_8,
KEY_KP_9,
KEY_KP_DASH,
KEY_KP_4,
KEY_KP_5,
KEY_KP_6,
KEY_KP_PLUS,
KEY_KP_1,
KEY_KP_2,
KEY_KP_3,
KEY_KP_0,
KEY_KP_PERIOD,
KEY_SYSREQ,
KEY_EUROPE_2,
KEY_F11,
KEY_F12,
KEY_KP_EQUAL,
// Number of keys (excluding aliases below)
KEYS,
// aliases
KEY_DIV = KEY_7,
KEY_DEL = KEY_KP_PERIOD,
KEY_UP = KEY_KP_8,
KEY_DOWN = KEY_KP_2,
KEY_LEFT = KEY_KP_4,
KEY_RIGHT = KEY_KP_6,
};
Scancode scancode;
// bit masks for the modifier keys
bool shift : 1, alt_left : 1, alt_right : 1, ctrl_left : 1, ctrl_right : 1,
caps_lock : 1, num_lock : 1, scroll_lock : 1;
/*! \brief Default constructor: Instantiates an invalid key by setting ASCII,
* scan code, and modifier bits to 0
*/
Key()
: scancode(KEY_INVALID),
shift(false),
alt_left(false),
alt_right(false),
ctrl_left(false),
ctrl_right(false),
caps_lock(false),
num_lock(false),
scroll_lock(false) {}
/*! \brief Invalid keys have a scancode = 0
* \return Checks whether a key is valid.
*/
bool valid() const { return scancode != KEY_INVALID && scancode < KEYS; }
/*! \brief Marks the key as invalid by setting the scan code to 0.
*
*/
void invalidate() { scancode = KEY_INVALID; }
/*! \brief Get the key's ASCII value
* \return the key's ASCII value
*/
unsigned char ascii() const;
/*! \brief Indicates whether the ALT modifier is set
* \return `true` if ALT key was pressed during key press
*/
bool alt() const { return alt_left || alt_right; }
/*! \brief Indicates whether the CTRL modifier is set
* \return `true` if CTRL key was pressed during key press
*/
bool ctrl() const { return ctrl_left || ctrl_right; }
};

181
object/outputstream.cc Normal file
View File

@@ -0,0 +1,181 @@
#include "outputstream.h"
// operator <<: Converts the value in given data type to a string
// Print a single character (trivial)
OutputStream& OutputStream::operator<<(char c) {
put(c);
return *this;
}
OutputStream& OutputStream::operator<<(unsigned char c) {
return *this << static_cast<char>(c);
}
// Printing a null-terminated string
OutputStream& OutputStream::operator<<(const char* string) {
while ((*string) != '\0') {
put(*string);
string++;
}
return *this;
}
OutputStream& OutputStream::operator<<(bool b) {
return *this << (b ? "true" : "false");
}
// Print integral numbers in number system base.
// All signed types are promoted to long long,
// all unsigned types to unsigned long long.
OutputStream& OutputStream::operator<<(short ival) {
return *this << static_cast<long long>(ival);
}
OutputStream& OutputStream::operator<<(unsigned short ival) {
return *this << static_cast<unsigned long long>(ival);
}
OutputStream& OutputStream::operator<<(int ival) {
return *this << static_cast<long long>(ival);
}
OutputStream& OutputStream::operator<<(unsigned int ival) {
return *this << static_cast<unsigned long long>(ival);
}
OutputStream& OutputStream::operator<<(long ival) {
return *this << static_cast<long long>(ival);
}
OutputStream& OutputStream::operator<<(unsigned long ival) {
return *this << static_cast<unsigned long long>(ival);
}
// Print a signed , integral number.
OutputStream& OutputStream::operator<<(long long ival) {
/* Print '-' if number is negative
*
* In case ival is equal to LONG_LONG_MIN (0x8000000000000000), this
* multiplication with -1 will overflow and, as for all signed overflows,
* is not specified in C/C++. Thus, this operation will only work when
* the system uses two's complement:
* ~(0x8000000000000000) + 1 = 0x7fffffffffffffff + 1 = 0x8000000000000000
*
* When casting 0x8000000000000000 to unsigned long long, the value will
* be (correctly) interpreted as -(LONG_LONG_MIN).
*
* A solution conforming (more) to the standard could be:
* if ((ival < 0) && (base == 10)) {
* put('-');
* if (ival == LONG_LONG_MIN) {
* return *this << static_cast<unsigned long long>(LONG_LONG_MAX -
* (LONG_LONG_MAX + LONG_LONG_MIN)); } else { return *this <<
* static_cast<unsigned long long>(-ival);
* }
* (However it introduces additional overhead)
*/
if ((ival < 0) && (base == 10)) {
put('-');
ival = -ival;
}
// Print the remaining positive number using the unsigned output
return *this << static_cast<unsigned long long>(ival);
}
// Print a unsigned, integral number.
OutputStream& OutputStream::operator<<(unsigned long long ival) {
if (base == 0) {
base = 16;
}
if (base == 2) {
put('0');
put('b');
} else if (base == 8) {
put('0'); // octal numbers are prefixed with 0
} else if (base == 16) {
put('0'); // hexadecimal numbers are prefixed with 0x
put('x');
}
// Determine the largest potency in the number system used, which is
// still smaller than the number to be printed
unsigned long long div;
for (div = 1; ival / div >= static_cast<unsigned long long>(base);
div *= base) {
}
// print number char by char
for (; div > 0; div /= static_cast<unsigned long long>(base)) {
auto digit = ival / div;
if (digit < 10) {
put(static_cast<char>('0' + digit));
} else {
put(static_cast<char>('a' + digit - 10));
}
ival %= div;
}
return *this;
}
// Print a pointer as hexadecimal number
OutputStream& OutputStream::operator<<(const void* ptr) {
int oldbase = base;
base = 16;
*this << reinterpret_cast<uintptr_t>(ptr);
base = oldbase;
return *this;
}
// Calls one of the manipulator functions
OutputStream& OutputStream::operator<<(OutputStream& (*f)(OutputStream&)) {
return f(*this);
}
/* STREAM MANIPULATORS
*
* The functions below take and return a reference to an OutputStream object
* and are called by OutputStream& operator << (OutputStream& (*f)
* (OutputStream&)); The purpose of theses manipulator functions is modifying
* the behavior of the stream the are executed on, such as changing the number
* system.
*/
// flush: Explicit buffer flush
OutputStream& flush(OutputStream& os) {
os.flush();
return os;
}
// endl: Inserts a newline to the output
OutputStream& endl(OutputStream& os) {
os << '\n' << flush;
return os;
}
// bin: Selects the binary number system
OutputStream& bin(OutputStream& os) {
os.base = 2;
return os;
}
// oct: Selects the octal number system
OutputStream& oct(OutputStream& os) {
os.base = 8;
return os;
}
// dec: Selects the decimal number system
OutputStream& dec(OutputStream& os) {
os.base = 10;
return os;
}
// hex: Selects the hexadecimal number system
OutputStream& hex(OutputStream& os) {
os.base = 16;
return os;
}

209
object/outputstream.h Normal file
View File

@@ -0,0 +1,209 @@
/*! \file
* \brief This file contains the \ref OutputStream
*
* Along with the class OutputStream itself, this file contains definitions for
* the manipulators \ref hex, \ref dec, \ref oct, and \ref bin, which are used
* for changing the radix, and \ref endl for signaling the end of the current
* line.
* \ingroup io
*
* \par Manipulators
* To simplify formatting text and numbers using the class OutputStream, we
* define so-called manipulators. For example, the expression <tt>kout << "a = "
* << dec << a << " is hexadecimal " << hex << a << endl;</tt> should, at first,
* print the value stored in decimal and then in hexadecimal form, followed by a
* line break. The intended properties can be realized by implementing \ref hex,
* \ref dec, \ref oct, \ref bin, and \ref endl as functions (i.e., they are, in
* particular, not methods of \ref OutputStream) that take (as first parameter)
* and return a reference to an OutputStream object. When compiling the
* expression shown above, the method <tt>OutputStream& OutputStream::operator<<
* ((*f*) (OutputStream&))</tt> is chosen when one of the functions \ref hex,
* \ref dec, \ref oct, \ref bin, or \ref endl is streamed into an \ref
* OutputStream, which finally will execute the passed function.
*
* \note The term manipulator originates from the book
* [The C++ Programming Language](http://www.stroustrup.com/4th.html)
* by Bjarne Stroustrup. Refer to this book for further explanations.
*/
#pragma once
#include "../types.h"
#include "./stringbuffer.h"
/*! \brief The class OutputStream corresponds, essentially, to the class ostream
* from the C++ IO-Stream library.
*
* As relying on the method \ref Stringbuffer::put() is quite cumbersome when
* not only printing single characters, but numbers and whole strings, the
* class OutputStream provides a convenient way of composing output of
* variables of varying data types. Therefore, OutputStream implements shift
* operators `operator<<`` for various data types (similar to those known from
* the C++ IO-Stream library)
*
* For further convenience, OutputStream also allows printing integral numbers
* in decimal, binary, octal, and hexadecimal format. Remember that, for
* negative numbers, the sign is only printed when using the decimal number
* system; for binary, octal, and hex, the number is printed as stored in the
* machine word without interpreting the sign. For Intel CPUs, two's complement
* is used for storing negative values, `-1`, for example, will print hex
* `FFFFFFFF` and octal `37777777777`.
*
* OutputStream's public methods/operators all return a reference to the object
* they are called on (i.e. `*this`). Returning `*this` allows chaining those
* stream operators in a single expression, such as
* <tt>kout << "a = " << a</tt>;
*
* At this point in time, OutputStream implements `operator<<`` for chars,
* strings and whole numbers. An additional `operator<<` allows using
* manipulators whose detailed description is given below.
*/
class OutputStream : public Stringbuffer {
OutputStream(const OutputStream&) = delete;
OutputStream& operator=(const OutputStream&) = delete;
public:
/*! \brief Number system used for printing integral numbers (one of 2,
* 8, 10, or 16)
*/
int base;
/*! \brief Default constructor. Initial number system is decimal.
*
*/
OutputStream() : base(10) {}
/*! \brief Destructor
*/
virtual ~OutputStream() {}
/*! \brief Clears the buffer.
*
* Pure virtual method that must be implemented by derived
* (non-abstract) classes.
* Formatting of the buffer contents can be implemented differently by
* different derived classes
*/
virtual void flush() = 0;
/*! \brief Print a single character
*
* \param c Character to be printed
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& operator<<(char c);
/*! \brief Print a single character
* \note In C, there are no "characters" in that sense, but only
* integers. A `char`, therefore, is a 8 bit number with the most
* significant bit (optionally) representing a sign.
* Depending on whether signed or not, the value ranges are [-128, 127]
* or [0; 255]. For GCC, a `char` is a `signed char`.
*
* \param c Character to be printed
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& operator<<(unsigned char c);
/*! \brief Printing a null-terminated string
*
* \param string String to be printed
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& operator<<(const char* string);
/*! \brief Print a boolean value
*
* \param b Boolean to be printed
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& operator<<(bool b);
/*! \brief Print an integral number in radix base
*
* \param ival Number to be printed
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& operator<<(short ival);
/// \copydoc OutputStream::operator<<(short)
OutputStream& operator<<(unsigned short ival);
/// \copydoc OutputStream::operator<<(short)
OutputStream& operator<<(int ival);
/// \copydoc OutputStream::operator<<(short)
OutputStream& operator<<(unsigned int ival);
/// \copydoc OutputStream::operator<<(short)
OutputStream& operator<<(long ival);
/// \copydoc OutputStream::operator<<(short)
OutputStream& operator<<(unsigned long ival);
/// \copydoc OutputStream::operator<<(short)
OutputStream& operator<<(long long ival);
/// \copydoc OutputStream::operator<<(short)
OutputStream& operator<<(unsigned long long ival);
/*! \brief Print a pointer as hexadecimal number
*
* \param ptr Pointer to be printed
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& operator<<(const void* ptr);
/*! \brief Calls one of the manipulator functions.
*
* Method that calls the manipulator functions defined below, which
* allow modifying the stream's behavior by, for instance, changing the
* number system.
*
* \param f Manipulator function to be called
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& operator<<(OutputStream& (*f)(OutputStream&));
};
/*! \brief Enforces a buffer flush.
*
* \param os Reference to stream to be flushed.
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& flush(OutputStream& os);
/*! \brief Prints a newline character to the stream and issues a buffer flush.
*
* \param os Reference to stream to be modified.
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& endl(OutputStream& os);
/*! \brief Print subsequent numbers in binary form.
*
* \param os Reference to stream to be modified.
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& bin(OutputStream& os);
/*! \brief Print subsequent numbers in octal form.
*
* \param os Reference to stream to be modified.
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& oct(OutputStream& os);
/*! \brief Print subsequent numbers in decimal form.
*
* \param os Reference to stream to be modified.
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& dec(OutputStream& os);
/*! \brief Print subsequent numbers in hex form.
*
* \param os Reference to stream to be modified.
* \return Reference to OutputStream os; allows operator chaining.
*/
OutputStream& hex(OutputStream& os);

293
object/queue.h Normal file
View File

@@ -0,0 +1,293 @@
/*! \file
* \brief Templated \ref Queue for arbitrary objects.
*/
#pragma once
#include "../arch/core.h"
#include "../debug/assert.h"
#include "../object/outputstream.h"
#include "../types.h"
/*! \brief Templated Queue for arbitrary objects.
*
* Queue is implemented by a head-object (Queue<T>) and next-pointers embedded
* in the queued objects. This Queue supports arrays of next-pointers by passing
* an index into the constructor identifying the index into the next-pointer
* array. By passing a different get_link function into the constructor, the
* member name of the next-pointer array can be changed and objects can be
* contained in different independent queues.
*/
template <class T>
class Queue {
/*! \brief Default get_link implementation returns a pointer to the
* link_index'th element of the next-pointer array.
* The function assumes a member named "queue_link" that stores the
* next-pointer.
*
* If your object contains a queue_link member you can just ignore this
* function and the get_link keyword argument of the constructor.
*
* \param[in] obj the object whose link should be accessed.
* \param[in] link_index the index within the array.
*
* \return A pointer to the next-object pointer.
*/
static T** default_get_link(T& obj, unsigned link_index) {
assert(link_index < sizeof(T::queue_link) / sizeof(void*));
return &obj.queue_link[link_index];
}
/// Type definition for the get_link function
typedef T** (*NextFunc)(T&, unsigned);
/// Queue-local index into the next-pointer array
unsigned link_index;
/// Provides the same signature for single- and multi-core Queue
T** get_link_wrapped(T& obj) { return get_link(obj, link_index); }
/// Function pointer to the get_link function, called whenever the
/// next pointer array is referenced
const NextFunc get_link;
/// head points to the first element (the one returned on first dequeue).
/// Can be nullptr if the queue is empty.
T* head;
/// tail points to the last element (the one last added).
/// Is only valid if head != nullptr
T* tail;
// Prevent copies and assignments
Queue(const Queue&) = delete;
Queue& operator=(const Queue&) = delete;
public:
/*! \brief Minimal forward iterator
* You can use this iterator to iterate the queue like a normal STL container.
* It only supports forward iteration, since the queue is single linked.
*/
class Iterator {
private:
Queue<T>& queue;
T* current;
friend class Queue<T>;
Iterator(Queue<T>& queue, T* current) : queue(queue), current(current) {}
public:
Iterator operator+(unsigned num) {
if (current == nullptr) {
return *this;
}
T* temp = current;
while (num--) {
temp = queue.next(*temp);
}
return Iterator(queue, temp);
}
// pre increment
Iterator& operator++() {
current = queue.next(*current);
return *this;
}
// post increment
Iterator operator++(int) {
auto temp = Iterator(queue, current);
current = queue.next(*current);
return temp;
}
T* operator*() { return current; }
bool operator==(const Iterator& other) { return current == other.current; }
bool operator!=(const Iterator& other) { return !(*this == other); }
};
constexpr Queue(Queue&&) = default;
/*! \brief Constructor
* \param[in] link_index denotes the index into the next-pointer array
* to be used by this
*queue-object
* \param[in] get_link A function pointer to the get_link, i.e. a function
* which returns a pointer to the
*next-pointer of an element in the Queue.
*/
explicit Queue(unsigned link_index, NextFunc get_link = default_get_link)
: link_index(link_index),
get_link(get_link),
head(nullptr),
tail(nullptr) {}
/*! \brief Enqueues the provided item at the end of the queue. If the element
* is already contained in the queue, false will be returned
* \param[in] item element to be appended (enqueued).
* \return false if the element already was enqueued (and nothing was done)
* or not (and it is now enqueued, then true)
*/
bool enqueue(T& item) {
T** nextptr = get_link_wrapped(item);
if (*nextptr != nullptr || (head != nullptr && tail == &item)) {
return false;
}
*nextptr = nullptr;
if (head == nullptr) {
head = tail = &item;
} else {
assert(tail != nullptr);
*get_link_wrapped(*tail) = &item;
tail = &item;
}
return true;
}
/*! \brief insert a new element at the start of the queue
* \param[in] item the new item to add
* \return true if successful, false if item was already in the queue
**/
bool insertFirst(T& item) {
T** nextptr = get_link_wrapped(item);
if (*nextptr != nullptr || (head != nullptr && tail == &item)) {
return false;
}
if (head == nullptr) {
tail = &item;
}
*nextptr = head;
head = &item;
return true;
}
/*! \brief Insert a new element item into the list after an element after.
* Returns false if item is already in the/a list or after is not in this
*list
* \param[in] after the element after which the new one should be inserted
* \param[in] item the new element to add
* \return true if successful, false if item was in the list or after was not
**/
bool insertAfter(T& after, T& item) {
// if queue is empty there is no after
// and tail is not valid so we need to check head here
if (head == nullptr) {
return false;
}
if (&after == tail) {
return enqueue(item);
}
T** nextptr = get_link_wrapped(item);
// if item is already in the list return false
if (*nextptr != nullptr || tail == &item) {
return false;
}
T** pnextptr = get_link_wrapped(after);
// if after is NOT in the list, return false
if (!(pnextptr != nullptr || tail == &after)) {
return false;
}
*nextptr = *pnextptr;
*pnextptr = &item;
return true;
}
/*! \brief return the next element of a given one or nullptr if the end is
*reached
* \param[in] item the current item
* \return the next element or nullptr if the end is reached or the item is
*not in this list
**/
T* next(T& item) {
T** nextptr = get_link_wrapped(item);
// if item is already in the list return nullptr
if (head == nullptr || (*nextptr == nullptr && tail != &item)) {
return nullptr;
}
return *nextptr;
}
/*! \brief Return whether or not the queue is empty
* \return True if the queue is empty or false otherwise.
*/
bool is_empty() const { return (head == nullptr); }
/*! \brief Removes the first element in the queue and returns it.
* \note Does not update the tail-pointer
* \return Pointer to the removed item or `nullptr` if the queue was empty.
*/
T* dequeue() {
T* out = head;
if (head != nullptr) {
T** nextptr = get_link_wrapped(*head);
head = *nextptr;
*nextptr = nullptr;
}
return out;
}
/*! \brief Removes a given element from the queue and returns that element,
* or nullptr if it was not present
* \return pointer to the removed element, or nullptr if not present
*/
T* remove(T* that) {
if (!that) return nullptr;
T* cur = head;
T** next_link;
if (head == that) {
head = *get_link_wrapped(*head);
*get_link_wrapped(*that) = nullptr;
return that;
}
while (cur) {
next_link = get_link_wrapped(*cur);
if (*next_link == that) {
*next_link = *get_link_wrapped(**next_link);
if (that == tail) {
tail = cur;
}
*get_link_wrapped(*that) = nullptr;
return that;
}
cur = *next_link;
}
return nullptr;
}
/// get an iterator to the first element
Queue<T>::Iterator begin() { return Queue<T>::Iterator(*this, head); }
/// get an iterator that marks the end of list
Queue<T>::Iterator end() { return Queue<T>::Iterator(*this, nullptr); }
/// get the first element of the queue
T* first() { return head; }
/// get the last element of the queue
T* last() { return (head == nullptr ? nullptr : tail); }
};
/*! \brief Overload stream operator for list printing.
*
* With this a list can be printed. The elements itself are not printed, just
* the pointer.
*/
template <class T>
OutputStream& operator<<(OutputStream& os, Queue<T>& queue) {
os << "{";
for (typename Queue<T>::Iterator it = queue.begin(); it != queue.end();
++it) {
os << *it;
if (it + 1 != queue.end()) {
os << ", ";
}
}
return os << "}";
}

3
object/stringbuffer.cc Normal file
View File

@@ -0,0 +1,3 @@
#include "stringbuffer.h"
void Stringbuffer::put(char c) { (void)c; }

71
object/stringbuffer.h Normal file
View File

@@ -0,0 +1,71 @@
/*! \file
* \brief \ref Stringbuffer composes single characters into a buffer
*/
#pragma once
#include "../types.h"
/*! \brief The class Stringbuffer composes single characters into a longer text
* that can be processed on block.
*
* To make Stringbuffer as versatile as possible, the class does make
* assumptions about neither the underlying hardware, nor the meaning of
* "processing". When flush() is called (i.e., either on explicit request or
* once the buffer is full). To be hardware independent, flush() is to be
* implemented by the derived classes.
*
* \par Hints for Implementation
* Use a buffer of fixed size for caching characters, which should be
* accessible by derived classes.
* Keep in mind that the derived implementation of flush() will need to know
* about numbers of characters in the buffer.
*
* \par Notes
* Reason for the existence of this class is that generating longer texts is
* often implemented by assembly of small fragments (such as single characters
* or numbers).
* However, writing such small fragments directly to (for example) screen is
* quite inefficient (e.g., due to the use of IO ports, syscalls, or locks) and
* can be improved drastically by delaying the output step until the assembly
* is finished (or the buffer runs full).
*/
class Stringbuffer {
// Prevent copies and assignments
Stringbuffer(const Stringbuffer&) = delete;
Stringbuffer& operator=(const Stringbuffer&) = delete;
// All variables and methods are protected in this class,
// as the derived classes need direct access to be buffer,
// the constructor, the destructor, and the method put.
// flush() is to be implemented either way and may be redefined
// as public.
protected:
/*! \brief Constructor; Marks the buffer as empty
*/
Stringbuffer() {}
/*! \brief Inserts a character into the buffer.
*
* Once the buffer is full, a call to flush() will be issued and
* thereby clearing the buffer.
*
* \param c Char to be added
*
* \todo(11) Implement
*/
void put(char c);
/*! \brief Flush the buffer contents
*
* This method is to be defined in derived classes, as only those know
* how to print characters.
* flush() is required to reset the position pos.
*/
virtual void flush() = 0;
public:
/*! \brief Destructor (nothing to do here)
*/
virtual ~Stringbuffer() {}
};