From 5bfe56aafddb52ea8dff1359195f3fa74154a621 Mon Sep 17 00:00:00 2001 From: Alexander Lochmann Date: Fri, 22 Sep 2023 11:19:29 +0200 Subject: [PATCH] Skeleton --- .gitignore | 2 + .gitlab-ci.yml | 32 +++++ CPPLINT.cfg | 16 +++ LICENSE | 10 ++ README.md | 42 +++++++ object/outputstream.cc | 1 + object/outputstream.h | 237 +++++++++++++++++++++++++++++++++++++ object/stringbuffer.cc | 2 + object/stringbuffer.h | 76 ++++++++++++ test-stream/Makefile | 22 ++++ test-stream/console_out.cc | 2 + test-stream/console_out.h | 33 ++++++ test-stream/file_out.cc | 2 + test-stream/file_out.h | 59 +++++++++ test-stream/test.cc | 36 ++++++ 15 files changed, 572 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 CPPLINT.cfg create mode 100644 LICENSE create mode 100644 README.md create mode 100644 object/outputstream.cc create mode 100644 object/outputstream.h create mode 100644 object/stringbuffer.cc create mode 100644 object/stringbuffer.h create mode 100644 test-stream/Makefile create mode 100644 test-stream/console_out.cc create mode 100644 test-stream/console_out.h create mode 100644 test-stream/file_out.cc create mode 100644 test-stream/file_out.h create mode 100644 test-stream/test.cc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ccf0ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.build* +.solution diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..9d38df3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,32 @@ +# This is a configuration file for GitLab Continuous Integration (CI). +# +# At each "push", GitLab will use this file (named '.gitlab-ci.yml') to start +# a docker container with the image of a Linux distribution (in this case +# Ubuntu Focal) with an already installed build tool chain and compile the +# source in the git repository. +# The status (indicated by the exit code of 'make') is clearly visible on the +# project page of your repository in GitLab: +# If a commit contains an invalid (untranslatable) state, an error (red 'x') +# is displayed and the author is notified by mail. +# +# Please note: This will only test the build of your operating system, +# but not the [correct] functionality! +# +#Further information: https://docs.gitlab.com/ee/ci/ + +GCC-Build: + image: inf4/stubs:gcc-x64 + script: + - if [ -f Makefile ] ; then make clean ; make ; fi + +# Optional: Use Clang 11 +#Clang-Build: +# image: inf4/stubs:clang-x64 +# script: +# - make clean ; make CXX=clang++-11 + +# Check style guide using cpplint +linter: + image: inf4/stubs:cpplint + script: + - if [ -f Makefile ] ; then make clean ; make lint ; fi diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 0000000..2622a44 --- /dev/null +++ b/CPPLINT.cfg @@ -0,0 +1,16 @@ +# StuBS Coding Style Checker +# +# Wir orientieren uns grob an den Google C++ Style Guide ( http://google.github.io/styleguide/cppguide.html ) +# mit primär folgenden Änderungen/Anpassungen: +# +# - Tabs statt Leerzeichen. Spart Bytes ;) +# - Zeilenlänge ist 120 +# - Keine Angaben zum Copyright +# - Aufgrund des Aufgabenbuildsystems sind neue / leere Zeilen leider nicht immer vermeidbar +# +# Zum Prüfen empfiehlt sich beispielsweise das Pythonscript CPPLINT.py ( https://github.com/cpplint/cpplint ) +# welches mit dieser Konfigurationsdatei arbeiten kann. +# +set noparent +filter=-whitespace/tab,-legal/copyright,-runtime/int,-runtime/threadsafe_fn,-readability/todo,-build/include_subdir,-runtime/references,-build/include_what_you_use,-whitespace/blank_line +linelength=120 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..641387f --- /dev/null +++ b/LICENSE @@ -0,0 +1,10 @@ +Copyright 1998-2002 Institut für Verteilte Systeme (IVS), Otto-von-Guericke-Universität Magdeburg +Copyright 2002-2022 Lehrstuhl für Informatik 4, Friedrich-Alexander-Universität Erlangen-Nürnberg + +Diese Vorlage dient als Grundlage für Lehrveranstaltungen und darf nicht ohne vorherige, schriftliche Erlaubnis der Urheberrechtsinhaber veröffentlicht oder weitergegeben werden. +Es ist erlaubt und wünschenswert, diese Vorlage als Inspiration für eigene Projekte zu verwenden, es wird allerdings erbeten, dass die Vorgabe nicht mit deiner Lösung veröffentlicht wird. +Wir, als Lehrende, möchten alle teilnehmenden Studierenden dazu ermutigen eine eigene Lösung zu erstellen; eine veröffentlichte Lösung ist ein Anreiz zum Abschreiben, den wir gerne vermeiden möchten. + +This skeleton is provided as a foundation for educational purposes and therefore MUST NOT BE DISTRIBUTED OR PUBLISHED without prior, written consent of the copyright holders. +You are free to use this skeleton as inspiration for your projects, but, please, do not publish it along with your solution. +We, as lecturers, want to encourage every participating student to write a solution themself; a public solution is an allurement to copying we want to avoid. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6b362ac --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +MPStuBS - Multiprozessor Studenten Betriebssystem +================================================= + +Coding Guidelines +----------------- + +Similar to [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) but with following exceptions: + - No license boilerplate + - *Tabs* instead of *Spaces* + - Line length of 120 characters + - `#pragma once` instead of `#include` guards + +The code should be *self-documenting*, don't state the obvious! +However, this does not make comments superfluous: +Since good naming is sometimes not enough, more advanced parts need to be documented, +so any operating system developer should be able to easily understand your code. + +### Naming Convention + + - **Variables**: lowercase with underscore + + char* variable_name; + + - **Constants** (and **enum** values): uppercase with underscore + + const int CONST_VALUE = 42; + + - **Type Names** (`class`/`struct`/`namespace`/`enum`): Capital letter, camel case + + class SomeClassName; + + - **Methods/Functions** (C++): start with lowercase letter, then camel case + + void someFunctionName(); + + - **extern "C" Functions**: lowercase with underscore (like variables). + + void interrupt_handler(int vector); + + - **File Names**: lowercase, main type name, underscores only if is a sub type + + folder/classname.cc diff --git a/object/outputstream.cc b/object/outputstream.cc new file mode 100644 index 0000000..5a5a450 --- /dev/null +++ b/object/outputstream.cc @@ -0,0 +1 @@ +#include "outputstream.h" diff --git a/object/outputstream.h b/object/outputstream.h new file mode 100644 index 0000000..e9d3e7b --- /dev/null +++ b/object/outputstream.h @@ -0,0 +1,237 @@ +/*! \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 + * kout << "a = " << dec << a << " is hexadecimal " << hex << a << endl; + * 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 show above, the method + * OutputStream& OutputStream::operator<< ((*f*) (OutputStream&)) + * is chosen when one of the functions \ref hex, \ref dec, \ref oct, \ref bin, or \ref endl + * is streamed 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 "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 + * kout << "a = " << a; + * + * 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 { + 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. + * + * \todo Implement Constructor + * + */ + OutputStream() {} + + /*! \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 + * + * \todo Implement Operator + * + * \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`. + * + * \todo Implement Operator + * + * \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 + * + * \todo Implement Operator + * + * \param string String to be printed + * \return Reference to OutputStream os; allows operator chaining. + */ + OutputStream& operator << (const char* string); + + /*! \brief Print a boolean value + * + * \todo Implement Operator + * + * \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` + * + * \todo Implement Operator + * + * \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 + * + * \todo Implement Operator + * + * \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. + * + * \todo Implement Operator + * + * \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. + * + * \todo Implement Manipulator + * + * \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. + * + * \todo Implement Manipulator + * + * \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. + * + * \todo Implement Manipulator + * + * \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. + * + * \todo Implement Manipulator + * + * \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. + * + * \todo Implement Manipulator + * + * \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. + * + * \todo Implement Manipulator + * + * \param os Reference to stream to be modified. + * \return Reference to OutputStream os; allows operator chaining. + */ +OutputStream& hex(OutputStream& os); diff --git a/object/stringbuffer.cc b/object/stringbuffer.cc new file mode 100644 index 0000000..bb96473 --- /dev/null +++ b/object/stringbuffer.cc @@ -0,0 +1,2 @@ +#include "stringbuffer.h" + diff --git a/object/stringbuffer.h b/object/stringbuffer.h new file mode 100644 index 0000000..152cc92 --- /dev/null +++ b/object/stringbuffer.h @@ -0,0 +1,76 @@ +/*! \file + * \brief \ref Stringbuffer composes single characters into a buffer + */ + +#pragma once + +/*! \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: + /// buffer containing characters that will be printed upon flush() + char buffer[80]; + /// current position in the buffer + long unsigned pos; + + /*! \brief Constructor; Marks the buffer as empty + * + * \todo Complete Constructor + */ + 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. + * + * \todo Implement Method + * + * \param c Char to be added + */ + 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() { } +}; diff --git a/test-stream/Makefile b/test-stream/Makefile new file mode 100644 index 0000000..a72c78a --- /dev/null +++ b/test-stream/Makefile @@ -0,0 +1,22 @@ +VERBOSE = @ +OBJDIR = build +CXX = g++ +MKDIR = mkdir +CC_SOURCES = ../test-stream/console_out.cc ../test-stream/test.cc ../test-stream/file_out.cc ../object/outputstream.cc ../object/stringbuffer.cc +CXXFLAGS = -std=c++11 -m64 -I../object -I. +TARGET = $(OBJDIR)/test + +all: run + +run: $(TARGET) + @./$< + +$(TARGET): $(CC_SOURCES) + $(VERBOSE) $(MKDIR) -p $(OBJDIR) + $(VERBOSE) $(CXX) -o $@ $(CXXFLAGS) $^ + +clean: + @echo "RM $(OBJDIR)" + $(VERBOSE) rm -rf $(OBJDIR) + +.PHONY: all run clean diff --git a/test-stream/console_out.cc b/test-stream/console_out.cc new file mode 100644 index 0000000..adbe263 --- /dev/null +++ b/test-stream/console_out.cc @@ -0,0 +1,2 @@ +#include "console_out.h" + diff --git a/test-stream/console_out.h b/test-stream/console_out.h new file mode 100644 index 0000000..222b7a5 --- /dev/null +++ b/test-stream/console_out.h @@ -0,0 +1,33 @@ +/*! \file + * \brief \ref ConsoleOut "Console" \ref OutputStream "output" (for the voluntary C++ exercise only) + */ + +#pragma once + +#include "outputstream.h" + +/*! \brief Write text on console (`STDOUT`) + * + * This class allows writing to the console similar to `std::cout` from the standard C++ library. + * The class is derived from \ref OutputStream. + */ +class ConsoleOut : public OutputStream { + // Prevent copies and assignments + ConsoleOut(const ConsoleOut&) = delete; + ConsoleOut& operator=(const ConsoleOut&) = delete; + + public: + /*! \brief Constructor + * + * \todo Implement constructor + */ + ConsoleOut(); + + /*! \brief Output the string on the screen. + * + * The implementation should solely use `putchar()` + * + * \todo Implement virtual method + */ + virtual void flush() override; //NOLINT +}; diff --git a/test-stream/file_out.cc b/test-stream/file_out.cc new file mode 100644 index 0000000..91c63cc --- /dev/null +++ b/test-stream/file_out.cc @@ -0,0 +1,2 @@ +#include "file_out.h" + diff --git a/test-stream/file_out.h b/test-stream/file_out.h new file mode 100644 index 0000000..034b9ef --- /dev/null +++ b/test-stream/file_out.h @@ -0,0 +1,59 @@ +/*! \file + * \brief \ref FileOut "File" \ref OutputStream "output" (for the voluntary C++ exercise only) + */ + +#pragma once + +#include "outputstream.h" + +/*! \brief Write text into file + * + * This class allows a comfortable output to a file only by using the elementary + * system calls `open()` / `write()` / `close()` and (optional) `fsync()`. + * The class is derived from \ref OutputStream. + */ +class FileOut : public OutputStream { + // TODO: Add (private) attributes, if required + + public: + /*! \brief Constructor + * + * Opens the file for writing using the system call `open()`. + * \param path Path to the output file + * + * \todo Implement constructor + */ + explicit FileOut(const char * path); + + /*! \brief Destructor + * + * Close the output file (using the system call `close()`) + * + * \todo Implement destructor + */ + virtual ~FileOut(); + + /*! \brief Get path of the output file + * + * \return Path to output file (as defined in constructor) + * + * \todo Implement Method + */ + const char * getPath(); + + /*! \brief Number of output files which are currently opened (with this class) + * + * \return Number of active files + * + * \todo Implement Method + */ + static int count(); + + /*! \brief Write the string to the open file. + * + * The implementation should only use the system calls `write()` and `fsync()`. + * + * \todo Implement virtual Method + */ + virtual void flush() override; //NOLINT +}; diff --git a/test-stream/test.cc b/test-stream/test.cc new file mode 100644 index 0000000..3bed04a --- /dev/null +++ b/test-stream/test.cc @@ -0,0 +1,36 @@ +#include "console_out.h" +#include "file_out.h" + +ConsoleOut cout; +FileOut foo("foo.txt"); + +int main() { + cout << "Console Test -> " << endl; + cout << " bool: " << true << " -> true" << endl; + cout << " zero: " << 0 << " -> 0" << endl; + cout << " binary: " << bin << 42 << dec << " -> 0b101010" << endl; + cout << " octal: " << oct << 42 << dec << " -> 052" << endl; + cout << " hex: " << hex << 42 << dec << " -> 0x2a" << endl; + cout << " uint64_t max: " << ~((unsigned long long)0) << " -> 18446744073709551615" << endl; + cout << " int64_t max: " << ~(1ll<<63) << " -> 9223372036854775807" << endl; + cout << " int64_t min: " << (1ll<<63) << " -> -9223372036854775808" << endl; + cout << " some int64_t: " << (-1234567890123456789) << " -> -1234567890123456789" << endl; + cout << " some int64_t: " << (1234567890123456789) << " -> 1234567890123456789" << endl; + cout << " pointer: " << reinterpret_cast(1994473406541717165ull) << " -> 0x1badcafefee1dead" << endl; + cout << endl; + + cout << "File Test" << endl + << " currently open: " << FileOut::count() << endl + << " writing into '" << foo.getPath() << "'..." << endl; + foo << "C makes it easy to shoot yourself in the foot;" << endl; + foo << "C++ makes it harder, but when you do it blows your whole leg off." << endl; + { + FileOut bar("bar.txt"); + cout << " opened the " << FileOut::count() << ". file, " << endl; + cout << " writing into '" << bar.getPath() << "'..." << endl; + bar << "Anyone who claims to have the perfect programming language is either a fool or a salesman or both" << endl; + } + cout << " having only " << FileOut::count() << " file opened since the other is out of scope" << endl; + + return 0; +}