<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on erenon.hu</title>
    <link>http://erenon.hu/posts/</link>
    <description>Recent blogposts on erenon.hu</description>
    <generator>Hugo -- gohugo.io</generator>
    
    <lastBuildDate>Thu, 11 Dec 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="http://erenon.hu/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Summary of Readings</title>
      <link>http://erenon.hu/posts/2025-12-11-summary-of-readings/</link>
      <pubDate>Thu, 11 Dec 2025 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2025-12-11-summary-of-readings/</guid>
      <description>&lt;p&gt;A non-exhaustive list of non-fiction books in no particular order I read recently,
with some recommendation. This post was inspired
by the &lt;a href=&#34;https://eli.thegreenplace.net/tag/book-reviews&#34;&gt;similar posts&lt;/a&gt; of &lt;a href=&#34;https://eli.thegreenplace.net/&#34;&gt;Eli Bendersky&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;faith&#34;&gt;Faith&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/56429777-bible&#34;&gt;The Old Testament&lt;/a&gt;.
Reading it was a challenge of the faith. Long and often difficult to understand.
It is interesting to perceive how the faith of authors change over time,
as they refine their understanding of their Lord, in the context of their history.
My understanding was greatly helped by also reading concurrently:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/199245743-az-sz-vets-g-keletkez-se-i--ii&#34;&gt;Az Ószövetség keletkezése I.-II.&lt;/a&gt; by Rózsa Huba.
A comprehensive review of the studies of every book in the Old Testament. For each book,
it explains its origins, genre, historical background, content, relation to the other books,
and the views and theories of different scholars. This book is its own illustration
in a fascinating and unique way.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;history&#34;&gt;History&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/415654.The_Jewish_War&#34;&gt;The Jewish War&lt;/a&gt; by Flavius Josephus.
An account centered on the Jewish rebellion against the Roman around A.D. 66, starting before
Herod the Great, ending after the destruction of Jerusalem. Long and detailed, but with many
factual errors (and suspected cosmetics), from Josephus, once a Jewish general of the rebellion,
turned to writing after falling captive to Vespasianus. Take away: apparently, people changed
little in 2000 years: greed, hubris, envy, cruelty: nothing new. Also: Herod had every reason to get mad.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;science&#34;&gt;Science&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/en/book/show/123025953&#34;&gt;Breaking Through: My Life in Science&lt;/a&gt; by Katalin Karikó.
Autobiography of the Nobel laureate mRNS pioneer, who played a great role in developing a COVID-19 vaccine.
Also a story of how an extremely determined girl from a family of simple means achieves
great success, through sheer will a continuous work - for decades.
I&amp;rsquo;d also like to acknowledge the seemingly unwavering support of her family.
There&amp;rsquo;s not much about COVID itself, books are referenced instead that explain it in details.
The second part (after Penn) felt rushed, the development mostly unclear to me.
I suppose &lt;em&gt;Kati&lt;/em&gt; had some reading due&amp;hellip;
The writing is not great - it is clearly written by a scientist.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;finance&#34;&gt;Finance&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/21535674-straight-to-hell&#34;&gt;Straight to Hell&lt;/a&gt; by John LeFevre.
Terrific stories about terrible people, written by someone who portrays himself as a similarly terrible
person, with a value system completely foreign to me. The (supposedly true) stories are entertaining
in a cringe way. Going through this book was the reading equivalent of the guilty pleasure inducing
overeating of chocolate cookies.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>Multidimensional Make Targets</title>
      <link>http://erenon.hu/posts/2025-03-18-multimake/</link>
      <pubDate>Tue, 18 Mar 2025 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2025-03-18-multimake/</guid>
      <description>&lt;p&gt;This is about describing targets in &lt;a href=&#34;https://www.gnu.org/software/make/manual/html_node/&#34;&gt;GNU Make&lt;/a&gt; that depend
on many parameters that need to be sampled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example use-case 1&lt;/strong&gt;: There&amp;rsquo;s a simulation with many input parameters,
e.g: weather simulation, with current temperature, humidity, date,
duration parameters. You want to run the simulation for some
combinations of these parameters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example use-case 2&lt;/strong&gt;: There&amp;rsquo;s a remote database the can be queried.
You want to sample larger batches, and apply a pipeline
of transformations on them, iteratively, as you figure out
what&amp;rsquo;s really needed.&lt;/p&gt;
&lt;p&gt;Describing these tasks in a &lt;code&gt;Makefile&lt;/code&gt; is helpful because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It makes dependencies and data-flow explicit (vs. manual changes and ad-hoc intermediate files)&lt;/li&gt;
&lt;li&gt;Slow steps can be cached (simulation, database download)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make&lt;/code&gt; can execute independent steps parallel, greatly reducing execution time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This post is about handling intermediate outputs, that depend on many parameters.
Consider this motivating example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-make&#34; data-lang=&#34;make&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DATE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?=&lt;/span&gt; 2025-03-15
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LONGITUDE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?=&lt;/span&gt; 19.0527693
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LATITUDE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?=&lt;/span&gt; 47.4943593
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;RESOLUTION&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?=&lt;/span&gt; min
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;temperature&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;download_temperature&lt;/span&gt;.&lt;span class=&#34;n&#34;&gt;py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ./$&amp;lt; --date &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;DATE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --longitude &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LONGITUDE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --latitude &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LATIDUTE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --resolution &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;RESOLUTION&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case, by default, &lt;code&gt;make temperature&lt;/code&gt; downloads the measurements for a pre-defined location and day.
The user can override the default using environment variables, e.g: &lt;code&gt;DATE=2025-03-16 make temperature&lt;/code&gt;
will download it for the next day. However, there are multiple problems here, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;After a successful download, changing the variables will not prompt &lt;code&gt;make&lt;/code&gt; to re-execute the download.
It doesn&amp;rsquo;t know that the target depends on those variables, and it will consider it up-to-date.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We can&amp;rsquo;t have multiple measurements (with different parameters) in our tree at the same time,
e.g: to compare them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If we have a similar &amp;ldquo;humidity&amp;rdquo; target, it gets easy to accidentally combine different measurements
that are otherwise unrelated.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To fix these, we need to make the dependency of the target on the variables explicit.
The key idea is that we need to encode the variables in the target name.
For example, instead of simply having &amp;ldquo;temperature&amp;rdquo;, we&amp;rsquo;ll have &amp;ldquo;o/2025-03-15/19.0527693/47.4943593/min/temperature&amp;rdquo;.
This immediately solves all of the above.&lt;/p&gt;
&lt;p&gt;But how do we describe this in a Makefile? We can&amp;rsquo;t enumerate all the possible combinations.
Ideally, make &lt;a href=&#34;https://www.gnu.org/software/make/manual/html_node/Pattern-Intro.html&#34;&gt;pattern rules&lt;/a&gt; could support something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-make&#34; data-lang=&#34;make&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# not actual make syntax
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;o/%DATE%/%LONGITUDE%/%LATITUDE%/%RESOLUTION%/temperature&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;download_temperature&lt;/span&gt;.&lt;span class=&#34;n&#34;&gt;py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ./$&amp;lt; --date &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;DATE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --longitude &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LONGITUDE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --latitude &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LATIDUTE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --resolution &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;RESOLUTION&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;but it doesn&amp;rsquo;t:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A pattern rule contains the character ‘%’ (exactly one of them) in the target.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead, we&amp;rsquo;ll emulate this, using &lt;a href=&#34;https://www.gnu.org/software/make/manual/html_node/Pattern_002dspecific.html&#34;&gt;Pattern-specific Variable Values&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-make&#34; data-lang=&#34;make&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# when requesting &amp;#34;o/2025-03-15/19.0527693/47.4943593/min/temperature&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# split the target name ($@) into a list, and set the variables using the directory names.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;o/%&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DATE&lt;/span&gt; = &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;word&lt;/span&gt; 2,&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;subst&lt;/span&gt; /, ,&lt;span class=&#34;k&#34;&gt;$&lt;/span&gt;@&lt;span class=&#34;k&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;o/%&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;LONGITUDE&lt;/span&gt; = &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;word&lt;/span&gt; 3,&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;subst&lt;/span&gt; /, ,&lt;span class=&#34;k&#34;&gt;$&lt;/span&gt;@&lt;span class=&#34;k&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;o/%&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;LATITUDE&lt;/span&gt; = &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;word&lt;/span&gt; 4,&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;subst&lt;/span&gt; /, ,&lt;span class=&#34;k&#34;&gt;$&lt;/span&gt;@&lt;span class=&#34;k&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;o/%&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RESOLUTION&lt;/span&gt; = &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;word&lt;/span&gt; 5,&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;subst&lt;/span&gt; /, ,&lt;span class=&#34;k&#34;&gt;$&lt;/span&gt;@&lt;span class=&#34;k&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;o/%/temperature&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;download_temperature&lt;/span&gt;.&lt;span class=&#34;n&#34;&gt;py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ./$&amp;lt; --date &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;DATE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --longitude &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LONGITUDE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --latitude &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LATIDUTE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --resolution &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;RESOLUTION&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;o/%/humidity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;download_humidity&lt;/span&gt;.&lt;span class=&#34;n&#34;&gt;py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ./$&amp;lt; --date &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;DATE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --longitude &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LONGITUDE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --latitude &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LATIDUTE&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; --resolution &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;RESOLUTION&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;see-also&#34;&gt;See Also&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://justine.lol/make/&#34;&gt;Using Landlock to Sandbox GNU Make&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tech.davis-hansson.com/p/make/&#34;&gt;Your Makefiles are wrong&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>To hang an Icon</title>
      <link>http://erenon.hu/posts/2025-03-14-to-hang-an-icon/</link>
      <pubDate>Fri, 14 Mar 2025 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2025-03-14-to-hang-an-icon/</guid>
      <description>&lt;p&gt;I wanted to hang a picture, in a way that it doesn&amp;rsquo;t touch the wall directly,
using a protruding hanger. If the hanger is difficult to observe, it makes
a nice floating effect.&lt;/p&gt;
&lt;p&gt;I couldn&amp;rsquo;t find a &amp;ldquo;µ&amp;rdquo; shaped piece of metal, and I don&amp;rsquo;t have right drills for metal either,
so I decided to 3D-print the appropriate hanger. This would have been my first time doing this.&lt;/p&gt;
&lt;p&gt;First, I needed a 3D modelling software, that is free and Linux compatible.
From the &lt;a href=&#34;https://wiki.archlinux.org/title/List_of_applications/Science#Computer-aided_design&#34;&gt;Arch Linux offering&lt;/a&gt;,
I quickly selected &lt;a href=&#34;https://openscad.org/&#34;&gt;OpenSCAD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;OpenSCAD is a wonderful software - instead of moving the model elements with a mouse,
it lets you describe the model using code, something I&amp;rsquo;m familiar with.
It is lightweight, with a clear, responsive UI.
By skimming the &lt;a href=&#34;https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Primitive_Solids#cube&#34;&gt;wiki&lt;/a&gt; and looking at examples,
I was able to create a simple model in less than 50 minutes.
A testament of a great piece of software indeed.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;http://erenon.hu/img/openscad.png&#34; alt=&#34;Screenshot of OpenSCAD, showing the editor, the rendered model, and the render status&#34;&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;a href=&#34;https://gist.github.com/erenon/9788cdbe3c79128a117406eb3c7af36f&#34;&gt;code producing this model&lt;/a&gt; is short and simple.
The parametric nature allows quick changes, e.g: of thickness or depth.
After exporting the model to &lt;a href=&#34;https://en.wikipedia.org/wiki/STL_(file_format)&#34;&gt;.stl&lt;/a&gt;,
I asked a friend to print it (thanks!). The Prusa machine did a beautiful job:&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;http://erenon.hu/img/icon-pin.jpg&#34; alt=&#34;The 3D-printed icon hanger, with a lego figure to scale&#34;&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Finally, after a bit of measuring and drilling, the final composition:
modern and antique co-existing in perfect harmony.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;http://erenon.hu/img/icon-and-screen.jpg&#34; alt=&#34;A monitor and the Trinity icon by Andrei Rublev side by side&#34;&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
</description>
    </item>
    <item>
      <title>Restore file from editor memory</title>
      <link>http://erenon.hu/posts/2024-10-14-restore-from-memory/</link>
      <pubDate>Mon, 14 Oct 2024 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2024-10-14-restore-from-memory/</guid>
      <description>&lt;p&gt;I was editing a large text file over &lt;a href=&#34;https://github.com/libfuse/sshfs&#34;&gt;sshfs&lt;/a&gt; using &lt;code&gt;nvim&lt;/code&gt;.
Probably due to a lose network cable, during save, the connection broke,
and only half of the data reached the remote computer, the other
half remained in the send buffers of the kernel.&lt;/p&gt;
&lt;p&gt;The editor was configured to use no swapfiles, therefore it wrote
the target file directly: at this point the second half of the file
was &lt;em&gt;lost&lt;/em&gt;, and the editor stuck in disk sleep.&lt;/p&gt;
&lt;p&gt;The situation did not improve after the network connection was restored
and the share re-mounted (after it was forcefully unmounted&amp;hellip;).
The last backup was too old, and I needed the data.&lt;/p&gt;
&lt;h2 id=&#34;solution&#34;&gt;Solution&lt;/h2&gt;
&lt;p&gt;Get the PIDs of the currently running editors:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ps -A &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep nvim
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;PID&amp;gt;s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s find which editor instance was writing the file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ls -l /proc/&amp;lt;PID&amp;gt;/fd/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;13&lt;/span&gt; -&amp;gt; path/to/our/file
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can check that this editor is indeed stuck, waiting for the
kernel to complete a write operation, that&amp;rsquo;ll never happen:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /proc/&amp;lt;PID&amp;gt;/status &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep State
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;State: D &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;disk sleep&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this state, the process cannot be attached to,
&lt;code&gt;gdb&lt;/code&gt; or &lt;code&gt;gcore&lt;/code&gt; will hang if tried. Fortunately, &lt;a href=&#34;https://man7.org/linux/man-pages/man5/proc_pid_mem.5.html&#34;&gt;&lt;code&gt;/proc/&amp;lt;PID&amp;gt;/mem&lt;/code&gt;&lt;/a&gt;
is still readable. The file is sparse, only offsets mapped
by that process are readable. &lt;a href=&#34;https://man7.org/linux/man-pages/man5/proc_pid_maps.5.html&#34;&gt;&lt;code&gt;/proc/&amp;lt;PID&amp;gt;/maps&lt;/code&gt;&lt;/a&gt; gives
the valid offsets. A &lt;a href=&#34;https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux/6302#6302&#34;&gt;python script from StackExchange&lt;/a&gt; will save
the readable sections into a file. Slightly improved:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Original https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux/6302#6302&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;re&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;123456&amp;#39;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;# change this&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;maps_file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/proc/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/maps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;mem_file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/proc/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/mem&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;output_file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;self.dump&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;maps_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;readlines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# for each mapped region&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# if this is a readable region&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;mem_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;seek&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# seek to region start&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mem_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# read region contents&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;output_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# dump contents to standard output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;OSError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;failed to read from &amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;maps_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;mem_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;output_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this script will save the memory of the editor,
that contains the complete file &amp;ndash; in chunks.
Fortunately, the internal structure is line-oriented, and tends to follow the
time of insertions. The lines in each chunk are in reverse order, use &lt;code&gt;tac&lt;/code&gt; to
fix that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;strings nvim.dump &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; tac &amp;gt; nvim.strings
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;takeaway&#34;&gt;Takeaway&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;set swapfile&lt;/code&gt;, when editing over sshfs&lt;/li&gt;
&lt;li&gt;More frequent backups&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t panic.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>No Miracles</title>
      <link>http://erenon.hu/posts/2024-04-04-no-miracles/</link>
      <pubDate>Thu, 04 Apr 2024 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2024-04-04-no-miracles/</guid>
      <description>&lt;p&gt;One would argue that miracles do not exist.&lt;/p&gt;
&lt;p&gt;Long time ago, a prominent member of a now mostly abandoned web forum offered to lend
a technical book (JavaScript: The Good Parts) to me, an absolute unknown nobody. For free. No strings attached.
Obviously, it isn&amp;rsquo;t a miracle, just a selfless act of kindness. I was still flattered.&lt;/p&gt;
&lt;p&gt;15 years later, I was wondering what happened to him. His blog had a very memorable domain name.
Lacking a device, I couldn&amp;rsquo;t check it out immediately, and the memory faded. The very next day,
a post from that very blog popped up in my RSS reader, coming through the HackerNews &amp;ldquo;best&amp;rdquo; feed.
A feed that selects the top few posts of thousands of submissions all over the world.
A lucky coincidence, we can conclude, no miracle.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://20y.hu/~slink/journal/xylophone-duplo/&#34;&gt;The article&lt;/a&gt; is about a scientific experiment (if you squint a bit). Given the connection above,
I felt compelled to verify the results by reproduction, especially because I had all the required resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some colored pieces of DUPLO&lt;/li&gt;
&lt;li&gt;A xylophone of matching (somewhat) colors&lt;/li&gt;
&lt;li&gt;An interested toddler&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please find the anonymized results of the reproduced experiment in the diagram below (Figure 1):&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;http://erenon.hu/img/xylophone.jpg&#34; alt=&#34;Colored duplo pieces arranged in a matrix, with two figures at opposite corners, and a similarly colored xylophone&#34;&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I can confirm that, in accordance with the original research:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Toddler beating the xylophone can instruct papa correctly to find the daughter&lt;/li&gt;
&lt;li&gt;Toddler listening to the xylophone can move the daughter correctly to find papa&lt;/li&gt;
&lt;li&gt;and have fun in the meantime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, we found that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Collectible flowers can be added to the map, and a mini inventory (separate flat piece),
so the daughter can find and properly greet mama&lt;/li&gt;
&lt;li&gt;Introducing windows that can be opened and climbed through works&lt;/li&gt;
&lt;li&gt;Only listening to the xylophone without looking at the colors doesn&amp;rsquo;t work&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apparently, you can have this simple, no-computer equipment, and still have fun with your kid,
science concur. So we experiment, explore, build and laugh together, freely.&lt;/p&gt;
&lt;p&gt;And one might argue, that miracles do not exist.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Summary of Readings</title>
      <link>http://erenon.hu/posts/2023-10-20-summary-of-readings/</link>
      <pubDate>Fri, 20 Oct 2023 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2023-10-20-summary-of-readings/</guid>
      <description>&lt;p&gt;A non-exhaustive list of non-fiction books in no particular order I read recently,
with some recommendation. This post was inspired
by the &lt;a href=&#34;https://eli.thegreenplace.net/tag/book-reviews&#34;&gt;similar posts&lt;/a&gt; of &lt;a href=&#34;https://eli.thegreenplace.net/&#34;&gt;Eli Bendersky&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;adventure&#34;&gt;Adventure&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/139069.Endurance&#34;&gt;Endurance: Shackleton&amp;rsquo;s Incredible Voyage&lt;/a&gt; by Alfred Lansing.
The true story of how the members of a failed Antarctic expedition struggle for years to get home.
Based on journals and interviews with the survived. Excellent writing, recommended.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;biography&#34;&gt;Biography&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/133800004-surely-you-are-joking-mr-feynman&#34;&gt;Surely you are joking, Mr. Feynman&lt;/a&gt; by Richard P. Feynman.
Edited personal stories told by the Nobel prize laureate author, presented in an informal, entertaining style,
without any forced political correctness. The focus is on physics, education, social interactions and &lt;em&gt;girls&lt;/em&gt;,
strongly from the authors point of view.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/141499.Failure_is_Not_an_Option&#34;&gt;Failure is Not an Option&lt;/a&gt; by Gene Kranz.
Memoir, focusing on the U.S. space program, from Mercury through Gemini to Apollo - from the first lauch to the first
manned Moon landing and more. It is especially about the Mission Control Center. To work is insightful as
it shows how the space program (the preparations and missions) was operated. Marta is a real hero.
The writing is sometimes unclear and overall a bit long.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;sociology&#34;&gt;Sociology&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://moly.hu/konyvek/kormendy-lajos-a-kozosseg-nelkuli-ember&#34;&gt;A közösség nélküli ember&lt;/a&gt;, Körmendy Lajos.
Első (jelentősen nagyobbik felében) áttekintést ad a nyugat-európai közösségek (család, falu, céh, rend) fejlődéséről,
azok szerepéről, gazdaság és személyiségformáló hatásáról. Tárgyalja az individualista-kollektivista emberek
hatását a társadalom különböző rétegeiben, a modernizáció, szekularizáció, racionalizmus, kapitalizmus dinamikáját, kölcsönhatását
az egyénnel és közösségeivel. A felállított modellek, az ismertetett fogalomkészlet szemléletformáló hatású.
A második részben értelmezi a modern társadalom egyes jelenségeit (pl.: tömegember, divat, ellenkultúra),
és becsléseket tesz a változó demográfiájú, kulturális összetételű Európa jövőjére vonatkozóan.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;software&#34;&gt;Software&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/18743934-stay-awhile-and-listen&#34;&gt;Stay Awhile and Listen: Book I&lt;/a&gt; by David L. Craddock.
An overview of of Condor (later Blizzard North) was founded, how Diablo I was developed.
The book is mainly a big collection of quotes, compiled from interview conducted by the author with the original developers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/52651926-stay-awhile-and-listen&#34;&gt;Stay Awhile and Listen: Book II&lt;/a&gt; by David L. Craddock.
The story of how Diablo II was produced, with additional info about other Blizzard Entertainment games of the same time,
especially Starcraft I. It is a better edited collection of quotes, but the cohesion is still pretty weak.
Like the first one, this book is also way too light on technical details.
The author does not assume that the reader played any of the games.
I&amp;rsquo;m otherwise grateful to the author for the interviews.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>Selfhosted RSS Reader</title>
      <link>http://erenon.hu/posts/2023-08-01-selfhosted-rss/</link>
      <pubDate>Tue, 01 Aug 2023 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2023-08-01-selfhosted-rss/</guid>
      <description>&lt;p&gt;For some time I was using a simple &lt;a href=&#34;https://en.wikipedia.org/wiki/RSS&#34;&gt;RSS&lt;/a&gt; reader on my phone.
However, I wanted to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read the same set of feeds on multiple devices&lt;/li&gt;
&lt;li&gt;Archive the scraped content&lt;/li&gt;
&lt;li&gt;Search the archived content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I looked around for different options.
I ruled out proprietary options, fearing their end of life (e.g: Google Reader)
or gradual degradation - I wanted something stable.&lt;/p&gt;
&lt;p&gt;The chosen architecture consists of (1) a server component, that periodically
scrapes and archives the configured feeds, exports the content to authorized clients,
and tracks read status (So if I read an article on one device, it&amp;rsquo;ll appear as
read on all the connected devices), and (2) client programs on different devices with screens.&lt;/p&gt;
&lt;p&gt;For the server side, I chose &lt;a href=&#34;https://miniflux.app/&#34;&gt;miniflux&lt;/a&gt;. It is easy to deploy (requires PostgreSQL),
sufficiently fast on a Raspberry Pi4 (written in Go), and provides a pleasingly simple
web interface, for desktop use. Note that miniflux offers a &lt;a href=&#34;https://miniflux.app/hosting.html&#34;&gt;paid, hosted service&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For mobile, there are several options with different qualities.
On Android, I found &lt;a href=&#34;https://play.google.com/store/apps/details?id=com.seazon.feedme&amp;amp;hl=en&amp;amp;gl=US&#34;&gt;FeedMe&lt;/a&gt; really good. Enable the Fever API in miniflux
(Settings/Integrations/Fever) to make them connect.&lt;/p&gt;
&lt;p&gt;On iOS, &lt;a href=&#34;https://netnewswire.com/&#34;&gt;NetNewsWire&lt;/a&gt; is an absolute treat. Enable the Google Reader API in
miniflux, and use the FreshRSS option in the client to make them connect.&lt;/p&gt;
&lt;p&gt;All of these components (miniflux, FeedMe, NetNewsWire) can also get the original HTML
content and convert it to simplified text (with images), in case the content in the RSS
feed is missing or incomplete. A non-trivial, and really useful feature.&lt;/p&gt;
&lt;p&gt;The server runs on a network that doesn&amp;rsquo;t have a public IP address.
I made it available to other devices using &lt;a href=&#34;https://tailscale.com/&#34;&gt;tailscale&lt;/a&gt;, that is
also warmly recommended, and surprisingly easy to set up.
It uses &lt;a href=&#34;https://tailscale.com/blog/how-nat-traversal-works/&#34;&gt;various tricks&lt;/a&gt; to bypass NAT based firewalls,
and works in every scenario I tested so far.&lt;/p&gt;
&lt;p&gt;The result is a securely available, robust, fast, clean news feed.&lt;/p&gt;
&lt;h2 id=&#34;considered-alternatives&#34;&gt;Considered Alternatives&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;FreshRSS: I didn&amp;rsquo;t like the HTML interface&lt;/li&gt;
&lt;li&gt;Microflux: Slow and confusing UI&lt;/li&gt;
&lt;li&gt;Miniflutt: Does not store content locally, always needs to re-fetch everything on start (no offline mode)&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>Why Bazel</title>
      <link>http://erenon.hu/posts/2023-07-18-why-bazel/</link>
      <pubDate>Tue, 18 Jul 2023 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2023-07-18-why-bazel/</guid>
      <description>&lt;p&gt;Some time ago I was tasked with improving the build system of a large company.
Requirements: build (compile and test) a large amount of C++ code (n*100k+ lines), targeting various versions of Linux and Windows,
scattered in several repositories,
using an enormous number of dependencies (mostly stored as binaries), mixed with Python tests,
with the possibility of extending it to Java.&lt;/p&gt;
&lt;p&gt;This post explains why &lt;a href=&#34;https://bazel.build/&#34;&gt;Bazel&lt;/a&gt; was chosen for this particular task. This is not a general introduction for Bazel,
nor a thorough comparison against other build systems.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;The then current build system was based on a fork of a lesser known &lt;a href=&#34;https://www.gnu.org/software/make/&#34;&gt;Makefile&lt;/a&gt; generator.
It had the following issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The only form of caching was with &lt;a href=&#34;https://ccache.dev/&#34;&gt;ccache&lt;/a&gt;, that stores object files only, and has its own limitations.&lt;/li&gt;
&lt;li&gt;The generated Makefiles allowed one to change a dependency without triggering a re-build
(this is because &lt;code&gt;make&lt;/code&gt; uses &lt;code&gt;mtime&lt;/code&gt; for change detection), causing hard to debug issues,
causing developers to lose trust in the system, causing them to regularly &lt;code&gt;make clean&lt;/code&gt; first,
further increasing resource usage.&lt;/li&gt;
&lt;li&gt;The development of the tool was halted, there was no hope to get C++ Modules support for it.&lt;/li&gt;
&lt;li&gt;The build system did not cover running tests. Even if the smallest change was made in a very large
repository (think monorepo), all the tests must be re-run, taking a lot of time and resources.&lt;/li&gt;
&lt;li&gt;Because running all the tests was slow, developers skipped them, or guessed, failing in CI much later,
losing time.&lt;/li&gt;
&lt;li&gt;Multiplatform builds were time consuming and difficult, making developers to push it on the CI,
and running long cycles on failure.&lt;/li&gt;
&lt;li&gt;Getting a project and all its dependencies build with a particular set of compiler flags (e.g: &lt;code&gt;-fsanitize=address&lt;/code&gt;)
was next to impossible, turning easy-to-find bugs to terribly difficult to debug failures.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The Solution&lt;/h2&gt;
&lt;p&gt;I chose to migrate to Bazel, that offers a set of self-reinforcing benefits.
To understand these benefits, we need to understand how Bazel works (supposed to work).
I&amp;rsquo;m going to simplify things for brevity.&lt;/p&gt;
&lt;p&gt;Bazel is similar to &lt;code&gt;make&lt;/code&gt;. The user defines a set of rules, that describe how the project can be built.
Each rule has a set of inputs and an expected set of outputs.
The input set contains e.g: a command, that defines what should be executed to turn the inputs into the output.
The input set also contains a program (e.g: a compiler) that transforms the input files into the output files.
The output of one rule can be the input of another, forming a tree.
To build a particular output, first the system builds the dependencies of the rule (the inputs that are
outputs of other rules), then it executes the rule.&lt;/p&gt;
&lt;p&gt;Bazel is an improvement over &lt;code&gt;make&lt;/code&gt;. First, it enforces (tries hard to) that
rules are only allowed to read their declared inputs, to produce the output,
and cannot depend on anything else (e.g: system time, network, etc).
A build executed in this fashion is called a &lt;em&gt;Hermetic Build&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If a build is really hermetic, executing it on the same source
always yields to the same output, therefore it is a &lt;em&gt;Reproducible Build&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For the first full build, there&amp;rsquo;s little difference compared to &lt;code&gt;make&lt;/code&gt;.
For subsequent, incremental builds however, there are plenty.&lt;/p&gt;
&lt;p&gt;Because of the reproducible property, if the inputs of a rule
did not change, the output wouldn&amp;rsquo;t change either, therefore the rule
does not need to re-run.
For example, if a comment is removed, but the compiler produces the same object
file, the link step will not be executed again.
Bazel manages tests, similar to compilation, therefore, if the test binary
(and its inputs) did not change, the test doesn&amp;rsquo;t need to re-run either.&lt;/p&gt;
&lt;p&gt;To decide whether the input has changed (compared to the previously built output),
Bazel uses the hash of the input (compared to &lt;code&gt;make&lt;/code&gt;, that uses &lt;code&gt;ctime&lt;/code&gt;).
This fixes the correctness issues, and a key piece for distributed execution and caching.&lt;/p&gt;
&lt;p&gt;It is possible to setup a &lt;em&gt;remote executor&lt;/em&gt; cluster (e.g: &lt;a href=&#34;https://github.com/buildbarn&#34;&gt;buildbarn&lt;/a&gt;),
that accepts rules to be executed, along with the inputs.
If the rule for the given set of input was already executed,
it returns the cached result, otherwise it executes the rule,
caches then returns the result. This includes not just compilation,
but also running the tests (where the test results are cached).&lt;/p&gt;
&lt;p&gt;This improves the developer workflow on several points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Fetching and building the main branch of a repository is quick,
as that state is probably already built and cached.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After a change, it is to run all the affected tests, and only those,
implcitly caching the results at the same time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The CI gets also fast, because it just fetches the repository,
and probably gets back the green results of the tests,
produced from the previous step.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The remote executor can support multiplatform builds,
saving the programmer from shipping the code manually to multiple platforms
for building.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also greatly reduces the overall resource usage.&lt;/p&gt;
&lt;p&gt;Because of its efficiency, Bazel allows one to put more and more code (also dependencies)
into a single repository, building them together. This unlocks further opportunities:
fully static builds for better optimization and simplicity, build governed flags (e.g: sanitizers).&lt;/p&gt;
&lt;p&gt;Bazel is not without difficulties: making builds hermetic, maintaining proper state (the input hash, output storage),
operating the remote executor and cache, etc., is a demanding task.
Therefore Bazel is better suited for larger projects with more developers.
During selection, CMake was considered and evaluated, but apart from it being a popular build system,
it does not solve any of the issues Bazel does.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Summary of Readings</title>
      <link>http://erenon.hu/posts/2023-04-10-summary-of-readings/</link>
      <pubDate>Mon, 10 Apr 2023 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2023-04-10-summary-of-readings/</guid>
      <description>&lt;p&gt;A non-exhaustive list of recently read non-fiction books in no particular order.&lt;/p&gt;
&lt;h2 id=&#34;hit&#34;&gt;Hit&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/60017941-b-jte-csaba-f-vesk-nyve-2---boldogs-ghoz-seg-t-gondolatok&#34;&gt;Böjte Csaba füveskönyve 2.&lt;/a&gt;, Böjte Csaba.
Rövid, tömör, önmagukban megálló, bölcs gondolatok. Csaba atya férfias szeretete átsugárzik a sorain.
Szülőknek és útkeresőknek kifejezetten ajánlom.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;industrial-design&#34;&gt;Industrial Design&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/17290807-the-design-of-everyday-things&#34;&gt;The Design of Everyday Things&lt;/a&gt;, by Donald A. Norman.
A few good ideas on how to design user interfaces (or in general, anything). Overall, the information density of this book is very
low, it was a huge drag. I recommend to read a summary instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;psychology&#34;&gt;Psychology&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/48854743-ami-igaz-n-sz-m-t&#34;&gt;Ami igazán számít&lt;/a&gt; by Pál Ferenc.
A könyv hátáról: &amp;ldquo;Ez a könyv szándékom szerint az eddigi szakmai
tanulmányaim és emberi tapasztalataim összefoglalását is jelenti. Arra
vágytam, hogy amit a különböző humán területek tudásban, kutatási
eredményekben kidolgoztak, az eljuthasson azokhoz, akiket érint, akiknek a
legnagyobb szükségük van rá. Hozzátok. Ezért több mint tíz nagy téma került
a kötetbe, reményem szerint összegyűjtve a leghasznosabb információkat
egy-egy területre vonatkozóan.&amp;rdquo;
Jól felépített, fontos témákat érintő, tudományos eredményeket és személyes sztorikat jól vegyítő,
olvasmányos könyv. Ajánlom.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/30753738-the-choice&#34;&gt;The Choice&lt;/a&gt; by Edith Eva Eger.
Autobiography, from coming of age, through Auschwitz and other camps, to a new land.
From trauma to healing, through acceptance and love. Excellent writing, with suspense and drama,
pierced through by an ever positive attitude.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/25701004-mens-search-for-meaning&#34;&gt;Men&amp;rsquo;s Search for Meaning&lt;/a&gt; by Viktor Frankl.
A short, two parts book from a famous Holocaust survivor. In the first part,
the author describes his concentration camp experiences from an analytical
perspective, focusing on the behaviour of inmates and guards. The second
part is a short introduction to Logotherapy, a psychotherapy school founded
by the author that emphasises the importance of finding the meaning of life,
and actualizing possibilities presented by it. Theories of the second part
are underpinned by examples from the first part, making the book whole. It
has a lot of food for thought, e.g:
&lt;em&gt;&amp;ldquo;It’s not what you expect from life, but what life expects from you.&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;economy&#34;&gt;Economy&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/26530355-misbehaving&#34;&gt;Misbehaving: The Making of Behavioral Economics&lt;/a&gt; by Richard Thaler.
Part professional autobiography, part the story of how behavioral economics was developed and popularized.
The mainline is illustrated with memorable stories (mainly experiments and surprising incidents),
generous praise of fellow behavioral economists,
and an endless stream of jabs pointed at classical economists.
A shorter summary focusing on the presented findings would have been appreciated to save pages:
it is too shallow for a biography, and too verbose otherwise, while still thought provoking.
Read &lt;a href=&#34;https://www.goodreads.com/book/show/11468377-thinking-fast-and-slow&#34;&gt;Thinking, Fast and Slow&lt;/a&gt; first.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;honorable-mentions&#34;&gt;Honorable Mentions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Agamemnon by Aeschylus. I barely understand the English version. I also tried an older Hungarian one,
with similar results. It appears that &lt;a href=&#34;https://en.wikipedia.org/wiki/Software_rot&#34;&gt;bit rot&lt;/a&gt; applies to literature as well.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>LedgerSplit vim function</title>
      <link>http://erenon.hu/posts/2023-03-09-ledger-split-vim/</link>
      <pubDate>Thu, 09 Mar 2023 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2023-03-09-ledger-split-vim/</guid>
      <description>&lt;p&gt;I manage my finances using &lt;a href=&#34;https://www.ledger-cli.org/&#34;&gt;ledger&lt;/a&gt;.
Periodically, I export my bank statements and push them through a &lt;a href=&#34;https://www.gnu.org/software/make/&#34;&gt;make&lt;/a&gt;
pipeline to get a ledger file. The conversion assigns transactions
to accounts based on a set of rules, e.g: If the payee is &amp;ldquo;Foo Shop&amp;rdquo;,
the account is going to be &amp;ldquo;Expenses:Food&amp;rdquo;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-ledger&#34; data-lang=&#34;ledger&#34;&gt;2023-03-09 Foo Shop
    Expenses:Food           1000 HUF
    Assets:Card
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, sometimes I buy e.g: toothpaste in &amp;ldquo;Foo Shop&amp;rdquo;, that I don&amp;rsquo;t want to eat.
I do not want to contaminate my ledger, so I save the receipt and mark the item.
During the periodic export, I manually fix the ledger file in &lt;a href=&#34;https://www.vim.org/&#34;&gt;vim&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-ledger&#34; data-lang=&#34;ledger&#34;&gt;2023-03-09 Foo Shop
    Expenses:Food            750 HUF
    Expenses:Health          250 HUF
    Assets:Card
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since I have an engineering degree, most of the time I get this subtraction right.
For the same reason, I made a tiny vim script, that automates some of this.
Standing on this line:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-ledger&#34; data-lang=&#34;ledger&#34;&gt;    Expenses:Food           1000 HUF
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Issuing this command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;:LedgerSplit 100+200
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Modifies the posting like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-ledger&#34; data-lang=&#34;ledger&#34;&gt;    Expenses:Food           700 HUF
    Expenses:Food           300 HUF
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It splits the given amount from the current posting, and moves it to the next line.
It handles expressions (as shown) and decimal fractions.
After this I just need to fix the second account name.&lt;/p&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;
&lt;p&gt;To &lt;code&gt;.vimrc&lt;/code&gt;, add:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-vim&#34; data-lang=&#34;vim&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;au&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BufRead&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;BufNewFile&lt;/span&gt; *.&lt;span class=&#34;nx&#34;&gt;ledger&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;filetype&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ledger&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To &lt;code&gt;.vim/after/ftplugin/ledger.vim&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-vim&#34; data-lang=&#34;vim&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LedgerSplit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;abort&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pat&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;[0-9\\.]\\+&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;old&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;str2float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;matchstr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;substitute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;%.2f&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;old&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;:&lt;span class=&#34;nx&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;\\.00$&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;split&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;substitute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;%.2f&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;:&lt;span class=&#34;nx&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;\\.00$&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;call&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;setline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;substitute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;call&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;substitute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endfunction&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LedgerSplit&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;call&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LedgerSplit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;lt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    <item>
      <title>The disgust of sequential processing of binary WebSocket messages in JavaScript</title>
      <link>http://erenon.hu/posts/2023-03-09-javascript-binary-ws/</link>
      <pubDate>Thu, 09 Mar 2023 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2023-03-09-javascript-binary-ws/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s process binary WebSocket messages in the browser, in the order the server has sent them.
The &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications&#34;&gt;Writing WebSocket client applications&lt;/a&gt; MDN article is a reasonable place to start:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;WebSocket&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ws://foo.bar/endpoint&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;onmessage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we were to receive &lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc6455#section-5.6&#34;&gt;text messages&lt;/a&gt;, this&amp;rsquo;d be all we need.
However, with binary messages, we&amp;rsquo;ll get:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Blob { size: 16, type: &amp;#34;&amp;#34; }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ugh. So we need to extract the received bytes from this blob first.
Also note that, about &lt;code&gt;event.data&lt;/code&gt; &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data&#34;&gt;MDN says&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The data sent by the message emitter; this can be any data type.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Therefore, a correct program should check if it really received a blob.
Unfortunately, &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Blob&#34;&gt;Blob&lt;/a&gt; does not provide a synchronous way to get to its data,
only through an asynchronous promise (and this will be a pain later):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;WebSocket&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ws://foo.bar/endpoint&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;onmessage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;arrayBuffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point, we have an &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer&#34;&gt;ArrayBuffer&lt;/a&gt;. It still does not allow reading its data,
we have to use another wrapper:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;WebSocket&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ws://foo.bar/endpoint&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;onmessage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;arrayBuffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DataView&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getUint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* offset */&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* little endian */&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, we managed to read a 32 bit unsigned number from the binary payload.
The &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView&#34;&gt;DataView&lt;/a&gt; documentation says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The DataView view provides a low-level interface for reading and writing multiple number types in a binary ArrayBuffer, without having to care about the platform&amp;rsquo;s endianness.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Obviously, we still need to care about the endinanness of the message!&lt;/p&gt;
&lt;h2 id=&#34;ordering&#34;&gt;Ordering&lt;/h2&gt;
&lt;p&gt;So this was it? It wasn&amp;rsquo;t that hard! Yes, and this solution is also wrong.
We wanted to process the messages in the order the server has sent them.
However, while we wait for the array buffer promise that fetches our data (see: &lt;code&gt;then&lt;/code&gt;),
the onmessage handler yields to the runtime, that is free to schedule another task.
It is even free to schedule another onmessage invocation, and that promise might be ready earlier.
Therefore, it is possible, that we&amp;rsquo;ll process messages out of order.
If the message rate is high enough, this possibility becomes a certainty.&lt;/p&gt;
&lt;p&gt;One possible solution is to queue the promises, and process them in queue order, instead of resolution order:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Sequencer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;xs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;x0id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;undefined&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;x0id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;undefined&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;reserve&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nx&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;undefined&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;x0id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nx&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;x0id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;processNextMessage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;undefined&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DataView&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getUint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* offset */&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* little endian */&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Sequencer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;WebSocket&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ws://foo.bar/endpoint&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;onmessage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;messages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reserve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;arrayBuffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;processNextMessage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;A tiny change in the requirements (binary messages instead of text messages)
inflicted a great deal of complexity on the correct implementation.
It is sad to see how seemingly simple tasks can turn out to be not so simple.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Sway sessions à la tmux</title>
      <link>http://erenon.hu/posts/2023-01-20-sway-sessions-a-la-tmux/</link>
      <pubDate>Fri, 20 Jan 2023 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2023-01-20-sway-sessions-a-la-tmux/</guid>
      <description>&lt;p&gt;A window manager allows the user to organize windows of running programs
on a desktop. Desktops are mapped to outputs (i.e: monitors).
Most window managers support &lt;a href=&#34;https://en.wikipedia.org/wiki/Virtual_desktop&#34;&gt;Virtual Desktops&lt;/a&gt;, allowing the user
to move windows to separate desktops and quickly switch which desktop is shown on a given output
(how quickly depends on the window manager: on popular commercial systems, not so quickly).&lt;/p&gt;
&lt;p&gt;The used workspaces might be shown to the user in a taskbar-like fashion, e.g: on &lt;a href=&#34;https://swaywm.org/&#34;&gt;Sway&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[1] [2] [3]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Virtual desktops (or workspaces, in the &lt;a href=&#34;https://i3wm.org/&#34;&gt;i3&lt;/a&gt;/sway terminology) enlarge the available screen estate.
By putting certain windows on well-known workspaces, one can quickly find them without cycling
through all the windows by &lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;Tab&lt;/kbd&gt;-ing (or &lt;a href=&#34;https://wanderingstan.com/wp-content/uploads/2009/07/alt-tab-flowcharts-labeled-2.png&#34;&gt;even&lt;/a&gt;&amp;hellip;).
For example, I always put a browser to workspace 1, an editor to workspace 2, and a shell to workspace 3.
This way I can always quickly jump to any of them, by pressing &lt;kbd&gt;Mod&lt;/kbd&gt;+&lt;kbd&gt;1/2/3&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;What happens if I have to work on two things (&lt;em&gt;foo&lt;/em&gt; and &lt;em&gt;bar&lt;/em&gt;) concurrently? I can open 3 more workspaces:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[1] [2] [3] [4] [5] [6]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now this is a bit unfortunate. If the workload is dynamic enough, I&amp;rsquo;ll have a hard time
quickly telling what is running where. It can be improved by renaming some workspaces, e.g:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ swaymsg rename workspace &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; to 1:foo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ swaymsg rename workspace &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; to 4:bar
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So it&amp;rsquo;ll look like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[1:foo] [2] [3] [4:bar] [5] [6]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While it is certainly better, it isn&amp;rsquo;t ideal. Muscle memory doesn&amp;rsquo;t help any more: while editing the second project
(workspace 5), pressing the usual &lt;kbd&gt;Mod&lt;/kbd&gt;+&lt;kbd&gt;3&lt;/kbd&gt; will jump to the shell of the first
project (workspace 3), not to workspace 6, that hosts the shell of the second project.&lt;/p&gt;
&lt;p&gt;Also, if I want to add a new workspace to the working set of &lt;em&gt;foo&lt;/em&gt;&amp;hellip; I can&amp;rsquo;t,
without shifting subsequent workspaces to the right first.&lt;/p&gt;
&lt;p&gt;Furthermore, pressing &lt;kbd&gt;Mod&lt;/kbd&gt;+&lt;kbd&gt;6&lt;/kbd&gt; with left hand only is not comfortable to me.&lt;/p&gt;
&lt;p&gt;The famous &lt;a href=&#34;https://github.com/tmux/tmux&#34;&gt;tmux&lt;/a&gt; terminal multiplexer offers a solution: it calls them &lt;em&gt;sessions&lt;/em&gt;.
Each session holds a distinct set of workspaces (windows, in the tmux terminology).
Sessions can be named and easily switched to. Only the workspaces of the selected session
are shown, and pressing the right combination selects the numbered workspace of the active session.
This is (almost) exactly what I wanted. Sway does not provide this functionality out of the box,
so I created some scripts to emulate it. Let&amp;rsquo;s see:&lt;/p&gt;
&lt;h2 id=&#34;scripts-to-emulate-sessions&#34;&gt;Scripts to emulate sessions&lt;/h2&gt;
&lt;p&gt;First, there&amp;rsquo;s &lt;code&gt;sway_rename_workspace.py&lt;/code&gt;, that simply wraps &lt;code&gt;swaymsg rename workspace&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Prompts for a name and renames the current workspace to it, ESC cancels.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# It preserves the number of the workspace - therefore the ordering.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ui_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prompt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;check_output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/dmenu&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-b&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-p&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prompt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;encoding&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ascii&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;focused_workspace_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;check_output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/swaymsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-t&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;get_workspaces&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loads&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;focused&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;num&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ui_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;rename workspace&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;num&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;focused_workspace_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;check_output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/swaymsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;rename&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;workspace&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;num&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It prompts for a name using &lt;a href=&#34;https://tools.suckless.org/dmenu/&#34;&gt;dmenu&lt;/a&gt;. Perhaps dmenu is not the most precise choice,
being an &lt;a href=&#34;https://www.x.org/wiki/&#34;&gt;X&lt;/a&gt; application, but it was already installed and perfectly matches my use-case.&lt;/p&gt;
&lt;p&gt;Second, there&amp;rsquo;s &lt;code&gt;sway_select_session.py&lt;/code&gt;, slightly more than a one-liner:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Prompts for a workspace name, ESC cancels.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# If a name is given, it looks for the selected workspace.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# If a workspace is found, it moves that workspace, and every&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# workspace to the right -- until a different named-workspace is found --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# to the beginning, and moves every other workspace to the end,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# starting at workspace number 10.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# If no workspace matches the given name, it will be created,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# and every other workspace will be moved to the end.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# It does not touch workspaces given in `protected_workspaces`.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# do not move these workspaces&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;protected_workspaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ui_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prompt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;selection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;check_output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/dmenu&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-b&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-p&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prompt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;selection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;encoding&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ascii&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;strip_before&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;selected_range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;:&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;renumber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;tag&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tag&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tag&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# select a session, find the range of workspaces that belongs to it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;check_output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/swaymsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-t&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;get_workspaces&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loads&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;sessions&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;session&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;sessions&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;selected_session&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ui_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;select session&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sessions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;selected_range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;selected_session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# move selected worspaces to si..., deselected workspaces to di...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;si&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;di&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;enumerate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;num&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;protected_workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;si&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;protected_workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;si&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;renumber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;si&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;si&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;di&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;protected_workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;di&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# first deselected workspace is unnamed, name it &amp;#34;main&amp;#34; to avoid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# merging it with the selected session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;di&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;:main&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;renumber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;di&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;di&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# move them to tmp names to avoid clashes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/swaymsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;rename&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;workspace&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;_&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# move them back&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/swaymsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;rename&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;workspace&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;_&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# finally, jump to the leading workspace if the selected session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# (this also creates the session if it is a new one)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;tag&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strip_before&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;selected_session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;protected_workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/swaymsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;--&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;workspace&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;--no-auto-back-and-forth&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tag&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So it prompts for a name (a session name), identifies the workspaces that belong to that session,
moves them to the beginning, and moves the others to the end. Let&amp;rsquo;s see an example. We start with:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[1:foo] [2] [3] [4:bar] [5] [6]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then when we select &amp;ldquo;4:bar&amp;rdquo;, we&amp;rsquo;ll end up with:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[1:bar] [2] [3] [10:foo] [11] [12]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;hellip;where &amp;ldquo;1:bar&amp;rdquo; was &amp;ldquo;4:bar&amp;rdquo;, &amp;ldquo;2&amp;rdquo; was &amp;ldquo;5&amp;rdquo;, &amp;ldquo;3&amp;rdquo; was &amp;ldquo;6&amp;rdquo;, &amp;ldquo;10:foo&amp;rdquo; was &amp;ldquo;1:foo&amp;rdquo;, &amp;ldquo;11&amp;rdquo; was &amp;ldquo;2&amp;rdquo; and &amp;ldquo;12&amp;rdquo; was &amp;ldquo;3&amp;rdquo;.
This new layout allows muscle memory to kick in, keeps frequently used workspace numbers small,
and also adds a gap between the first and the other sessions: if needed, workspace 4 can be quickly added to
the &lt;em&gt;bar&lt;/em&gt; session.&lt;/p&gt;
&lt;p&gt;A personal twist appears here: 1 and 9 are &lt;code&gt;protected_workspaces&lt;/code&gt;, virtually part
of every every session &amp;ndash; I never want to move them. I keep there communication tools,
a journal, a music player, etc.&lt;/p&gt;
&lt;p&gt;Finally, if you want to stop &lt;em&gt;sessioning&lt;/em&gt;, there&amp;rsquo;s &lt;code&gt;sway_flatten_sessions.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Assign each workspace the lowest number still available.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# do not move these workspaces&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;protected_workspaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;check_output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/swaymsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-t&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;get_workspaces&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loads&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;num&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;protected_workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;session&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ws&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;protected_workspaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rn&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;renames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;subprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/swaymsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;rename&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;workspace&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This simply renumbers the workspaces in order, putting them next to each other, removing any gap.
To tie them together, I&amp;rsquo;m using the following sway piece of config:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set $mode_session session [s]elect [r]ename [f]latten
bindsym $mod+s mode &amp;#34;$mode_session&amp;#34;
mode &amp;#34;$mode_session&amp;#34; {
    bindsym s exec --no-startup-id ~/bin/way_select_session.py, mode &amp;#34;default&amp;#34;
    bindsym r exec --no-startup-id ~/bin/way_rename_workspace.py, mode &amp;#34;default&amp;#34;
    bindsym f exec --no-startup-id ~/bin/way_flatten_sessions.py, mode &amp;#34;default&amp;#34;

    bindsym Escape mode &amp;#34;default&amp;#34;
    bindsym Return mode &amp;#34;default&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This allows very quick session renaming (&lt;kbd&gt;Mod&lt;/kbd&gt;+&lt;kbd&gt;s&lt;/kbd&gt;&lt;kbd&gt;r&lt;/kbd&gt;) and selection (&lt;kbd&gt;Mod&lt;/kbd&gt;+&lt;kbd&gt;s&lt;/kbd&gt;&lt;kbd&gt;s&lt;/kbd&gt;).
The presented code is also available on &lt;a href=&#34;https://gist.github.com/erenon/024679fecf849454f54d245537f3a783&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Summary of Readings</title>
      <link>http://erenon.hu/posts/2022-12-27-summary-of-readings/</link>
      <pubDate>Tue, 27 Dec 2022 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2022-12-27-summary-of-readings/</guid>
      <description>&lt;p&gt;A list of non-fiction books in no particular order I read recently,
with some recommendation. This post was inspired by the &lt;a href=&#34;https://eli.thegreenplace.net/tag/book-reviews&#34;&gt;similar posts&lt;/a&gt; of &lt;a href=&#34;https://eli.thegreenplace.net/&#34;&gt;Eli Bendersky&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;energy&#34;&gt;Energy&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/en/book/show/50878951-how-to-drive-a-nuclear-reactor&#34;&gt;How to Drive a Nuclear Reactor&lt;/a&gt;
by Colin Tucker.
A high level tour for laymans on how a specific kind of nuclear reactors (PWR) work.
Goes from the reactor, through the two circuits, to the turbine and the grid.
Deftly written, makes you believe you actually understand what&amp;rsquo;s going on,
but still highlights the enormous complexity of such an installation.
While reading, I recommend to keep a list of abbreviations, they come up frequently,
but only explained once.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/43700662-the-future-of-fusion-energy&#34;&gt;The Future Of Fusion Energy&lt;/a&gt;
by Jason Parisi, Justin Ball.
A long introduction to fusion energy, goals and challenges, prospects. The book covers the past,
present, and the expected future of the field (no, we&amp;rsquo;ll not expected to have commercial
fusion reactors at least until 2100, sorry). It attempts to explain things without mathematics.
The authors could use a bit fewer footnotes&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.
The second chapter of the book could stand on its own: a comprehensive, but accessible, list
of energy sources of the Earth.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;environment&#34;&gt;Environment&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/705418.The_Limits_to_Growth&#34;&gt;Limits To Growth&lt;/a&gt;
by Donella H. Meadows, Dennis L. Meadows, Jørgen Randers.
A long time ago, a group of scientists built a computer model to guess
how the apparently limitless growth of the economy will affect
the future of humankind. The results were published in 1972,
urging profound and immediate changes.
Not much change was made. The 30 year update of the book confirms
that we are on track to our predicted doom, and again proposes
reduction of consumption, waste, greed; a fair society and more birth control
as a solution. A depressing read, forecasting the untimely death of billions
in this century.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;software&#34;&gt;Software&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/52824295-the-making-of-prince-of-persia&#34;&gt;The Making of Prince of Persia&lt;/a&gt;
by Jordan Mechner.
Relevant journal entries verbatim, from the author of the famous computer game,
around 1985-1993. Shows how Jordan struggles between game development and screenwriting,
and the fascinating self-doubt of an extremely talented and professionally successful youth.
Easy to read, exciting, inspirational.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/17901833-the-making-of-karateka&#34;&gt;The Making of Karateka&lt;/a&gt;
by Jordan Mechner.
A prequel to &lt;em&gt;The Making of Prince of Persia&lt;/em&gt;, this time about a less well-known game, Karateka.
Similarly cute and easy to read. The amount and quality of output this young boy produces is fascinating.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/222146.Masters_of_Doom&#34;&gt;Masters of Doom&lt;/a&gt; by David Kushner.
A documentary on how the computer game developer company &lt;em&gt;id&lt;/em&gt; was founded and operated,
from Commander Keen through Doom to Quake I-II, mostly focusing on John Carmack and John Romero,
their different personalities and motivations &amp;ndash; without technical details.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;These (sometimes) (slightly) funny footnotes do not really add value, but break the flow of reading.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>Summary of Readings</title>
      <link>http://erenon.hu/posts/2022-09-08-summary-of-readings/</link>
      <pubDate>Thu, 08 Sep 2022 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2022-09-08-summary-of-readings/</guid>
      <description>&lt;p&gt;A non-exhaustive list of non-fiction books in no particular order I read recently,
with some recommendation. This post was inspired
by the &lt;a href=&#34;https://eli.thegreenplace.net/tag/book-reviews&#34;&gt;similar posts&lt;/a&gt; of &lt;a href=&#34;https://eli.thegreenplace.net/&#34;&gt;Eli Bendersky&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;engineering&#34;&gt;Engineering&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/1354758&#34;&gt;Kelly: More Than My Share of It All&lt;/a&gt; by Clarence &amp;ldquo;Kelly&amp;rdquo; Johnson.
The autobiography of a aeronautical engineer genius, the founder of the Skunk Works.
Concise and honest, inspirational.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/101438.Skunk_Works&#34;&gt;Skunk Works: A Personal Memoir of My Years at Lockheed&lt;/a&gt; by Ben Rich.
A story of the development the U2, Have Blue and F-117, the SR-71 Blackbird and more.
Filled with personal anecdotes, not too technical but still informational.
A tale of amazing engineering prowess.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/20697540-mars-rover-curiosity&#34;&gt;Mars Rover Curiosity: An Inside Account from Curiosity&amp;rsquo;s Chief Engineer&lt;/a&gt; by Rob Manning.
An other story of an elite engineering group, told by a lead engineer.
A super expensive project with tight deadlines, much excitement, and no ordinary problems.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/1101290.Dealers_of_Lightning&#34;&gt;Dealers of Lightning: Xerox PARC and the Dawn of the Computer Age&lt;/a&gt; by Michael Hiltzik.
The history of how the PARC research center was founded, the inventions that came out of it,
and how Xerox capitalized on this precious gem. Somewhat longer, chapters dedicated to distinct researchers,
management being the overarching cohesion.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/7090.The_Soul_of_a_New_Machine&#34;&gt;The Soul of a New Machine&lt;/a&gt; by Tracy Kidder.
A detailed account of how a particular computer was developed in the late 1970s in the U.S,
mostly by enthusiast, young professionals &amp;ndash; with a twist at the end.
The author is a master of his craft (journalism).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;software&#34;&gt;Software&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/53011383-unix&#34;&gt;UNIX: A History and a Memoir&lt;/a&gt; by Brian Kernighan.
A very interesting story of how the original Unix was invented - as seen by Brian,
and how now-familiar tools were developed alongside.
Recommended for the users of UNIX derivative systems, and tools like bash, make, awk, etc.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/160171.Just_for_Fun&#34;&gt;Just for Fun: The Story of an Accidental Revolutionary&lt;/a&gt;, by Linus Torvalds and David Diamond.
An easy, funny, part auto-biography book about Linus Torvalds, and the early years of Linux.
Not too technical, but still interesting and insightful.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/39996759&#34;&gt;A Philosophy of Software Design&lt;/a&gt; by John Ousterhout.
An attempt to present abstract software design ideas, generic rules?
My takeaway: shallow abstractions are wrong. Otherwise, not so much, unfortunately.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;history&#34;&gt;History&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/10534.The_Art_of_War&#34;&gt;The Art of War&lt;/a&gt; by Sun Tzu.
Written in 5th century BC, a collection of military maxims, commented by later interpreters,
then again translated and commented by many, including Lionel Giles.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/131305.Fermat_s_Last_Theorem&#34;&gt;Fermat&amp;rsquo;s Last Theorem&lt;/a&gt; by Simon Singh.
The author tells how the theorem was finally solved, starting with the ancient greeks and ending up
at modern mathematics. Unfortunately, to remain comprehensible for casual readers,
there are not much mathematical details. I think this is not the authors fault &amp;ndash; modern mathematics simply
transcended most of us.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;social-and-psychology&#34;&gt;Social and Psychology&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/11468377-thinking-fast-and-slow&#34;&gt;Thinking, Fast and Slow&lt;/a&gt; by Daniel Kahneman.
A collection of important psychological phenomena, that helps to understand why do we feel, think, decide, and get things wrong the way we do.
Very long, but extremely insightful.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/39088545-the-formula&#34;&gt;The Formula: The Universal Laws of Success&lt;/a&gt; by Albert-László Barabási.
An analysis of how valuation of achievements at the highest level does (not) work.
An interesting, data driven, simple message, in a bit lengthy and sensationalist form.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;money-and-economy&#34;&gt;Money and Economy&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/25733505-a-man-for-all-markets&#34;&gt;A Man for All Markets&lt;/a&gt; by Edward O. Thorp.
An autobiography telling a story of how a poor, but super intelligent boy discovers the world,
and denies the &amp;ldquo;it cannot be done&amp;rdquo; sentiment. From university through Las Vegas to Wall Street.
Recommended for aspiring quants and hedge fund operators.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/24724602&#34;&gt;Flash Boys: A Wall Street Revolt&lt;/a&gt; by Michael Lewis.
A fun, easy to read, short story about high frequency trading in the US, based on actual events.
Unfortunately, some argue it is incorrect, factually wrong, written in bad faith, or simply an ad of a specific exchange.
There&amp;rsquo;s even a rebuttal book. Still, it is a fun read.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;parenting&#34;&gt;Parenting&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/769016&#34;&gt;How to Talk So Kids Will Listen &amp;amp; Listen So Kids Will Talk&lt;/a&gt; by Adele Faber, Elaine Mazlish.
A great book with practical tips on &lt;em&gt;difficult&lt;/em&gt; but everyday situations when dealing with children.
Half of the book is tips - just the right amount, half of it is case-studies.
The presented techniques do work. Highly recommended for parents with small children.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/213186.Raising_An_Emotionally_Intelligent_Child&#34;&gt;Raising An Emotionally Intelligent Child&lt;/a&gt; by John Gottman.
The author argues that understanding, reflecting and labeling the child&amp;rsquo;s emotions is beneficial in may ways.
True. The book information/letter ratio is a bit low.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>The Unreasonable Effectiveness of Doing Nothing</title>
      <link>http://erenon.hu/posts/2022-08-08-the-unreasonable-effectiveness/</link>
      <pubDate>Thu, 08 Sep 2022 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2022-08-08-the-unreasonable-effectiveness/</guid>
      <description></description>
    </item>
    <item>
      <title>Static storage shared between compilation units</title>
      <link>http://erenon.hu/posts/2022-03-30-shared-static-storage/</link>
      <pubDate>Wed, 30 Mar 2022 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2022-03-30-shared-static-storage/</guid>
      <description>&lt;p&gt;Suppose you want to create a static container compilation time,
that can be inserted into by different translation units,
and can be iterated runtime. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// a.cpp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;STORE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;names&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// b.cpp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;STORE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;names&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;bob&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// c.cpp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;STORE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;names&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;charlie&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;names&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cout&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// prints alice,bob,charlie, in unspecified order
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I know of no way in standard C or C++ that&amp;rsquo;d allow this.
However, with platform specific tools, it is possible
to implement &lt;code&gt;STORE&lt;/code&gt; and &lt;code&gt;fetch&lt;/code&gt; - this post describes how.
The complete source code is available in the &lt;a href=&#34;https://github.com/erenon/static-storage&#34;&gt;companion repository&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;theory&#34;&gt;Theory&lt;/h1&gt;
&lt;p&gt;The following discussion is constrained to the linux, macOS
and windows platforms. Each platform in scope has a mainstream
execution format, that defines how an executable binary should look like.
&lt;a href=&#34;https://en.wikipedia.org/wiki/Executable_and_Linkable_Format&#34;&gt;ELF&lt;/a&gt; for linux, &lt;a href=&#34;https://en.wikipedia.org/wiki/Mach-O&#34;&gt;Mach-O&lt;/a&gt; for macOS, &lt;a href=&#34;https://en.wikipedia.org/wiki/Portable_Executable&#34;&gt;PE&lt;/a&gt; for windows.
Each of these formats define headers (metadata) and sections (data).
Sections can contain e.g: program code, exported symbol information debug symbols,
or even arbitrary data. We are going to create and populate a custom section
(&lt;code&gt;STORE&lt;/code&gt;), and read to contents of that section runtime (&lt;code&gt;fetch&lt;/code&gt;).&lt;/p&gt;
&lt;h1 id=&#34;put-data-into-custom-sections&#34;&gt;Put Data into Custom Sections&lt;/h1&gt;
&lt;p&gt;On each platform, a different construct can be used
to put a given static variable into a specific section:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;linux/ELF: &lt;a href=&#34;https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-section-variable-attribute&#34;&gt;section attribute&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define STORE(name, str) {               \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  __attribute__((section(name), used))   \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  static constexpr const char s[] = str; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;used&lt;/code&gt; attribute instructs the compiler not to remove &lt;code&gt;s&lt;/code&gt;,
even if it appears unused. The curly braces around the definition
prevent name conflicts (e.g: if there are multiple &lt;code&gt;STORE&lt;/code&gt; invocations
in the same scope).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;macOS/Mach-O: sections are grouped into segments,
the segment name must be also specified to the attribute:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define STORE(name, str) {                               \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  __attribute__((&amp;#34;__DATA_CONST,&amp;#34; section(name), used))   \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  static constexpr const char s[] = str;                 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;windows/PE: &lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/cpp/allocate&#34;&gt;allocate specifier&lt;/a&gt;. I couldn&amp;rsquo;t find the
equivalent of the &lt;code&gt;used&lt;/code&gt; attribute, so you have to convince
the compiler otherwise, that the variable is used (e.g: by
actually using it) - not shown here. Also each section need to be initialized first
with a &lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/preprocessor/const-seg&#34;&gt;const_seg&lt;/a&gt; pragma. The pragma must be in global/namespace scope,
it can&amp;rsquo;t be added to the &lt;code&gt;STORE&lt;/code&gt; macro.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define STORE(name, str) {               \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  __declspec(allocate(name))             \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  static constexpr const char s[] = str; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#pragma const_seg(&amp;#34;names&amp;#34;) &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// user code, required once for each segment/translation unit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This definition makes it possible to use &lt;code&gt;STORE&lt;/code&gt; inside functions.
Similarly, a &lt;code&gt;STORE_GLOBAL&lt;/code&gt; macro can be created, that works in global/namespace scope.&lt;/p&gt;
&lt;p&gt;One might be tempted to prefix the custom section name with a leading dot (ELF/PE)
or double underscore (Mach-O) to match conventions of the well known sections
(e.g: &lt;code&gt;.text&lt;/code&gt; or &lt;code&gt;.rodata&lt;/code&gt;). However, that&amp;rsquo;d be incorrect: the leading decoration
is there to separate platform specified sections from user defined custom sections.
For example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Section names with a dot (.) prefix are reserved for the system, although applications may use these sections if their existing meanings are satisfactory.
Applications may use names without the prefix to avoid conflicts with system sections.
&lt;br&gt;&lt;a href=&#34;https://refspecs.linuxfoundation.org/elf/elf.pdf&#34;&gt;Executable and Linkable Format&lt;/a&gt;, 1-16&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To verify that &lt;code&gt;STORE&lt;/code&gt; works, create a test binary that invokes it,
and run a platform specific tool that dumps the contents of a specified section
of the specified binary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;linux/ELF: &lt;a href=&#34;https://man7.org/linux/man-pages/man1/readelf.1.html&#34;&gt;readelf&lt;/a&gt;: &lt;code&gt;readelf -x names binary&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS/Mach-O: &lt;a href=&#34;https://www.manpagez.com/man/1/otool/&#34;&gt;otool&lt;/a&gt;: &lt;code&gt;otool -s __DATA_CONST names binary -V&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;windows/PE: &lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/build/reference/dumpbin-command-line&#34;&gt;dumpbin&lt;/a&gt;: &lt;code&gt;dumpbin.exe /SECTION:names binary /RAWDATA&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The strings we invoked &lt;code&gt;STORE&lt;/code&gt; with should appear in the output of these commands.&lt;/p&gt;
&lt;h1 id=&#34;gcc-complications&#34;&gt;GCC Complications&lt;/h1&gt;
&lt;p&gt;All trivial so far. Unfortunately, if &lt;code&gt;STORE&lt;/code&gt; is used in functions
with different linkage (e.g: in a normal function and in an inline or template function),
GCC will produce a section type conflict.
The situation is analyzed in a &lt;a href=&#34;https://stackoverflow.com/questions/35091862/inline-static-data-causes-a-section-type-conflict&#34;&gt;stackoverflow question&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The issue stems from the fact that GCC must give special care to
some function local static variables:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Function-local static objects in all definitions of the same inline function
(which may be implicitly inline) all refer to the same object defined in one
translation unit, as long as the function has external linkage. &lt;br&gt;
&lt;a href=&#34;https://en.cppreference.com/w/cpp/language/storage_duration&#34;&gt;cppreference&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To ensure uniqueness, those variables are put into a comdat section,
other variables are not, and GCC is unable to resolve this situation.
To avoid GCC messing up the section type, it has to be specified explicitly.
Unfortunately, it cannot be specified using the section attribute,
and I found no way to convince GCC to forget about the standard requirements,
so inline assembly needs to be used:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define STORE(name, str)                              \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  __asm__ (                                           \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    &amp;#34;.pushsection \&amp;#34;&amp;#34; name &amp;#34;\&amp;#34;,\&amp;#34;?\&amp;#34;,@progbits&amp;#34; &amp;#34;\n&amp;#34;  \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    &amp;#34;.asciz \&amp;#34;&amp;#34; str &amp;#34;\&amp;#34;&amp;#34;                        &amp;#34;\n&amp;#34;  \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    &amp;#34;.popsection&amp;#34;                               &amp;#34;\n&amp;#34;  \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;a href=&#34;https://sourceware.org/binutils/docs/as/PushSection.html&#34;&gt;.pushsection&lt;/a&gt; and &lt;a href=&#34;https://sourceware.org/binutils/docs/as/Asciz.html&#34;&gt;.asciz&lt;/a&gt; assembler pseudo directives are used
to put a string value into a specific section, without actually creating
a variable that is restricted by C++ rules. The quoting mess is a bit
scary, but there&amp;rsquo;s nothing too complicated here.&lt;/p&gt;
&lt;h1 id=&#34;get-data-from-custom-sections&#34;&gt;Get data from custom sections&lt;/h1&gt;
&lt;p&gt;We have a &lt;code&gt;STORE&lt;/code&gt; macro, that takes two string literals,
and puts one into the section indicated by the other.
Now we only need to extract the data runtime that was inserted compile time.
To do that, we just need to replicate a small portion of what
readelf/otool/dumpbin does. The implementation is platform specific,
let&amp;rsquo;s see the linux/ELF case for illustration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Get every string STOREd in `name` in the binary at `path`.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_binary_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// Create a random access view of `path` (error handling on read included)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryMappedFile&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// Read the ELF header.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// ElfW(Ehdr) expands to Efl32_Ehdr or Elf64_Ehdr,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// according to the current platform.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;ElfW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Ehdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ehdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ehdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ehdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// Read the section headers from the location
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// indicated by the ELF header.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ElfW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Shdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shdrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ehdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e_shnum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ehdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e_shoff&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shdrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ElfW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Shdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shdrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// The section headers do not contain their name,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// only an offset into the section header string table.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// Locate this string table:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ElfW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string_table_offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shdrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ehdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e_shstrndx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// For each section
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ElfW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Shdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;shdr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shdrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Get the section name from the string table
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string_view&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;section_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string_table_offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;c1&#34;&gt;// This is the section we are looking for.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// Get all the strings.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;      &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ElfW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Xword&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string_view&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh_offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;emplace_back&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;c1&#34;&gt;// no break or early return here.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// section names do not need to be unique.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The full source is available in the &lt;a href=&#34;https://github.com/erenon/static-storage&#34;&gt;companion repository&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;use-case&#34;&gt;Use case&lt;/h1&gt;
&lt;p&gt;This looks cool (?), but why? The &lt;a href=&#34;https://github.com/morganstanley/binlog/blob/hiperf/include/binlog/create_source.hpp&#34;&gt;binlog&lt;/a&gt; high performance log library
uses a more advanced variation of this technique to store the metadata
of log invocations in the binary, avoiding one-time setup for each
invocation that&amp;rsquo;d be otherwise necessary.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Preprocessor foreach compile time</title>
      <link>http://erenon.hu/posts/2022-03-07-preprocessor_foreach_compile_time/</link>
      <pubDate>Mon, 07 Mar 2022 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2022-03-07-preprocessor_foreach_compile_time/</guid>
      <description>&lt;p&gt;Assume you want to create a preprocessor macro that takes a macro &lt;code&gt;F&lt;/code&gt;
and a variable number of arguments. When expanded, it invokes &lt;code&gt;F&lt;/code&gt; with each argument:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FOREACH(F, ...) &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;FOREACH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// expands to: G(a) G(b) G(c)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Such a macro can be useful for example for a log library.
There are several ways to implement it.
Here, we compare three different implementations,
with regard to complexity, ease of change, and compile time.&lt;/p&gt;
&lt;p&gt;The complete source code used for testing is available at &lt;a href=&#34;https://github.com/erenon/pp-foreach-comparison&#34;&gt;erenon/pp-foreach-comparison&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;contenders&#34;&gt;Contenders&lt;/h2&gt;
&lt;p&gt;The first implementation is taken from &lt;strong&gt;&lt;a href=&#34;http://binlog.org&#34;&gt;binlog&lt;/a&gt;&lt;/strong&gt;.
It consists of a macro that counts the number of arguments,
and &lt;code&gt;N&lt;/code&gt; foreach macros, one for each arity.
It is generated by a python script, &lt;code&gt;binlog.py&lt;/code&gt;.
&lt;code&gt;N&lt;/code&gt; is a parameter of the generator script, here, &lt;code&gt;N=512&lt;/code&gt;.
This version is slightly different from the original one used in binlog,
to match the capabilities of the other contenders, for a fair comparison.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;N&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;512&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#define CAT(a, b) CAT_I(a, b)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#define CAT_I(a, b) a ## b&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#define COUNT(...) COUNT_I(__VA_ARGS__&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;reversed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;N&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;,&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#define COUNT_I(&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;N&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;a&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;,&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;...) a&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;N&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#define FOREACH(F, ...) CAT(FOREACH_, COUNT(__VA_ARGS__)) (F, __VA_ARGS__)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;N&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#define FOREACH_&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;(F&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;j&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;,a&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;j&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; F(a&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For &lt;code&gt;N=3&lt;/code&gt;, it looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define CAT(a, b) CAT_I(a, b)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define CAT_I(a, b) a ## b
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define COUNT(...) COUNT_I(__VA_ARGS__,2,1,0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define COUNT_I(a1,a2,a3,...) a3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FOREACH(F, ...) CAT(FOREACH_, COUNT(__VA_ARGS__)) (F, __VA_ARGS__)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FOREACH_1(F,a0) F(a0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FOREACH_2(F,a0,a1) F(a0) F(a1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FOREACH_3(F,a0,a1,a2) F(a0) F(a1) F(a2)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: I&amp;rsquo;m the author of binlog.&lt;/p&gt;
&lt;p&gt;The second implementation is taken from &lt;strong&gt;&lt;a href=&#34;https://github.com/swansontec/map-macro&#34;&gt;swansontec&lt;/a&gt;&lt;/strong&gt;.
It is a complex, but documented solution.
It works with up to &lt;code&gt;N=364&lt;/code&gt; arguments. The idea can be expanded to
support more arguments. The entry point of the solution
is renamed to &lt;code&gt;FOREACH&lt;/code&gt; in the snippet below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* Created by William Swanson in 2012. Public Domain. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define EVAL0(...) __VA_ARGS__
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define EVAL(...)  EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_END(...)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_OUT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_COMMA ,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_GET_END2() 0, MAP_END
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_GET_END1(...) MAP_GET_END2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_GET_END(...) MAP_GET_END1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_NEXT0(test, next, ...) next MAP_OUT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_NEXT(test, next)  MAP_NEXT1(MAP_GET_END test, next)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_LIST_NEXT(test, next)  MAP_LIST_NEXT1(MAP_GET_END test, next)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Applies the function macro `f` to each of the remaining parameters.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FOREACH(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Applies the function macro `f` to each of the remaining parameters and
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * inserts commas between the results.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The third implementation is taken from &lt;strong&gt;&lt;a href=&#34;https://github.com/morganstanley/hobbes/&#34;&gt;hobbes&lt;/a&gt;&lt;/strong&gt;.
It is a complex solution.
It works with up to &lt;code&gt;N=513&lt;/code&gt; arguments, probably it can be changed
to support more. The entry point of the solution
is renamed to &lt;code&gt;FOREACH&lt;/code&gt; in the snippet below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Copyright 2016 Morgan Stanley
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;); you may not use this file except in compliance with the License. You may obtain a copy of the License at
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// http://www.apache.org/licenses/LICENSE-2.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// https://github.com/morganstanley/hobbes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_FIRST(a, ...) a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SECOND(a, b, ...) b
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_CAT(a,b) a ## b
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_IS_PROBE(...) HOBPP_SECOND(__VA_ARGS__, 0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_PROBE() ~, 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_NOT(x) HOBPP_IS_PROBE(HOBPP_CAT(HOBPP_SNOT_, x))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SNOT_0 HOBPP_PROBE()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_BOOL(x) HOBPP_NOT(HOBPP_NOT(x))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_IF_ELSE(condition) HOBPP_SIF_ELSE(HOBPP_BOOL(condition))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SIF_ELSE(condition) HOBPP_CAT(HOBPP_SIF_, condition)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SIF_1(...) __VA_ARGS__ HOBPP_SIF_1_ELSE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SIF_0(...)             HOBPP_SIF_0_ELSE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SIF_1_ELSE(...)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SIF_0_ELSE(...) __VA_ARGS__
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EMPTY()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL(...) HOBPP_EVAL256(__VA_ARGS__)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL256(...) HOBPP_EVAL128(HOBPP_EVAL128(__VA_ARGS__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL128(...) HOBPP_EVAL64(HOBPP_EVAL64(__VA_ARGS__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL64(...) HOBPP_EVAL32(HOBPP_EVAL32(__VA_ARGS__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL32(...) HOBPP_EVAL16(HOBPP_EVAL16(__VA_ARGS__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL16(...) HOBPP_EVAL8(HOBPP_EVAL8(__VA_ARGS__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL8(...) HOBPP_EVAL4(HOBPP_EVAL4(__VA_ARGS__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL4(...) HOBPP_EVAL2(HOBPP_EVAL2(__VA_ARGS__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL2(...) HOBPP_EVAL1(HOBPP_EVAL1(__VA_ARGS__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_EVAL1(...) __VA_ARGS__
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_DEFER2(m) m HOBPP_EMPTY HOBPP_EMPTY()()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_HAS_PARGS(...) HOBPP_BOOL(HOBPP_FIRST(HOBPP_SEOAP_ __VA_ARGS__)())
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SEOAP_(...) HOBPP_BOOL(HOBPP_FIRST(HOBPP_SEOA_ __VA_ARGS__)())
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SEOA_() 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FOREACH(f, VS...) HOBPP_EVAL(HOBPP_MAPP(f, VS))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_MAPP(f, H, T...)        \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  f(H)                                \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  HOBPP_IF_ELSE(HOBPP_HAS_PARGS(T))(  \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    HOBPP_DEFER2(HOBPP_SMAPP)()(f, T) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  )(                                  \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;  )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define HOBPP_SMAPP() HOBPP_MAPP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;compile-time-comparison&#34;&gt;Compile Time Comparison&lt;/h2&gt;
&lt;p&gt;To measure the time it takes to preprocess a solution,
for each solution, a source file is generated, that includes the implementation,
and invokes &lt;code&gt;FOREACH&lt;/code&gt; with &lt;code&gt;M&lt;/code&gt; arguments, 32 times. Then the time the compiler spends
in user space, while preprocessing the file is measured.
Finally, measured time is plotted against the number of arguments:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
  &lt;img src=&#34;http://erenon.hu/img/gcc.svg&#34; alt=&#34;Plot showing how much time different solutions take to preprocess with gcc, as the number of arguments increases&#34;&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&#34;http://erenon.hu/img/clang.svg&#34; alt=&#34;Plot showing how much time different solutions take to preprocess with clang, as the number of arguments increases&#34;&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The measurement was done using gcc 11.1.0 and clang 13.0.0, on Linux 5.16,
running on a x86_64 AMD machine.
From the plots we can infer that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Preprocessing binlog takes a small, constant amount of time,
regardless the number of &lt;code&gt;FOREACH&lt;/code&gt; arguments.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The preprocessing time of swansontec and hobbes linearly
increases as the number of arguments increases. The hobbes solution
is roughly two times slower than swansontec.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There is no meaningful case where swansontec or hobbes would be faster than binlog.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;clang is almost three times slower to preprocess these files, than gcc.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;implementation-comparison&#34;&gt;Implementation Comparison&lt;/h2&gt;
&lt;p&gt;The three solutions are also different implementation wise.
I&amp;rsquo;m giving here a comparison along a mix of subjective and objective axes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;&lt;/th&gt;
&lt;th style=&#34;text-align:center&#34;&gt;binlog&lt;/th&gt;
&lt;th style=&#34;text-align:center&#34;&gt;swansontec&lt;/th&gt;
&lt;th style=&#34;text-align:center&#34;&gt;hobbes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Complexity&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;low&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;high&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Extensibility&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;easy&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;difficult&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;difficult&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Documentation&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;no&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;yes&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;File Size (N&amp;lt;=512)&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;1.6 MB&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;2 kB&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;2 kB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Generated Code&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;yes&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;no&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;License&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;Apache 2&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;Public Domain&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;Apache 2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;By &lt;em&gt;Complexity&lt;/em&gt;, I mean the effort required to understand and verify the correctness of the implementation.
By &lt;em&gt;Extensibility&lt;/em&gt;, I mean the effort required to change the behavior of the implementation,
e.g: support 0 arguments, pass along a constant data value, increase &lt;code&gt;N&lt;/code&gt;, inject a token between &lt;code&gt;F&lt;/code&gt; calls, etc.&lt;/p&gt;
&lt;h2 id=&#34;verdict&#34;&gt;Verdict&lt;/h2&gt;
&lt;p&gt;Interestingly, less code doesn&amp;rsquo;t mean faster to compile (preprocess) code.
Despite that the generated source code of the binlog solution is almost a 1000 times bigger,
it is still favourable compile time performance wise.
Furthermore, the straightforward, simple solution is easier to understand and modify.
Therefore, if code generation or having large source files is not an issue,
I see no reason to use a more complicated solution.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Snapcast on Demand</title>
      <link>http://erenon.hu/posts/2022-02-21-snapcast-on-demand/</link>
      <pubDate>Mon, 21 Feb 2022 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2022-02-21-snapcast-on-demand/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/badaix/snapcast&#34;&gt;Snapcast&lt;/a&gt; is a wonderful piece of software: a multiroom client-server audio
player, where all clients are time synchronized with the server to play
perfectly synced audio. It allows you to play the same music
in different rooms, at the same time, using multiple devices.&lt;/p&gt;
&lt;p&gt;Normally, I listen to music on my desktop. Sometimes, however,
I&amp;rsquo;d like to broadcast music to another room. In that room, there&amp;rsquo;s
a raspberry pi connected to a set of speakers.
I wanted to make the switch between local and multiroom playback
as easy as possible. This post describes my approach.&lt;/p&gt;
&lt;p&gt;Normally, my &lt;a href=&#34;https://cmus.github.io/&#34;&gt;music player&lt;/a&gt; uses the pulse-audio interface
to interact with the audio system (PipeWire, currently) that uses
ALSA to talk to the actual device:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cmus -&amp;gt; pulse -&amp;gt; pipewire -&amp;gt; alsa -&amp;gt; device
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We are going to add a virtual sink to pulse audio, a file sink,
where the Snapcast server will pick up the music from:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cmus -&amp;gt; pulse -&amp;gt; file -&amp;gt; snapcast server -&amp;gt; snapcast client -&amp;gt; alsa -&amp;gt; device
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To this chain, we can add multiple snapcast clients, to achieve a multiroom sound system.
It is important that the snapcast client should not use the pulse audio interface,
to avoid infinite loops. Read more about these pieces in this &lt;a href=&#34;https://github.com/badaix/snapcast/issues/821#issuecomment-789538906&#34;&gt;post from badaix&lt;/a&gt;.
By changing the active pulse sink, we can switch between local and multiroom audio.
The advantage of this setup is that it is player agnostic: it works with
every music player that can talk to pulse, and requires no additional player configuration.&lt;/p&gt;
&lt;p&gt;Theory aside, let&amp;rsquo;s get to work.
First, build snapcast from &lt;a href=&#34;https://aur.archlinux.org/packages/snapcast&#34;&gt;AUR&lt;/a&gt;, with a change to minimize dependencies:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;diff --git a/PKGBUILD b/PKGBUILD
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;index e5033a8..5efe228 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;--- a/PKGBUILD
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+++ b/PKGBUILD
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;@@ -7,7 +7,7 @@ pkgdesc=&amp;#34;Synchronous multi-room audio player&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt; arch=(&amp;#39;x86_64&amp;#39; &amp;#39;armv6h&amp;#39; &amp;#39;armv7h&amp;#39; &amp;#39;aarch64&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; url=&amp;#34;https://github.com/badaix/snapcast&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; license=(&amp;#39;GPL&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-depends=(alsa-lib avahi libvorbis flac opus expat libsoxr libpulse)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+depends=(alsa-lib)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt; makedepends=(cmake alsa-utils boost)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; install=&amp;#34;snapcast.install&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; backup=(&amp;#39;etc/default/snapserver&amp;#39; &amp;#39;etc/default/snapclient&amp;#39; &amp;#39;etc/snapserver.conf&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;@@ -25,6 +25,12 @@ build() {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;     cmake -B build -S . \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           -DCMAKE_BUILD_TYPE=None \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           -DCMAKE_INSTALL_PREFIX=/usr \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+          -DBUILD_WITH_FLAC=Off \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+          -DBUILD_WITH_VORBIS=Off \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+          -DBUILD_WITH_TREMOR=Off \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+          -DBUILD_WITH_OPUS=Off \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+          -DBUILD_WITH_AVAHI=Off \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+          -DBUILD_WITH_EXPAT=Off \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;           -Wno-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     make -C build
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create systemd units:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-SYSTEMD&#34; data-lang=&#34;SYSTEMD&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# .config/systemd/user/snapserver.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Snapcast server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Documentation&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;man:snapserver(1)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;After&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;network-online.target time-sync.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ExecStart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;/usr/bin/snapserver --logging.sink=system --server.datadir=/tmp/ --stream.codec=pcm --http.enabled=false --tcp.enabled=false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Restart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;on-failure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-SYSTEMD&#34; data-lang=&#34;SYSTEMD&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# .config/systemd/user/snapclient.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Snapcast client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Documentation&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;man:snapclient(1)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;After&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;network-online.target time-sync.target sound.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ExecStart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;/usr/bin/snapclient --logsink=system -h my_desktop_hostname&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Restart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;on-failure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Copy the snapclient service file to the raspberry pi.
To allow running user services via ssh, &lt;a href=&#34;https://bbs.archlinux.org/viewtopic.php?id=232424&#34;&gt;enable PAM&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# /etc/ssh/sshd_config
UsePAM yes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add the virtual pulse audio sink:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# .config/pulse/default.pa
load-module module-pipe-sink file=/tmp/snapfifo sink_name=Snapcast format=s16le rate=48000
set-default-sink alsa_output.pci-0000_00_14.2.analog-stereo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Create a script that switches to the virtual sink and starts the required services,
then undoes everything on exit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; -xeu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LOCAL_SINK&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;pactl get-default-sink&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;trap&lt;/span&gt; cleanup EXIT INT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cleanup&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ssh my_remote_host &lt;span class=&#34;s1&#34;&gt;&amp;#39;systemctl --user stop snapclient.service&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  systemctl stop --user snapserver.service snapclient.service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  pactl set-default-sink &lt;span class=&#34;nv&#34;&gt;$LOCAL_SINK&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl start --user snapserver.service snapclient.service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh my_remote_host &lt;span class=&#34;s1&#34;&gt;&amp;#39;systemctl --user start snapclient.service&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pactl set-default-sink Snapcast
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I decided not to run snapclient on the pi all the time:
if the server is down (i.e: I&amp;rsquo;m not using it, or the desktop is powered off),
it tries to reconnect every second, as expected, and I considered that a waste of resources.&lt;/p&gt;
&lt;p&gt;To avoid systemd stopping the remote service after ssh disconnects,
on the remote machine, do:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo loginctl enable-linger &lt;span class=&#34;nv&#34;&gt;$USER&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    <item>
      <title>Review of Thriving in a Crowded and Changing World</title>
      <link>http://erenon.hu/posts/cpp_hopl_comments/</link>
      <pubDate>Sun, 09 Jan 2022 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/cpp_hopl_comments/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://dl.acm.org/doi/pdf/10.1145/3386320&#34;&gt;Thriving in a Crowded and Changing World&lt;/a&gt; is a bulky paper written by
C++ author Bjarne Stroustrup for the &lt;em&gt;History of Programming Languages&lt;/em&gt;
ACM conference. I collected a few quotes below that I found interesting.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I wanted that tool to write a version of the Unix kernel that could work on
multiple processors connected through a local area network or a shared memory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What happened to that vision?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the late 1980s, the power of computers increased dramatically, I added Templates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This solved the too much computer power issue for a very long time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;iostreams &amp;ndash; an elaboration by Jerry Schwartz and the standards committee of my simple 1984
streams library to handle a wide variety of character types, locales, and buffering strategies.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I wonder what he means by &amp;ldquo;elaboration&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I was highly amused to find that representatives from Intel and IBM effectively
vetoed that idea by pointing out that by adopting the Java memory model for C++
we would slow down all JVMs by a factor of at least two. Consequently, to
preserve the performance of Java, we had to adopt a far more complex model for
C++. Ironically and predictably, C++ was then criticized for having a more
complicated memory model than Java.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately, I couldn&amp;rsquo;t find a reference for that.
I admire tough that the committee has such high ethics.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is not just &amp;ldquo;design by committee;&amp;rdquo; it is &amp;ldquo;design by a confederation of committees.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I consider the threads-and-locks level of concurrency the worst model for application
use of concurrency&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Why did we require that programmers should use &lt;code&gt;constexpr&lt;/code&gt; to mark functions that can be
executed at compile time? In principle, the compiler can figure out what can be computed at
compile time, but without an annotation, users would be at the mercy of variations in cleverness of
compilers and a compiler would need to keep bodies of all functions around &amp;ldquo;forever&amp;rdquo; in case they
were needed for constant expression evaluation&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I always wondered, didn&amp;rsquo;t know that. Unfortunately, I still do not really understand why.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Unfortunately, beginners flock to examine the most horrendously specialized
code and take great pride in explaining it (often erroneously) to others.
Bloggers and speakers enhance their reputations by showing off hair-raising
examples. This is a major source of C++’s reputation for complexity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don&amp;rsquo;t get what&amp;rsquo;s wrong with &lt;code&gt;using swallow = int[]; (void)swallow{0, (f(t), int{})...};&lt;/code&gt;.
Rolls off the tongue.&lt;/p&gt;
&lt;p&gt;On exception specifications:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As I feared, that led to maintenance problems, run-time overhead as an
exception was repeatedly checked along the unwinding path, and bloated source
code. For C++11, exception specifications were deprecated and for C++17, we
finally removed them (unanimously).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Thank you guys for using &lt;code&gt;using namespace std;&lt;/code&gt;, forcing the committee to chose
the technically correct names.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;They are called unordered (e.g., &lt;code&gt;unordered_map&lt;/code&gt;) to distinguish them from
the older, ordered, standard containers (e.g., &lt;code&gt;map&lt;/code&gt;) and because the obvious
names (e.g., &lt;code&gt;hash_map&lt;/code&gt;) had been used extensively in other libraries before
C++11.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On digit separators:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many, including me, argued for using the underscore as a separator (as in
several other languages). Unfortunately, people were using the underscore
as part of user-defined literal suffixes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Past forced mistakes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The prefix &lt;code&gt;template&amp;lt;...&amp;gt;&lt;/code&gt; syntax was not my first choice when I designed
templates. It was forced upon me by people worried that templates would be
misused by less competent programmers, leading to confusion and errors. The
heavy syntax for exception handling, &lt;code&gt;try { ... } catch( ... ) { ... }&lt;/code&gt;, was a
similar story.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The bashing of C++17 is a recurring theme:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have not been able to identify any unifying themes for the C++17 features. These features
look to be merely a set of &amp;ldquo;bright ideas&amp;rdquo; thrown into the language and standard library as voting
majorities could be found.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now I understand why:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;auto {x ,y ,z} = f (); // proposed
auto [x ,y ,z] = f (); // accepted
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;This is a minor change, but I think a mistake.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Regarding optional/variant/any:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I see these three variants of the idea of a discriminated union as a stop-gap measure. Functional-
programming-style pattern matching is a far more elegant, general, and
potentially more efficient solution to the problems with unions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I consider operator dot a prime example of the members of the committee lacking
a shared view of what C++ is and what it should become. Thirty years, six proposals, many
discussions, much design and implementation work, and we have nothing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;In retrospect, I don’t think that the object-oriented notation (e.g., x.f(y)) should ever have
been introduced. The traditional mathematical notation f(x,y) is sufficient. As a side benefit, the
mathematical notation would naturally have given us multi-methods, thereby saving us from the
visitor pattern workaround.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Regarding the spaceship operator &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So, instead of a facility that made trivial examples work as expected for novices, we got an
elaborate facility that allows experts to carefully craft subtle comparisons.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Expectations:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Coroutines - should be very fast and simple.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At this point in the paper, you just know the final result:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a complete explanation of the proposals
written, presented, and discussed is beyond the scope of this paper. Here, I
present only an outline. There are simply too many complex details for anything
else; the papers alone run into many hundreds of pages&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The conclusion was that it might be
possible to get the best of both approaches, but that would require serious study. That study took
years, yielded no clear results, and in the meantime more proposals emerged.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How dare you, Dennis:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;At the time, I heard the priceless comment: &amp;ldquo;Dennis isn’t a C
expert; he never comes to the meetings&amp;rdquo;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The only thing that increases faster than hardware performance is human expectation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don&amp;rsquo;t think this is true. The perceived latency of computers just got worse
in general in the past decades. I blame bloated software.
Humans expect a fluent user interface.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Nextcloud on RPi4 with Arch Linux</title>
      <link>http://erenon.hu/posts/2021-10-12-nextcloud-on-rpi/</link>
      <pubDate>Tue, 12 Oct 2021 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2021-10-12-nextcloud-on-rpi/</guid>
      <description>&lt;p&gt;I experimented with &lt;a href=&#34;https://nextcloud.com/&#34;&gt;Nextcloud&lt;/a&gt; 22 on a Raspberry Pi 4 8 GB,
running the aarch64 build of Arch Linux ARM.
I found the setup too slow to be useful: even with caching,
switching between tabs on the web interface takes seconds,
while &lt;code&gt;top&lt;/code&gt; shows &lt;code&gt;php-fpm&lt;/code&gt; consuming a lot of CPU.
Either further tuning would be required, or this hardware is simply
too slim for this kind of workload, or more efficient software needs to be produced.&lt;/p&gt;
&lt;p&gt;For the reference, I share my configuration below.
It is a summation of the &lt;a href=&#34;https://wiki.archlinux.org/title/Nextcloud&#34;&gt;Arch Linux nextcloud documentation&lt;/a&gt;,
and of the pages referenced by it, including the official documentation,
choosing one particular configuration, instead of presenting many alternatives:
PostgreSQL, nginx, php, php-fpm and nextcloud.&lt;/p&gt;
&lt;h2 id=&#34;postgresql&#34;&gt;PostgreSQL&lt;/h2&gt;
&lt;p&gt;Install the package, init a user and database:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pacman -Syu postgresql
# su -l postgres
[postgres]$ initdb --locale=en_US.UTF-8 -E UTF8 -D /var/lib/postgres/data
[postgres]$ createuser -h localhost -P nextcloud
[postgres]$ createdb -O nextcloud nextcloud
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make it listen on a local UNIX domain socket only:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# vim /var/lib/postgres/data/postgresql.conf
listen_addresses = &#39;&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;nginx&#34;&gt;nginx&lt;/h2&gt;
&lt;p&gt;Install the package and setup a site, according to the &lt;a href=&#34;https://docs.nextcloud.com/server/latest/admin_manual/installation/nginx.html&#34;&gt;nextcloud nginx docs&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pacman -Syu nginx
$ cat /etc/nginx/nginx.conf
user http;
worker_processes  4;

events {
    worker_connections  128;
}

http {
    include       mime.types;
    default_type  application/octet-stream;


    sendfile        on;
    keepalive_timeout  65;

    # https://wiki.archlinux.org/title/Nginx#Warning:_Could_not_build_optimal_types_hash
    types_hash_max_size 4096;
    server_names_hash_bucket_size 128;

    upstream php-handler {
        server unix:/run/nextcloud/nextcloud.sock;
    }

    server {
        listen       80;
        server_name  &amp;lt;your_hostname&amp;gt;;
        root /usr/share/webapps;

        location ^~ /.well-known {
          root /usr/share/webapps/nextcloud;

          # The rules in this block are an adaptation of the rules
          # in the Nextcloud `.htaccess` that concern `/.well-known`.
          location = /.well-known/carddav { return 301 /nextcloud/remote.php/dav/; }
          location = /.well-known/caldav  { return 301 /nextcloud/remote.php/dav/; }
          location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
          location /.well-known/pki-validation    { try_files $uri $uri/ =404; }
          # Let Nextcloud&#39;s API for `/.well-known` URIs handle all other
          # requests by passing them to the front-end controller.
          return 301 /nextcloud/index.php$request_uri;
        }

        location ^~ /nextcloud {
          # set max upload size
          client_max_body_size 10M;
          fastcgi_buffers 64 4K;

          # Enable gzip but do not remove ETag headers
          gzip on;
          gzip_vary on;
          gzip_comp_level 4;
          gzip_min_length 256;
          gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
          gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

          # Pagespeed is not supported by Nextcloud, so if your server is built
          # with the `ngx_pagespeed` module, uncomment this line to disable it.
          #pagespeed off;

          # HTTP response headers borrowed from Nextcloud `.htaccess`
          add_header Referrer-Policy                      &amp;quot;no-referrer&amp;quot;   always;
          add_header X-Content-Type-Options               &amp;quot;nosniff&amp;quot;       always;
          add_header X-Download-Options                   &amp;quot;noopen&amp;quot;        always;
          add_header X-Frame-Options                      &amp;quot;SAMEORIGIN&amp;quot;    always;
          add_header X-Permitted-Cross-Domain-Policies    &amp;quot;none&amp;quot;          always;
          add_header X-Robots-Tag                         &amp;quot;none&amp;quot;          always;
          add_header X-XSS-Protection                     &amp;quot;1; mode=block&amp;quot; always;

          # Remove X-Powered-By, which is an information leak
          fastcgi_hide_header X-Powered-By;

          # Specify how to handle directories -- specifying `/nextcloud/index.php$request_uri`
          # here as the fallback means that Nginx always exhibits the desired behaviour
          # when a client requests a path that corresponds to a directory that exists
          # on the server. In particular, if that directory contains an index.php file,
          # that file is correctly served; if it doesn&#39;t, then the request is passed to
          # the front-end controller. This consistent behaviour means that we don&#39;t need
          # to specify custom rules for certain paths (e.g. images and other assets,
          # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
          # `try_files $uri $uri/ /nextcloud/index.php$request_uri`
          # always provides the desired behaviour.
          index index.php index.html /nextcloud/index.php$request_uri;

          # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
          location = /nextcloud {
              if ( $http_user_agent ~ ^DavClnt ) {
                  return 302 /nextcloud/remote.php/webdav/$is_args$args;
              }
          }

          # Rules borrowed from `.htaccess` to hide certain paths from clients
          location ~ ^/nextcloud/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)    { return 404; }
          location ~ ^/nextcloud/(?:\.|autotest|occ|issue|indie|db_|console)                  { return 404; }

          # Ensure this block, which passes PHP files to the PHP process, is above the blocks
          # which handle static assets (as seen below). If this block is not declared first,
          # then Nginx will encounter an infinite rewriting loop when it prepends
          # `/nextcloud/index.php` to the URI, resulting in a HTTP 500 error response.
          location ~ \.php(?:$|/) {
              fastcgi_split_path_info ^(.+?\.php)(/.*)$;
              set $path_info $fastcgi_path_info;

              try_files $fastcgi_script_name =404;

              include fastcgi_params;
              fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
              fastcgi_param PATH_INFO $path_info;
              #fastcgi_param HTTPS on;
              fastcgi_param HTTPS off;

              fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
              fastcgi_param front_controller_active true;     # Enable pretty urls
              fastcgi_pass  php-handler;

              fastcgi_intercept_errors on;
              fastcgi_request_buffering off;
          }

          location ~ \.(?:css|js|svg|gif|png|jpg|ico)$ {
              try_files $uri /nextcloud/index.php$request_uri;
              expires 6M;         # Cache-Control policy borrowed from `.htaccess`
              access_log off;     # Optional: Don&#39;t log access to assets
          }

          location ~ \.woff2?$ {
              try_files $uri /nextcloud/index.php$request_uri;
              expires 7d;         # Cache-Control policy borrowed from `.htaccess`
              access_log off;     # Optional: Don&#39;t log access to assets
          }

          # Rule borrowed from `.htaccess`
          location /nextcloud/remote {
              return 301 /nextcloud/remote.php$request_uri;
          }

          location /nextcloud {
              try_files $uri $uri/ /nextcloud/index.php$request_uri;
          }
        }

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;php&#34;&gt;php&lt;/h2&gt;
&lt;p&gt;Install php, enable the required modules and enable caching:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pacman -Syu php php-apcu php-gd php-imap php-intl php-pgsql php-sqlite

$ snip /etc/php/php.ini
memory_limit = 1024M
extension=bz2
extension=gd
extension=gmp
extension=imap
extension=intl
extension=pdo_pgsql
extension=pgsql

$ cat /etc/php/conf.d/apcu.ini
extension=apcu.so
apc.ttl=7200
apc.enable_cli=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Opcache is enabled by default.&lt;/p&gt;
&lt;h2 id=&#34;php-fpm&#34;&gt;php-fpm&lt;/h2&gt;
&lt;p&gt;This is a daemon that takes requests from nginx and executes them with php. Install and configure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pacman -Syu php-fpm

$ snip /etc/php/php-fpm.conf
systemd_interval = 1h

$ cat /etc/php/php-fpm.d/nextcloud.conf
[nextcloud]
user = nextcloud
group = nextcloud
listen = /run/nextcloud/nextcloud.sock
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp

; should be accessible by your web server
listen.owner = http
listen.group = http

pm = dynamic
pm.max_children = 15
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

$ cat /etc/systemd/system/php-fpm.service.d/override.conf
[Service]
ReadWritePaths=/var/lib/nextcloud/data
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;nextcloud&#34;&gt;nextcloud&lt;/h2&gt;
&lt;p&gt;Install the php files, setup the database:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pacman -Syu nextcloud
# /usr/bin/occ maintenance:install --database pgsql --database-name nextcloud --database-host /run/postgresql --database-user nextcloud --database-pass nextcloudpw --data-dir /var/lib/nextcloud/data -vvv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enable APCu:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ snip /usr/share/webapps/nextcloud/config/config.php
&#39;memcache.local&#39; =&amp;gt; &#39;\OC\Memcache\APCu&#39;,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the required services and check their status:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# systemctl start postgresql nginx php-fpm
# systemctl status postgresql nginx php-fpm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The nextcloud web UI should be accessible at http://&amp;lt;your_hostname&amp;gt;/nextcloud.&lt;/p&gt;
&lt;h2 id=&#34;appendix&#34;&gt;Appendix&lt;/h2&gt;
&lt;p&gt;To see the difference between a current file and the package maintainers original version,
use (&lt;a href=&#34;https://bbs.archlinux.org/viewtopic.php?id=177570&#34;&gt;source&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function qkkdiff-file {
  if [[ &amp;quot;$1&amp;quot; == &amp;quot;&amp;quot; ]];
  then
      echo &amp;quot;Example usage: qkkdiff-file /etc/pacman.conf&amp;quot;
  elif [[ -f &amp;quot;$1&amp;quot; ]];
  then
      pkg=&amp;quot;$(pacman -Qo $1 | awk &#39;//{printf &amp;quot;%s-%s&amp;quot;, $(NF-1), $NF;}&#39;)&amp;quot;
      bsdtar -xOf /var/cache/pacman/pkg/${pkg}-$(uname -m).pkg.tar.xz &amp;quot;${1/\//}&amp;quot; | diff - &amp;quot;$1&amp;quot;
      return 0
  else
      echo &amp;quot;The provided file ${1} does not exist.&amp;quot;
      return 1
  fi
}
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    <item>
      <title>Input Latency</title>
      <link>http://erenon.hu/posts/2021-10-11-input-latency/</link>
      <pubDate>Mon, 11 Oct 2021 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2021-10-11-input-latency/</guid>
      <description>&lt;p&gt;This post was inspired by &lt;a href=&#34;https://danluu.com/input-lag/&#34;&gt;Dan Luu: Computer Latency&lt;/a&gt;.
I felt that my work setup (see below) is sluggish, even though I got used to it.
I wanted to quantify how sluggish it really is.
Also, VSCode was advertised to offer a lower latency experience (compared to ssh+vim),
as it runs and renders on the local machine: I didn&amp;rsquo;t feel it - I wanted to verify my observation,
knowing that I use vim for too long to acknowledge its shortcomings.&lt;/p&gt;
&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Program&lt;/th&gt;
&lt;th style=&#34;text-align:right&#34;&gt;Latency (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;kitty&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;gvim&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;vscode&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;140&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;remote&lt;sup&gt;2&lt;/sup&gt; vim&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;195&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;remote vscode&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;remote Visual Studio&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;210&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These are the test results of the latency between a keypress and the display of a character (see below for details).
Results are rounded to the nearest &lt;code&gt;5 ms&lt;/code&gt; to avoid the impression of false precision.&lt;/p&gt;
&lt;p&gt;The measurement confirms that typing on this particular remote setup is twice as bad as locally,
and that the additional latency of vscode (compared to vim) is greater than the latency of ssh.&lt;/p&gt;
&lt;h2 id=&#34;experimental-setup&#34;&gt;Experimental setup&lt;/h2&gt;
&lt;p&gt;A local computer was filmed with a 240 FPS camera.
Latency is defined as the time (number of frames) elapsed between a keyboard key starts moving,
and the matching character appears on screen. The declared latency is a rounded average of several
key presses, without much deviation. No particular care was taken to achieve the lowest latency possible
(e.g: CPU pinning, interrupt pinning, reducing workload) &amp;ndash; as that&amp;rsquo;s not how I normally work &amp;ndash;
but the computers used were under no particular load in general.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;local computer&lt;/strong&gt; runs on a AMD FX 8320, with a standard HP KU-0316 keyboard attached via USB,
and a 4K 60Hz AOC monitor attached via DisplayPort. The operating system is Linux 5.14.7,
running the latest software from the ArchLinux distribution, including the i3 window manager.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;remote computer&lt;/strong&gt; is a dedicated machine, hosted in a datacenter, ~1500 kilometers away from
the local computer, with a modern Intel CPU and plenty of RAM. It is accessed with xfreerdp,
and runs Windows 10.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;remote server&lt;/strong&gt; is a server class Intel machine of unspecified details, runs Linux 3,
and somewhat older versions of software. Regarding physical location, it is close to the remote computer described above.
It is shared between different users, but during the test, no particular load was observed.&lt;/p&gt;
&lt;h2 id=&#34;contenders&#34;&gt;Contenders&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://sw.kovidgoyal.net/kitty/&#34;&gt;kitty&lt;/a&gt; is a powerful terminal emulator, version 0.23.1 was used.
Runs on the &lt;strong&gt;local computer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.vim.org/&#34;&gt;gvim&lt;/a&gt; is the GTK GUI version of the venerated editor, version 8.2.
Runs on the &lt;strong&gt;local computer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://code.visualstudio.com/&#34;&gt;vscode&lt;/a&gt; is a recent Microsoft product, version 1.60.2.
Runs on the &lt;strong&gt;local computer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;remote &lt;a href=&#34;https://code.visualstudio.com/&#34;&gt;vscode&lt;/a&gt; runs on the &lt;strong&gt;remote computer&lt;/strong&gt;, version 1.53.2.&lt;/p&gt;
&lt;p&gt;remote &lt;a href=&#34;https://visualstudio.com/&#34;&gt;Visual Studio&lt;/a&gt; runs on the &lt;strong&gt;remote computer&lt;/strong&gt;, version 16.9.2.&lt;/p&gt;
&lt;p&gt;remote&lt;sup&gt;2&lt;/sup&gt; &lt;a href=&#34;https://www.vim.org/&#34;&gt;vim&lt;/a&gt; is version 8.2. It runs in a tmux instance on the &lt;strong&gt;remote server&lt;/strong&gt;,
accessed via PuTTY, that runs on the &lt;strong&gt;remote computer&lt;/strong&gt; (that is accessed via xfreerdp, from the &lt;strong&gt;local computer&lt;/strong&gt;),
totalling to 2 logical hops in one direction.
Contorted? Yes. Unrealistic? Well, that&amp;rsquo;s how I have to work&amp;hellip;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Bootstrap aarch64 image on X86</title>
      <link>http://erenon.hu/posts/2021-03-21-aarch64-image-on-x86/</link>
      <pubDate>Sun, 21 Mar 2021 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2021-03-21-aarch64-image-on-x86/</guid>
      <description>&lt;p&gt;The current (2021-Feb-05) aarch64 &lt;a href=&#34;https://archlinuxarm.org/platforms/armv8/broadcom/raspberry-pi-4&#34;&gt;Arch Linux ARM&lt;/a&gt; image has &lt;a href=&#34;https://archlinuxarm.org/forum/viewtopic.php?f=67&amp;amp;t=15190&#34;&gt;issues&lt;/a&gt; on 8GB Raspberry Pi 4 models:
It does not bring up the network interface, and the USB ports are not working,
making any interactive fix difficult (without serial cable).&lt;/p&gt;
&lt;p&gt;A possible fix is to replace the default kernel with a Raspberry specific one.
It still results in a 64bit system, only with a non-upstream kernel.
If there&amp;rsquo;s a different ARM system available, it is trivial to mount/boot the SD card there and apply the required changes.
The solution below, however, only requires a single working, linux supported hardware, e.g: x86.
This description is Arch Linux specific. For ubuntu, see &lt;a href=&#34;https://disconnected.systems/blog/raspberry-pi-archlinuxarm-setup/&#34;&gt;this blogpost on Disconnected Systems&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;install-qemu&#34;&gt;Install Qemu&lt;/h2&gt;
&lt;p&gt;Qemu makes it possible to run arm binaries on x86 (and many more combinations).
Install &lt;a href=&#34;https://aur.archlinux.org/packages/qemu-user-static/&#34;&gt;qemu-user-static&lt;/a&gt; from AUR. To make the resulting package smaller,
add &lt;code&gt;--target-list=aarch64-linux-user&lt;/code&gt; to the configure invocation in the &lt;code&gt;PKGBUILD&lt;/code&gt; file.
This package requires glib-static and pcre-static, the former takes some time to build.
Due to the number of dependencies, consider building in a &lt;a href=&#34;https://wiki.archlinux.org/index.php/DeveloperWiki:Building_in_a_clean_chroot&#34;&gt;clean chroot&lt;/a&gt;.
(Do not forget to &lt;a href=&#34;https://wiki.archlinux.org/index.php/Makepkg&#34;&gt;set &lt;code&gt;-j&lt;/code&gt; in &lt;code&gt;MAKEFLAGS&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Install the built qemu-user-static package. Read the &lt;a href=&#34;https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html&#34;&gt;introduction of binfmt&lt;/a&gt;,
then setup the config accordingly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat /etc/binfmt.d/qemu-static.conf
:qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/bin/qemu-aarch64-static:CF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, restart the service that - if there&amp;rsquo;s a config file - mounts the filesystem described by the doc above:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo systemctl restart systemd-binfmt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the system can run aarch64 binaries using qemu.&lt;/p&gt;
&lt;h2 id=&#34;install-arch-linux-arm&#34;&gt;Install Arch Linux ARM&lt;/h2&gt;
&lt;p&gt;Follow the &lt;a href=&#34;https://archlinuxarm.org/platforms/armv8/broadcom/raspberry-pi-4&#34;&gt;install guide&lt;/a&gt;. Use the &lt;code&gt;ArchLinuxARM-rpi-aarch64-latest.tar.gz&lt;/code&gt; archive,
but do not change &lt;code&gt;/etc/fstab&lt;/code&gt;! Also, before unmounting root and boot, do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo arch-chroot root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will drop you into the installed ARM system. Add the alarm repos, and install the raspberry kernel:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pacman-key --init
$ pacman-key --populate archlinuxarm
$ pacman -S --needed linux-raspberrypi4 raspberrypi-bootloader raspberrypi-bootloader-x raspberrypi-firmware firmware-raspberrypi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a good time to also complete the official &lt;a href=&#34;https://wiki.archlinux.org/index.php/installation_guide&#34;&gt;Installation Guide&lt;/a&gt;.
After done, copy root/boot over boot again, and unmount the partitions.
Again, do not modify &lt;code&gt;etc/fstab&lt;/code&gt;, contrary to the alarm docs.&lt;/p&gt;
&lt;p&gt;With everything done correctly, using the patched image, the network and USB ports of the 8GB Raspberry Pi 4
are fine again!&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Automated, encrypted b2 backups with rclone</title>
      <link>http://erenon.hu/posts/encrypted_backup_rclone/</link>
      <pubDate>Wed, 03 Mar 2021 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/encrypted_backup_rclone/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s how to create automated, secure backups on &lt;a href=&#34;http://blackblaze.com/&#34;&gt;blackblaze&lt;/a&gt; using &lt;a href=&#34;https://rclone.org/&#34;&gt;rclone&lt;/a&gt; and systemd.&lt;/p&gt;
&lt;p&gt;First, &lt;a href=&#34;https://www.backblaze.com/b2/sign-up.html&#34;&gt;register&lt;/a&gt; a new blackblaze b2 account if you don&amp;rsquo;t have one already. Make sure you select the right
region during sign-up - it cannot be changed later. Create a new bucket, name it after your hostname.
Create a b2 rclone remote (see &lt;a href=&#34;https://rclone.org/b2/&#34;&gt;doc&lt;/a&gt;).
Create a crypt overlay over the b2 remote (see &lt;a href=&#34;https://rclone.org/crypt/&#34;&gt;doc&lt;/a&gt;). Generate long passwords.&lt;/p&gt;
&lt;p&gt;The resulting config is something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat ~/.config/rclone/rclone.conf
[b2]
type = b2
account = *redacted*
key = *redacted*

[b2_HOSTNAME_crypt]
type = crypt
remote = b2:HOSTNAME
filename_encryption = standard
directory_name_encryption = true
password = *redacted*
password2 = *redacted*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Try &lt;code&gt;rclone listremotes&lt;/code&gt;, &lt;code&gt;rclone copy&lt;/code&gt;, &lt;code&gt;rclone mount&lt;/code&gt; to see if it works.
Use the b2 online browser to verify that the uploaded data is encrypted.&lt;/p&gt;
&lt;p&gt;If you want to backup your home directory, chances are, you don&amp;rsquo;t want to backup it all (e.g: ignore .cache).
Create an rclone filter file that selects the files to sync:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat ~/.config/rclone/filter.txt
- .cache/**
+ **
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;rclone ncdu --filter-from .config/rclone/filter.txt .&lt;/code&gt; to verify that the right files are selected.&lt;/p&gt;
&lt;h2 id=&#34;automate-with-systemd&#34;&gt;Automate with systemd&lt;/h2&gt;
&lt;p&gt;To sync the directory, use &lt;code&gt;rclone sync&lt;/code&gt;. To avoid overwriting previous backups, use &lt;code&gt;--backup-directory&lt;/code&gt;.
The following &lt;a href=&#34;https://www.freedesktop.org/software/systemd/man/systemd.service.html&#34;&gt;systemd service&lt;/a&gt; can be used:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat ~/.config/systemd/user/rclone.service
[Unit]
Description=&amp;quot;Sync selected parts of home directory to b2 encrypted remote storage&amp;quot;

[Service]
Type=oneshot
ExecStart=%s -c &#39;rclone sync --filter-from %E/rclone/filter.txt %h b2_%H_crypt:home --b2-hard-delete --fast-list --transfers 32 --backup-dir b2_%H_crypt:home-$$(date +%%u)&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will keep 1 week worth of data without overwriting. A simple &lt;a href=&#34;https://www.freedesktop.org/software/systemd/man/systemd.timer.html&#34;&gt;systemd timer&lt;/a&gt; can make it run daily:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat ~/.config/systemd/user/rclone.timer
[Unit]
Description=&amp;quot;Sync daily&amp;quot;

[Timer]
OnCalendar=*-*-* 12:15:00
FixedRandomDelay=true
Persistent=true

[Install]
WantedBy=timers.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enable and start the timer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ systemctl --user enable rclone.timer
$ systemctl --user start rclone.timer
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;backup-the-config&#34;&gt;Backup the config&lt;/h2&gt;
&lt;p&gt;If the rclone encryption passwords are stored in the directory that is being backed up,
in case of a local failure, the remote content cannot be restored.
Therefore, the rclone config must be stored separately.
The &lt;a href=&#34;https://wiki.archlinux.org/index.php/Paperkey&#34;&gt;Paperkey&lt;/a&gt; page offers a couple of ideas.
For example, to get a printable version of an encrypted config, do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ qrencode --8bit -t svg -lH -o config.svg &amp;lt; encypted_config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To recover:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ zbarcam -1 --raw -q -Sbinary
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The encryption can happen with e.g: &lt;code&gt;gpg&lt;/code&gt;, that requires a similar treatment of the private key.&lt;/p&gt;
&lt;h2 id=&#34;note-about-browsers&#34;&gt;Note about browsers&lt;/h2&gt;
&lt;p&gt;Browsers tend to write their persistent storage frequently, making it more difficult
to create a consistent snapshot of them. To reduce the chance that a browser write
conflicts with the sync action, use &lt;a href=&#34;https://wiki.archlinux.org/index.php/profile-sync-daemon&#34;&gt;Profile Sync Daemon&lt;/a&gt;
It will move the browser storage to memory, and sync it back periodically.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Store SSH key passphrase in password manager</title>
      <link>http://erenon.hu/posts/2021-02-08-ssh-with-pass/</link>
      <pubDate>Mon, 08 Feb 2021 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2021-02-08-ssh-with-pass/</guid>
      <description>&lt;p&gt;The &lt;code&gt;ssh-agent&lt;/code&gt; program can be used to manage unlocked SSH keys.
The &lt;code&gt;ssh-add&lt;/code&gt; program can be used to add keys to a running &lt;code&gt;ssh-agent&lt;/code&gt;.
By default, &lt;code&gt;ssh-add&lt;/code&gt; uses TTY to ask for a password,
if the selected key is password protected.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://www.passwordstore.org/&#34;&gt;pass&lt;/a&gt; program is a password manager that follows the Unix philosophy.
If you want to use &lt;code&gt;pass&lt;/code&gt; together with ssh-add, first add the private key
password to it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ pass insert ssh/identityname_rsa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then create this wrapper program &lt;code&gt;/home/user/bin/askpass.sh&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# $1 ~= Enter passphrase for &amp;#39;.ssh/$KEYNAME&amp;#39;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; /usr/bin/pass &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;grep -o &lt;span class=&#34;s2&#34;&gt;&amp;#34;ssh/[^:&amp;#39;]\+&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Export the following variables, e.g: by adding them to you shell rc:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SSH_ASKPASS_REQUIRE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;force
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SSH_ASKPASS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/user/bin/askpass.sh&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now whenever an SSH key is needed (e.g: for &lt;code&gt;git&lt;/code&gt; or &lt;code&gt;ssh&lt;/code&gt; client),
&lt;code&gt;ssh-add&lt;/code&gt; will turn to &lt;code&gt;pass&lt;/code&gt;, giving it the key name to be unlocked,
and in case the gpg key is not unlocked at the moment, &lt;code&gt;pass&lt;/code&gt; will
prompt you to enter your password, then safely transmit the key
passphrase to &lt;code&gt;ssh-add&lt;/code&gt;, that will add the key to the running &lt;code&gt;ssh-agent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If git does pop up the gpg unlock dialog, but prints
&amp;ldquo;gpg: decryption failed: No secret key&amp;rdquo; instead,
make sure the &lt;a href=&#34;https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html&#34;&gt;GPG_TTY environment variable&lt;/a&gt; is exported:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;GPG_TTY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TTY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If git still does not ask for a password, and no such error is printed:
If the private key is not the default one (e.g: &lt;code&gt;.ssh/id_*&lt;/code&gt;), the host-key association
must be configured in &lt;code&gt;.ssh/config&lt;/code&gt;, e.g:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Host github.com
  IdentityFile ~/.ssh/your_nondefault_key
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    <item>
      <title>Useful FreeRDP flags for good performance</title>
      <link>http://erenon.hu/posts/2021-01-25-freerdp/</link>
      <pubDate>Mon, 25 Jan 2021 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2021-01-25-freerdp/</guid>
      <description>&lt;p&gt;I need to use RDP to connect to a Windows 10 machine.
Equipped with a 4k display, on Arch Linux, with FreeRDP 2.2.0, the following combination of flags turned out to be the best:
&lt;code&gt;-grab-keyboard /gfx-h264:avc444 +gfx-progressive&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To make it work, first make sure &lt;a href=&#34;https://wiki.archlinux.org/index.php/Hardware_video_acceleration&#34;&gt;hardware acceleration&lt;/a&gt; is enabled. With AMD:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo pacman -Sy xf86-video-amdgpu libva-mesa-driver freerdp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then add this .desktop file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat .local/share/applications/xfreerdp.desktop
[Desktop Entry]
Name=XFreeRDP
Comment=Connect to remote desktops using RDP
Exec=xfreerdp %U -grab-keyboard /gfx-h264:avc444 +gfx-progressive
Terminal=false
Type=Application
StartupNotify=false
MimeType=application/x-rdp-type;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Without &lt;code&gt;/gfx-h264:avc444&lt;/code&gt;, certain UI elements (e.g: red text on dark background in PuTTY) are rendered unreadable.
Without &lt;code&gt;+gfx-progressive&lt;/code&gt;, scrolling is extremely laggy, a full redraw (switching window) can take a second.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-grab-keyboard&lt;/code&gt; is used to let i3 key bindings take effect even in full screen mode.
If this doesn&amp;rsquo;t work for you, take a look at &lt;a href=&#34;https://wiki.openthinclient.org/questions/5538073/freerdp-slow-video-performance---low-framerate&#34;&gt;this post&lt;/a&gt; and the &lt;a href=&#34;https://man.archlinux.org/man/community/freerdp/xfreerdp.1.en&#34;&gt;freerdp man page&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Overloading by concept without concepts in C&#43;&#43; part II</title>
      <link>http://erenon.hu/posts/2021-01-14-concept-overloading-2/</link>
      <pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2021-01-14-concept-overloading-2/</guid>
      <description>&lt;p&gt;In the previous post, we&amp;rsquo;ve seen how to select an overload from a set of functions
based on &lt;em&gt;concepts&lt;/em&gt; matching the arguments, without actual concept support in the C++ language.
The described solution allows a library to define a single set of overloads.&lt;/p&gt;
&lt;p&gt;However, how to allow the client to override the library provided overload for a given concept?
Simply adding a specialization would result in ambiguous function calls.
If not today, probably later, when the library changes - making the solution hard to maintain.&lt;/p&gt;
&lt;p&gt;A conflict free solution simply adds an indirection:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If user defined overload exists, call that&lt;/li&gt;
&lt;li&gt;Otherwise call the library defined function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This clearly models what we want to achieve. This is how it looks in code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename, typename = void&amp;gt; struct CustomF {
  CustomF() = delete; // detection will use is_constructible
};

template &amp;lt;typename, typename = void&amp;gt; struct BuiltinF; // default case undefined
// Specializations for supported concepts as shown before follow...

template &amp;lt;typename T&amp;gt; struct F {
  using type = std::conditional_t&amp;lt;
    std::is_constructible&amp;lt;CustomF&amp;lt;T&amp;gt;&amp;gt;::value,
    CustomF&amp;lt;T&amp;gt;,
    BuiltinF&amp;lt;T&amp;gt;
  &amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;type&lt;/code&gt; member of &lt;code&gt;F&lt;/code&gt; will be either the user defined custom logic (if the type is constructible),
or the built-in logic. The wrapper that hides the invocation remains the same:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename T&amp;gt;
void f(const T&amp;amp; t) { detail::F&amp;lt;T&amp;gt;::f(t); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s it! Now clients can easily add &lt;code&gt;CustomF&lt;/code&gt; specializations, without risking conflicts
with the present or future specializations of &lt;code&gt;BuiltinF&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is the system that the &lt;a href=&#34;http://binlog.org/&#34;&gt;binlog&lt;/a&gt; serializer library, &lt;a href=&#34;http://binlog.org/Mserialize.html&#34;&gt;mserialize&lt;/a&gt; &lt;a href=&#34;https://github.com/Morgan-Stanley/binlog/blob/dev/include/mserialize/detail/Serializer.hpp#L54&#34;&gt;uses&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Overloading by concept without concepts in C&#43;&#43;</title>
      <link>http://erenon.hu/posts/2019-09-12-concept-overloading/</link>
      <pubDate>Thu, 12 Sep 2019 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2019-09-12-concept-overloading/</guid>
      <description>&lt;p&gt;Given the following function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void f(int);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suppose you want to make the behaviour of this function
to depend on the argument type. A simple way to use overloading:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void f(int);
void f(double);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What if you want to define an overload for a set of types,
which satisfy a given concept? As of C++20, concepts
can be used for overloading. Before that, the function has to be
converted to a template. However, since C++ does not allow
specialization of function templates, first change the
function into a type template with a single static member:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename&amp;gt; struct F; // default case undefined
template &amp;lt;&amp;gt; struct F&amp;lt;int&amp;gt; { static void f(int); };
template &amp;lt;&amp;gt; struct F&amp;lt;double&amp;gt; { static void f(double); };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is equivalent with the above, but now we can apply
various kind of &lt;a href=&#34;https://en.cppreference.com/w/cpp/language/sfinae&#34;&gt;SFINAE&lt;/a&gt; magic to enable different specializations
for different kind of types. Common techniques use &lt;code&gt;std::enable_if&lt;/code&gt;,
expression SFINAE and others. Here a simple, yet powerful
solution is presented, which is based on &lt;a href=&#34;https://en.cppreference.com/w/cpp/types/void_t&#34;&gt;&lt;code&gt;void_t&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;class... &amp;gt;
using void_t = void;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This innocent looking type alias resolves to &lt;code&gt;void&lt;/code&gt;,
if the template arguments are valid, otherwise it
becomes a substitution failure.&lt;/p&gt;
&lt;p&gt;To use it, add an extra, defaulted template argument to &lt;code&gt;F&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename, typename = void&amp;gt; struct F; // default case undefined
template &amp;lt;&amp;gt; struct F&amp;lt;int, void&amp;gt; { static void f(int); };
template &amp;lt;&amp;gt; struct F&amp;lt;double, void&amp;gt; { static void f(double); };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far, there&amp;rsquo;s no behavioural change. However, no we can easily
add overloads which apply to a set of types, e.g:
types which has a specific nested type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename Container&amp;gt;
struct F&amp;lt;Container, void_t&amp;lt;typename Container::value_type&amp;gt;&amp;gt; {
  static void f(Container const&amp;amp;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A different example, specialize for types with a specific member:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename Lockable&amp;gt;
struct F&amp;lt;Lockable, void_t&amp;lt;decltype(Lockable::lock)&amp;gt; {
  static void f(Lockable const&amp;amp;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This technique also integrates well with standard type predicates.
First, define the following helper:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename Cond&amp;gt;
using enable_spec_if = void_t&amp;lt;std::enable_if_t&amp;lt;Cond::value&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename Trivial&amp;gt;
struct F&amp;lt;Trivial, enable_spec_if&amp;lt;std::is_trivial&amp;lt;Trivial&amp;gt;&amp;gt;&amp;gt; {
  static void f(Trivial const&amp;amp;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given C++17 is available, a similar, slightly longer solution is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename Trivial&amp;gt;
struct F&amp;lt;Trivial, std::enable_if_t&amp;lt;std::is_trivial_v&amp;lt;Trivial&amp;gt;, void&amp;gt;&amp;gt; {
  static void f(Trivial const&amp;amp;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A limitation of this technique (and also several others),
that specializations must be mutually exclusive for the
types they are used.&lt;/p&gt;
&lt;p&gt;To make usage easier, we can add a function template,
making it the only entry point of the operation,
while hiding &lt;code&gt;F&lt;/code&gt; in the detail namespace:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename T&amp;gt;
void f(const T&amp;amp; t) { detail::F&amp;lt;T&amp;gt;::f(t); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the next part, we&amp;rsquo;ll cover how to allow third parties to define
additional specializations, without conflicting with built in ones.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Applied Ephemeralization</title>
      <link>http://erenon.hu/posts/2019-04-28-applied-ephemeralization/</link>
      <pubDate>Sun, 28 Apr 2019 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2019-04-28-applied-ephemeralization/</guid>
      <description>&lt;dl&gt;
&lt;dt&gt;Ephemeralization&lt;/dt&gt;
&lt;dd&gt;Do more and more with less and less until eventually you can do everything with nothing&lt;/dd&gt;
&lt;dt&gt;Ephemeralism&lt;/dt&gt;
&lt;dd&gt;Do more with less, until you can do everything with nothing&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Also see &lt;a href=&#34;https://en.wikipedia.org/wiki/Ephemeralization&#34;&gt;Wikipedia on Ephemeralization&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>How to share a calendar entry</title>
      <link>http://erenon.hu/posts/2018-05-30-how-to-share-a-calendar-entry/</link>
      <pubDate>Wed, 30 May 2018 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2018-05-30-how-to-share-a-calendar-entry/</guid>
      <description>&lt;p&gt;Suppose you&amp;rsquo;d like to share a calendar event over the internet,
e.g: on your website. The goal is to be support as many platforms as possible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Desktop calendars (Thunderbird, macOS iCal, Outlook)&lt;/li&gt;
&lt;li&gt;Web applications (Google Calendar)&lt;/li&gt;
&lt;li&gt;Smartphone applications (Google Calendar App, iOS iCal)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most intuitive way is to create the event in your calendar of
choice, and send the invites in the native way provided by the application.
However, such methods tend to work only if the recipient uses the same
system, which isn&amp;rsquo;t a plausible assumption &amp;ndash; we need something platform independent.&lt;/p&gt;
&lt;h3 id=&#34;using-icalendar&#34;&gt;Using iCalendar&lt;/h3&gt;
&lt;p&gt;Wikipedia indicates that &lt;a href=&#34;https://en.wikipedia.org/wiki/ICalendar&#34;&gt;iCalendar&lt;/a&gt; is well supported format, which
&lt;em&gt;allows Internet users to send meeting requests and tasks to other Internet users by sharing or sending files in this format through various methods.&lt;/em&gt;
&amp;ndash; exactly what we are looking for. Let&amp;rsquo;s create an &lt;code&gt;.ics&lt;/code&gt; file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
X-WR-TIMEZONE:Europe/Budapest
BEGIN:VEVENT
UID:your_eventname@yourdomain.com
DTSTART:20180530T130000Z
DTEND:20180530T143000Z
DESCRIPTION:The description of your event
LOCATION:The location of your event
SEQUENCE:0
SUMMARY:The summary of your event
END:VEVENT
END:VCALENDAR
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are &lt;a href=&#34;https://tools.ietf.org/html/rfc5545&#34;&gt;other fields&lt;/a&gt;.
The fields are mostly self describing.
In free form text values, &lt;code&gt;,&lt;/code&gt; and &lt;code&gt;;&lt;/code&gt; characters must be escaped by a backslash.
The &lt;code&gt;tzselect&lt;/code&gt; command can be used to get the name of your timezone.
The &lt;code&gt;DTSTART&lt;/code&gt; and &lt;code&gt;DTEND&lt;/code&gt; fields are in &lt;a href=&#34;https://en.wikipedia.org/wiki/Coordinated_Universal_Time&#34;&gt;UTC&lt;/a&gt;, 20180530T130000 is 2018. 05. 30, 13:00:00.
If all this sound difficult, create your event in a suitable calendar application
and export it to &lt;code&gt;.ics&lt;/code&gt; from there.&lt;/p&gt;
&lt;p&gt;Sharing this file will cover desktop calendars associated with the ics extension
and the iOS calendar. It probably will not work with the Google Calendar website,
and &amp;ndash; surprisingly &amp;ndash; it will not work on Android as expected.&lt;/p&gt;
&lt;h3 id=&#34;fixing-android&#34;&gt;Fixing Android&lt;/h3&gt;
&lt;p&gt;On Android, after download, a toast message will be shown, saying:
&amp;ldquo;Unable to launch event&amp;rdquo;. Internet wisdom blames various components.
On the phones I had access to, Logcat show that the Calendar app does not
have permission to read downloaded files.&lt;/p&gt;
&lt;p&gt;Fortunately, there&amp;rsquo;s a way around: Android allows &lt;a href=&#34;https://stackoverflow.com/questions/42936576/open-android-calendar-with-intent-from-web-chrome&#34;&gt;emitting intents by links&lt;/a&gt;, in some cases.
The proper syntax and the allowed fields can be discovered by looking at the &lt;a href=&#34;https://android.googlesource.com/platform/packages/apps/Calendar/+/donut-release/AndroidManifest.xml&#34;&gt;source&lt;/a&gt;,
or &lt;a href=&#34;https://stackoverflow.com/questions/22757908/google-calendar-render-action-template-parameter-documentation&#34;&gt;StackOverflow&lt;/a&gt;.
Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://www.google.com/calendar/event?action=TEMPLATE&amp;amp;text=your_event&amp;amp;dates=20180530T130000Z/20180530T143000Z&amp;amp;location=location of the event&amp;amp;sprop=yourdomain.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Calendar event&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Values need to be &lt;a href=&#34;https://en.wikipedia.org/wiki/Percent-encoding&#34;&gt;urlencoded&lt;/a&gt;.
Clicking on this link on Android will open the Google Calendar application, if installed,
with the specified event shown to be saved. Fortunately, this link will work for the
browser based Google Calendar as well!&lt;/p&gt;
&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s put all this together. By default, we should link to the &lt;code&gt;.ics&lt;/code&gt; file,
as it works on most platforms. On Android, the link has to be changed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;event.ics&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;data-gcal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://www.google.com/calendar/event?...&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ics-link&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Calendar event&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;replaceIcs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.ics-link&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;href&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getAttribute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;data-gcal&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addEventListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;DOMContentLoaded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// NB: there are more robust ways to check for android
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;userAgent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;navigator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;userAgent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toLowerCase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;userAgent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;indexOf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;android&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;replaceIcs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What about desktop clients using the Google Calendar web application?
It is difficult if possible to tell what kind of calendar solution such users would like to use.
However, there&amp;rsquo;s a hack, which detects if the &lt;a href=&#34;http://www.tomanthony.co.uk/blog/detect-visitor-social-networks/&#34;&gt;user is logged in to a google account&lt;/a&gt;.
If desired, as an imperfect solution, ics links can be also replaced, if such a login is detected.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;It is more difficult to share a calendar entry over the internet as expected.
Application native ways do not interoperate, while some platforms are hard to target by standard solutions.
While the solution shown above covers a large percentage of use-cases, it still might fail in others.
A free web needs more support of open standards!&lt;/p&gt;
</description>
    </item>
    <item>
      <title>The sorcerers frog</title>
      <link>http://erenon.hu/posts/2014-03-29-the-sorcerers-frog/</link>
      <pubDate>Sat, 29 Mar 2014 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2014-03-29-the-sorcerers-frog/</guid>
      <description>&lt;p&gt;Once there was a sorcerer who had the most shining gold. A simple and mild
man came to him and wanted to buy this gold from him. The sorcerer said to
the simple man: &lt;em&gt;&amp;ldquo;You will not receive this gold, unless you give me better
gold and in larger quantity.&amp;rdquo;&lt;/em&gt; The man said: &lt;em&gt;&amp;ldquo;I have such a great desire for
your gold that I will give you what you want rather than losing it.&amp;rdquo;&lt;/em&gt; He then
gave the sorcerer better gold and in larger quantity and received the shining
gold from him and put it in a casket, thinking of making himself a ring from
it for his finger.&lt;/p&gt;
&lt;p&gt;After a short time, the sorcerer approached that simple man and said: &lt;em&gt;&amp;ldquo;The
gold you bought from me and laid in your casket is not gold, as you thought,
but the most ugly frog. It has been fostered in my chest and fed with my
food. And in order for you to test and know that this is true, you may
open the casket and you will see how the frog will jump to my chest
where it was fostered.&amp;rdquo;&lt;/em&gt; When the man wanted to open it and find out if
it was true, the frog appeared in the casket. The cover of the casket
was hanging on four hinges that were about to break and fall off soon.
Immediately when the cover of the casket was opened, the frog saw the
sorcerer and jumped into his chest.&lt;/p&gt;
&lt;p&gt;When the servants and friends of the simple man saw this, they said to him:
&lt;em&gt;&amp;ldquo;Lord, this most fine gold is in the frog, and if you want, you can easily
get the gold.&amp;rdquo;&lt;/em&gt; The man said: &lt;em&gt;&amp;ldquo;How can I get it?&amp;rdquo;&lt;/em&gt; They replied: &lt;em&gt;&amp;ldquo;If
someone took a sharp and heated spear and thrust it into the hollow part of
the frog&amp;rsquo;s back, he would quickly get the gold out. But if he cannot find any
hollow in the frog, he should then, with the greatest force and effort,
thrust his spear into it, and this is how you will get back the gold
you bought.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Celestial Revelations&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Bootstrap Typeahead submit on select</title>
      <link>http://erenon.hu/posts/2013-03-02-bootstrap-typeahead-submit-on-select/</link>
      <pubDate>Sat, 02 Mar 2013 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2013-03-02-bootstrap-typeahead-submit-on-select/</guid>
      <description>&lt;p&gt;The &lt;a href=&#34;http://twitter.github.com/bootstrap/index.html&#34;&gt;Twitter Bootstrap&lt;/a&gt;s &lt;a href=&#34;http://twitter.github.com/bootstrap/javascript.html#typeahead&#34;&gt;Typeahead&lt;/a&gt; is a very nice way to provide autocomplete functionality on your text inputs. However, the default configuration might be a bit confusing. When the user clicks on a suggestion in the dropdown menu, the utility populates the input but doesn&amp;rsquo;t submit the form. It&amp;rsquo;s usually ok, but sometimes (e.g: search boxes) it&amp;rsquo;s frustrating. Here&amp;rsquo;s how to change it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#your-input-box&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;typeahead&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;source&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;updater&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;form&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;submit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This snippet autosubmits the form if the user clicks on a suggestion or selects it by keyboard.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Imprisoned Toroid - Raytrace on CUDA</title>
      <link>http://erenon.hu/posts/2012-11-25-imprisoned-toroid-raytrace-on-cuda/</link>
      <pubDate>Sun, 25 Nov 2012 00:00:00 +0000</pubDate>
      <guid>http://erenon.hu/posts/2012-11-25-imprisoned-toroid-raytrace-on-cuda/</guid>
      <description>&lt;p&gt;The following video is created by a C++/CUDA program using the theory of raytracing
which was the most interesting piece of knowledge I learned on the university this semester.&lt;/p&gt;
&lt;p&gt;Because of its as-good-as-it-should-be quality of the source, I can&amp;rsquo;t opensource it at this precise moment.
Meanwhile, take a look at &lt;a href=&#34;https://developer.nvidia.com/optix&#34;&gt;OptiX&lt;/a&gt;, which I found a month later I had completed this.&lt;/p&gt;
&lt;p&gt;The video was rendered at 24 FPS, render time appr. 3 hours.&lt;/p&gt;
&lt;iframe width=&#34;480&#34; height=&#34;360&#34; src=&#34;http://www.youtube.com/embed/oYdYEcjz7_4&#34; frameborder=&#34;0&#34;&gt; &lt;/iframe&gt;
&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=oYdYEcjz7_4&#34;&gt;YouTube&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
  </channel>
</rss>
