Handout
This commit is contained in:
62
object/bbuffer.h
Normal file
62
object/bbuffer.h
Normal 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
118
object/key.cc
Normal 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
165
object/key.h
Normal 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
181
object/outputstream.cc
Normal 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
209
object/outputstream.h
Normal 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
293
object/queue.h
Normal 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
3
object/stringbuffer.cc
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "stringbuffer.h"
|
||||
|
||||
void Stringbuffer::put(char c) { (void)c; }
|
||||
71
object/stringbuffer.h
Normal file
71
object/stringbuffer.h
Normal 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() {}
|
||||
};
|
||||
Reference in New Issue
Block a user