diff --git a/.github/workflows/publish-pages.yml b/.github/workflows/publish-pages.yml deleted file mode 100644 index 8328f49..0000000 --- a/.github/workflows/publish-pages.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Deploy to GH Pages - -on: - push: - branches: - - main - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - name: Publish site - runs-on: ubuntu-latest - steps: - - name: Checkout main - uses: actions/checkout@v3 - - - name: Build and deploy - uses: shalzz/zola-deploy-action@v0.20.0 - env: - BUILD_ONLY: true - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: "public" - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 75ccd5a..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.idea/ -public - diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 2d42247..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.associations": { - "*.html": "liquid" - } -} diff --git a/404.html b/404.html new file mode 100644 index 0000000..55c251c --- /dev/null +++ b/404.html @@ -0,0 +1,62 @@ + + + + + +404 + + + + + + + + + + + + +
+ +
+ +
+ + +
+ +
+

Lost?

+

This page does not exist.

+
+ +
+ + + + +
+ + + + + \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..7254db4 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +thoughts.henrygressmann.de diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 7051eb5..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,25 +0,0 @@ -License for code only (not blog content): - -MIT License - -Copyright (c) 2022 Henry Gressmann -Copyright (c) 2019 Paweł Romanowski -Copyright (c) 2019 panr - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/buttons.css b/buttons.css new file mode 100644 index 0000000..c7b9eb7 --- /dev/null +++ b/buttons.css @@ -0,0 +1 @@ +.button-container{display:table;margin-left:auto;margin-right:auto}button,.button,a.button{position:relative;display:flex;align-items:center;justify-content:center;padding:8px 18px;margin-bottom:5px;text-align:center;border-radius:8px;border:1px solid transparent;appearance:none;cursor:pointer;outline:none}button.outline,.button.outline,a.button.outline{background:transparent;box-shadow:none;padding:8px 18px}button.outline :hover,.button.outline :hover,a.button.outline :hover{transform:none;box-shadow:none}button.primary,.button.primary,a.button.primary{box-shadow:0 4px 6px rgba(50,50,93,0.11),0 1px 3px rgba(0,0,0,0.08)}button.primary:hover,.button.primary:hover,a.button.primary:hover{box-shadow:0 2px 6px rgba(50,50,93,0.21),0 1px 3px rgba(0,0,0,0.08)}button.link,.button.link,a.button.link{background:none;font-size:1rem}button.small,.button.small,a.button.small{font-size:.8rem}button.wide,.button.wide,a.button.wide{min-width:200px;padding:14px 24px}a.read-more,a.read-more:hover,a.read-more:active{display:inline-flex;background:none;box-shadow:none;padding:0;margin:20px 0;max-width:100%}.code-toolbar{margin-bottom:20px}.code-toolbar .toolbar-item a{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px 8px;margin-bottom:5px;text-align:center;font-size:13px;font-weight:500;border-radius:8px;border:1px solid transparent;appearance:none;cursor:pointer;outline:none} diff --git a/config.toml b/config.toml deleted file mode 100644 index d0ebf4b..0000000 --- a/config.toml +++ /dev/null @@ -1,27 +0,0 @@ -base_url="https://blog.henrygressmann.de" -build_search_index=true -compile_sass=true -description="a blog about software development, technology and other stuff" -generate_feeds=true - -taxonomies=[{name="tags"}, {name="series", feed=true}] - -title="henry's blog" - -[markdown] -extra_syntaxes_and_themes=["highlight_themes", "syntaxes"] -highlight_code=true -highlight_theme="dracula" - -[extra] -accent_color="blue" -background_color="green" - -# Whether to show links to earlier and later posts -# on each post page (defaults to true). -enable_post_view_navigation=true - -# The text shown at the bottom of a post, -# before earlier/later post links. -# Defaults to "Thanks for reading! Read other posts?" -post_view_navigation_prompt="Thanks for reading! Read other posts?" diff --git a/content/2023/_index.md b/content/2023/_index.md deleted file mode 100644 index 2d0d600..0000000 --- a/content/2023/_index.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -transparent: true ---- \ No newline at end of file diff --git a/content/2023/koi.md b/content/2023/koi.md deleted file mode 100644 index 805f3d2..0000000 --- a/content/2023/koi.md +++ /dev/null @@ -1,313 +0,0 @@ -+++ -title = "Koi, the kinda okay image format" -description = "Creating a new lossless image format to learn about image compression and some notes on performance optimization" -date = 2023-04-02 -updated = 2023-05-15 -aliases = ["koi"] - -[taxonomies] -tags = ["koi", "image"] -+++ - -{% quote (class="info")%} -This post has been updated to reflect changes in the koi file format. The original post can be found [here](https://github.com/explodingcamera/blog/blob/8a3c81d81a251b4ac64714c4e5f60a2c07376551/content/koi.md). -{% end %} - -I've been working on a new image format called [koi](https://github.com/explodingcamera/koi) - **the kinda okay image format** -and I wanted to share some details. - -It's a lossless image format based on ideas from [qoi](https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compression) and [qoir](https://nigeltao.github.io/blog/2022/qoir.html) that is designed to use a small amount of memory and be fast to decode. I have exactly zero experience with image formats, so this is a learning experience for me. As it currently stands, it outperforms many other image formats in terms of compression ratio and decoding speed on images with a lot of flat colors, but more on that later. - -# The basics - -To start, let's look at how the file format is structured. - -To identify the file format, the first 8 bytes must be `KOI `. Following that is a header with metadata about the image. The header is a series of key-value pairs, encoded using Binary JSON (BSON). The following keys are supported: - -- `v` (version): The file format version -- `w` (width): The image width in pixels. Must be greater than zero. (unsigned 64-bit integer) -- `h` (height): The image height in pixels. Must be greater than zero. (unsigned 64-bit integer) -- `c` (channels): The number of channels. Must be `1`, `2`, `3` or `4`. -- `x` (compression): The compression algorithm. Must be `0` (none) or `1` (LZ4). -- `s` (color space): The color space. Must be `0` (sRGB with linear alpha), `1` (all channels linear) - There's also two optional keys: - -- `e` (exif): The EXIF data as a byte array. -- `b` (block size): The size of blocks, smaller blocks allow for parallel processing, larger blocks allow for better compression. Used by the encoder for the maximum size of uncompressed blocks. Can be up to 199992 bytes. (unsigned 32-bit integer) - -The header is followed by a series of blocks, each of which is a chunk of compressed image data. Each block is prefixed with a small header that contains the length of the block and the number of pixels it contains. These are stored as 4-byte unsigned integers, encoded in little endian order. - -Koi supports 1-4 channels (grayscale, rgba, and both with alpha) and stores 8 bits per channel. - -The image data is stored as a series of chunks, each of which is one to five bytes long and only corresponds to a single pixel. This is a pretty big departure from most image formats, which store data in blocks of 4x4 or 8x8 pixels. The reason for this is that it allows for very fast decoding, since the decoder only needs to keep track of the last pixel value. Initially, koi also had support for run-length encoding and a cache of recently used pixel values, but I removed those features because they didn't improve the compression ratio in my benchmark suite and made decoding and encoding a lot slower. - -The following chunk types are currently supported, with space for more in the future: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
┌─ OP_DIFF ───────────────┐
-│         Byte[0]         │
-│  7  6  5  4  3  2  1  0 │
-│───────┼─────────────────│
-│  0  0 │      diff       │
-└───────┴─────────────────┘

- This chunk can store the color difference between the current pixel and the previous pixel in the image more efficiently when the difference is small. The difference is stored as a unsigned integer with a bias of 2. Similar to QOI's OP_DIFF -
-

-
┌─ OP_LUMA ───────────────┬────────────────────────┐
-│         Byte[0]         │      Byte[1]           │
-│  7  6  5  4  3  2  1  0 │ 7  6  5  4  3  2  1  0 │
-│───────┼─────────────────│───────────┼────────────│
-│  0  0 │       dg        │  dr - dg  │  db - dg   │
-└───────┴─────────────────┴───────────┴────────────┘

- Similar to OP_DIFF, but stores the difference between the current pixel and the previous pixel's luma value. -
-

-
┌─ OP_DIFF_ALPHA ─────────┐
-│         Byte[0]         │
-│  7  6  5  4  3  2  1  0 │
-│───────┼─────────────────│
-│  1  1 │      diff       │
-└───────┴─────────────────┘

- The difference between the current pixel's alpha value and the previous pixel's alpha value. Stored as unsigned integers with a bias of 2. Lengths above 59 are illegal since they are used by other chunk types. -

-
┌─ OP_SAME ───────────────┐
-│         Byte[0]         │
-│  7  6  5  4  3  2  1  0 │
-│─────────────────────────│
-│  1  0  0  0  0  0  0  0 │
-└─────────────────────────┘

- Ignore the current pixel, and use the previous pixel instead, this is useful for images with large areas of the same color and is easily compressible. -

-
┌─ OP_GRAY ───────────────┐
-│         Byte[0]         │
-│  7  6  5  4  3  2  1  0 │
-│─────────────────────────│
-│  1  1  1  1  1  1  0  0 │
-└─────────────────────────┘

- Grayscale pixel, can also be also be used in images with more than one channel if the color values are the same across all channels. Followed by a single byte containing the grayscale value. -

-
┌─ OP_GRAY_ALPHA ─────────┐
-│         Byte[0]         │
-│  7  6  5  4  3  2  1  0 │
-│─────────────────────────│
-│  1  1  1  1  1  1  0  1 │
-└─────────────────────────┘

- Grayscale pixel with alpha, can also be also be used in images with more than one channel if the pixel is the same in all channels. Followed by two bytes containing the grayscale value and alpha value respectively. -

-
┌─ OP_RGB ────────────────┐
-│         Byte[0]         │
-│  7  6  5  4  3  2  1  0 │
-│─────────────────────────│
-│  1  1  1  1  1  1  1  0 │
-└─────────────────────────┘

- RGB pixel, followed by three bytes containing the red, green and blue values. -

-
┌─ OP_RGBA ───────────────┐
-│         Byte[0]         │
-│  7  6  5  4  3  2  1  0 │
-│─────────────────────────│
-│  1  1  1  1  1  1  1  1 │
-└─────────────────────────┘

- RGBA pixel, followed by four bytes containing the red, green, blue and alpha values. -

-
┌─ OP_SAME ───────────────┐
-│         Byte[0]         │
-│  7  6  5  4  3  2  1  0 │
-│─────────────────────────│
-│  0  0  0  0  0  0  0  0 │
-└─────────────────────────┘

- The current pixel is the same as the previous pixel. -

- -While developing the format, I also experimented with a few other chunk types, but more often than not they ended up being less compressible and slower to decode than the ones listed above. - -These chunks are compressed using [lz4](https://lz4.github.io/lz4/), which is, while not the most efficient compression algorithm, very fast to decode and encode. When encoding, the reference implementation allows choosing between `lz4-flex`, a pure rust implementation, and `lz4`/`lz4-hc`, which are bindings to the C implementation and specify the compression level. When decoding, the reference implementation uses `lz4-flex`, since it is faster than the C implementation in my benchmarks. - -The last block is followed by 4 zero bytes to indicate the end of the stream, so block sizes are not allowed to be 0. - -# Performance - -To compare the performance of koi to other image formats, I wrote a simple benchmark which is available [here](https://github.com/explodingcamera/koi-rs/tree/main/koi-bench). The benchmark decodes a series of images and measures the time it takes to decode them. The images are taken from the [Qoi Benchmark Suite](https://qoiformat.org/benchmark/). My results were achieved on a Ryzen 7 5800X with 32GB of RAM and images are decoded directly from and into memory. - -``` -┌─────────────────────────────────────┐ -│ Overall │ -├─────────┬─────────┬─────────┬───────┤ -│ format │ encode │ decode │ ratio │ -├─────────┼─────────┼─────────┼───────┤ -│ Png │ 152.64s │ 6.29s │ 0.25 │ -│ PngFast │ 3.73s │ 5.32s │ 0.30 │ -│ Koi │ 26.20s │ 4.21s │ 0.26 │ -│ KoiFast │ 7.80s │ 4.11s │ 0.28 │ -│ Qoi │ 4.65s │ 3.39s │ 0.28 │ -└─────────┴─────────┴─────────┴───────┘ -``` - -When looking at the overall benchmark results, Koi sits somewhere in the middle between the different png profiles. Koi actually outperforms the other formats in a lot of categories, except -for photos with a lot of detail, where it is still pretty competetive considering the simple architecture of the format. - -``` -Koi's worst results -┌─────────────────────────────────────┐ -│ images/photo_tecnick │ -├─────────┬─────────┬─────────┬───────┤ -│ format │ encode │ decode │ ratio │ -├─────────┼─────────┼─────────┼───────┤ -│ Png │ 27133ms │ 1151ms │ 0.55 │ -│ PngFast │ 551ms │ 799ms │ 0.58 │ -│ Koi │ 5065ms │ 709ms │ 0.60 │ -│ KoiFast │ 1246ms │ 675ms │ 0.62 │ -│ Qoi │ 829ms │ 630ms │ 0.60 │ -└─────────┴─────────┴─────────┴───────┘ -``` - -The best case scenario for Koi is with images with a lot of transparency and grey colors, like in the icon_512 suite: - -``` -┌─────────────────────────────────────┐ -│ images/icon_512 │ -├─────────┬─────────┬─────────┬───────┤ -│ format │ encode │ decode │ ratio │ -├─────────┼─────────┼─────────┼───────┤ -│ Png │ 2005ms │ 122ms │ 0.05 │ -│ PngFast │ 102ms │ 149ms │ 0.10 │ -│ Koi │ 439ms │ 118ms │ 0.05 │ -│ KoiFast │ 174ms │ 123ms │ 0.06 │ -│ Qoi │ 109ms │ 74ms │ 0.08 │ -└─────────┴─────────┴─────────┴───────┘ -``` - -And for screenshots, Koi can also hold its own: - -``` -┌─────────────────────────────────────┐ -│ images/screenshot_web │ -├─────────┬─────────┬─────────┬───────┤ -│ format │ encode │ decode │ ratio │ -├─────────┼─────────┼─────────┼───────┤ -│ Png │ 4593ms │ 277ms │ 0.08 │ -│ PngFast │ 172ms │ 274ms │ 0.12 │ -│ Koi │ 886ms │ 182ms │ 0.07 │ -│ KoiFast │ 359ms │ 185ms │ 0.08 │ -│ Qoi │ 209ms │ 146ms │ 0.08 │ -└─────────┴─────────┴─────────┴───────┘ -``` - -# Safety - -I'm assuming untrustworthy input, so the decoder is written in a way that it can't panic or cause undefined behavior. All inputs are checked for a maximum size, so the decoder can't be tricked into allocating a huge amount of memory. - -There are a few places where I use `unsafe` code, due to some rust features still being unstable, but I'm trying to keep it to a minimum. - -# Performance optimizations - -Having a benchmark suite has been very useful for optimizing the format, but profiling the encoder and decoder directly using [perf](https://perf.wiki.kernel.org/index.php/Main_Page) and [hotspot](https://github.com/KDAB/hotspot) also helped to pinpoint bottlenecks in the code. - -By comparing the results after small, incremental changes, I was able to improve the performance of both the encoder and decoder by more than 200%. The biggest improvements were made by rearchitecting the hot paths, where a lot of time was spend on the `Write` and `Read` traits. Here, I now instead use mutable slices to write and read data directly into the output buffer without having to keep track of the current position, this alone improved the performance by 50% over the previous version where I kept track of the current position using a local variable. - -```rust -// Old version (simplified) -let data = [0; 1024]; - -// Using Cursor instead of a local variable was even slower, at least in this case -let mut pos = 0; - -for chunk in chunks { - match chunk { - OP_RGB => { - let px = Pixel::from(data[pos..pos + 3]) - pos += 3; - } - } -} - -// New version -let mut data = [0; 1024]; - -for chunk in chunks { - let px: Pixel; - (data, px) = match chunk { - [OP_RGBA, r, g, b, a, rest @ ..] => (rest, Pixel::::from([*r, *g, *b, *a])) - } -} -``` - -In some places, writing ideomatic Rust code also had a big impact on the performance. While the Rust compiler is very good at optimizing code, it can't do magic and sometimes it's necessary to help it out a bit. For example, in the main loop of the decoder, I was using something like `chunks.iter().fold(|chunk| ...).collect()` , which even when refactored to not allocate a new vector for every chunk, was still a lot slower than using loops directly. The promise of zero-cost abstractions didn't hold up here, and while I normally go for readability over performance, in this case it was necessary to make the code a bit more verbose to get the performance I wanted. - -A different case where the Rust compiler was able to optimize the code very well was when I started to use `#[inline]` on some of my functions. Here, more often then not, using `#[inline]` decreased the performance of the code, so this is something that should be used with caution and only after profiling. - -# Conclusion - -Creating a new image format is actually a lot of fun, and It's totally possible to create something that works well for special use cases like icons or game assets. When taking the right shortcuts, it's possible to implement something usable in a few days when utilizing existing compression algorithms and metadata formats, without needing to utilize more advanced compression algorithms. Switching to a more advanced compression algorithm like Zstd or Brotli also enables the format to be purpose built for a specific decompression budget. - -I'm happy with the results so far and as a next step I want to add Rust `no_std` support so I can use it in the kernel project I'm working on to test displaying images on the screen. - -Some other things I want to look into is adding support for more color spaces, like YCbCr and CMYK, and maybe even support for animation. I also want to look into adding support for more compression algorithms, like LZ4 or Zstd, and maybe even support for lossy compression. Also, I'm not happy with BSON as a metadata format, so I want to look into using something like [MessagePack](https://msgpack.org/index.html) instead. And lastly, there's still a lot of room for improvement on the performance side, and it should be possible to get the performance even faster than for Qoi, at least for the fast profile. - -The source code for the encoder, decoder and benchmark is available on [GitHub](https://github.com/explodingcamera/koi-rs) and licensed under the ISC license. diff --git a/content/2023/spaify.md b/content/2023/spaify.md deleted file mode 100644 index 5dfb946..0000000 --- a/content/2023/spaify.md +++ /dev/null @@ -1,72 +0,0 @@ -+++ -title = "Introducing Spaify - Seamless page transitions for your static site" -description = "A new npm package to add seamless page transitions to your static site, with less than 1kb of JavaScript" -date = 2023-05-14 -aliases = ["spaify"] - -[taxonomies] -tags = ["typescript", "open source", "webdev"] -+++ - -By default, when you click a link on a website, the browser will load the new page from the server and replace the current page with the new one. This is the default behaviour for static sites, and it works well. Spaify lets you skip this step and load the new page in the background, so that when you click a link, the new page is already loaded and ready to be displayed. This makes the page transition seamless, and it's a great way to improve the user experience of your site, and progressively enhance it. And the best part is that it's super easy to use, and it's less than 1kb of JavaScript (minified and gzipped). - -All you need to do is to import the Spaify script, add `data-spaify-main` to the element that contains the content that you want to replace, and add `data-spaify-ignore` to any link that you don't want to be handled by Spaify. You can also add `data-spaify-run="once"` to any script tag that you want to run only once, when the page is loaded for the first time, and `data-spaify-run="always"` to any script tag that you want to run every time the page is loaded. Here's an example: - -```html - - - - - - - - - - - - - - - - -
- - - - page 1 - - - page 2 - external link -
- - - -``` - -With the small amount of JavaScript that Spaify adds to your site, you can also include it inlined in your HTML, so that you don't have to make an extra request to load it: - -```html - -``` - -If you want to see it in action, just click on any link on this site. And if you want to see the code, you can check out the [source code](https://github.com/explodingcamera/esm/tree/main/packages/spaify) on GitHub (licensed under the MIT license). - -# Previous Work - -There are a few other libraries that do something similar, but they all have some drawbacks. For example, [barba.js](https://barba.js.org/) is a great library, but it's quite heavy (about 10kb) and it requires you to write a lot of code to get it working. There's also the (unfortunately named) [turbo](https://github.com/hotwired/turbo), which has a lot of features, but it's also quite heavy (about 20kb) and it's not very easy to use. Spaify specifically targets static sites, and it's designed to be as simple as possible to use. Of course, if you need more features, like animations, you can always use one of the other libraries, but for many of my sites, Spaify was a drop-in replacement. diff --git a/content/2024/_index.md b/content/2024/_index.md deleted file mode 100644 index 2d0d600..0000000 --- a/content/2024/_index.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -transparent: true ---- \ No newline at end of file diff --git a/content/2024/books.md b/content/2024/books.md deleted file mode 100644 index 16d4ef4..0000000 --- a/content/2024/books.md +++ /dev/null @@ -1,94 +0,0 @@ -+++ -title = "My favorite books of 2023" -description = "An overview of the best books that I read in 2023" -date = 2024-01-19 - -[taxonomies] -tags = [] -+++ - -2023 has been a fantastic year for reading for me. Early in the year, I switched to reading on my Kindle Oasis, and the low friction has been great (amazon just needs to stop worrying so much about preventing jailbreaking). I've also been catching up on some of the classics on my reading list, but as always, the list is even longer now than it was at the start of the year. I hope you find something interesting, and if you have any recommendations for me, please let me know! - -This year, I planned to go out of my comfort zone and read more non-fiction, but in the end, almost all of my favorite books still ended up being sci-fi. - -
- -# [The Teixcalaan Series](https://www.goodreads.com/series/233352-teixcalaan) - -You can easily get lost in this world filled with intrigues, politics, and a unique culture. A diverse cast of characters with their own motivations and goals that don't feel forced or tokenized. It's one of my favorite series of all time, and I'm looking forward to more books set in this universe. - -
- -# [The Murderbot Diaries](https://www.goodreads.com/series/191900-the-murderbot-diaries) by Martha Wells - -If you need something more light-hearted, I highly recommend the Murderbot Diaries. It's a series of novellas about a murderous robot that wants to be left alone to watch TV. Even though he's not human, Martha Wells does a fantastic job of creating an empathetic character. While there are quite a few books out already, most are pretty short, and you won't be able to put them down. The latest book in the series came out at the end of 2023, and I'm looking forward to more books in the series. - -
- -# [The Hitchhiker's Guide to the Galaxy](https://www.goodreads.com/series/40957-the-hitchhiker-s-guide-to-the-galaxy) by Douglas Adams - -The movie was never really my thing, but I'm glad I finally read the book. Douglas Adams's writing style is just so fun, but sometimes it can be too much. -It has something for everyone, but I recommend stopping after 'So Long, and Thanks for All the Fish', the best book in the series. -A couple of days ago, I also started reading [Colour of Magic](https://www.goodreads.com/book/show/8695.The_Colour_of_Magic) by Terry Pratchett. So far it's a very similar style of humor, without as much absurdity - I'm looking forward to reading more of Discworld. - -
- -# [Ra](https://www.goodreads.com/book/show/57891607-ra) by Sam Hughes - -This is a unique and super creative take on "Magic Systems," but don't let that fool you: this is a hard sci-fi book. Most of the abstracts and summaries I've seen online don't do it justice, and it's worth finishing the book even if you're not sure about it at the start: great plot twists and a very satisfying ending. -I also recommend [Valuable Humans in Transit and Other Stories](https://www.goodreads.com/book/show/63198504-valuable-humans-in-transit-and-other-stories?ref=nav_sb_ss_1_26) and [There Is No Antimemetics Division](https://www.goodreads.com/book/show/54870256-there-is-no-antimemetics-division?ref=rae_2) by Qntm/Sam Hughes, they're both free on [his website](https://qntm.org/scp). - -
- -# [The Metamorphosis of Prime Intellect](https://www.goodreads.com/book/show/64341.The_Metamorphosis_of_Prime_Intellect) by Roger Williams - -I found this book from a recommendation on a [Podcast](https://www.youtube.com/watch?v=dNrTrx42DGQ) about AI safety and AGI. It explores some interesting topics around the nature of consciousness, immortality, and Singularity. Still, I must warn you that the book can sometimes be pretty dark and disturbing. It takes place in a world without moral constraints, and the author doesn't shy away from the darker side of human nature. - -[A Casino Odyssey in Cyberspace](https://www.goodreads.com/book/show/41569567-a-casino-odyssey-in-cyberspace) is a sequel to this book, going more into a post-scarcity world, -and if you liked the first book you should also check his [The Mortal Passage Trilogy](https://www.goodreads.com/book/show/24237204-the-mortal-passage-trilogy?ac=1&from_search=true&qid=DT495nnrDR&rank=1). Most of his books are available for free on his [website](http://localroger.com/prime-intellect/). - -
- -# [Do Androids Dream of Electric Sheep?](https://www.goodreads.com/book/show/36402034-do-androids-dream-of-electric-sheep) by Philip K. Dick - -I don't know why I've not read it or watched the movies until now; PKD is just a master at thought-provoking world-building. -There's so much of the same story on self-aware robots, but PKD's somehow over 50-year-old book still feels fresh. -The most exciting part for me was the society's view of animals and how they've become a symbol of status and empathy. - -
- -# [UNIX: A History and a Memoir](https://www.goodreads.com/book/show/53011383-unix) by Brian W. Kernighan - -This is a surprisingly inspiring book about the history of UNIX and the people behind it. It just makes you jealous to meet all of these people working at Bell Labs and the early days of computing. The book goes into the philosophy of UNIX and the design decisions that made it so successful, but it's more focused on the people and the project's history. - -
- -# [The Phantom Tollbooth](https://www.goodreads.com/book/show/378.The_Phantom_Tollbooth) by Norton Juster - -It's essentially a children's book, but I found it very enjoyable as an adult. It's just so clever and fun, something that can improve a bad day, and I'm sure I'll be reading it again. It helps you remember to slow down and enjoy the journey. - -
- -# Honorable Mentions - -As an honorable mention, I also want to mention some books that I read this year. These held up surprisingly well and all of them are worth a read (or re-read). - -## > [Brave New World](https://www.goodreads.com/book/show/5129.Brave_New_World) by Aldous Huxley - -## > [1984](https://www.goodreads.com/book/show/40961427-1984) by George Orwell - -## > [Fahrenheit 451](https://www.goodreads.com/book/show/13079982-fahrenheit-451) by Ray Bradbury - -## > [The Time Machine](https://www.goodreads.com/book/show/2493.The_Time_Machine?ref=nav_sb_ss_5_9) by H.G. Wells - -## > [Ender’s Game](https://www.goodreads.com/book/show/375802.Ender_s_Game) by Orson Scott Card - -## > [Sidhartha](https://www.goodreads.com/book/show/52036.Siddhartha) by Hermann Hesse - -## > [The Count of Monte Cristo](https://www.goodreads.com/book/show/7126.The_Count_of_Monte_Cristo) by Alexandre Dumas - -## > [Slaughterhouse-Five](https://www.goodreads.com/book/show/4981.Slaughterhouse_Five) by Kurt Vonnegut - -
- -I hope you found something interesting in this list, and for more recommendations you can check out my [Goodreads page](https://www.goodreads.com/review/list/129153443-henry?shelf=read) or [my reading list](https://www.goodreads.com/review/list/129153443-henry?shelf=to-read). diff --git a/content/2024/liwan/dashboard.jpg b/content/2024/liwan/dashboard.jpg deleted file mode 100644 index e514ddc..0000000 Binary files a/content/2024/liwan/dashboard.jpg and /dev/null differ diff --git a/content/2024/liwan/index.md b/content/2024/liwan/index.md deleted file mode 100644 index 6ff705d..0000000 --- a/content/2024/liwan/index.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Lightweight Web Analytics with Liwan -description: "A look at my new project Liwan, a lightweight, privacy-focused web analytics tool." -date: 2024-12-18 ---- - -> This post explores some of this project's background and technical aspects. If you're more so interested in Liwan itself, you can check out the [demo instance](https://demo.liwan.dev/p/liwan.dev) and the docs on [liwan.dev](https://liwan.dev). The source code available on [GitHub](https://github.com/explodingcamera/liwan) under the AGPL-3.0 license. - -This summer, I started working on a small tool for collecting various metrics on my websites and this blog to see what posts are popular and also just to have a better understanding of how people use my sites. It's grown into a very useful tool for me and I've recently released the first major version of it, so I thought I'd write a bit about it. - -{{ figure(caption = "The Liwan Dashboard for one of my websites.", position="center", src="./dashboard.jpg") }} - -Over the last ~5 years, I've tried out probably ten different analytics platforms after bailing on Google Analytics due to privacy concerns, but nothing ticked all the boxes for me: - -**I wanted strong privacy guarantees**. First of all, because it should be the default across all websites, but also because of how (rightfully) painful GDPR compliance can be when you collect too much data. I don't need to know your IP address or your browsing habits. There's no real reason for this data to leave my server; even when anonymized, there's always a risk of it being misused. - -**It should be set and forget**. I love that self-contained, statically linked binaries are making a comeback due to being the default with "newer" languages such as Go and Rust, which is fantastic for self-hosted software. Around the time I started working on this project, DuckDB 1.0 was also announced and later released, which seemed like a perfect tool for this. I value a simple setup process more than the ability to hyper-scale prematurely and endlessly customize everything. - -**Truly Open Source**: One of my favorite tools I tried was [fathom analytics](https://usefathom.com/), which sadly (but understandably) switched to a completely closed source SaaS model. I want to 'own' the data, even if the user's data is eventually anonymized; it's hard to trust what's happening on a server I don't control. I really like [plausible](https://plausible.io/)'s open core model but I wanted something a bit more lightweight and opinionated. - -**Multi Website**: As I mentioned, I want to aggregate data from 10+ different sites, so multi-website support also had to be great. One platform I experimented with early on was (GoatCounter)[https://www.goatcounter.com/]. While the UI is bare bones, it's also very lightweight and available with an embedded database. Sadly, it does not offer great multi-website support and is generally not as feature-rich as I would like. - -Setting out with those and some more goals in mind, I started building [Liwan.dev](https://liwan.dev), which, as often happens, had a lot of feature creep but now ended up in a place I'm very happy with. - -# Simple Software - -I love abstractions. Not in the OOP or the JavaScript way but in how a robust, tested, and, most importantly, focused library or app feels to use. This is one of the reasons I am so drawn to software engineering in the first place: the ability always to go one level deeper and understand how things work. - -You can overplay this - see the NPM ecosystem (this is honestly a bit of a strawman, [left-pad](https://en.wikipedia.org/wiki/Npm_left-pad_incident) was not that bad), but it is a nice contrast from the attention-seeking everything apps and magic frameworks. There is a hard-to-find balance here - I won't argue in favor of building on hundreds of layers of magic and abstractions even when ignoring the supply chain concerns - but I really enjoy the current balance in the Rust ecosystem. The language itself doesn't matter, though; I mainly use it because I enjoy working with it. - -Liwan is built on hundreds of open-source libraries; you can see the list for yourself on the [attributions page](https://demo.liwan.dev/attributions) shipped with every copy of Liwan. However, this doesn't mean it's not lightweight. Even when using the 'modern' web stack, resource-heavy and slow websites don't have to follow automatically. The dashboard is built using [Astro](https://astro.build/), a web framework built around reducing unnecessary overhead. - -A big part of the complexity of similar projects often comes from overly customizable Graph libraries. To reduce the amount of code that needs to be sent to the user, I build custom Graph and Map components directly on [d3](https://d3js.org/), keeping the entire JavaScript bundle shipped to users below 250kb. This even includes a large number of different icons and the data for the world map, which uses an heavily optimized [topojson](https://github.com/topojson/topojson) file I created with data from the [natural earth project](https://www.naturalearthdata.com/). - -{{ figure(caption = "Liwan's PageSpeed Insights.", position="center", src="./pagespeed.jpg") }} - -On the backend side, the main contributors to the amount of code are the embedded databases, DuckDB for events, and SQLite for the user data and authentication. The dashboard is transformed into static HTML, CSS, and JS and bundled with the rest of the code, something I've recently started doing with some of my other projects as well. Producing a single, universal artifact simplifies the entire build process and packaging containers greatly (Something that could be pushed even further using [Actually Portable Executable](https://justine.lol/ape.html)), but I also decided to package it up as a Docker container as well in case you prefer that. This container just contains the Liwan binary and nothing else thanks to the static linking. - -To maximize compatibility across different Linux distro (you might have had issues with glibc on alpine containers before if you've worked with docker imaged), Liwan is also statically linked with [musl libc](https://musl.libc.org/) and uses [rustls](https://github.com/rustls/rustls) instead of linking against OpenSSL. - -Simple software doesn't stop here, however. Liwan is also built to require only a minimal amount of configuration and settings from the user before it is ready to process events. You execute the binary, and Liwan is ready. Everything will be placed in the correct place according to XDG Base Directory Specification, and you can start sending events to it right away. - -Early on, I also decided to add an onboarding page for users to create their initial user account. This page can only be accessed from a URL printed to the console on the first startup, protecting you from accidentally exposing this screen to the public internet and removing the need for default passwords. - -# Open Source - -As a small side note on the open-source part, I've thought a lot about how I wanted to license this project. I ended up going with the AGPL-3.0, which I'm not 100% happy with but is the best compromise for now. My one big gripe with the AGPL-3.0 is the mostly undefined virality boundaries, which probably apply less in the EU (to remedy this a bit, the tracker script is also available under the MIT license). In other projects, I usually use the MIT + Apache 2.0 dual license that is so common in the Rust ecosystem. Still, I want flexibility to allow me to monetize Liwan more easily in the future. To have the possibility to relicense it later and not need to set up a CLA, all contributions also need to be provided under the MIT license as well, something I haven't seen in many other projects and seems like a good compromise for both sides. - -The main alternative I considered was the EUPL. I've only seen it used on very few projects, but it's a bit more permissive than the AGPL. The author of GoatCounter has some interesting thoughts on why he chose it on his [blog](https://www.arp242.net/license.html). I don't like the license's wording: It allows relicensing the work under entirely different terms, and it just feels like a mess to me. It tries to be an "Interoperable Copyleft" license and explicitly states it's compatible with the GPL 2 and 3, but the GPL itself is _probably_ incompatible with the EUPL ([see gnu.org](https://www.gnu.org/licenses/license-list.html#EUPL-1.1)), _except_ if you do a weird two-step relicensing dance. But I've [read in forums](https://interoperable-europe.ec.europa.eu/collection/eupl/discussion/how-does-fsf-considers-eupl) that it still keeps the terms of the EUPL, so relicensing doesn't actually weaken it? I don't know, it's just a mess. - -# Try it out! - -Liwan is not perfect. There are many things I could optimize more, starting supporting huge datasets - going into the tens of millions of events can slow down things considerably - but it fits my personal use case (nearly) perfectly and probably a lot of others too. - -Just grab the latest version and try it out for yourself: - -```bash -# Download the latest release -curl -JLO 'https://github.com/explodingcamera/liwan/releases/latest/download/liwan-x86_64-unknown-linux-musl.tar.gz' - -# Ensure the ~/.local/bin directory exists (You might want to add it to your PATH) -mkdir -p ~/.local/bin - -# Extract the binary -tar -xzf liwan-x86_64-unknown-linux-musl.tar.gz -C ~/.local/bin liwan - -# Make the binary executable -chmod +x ~/.local/bin/liwan - -# Run the binary -liwan --help -``` diff --git a/content/2024/liwan/pagespeed.jpg b/content/2024/liwan/pagespeed.jpg deleted file mode 100644 index f1f1aff..0000000 Binary files a/content/2024/liwan/pagespeed.jpg and /dev/null differ diff --git a/content/2024/tinywasm/assets/code.jpg b/content/2024/tinywasm/assets/code.jpg deleted file mode 100644 index 5316ed8..0000000 Binary files a/content/2024/tinywasm/assets/code.jpg and /dev/null differ diff --git a/content/2024/tinywasm/assets/flamegraph.jpg b/content/2024/tinywasm/assets/flamegraph.jpg deleted file mode 100644 index c1db085..0000000 Binary files a/content/2024/tinywasm/assets/flamegraph.jpg and /dev/null differ diff --git a/content/2024/tinywasm/assets/hn.jpg b/content/2024/tinywasm/assets/hn.jpg deleted file mode 100644 index 7573c60..0000000 Binary files a/content/2024/tinywasm/assets/hn.jpg and /dev/null differ diff --git a/content/2024/tinywasm/assets/image.png b/content/2024/tinywasm/assets/image.png deleted file mode 100644 index b153b52..0000000 Binary files a/content/2024/tinywasm/assets/image.png and /dev/null differ diff --git a/content/2024/tinywasm/assets/progress-mvp.svg b/content/2024/tinywasm/assets/progress-mvp.svg deleted file mode 100644 index 54f80c8..0000000 --- a/content/2024/tinywasm/assets/progress-mvp.svg +++ /dev/null @@ -1,63 +0,0 @@ - - -WebAssembly 1.0 Test Suite - - -Tests Passed - - -TinyWasm Version - - - - - - - - -0 - - - -5000 - - - -10000 - - - -15000 - - - -20000 - - - - -v0.0.4 (9258) - - - -v0.0.5 (11135) - - - -v0.1.0 (17630) - - - -v0.2.0 (19344) - - - -v0.3.0 (20254) - - - - - - - - diff --git a/content/2024/tinywasm/index.md b/content/2024/tinywasm/index.md deleted file mode 100644 index e22f0fa..0000000 --- a/content/2024/tinywasm/index.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: "TinyWasm: How I wrote my own WebAssembly Runtime" -description: "Looking back at the development of TinyWasm, a small WebAssembly runtime written in Rust." -date: 2024-10-13 ---- - -After a short hiatus from writing on this blog, I'm back with an update on what I've been working on lately (or rather slowly catching up on the backlog of posts I wanted to write). - -I finally finished writing my bachelor's thesis on WebAssembly and Edge Computing this summer. More on that in a later post, -but for now, I wanted to talk about another project I worked on earlier this year that inspired much of the work I did -for my thesis: [TinyWasm](https://github.com/explodingcamera/tinywasm), a fully compliant WebAssembly runtime written in Rust. - -## **TinyWasm** - -When writing my posts on [OS Development](https://blog.henrygressmann.de/series/rust-os/) last year, I got interested in WebAssembly -and wanted to try it out inside the kernel. I was fed up with writing context-switching and memory management code, -and WebAssembly looked like an easy way to run existing code in the operating system. -I looked at the existing interpreters and compilers for WebAssembly, but they were all either too complex or had too many dependencies for my taste (I was going for embedded systems, so it had to be lightweight). - -With just a bit of prior experience with WebAssembly and compilers/interpreters, I now had the topic for my capstone project: -Building a WebAssembly runtime. I've been pretty burned on a lot of (unnecessarily) complex projects in the past, so to keep myself on track, -I decided to set out some constraints at the start to finish it on time: - -1. **No Platform-Specific Code**: My first goal was to remove all dependencies on platform-specific code so - everything could work in Rust's `no_std` environment (and potentially in my OS). This also meant I was limited in the libraries I could use. - Thankfully, an excellent crate for parsing WebAssembly binaries already existed: - [`wasmparser`](https://github.com/bytecodealliance/wasm-tools). At the time, it didn't support `no_std`, but I was able to fork it and make it work. - -2. **Build the MVP**: Focus on the initial version of WebAssembly, so no threads, no SIMD, no garbage collection, etc. - -3. **Keep it simple**: I wanted the codebase to be as small and readable as possible to make it easier to integrate into other projects, - such as my OS. No premature optimization (There has already been a [fork of TinyWasm](https://github.com/reef-runtime) used as a base for a distributed WebAssembly runtime). - -4. **No Unsafe Code**: This came a bit later, but I decided to avoid unsafe Rust code entirely (maybe something for another post). While this excludes some optimizations like using virtual memory to optimize bounds checking in Wasm memory, it also forces me to write simpler code. - -I started by taking a simple "Hello World" WebAssembly program and tried to infer everything I needed without looking at the specification. -Surprisingly, this worked well, and in a short time, I had a simple interpreter that could run very basic programs. - -{{ figure(caption = "The first test version of the interpreter.", position="center", src="./assets/code.jpg", link="https://github.com/explodingcamera/tinywasm/blob/93f8e10a8c15cbcf0d09517869016c32c6bd47eb/crates/tinywasm/src/module/mod.rs#L131-L185") }} - -With this newly gained confidence, I scrapped the initial codebase and started from scratch. -Starting by defining the structure of the interpreter and the different components it would need, I quickly realized that -I would need a lot of tests to make sure everything worked as expected. -Thankfully, I didn't have to write all of these tests myself, as the reference interpreter conveniently already has -[thousands of them](https://github.com/WebAssembly/testsuite) covering a lot of edge cases. Plumbing these tests into my test suite was a bit -of a pain, but in the end, I had a script that would run all of the relevant tests and give me a nice graph of -how many tests I had passed (and some dopamine when the number went up). - -{{ figuresvg(caption = "", position="center", src="content/2024/tinywasm/assets/progress-mvp.svg") }} - -Now that I had a good test suite, I started implementing the interpreter. The WebAssembly specification is thorough, -but it's also dense with abstract concepts and mathematical notation. -I spent a lot of time looking at different interpreters and their APIs to get a better understanding of how things were supposed -to work (I can recommend the trusty [grep.app](https://grep.app/) for this). - -For the actual implementation, I mainly started by taking a couple of tests from one of the test suites and trying to get them to pass, -which worked surprisingly well. Slowly but surely, the numbers went up, and more and more tests passed. - -Predictably, once I reached only about 80/2000+ test cases left, I still had about 20% of my work and -a couple of long nights ahead of me. Finally, once all the tests passed, I compiled the interpreter to WebAssembly -and ran it using TinyWasm. It worked on the first try. I was completely surprised, but LLVM randomly did the right optimizations that made it work, -and its code didn't trigger any of the remaining edge cases/bugs. - -## **Optimization** - -Once I had a (mostly) working interpreter, I started looking into profiling and optimizing the code. I had a few ideas on how to make it faster, -but I wanted to optimize only the parts that were slow and not add any additional complexity to the codebase. -I started by profiling the interpreter using `perf`, `cargo-flamegraph` and later `samply` to understand where the bottlenecks were. To keep things going in the right direction, -I also added some basic benchmarks using `criterion` to ensure I didn't accidentally make things slower. - -{{ figure(caption = "A flamegraph using Firefox's profiler & samply", src="./assets/flamegraph.jpg") }} - -Initially, the biggest overhead was matching opcodes in the interpreter loop. Without using unsafe code, -I had to nudge the compiler in the right direction to generate jump tables for the opcodes. Thankfully, a couple of -`#[inline(always)]` annotations and some moving code around did the trick, giving me a nice +50% speedup. -There's not a lot of information on how to do this in Rust, but a [post](https://pliniker.github.io/post/dispatchers/) -hints at this probably being the easiest cross-platform way to do it. - -From there, I also looked into reducing the size of the bytecode and the interpreter itself. TinyWasm uses a custom bytecode format -that's a bit easier to execute than the standard WebAssembly format and can be zero-copy deserialized (powered by [`rkyv`](https://github.com/rkyv/rkyv)). -This bytecode is represented by a big enum with all the different opcodes and their arguments, and without any optimizations, it was about 32 bytes per instruction. -To reduce this, I removed some redundant information that could be inferred from the context and added more specialized opcodes for common patterns (Super Instructions). -Currently, the bytecode is about 16 bytes per instruction, which is a nice improvement to memory usage and performance due to better memory alignment. - -Currently, The biggest bottleneck is the stack, mainly `push` and `pop` operations. I'm currently looking into ways to optimize this, but it's tricky without using unsafe code. Other runtimes, such as [wasmi](https://wasmi-labs.github.io/blog/posts/wasmi-v0.32/), show that register-based interpreters are much faster. However, I'm not sure if I want to go down that route yet, as it would add a lot of complexity to parsing, and I'd like to stay -as close to the original WebAssembly model as possible. - -For actual performance, I'm currently at about 1/3 of the speed of wasmi, which is pretty good considering the size of the codebase. These benchmarks are not available online yet as this was part of my thesis, but whenever I get around to cleaning them up, I'll publish them on GitHub as well. - -## **Conclusion** - -I was super happy with the results, and I'm still pushing the odd update here and there. The next step is SIMD support (currently in the works), for which I recently refactored the stack to use a more efficient representation (SoA for differently sized types). After that, I'll look into adding threads and moving to support the WebAssembly System Interface (WASI). However, I'm waiting for the spec to stabilize before I start implementing it. - -As of now, TinyWasm supports WebAssembly V2 (without SIMD and threads) and several other proposals, such as reference types and bulk memory operations, so most programs should work fine. After submitting it as my capstone project, I also posted it on HN and Reddit, where I got some nice feedback and a few stars on GitHub (obviously the most important part). - -{{ figure(caption = "Internet points are important.", position="center", src="./assets/hn.jpg", link="https://news.ycombinator.com/item?id=39627410") }} - -If you're interested in checking it out or maybe even contributing, TinyWasm is up -on [GitHub](https://github.com/explodingcamera/tinywasm) and also on [crates.io](https://crates.io/crates/tinywasm). -Feel free to poke around, open issues, or even submit a PR (I recently improved the test suite and added a small contribution guide). - -## **Further Reading** - -[Crafting Interpreters](https://craftinginterpreters.com/) by Robert Nystrom is probably the best introduction to the field. -Going from there, I can also recommend the [Writing an Interpreter in Go](https://interpreterbook.com/)/[Writing a Compiler in Go](https://compilerbook.com/) -books or the [Writing Interpreters in Rust Guide](httphttps://rust-hosted-langs.github.io/book/). I mostly looked at the source code of other interpreters, though, so don't be scared by all the theory. Simple interpreters are surprisingly easy to write, and a small one can be a great weekend project. diff --git a/content/_index.md b/content/_index.md deleted file mode 100644 index 8bc0069..0000000 --- a/content/_index.md +++ /dev/null @@ -1,3 +0,0 @@ -+++ -sort_by = "date" -+++ diff --git a/content/pages/_index.md b/content/pages/_index.md deleted file mode 100644 index 800a244..0000000 --- a/content/pages/_index.md +++ /dev/null @@ -1,3 +0,0 @@ -+++ -render = false -+++ diff --git a/content/private/_index.md b/content/private/_index.md deleted file mode 100644 index 800a244..0000000 --- a/content/private/_index.md +++ /dev/null @@ -1,3 +0,0 @@ -+++ -render = false -+++ diff --git a/content/private/scam.md b/content/private/scam.md deleted file mode 100644 index b56b00c..0000000 --- a/content/private/scam.md +++ /dev/null @@ -1,92 +0,0 @@ -+++ -title = "Checkliste - Wie erkenne ich einen Betrugsversuch?" -date = 2023-08-08 -transparent = true -draft = true -+++ - -Internetbetrug ist ein großes Problem, das jeden treffen kann. Die Betrüger werden immer raffinierter und es ist nicht immer einfach, einen Betrugsversuch zu erkennen. -Selbst Computerexperten werden manchmal Opfer von Betrügern, und die Auswirkungen werden immer schlimmer. Selbst scheinbar harmlose Betrugsversuche können zu großen Problemen führen, wie z.B. Identitätsdiebstahl, Kontosperrungen oder Erpressung. - -Diese Checkliste soll dir helfen, Betrugsversuche zu erkennen und dich davor zu schützen. - -
- -## 1. Lasse dich nicht unter Druck setzen - -Meiner Erfahrung nach ist die wichtigste Regel, sich nicht unter Druck setzen zu lassen. -Durch Druck wird deine Fähigkeit, rational zu denken, beeinträchtigt. Betrüger nutzen dies aus, um dich zu manipulieren. - -- Ein Gewinnspiel, das dir sagt, dass du nur noch 5 Minuten Zeit hast, um teilzunehmen - das ist ein Betrugsversuch. -- Ein Anruf, der dir sagt, dass du nur noch 5 Minuten Zeit hast, um deine Kontodaten zu aktualisieren - das ist ein Betrugsversuch. - -Lasse dir Zeit, um die Situation zu analysieren und zu entscheiden, ob du teilnehmen möchtest oder nicht. -Eine seriöse Organisation wird dir immer Zeit geben, um eine Entscheidung zu treffen. - -
- -## 2. Bei allen eingehenden Anrufen skeptisch seien - -Betrüger können ihre Nummer fälschen, um so zu tun, als ob sie von einer vertrauenswürdigen Quelle stammen.\ -Wenn du einen Anruf von einer unbekannten Nummer erhältst, die sich als eine vertrauenswürdige Quelle ausgibt, suche nach der Nummer im Internet, um zu sehen, ob sie mit Betrug in Verbindung gebracht wurde. -

- -## 3. Sei vorsichtig bei Links in E-Mails - -Betrüger können Links in E-Mails verwenden, um dich auf gefälschte Websites zu locken, die wie die echten aussehen.\ -
-**Beispiel:**\ - Du erhältst eine E-Mail von deiner Bank, in der du aufgefordert wirst, deine Kontodaten zu aktualisieren.\ - Die E-Mail enthält einen Link, der dich auf eine Website weiterleitet, auf der du deine Kontodaten eingeben sollst.\ - -1. Überprüfe die E-Mail-Adresse des Absenders\ - z.B. `noreply@info.ing.de` ist echt, `ingbanking@cdas.gov.my` ist gefälscht -2. Ist die E-Mail dringend?\ - z.B. `Ihr Konto wurde gesperrt!` ist ein Hinweis auf einen Betrugsversuch -3. überprüfe die URL der Website\ - z.B. `https://www.ing.de/` ist echt, `https://www.ing.de.ingbanking.com.tk/` ist gefälscht) - -
- -## 4. Malware/Viren vermeiden - -Betrüger können Anhänge in E-Mails verwenden, um Malware auf deinem Computer zu installieren. Am weitesten verbreitet sind Programme, die deine Passwörter stehlen und dich ausspionieren.\ -Außerdem habe ich persönlich bereits mehrmals Cyberangriffe gesehen, bei denen die Angreifer die Kontrolle über den Computer übernehmen und Lösegeld verlangen. - -**Mögliche Angriffspunkte sind:** - -1. **E-Mail-Anhänge**\ - Oft sind Viren in E-Mail-Anhängen versteckt.\ - Öffne niemals Anhänge, die du nicht erwartest! -2. **Anti-Viren-Programme**\ - Perfiderweise gibt es auch Anti-Viren-Programme, die Malware installieren.
- Folge niemals Anweisungen von Anti-Viren-Programmen, die du nicht selbst installiert hast. Wenn du dir nicht sicher bist, ob du ein Anti-Viren-Programm installiert hast, lass es von einem Experten überprüfen. -3. **Downloads**\ - Öffne niemals Dateien, die du von dir unbekannten Quellen heruntergeladen hast.\ -4. **Unseriöse Websites**\ - Besuche nie unseriöse Websites, wie z.B. Seiten, die Filme oder Bücher kostenlos anbieten, ohne einen Ad-Blocker zu verwenden. Dazu mehr später. - -
- -# Bekannte Betrugsversuche - -Ich habe bereits hunderte von Betrugsversuchen gesehen und die häufigsten zusammengefasst. - -- Enkeltrick -- Telefonanrufe -- Gewinnspiele -- DHL Paketbenachrichtigung -- Banken -- WhatsApp Betrug - -# Prävention - -- **Ad-Blocker verwenden**\ - Meiner Meinung nach ist ein guter Ad-Blocker besser als jedes Anti-Viren-Programm.\ - Viele Betrugsversuche starten mit Werbung. Außerdem verhindert ein Ad-Blocker weitere Betrugsversuche, indem er dich vor unseriösen Websites schützt.\ - Ich empfehle uBlock Origin für Chrome und Firefox. Dieser Ad-Blocker ist kostenlos und sicher.\ - [Für Firefox installieren](https://addons.mozilla.org/de/firefox/addon/ublock-origin/)\ - [Für Chrome installieren](https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm) -

-- **Auf die Sicherheit deines Mobiltelefons vertrauen**\ - Android und iOS sind sehr sichere Betriebssysteme. Wenn du ein Mobiltelefon verwendest, kannst du davon ausgehen, dass es sicher ist. Viele Betrugsversuche nutzen deine Unsicherheit aus, um dich zu manipulieren. Oft wird dir gesagt, dass dein Mobiltelefon unsicher ist und du eine App installieren musst, um es zu schützen. **Das ist ein Betrugsversuch.** diff --git a/content/private/urls.svg b/content/private/urls.svg deleted file mode 100644 index 6b33fdd..0000000 --- a/content/private/urls.svg +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - https://paypal.com - - - - - - - - - service@palypal.com - - - - - ✅ - - - - ✅ - - - - - - - - https://paypal.com.u27sndf.com - - - - - ❌ - - - - ❌ - - - - - - - - servicepaypal@emailgolobal.com - - - - \ No newline at end of file diff --git a/content/rust-os/1-hello-riscv/assets/boot.svg b/content/rust-os/1-hello-riscv/assets/boot.svg deleted file mode 100644 index cd94dc0..0000000 --- a/content/rust-os/1-hello-riscv/assets/boot.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - Device specific -firmware -(M-Mode)Device specific -firmware -(M-Mode)OpenSBI -(M-Mode)OpenSBI -(M-Mode)Our Kernel -(S-Mode)Our Kernel -(S-Mode)RAM -0x80000000RAM -0x80000000System ROMSystem ROMRAM -0x80200000RAM -0x80200000Loads OpenSBI -into the RAMLoads OpenSBI -into the RAMLoads the Kernel -and Device Tree into -the RAMLoads the Kernel -and Device Tree into -the RAM \ No newline at end of file diff --git a/content/rust-os/1-hello-riscv/assets/boot1.svg b/content/rust-os/1-hello-riscv/assets/boot1.svg deleted file mode 100644 index 8dd6958..0000000 --- a/content/rust-os/1-hello-riscv/assets/boot1.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - Device specific -firmwareDevice specific -firmwareSecond-stage boot -loader - -e.g Grub or Windows -Boot ManagerSecond-stage boot -loader - -e.g Grub or Windows -Boot ManagerThe KernelThe KernelSystem ROMSystem ROMFirst Stage Bootloader - -e.g UEFI or CorebootFirst Stage Bootloader - -e.g UEFI or CorebootSystem ROMSystem ROMSystem ROMSystem ROMSystem ROMSystem ROMOn Disk/NetworkOn Disk/NetworkOn Disk/NetworkOn Disk/NetworkLoads the Kernel into -memory, e.g from diskLoads the Kernel into -memory, e.g from diskLoads the SSB, -e.g from an address -specified in GPTLoads the SSB, -e.g from an address -specified in GPT \ No newline at end of file diff --git a/content/rust-os/1-hello-riscv/assets/elf.svg b/content/rust-os/1-hello-riscv/assets/elf.svg deleted file mode 100644 index 9d7a8fa..0000000 --- a/content/rust-os/1-hello-riscv/assets/elf.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - -ELF HeaderELF HeaderProgram header -tableProgram header -table.text.textThis is where our code will -be stored and the CPU will -start executing fromThis is where our code will -be stored and the CPU will -start executing from.rodata.rodata.data.data.bss.bss.stack.stackread-only data, such as static -variables and string literalsread-only data, such as static -variables and string literalsInitialized mutable data, -such as global variables and -static mutable dataInitialized mutable data, -such as global variables and -static mutable dataUninitialized global -variables, like arrays and -structsUninitialized global -variables, like arrays and -structsLocal variables and function -argumentsLocal variables and function -arguments \ No newline at end of file diff --git a/content/rust-os/1-hello-riscv/assets/rings.svg b/content/rust-os/1-hello-riscv/assets/rings.svg deleted file mode 100644 index 517d410..0000000 --- a/content/rust-os/1-hello-riscv/assets/rings.svg +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - Machine Mode - - - Machine Mode - - - - - Supervisor Mode - - - Supervisor Mode - - - - - User Mode - - - User Mode - - - - - OpenSBI - - - OpenSBI - - - - - our Kernel - - - our Kernel - - - - - User Processes - - - User Processes - - - \ No newline at end of file diff --git a/content/rust-os/1-hello-riscv/assets/sbi.svg b/content/rust-os/1-hello-riscv/assets/sbi.svg deleted file mode 100644 index 38c5bfe..0000000 --- a/content/rust-os/1-hello-riscv/assets/sbi.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - SupervisorSupervisorOpenSBI -(SEE)OpenSBI -(SEE)2. ECALLa0: arg1 -a1: arg2 -... - -a6: SBI function id -a7: SBI extension IDa0: arg1 -a1: arg2 -... - -a6: SBI function id -a7: SBI extension ID1. set -registersSupervisorSupervisorjumpa0: error -a1: valuea0: error -a1: valueread -registers \ No newline at end of file diff --git a/content/rust-os/1-hello-riscv/index.md b/content/rust-os/1-hello-riscv/index.md deleted file mode 100644 index 56dcc5e..0000000 --- a/content/rust-os/1-hello-riscv/index.md +++ /dev/null @@ -1,376 +0,0 @@ -+++ -title = "Operating Systems in Rust #1: Hello RISC-V" -description = "In this first post, we'll set up our environment and write a simple program that prints 'Hello World' to the screen." -date = 2023-05-07 -updated = 2024-03-14 -aliases = ["osdev-1"] -transparent = true - -[taxonomies] -tags = ["rust", "riscv", "kernel"] -series = ["rust-os"] -+++ - -{% quote (class="info")%} - -This is a series of posts about my journey creating a kernel in Rust. You can find the code for this project [here](https://github.com/explodingcamera/pogos/tree/part-1) and all of the posts in this series [here](/series/rust-os/). - -{% end %} - -# Background - -I've been interested in operating systems for a while now, and with many of the recent advancements in Rust's role in the OS ecosystem, I thought it would be fun to try and write a kernel in Rust. -I've found that many blogs and guides on writing kernels and operating systems are either pretty outdated or not very accessible, so this will be a different (and hopefully more fun) approach, fully utilizing the Rust ecosystem to get up and running quickly and minimizing the use of unsafe and assembly code. - -This series requires a basic understanding of Rust or similar languages. You'll see some commands for a Linux terminal—Arch Linux in my case—but don't worry if you're on macOS, Windows, or using another Linux distro; a little tweaking should get things running smoothly. - -To follow along, I've also created a [GitHub Repo](https://github.com/explodingcamera/pogos) with a branch for each part of the series. You can find the code for this part [here](https://github.com/explodingcamera/pogos/tree/part-1). - -{{toc}} -
- -## RISC-V and other CPU Architectures - -X86 is currently the dominant CPU architecture and has recently lost a bit of market share to emerging ARM CPUs like the [Apple M Series](https://en.wikipedia.org/wiki/Apple_M2) or [AWS Graviton](https://en.wikipedia.org/wiki/AWS_Graviton). For this series, however, we'll be targeting RISC-V. RISC-V is a CPU Architecture released in 2015 under royalty-free open-source licenses. By focusing on small and modular instruction extensions and being so new, it avoids the sizeable historical baggage and weird design decisions plaguing x86 (check out [this](https://mjg59.dreamwidth.org/66109.html) article to see the horrendous boot process in action). RISC-V was also designed to be extensible, allowing custom instruction extensions to be added to the base ISA. This enables us to use the same kernel on a wide range of CPUs, from small embedded devices to high-performance servers. - -I'll use [QEMU](https://www.qemu.org/) to run our kernel. QEMU is a virtual machine that can emulate various CPUs and devices, including RISC-V. At the end of this series, we'll also run it on an actual board (my [MangoPi MQ-Pro](https://mangopi.org/mangopi_mqpro) recently arrived, and I'm excited to try it). - -# Setting up the environment - -Recent versions of Rust have made all the setup around building bare metal applications a lot easier than it used to be. We can now do most of our development with stable Rust and only need to use nightly for a few features later. - -Before we start writing code, we'll need to install some things, including Rust and QEMU. The following commands will install Rust and QEMU on Arch Linux, but you can find instructions for other operating systems [here](https://www.rust-lang.org/tools/install) and [here](https://www.qemu.org/download/). - -{{ file(name = "terminal")}} - -```bash - -# Install rust if you haven't already -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - -# Install qemu and the riscv64 toolchain -sudo pacman -S qemu qemu-system-riscv - -# Create a new cargo project (this will be our kernel) -cargo init --bin --name kernel - -# To help us get rid of some boilerplate, we'll add `riscv-rt` to our project. -# This crate provides a small runtime, including a linker script and a trap handler. -# We also need to enable s-mode to use the supervisor mode runtime, more on this later -cargo add riscv-rt --features s-mode -``` - -Next, we need to create config files to tell cargo more about our project. We'll start by creating a `rust-toolchain.toml` file to specify the version of Rust we want to use, and a `.cargo/config.toml` file to specify the target we want to build for and some linker flags. - -{{ file(name = "rust-toolchain.toml")}} - -```toml - -[toolchain] -channel="stable" # We'll just use the most recent stable version of Rust for now -targets=["riscv64gc-unknown-none-elf"] # Build a riscv ELF executable, more on this later -``` - -{{ file(name = ".cargo/config.toml")}} - -```toml - -[build] -target = "riscv64gc-unknown-none-elf" - -[target.riscv64gc-unknown-none-elf] -# Pass our executable to qemu when running `cargo run`. -runner = "qemu-system-riscv64 -m 2G -machine virt -nographic -serial mon:stdio -kernel" - -# Linker flags -rustflags = [ - "-Clink-arg=-Tmemory.x", - "-Clink-arg=-Tlink.x", -] -``` - -The executable format we'll use is ELF, as you can see from the `riscv64gc-unknown-none-elf` target we specified above. ELF is the format used by Linux and most other UNIX-like operating systems for storing executables. Other common formats are PE (windows) and Mach-O (macOS). On x86, we'd have to endure the pain of dealing with PE binaries. - -{{ figuresvg(caption = "ELF Memory Layout", position="center", src="content/rust-os/1-hello-riscv/assets/elf.svg") }} - -One thing you might also have noticed is the `-Tmemory.x` and `-Tlink.x`, our linker scripts. For typical applications, these configuration files are generated by the compiler automatically, but for bare metal applications like ours, we need to specify how our program should be laid out in memory. - -`riscv-rt` already ships with a basic linker script, so we will only need to tell it some basic information about the memory layout of the device we want to run it on. In this case, we'll put the entire kernel in RAM and give it a size of 16MB. The address `0x80200000` is the start of the RAM in QEMU's virtual machine, and `16M` is the amount of RAM we want to use. - -{{ file(name = "memory.x")}} - -```ld - -MEMORY -{ - RAM : ORIGIN = 0x80200000, LENGTH = 16M -} - -REGION_ALIAS("REGION_TEXT", RAM); -REGION_ALIAS("REGION_RODATA", RAM); -REGION_ALIAS("REGION_DATA", RAM); -REGION_ALIAS("REGION_BSS", RAM); -REGION_ALIAS("REGION_HEAP", RAM); -REGION_ALIAS("REGION_STACK", RAM); -``` - -Wiith this file in place, we need to tell the linker where to find it. We can do this by adding a `build.rs` file to our project: - -{{ file(name = "build.rs")}} - -```rust - -use std::env; -use std::fs; -use std::path::PathBuf; - -fn main() { - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - - fs::write(out_dir.join("memory.x"), include_bytes!("memory.x")).unwrap(); - println!("cargo:rustc-link-search={}", out_dir.display()); - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} -``` - -We can now write our first program to ensure everything works as expected. For now, we'll write a simple program that loops forever. We'll also need to add a [custom panic handler](https://doc.rust-lang.org/nomicon/panic-handler.html) to get our program to compile. - -{{ file(name = "src/main.rs")}} - -```rust - -#![no_std] -#![no_main] - -use riscv_rt::entry; -mod utils; - -// We need to specify a panic handler for no_std programs to compile, -// for now this is just a placeholder -#[panic_handler] -fn panic(info: &core::panic::PanicInfo) -> ! { loop {} } - -#[entry] -fn main() -> ! { - loop {} // Busy loop forever -} -``` - -Now we can build and run our program: - -{{ file(name = "terminal")}} - -```bash - -cargo run -``` - -And there we go! We've got a working environment set up! -To stop the program, press `Ctrl + A` followed by `X`. - -
- -# Booting on RISC-V - -## RISC-V Privilege Levels - -To better understand how this works behind the scenes, we first have to know a bit more about RISC-V's privilege levels. RISC-V has three privilege levels, sometimes called _rings_ or _modes_. - -Firmware runs in Machine mode, the highest [privilege level](http://docs.keystone-enclave.org/en/latest/Getting-Started/How-Keystone-Works/RISC-V-Background.html#RISC-V-privilieged-isa). -This is where the bootloader and the Supervisor Execution Environment (SEE) run. This SEE is a piece of software that provides a small abstraction layer between the kernel and the hardware, loads the kernel into memory, and jumps to it. Our kernel will run in Supervisor-mode, which is the second-highest privilege level. Finally, applications will run in User-mode, the lowest privilege level. - -{{ figuresvg(caption = "The three privilege levels of RISC-V", position="center", src="/content/rust-os/1-hello-riscv/assets/rings.svg") }} - -## SBI and OpenSBI - -Compared to other CPU Architectures, RISC-V's boot process is relatively straightforward (If you don't try to force UEFI onto it). -We use OpenSBI as our Supervisor Execution Environment (SEE), our _M-mode RUNTIME firmware_. - -The version shipping with QEMU uses a Jump Address ([_FW_JUMP_](https://github.com/riscv-software-src/opensbi/blob/master/docs/firmware/fw_jump.md)), in this case, `0x80200000`. -This is a location in memory where we'll put our kernel, using the `-kernel` flag we set in our `.cargo/config.toml` file earlier. From there, OpenSBI will run some initialization code and jump to our kernel. - -{{ figure(caption = "Traditional Boot Flow", position="center", src="./assets/boot1.svg") }} - -{{ figure(caption = "QEMU RISC-V Boot Flow", position="center", src="./assets/boot.svg") }} - -This architecture has a lot of benefits: SBI puts an abstraction layer between the kernel and the hardware, which allows us to write a single kernel that can run on any RISC-V CPU, regardless of the extensions it supports, as long as it has an SBI implementation. SBI also provides many functions, such as printing and reading from the console. It also loads a flattened device tree (FDT) into memory, which we'll use later to get information about the hardware. - -{{ figure(caption = "Calling SBI", position="center", src="./assets/sbi.svg") }} - -To interact with SBI, we will use the `ecall` instruction, a trap instruction that will cause the CPU to jump to the Supervisor Execution Environment. The SEE handler will then handle the trap, call the appropriate function, and return to the kernel to continue execution. The SBI specification can be found [here](https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc). - -
- -# Hello world - -Let's write some actual code! First, let's check off a simple hello world, print "Hello world!" to the console, and then shut down the machine. - -## Printing - -Since we're in a `no_std` environment, we can't use the standard library and must implement a print function ourselves. We'll be using the `sbi` crate to interact with our Supervisor Execution Environment (SEE) OpenSBI, which provides a `console_putchar` function that we can use to print a single character to the console. Using this crate, we can create a simple print function that prints a string to the console. This print function iterates over the characters in the string and prints them to the QEMU debug console. - -{% quote (class="info")%} -`console_putchar` is now part of the SBI Debug Extension, which is not yet available in all SBI implementations. We'll use the legacy console putchar function for now, but we'll switch to the debug extension once it's more widely available. -{% end %} - -{{ file(name = "src/utils.rs")}} - -```rust - -pub fn print(t: &str) { - for c in t.chars() { - sbi::legacy::console_putchar(c.try_into().unwrap_or(b'?')) - } -} -``` - -To get super fancy with our print function, we can also implement a macro that allows us to print to the console using the same syntax as the standard library's `println!` macro. All we need to do for this is implement the `core::fmt::Write` trait for our print function. - -{{ file(name = "src/utils.rs")}} - -```rust - -struct Writer {} - -pub fn print_args(t: core::fmt::Arguments) { - use core::fmt::Write; - let mut writer = Writer {}; - writer.write_fmt(t).unwrap(); -} - -impl core::fmt::Write for Writer { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - print(s); - Ok(()) - } -} - -#[macro_export] -macro_rules! print { - ($fmt:literal$(, $($arg: tt)+)?) => { - $crate::utils::print_args(format_args!($fmt $(,$($arg)+)?)) - } -} - -#[macro_export] -macro_rules! println { - ($fmt:literal$(, $($arg: tt)+)?) => {{ - $crate::print!($fmt $(,$($arg)+)?); - $crate::utils::print("\n"); - }}; - () => { - $crate::utils::print("\n"); - } -} -``` - -While we're at it, let's also add a quick method to shut down the system: - -{{ file(name = "src/utils.rs")}} - -```rust - -pub fn shutdown() -> ! { - let _ = sbi::system_reset::system_reset( - sbi::system_reset::ResetType::Shutdown, - sbi::system_reset::ResetReason::NoReason, - ); - unreachable!("System reset failed"); -} -``` - -## Panic handler - -Notice the `.unwrap()` call in the `print_args` function? With our current panic handler, this will cause our program to halt the CPU and tell us nothing about what went wrong. Instead, we'll change our panic handler to print the panic message to the console and then shut down the system (more on this can be found in the [rust nomicon](https://doc.rust-lang.org/nomicon/panic-handler.html)). - -{{ file(name = "src/panic_handler.rs")}} - -```rust - -use crate::println; -use core::{hint::unreachable_unchecked, panic::PanicInfo}; -use sbi::system_reset::{ResetReason, ResetType}; - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - println!("A panic occurred: {info}"); - - let _ = sbi::system_reset::system_reset( - ResetType::Shutdown, - ResetReason::SystemFailure - ); - - println!("System reset failed"); - // We need to loop forever to satisfy the `!` return type, - // since `!` effectively means "this function never returns". - loop {} -} -``` - -## Entry point - -Now, let's put it all together. We'll import our newly created `utils` module and add a new argument to our `main` function. This argument is passed to us by OpenSBI using the `a0` register and contains the hart id of the hart that is executing our program. - -{% quote (class="info")%} -Hart is the RISC-V term for a CPU core. A RISC-V system can have multiple harts, each with a register state and program counter. -{% end %} - -Another thing of note is the `#[entry]` macro we used to define the entry point of our program. This macro is provided by the `riscv-rt` crate and is used to define the entry point of our program, which is called after the runtime has set up the stack and other things for us. - -This is done using some inline assembly in there, which is pretty well documented in their [source code](https://github.com/rust-embedded/riscv/blob/master/riscv-rt/src/asm.rs). It's a great resource to check out if you're interested in how it works or want to write your own runtime or a custom linker script as your kernel grows. - -{{ file(name = "src/main.rs")}} - -```rust - -#![no_std] -#![no_main] - -extern crate riscv_rt; - -use riscv_rt::entry; -mod panic_handler; -mod utils; - -#[entry] -fn main(a0: usize) -> ! { - println!("Hello world from hart {}!", a0); - utils::shutdown(); -} -``` - -
- -# Review - -Once we run `cargo run`, we should see "Hello world!" printed on the console: - -{{ file(name = "terminal")}} - -``` - -$ cargo run - -OpenSBI v1.2 - ____ _____ ____ _____ - / __ \ / ____| _ \_ _| - | | | |_ __ ___ _ __ | (___ | |_) || | - | | | | '_ \ / _ \ '_ \ \___ \| _ < | | - | |__| | |_) | __/ | | |____) | |_) || |_ - \____/| .__/ \___|_| |_|_____/|____/_____| - | | - |_| - -Hello world from hart 0! -``` - -In the next few posts, we'll start handling interrupts and exceptions, allocate data on the heap, set up a page table, and much more! - -To dive in deeper, I also recommend reading Phil Oppermann's fantastic [blog](https://os.phil-opp.com/), where he creates a kernel in Rust for the x86 architecture, and Stephen Marz's [blog](https://osblog.stephenmarz.com/) about RISC-V and Rust. - -{% quote (class="info")%} - -The next post in this series is available here: [Operating Systems in Rust #2: Shell](/rust-os/2-shell/). - -{% end %} diff --git a/content/rust-os/2-shell/assets/linear-allocator.svg b/content/rust-os/2-shell/assets/linear-allocator.svg deleted file mode 100644 index e2a9b9a..0000000 --- a/content/rust-os/2-shell/assets/linear-allocator.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - Our "head"Our "head"Chunks of memory -(e.g a Vec<>)Chunks of memory -(e.g a Vec<>)Free MemoryFree MemoryOffset to ensure -byte-alignmentOffset to ensure -byte-alignment \ No newline at end of file diff --git a/content/rust-os/2-shell/index.md b/content/rust-os/2-shell/index.md deleted file mode 100644 index 1cf9ad9..0000000 --- a/content/rust-os/2-shell/index.md +++ /dev/null @@ -1,299 +0,0 @@ -+++ -transparent = true -title = "Operating Systems in Rust #2: Shell" -description = "Continuing with the kernel we started in the previous post, we'll add a simple shell and a global allocator to use heap allocated data structures." -date = 2023-07-08 -updated = 2024-03-01 - -[taxonomies] -tags = ["rust", "riscv", "kernel"] -series = ["rust-os"] -+++ - -{% quote (class="info")%} - -This is a series of posts about my journey creating a kernel in Rust. You can find the code for this project [here](https://github.com/explodingcamera/pogos/tree/part-2) and all of the posts in this series [here](/series/rust-os/). - -{% end %} - -Now that we have a basic kernel that can print to the screen, we can start building out some more functionality. -I first want to create a simple shell that will allow us to run some commands and more easily interact with our system. - -As I mentioned in the previous post, we can't yet use heap-allocated data structures, so we'll start with implementing a Global Allocator. This will allow us to use APIs like `Box` and `Vec` anywhere in our kernel, making our lives much easier. - - - -# Memory Allocators - -To better understand global allocators, we'll create a simple linear allocator to allocate memory from a fixed-size buffer. This allocator will only be able to allocate memory, not free it, but it will be enough to get us started. - -A linear allocator - sometimes also called an arena allocator - just keeps track of the current index of the buffer and allocates memory from there - just as simple as it can get. These allocators are very fast but also very limited in their use cases. In the real world, they are often used where you need to allocate a lot of memory and then free it all at once, like in a game engine. They can also be used where you know you will only need a small amount of memory and want to avoid dealing with the overhead of a more complex allocator, like in embedded systems. - -{{ figure(caption = "Linear Allocators", position="center", src="./assets/linear-allocator.svg") }} - -First, we'll create a new file, `src/linear-allocator.rs` and will create the basic data structure for our allocator: - -{{ file(name = "src/linear-allocator.rs") }} - -```rust - -use core::sync::atomic::{AtomicUsize}; - -pub struct LinearAllocator { - head: AtomicUsize, // the current index of the buffer - // AtomicUsize is a special type that allows us to safely share data - // between threads without using locks - - start: *mut u8, // raw pointer to the start of the heap - end: *mut u8, // raw pointer to the end of the heap -} - -// allow our allocator to be shared between threads -unsafe impl Sync for LinearAllocator {} - -impl LinearAllocator { - // create a new, empty allocator - pub const fn empty() -> Self { - Self { - head: AtomicUsize::new(0), - start: core::ptr::null_mut(), - end: core::ptr::null_mut(), - } - } - - // initialize the allocator with a pre-allocated buffer of memory - // - start is a raw pointer to the start of the buffer - // - size is the length of the buffer - pub fn init(&mut self, start: usize, size: usize) { - self.start = start as *mut u8; - self.end = unsafe { self.start.add(size) }; - } -} -``` - -## Global Allocator - -We'll also need to implement the `GlobalAlloc` trait, so Rust's `#[global_allocator]` compile time built-in knows how to use our allocator. - -This trait has two methods: `alloc` and `dealloc`. We'll only implement `alloc` for now since we won't be able to free memory with our implementation. - -The trait also requires marking our implementation as `unsafe` since we are dealing with raw pointers and memory addresses. - -{{ file(name = "src/linear-allocator.rs") }} - -```rust - -use core::alloc::{GlobalAlloc, Layout}; -use core::ptr::NonNull; -use core::sync::atomic::{Ordering}; - -unsafe impl GlobalAlloc for LinearAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - /* The byte multiple that our allocated memory must start at - most hardware architectures perform better when reading/writing - data at aligned addresses (e.g. 4 bytes, 8 bytes, etc.) so we - need to make sure that our memory is aligned properly - */ - let align = layout.align(); - - // The size is the number of bytes we need to allocate - let size = layout.size(); - - let mut head = self.head.load(Ordering::Relaxed); - - // Align the head to the required alignment - // e.g. if head is 1 and align is 4, we need to add 3 to head to get 4 - if head % align != 0 { - head += align - (head % align); - } - - // Move the head forward by the size of the allocation - let new_head = head + size; - - // are we out of memory? - if self.start.add(new_head) > self.end { - return core::ptr::null_mut(); - } - - self.head.store(new_head, Ordering::Relaxed); - NonNull::new_unchecked(self.start.add(head) as *mut u8).as_ptr() - } - - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { - // no-op - } -} -``` - -Before we can start using our allocator, we need to give it a region of memory use. We'll do this in our `src/heap.rs` file: - -{{ file(name = "src/heap.rs") }} - -```rust - -#[global_allocator] -static mut KERNEL_HEAP_ALLOCATOR: LinearAllocator = LinearAllocator::empty(); - -// this will allocate 128kb of memory in the .bss section -static mut KERNEL_HEAP: [u8; 0x20000] = [0; 0x20000]; - -/// Initialize the heap allocator. -pub unsafe fn init_kernel_heap() { - let heap_start = KERNEL_HEAP.as_ptr() as usize; - let heap_size = KERNEL_HEAP.len(); - KERNEL_HEAP_ALLOCATOR.init(heap_start, heap_size); -} -``` - -{{ file(name = "src/main.rs") }} - -```rust - -#![no_std] -#![no_main] -#![feature(panic_info_message)] -#![feature(allocator_api)] // new -#![feature(lazy_cell)] -#![allow(unused)] - -extern crate alloc; // new -extern crate riscv_rt; - -use riscv_rt::entry; -mod panic_handler; -mod utils; - -mod linear_allocator; // new -mod heap; // new - -#[entry] -fn main(a0: usize) -> ! { - println!("Hello world from hart {}!\n", a0); - - // Setup everything required for the kernel to run - unsafe { - heap::init_kernel_heap(); // new - } - - utils::shutdown(); -} -``` - -Now, we can use our allocator to allocate some memory! Let's create a `Vec` and push some values to it to make sure everything works as expected: - -{{ file(name = "src/main.rs") }} - -```rust - -let mut v = Vec::new(); - -v.push(1); -v.push(2); -v.push(3); - -println!("{:?}", v); - -// [1, 2, 3] -``` - -This is great, but we can only do a little with this allocator since we can't free memory. Alternative allocation strategies are, for example, [linked list allocators](https://os.phil-opp.com/allocator-designs/#linked-list-allocator), [binary buddy allocators](https://www.kernel.org/doc/gorman/html/understand/understand009.html), and [slab allocators](https://www.kernel.org/doc/gorman/html/understand/understand011.html). We won't be implementing any of these in here, but I encourage you to read about them and try to implement them yourself! Some of these are also available as crates on crates.io to use them as drop-in replacements for our naive implementation here ([linked_list_allocator](https://crates.io/crates/linked-list-allocator), [buddy_system_allocator](https://crates.io/crates/buddy_system_allocator) and [slabmalloc](https://crates.io/crates/slabmalloc)). - -# Shell - -With most of the essential rust features available, we can now start building our shell. This shell will allow us to interact with our kernel and inspect its state. -The shell will be a simple loop that reads characters from SBIs `console_getchar function and executes some basic commands. - -{{ file(name = "src/main.rs") }} - -```rust - -pub const ENTER: u8 = 13; -pub const BACKSPACE: u8 = 127; - -pub fn shell() { - print!("> "); - - let mut command = String::new(); // heap allocated strings! - - loop { - match sbi::legacy::console_getchar() { - Some(ENTER) => { - println!(); - process_command(&command); - command.clear(); - print!("> "); - } - Some(BACKSPACE) => { - if command.len() > 0 { - command.pop(); - print!("{}", BACKSPACE as char) - } - } - } - } -} - -fn process_command(command: &str) { - match command { - "help" | "?" | "h" => { - println!("available commands:"); - println!(" help print this help message (alias: h, ?)"); - println!(" shutdown shutdown the machine (alias: sd, exit)"); - } - "shutdown" | "sd" | "exit" => util::shutdown(), - "" => {} - _ => { - println!("unknown command: {command}"); - } - }; -} -``` - -Now, when we run our kernel, we'll be greeted with a prompt, and we can type in commands like `help` and `shutdown` to see the available commands and shut down the machine. - -``` -> help -available commands: - help print this help message (alias: h, ?) - shutdown shutdown the machine (alias: sd, exit) -> exit -``` - -## Debugging - -Being able to shut down the machine is great and all, but let's add some more functionality. We'll start by adding commands to trigger different exceptions so we can test our exception handler from the previous chapter. - -{{ file(name = "src/main.rs") }} - -```rust - -match command { - // ... - "pagefault" => { - // read from an invalid address to trigger a page fault - unsafe { core::ptr::read_volatile(0xdeadbeef as *mut u64); } - } - "breakpoint" => { - // ebreak triggers a breakpoint exception, a trap that can be used for debugging with gdb or similar tools - unsafe { asm!("ebreak") }; - } - // ... -} -``` - -When we run the `pagefault` command, we'll see that nothing happens. This is because we still need to set up a page table, and the kernel is still running in physical memory, something we want to change in the next chapter. - -Our breakpoint command, however, will trigger a breakpoint exception, and we'll see the following output: - -``` -Exception handler called -Trap frame: TrapFrame { ra: 2149605116, t0: 9, t1: 2149632682, t2: 22, t3: 5760, t4: 48, t5: 12288, t6: 1, a0: 8, a1: 0, a2: 2149663232, a3: 2149593222, a4: 110, a5: 2149663744, a6: 4, a7: 1 } -panicked at 'Exception cause: Exception(Breakpoint)', kernel/src/trap.rs:33:5 -``` - -We can now see the trap frame and the exception cause, so we can start debugging our kernel. You can also use an external debugger like [gdb](https://www.gnu.org/software/gdb/), which allows you to set breakpoints, inspect memory, step through code, and much more. -Setting up gdb is a bit more involved, so if you run into issues, check out this [guide](https://os.phil-opp.com/set-up-gdb/). - -There are a lot of improvements we can make to this shell. For some ideas, check out my `simple_shell` crate on [crates.io](https://crates.io/crates/simple_shell). I recommend you try implementing some of these yourself, as it's a fun exercise. Something that might be helpful is this ANSI escape code [cheat sheet](https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797) for processing user input like arrow keys, backspace, etc. - -After this relatively short chapter, we'll add multi-tasking to our kernel using async Rust so we can run multiple programs simultaneously. diff --git a/content/rust-os/3-async/index.md b/content/rust-os/3-async/index.md deleted file mode 100644 index 8e3459d..0000000 --- a/content/rust-os/3-async/index.md +++ /dev/null @@ -1,17 +0,0 @@ -+++ -transparent = true -title = "Operating Systems in Rust #3: Async" -description = "In this post, we'll be adding an async runtime to our kernel" -date = 2024-10-08 -draft = true - -[taxonomies] -tags = ["rust", "riscv", "kernel"] -series = ["rust-os"] -+++ - -{% quote (class="info")%} - -This is a series of posts about my journey creating a kernel in rust. You can find the code for this project [here](https://github.com/explodingcamera/pogos/tree/part-2) and all of the posts in this series [here](/series/rust-os/). - -{% end %} diff --git a/content/rust-os/_index.md b/content/rust-os/_index.md deleted file mode 100644 index 8f2398e..0000000 --- a/content/rust-os/_index.md +++ /dev/null @@ -1,4 +0,0 @@ -+++ -transparent = true -render = false -+++ diff --git a/content/snippets/_index.md b/content/snippets/_index.md deleted file mode 100644 index 9fb6820..0000000 --- a/content/snippets/_index.md +++ /dev/null @@ -1,3 +0,0 @@ -+++ -transparent = true -+++ diff --git a/content/snippets/upload.md b/content/snippets/upload.md deleted file mode 100644 index beff35d..0000000 --- a/content/snippets/upload.md +++ /dev/null @@ -1,56 +0,0 @@ -+++ -title = "Stop building your own file upload systems" -description = "Using presigned URLs to upload files directly to S3-like storage systems" -date = 2023-03-14 -draft = false - -[taxonomies] -tags = ["s3", "typescript", "serverless"] -+++ - -Instead of reinventing the wheel for every project and spending time understanding the intricacies of multipart uploads, you can upload files directly to S3/R2/whatever using a pre-signed URL. -You don't even need the full AWS SDK, you can use something more minimal like [aws4fetch](https://github.com/mhart/aws4fetch). - -Especially if you are using serverless, this is a great way to save time and money. - -```ts -import { AwsClient } from "aws4fetch"; - -const r2 = new AwsClient({ - accessKeyId: process.env.ACCESS_KEY, - secretAccessKey: process.env.ACCESS_SECRET, -}); - -export const getPresignedSignedUrl = async ( - key: string, - contentLength: number, - contentType: string = "image/jpeg" -) => { - const url = new URL( - `https://something.r2.cloudflarestorage.com/something/${key}` - ); - - // Only sign the URL if the content length is less than 10MB - if (contentLength > 10000000) return undefined; - - // Specify a custom expiry for the presigned URL, in seconds - url.searchParams.set("X-Amz-Expires", "3600"); - - const signed = await r2.sign( - new Request(url, { - method: "PUT", - headers: { - "Content-Type": contentType, - "Content-Length": contentLength.toString(), - }, - }), - { - aws: { signQuery: true, allHeaders: true }, - } - ); - - return signed.url; -}; -``` - -> Note that this will leak your account id and bucket name in the URL which should be fine for most use cases. diff --git a/elasticlunr.min.js b/elasticlunr.min.js new file mode 100644 index 0000000..79dad65 --- /dev/null +++ b/elasticlunr.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.6 + * + * Copyright (C) 2017 Oliver Nightingale + * Copyright (C) 2017 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oa{margin-right:10px}@media (max-width: 899px){.footer .copyright>a{margin:0}}@media (max-width: 899px){.footer .copyright{flex-direction:column;margin-top:10px}}@media (max-width: 899px){.footer .copyright-theme-sep{display:none}}@media (max-width: 899px){.footer .copyright-theme{font-size:0.75rem}} diff --git a/header.css b/header.css new file mode 100644 index 0000000..4e132d6 --- /dev/null +++ b/header.css @@ -0,0 +1 @@ +.header{display:flex;flex-direction:column;position:relative}.header__inner{display:flex;align-items:center;justify-content:space-between}.header__logo{display:flex;flex:1;margin:0 20px;display:flex}.header__logo a{flex:0 0 auto;max-width:100%}.header .menu{margin:20px 0}.header .menu__inner{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.header .menu__inner li.active{color:var(--accent-alpha-70)}.header .menu__inner li:not(:last-of-type){margin-right:20px;margin-bottom:10px;flex:0 0 auto}.header .menu__sub-inner{position:relative;list-style:none;padding:0;margin:0}.header .menu__sub-inner:not(:only-child){margin-left:20px}.header .menu__sub-inner-more{position:absolute;background:var(--background);box-shadow:var(--shadow);color:white;border:2px solid;margin:0;padding:10px;list-style:none;z-index:99;top:35px;left:0}.header .menu__sub-inner-more-trigger{color:var(--accent);user-select:none;cursor:pointer}.header .menu__sub-inner-more li{margin:0;padding:5px;white-space:nowrap} diff --git a/highlight_themes/dracula.tmTheme b/highlight_themes/dracula.tmTheme deleted file mode 100644 index b45d2f9..0000000 --- a/highlight_themes/dracula.tmTheme +++ /dev/null @@ -1,535 +0,0 @@ - - - - - - - - name - Dracula - settings - - - settings - - background - #282a36 - caret - #f8f8f0 - foreground - #f8f8f2 - invisibles - #3B3A32 - lineHighlight - #44475a - selection - #44475a - findHighlight - #effb7b - findHighlightForeground - #000000 - selectionBorder - #222218 - activeGuide - #9D550FB0 - bracketsForeground - #F8F8F2A5 - bracketsOptions - underline - bracketContentsForeground - #F8F8F2A5 - bracketContentsOptions - underline - tagsOptions - stippled_underline - - - - name - Comment - scope - comment - settings - - foreground - #6272a4 - fontStyle - - - - - name - String - scope - string - settings - - foreground - #f1fa8c - - - - name - Number - scope - constant.numeric - settings - - foreground - #bd93f9 - - - - name - Built-in constant - scope - constant.language - settings - - foreground - #bd93f9 - - - - name - User-defined constant - scope - constant.character, constant.other - settings - - foreground - #bd93f9 - - - - name - Variable - scope - variable - settings - - fontStyle - - - - - name - Ruby's @variable - scope - variable.other.readwrite.instance - settings - - fontStyle - - foreground - #ffb86c - - - - name - String interpolation - scope - constant.character.escaped, constant.character.escape, string source, string source.ruby - settings - - fontStyle - - foreground - #ff79c6 - - - - name - Keyword - scope - keyword - settings - - foreground - #ff79c6 - - - - name - Storage - scope - storage - settings - - fontStyle - - foreground - #ff79c6 - - - - name - Storage type - scope - storage.type - settings - - fontStyle - italic - foreground - #8be9fd - - - - name - Class name - scope - entity.name.class - settings - - fontStyle - underline - foreground - #50fa7b - - - - name - Inherited class - scope - entity.other.inherited-class - settings - - fontStyle - italic underline - foreground - #50fa7b - - - - name - Function name - scope - entity.name.function - settings - - fontStyle - - foreground - #50fa7b - - - - name - Function argument - scope - variable.parameter - settings - - fontStyle - italic - foreground - #ffb86c - - - - name - Tag name - scope - entity.name.tag - settings - - fontStyle - - foreground - #ff79c6 - - - - name - Tag attribute - scope - entity.other.attribute-name - settings - - fontStyle - - foreground - #50fa7b - - - - name - Library function - scope - support.function - settings - - fontStyle - - foreground - #8be9fd - - - - name - Library constant - scope - support.constant - settings - - fontStyle - - foreground - #6be5fd - - - - name - Library class/type - scope - support.type, support.class - settings - - fontStyle - italic - foreground - #66d9ef - - - - name - Library variable - scope - support.other.variable - settings - - fontStyle - - - - - name - Invalid - scope - invalid - settings - - background - #ff79c6 - fontStyle - - foreground - #F8F8F0 - - - - name - Invalid deprecated - scope - invalid.deprecated - settings - - background - #bd93f9 - foreground - #F8F8F0 - - - - name - JSON String - scope - meta.structure.dictionary.json string.quoted.double.json - settings - - foreground - #CFCFC2 - - - - name - diff.header - scope - meta.diff, meta.diff.header - settings - - foreground - #6272a4 - - - - name - diff.deleted - scope - markup.deleted - settings - - foreground - #ff79c6 - - - - name - diff.inserted - scope - markup.inserted - settings - - foreground - #50fa7b - - - - name - diff.changed - scope - markup.changed - settings - - foreground - #E6DB74 - - - - scope - constant.numeric.line-number.find-in-files - match - settings - - foreground - #bd93f9 - - - - scope - entity.name.filename - settings - - foreground - #E6DB74 - - - - scope - message.error - settings - - foreground - #F83333 - - - - name - JSON Punctuation - scope - punctuation.definition.string.begin.json - meta.structure.dictionary.value.json, punctuation.definition.string.end.json - meta.structure.dictionary.value.json - settings - - foreground - #EEEEEE - - - - name - JSON Structure - scope - meta.structure.dictionary.json string.quoted.double.json - settings - - foreground - #8be9fd - - - - name - JSON String - scope - meta.structure.dictionary.value.json string.quoted.double.json - settings - - foreground - #f1fa8c - - - - name - JSON: 6 deep - scope - meta meta meta meta meta meta meta.structure.dictionary.value string - settings - - foreground - #50fa7b - - - - name - JSON: 5 deep - scope - meta meta meta meta meta meta.structure.dictionary.value string - settings - - foreground - #ffb86c - - - - name - JSON: 4 deep - scope - meta meta meta meta meta.structure.dictionary.value string - settings - - foreground - #ff79c6 - - - - name - JSON: 3 deep - scope - meta meta meta meta.structure.dictionary.value string - settings - - foreground - #bd93f9 - - - - name - JSON: 2 deep - scope - meta meta meta.structure.dictionary.value string - settings - - foreground - #50fa7b - - - - name - JSON: 1 deep - scope - meta meta.structure.dictionary.value string - settings - - foreground - #ffb86c - - - - uuid - 83091B89-765E-4F0D-9275-0EC6CB084126 - colorSpaceName - sRGB - semanticClass - theme.dark.dracula - author - Zeno Rocha - - diff --git a/index.html b/index.html new file mode 100644 index 0000000..7d477b2 --- /dev/null +++ b/index.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+ +
+ + +
    + +
    + +
    + + + + +
    + + + + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..16d9e57 --- /dev/null +++ b/index.js @@ -0,0 +1,12 @@ +import barba from "https://cdn.skypack.dev/pin/@barba/core@v2.9.7-eogaRu5SwKJ4kikvgK0t/mode=imports,min/optimized/@barba/core.js"; + +barba.init({ + transitions: [ + { + name: "default-transition", + enter() { + // nextPreset(visualizer); + }, + }, + ], +}); \ No newline at end of file diff --git a/logo.css b/logo.css new file mode 100644 index 0000000..f663658 --- /dev/null +++ b/logo.css @@ -0,0 +1 @@ +.logo{color:var(--accent);display:flex;align-items:center;text-decoration:none} diff --git a/main.css b/main.css new file mode 100644 index 0000000..cbce96a --- /dev/null +++ b/main.css @@ -0,0 +1 @@ +html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}body{margin:0;padding:0;font-family:var(--font-family-mono);font-size:1rem;line-height:1.54;background-color:var(--background);color:var(--color);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%}@media (max-width: 683px){body{font-size:1rem}}h1,h2,h3,h4,h5,h6{display:flex;align-items:center;font-weight:bold;line-height:1.3}h1{font-size:1.4rem}h2{font-size:1.3rem}h3{font-size:1.2rem}h4,h5,h6{font-size:1.15rem}a{color:inherit}img{display:block;max-width:100%}img.left{margin-right:auto}img.center{margin-left:auto;margin-right:auto}img.right{margin-left:auto}p{margin-bottom:20px;font-family:var(--font-family-text);letter-spacing:-0.003em;line-height:32px;margin-top:2em;font-size:20px;margin-bottom:-0.46em;word-break:break-word;font-weight:400}figure{display:table;max-width:100%;margin:25px 0}figure.left img{margin-right:auto}figure.center img{margin-left:auto;margin-right:auto}figure.right img{margin-left:auto}figure figcaption{font-size:14px;padding:5px 10px;margin-top:5px;background:var(--accent);color:var(--background)}figure figcaption.left{text-align:left}figure figcaption.center{text-align:center}figure figcaption.right{text-align:right}code{font-family:Hack, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;font-feature-settings:normal;background:var(--accent-alpha-20);padding:1px 6px;margin:0 2px;font-size:0.95rem}pre{font-family:Hack, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;padding:20px;font-size:0.95rem;overflow:auto;border-top:1px solid rgba(255,255,255,0.1);border-bottom:1px solid rgba(255,255,255,0.1)}@media (max-width: 683px){pre{white-space:pre-wrap;word-wrap:break-word}}pre code{padding:0;margin:0;background:none}blockquote{border-top:1px solid var(--accent);border-bottom:1px solid var(--accent);margin:40px 0;padding:25px}@media (max-width: 683px){blockquote{padding-right:0}}blockquote:before{content:"”";font-family:Georgia, serif;font-size:3.875rem;position:absolute;left:-40px;top:-20px}blockquote p:first-of-type{margin-top:0}blockquote p:last-of-type{margin-bottom:0}blockquote p{position:relative}blockquote p:before{content:">";display:block;position:absolute;left:-25px;color:var(--accent)}table{table-layout:fixed;border-collapse:collapse;width:100%;margin:40px 0}table,th,td{border:1px dashed var(--accent);padding:10px}th{color:var(--accent)}ul,ol{margin-left:20px;padding:0}ul li,ol li{position:relative}@media (max-width: 683px){ul,ol{margin-left:20px}}ol ol{list-style-type:lower-alpha}.container{display:flex;flex-direction:column;padding:40px;max-width:864px;min-height:100vh;margin:0 auto}@media (max-width: 683px){.container{padding:20px}}.content{display:flex}hr{width:100%;border:none;background:var(--border-color);height:1px}.hidden{display:none} diff --git a/pagination.css b/pagination.css new file mode 100644 index 0000000..6ca2b37 --- /dev/null +++ b/pagination.css @@ -0,0 +1 @@ +.pagination{margin-top:50px}.pagination__title{display:flex;text-align:center;position:relative;margin:100px 0 20px}.pagination__title-h{text-align:center;margin:0 auto;padding:5px 10px;background:var(--background);font-size:.8rem;text-transform:uppercase;letter-spacing:.1em;z-index:1}.pagination__title hr{position:absolute;left:0;right:0;width:100%;margin-top:15px;z-index:0}.pagination__buttons{display:flex;align-items:center;justify-content:center}@media (max-width: 683px){.pagination__buttons{flex-direction:column}}.button{position:relative;display:inline-flex;align-items:center;justify-content:center;font-size:1rem;border-radius:8px;max-width:40%;padding:0;cursor:pointer;appearance:none}@media (max-width: 683px){.button{max-width:80%}}.button+.button{margin-left:10px}.button a{display:flex;padding:8px 16px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.button__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden} diff --git a/post.css b/post.css new file mode 100644 index 0000000..efa2ff3 --- /dev/null +++ b/post.css @@ -0,0 +1 @@ +.posts{width:100%;margin:0 auto}.post{width:100%;text-align:left;margin:20px 20px;padding:20px 0}@media (max-width: 899px){.post{max-width:660px}}.post:not(:last-of-type){border-bottom:1px solid var(--border-color)}.post .post-meta-inline,.post .post-meta{font-size:1rem;margin-bottom:10px;color:var(--accent-alpha-70)}.post-meta-inline{display:inline}.post-title{position:relative;margin:0;font-weight:normal}.post-title a{text-decoration:none}.post .post-tags-inline,.post .post-tags{margin-bottom:20px;font-size:1rem;opacity:0.5}.post-tags{display:block}.post-tags-inline{display:inline}@media (max-width: 683px){.post-tags-inline{display:block}}.post-content{margin-top:30px}.post-cover{border:20px solid var(--accent);background:transparent;margin:40px 0;padding:20px}@media (max-width: 683px){.post-cover{padding:10px;border-width:10px}}.post ul{list-style:none}.post ul li:before{content:"*";position:absolute;left:-20px;top:2px;color:var(--accent)}.post--regulation h1{justify-content:center}.post--regulation h2{justify-content:center;margin-bottom:10px}.post--regulation h2+h2{margin-top:-10px;margin-bottom:20px}.post-list .post-date{color:var(--accent-alpha-70);text-decoration:none}.post-list a{text-decoration:none}.post-list .post-list-title{text-decoration:underline}.post-list .post-tag{text-decoration:none} diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..ae076b1 --- /dev/null +++ b/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: +Allow: / +Sitemap: https://thoughts.henrygressmann.de/sitemap.xml diff --git a/sass/buttons.scss b/sass/buttons.scss deleted file mode 100644 index 7a864b0..0000000 --- a/sass/buttons.scss +++ /dev/null @@ -1,92 +0,0 @@ -.button-container { - display: table; - margin-left: auto; - margin-right: auto; -} - -button, -.button, -a.button { - position: relative; - display: flex; - align-items: center; - justify-content: center; - padding: 8px 18px; - margin-bottom: 5px; - text-align: center; - border-radius: 8px; - border: 1px solid transparent; - appearance: none; - cursor: pointer; - outline: none; - - /* variants */ - - &.outline { - background: transparent; - box-shadow: none; - padding: 8px 18px; - - :hover { - transform: none; - box-shadow: none; - } - } - - &.primary { - box-shadow: 0 4px 6px rgba(50, 50, 93, .11), 0 1px 3px rgba(0, 0, 0, .08); - - &:hover { - box-shadow: 0 2px 6px rgba(50, 50, 93, .21), 0 1px 3px rgba(0, 0, 0, .08); - } - } - - &.link { - background: none; - font-size: 1rem; - } - - - /* sizes */ - - &.small { - font-size: .8rem; - } - - &.wide { - min-width: 200px; - padding: 14px 24px; - } -} - -a.read-more, -a.read-more:hover, -a.read-more:active { - display: inline-flex; - background: none; - box-shadow: none; - padding: 0; - margin: 20px 0; - max-width: 100%; -} - -.code-toolbar { - margin-bottom: 20px; - - .toolbar-item a { - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - padding: 3px 8px; - margin-bottom: 5px; - text-align: center; - font-size: 13px; - font-weight: 500; - border-radius: 8px; - border: 1px solid transparent; - appearance: none; - cursor: pointer; - outline: none; - } -} diff --git a/sass/fonts/jetbrainsmono.scss b/sass/fonts/jetbrainsmono.scss deleted file mode 100644 index ec440f7..0000000 --- a/sass/fonts/jetbrainsmono.scss +++ /dev/null @@ -1,7 +0,0 @@ -@font-face { - font-family: "JetBrains Mono"; - src: url("/font/mono/JetBrainsMono.woff2") format("woff2-variations"); - font-style: italic; - font-display: swap; - font-weight: 100 1000; -} diff --git a/sass/fonts/libertinus.scss b/sass/fonts/libertinus.scss deleted file mode 100644 index 0f71477..0000000 --- a/sass/fonts/libertinus.scss +++ /dev/null @@ -1,336 +0,0 @@ -/** - * @version 7.031 - * @date 2021-01-17 - */ -/** - * Libertinus Font Faces - * ===================== - */ -/** - * Libertinus Serif - * ---------------- - */ -@font-face /* Libertinus Serif Regular Upright */ { - font-family: Libertinus Serif /*, Libertinus*/; - font-style: normal; - font-weight: 400; - src: local(Libertinus Serif), local(Libertinus Serif Regular), - local(Libertinus), - url("/fonts/libertinus/LibertinusSerif-Regular.woff2") format("woff2"); -} -@font-face /* Libertinus Serif Regular Italic */ { - font-family: Libertinus Serif /*, Libertinus*/; - font-style: italic; - font-weight: 400; - src: local(Libertinus Serif Italic), - url("/fonts/libertinus/LibertinusSerif-Italic.woff2") format("woff2"); -} -@font-face /* Libertinus Serif Semibold Upright */ { - font-family: Libertinus Serif /*, Libertinus*/; - font-style: normal; - font-weight: 600; - src: local(Libertinus Serif), local(Libertinus Serif Semibold), - url("/fonts/libertinus/LibertinusSerif-Semibold.woff2") format("woff2"); -} -@font-face /* Libertinus Serif Semibold Italic */ { - font-family: Libertinus Serif /*, Libertinus*/; - font-style: italic; - font-weight: 600; - src: local(Libertinus Serif Semibold Italic), - url("/fonts/libertinus/LibertinusSerif-SemiboldItalic.woff2") - format("woff2"); -} -@font-face /* Libertinus Serif Bold Upright */ { - font-family: Libertinus Serif /*, Libertinus*/; - font-style: normal; - font-weight: 700; - src: local(Libertinus Serif Bold), - url("/fonts/libertinus/LibertinusSerif-Bold.woff2") format("woff2"); -} -@font-face /* Libertinus Serif Bold Italic */ { - font-family: Libertinus Serif /*, Libertinus*/; - font-style: italic; - font-weight: 700; - src: local(Libertinus Serif Bold Italic), - url("/fonts/libertinus/LibertinusSerif-BoldItalic.woff2") format("woff2"); -} -/** - * ### Libertinus Serif Display ### - */ -@font-face /* Libertinus Serif Display */ { - font-family: Libertinus /*Serif*/ Display /*, Libertinus*/; - font-style: normal; - font-weight: 400; - src: local(Libertinus Serif Display), - url("/fonts/libertinus/LibertinusSerifDisplay-Regular.woff2") - format("woff2"); -} -/** - * ### Libertinus Serif Initials ### - */ -@font-face /* Libertinus Serif Initials */ { - font-family: Libertinus /*Serif*/ Initials /*, Libertinus*/; - font-style: normal; - font-weight: 400; - src: local(Libertinus Serif Initials), - url("/fonts/libertinus/LibertinusSerifInitials-Regular.woff2") - format("woff2"); -} -/** - * Libertinus Sans - * --------------- - */ -@font-face /* Libertinus Sans Regular Upright */ { - font-family: Libertinus Sans /*, Biolinum*/; - font-style: normal; - font-weight: 400; - src: local(Libertinus Sans), - url("/fonts/libertinus/LibertinusSans-Regular.woff2") format("woff2"); -} -@font-face /* Libertinus Sans Regular Italic */ { - font-family: Libertinus Sans /*, Biolinum*/; - font-style: italic; - font-weight: 400; - src: local(Libertinus Sans Italic), - url("/fonts/libertinus/LibertinusSans-Italic.woff2") format("woff2"); -} -@font-face /* Libertinus Sans Bold Upright */ { - font-family: Libertinus Sans /*, Biolinum*/; - font-style: normal; - font-weight: 700; - src: local(Libertinus Sans Bold), - url("/fonts/libertinus/LibertinusSans-Bold.woff2") format("woff2"); -} -/** - * Libertinus Math - */ -@font-face /* Libertinus Serif Math */ { - font-family: Libertinus /*Serif*/ Math; - font-style: normal; - font-weight: 400; - src: local(Libertinus Math), - url("/fonts/libertinus/LibertinusMath-Regular.woff2") format("woff2"); -} -/** - * Libertinus Mono - */ -@font-face /* Libertinus Serif Mono */ { - font-family: Libertinus /*Serif*/ Mono; - font-style: normal; - font-weight: 400; - src: local(Libertinus Mono), - url("/fonts/libertinus/LibertinusMono-Regular.woff2") format("woff2"); -} -/** - * Libertinus Keyboard - */ -@font-face /* Libertinus Serif Keyboard */ { - font-family: Libertinus Keyboard; - font-style: normal; - font-weight: 400; - src: local(Libertinus Keyboard), - url("/fonts/libertinus/LibertinusKeyboard-Regular.woff2") format("woff2"); -} - -/** - * Standard OpenType Features - * ========================== - * - * Libertinus supports several OpenType features, - * some are enabled by default (`-`) others have to be opted in (`+`), - * most can be accessed by high-level CSS properties and values: - * - * * Small Caps: features `font-variant` and `font-variant-caps` with values `small-caps` (`+smcp`) and `all-small-caps` (`+c2sc`) - * * Ligatures and Contextual Alternates: `-liga`, `+hlig`, `+dlig`; `-calt` - * * Kerning: `-kern` - * * Capital Spacing: `+cpsp`, `case` - * * Slashed zero: `+zero` - * * Numerals: `-tnum`, `+pnum`, `-lnum`, `+onum`, `+pnum+onum` - * * Vulgar fractions: `+frac` - * * Subscripts and Superscripts: `+sinf`, `+sups` - * * Diacritic marks: `-mark`, `-mkmk` - * * Stylistic alternates: `+salt` - */ -/** - * Cased forms: `+case` - * -------------------- - * - * All digits and some punctuation characters (i.e. parentheses, guillemets and hyphen) use shapes that better fit with all-uppercase text (`.cap` or `.sc`). - * - * This should really be automatically activated for `text-transform: uppercase`, but is not as of Level 4 of CSS Fonts and CSS Text. - */ -/** - * Standard ligatures: `-liga` - * --------------------------- - * - * In serif faces, the Latin lowercase letter sequences - * _ſſ_, _ſſi_, _ſſj_, _ſſk_, _ſſl_, _ſh_, _ſi_, _ſj_, _ſl_, _ſs_, _ſt_, - * _ff_, _ffh_, _ffi_, _ffj_, _ffk_, _ffl_, _fh_, _fi_, _fj_, _fk_, _fl_, _ft_ - * form default ligatures. - * In sans-serif faces, only the Latin lowercase letter sequences - * _ff_ (also if the second one has a shortened head) and _ft_ - * form default ligatures. - * - * The related feature `clig` is not used. - * - * The respective CSS property is `font-variant-ligatures` with the value `common-ligatures` (or `no-common-ligatures`). - */ -/** - * Discretionary and Historical Ligatures: `+dlig`, `+hlig` - * -------------------------------------------------------- - * - * The only historical ligatures supported are Latin lowercase _ st_ and _ct_ in all faces. - * - * In all faces, Latin lowercase letter sequences _tt_ and _tz_, - * in serif faces (i.e. non-sans-serif), also _Th_, _ck_ and _ch_, - * form discretionary ligatures. - * - * These can all be manually forced by using ZWJ (U+200D) between letters and just the default feature `liga`, too. - * - * The respective CSS property is `font-variant-ligatures` with the values `discretionary-ligatures` and `historical-ligatures`. - */ -/** - * Contextual alternates: `-calt` - * ------------------------------ - * - * In all faces, - * the Latin capital and small-capital letter *Q* gains a long tail if followed by either lowercase or small-capital letter *u* or *v*, and - * the Latin lowercase letter *f*, even when part of the ligature *ff*, has a shortened head if followed by closing parentheses, top quotation mark, lowercase letter with ascending left leg, lowercase letter with diacritic mark above or uppercase letter not starting with a leg or them on the left. - * - * The related feature `clig` is not used. - * - * The respective CSS property is `font-variant-ligatures` with the value `contextual` (or `no-contextual`). - */ -/** - * Localized alternates: `locl` - * ---------------------------- - * - * For Serbian and Macedonian, - * in all faces, the Cyrillic lowercase letter be *б* and - * in italic faces, the Cyrillic llowercase letters ghe *г*, gje *ѓ*, de *д*, pe *п* and te *т* - * are replaced by alternate glyphs. - * For Scandinavian languages, because of preferences in Sami typography, the Latin uppercase letter Eng *Ŋ* uses a alternative glyph, cf. `ss07`. - * For Turkic languages, - * ligatures where the Latin lowercase letter _i_ is the second part are deactivated, i.e. _fi_, _ffi_, _ſi_ and _ſſi_. - * The small-capitals handling of _i_ / _ı_ does not depend on `locl`, but is part of `smcp` (and `c2sc`). - */ -/** - * Numerals: `-tnum`, `+pnum`, `-lnum`, `+onum`, `+pnum+onum` - * ---------------------------------------------------------- - * - * Except for Libertinus Math, the standard digits can be forced - * to _old-style_ forms (with ascenders and descenders) with `onum` (`.taboldstyle`), - * to default _lining_ forms with `lnum`, - * to _proportional_ forms with `pnum` (`.fitted`), - * to _tabular_ (fixed-width) forms with `tnum`. - * The latter two also apply to euro *€* and yen *¥* currency symbols and trump the other features. - * - * The respective CSS property is `font-variant-numeric` with the values `oldstyle-nums`, `lining-nums`, `proportional-nums` and `tabular-nums`. - */ -/** - * Vulgar fractions: `+frac`; `subs`, `sups`; `sinf` - * ------------------------- - * - * The same sets of inferior/subscript and superior/superscript glyphs are used for various features. - * - * The respective CSS property for `frac` is `font-variant-numeric` with the value `diagonal-fractions`. - * The value `stacked-fraction` is not supported. - */ - -/** - * Advanced OpenType Font Features: Stylistic Sets - * =============================================== - */ - -@font-feature-values Libertinus, - Libertinus Serif, - Libertinus Sans, - Libertinus Math, - Libertinus Mono, - Libertinus Keyboard { - /** - * Stylistic alternates: `+salt` - * ----------------------------- - * - * For non-sans-serif faces, uppercase letters J, K, R and W use alternate glyphs (`.alt`). - * For small-caps faces, lowercase letters a, q, ŋ and ß use alternate glyphs (`.scalt` etc.). - * In all faces, - * Latin uppercase letters *Q*, eng *Ŋ* and eszet *ẞ*, - * Latin lowercase letters *h*, *y* and eszet *ß*, - * Greek lowercase letters beta *β*, theta *θ*, kappa *κ* and phi *φ*, - * Cyrillic letter be *б* and - * the ampersand *&* - * use alternate glyphs, cf. `calt`, `ss03`, `ss04`, `ss06`, `ss07`, `locl`. - * - * The respective CSS property is `font-variant-alternates` with the value `stylistic()`. - * The definition in CSS does not seem well thought out yet since it always requires a value name. - */ - @stylistic { - on: 1; - } - /** - * Stylistic Set 1 `ss01` _Low diaeresis on 'A', 'E', 'O'_ - * ------------------------------------------------------- - * - * In German, the umlaut dots above the Latin capital letters *A* and *O* (*Ä*, *Ö*) are sometimes moved further apart while the ones above *U* (*Ü*) are put closer together. - */ - @styleset { - low-diaeresis: 1; - } - /** - * Stylistic Set 2 `ss02` _Swashy 'J' 'K' 'R'_ - * ------------------------------------------- - * - * A bit more swashy, cursive look with elongated tails for Latin uppercase letter *K* and *R* and a left-hand horizontal top for *J*. - * This does not apply to sans-serif faces. - */ - @styleset { - swashy: 2; - } - /** - * Stylistic Set 3 `ss03` _'double s' to two 's'_ - * ---------------------------------------------- - * - * All variants of German eszett (lowercase *ß*, uppercase *ẞ* and small-capital) are rendered as round-s digraphs instead, very applicable to Swiss German texts. - */ - @styleset { - double-s: 3; - } - /** - * Stylistic Set 4 `ss04` _Upper case 'double s' to two 'S'_ - * --------------------------------------------------------- - * - * Only the uppercase German eszett *ẞ* is rendered as a round-s digraph, subset of `ss03`. - */ - @styleset { - uppercase-double-s: 4; - } - /** - * Stylistic Set 5 `ss05` _Crossed 'W'";_ - * -------------------------------------- - * - * Latin uppercase letter *W* is rendered as a ligature of two V, i.e. the middle verticals are longer, as used in the Wikipedia logo. - * This does not apply to sans-serif faces. - */ - @styleset { - crossed-w: 5; - } - /** - * Stylistic Set 6 `ss06` _Swash '&'_ - * ---------------------------------- - * - * The ampersand *&* is rendered as an _et_ ligature. - */ - @styleset { - swash-ampersand: 6; - } - /** - * Stylistic Set 7 `ss07` _Swap 'Eng' forms_ - * ----------------------------------------- - * - * The uppercase letter Eng *Ŋ*, i.e. an N with a descending tail on the right leg, uses the capital N glyph as its base instead of the default enlarged lowercase n glyph. The localized `locl` UC Style for Sami is changed the other way around. - */ - @styleset { - swap-eng: 7; - } -} diff --git a/sass/fonts/shantell.scss b/sass/fonts/shantell.scss deleted file mode 100644 index 1c94453..0000000 --- a/sass/fonts/shantell.scss +++ /dev/null @@ -1,7 +0,0 @@ -@font-face { - font-family: "Shantell Sans"; - src: url("/font/shantell/ShantellSans.woff2") format("woff2-variations"); - font-style: italic; - font-display: swap; - font-weight: 100 1000; -} diff --git a/sass/footer.scss b/sass/footer.scss deleted file mode 100644 index fe40590..0000000 --- a/sass/footer.scss +++ /dev/null @@ -1,59 +0,0 @@ -@import "variables"; - -.footer { - position: absolute; - display: flex; - bottom: 0; - padding: 2rem 2.4rem; - flex-grow: 0; - width: 100%; - - @media (max-width: $phone-max-width) { - justify-content: center; - } - - &__inner { - display: flex; - align-items: center; - justify-content: space-between; - margin: 0; - max-width: 100%; - } - - a { - color: inherit; - } - - .copyright { - display: flex; - flex-direction: row; - align-items: center; - font-size: 1.1em; - color: var(--light-color-secondary); - - &--user { - margin: auto; - text-align: center; - } - - &>a { - opacity: 0.3; - transition: opacity 0.2s ease-in-out; - text-decoration: none; - - &:hover { - opacity: 1; - } - - margin-right: 10px; - } - } - - - - .copyright-theme { - @media (max-width: $tablet-max-width) { - font-size: 0.75rem; - } - } -} \ No newline at end of file diff --git a/sass/header.scss b/sass/header.scss deleted file mode 100644 index 0f852d1..0000000 --- a/sass/header.scss +++ /dev/null @@ -1,59 +0,0 @@ -@import "variables"; - -@mixin menu { - position: absolute; - background: var(--background); - box-shadow: var(--shadow); - color: white; - border: 2px solid; - margin: 0; - padding: 10px; - list-style: none; - z-index: 99; -} - -.header { - height: 6rem; - margin: 0 3rem; - - @media (max-width: $phone-max-width) { - padding: 1.5rem; - margin: 0; - } - - ul { - display: flex; - - padding: 40px 0; - margin: 0; - list-style: none; - display: flex; - gap: 1rem; - - @media (max-width: $phone-max-width) { - padding: 0; - } - - @media (min-width: 80rem) { - position: fixed; - flex-direction: column; - gap: 0.2rem; - } - - li { - color: var(--color); - font-size: 1.8rem; - opacity: 0.3; - transition: opacity 0.2s ease-in-out; - - a { - text-decoration: none; - } - - &:hover, - &.active { - opacity: 0.6; - } - } - } -} diff --git a/sass/logo.scss b/sass/logo.scss deleted file mode 100644 index c02bf36..0000000 --- a/sass/logo.scss +++ /dev/null @@ -1,10 +0,0 @@ -.logo { - color: var(--color); - display: flex; - align-items: center; - text-decoration: none; - font-size: 3rem; - margin: 0; - opacity: .3; - padding: .8rem 1.4rem; -} \ No newline at end of file diff --git a/sass/main.scss b/sass/main.scss deleted file mode 100644 index f9b29ae..0000000 --- a/sass/main.scss +++ /dev/null @@ -1,396 +0,0 @@ -@import "variables"; - -html { - box-sizing: border-box; -} - -*, -*:before, -*:after { - box-sizing: inherit; -} - -body { - position: relative; - margin: 0; - padding: 0; - font-family: var(--font-family-text); - font-size: 1.2rem; - line-height: 1.54; - background-color: var(--background); - - background-image: linear-gradient(#0b0b0bae 1px, transparent 1px), - linear-gradient(to right, #0b0b0bae 1px, #000000 1px); - background-size: 20px 20px; - - color: var(--color); - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -webkit-overflow-scrolling: touch; - -webkit-text-size-adjust: 100%; - - @media (max-width: $phone-max-width) { - font-size: 1rem; - } -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: bold; - line-height: 1.3; - margin-bottom: 0.5rem; -} - -h1 { - font-size: 1.7em; - font-weight: 600; - margin-top: 1.5rem; - margin-bottom: 0.4rem; - color: var(--color); - // background: linear-gradient(93deg, #00ffe8 20%, #effbff 70%); - // -webkit-background-clip: text; - // background-clip: text; - // -webkit-text-fill-color: transparent; - // -webkit-box-decoration-break: clone; - // box-decoration-break: clone; -} - -h2 { - font-size: 1.2em; - font-size: 1.5rem; - font-weight: 400; -} - -h3 { - font-size: 1.3em; -} - -h4, -h5, -h6 { - font-size: 1em; -} - -a { - color: inherit; -} - -img, -svg { - display: block; - max-width: 100%; - height: auto; - - &.left { - margin-right: auto; - } - - &.center { - margin-left: auto; - margin-right: auto; - } - - &.right { - margin-left: auto; - } -} - -p { - margin-bottom: 20px; - font-family: var(--font-family-text); - letter-spacing: -0.003em; - line-height: 1.5; - margin-top: 0; - margin-bottom: 1rem; - font-size: 1.2em; - word-break: break-word; - font-weight: 400; - color: rgba(241, 236, 236, 0.86); - - a { - color: var(--color); - } - - > code { - display: inline-block; - } -} - -li > p { - margin-bottom: 0; -} - -figure { - display: table; - max-width: 100%; - margin: 25px 0; - width: 100%; - - &.left { - img, - svg { - margin-right: auto; - } - } - - &.center { - img, - svg { - margin-left: auto; - margin-right: auto; - } - } - - &.right { - img, - svg { - margin-left: auto; - } - } - - &.invert { - img, - svg { - filter: invert(1); - } - } - - &.mixblend { - img, - svg { - mix-blend-mode: exclusion; - } - } - - figcaption { - font-size: 14px; - padding: 5px 10px; - margin-top: 5px; - color: white; - - &.left { - text-align: left; - } - - &.center { - text-align: center; - } - - &.right { - text-align: right; - } - } -} - -code { - font-family: var(--font-family-mono); - font-feature-settings: normal; - background: var(--accent-alpha-20); - padding: 1px 4px 1.5px 4px; - font-size: 0.95rem; - line-height: 1.5; - border-radius: 3px; -} - -// code is the only element that can be a child of a

    -code.filename { - font-size: 0.85rem; - padding: 0.3rem 0.4rem; - border-radius: 0.4rem; - border-top-right-radius: 0; - border-bottom-left-radius: 0; - position: absolute; - line-height: 1.2; - - @media (max-width: $phone-max-width) { - border-top-left-radius: 0; - margin-left: -1.5rem; - } -} - -pre { - font-family: var(--font-family-mono); - padding: 0.8rem 0.7rem; - font-size: 0.95rem; - overflow: auto; - margin-bottom: 2rem; - margin-top: 0; - border-radius: 6px; - white-space: pre-wrap; - line-height: 1.5; - // @media (max-width: $phone-max-width) { - // white-space: pre-wrap; - // word-wrap: break-word; - // } - - @media (max-width: $phone-max-width) { - margin-left: -1.5rem; - margin-right: -1.5rem; - padding-left: 0.5rem; - line-height: 1.1; - border-radius: 0; - code { - padding-right: 2rem; - font-size: 0.85rem; - } - } - - code { - padding: 0; - margin: 0; - background: none; - white-space: pre; - line-height: 1; - } -} - -blockquote { - margin: 0; - margin-top: 2rem; - margin-bottom: 2rem; - padding: 0.5rem 1.5rem; - font-style: italic; - position: relative; - font-family: var(--font-family-text); - border-top-right-radius: 0.4rem; - border-bottom-right-radius: 0.4rem; - - @media (max-width: $phone-max-width) { - padding-right: 0; - } - - background: transparent; - border-left: 4px solid var(--accent); - - &.twitter { - // background: linear-gradient(45deg, #106da7, #053b5d); - border-left: 4px solid #106da7; - color: #fff; - } - - &.hackernews { - // background: linear-gradient(254deg, #8a3701, #c06506e8); - border-left: 4px solid #c06506e8; - color: #fff; - } - - &.info { - // padding: 1rem 1.2rem; - border-left: 4px solid var(--accent); - background: #282a36; - } - - // &.info { - // &:before { - // content: ""; - // } - - // &:after { - // content: ""; - // } - // } - - p:first-of-type { - margin-top: 0; - } - - p:last-of-type { - margin-bottom: 0; - } - - p { - position: relative; - } - - // &:before { - // content: "“"; - // display: block; - // position: absolute; - // left: 0px; - // top: -16px; - // font-size: 3.875rem; - // } - - // &:after { - // content: "”"; - // display: block; - // position: absolute; - // font-size: 3.875rem; - // right: 10px; - // bottom: -28px; - // line-height: 1; - // } -} - -table { - table-layout: auto; - border-collapse: collapse; - width: 100%; - margin: 40px 0; -} - -table, -th, -td { - border: 1px dashed var(--accent); - padding: 10px; -} - -td > * { - margin-bottom: 0; -} - -th { - color: var(--accent); -} - -ul, -ol { - margin-left: 20px; - padding: 0; - - li { - position: relative; - } -} - -ol ol { - list-style-type: lower-alpha; -} - -.container { - position: relative; - display: flex; - justify-content: center; - flex-direction: column; - padding: 0.5rem 3rem; - padding-top: 0rem; - padding-bottom: 5rem; - max-width: 60rem; - min-height: calc(100vh - 6rem); - margin: 0 auto; - - @media (max-width: $phone-max-width) { - padding: 1.5rem; - padding-top: 0rem; - } -} - -.content { - display: flex; -} - -hr { - width: 100%; - border: none; - background: var(--border-color); - height: 1px; -} - -.hidden { - display: none; -} diff --git a/sass/pagination.scss b/sass/pagination.scss deleted file mode 100644 index e8961e9..0000000 --- a/sass/pagination.scss +++ /dev/null @@ -1,77 +0,0 @@ -@import 'variables'; - -.pagination { - margin-top: 50px; - - &__title { - display: flex; - text-align: center; - position: relative; - margin: 100px 0 20px; - - &-h { - text-align: center; - margin: 0 auto; - padding: 5px 10px; - background: var(--background); - font-size: .8rem; - text-transform: uppercase; - letter-spacing: .1em; - z-index: 1; - } - - hr { - position: absolute; - left: 0; - right: 0; - width: 100%; - margin-top: 15px; - z-index: 0; - } - } - - &__buttons { - display: flex; - align-items: center; - justify-content: center; - - @media (max-width: $phone-max-width) { - flex-direction: column; - } - } -} - -.button { - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 1rem; - border-radius: 8px; - max-width: 40%; - padding: 0; - cursor: pointer; - appearance: none; - - @media (max-width: $phone-max-width) { - max-width: 80%; - } - - +.button { - margin-left: 10px; - } - - a { - display: flex; - padding: 8px 16px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - } - - &__text { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - } -} \ No newline at end of file diff --git a/sass/post.scss b/sass/post.scss deleted file mode 100644 index 6b4eee1..0000000 --- a/sass/post.scss +++ /dev/null @@ -1,206 +0,0 @@ -@import "variables"; - -.post-list > div.year { - user-select: none; - - h2 { - line-height: 1; - margin-left: -0.3rem; - padding-bottom: 0.5rem; - font-size: 2rem; - margin: 0; - line-height: 0.8; - font-weight: 600; - } - - &:not(:first-of-type) h2 { - margin-top: 2rem; - } -} - -.posts { - width: 100%; - margin: 0 auto; -} - -.toc__title { - cursor: pointer; - font-weight: 800; - font-size: 1.5rem; - margin-bottom: 0.5rem; - user-select: none; -} - -ul.toc { - margin-top: 0; - margin-bottom: 0; -} - -.post { - width: 100%; - text-align: left; - padding: 20px 0; - margin-bottom: 2rem; - - // @media (max-width: $tablet-max-width) { - // max-width: 660px; - // } - - &:not(:last-of-type) { - border-bottom: 1px solid var(--border-color); - } - - %meta { - font-size: 1.2em; - margin-bottom: 10px; - color: var(--color); - opacity: 0.8; - } - - &-meta { - @extend %meta; - } - - &-meta-small { - opacity: 0.7; - } - - &-meta-inline { - @extend %meta; - - display: inline; - } - - &-title { - position: relative; - margin: 0; - margin-bottom: 0.5rem; - font-weight: 600; - - a { - text-decoration: none; - } - } - - %tags { - margin-bottom: 20px; - font-size: 1rem; - } - - &-tags { - @extend %tags; - - display: block; - } - - &-tags-inline { - @extend %tags; - display: block; - - > a { - opacity: 0.7; - &:hover { - opacity: 1; - } - } - - @media (max-width: $phone-max-width) { - display: block; - } - } - - &-content { - margin-top: 1rem; - } - - &-cover { - border: 20px solid var(--accent); - background: transparent; - margin: 40px 0; - padding: 20px; - - @media (max-width: $phone-max-width) { - padding: 10px; - border-width: 10px; - } - } - - ul { - list-style: none; - - > li > ul { - margin-bottom: 0.5rem; - } - } - - ol { - padding-top: 0.5rem; - padding-left: 0.5rem; - - > li { - margin-bottom: 0.8rem; - } - - > li > ol { - margin-bottom: 0.5rem; - } - } -} - -.post-date { - font-variant: small-caps; -} - -.post-list { - margin: 0; - - > li { - position: relative; - margin-bottom: 0.7rem; - transition: all 0.2s ease-in-out; - - &:hover { - .post-date { - opacity: 0.9; - } - - .post-list-title { - opacity: 1; - } - - .post-list-description { - opacity: 1; - } - } - } - - .post-date { - color: #cecece; - opacity: 0.7; - text-decoration: none; - margin-left: 0.5rem; - font-weight: 600; - font-size: 1rem; - } - - a { - text-decoration: none; - } - - .post-list-title { - text-decoration: underline; - text-decoration-color: gray; - opacity: 0.9; - } - - .post-list-description { - margin-top: 0.2rem; - margin-bottom: 1.4rem; - opacity: 0.6; - } - - .post-tag { - text-decoration: none; - opacity: 0.4; - } -} diff --git a/sass/style.scss b/sass/style.scss deleted file mode 100644 index 39ddefe..0000000 --- a/sass/style.scss +++ /dev/null @@ -1,25 +0,0 @@ -@import "fonts/libertinus.scss"; -@import "fonts/jetbrainsmono.scss"; -@import "fonts/shantell.scss"; - -:root { - --accent: #fcfcfc; - --accent-alpha-70: #fcfcfcab; - --accent-alpha-20: #fcfcfc36; - - --background: #080809; - --color: white; - --border-color: rgba(255, 255, 255, 0.1); - - --font-family-mono: "JetBrains Mono", monospace; - --font-family-text: "Libertinus Serif", Georgia, Cambria, "Times New Roman", - Times, serif; -} - -@import "buttons"; -@import "header"; -@import "logo"; -@import "main"; -@import "post"; -@import "pagination"; -@import "footer"; diff --git a/sass/variables.scss b/sass/variables.scss deleted file mode 100644 index 3b95a9c..0000000 --- a/sass/variables.scss +++ /dev/null @@ -1,2 +0,0 @@ -$phone-max-width: 683px; -$tablet-max-width: 899px; diff --git a/search_index.en.js b/search_index.en.js new file mode 100644 index 0000000..84e41f8 --- /dev/null +++ b/search_index.en.js @@ -0,0 +1 @@ +window.searchIndex = {"fields":["title","body"],"pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5","index":{"body":{"root":{"docs":{},"df":0}},"title":{"root":{"docs":{},"df":0}}},"documentStore":{"save":true,"docs":{"https://thoughts.henrygressmann.de/":{"body":"","id":"https://thoughts.henrygressmann.de/","title":""},"https://thoughts.henrygressmann.de/pages/":{"body":"","id":"https://thoughts.henrygressmann.de/pages/","title":""}},"docInfo":{"https://thoughts.henrygressmann.de/":{"body":0,"title":0},"https://thoughts.henrygressmann.de/pages/":{"body":0,"title":0}},"length":2},"lang":"English"}; \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..9ac0784 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,9 @@ + + + + https://thoughts.henrygressmann.de/ + + + https://thoughts.henrygressmann.de/tags/ + + diff --git a/static/CNAME b/static/CNAME deleted file mode 100644 index 7ce98a7..0000000 --- a/static/CNAME +++ /dev/null @@ -1 +0,0 @@ -blog.henrygressmann.de diff --git a/static/favicon.ico b/static/favicon.ico deleted file mode 100644 index 69aae20..0000000 Binary files a/static/favicon.ico and /dev/null differ diff --git a/static/font/libertinus/LibertinusKeyboard-Regular.woff2 b/static/font/libertinus/LibertinusKeyboard-Regular.woff2 deleted file mode 100644 index 2db0a64..0000000 Binary files a/static/font/libertinus/LibertinusKeyboard-Regular.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusMath-Regular.woff2 b/static/font/libertinus/LibertinusMath-Regular.woff2 deleted file mode 100644 index 98dc5e0..0000000 Binary files a/static/font/libertinus/LibertinusMath-Regular.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusMono-Regular.woff2 b/static/font/libertinus/LibertinusMono-Regular.woff2 deleted file mode 100644 index fb62fca..0000000 Binary files a/static/font/libertinus/LibertinusMono-Regular.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSans-Bold.woff2 b/static/font/libertinus/LibertinusSans-Bold.woff2 deleted file mode 100644 index a898d25..0000000 Binary files a/static/font/libertinus/LibertinusSans-Bold.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSans-Italic.woff2 b/static/font/libertinus/LibertinusSans-Italic.woff2 deleted file mode 100644 index 3c4c693..0000000 Binary files a/static/font/libertinus/LibertinusSans-Italic.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSans-Regular.woff2 b/static/font/libertinus/LibertinusSans-Regular.woff2 deleted file mode 100644 index 8f0f029..0000000 Binary files a/static/font/libertinus/LibertinusSans-Regular.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSerif-Bold.woff2 b/static/font/libertinus/LibertinusSerif-Bold.woff2 deleted file mode 100644 index 2604c20..0000000 Binary files a/static/font/libertinus/LibertinusSerif-Bold.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSerif-BoldItalic.woff2 b/static/font/libertinus/LibertinusSerif-BoldItalic.woff2 deleted file mode 100644 index c1d3632..0000000 Binary files a/static/font/libertinus/LibertinusSerif-BoldItalic.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSerif-Italic.woff2 b/static/font/libertinus/LibertinusSerif-Italic.woff2 deleted file mode 100644 index f9331a7..0000000 Binary files a/static/font/libertinus/LibertinusSerif-Italic.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSerif-Regular.woff2 b/static/font/libertinus/LibertinusSerif-Regular.woff2 deleted file mode 100644 index 6193866..0000000 Binary files a/static/font/libertinus/LibertinusSerif-Regular.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSerif-Semibold.woff2 b/static/font/libertinus/LibertinusSerif-Semibold.woff2 deleted file mode 100644 index 268c66e..0000000 Binary files a/static/font/libertinus/LibertinusSerif-Semibold.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSerif-SemiboldItalic.woff2 b/static/font/libertinus/LibertinusSerif-SemiboldItalic.woff2 deleted file mode 100644 index a33601d..0000000 Binary files a/static/font/libertinus/LibertinusSerif-SemiboldItalic.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSerifDisplay-Regular.woff2 b/static/font/libertinus/LibertinusSerifDisplay-Regular.woff2 deleted file mode 100644 index c10593a..0000000 Binary files a/static/font/libertinus/LibertinusSerifDisplay-Regular.woff2 and /dev/null differ diff --git a/static/font/libertinus/LibertinusSerifInitials-Regular.woff2 b/static/font/libertinus/LibertinusSerifInitials-Regular.woff2 deleted file mode 100644 index 123738c..0000000 Binary files a/static/font/libertinus/LibertinusSerifInitials-Regular.woff2 and /dev/null differ diff --git a/static/font/mono/JetBrainsMono-Italic.woff2 b/static/font/mono/JetBrainsMono-Italic.woff2 deleted file mode 100644 index 98adc80..0000000 Binary files a/static/font/mono/JetBrainsMono-Italic.woff2 and /dev/null differ diff --git a/static/font/mono/JetBrainsMono.woff2 b/static/font/mono/JetBrainsMono.woff2 deleted file mode 100644 index 4cd1413..0000000 Binary files a/static/font/mono/JetBrainsMono.woff2 and /dev/null differ diff --git a/static/font/shantell/ShantellSans.woff2 b/static/font/shantell/ShantellSans.woff2 deleted file mode 100644 index 182df0b..0000000 Binary files a/static/font/shantell/ShantellSans.woff2 and /dev/null differ diff --git a/static/img/copyright-dalle2.png b/static/img/copyright-dalle2.png deleted file mode 100644 index c3627db..0000000 Binary files a/static/img/copyright-dalle2.png and /dev/null differ diff --git a/static/index.js b/static/index.js deleted file mode 100644 index 035835c..0000000 --- a/static/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// https://github.com/explodingcamera/esm/blob/main/packages/spaify -var l=new Map;l.set(window.location.href,document.cloneNode(!0));async function y(t,e){if(l.has(t))return {doc:l.get(t),firstLoad:!1};let n=await fetch(t,{signal:e});if(!n.ok)throw n.statusText;let i=new DOMParser().parseFromString(await n.text(),"text/html");return l.set(t,i),{doc:i,firstLoad:!0}}var g=(t={})=>{if(window.__spaify)throw new Error("Spaify's already initialized");window.__spaify=!0;let e=t.attribute||"data-spaify",n={attribute:e,selectors:{once:`[${e}-run=once]`,always:`[${e}-run=always]`,ignore:`a[${e}-ignore]`,main:`body *[${e}-main]`,...t.selectors}},i=o=>{let r=o.target.closest("a");!r||r.tagName!=="A"||window.location.origin!==r.origin||r.matches(n.selectors.ignore)||(o.preventDefault(),f(r.href),history.pushState({url:r.href},"",r.href));},c=new AbortController,f=async o=>{let r=()=>window.location.assign(o);c.signal&&c.abort(),c=new AbortController;let d;try{d=await y(o,c.signal);}catch(a){return a==="abort"&&r()}let s=d.doc.cloneNode(!0),h=s.querySelectorAll(`head script${n.selectors.always},body>script${n.selectors.always},${n.selectors.main} script:not(${n.selectors.once})`),p=s.querySelector(n.selectors.main),w=document.querySelector(n.selectors.main);if(!w||!p)return r();w.replaceWith(p),h.forEach(a=>m(a)),d.firstLoad&&s.querySelectorAll(n.selectors.once).forEach(a=>m(a)),s.title&&(document.title=s.title),window.scrollTo(0,0);},u=o=>f(window.location.href);return document.addEventListener("click",i),window.addEventListener("popstate",u),{unload:()=>{document.removeEventListener("click",i),window.removeEventListener("popstate",u);}}},m=t=>{if(!(t instanceof HTMLScriptElement))return;let e=document.createElement("script");e.innerHTML=t.innerHTML,t.innerHTML!==""&&(e.innerHTML=t.innerHTML),t.integrity!==""&&(e.integrity=t.integrity),t.referrerPolicy!==""&&(e.referrerPolicy=t.referrerPolicy),t.crossOrigin!==""&&(e.crossOrigin=t.crossOrigin),t.noModule!==!1&&(e.noModule=t.noModule),t.type!==""&&(e.type=t.type),t.src!==""&&(e.src=t.src),e.setAttribute("data-spaify","true"),document.head.appendChild(e);};g(); diff --git a/style.css b/style.css new file mode 100644 index 0000000..99005d4 --- /dev/null +++ b/style.css @@ -0,0 +1,2 @@ +:root{--accent: #2afff1;--accent-alpha-70: #2afff1ab;--accent-alpha-20: #2afff136;--background: #111318;--color: white;--border-color: rgba(255, 255, 255, 0.1);--font-family-mono: "Space Mono", monospace;--font-family-text: "Source Sans Pro", Georgia, Cambria, "Times New Roman", + Times, serif}.button-container{display:table;margin-left:auto;margin-right:auto}button,.button,a.button{position:relative;display:flex;align-items:center;justify-content:center;padding:8px 18px;margin-bottom:5px;text-align:center;border-radius:8px;border:1px solid transparent;appearance:none;cursor:pointer;outline:none}button.outline,.button.outline,a.button.outline{background:transparent;box-shadow:none;padding:8px 18px}button.outline :hover,.button.outline :hover,a.button.outline :hover{transform:none;box-shadow:none}button.primary,.button.primary,a.button.primary{box-shadow:0 4px 6px rgba(50,50,93,0.11),0 1px 3px rgba(0,0,0,0.08)}button.primary:hover,.button.primary:hover,a.button.primary:hover{box-shadow:0 2px 6px rgba(50,50,93,0.21),0 1px 3px rgba(0,0,0,0.08)}button.link,.button.link,a.button.link{background:none;font-size:1rem}button.small,.button.small,a.button.small{font-size:.8rem}button.wide,.button.wide,a.button.wide{min-width:200px;padding:14px 24px}a.read-more,a.read-more:hover,a.read-more:active{display:inline-flex;background:none;box-shadow:none;padding:0;margin:20px 0;max-width:100%}.code-toolbar{margin-bottom:20px}.code-toolbar .toolbar-item a{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px 8px;margin-bottom:5px;text-align:center;font-size:13px;font-weight:500;border-radius:8px;border:1px solid transparent;appearance:none;cursor:pointer;outline:none}.header{display:flex;flex-direction:column;position:relative}.header__inner{display:flex;align-items:center;justify-content:space-between}.header__logo{display:flex;flex:1;margin:0 20px;display:flex}.header__logo a{flex:0 0 auto;max-width:100%}.header .menu{margin:20px 0}.header .menu__inner{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.header .menu__inner li.active{color:var(--accent-alpha-70)}.header .menu__inner li:not(:last-of-type){margin-right:20px;margin-bottom:10px;flex:0 0 auto}.header .menu__sub-inner{position:relative;list-style:none;padding:0;margin:0}.header .menu__sub-inner:not(:only-child){margin-left:20px}.header .menu__sub-inner-more{position:absolute;background:var(--background);box-shadow:var(--shadow);color:white;border:2px solid;margin:0;padding:10px;list-style:none;z-index:99;top:35px;left:0}.header .menu__sub-inner-more-trigger{color:var(--accent);user-select:none;cursor:pointer}.header .menu__sub-inner-more li{margin:0;padding:5px;white-space:nowrap}.logo{color:var(--accent);display:flex;align-items:center;text-decoration:none}html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}body{margin:0;padding:0;font-family:var(--font-family-mono);font-size:1rem;line-height:1.54;background-color:var(--background);color:var(--color);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%}@media (max-width: 683px){body{font-size:1rem}}h1,h2,h3,h4,h5,h6{display:flex;align-items:center;font-weight:bold;line-height:1.3}h1{font-size:1.4rem}h2{font-size:1.3rem}h3{font-size:1.2rem}h4,h5,h6{font-size:1.15rem}a{color:inherit}img{display:block;max-width:100%}img.left{margin-right:auto}img.center{margin-left:auto;margin-right:auto}img.right{margin-left:auto}p{margin-bottom:20px;font-family:var(--font-family-text);letter-spacing:-0.003em;line-height:32px;margin-top:2em;font-size:20px;margin-bottom:-0.46em;word-break:break-word;font-weight:400}figure{display:table;max-width:100%;margin:25px 0}figure.left img{margin-right:auto}figure.center img{margin-left:auto;margin-right:auto}figure.right img{margin-left:auto}figure figcaption{font-size:14px;padding:5px 10px;margin-top:5px;background:var(--accent);color:var(--background)}figure figcaption.left{text-align:left}figure figcaption.center{text-align:center}figure figcaption.right{text-align:right}code{font-family:Hack, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;font-feature-settings:normal;background:var(--accent-alpha-20);padding:1px 6px;margin:0 2px;font-size:0.95rem}pre{font-family:Hack, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;padding:20px;font-size:0.95rem;overflow:auto;border-top:1px solid rgba(255,255,255,0.1);border-bottom:1px solid rgba(255,255,255,0.1)}@media (max-width: 683px){pre{white-space:pre-wrap;word-wrap:break-word}}pre code{padding:0;margin:0;background:none}blockquote{border-top:1px solid var(--accent);border-bottom:1px solid var(--accent);margin:40px 0;padding:25px}@media (max-width: 683px){blockquote{padding-right:0}}blockquote:before{content:"”";font-family:Georgia, serif;font-size:3.875rem;position:absolute;left:-40px;top:-20px}blockquote p:first-of-type{margin-top:0}blockquote p:last-of-type{margin-bottom:0}blockquote p{position:relative}blockquote p:before{content:">";display:block;position:absolute;left:-25px;color:var(--accent)}table{table-layout:fixed;border-collapse:collapse;width:100%;margin:40px 0}table,th,td{border:1px dashed var(--accent);padding:10px}th{color:var(--accent)}ul,ol{margin-left:20px;padding:0}ul li,ol li{position:relative}@media (max-width: 683px){ul,ol{margin-left:20px}}ol ol{list-style-type:lower-alpha}.container{display:flex;flex-direction:column;padding:40px;max-width:864px;min-height:100vh;margin:0 auto}@media (max-width: 683px){.container{padding:20px}}.content{display:flex}hr{width:100%;border:none;background:var(--border-color);height:1px}.hidden{display:none}.posts{width:100%;margin:0 auto}.post{width:100%;text-align:left;margin:20px 20px;padding:20px 0}@media (max-width: 899px){.post{max-width:660px}}.post:not(:last-of-type){border-bottom:1px solid var(--border-color)}.post .post-meta-inline,.post .post-meta{font-size:1rem;margin-bottom:10px;color:var(--accent-alpha-70)}.post-meta-inline{display:inline}.post-title{position:relative;margin:0;font-weight:normal}.post-title a{text-decoration:none}.post .post-tags-inline,.post .post-tags{margin-bottom:20px;font-size:1rem;opacity:0.5}.post-tags{display:block}.post-tags-inline{display:inline}@media (max-width: 683px){.post-tags-inline{display:block}}.post-content{margin-top:30px}.post-cover{border:20px solid var(--accent);background:transparent;margin:40px 0;padding:20px}@media (max-width: 683px){.post-cover{padding:10px;border-width:10px}}.post ul{list-style:none}.post ul li:before{content:"*";position:absolute;left:-20px;top:2px;color:var(--accent)}.post--regulation h1{justify-content:center}.post--regulation h2{justify-content:center;margin-bottom:10px}.post--regulation h2+h2{margin-top:-10px;margin-bottom:20px}.post-list .post-date{color:var(--accent-alpha-70);text-decoration:none}.post-list a{text-decoration:none}.post-list .post-list-title{text-decoration:underline}.post-list .post-tag{text-decoration:none}.pagination{margin-top:50px}.pagination__title{display:flex;text-align:center;position:relative;margin:100px 0 20px}.pagination__title-h{text-align:center;margin:0 auto;padding:5px 10px;background:var(--background);font-size:.8rem;text-transform:uppercase;letter-spacing:.1em;z-index:1}.pagination__title hr{position:absolute;left:0;right:0;width:100%;margin-top:15px;z-index:0}.pagination__buttons{display:flex;align-items:center;justify-content:center}@media (max-width: 683px){.pagination__buttons{flex-direction:column}}.button{position:relative;display:inline-flex;align-items:center;justify-content:center;font-size:1rem;border-radius:8px;max-width:40%;padding:0;cursor:pointer;appearance:none}@media (max-width: 683px){.button{max-width:80%}}.button+.button{margin-left:10px}.button a{display:flex;padding:8px 16px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.button__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.footer{padding:40px 0;flex-grow:0;opacity:0.5;margin-top:auto}.footer__inner{display:flex;align-items:center;justify-content:space-between;margin:0;width:760px;max-width:100%}@media (max-width: 899px){.footer__inner{flex-direction:column}}.footer a{color:inherit}.footer .copyright{display:flex;flex-direction:row;align-items:center;font-size:1rem;color:var(--light-color-secondary)}.footer .copyright--user{margin:auto;text-align:center}.footer .copyright>a{margin-right:10px}@media (max-width: 899px){.footer .copyright>a{margin:0}}@media (max-width: 899px){.footer .copyright{flex-direction:column;margin-top:10px}}@media (max-width: 899px){.footer .copyright-theme-sep{display:none}}@media (max-width: 899px){.footer .copyright-theme{font-size:0.75rem}} diff --git a/syntaxes/riscv.sublime-syntax b/syntaxes/riscv.sublime-syntax deleted file mode 100644 index 5189f22..0000000 --- a/syntaxes/riscv.sublime-syntax +++ /dev/null @@ -1,118 +0,0 @@ -%YAML 1.2 ---- -name: RISC-V Assembly -file_extensions: [riscv] -scope: source.rvasm -contexts: - main: - # Comments - - match: "(//|#)" - scope: punctuation.definition.comment.rvasm - push: - - meta_scope: comment.line.rvasm - - match: $ - pop: true - - match: /\* - scope: punctuation.definition.comment.rvasm - push: - - meta_scope: comment.block.rvasm - - match: \*/ - scope: punctuation.definition.comment.rvasm - pop: true - - match: ^\s*(\*)(?!/) - captures: - 1: punctuation.definition.comment.rvasm - - - include: strings - - # Symbols - - match: "^<(.*?)>:" - captures: - 1: entity.name.function.rvasm - - match: '^([^0-9\s][^\s]*?):' - captures: - 1: entity.name.function.rvasm - - # Options - - match: \.(option|section|globl)\s+([\w\.]+) - captures: - 1: keyword.operator.rvasm - 2: variable.language.rvasm - - match: \.(option|section|globl|space) - scope: keyword.operator.rvasm - - # Offsets - - match: <([^>+-]+)([+-]((0x)?[0-9a-fA-F]+))?> - captures: - 1: support.function.rvasm - 3: constant.numeric.rvasm - - # CSRs - - match: \b(mhartid|misa)\b - scope: support.type.rvasm - - - include: operators - - # Instructions - - match: \b(add|addi|addiw|addw|amoadd\.d|amoadd\.w|amoand\.d|amoand\.w|amomax\.d|amomax\.w|amomaxu\.d|amomaxu\.w|amomin\.d|amomin\.w|amominu\.d|amominu\.w|amoor\.d|amoor\.w|amoswap\.d|amoswap\.w|amoxor\.d|amoxor\.w|and|andi|andn|auipc|bdep|beq|bext|bfp|bge|bgeu|bgez|beqz|blt|bltu|bne|bnez|ret|call|snez|j|c\.add|c\.addi|c\.addi16sp|c\.addi4spn|c\.addiw|c\.addw|c\.and|c\.andi|c\.beqz|c\.bnez|c\.ebreak|c\.fld|c\.fldsp|c\.flw|c\.flwsp|c\.fsd|c\.fsdsp|c\.fsw|c\.fswsp|c\.j|c\.jal|c\.jalr|c\.jr|c\.ld|c\.ldsp|c\.li|c\.lui|c\.lw|c\.lwsp|c\.mv|c\.nop|c\.or|c\.sd|c\.sdsp|c\.slli|c\.srai|c\.srli|c\.sub|c\.subw|c\.sw|c\.swsp|c\.xor|clmul|clmulh|clmulr|clz|cmix|cmov|crc32\.b|crc32\.h|crc32\.w|crc32c\.b|crc32c\.h|crc32c\.w|csrrc|csrrci|csrrs|csrrsi|csrrw|csrr|csrw|csrrwi|ctz|div|divu|divuw|divw|dret|ebreak|ecall|fadd\.d|fadd\.q|fadd\.s|fclass\.d|fclass\.q|fclass\.s|fcvt\.d\.l|fcvt\.d\.lu|fcvt\.d\.q|fcvt\.d\.s|fcvt\.d\.w|fcvt\.d\.wu|fcvt\.l\.d|fcvt\.l\.q|fcvt\.l\.s|fcvt\.lu\.d|fcvt\.lu\.q|fcvt\.lu\.s|fcvt\.q\.d|fcvt\.q\.l|fcvt\.q\.lu|fcvt\.q\.s|fcvt\.q\.w|fcvt\.q\.wu|fcvt\.s\.d|fcvt\.s\.l|fcvt\.s\.lu|fcvt\.s\.q|fcvt\.s\.w|fcvt\.s\.wu|fcvt\.w\.d|fcvt\.w\.q|fcvt\.w\.s|fcvt\.wu\.d|fcvt\.wu\.q|fcvt\.wu\.s|fdiv\.d|fdiv\.q|fdiv\.s|fence|fence\.i|fence\.tso|feq\.d|feq\.q|feq\.s|fld|fle\.d|fle\.q|fle\.s|flq|flt\.d|flt\.q|flt\.s|flw|fmadd\.d|fmadd\.q|fmadd\.s|fmax\.d|fmax\.q|fmax\.s|fmin\.d|fmin\.q|fmin\.s|fmsub\.d|fmsub\.q|fmsub\.s|fmul\.d|fmul\.q|fmul\.s|fmv\.d\.x|fmv\.q\.x|fmv\.s\.x|fmv\.w\.x|fmv\.x\.d|fmv\.x\.q|fmv\.x\.s|fmv\.x\.w|fnmadd\.d|fnmadd\.q|fnmadd\.s|fnmsub\.d|fnmsub\.q|fnmsub\.s|frcsr|frflags|frrm|fscsr|fsd|fsflags|fsflagsi|fsgnj\.d|fsgnj\.q|fsgnj\.s|fsgnjn\.d|fsgnjn\.q|fsgnjn\.s|fsgnjx\.d|fsgnjx\.q|fsgnjx\.s|fsl|fsq|fsqrt\.d|fsqrt\.q|fsqrt\.s|fsr|fsri|fsrm|fsrmi|fsub\.d|fsub\.q|fsub\.s|fsw|gorc|gorci|grev|grevi|hfence\.bvma|hfence\.gvma|jal|jalr|li|la|neg|lb|lbu|ld|lh|lhu|lr\.d|lr\.w|lui|lw|lwu|max|maxu|min|minu|mret|mul|mulh|mulhsu|mulhu|mulw|mv|or|ori|orn|pack|packh|packu|pcnt|rdcycle|rdcycleh|rdinstret|rdinstreth|rdtime|rdtimeh|rem|remu|remuw|remw|rol|ror|rori|sb|sbclr|sbclri|sbext|sbexti|sbinv|sbinvi|sbreak|sbset|sbseti|sc\.d|sc\.w|scall|sd|sext\.b|sext\.h|sfence\.vma|sh|sh1add|sh2add|sh3add|shfl|shfli|sll|slli|slli\.rv32|slliw|sllw|slo|sloi|slt|slti|sltiu|sltu|sra|srai|srai\.rv32|sraiw|sraw|sret|srl|srli|srli\.rv32|srliw|srlw|sro|sroi|sub|subw|sw|unshfl|unshfli|uret|wfi|xnor|xor|xori)\b - scope: keyword.mnemonic.rvasm - - # Registers - - match: \b(ra|[sgtf]p)\b - scope: support.type.rvasm - - match: \b([xf][0-9]|[xf][012][0-9]|[xf]3[012]|ra|[sgtf]p|t[0-6]|f?s[0-9]|f?s1[01]|ft[0-7])\b - scope: storage.type.rvasm - - match: \b(f?a[0-7])\b - scope: keyword.operator.rvasm - - match: \bzero\b - scope: support.type.rvasm - - # Numbers - - match: \b-?(0x)?[0-9a-fA-F]+\b - scope: constant.numeric.rvasm - - strings: - - match: '"' - scope: punctuation.definition.string.begin.rvasm - push: - - meta_scope: string.quoted.double.rvasm - - match: '"' - scope: punctuation.definition.string.end.rvasm - pop: true - - include: string_escaped_char - - include: string_placeholder - - match: "'" - scope: punctuation.definition.string.begin.rvasm - push: - - meta_scope: string.quoted.single.rvasm - - match: "'" - scope: punctuation.definition.string.end.rvasm - pop: true - - include: string_escaped_char - - string_escaped_char: - - match: '(\\)$\n' - captures: - 1: punctuation.separator.continuation.rvasm - - match: \\(?:\\|[abefnrtv\'"?]|[0-3][0-9]{0,2}|[4-7][0-9]?|x[a-fA-F0-9]+|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8}) - scope: constant.character.escape.rvasm - - match: \\. - scope: invalid.illegal.unknown-escape.rvasm - - operators: - - match: <<|>>|&&|\|\| - scope: keyword.operator.rvasm - - match: <\=|>\=|\=\=|<|>|\!\= - scope: keyword.operator.rvasm - - match: \+|\-|\*|/|%|&|\||\^|~|! - scope: keyword.operator.rvasm - - match: '\?|:' - scope: keyword.operator.rvasm - - match: \(|{|\[ - scope: punctuation.section.group.begin.rvasm - - match: \)|}|\] - scope: punctuation.section.group.end.rvasm - - match: ',|\.' - scope: punctuation.separator.rvasm - - match: ";|:" - scope: punctuation.terminator.rvasm diff --git a/templates/404.html b/templates/404.html deleted file mode 100644 index b081fbc..0000000 --- a/templates/404.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "index.html" %} - -{% block title %} -404 -{% endblock title %} - -{% block header_menu %} -{{ menu_macros::menu_for(config=config, current_item="") }} -{% endblock header_menu %} - -{% block content %} -

    -

    {% block heading %}Lost?{% endblock heading %}

    -

    {% block message %}This page does not exist.{% endblock message %}

    -
    -{% endblock content %} diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index d8543ef..0000000 --- a/templates/index.html +++ /dev/null @@ -1,75 +0,0 @@ -{% import "macros/head.html" as head_macros -%} {% import "macros/menu.html" as -menu_macros -%} {% import "macros/post.html" as post_macros -%} - - - - - {%- block title %}{{ config.title }}{% endblock title -%} - {{ head_macros::head(config=config) }} {%- if config.generate_rss %} {% endif -%} {%- if - config.extra.favicon %} {% endif -%} {%- block extra_head %} {% - endblock extra_head -%} - - - - {% block header %} -
    - -
    - {% endblock header %} -
    -
    - {% block content %} -
    - {# - - Hello there! Welcome to my blog, a simple nook in the vast digital - landscape where I share my thoughts and experiences with programming, - everything open source and operating systems. Enjoy your stay and - happy coding! -
    -
    - #} {% set section = get_section(path="_index.md") %} {# -

    Newest Posts

    - #} {{ post_macros::list_posts(pages=section.pages) }} -
    - {% endblock content %} -
    -
    - {% block footer %} - - {% endblock footer %} - - - - - diff --git a/templates/macros/date.html b/templates/macros/date.html deleted file mode 100644 index e793735..0000000 --- a/templates/macros/date.html +++ /dev/null @@ -1,3 +0,0 @@ -{% macro now_year() %} - {{ now() | date(format="%Y") }} -{% endmacro %} diff --git a/templates/macros/head.html b/templates/macros/head.html deleted file mode 100644 index 415be74..0000000 --- a/templates/macros/head.html +++ /dev/null @@ -1,11 +0,0 @@ -{% macro head(config) %} - - - - - - - - -{% endmacro head %} \ No newline at end of file diff --git a/templates/macros/menu.html b/templates/macros/menu.html deleted file mode 100644 index 58314e3..0000000 --- a/templates/macros/menu.html +++ /dev/null @@ -1,51 +0,0 @@ -{% macro menu(config, current_path) %} - {%- if config.extra.menu_items %} - {%- set menu_items = config.extra.menu_items -%} - - {%- for item in menu_items %} - {%- set abs_item_url = item.url | replace(from="$BASE_URL", to=config.base_url) -%} - {%- set is_current = current_url == abs_item_url ~ "/" - or current_url is starting_with(abs_item_url) - -%} - {%- set is_base = abs_item_url == config.base_url - or abs_item_url == config.base_url ~ "/" - -%} - - {%- if is_base %} - {%- set_global base_item = item -%} - {% endif -%} - - {%- if is_current and not is_base %} - {%- set_global current_item = item -%} - {% endif -%} - {% endfor -%} - - {%- if not current_item and base_item %} - {# Did not match any menu URLs -- assume it's a blog post #} - {%- set current_item = base_item -%} - {% endif -%} - - {{ menu_macros::menu_for(config=config, current_item=current_item) }} - {% endif -%} -{% endmacro menu %} - -{% macro menu_for(config, current_item) %} - {%- if config.extra.menu_items %} - {%- set menu_items = config.extra.menu_items -%} - - - {% endif -%} -{% endmacro menu %} - diff --git a/templates/macros/post.html b/templates/macros/post.html deleted file mode 100644 index cf5d901..0000000 --- a/templates/macros/post.html +++ /dev/null @@ -1,148 +0,0 @@ -{% macro content(page, summary) %} -{%- if summary and page.summary %} -
    - {{ page.content | safe }} -
    - -{% else %} -
    - {{ page.content | safe | replace(from="{{toc}}", to=post_macros::toc(page=page)) | safe }} -
    -{%- endif %} -{% endmacro content %} - -{% macro toc(page) %} -{%- if page.toc %} -
    - Table of Contents -
      - {% for h1 in page.toc %} -
    • - {{ h1.title }} - {% if h1.children %} - - {% endif %} -
    • - {% endfor %} -
    -
    -{% endif %} -{% endmacro toc %} - -{% macro date(page) %} - -{% endmacro post_date %} - -{% macro updated(page) %} - - {%- if page.updated %} - last updated on - {{ page.updated | date(format="%h %d, %Y") }} - {% endif -%} - -{% endmacro updated %} - - -{% macro earlier_later(page) %} -{%- if config.extra.enable_post_view_navigation and page.earlier or page.later %} - -{% endif -%} -{% endmacro earlier_later %} - - -{% macro header(page) %} -

    {{ page.title }}

    - -{{ post_macros::tags(page=page) }} - -{% endmacro header %} - - -{% macro list_posts(pages) %} - -{% endmacro list_posts %} - - -{% macro tags(page, short=false) %} -{%- if page.taxonomies and page.taxonomies.tags %} - -{% endif -%} -{% endmacro tags %} \ No newline at end of file diff --git a/templates/page.html b/templates/page.html deleted file mode 100644 index 4232ae2..0000000 --- a/templates/page.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "index.html" %} - -{% block content %} - -
    - {{ post_macros::header(page=page) }} - {{ post_macros::content(page=page, summary=false) }} - {{ post_macros::earlier_later(page=page) }} -
    -{% endblock content %} \ No newline at end of file diff --git a/templates/series/list.html b/templates/series/list.html deleted file mode 100644 index 2b13659..0000000 --- a/templates/series/list.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "index.html" %} - -{% block content %} -
    -

    all series

    - - -
    -{% endblock content %} \ No newline at end of file diff --git a/templates/series/rust-kernel.html b/templates/series/rust-kernel.html deleted file mode 100644 index 20efd24..0000000 --- a/templates/series/rust-kernel.html +++ /dev/null @@ -1 +0,0 @@ -pog \ No newline at end of file diff --git a/templates/series/single.html b/templates/series/single.html deleted file mode 100644 index 56dcc11..0000000 --- a/templates/series/single.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "index.html" %} - -{% block content %} -
    -

    - {{ term.name }} - ({{ term.pages | length }} post{{ term.pages | length | pluralize }}) -

    - - - Show all series - - - {{ post_macros::list_posts(pages=term.pages) }} -
    -{% endblock content %} \ No newline at end of file diff --git a/templates/shortcodes/figure.html b/templates/shortcodes/figure.html deleted file mode 100644 index 62e2417..0000000 --- a/templates/shortcodes/figure.html +++ /dev/null @@ -1,15 +0,0 @@ -{% if src %} -
    - {% if link %} - - {% endif %} - - {% if link %} - - {% endif %} - - {% if caption %} -
    {{ caption }}
    - {% endif %} -
    -{% endif %} diff --git a/templates/shortcodes/figuresvg.html b/templates/shortcodes/figuresvg.html deleted file mode 100644 index 9bf24c9..0000000 --- a/templates/shortcodes/figuresvg.html +++ /dev/null @@ -1,18 +0,0 @@ -{% if src %} -
    - {% if link %} - - {% endif %} - - {% set data = load_data(path=src, format="plain") %} - {{ data | safe }} - - {% if link %} - - {% endif %} - - {% if caption %} -
    {{ caption }}
    - {% endif %} -
    -{% endif %} diff --git a/templates/shortcodes/file.html b/templates/shortcodes/file.html deleted file mode 100644 index 367933c..0000000 --- a/templates/shortcodes/file.html +++ /dev/null @@ -1,3 +0,0 @@ -{% if name %} -{{ name }} -{% endif %} \ No newline at end of file diff --git a/templates/shortcodes/image.html b/templates/shortcodes/image.html deleted file mode 100644 index 410be76..0000000 --- a/templates/shortcodes/image.html +++ /dev/null @@ -1,3 +0,0 @@ -{% if src %} - -{% endif %} diff --git a/templates/shortcodes/quote.md b/templates/shortcodes/quote.md deleted file mode 100644 index b7a689d..0000000 --- a/templates/shortcodes/quote.md +++ /dev/null @@ -1,14 +0,0 @@ -
    -

    - {{ body | markdown(inline=true) }} -

    - -{% if author %} -— {{author}} - -{% if url %} -{{url_text}} -{% endif %} -{% endif %} - -
    diff --git a/templates/tags/list.html b/templates/tags/list.html deleted file mode 100644 index 0d2e6bf..0000000 --- a/templates/tags/list.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "index.html" %} - -{% block content %} -
    -

    all tags

    - - -
    -{% endblock content %} diff --git a/templates/tags/single.html b/templates/tags/single.html deleted file mode 100644 index 7895912..0000000 --- a/templates/tags/single.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "index.html" %} - -{% block content %} -
    -

    - tag: #{{ term.name }} - ({{ term.pages | length }} post{{ term.pages | length | pluralize }}) -

    - - - Show all tags - - - {{ post_macros::list_posts(pages=term.pages) }} -
    -{% endblock content %} diff --git a/README.md b/variables.css similarity index 100% rename from README.md rename to variables.css