<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title>Sitegui&apos;s Blog</title><id>https://sitegui.dev/</id><updated>2026-05-20T06:45:02.633903855+00:00</updated><author><name>sitegui</name></author><category term="programming"/><category term="math"/><category term="boardgames"/><icon>https://sitegui.dev/static/favicon.png</icon><link href="https://sitegui.dev/feed.xml" rel="self"/><subtitle>My personal webspace, with content about programming, math and boardgames</subtitle><entry><title>Dissecting a delta table: just a bunch of JSON and parquet files</title><id>2c989c9d-d923-4461-a4be-edb9d45c7c85</id><updated>2026-05-06T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="programming"/><category term="data-engineering"/><category term="Spark"/><link href="https://sitegui.dev/post/2026/05/dissecting-a-delta-table-just-a-bunch-of-JSON-and-parquet-files" rel="alternate"/><published>2026-05-06T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/05/dissecting-a-delta-table-just-a-bunch-of-JSON-and-parquet-files" type="html">&lt;p&gt;Today I&apos;ll share some points that I&apos;ve learned while playing with Spark, Parquet and the Delta format. Even if you don&apos;t
use these technologies, I hope you can spot some neat ideas to reuse somewhere else later.&lt;/p&gt;
&lt;p&gt;I like to picture in my head that the most important architectural distinction between a datalake table and a typical
database (like Postgres) is that compute and storage are handled very separately: the table&apos;s data is stored in one
distributed system (typically a cloud object storage), while another distributed system (or even multiple ones) read and
write to those files that compose the table.&lt;/p&gt;
&lt;p&gt;There are competing formats to represent these tables, with distinct trade-offs of course: Delta, Iceberg, Hudi. But
from my research, I don&apos;t think there
is &lt;a href=&quot;https://datavidhya.com/blog/delta-lake-vs-apache-iceberg/&quot;&gt;anything fundamentally different&lt;/a&gt; between then. This post
will focus on Delta, but most of it should be easily transposable for the others.&lt;/p&gt;
&lt;p&gt;I like to understand technical solutions by framing the fundamental problems that they aim to solve best, so I&apos;ll
present it like that. Just remember that, although I have read
&lt;a href=&quot;https://github.com/delta-io/delta/blob/461fb09192cc11f4dd3a929eefc2b38830ef6e70/PROTOCOL.md&quot;&gt;the specification&lt;/a&gt; and
have used Spark with Delta tables for years, I didn&apos;t invent any of this: I&apos;m just an outside observer who
can be wrong. If you spot a misconception, please tell me in the comments!&lt;/p&gt;
&lt;h2&gt;How it solves its main challenges&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;continue-reading&quot; class=&quot;continue-reading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Very large tables: pruning files&lt;/h3&gt;
&lt;p&gt;The Delta format (and datalakes in general) aims to work effectively with very large tables, with petabytes
of data over trillions of rows. To allow for this, the data is divided into multiple files which are themselves divided
in chunks. The format then has ways to drastically reduce how many files and how much of these files need to be accessed
in order to answer queries.&lt;/p&gt;
&lt;p&gt;To cut early which files are read, Delta keeps track of some simple statistics about each column in each file: number of
nulls, maximum and minimum value. To give a concrete example, let&apos;s explore a very simple Delta table made of 3 files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;my-table/_delta_log/00000000000000000000.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;my-table/part-00000-0d56d77a-f779-46e3-adf2-7eaa14656c20-c000.zstd.parquet&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;my-table/part-00000-a6ac1d2e-304a-465e-b549-ccc442aa3855-c000.zstd.parquet&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;.parquet&lt;/code&gt; files contain the actual rows&apos; data, but put them aside for now: we&apos;ll talk about them in a minute.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;_delta_log/00000000000000000000.json&lt;/code&gt; file is a JSON-lines file that describes the table itself. It has a funny
name (not judging!), but you can imagine that it&apos;s somehow linked to history and evolution of the table. In it, each
parquet file that constitutes the table has a record like this:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;part-00000-0d56d77a-f779-46e3-adf2-7eaa14656c20-c000.zstd.parquet&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;partitionValues&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {},
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;size&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1040477&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;modificationTime&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1778102420000&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;dataChange&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;stats&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;numRecords&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:10,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;minValues&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:{&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:0,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;gz ovkfgoconqkwxf alpdjfuk rqvvb&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;edition&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:12},&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;maxValues&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:{&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:9,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;zulxlljs fizl qtivsko dkb&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;edition&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:90},&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;nullCount&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:{&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:0,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:0,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;edition&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:0,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;days&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:0,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;games&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:0,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;editors&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:0,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;visitors&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:0}}&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look who&apos;s there: &lt;code&gt;stats&lt;/code&gt;! It&apos;s a JSON inside a JSON, why not... Here, I&apos;ll format it so we can take a closer look:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;numRecords&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;minValues&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;gz ovkfgoconqkwxf alpdjfuk rqvvb&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;edition&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;12
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  },
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;maxValues&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;zulxlljs fizl qtivsko dkb&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;edition&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;90
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  },
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;nullCount&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;edition&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;days&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;games&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;editors&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;visitors&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool! So it has 10 rows, &lt;code&gt;id&lt;/code&gt; is between 0 and 9, &lt;code&gt;name&lt;/code&gt; is between &amp;quot;gz ovkfgoconqkwxf alpdjfuk rqvvb&amp;quot; and &amp;quot;zulxlljs
fizl qtivsko dkb&amp;quot;, no column has any null.&lt;/p&gt;
&lt;p&gt;This is a silly example, but in a more realistic data and query it can be very handy. Say you&apos;re doing&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;select &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;*
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; my_table
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;where&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; created_at &amp;gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;2026-01-01&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;#39;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A Delta-compatible engine will read the &lt;code&gt;00000000000000000000.json&lt;/code&gt; file to extract the list of all files, then use
the statistics to decide which files are even worth considering.&lt;/p&gt;
&lt;p&gt;But to be frank, I was somewhat shocked that the statistics are so simple (just max, min and null count)! If the column
you&apos;re searching on has naturally a similar range in all files (for example, it&apos;s a UUID v4, product name or event
kind), it seems to me that this file pruning will be much less effective. The Delta format has no such concept of
&amp;quot;indexes&amp;quot; as other databases have. Also, it surprised me that columns with array of structs don&apos;t get statistics at all!&lt;/p&gt;
&lt;h3&gt;Very large tables: reading less data&lt;/h3&gt;
&lt;p&gt;Delta uses the parquet format to store the rows. Parquet is pretty neat. If you have time to spare, go
and &lt;a href=&quot;https://github.com/apache/parquet-format/blob/96edf77704b60b6f3ca2232c218c64eff6c874d3/README.md&quot;&gt;read the summary&lt;/a&gt;
on the project&apos;s page. For our discussion right now, you only have to know that parquet divides the table horizontally
into &amp;quot;row groups&amp;quot; and that, in each row group, the data for each column is stored sequentially. The end of the file
contains a footer with the metadata and offsets for each column in each row group:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/05/dissecting-a-delta-table-just-a-bunch-of-JSON-and-parquet-files-01.png&quot; alt=&quot;simplified-parquet-sections&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So instead of reading each file in full, the Delta engine first reads only the footer; then depending on the queried
columns it reads only the segments of the file that are of interest. This is very important for datalake tables, since
the files are usually stored in a separate cloud service.&lt;/p&gt;
&lt;p&gt;Small parenthesis here: this separate cloud service is usually a &amp;quot;dumb&amp;quot; object storage, but it&apos;s extra cool when you
realise that it can be something much more refined! For example, it can be a service that implements authorisation over
which columns the requesting user can access to protect sensitive information.&lt;/p&gt;
&lt;p&gt;Let&apos;s take a train back to the statistics of the columns. While the Delta format keeps some very simple statistics as
part of the JSON file that links each parquet file to the table, the parquet itself is more sophisticated! First,
because it does so per row group. Second, because it can store the list of unique values (if it&apos;s small) or a &lt;a href=&quot;https://parquet.apache.org/docs/file-format/bloomfilter/&quot;&gt;bloom
filter&lt;/a&gt; (if there are many distinct values).&lt;/p&gt;
&lt;h3&gt;Very large tables: distributed reading&lt;/h3&gt;
&lt;p&gt;That&apos;s easy: the table is split into multiple parquet files, each one may be further broken down in row groups. Each
row group can be handled independently, so a distributed system (say a cluster with many machines and cores) can divide
the work to reduce total processing time.&lt;/p&gt;
&lt;h3&gt;Consistent data schema and schema evolution&lt;/h3&gt;
&lt;p&gt;A typical use case for datalake tables is to keep a history of multiple months or even years of data. Just like a SQL
database like Postgres, each table has a schema (set of column names and types), which has to be migrated as the
underlying data evolves. However, unlike Postgres, the allowed changes in the schema are designed so that historical
data (that is, old parquet files) don&apos;t need to be rewritten.&lt;/p&gt;
&lt;p&gt;In practice, in the example table above the &lt;code&gt;00000000000000000000.json&lt;/code&gt; file also contains a record like:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;metaData&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;6a8b2481-493a-4796-a77d-ed5e1b70a1e1&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;provider&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;parquet&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {}
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    },
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;schemaString&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;fields&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;:[...]}&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;partitionColumns&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: [],
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;configuration&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: {},
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;createdTime&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1778102418300
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, another JSON in a JSON in &lt;code&gt;schemaString&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Any evolution to the schema will produce a new &lt;code&gt;metaData&lt;/code&gt; record with the full updated schema.&lt;/p&gt;
&lt;p&gt;So that no previous parquet file needs to rewritten, Delta only allows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;adding a new column: reading a previous parquet file will assume &lt;code&gt;null&lt;/code&gt; for it&lt;/li&gt;
&lt;li&gt;removing a column: the data is left in previous parquet files, but no longer visible for the engine&lt;/li&gt;
&lt;li&gt;changing the order of a column: it&apos;s just superficial&lt;/li&gt;
&lt;li&gt;some specific change of types, called type &amp;quot;widening&amp;quot;: readers have to convert on the fly as needed&lt;/li&gt;
&lt;li&gt;renaming a column: requires &amp;quot;column mapping&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This last one is the most complex one and has to be enabled in more recent Delta versions. With this, the name of the
column in the parquet files is just a UUID. The Delta metadata in the JSON file then maps each UUID to a user-visible
column name.&lt;/p&gt;
&lt;p&gt;The schema string is quite large, and it has to be repeated in its entirety every time it evolves. I guess for most
tables that&apos;s negligible, but we can imagine a pathological case for tables with thousands of columns that evolve
frequently.&lt;/p&gt;
&lt;h3&gt;Append data&lt;/h3&gt;
&lt;p&gt;Many use cases of Delta tables require adding more data to it in batches, usually by some automatic ingestion pipeline
every hour or day. Delta deals with this by versioning the table and splitting the operation into two distinct steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;write new parquet files&lt;/li&gt;
&lt;li&gt;commit the new version&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first step can take an arbitrarily long time, and multiple concurrent writers can do this at the same time.
Crucially, these new files are &amp;quot;dangling&amp;quot; so far and no reader can see them.&lt;/p&gt;
&lt;p&gt;The second step has to be done atomically by writing a new versioned file with the next version number. In our examples
above, it would be &lt;code&gt;00000000000000000001.json&lt;/code&gt;. All these JSON files have to be read and merged in sequence by future
readers.&lt;/p&gt;
&lt;p&gt;Delta uses &amp;quot;optimistic&amp;quot; concurrency. To illustrate, when two writers work in parallel they can do step 1 at their own
pace, but will have to serially do step 2. In the example below, writer 1 commits first, so writer 2 has to check that
what writer 1 has done does not conflict with its work, then commit. For appending data that&apos;s usually fine, but for
updates it may require writer 2 to start over.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/05/dissecting-a-delta-table-just-a-bunch-of-JSON-and-parquet-files-02.png&quot; alt=&quot;a timeline of changes illustrating the optimistic concurrent model&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However, note how this architecture has one great weakness: each addition requires writing a new parquet file then
commiting a new version. Also, every future reader has to read all the small JSON files to glue them all together. So
Delta is a catastrophic format if you want to do multiple updates every second!&lt;/p&gt;
&lt;p&gt;To mitigate the ever-increasing number of JSON files to read, the writer may decide to compact the history. For example:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;my-table/_delta_log/00000000000000000000.json
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;my-table/_delta_log/00000000000000000001.json
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;my-table/_delta_log/00000000000000000002.json
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;my-table/_delta_log/00000000000000000003.json
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;my-table/_delta_log/00000000000000000004.json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;can be compacted as&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;my-table/_delta_log/00000000000000000004.json
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;my-table/_delta_log/00000000000000000004.checkpoint.parquet
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;.checkpoint.parquet&lt;/code&gt; file containing the &lt;code&gt;add&lt;/code&gt; entries of all versions so far, and the &lt;code&gt;.json&lt;/code&gt; file containing only
the last &lt;code&gt;metaData&lt;/code&gt; entry.&lt;/p&gt;
&lt;h3&gt;Delete and update data&lt;/h3&gt;
&lt;p&gt;Delta represents &amp;quot;removed&amp;quot; data without actually removing them 😜. It uses &amp;quot;delete vector&amp;quot;: a new binary file that
describes which rows in the parquet files should be considered removed.&lt;/p&gt;
&lt;p&gt;Again, this produces a new version with a &lt;code&gt;delete&lt;/code&gt; entry, and the same optimistic concurrency model and compaction logic
described above apply.&lt;/p&gt;
&lt;p&gt;Delta can update data by either producing a &lt;code&gt;delete&lt;/code&gt; followed by an &lt;code&gt;add&lt;/code&gt; in the same version, or using a dedicated
&lt;code&gt;cdc&lt;/code&gt; (which stands for change data capture). I could not learn enough about the CDC feature, so I&apos;ll not try to explain
it here.&lt;/p&gt;
&lt;h3&gt;Optimisation and clean-up&lt;/h3&gt;
&lt;p&gt;As you may imagine, as the table evolves due to writes, the data may become too much fragmented and removed data no
longer accessible by readers still taking space, etc. A Delta engine implements a &lt;code&gt;VACCUM&lt;/code&gt; and &lt;code&gt;OPTIMIZE&lt;/code&gt; commands to
rewrite recently added and modified parquet files.&lt;/p&gt;
&lt;p&gt;There&apos;s a sweet spot: too many parquet files bring a lot of metadata overhead, too few don&apos;t allow enough parallelism.&lt;/p&gt;
&lt;p&gt;Also, newer implementations sort and divide the rows in the row groups and parquet files to improve the
relevance of the column statistics for file and row-group pruning. They&apos;ve named this feature &amp;quot;liquid clustering&amp;quot;, which
my brain finds too much marketing-y.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;span class=&quot;fun&quot;&gt;That&apos;s all for now, thanks for reading!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Delta is a pretty neat format that solves some hard problems not with magic, but
with a bunch of JSON and parquet files. At the same time, its design also brings some major pain points... I hope that
you&apos;ve learned something new, &apos;cause I certainly did.&lt;/p&gt;
</content></entry><entry><title>My homelab: an old laptop behind the fridge</title><id>e4021a1e-ab78-4d0f-89bd-fb7cd3ef7101</id><updated>2026-03-30T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="homelab"/><link href="https://sitegui.dev/post/2026/03/my-homelab-an-old-laptop-behind-the-fridge" rel="alternate"/><published>2026-03-30T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/03/my-homelab-an-old-laptop-behind-the-fridge" type="html">&lt;p&gt;This very website is served from my home by an Ubuntu Server running on old hardware behind my fridge:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/03/my-homelab-an-old-laptop-behind-the-fridge/setup.png&quot; alt=&quot;my homelab setup&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve talked &lt;a href=&quot;https://www.meetup.com/human-talks-angers/&quot;&gt;in a local dev meetup&lt;/a&gt; about my setup, you can check the slides
here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sitegui.dev/post/2026/03/my-homelab-an-old-laptop-behind-the-fridge/homelab_en.pdf&quot;&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/03/my-homelab-an-old-laptop-behind-the-fridge/slides.png&quot; alt=&quot;my slides&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My homelab is at the same time a &amp;quot;production&amp;quot; environment for my digital life and &amp;quot;staging&amp;quot; environment for all sort of
crazy project that I want to play with. It&apos;s amazing what 10-year-old hardware is capable of!&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;continue-reading&quot; class=&quot;continue-reading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Maybe someday I&apos;ll invest into another second-hand to create separate environments.&lt;/p&gt;
&lt;p&gt;All my homelab config is available &lt;a href=&quot;https://git.sitegui.dev/sitegui/home-lab&quot;&gt;this repo in my git forge&lt;/a&gt; (also itself
hosted behind the fridge, as you may have guessed 😜).&lt;/p&gt;
&lt;p&gt;In a future post, I want to take the layers of my setup apart and write about the different problems that I&apos;ve solved
and solutions that I&apos;ve found. Until next time: &lt;span class=&quot;fun&quot;&gt;go and play!&lt;/span&gt; The world is large and there are a
lot of very good
self-hostable projects around. You can start simple.&lt;/p&gt;
</content></entry><entry><title>Performance advice nugget 01: use flat representations</title><id>1ade03c4-f729-41e0-a0ed-623336d2be6d</id><updated>2026-03-21T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="programming"/><category term="performance"/><category term="PAN"/><link href="https://sitegui.dev/post/2026/03/performance-advice-nugget-1-use-flat-representations" rel="alternate"/><published>2026-03-21T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/03/performance-advice-nugget-1-use-flat-representations" type="html">&lt;p&gt;This is my last week on my current job, and I was reflecting about some of the things that I&apos;ve learned in the past 7
years there. There&apos;s a lot of course! As a staff data engineer, I worked with many teams and codebases and learned a
couple of tricks at scale. So I&apos;m starting a new blog series &amp;quot;performance advice nugget&amp;quot; (or &lt;em&gt;PAN&lt;/em&gt; for short), in which
I&apos;ll share some insights of what worked quite well in practice.&lt;/p&gt;
&lt;p&gt;So welcome to &lt;em&gt;PAN&lt;/em&gt; 01: &lt;strong&gt;use flat representations&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I&apos;ll try to make these posts quite short. As usual with everything related to &amp;quot;performance&amp;quot;, you should
always measure and benchmark with your real workload, and always balance whether additional complexity is worth the
performance gains.&lt;/p&gt;
&lt;p&gt;Nice, forewords are said and out of the way. Let&apos;s focus on the matter: imagine that you are handling a data that has
multiple levels, for example, a paragraph, that is made of sentences, each made of words, each made of characters:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/03/performance-advice-nugget-1-use-flat-representations-01.png&quot; alt=&quot;a paragraph, made of setences, words and characters&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;continue-reading&quot; class=&quot;continue-reading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One straight way to model this is with lists of lists:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;class &lt;/span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;Paragraph&lt;/span&gt;&lt;span style=&quot;color:#f2f0ec;&quot;&gt;:
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    sentences: list[Sentence]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;class &lt;/span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;Sentence&lt;/span&gt;&lt;span style=&quot;color:#f2f0ec;&quot;&gt;:
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    words: list[Word]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;class &lt;/span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;Word&lt;/span&gt;&lt;span style=&quot;color:#f2f0ec;&quot;&gt;:
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    chars: list[str]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;data = &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Paragraph&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;([
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Sentence&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;([
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Word&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;([&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;H&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;o&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;]),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Word&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;([&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;W&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;o&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;d&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;]),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    ]),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Sentence&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;([
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Word&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;([&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;B&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;]),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    ]),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;])
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: don&apos;t overthink &lt;code&gt;Word&lt;/code&gt; in this synthetic example! In a more realistic setup, &lt;code&gt;Word&lt;/code&gt; could probably just be a
&lt;code&gt;str&lt;/code&gt;. But here I&apos;m using it to illustrate the &amp;quot;layered&amp;quot; modeling.&lt;/p&gt;
&lt;p&gt;Today&apos;s &lt;em&gt;PAN&lt;/em&gt; is: don&apos;t nest the lists! Instead, keep the leaf information (here the chars) flat in a single list.
Then use other lists to index into the flat representation:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;class &lt;/span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;FlatParagrah&lt;/span&gt;&lt;span style=&quot;color:#f2f0ec;&quot;&gt;:
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    characters: list[str]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    words: list[WordInfo]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    sentences: list[SentenceInfo]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;class &lt;/span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;WordInfo&lt;/span&gt;&lt;span style=&quot;color:#f2f0ec;&quot;&gt;:
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    num_chars: int
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;class &lt;/span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;SentenceInfo&lt;/span&gt;&lt;span style=&quot;color:#f2f0ec;&quot;&gt;:
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    num_chars: int
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    num_words: int
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;data = &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FlatParagrah&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;characters&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;=[&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;H&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;o&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;W&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;o&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;d&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;B&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;],
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;words&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;=[
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;WordInfo&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;WordInfo&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;WordInfo&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    ],
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;sentences&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;=[
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SentenceInfo&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;num_chars&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;num_words&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SentenceInfo&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;num_chars&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;num_words&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    ],
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why? Two main reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CPU caching&lt;/strong&gt;: CPUs are more efficient when accessing data that is packed closer together&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;less allocations&lt;/strong&gt;: each list has to be allocated and deallocated individually. The flat design has a fixed and
small number of lists&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Is it true?&lt;/h2&gt;
&lt;p&gt;Well... as it&apos;s usual with data structures, it will highly depend on what you actually do with the data! In our case, it
worked wonders.&lt;/p&gt;
&lt;p&gt;I&apos;ve prepared a micro benchmark in Rust to illustrate, you can check the source code
for &lt;a href=&quot;https://git.sitegui.dev/sitegui/performance-advice-nuggets/src/commit/9dd505f95117336a620ff9d78c0aa561dfa834a1/src/paragraph.rs&quot;&gt;Paragraph&lt;/a&gt;
and &lt;a href=&quot;https://git.sitegui.dev/sitegui/performance-advice-nuggets/src/commit/9dd505f95117336a620ff9d78c0aa561dfa834a1/src/flat_paragraph.rs&quot;&gt;FlatParagraph&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Paragraph&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;FlatParagraph&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Gain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;2100 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;370 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;5.7 x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deallocate&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;460 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;39 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;12 x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iterate over words&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;91 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;100 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;0.91 x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iterate over chars&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;110 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;32 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;3.4 x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create with inserted word&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;2400 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;350 ns&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;6.9 x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The last operation is the one that our codebase did the most: create a new Paragraph from an existing one by inserting a
new word in the middle of it. Something like:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;class &lt;/span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;Paragraph&lt;/span&gt;&lt;span style=&quot;color:#f2f0ec;&quot;&gt;:
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;def &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;with_inserted_word&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;paragraph_i&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: int,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;word_i&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: int,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;word&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: list[int],
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    ) -&amp;gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;Paragraph&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;#39;:
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;pass
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we&apos;ve used a lot Rust&apos;s type system to provide an ergonomic and safe API for the rest of the codebase to handle the
data, allowing it to cut and slice the &amp;quot;paragraph&amp;quot; however necessary.&lt;/p&gt;
</content></entry><entry><title>Cooperative Buffon π</title><id>d0743a38-5176-48bf-b284-aed766b4ffb4</id><updated>2026-03-14T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="maths"/><category term="programming"/><link href="https://sitegui.dev/post/2026/03/cooperative-buffon-pi" rel="alternate"/><published>2026-03-14T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/03/cooperative-buffon-pi" type="html">&lt;p&gt;Happy &lt;a href=&quot;https://en.wikipedia.org/wiki/Pi_Day&quot;&gt;pi day&lt;/a&gt;! (not to be confused with pie day, which has more pastry but is
less sweat).&lt;/p&gt;
&lt;p&gt;I need your help: please refresh this page to throw more matches to the ground!&lt;br /&gt;
I&apos;ll use &lt;a href=&quot;https://en.wikipedia.org/wiki/Buffon%27s_needle_problem&quot;&gt;Comte de Buffon&apos;s result&lt;/a&gt; to estimate π.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/special/cooperative-buffon-pi.svg&quot; alt=&quot;matches on the floor&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The underlying relation is:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;                          2 * match_size
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;P(match_crosses_a_line) = --------------
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;                           π * line_gap
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;continue-reading&quot; class=&quot;continue-reading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So π can be estimated as:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    2 * match_size
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;π ~ --------------
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;     P * line_gap
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the image above &lt;code&gt;match_size/line_gap&lt;/code&gt; = 1/2, so this reduces to&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    1
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;π ~ -
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    P
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can
check &lt;a href=&quot;https://git.sitegui.dev/sitegui/blog/src/commit/3652ce1f7f5b9293ab2cd34f0cfdf12c9ca14679/src/special/cooperative_buffon_pi.rs&quot;&gt;the source code&lt;/a&gt;
here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: a friend told me that it would be nice to know which of the matches you&apos;ve just thrown into the floor. So
I&apos;ve added a circle around it :)&lt;/p&gt;
</content></entry><entry><title>Understanding Bytes in Rust, one bit at a time</title><id>9aaf95cc-858e-40df-bf2c-a53b8f53668f</id><updated>2026-02-27T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="programming"/><category term="rust"/><category term="bytes"/><link href="https://sitegui.dev/post/2026/02/understanding-bytes-in-rust-one-bit-at-a-time" rel="alternate"/><published>2026-02-27T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/02/understanding-bytes-in-rust-one-bit-at-a-time" type="html">&lt;p&gt;In Rust, the tokio&apos;s ecosystem has a fundamental crate called &lt;a href=&quot;https://crates.io/crates/bytes&quot;&gt;&lt;code&gt;bytes&lt;/code&gt;&lt;/a&gt; that abstracts
and helps dealing with bytes (you don&apos;t say!). I&apos;ve indirectly used it a billion times and I thought that I had a good
mental model of how it worked.&lt;/p&gt;
&lt;p&gt;So, in the spirit of the &lt;a href=&quot;https://www.youtube.com/playlist?list=PLqbS7AVVErFirH9armw8yXlE6dacF-A6z&quot;&gt;&amp;quot;decrusting&amp;quot; series&lt;/a&gt;
by the excellent Jon Gjengset, I&apos;ve decided to peek behind the curtains to understand more what axum, tokio, hyper and
the kind do to them bytes! The code is well written, but surprisingly complex. I understand now what it does, but I
still don&apos;t fully grasp &lt;em&gt;why&lt;/em&gt; it does some things in a certain way.&lt;/p&gt;
&lt;p&gt;I&apos;m ready to share with you my discoveries. I hope that you are sitting, laying or squatting comfortably. This is the
first post in a small series. I&apos;m legally required by my marketing department to remind you that you can &lt;a href=&quot;#end-of-page&quot;&gt;subscribe to
my low-traffic newsletter&lt;/a&gt;, so that you&apos;ll know when new posts are up!&lt;/p&gt;
&lt;p&gt;A quick note before we start: this posts is based on the current &lt;code&gt;bytes&lt;/code&gt; version 1.11.1.&lt;/p&gt;
&lt;h2&gt;What the Bytes?&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;continue-reading&quot; class=&quot;continue-reading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;bytes&lt;/code&gt; crate does many things, but its public API is quite small: it basically has two structs and two traits.
Quoting from the documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;struct Bytes&lt;/code&gt;: a cheaply cloneable and sliceable chunk of contiguous memory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;struct BytesMut&lt;/code&gt;: a unique reference to a contiguous slice of memory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trait Buf&lt;/code&gt;: read bytes from a buffer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trait BufMut&lt;/code&gt;: a trait for values that provide sequential write access to bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this series, I&apos;ll focus on the &lt;code&gt;Bytes&lt;/code&gt; struct, because yeah, it&apos;s not as simple as one may be led to believe!&lt;/p&gt;
&lt;h2&gt;A compelling example&lt;/h2&gt;
&lt;p&gt;I would like to start concrete, with an example of what the problem that &lt;code&gt;Bytes&lt;/code&gt; helps solve: zero-copy parsing.
Imagine that you&apos;re parsing HTTP requests and your code receives these bytes from the network:&lt;/p&gt;
&lt;pre style=&quot;background-color:#111; color:#eee&quot;&gt;
&lt;span style=&quot;background-color:#633&quot;&gt;GET&lt;/span&gt; &lt;span style=&quot;background-color:#363&quot;&gt;/post/2026/some-post&lt;/span&gt; &lt;span style=&quot;background-color:#336&quot;&gt;HTTP/2&lt;/span&gt;
&lt;span style=&quot;background-color:#366&quot;&gt;Host&lt;/span&gt;: &lt;span style=&quot;background-color:#636&quot;&gt;sitegui.dev&lt;/span&gt;
&lt;span style=&quot;background-color:#366&quot;&gt;Accept&lt;/span&gt;: &lt;span style=&quot;background-color:#636&quot;&gt;text/html&lt;/span&gt;
&lt;span style=&quot;background-color:#366&quot;&gt;Referer&lt;/span&gt;: &lt;span style=&quot;background-color:#636&quot;&gt;https://sitegui.dev/&lt;/span&gt;
&lt;span style=&quot;background-color:#366&quot;&gt;Connection&lt;/span&gt;: &lt;span style=&quot;background-color:#636&quot;&gt;keep-alive&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You would like to efficiently parse it into a struct like this:&lt;/p&gt;
&lt;pre style=&quot;background-color:#111; color:#eee&quot;&gt;
struct Request {
    method: &lt;span style=&quot;background-color:#633&quot;&gt;Something&lt;/span&gt;,
    path: &lt;span style=&quot;background-color:#363&quot;&gt;Something&lt;/span&gt;,
    version: &lt;span style=&quot;background-color:#336&quot;&gt;Something&lt;/span&gt;,
    /// Note: headers can repeat, so I&apos;m using a
    /// `Vec` not a `HashMap` to represent them
    headers: Vec&lt;(&lt;span style=&quot;background-color:#366&quot;&gt;Something&lt;/span&gt;, &lt;span style=&quot;background-color:#636&quot;&gt;Something&lt;/span&gt;)&gt;,
}
&lt;/pre&gt;
&lt;p&gt;This post is not about the parsing bit, instead it&apos;s about the bytes themselves. What should we choose as &lt;code&gt;Something&lt;/code&gt;
above?&lt;/p&gt;
&lt;p&gt;If you chose a type like &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;, your parser would need to allocate and copy the information into many
multiple instances of &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;. But with &lt;code&gt;Bytes&lt;/code&gt;, all these instances are cheap to produce as they share the same
storage (your initial bytes are represented only once) and each instance then has a &amp;quot;window&amp;quot; to
this buffer. The underlying buffer will only be deallocated when &lt;em&gt;all&lt;/em&gt; the instances sharing it are dropped.&lt;/p&gt;
&lt;p&gt;As usual in computing, using &lt;code&gt;Bytes&lt;/code&gt; (or any &amp;quot;shared&amp;quot; construction) vs &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; (or any &amp;quot;owned&amp;quot;
construction) is a matter of trade-offs. For example, if your parser code only produces output that references a
small part of the input, it&apos;s probably better to copy over just that. Otherwise, the &lt;code&gt;Bytes&lt;/code&gt; instances will keep the
whole input around in memory.&lt;/p&gt;
&lt;h2&gt;An initial mental model&lt;/h2&gt;
&lt;p&gt;As I said before, I had a useful mental model before embarking in this exploration: a &lt;code&gt;Bytes&lt;/code&gt; instance simply has a
reference-counted shared reference to the actual bytes and some information to where in the buffer those bytes are.
The fundamental operations available are:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;trait &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;BytesStorage {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// Create a new instance with some data
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// It should &amp;quot;adopt&amp;quot; the data, not copy it
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Vec&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;gt;) -&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;Self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// Create a new instance that references some part of this data
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// Constant time, and it should not copy the actual data
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;range&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Range&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;usize&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;gt;) -&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;Self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// Return the bytes slice
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// Very cheap
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;as_ref&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;) -&amp;gt; &amp;amp;[&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;];
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could draft a similar-in-spirit implementation using &lt;code&gt;Arc&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;pub struct &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;SharedBytes {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;range&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Range&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;usize&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Arc&amp;lt;Vec&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;gt;&amp;gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;impl &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;BytesStorage &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;for &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;SharedBytes {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Vec&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;gt;) -&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;Self &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;Self &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            range: &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;len&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            data: Arc::new(data),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;range&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Range&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;usize&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;gt;) -&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;Self &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;// Translate the &amp;quot;local&amp;quot; range to a &amp;quot;global&amp;quot; range
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; root_range = range.start + &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;.range.start..range.end + &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;.range.start;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;Self &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            range: root_range,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            data: &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;.data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;clone&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;as_ref&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;) -&amp;gt; &amp;amp;[&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;] {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;.data[&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;.range.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;clone&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;()]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note how &lt;code&gt;new()&lt;/code&gt; does not clone the data, instead the &lt;code&gt;Vec&lt;/code&gt; is adopted into the &lt;code&gt;SharedBytes&lt;/code&gt; instance.
Also, &lt;code&gt;slice()&lt;/code&gt; clones the &lt;code&gt;Arc&amp;lt;_&amp;gt;&lt;/code&gt;, which uses reference counting and avoids cloning the actual data in the &lt;code&gt;Vec&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, &lt;code&gt;as_ref()&lt;/code&gt; is not very good for two reasons: Rust will execute bound checks to ensure that the range is valid
at every access, and the actual buffer is behind two memory references &lt;code&gt;Arc&lt;/code&gt; (in &lt;code&gt;SharedBytes&lt;/code&gt;) -&amp;gt; &lt;code&gt;Vec&lt;/code&gt; -&amp;gt; buffer.&lt;/p&gt;
&lt;p&gt;The actual &lt;code&gt;Bytes&lt;/code&gt; implementation avoids these two problems by storing the slice components (pointer to first byte and
length) as two fields directly in the struct, trading off some bloat in the struct and a small performance hit in the
&lt;code&gt;slice()&lt;/code&gt; operation for a simpler &lt;code&gt;as_slice()&lt;/code&gt;:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;pub struct &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;Bytes {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;ptr&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: *const &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;len&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;usize&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;,
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;// ... more fields ...
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;impl &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;Bytes {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    #[&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;inline&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;as_slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;) -&amp;gt; &amp;amp;[&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;] {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;unsafe &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{ slice::from_raw_parts(&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;.ptr, &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;.len) }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Let&apos;s test our mental model&lt;/h2&gt;
&lt;p&gt;In Rust, you can set a different global memory allocator like this:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;std::alloc::{GlobalAlloc, Layout, System};
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// A global allocator that can track all allocations made
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;struct &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;TrackingAllocator;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;// Ask Rust to use our allocator as the global allocator
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;#[&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;global_allocator&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;]
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;static &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;ALLOCATOR&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: TrackingAllocator = TrackingAllocator;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;unsafe impl &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;GlobalAlloc &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;for &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;TrackingAllocator {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;unsafe fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;alloc&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;layout&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Layout) -&amp;gt; *&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;mut u8 &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;// Do stuff
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;unsafe &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{ System.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;alloc&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(layout) }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;unsafe fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;dealloc&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;ptr&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: *&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;mut u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;layout&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Layout) {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;// Do stuff
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;unsafe &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{ System.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;dealloc&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(ptr, layout) };
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There
is &lt;a href=&quot;https://github.com/rust-lang/wg-allocators&quot;&gt;a Rust working group to have a more fine control of the allocator&lt;/a&gt;,
allowing us to overwrite it only for some parts of the code. But for this reduced example, it&apos;s okay to capture all
allocations.&lt;/p&gt;
&lt;p&gt;I used this mechanism to track exactly which allocations and deallocations the code was doing for &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;, our own
&lt;code&gt;SharedBytes&lt;/code&gt; and &lt;code&gt;bytes::Bytes&lt;/code&gt; and where in the source code they
happened. &lt;a href=&quot;https://git.sitegui.dev/sitegui/blog-post-understanding-bytes&quot;&gt;You can check the full code here&lt;/a&gt;,
if you are curious or want to reproduce my work.&lt;/p&gt;
&lt;p&gt;To simulate the HTTP parser, I&apos;m using this simple code:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse_request&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;lt;B: BytesStorage&amp;gt;(&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;: Vec&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;u8&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;gt;) -&amp;gt; Request&amp;lt;B&amp;gt; {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; data = B::new(data);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    Request {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        method: data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        path: data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        version: data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;25&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;31&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        headers: vec![
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            (data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;33&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;37&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;), data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;39&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;)),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            (data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;52&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;58&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;), data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;60&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;69&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;)),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            (data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;71&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;78&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;), data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;80&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;)),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;            (data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;102&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;112&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;), data.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;114&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;124&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;)),
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        ],
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    }
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following table shows what each line of the code above allocates for &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;, &lt;code&gt;SharedBytes&lt;/code&gt; and &lt;code&gt;bytes::Bytes&lt;/code&gt;, in
bytes.&lt;/p&gt;
&lt;p&gt;But before looking at the results, take a while to run this code against your mental model and try to predict
how many allocations will occur for each implementation and where that will happen.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code source&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;&lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;&lt;code&gt;SharedBytes&lt;/code&gt;&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;&lt;code&gt;bytes::Bytes&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data = B::new(data)&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;40&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;method: data.slice(0..3)&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;3&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;path: data.slice(4..24)&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;20&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;version: data.slice(25..31)&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;6&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vec![...]&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;192&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;192&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8x &lt;code&gt;data.slice(...)&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;57&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;298&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;232&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;280&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As I expected, &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; allocates a new independent buffer in every call to &lt;code&gt;.slice()&lt;/code&gt;, with the necessary capacity for
the data piece then copy it. &lt;code&gt;SharedBytes&lt;/code&gt; allocates the &lt;code&gt;Arc&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt; once at the start, and later the
&lt;code&gt;Vec&amp;lt;(B, B)&amp;gt;&lt;/code&gt; with 8
× 24 bytes.&lt;/p&gt;
&lt;p&gt;I want to explore in a separate blog post the whys and the hows &lt;code&gt;Arc&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt; has 40 bytes, and also compare it
against &lt;code&gt;Arc&amp;lt;[u8]&amp;gt;&lt;/code&gt;. Stay
tuned!&lt;/p&gt;
&lt;p&gt;The behavior of &lt;code&gt;Bytes&lt;/code&gt; was interesting to me for two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it somehow defers the first allocation: &lt;code&gt;B::new()&lt;/code&gt; doesn&apos;t allocate (in this case), but the first call to &lt;code&gt;.slice()&lt;/code&gt;
does. Spoiler alert: it&apos;s pretty neat but complex. I&apos;ll explore it in details later on.&lt;/li&gt;
&lt;li&gt;it&apos;s big! 32 bytes each instance. So &lt;code&gt;Vec&amp;lt;(B, B)&amp;gt;&lt;/code&gt; has 8 × 32 bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;No &lt;code&gt;Arc&lt;/code&gt; in sight&lt;/h2&gt;
&lt;p&gt;Armed with the above initial mental model, it&apos;s not a surprise that the documentation and comments in the file
implementing &lt;code&gt;Bytes&lt;/code&gt; mention &lt;code&gt;Arc&lt;/code&gt; 6 times. What is a bit eyebrow-raising is that the code itself does not use &lt;code&gt;Arc&lt;/code&gt;
at all!&lt;/p&gt;
&lt;p&gt;Maybe it&apos;s related to the defered allocation we saw before? Maybe it&apos;s related to the 32 bytes needed for each instance?
Well, kind of...&lt;/p&gt;
&lt;p&gt;I&apos;ll use memory diagrams like this one below to represent what the structs (in grey) are made of. Simple numerical
fields are in yellow and pointers are in green. In parentheses, I&apos;ve put the memory size in bytes, assuming a 64-bit
system.&lt;/p&gt;
&lt;p&gt;For example, in Rust you can represent a &lt;em&gt;growable&lt;/em&gt; owned sequence with &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt; and a &lt;em&gt;fixed&lt;/em&gt; owned sequence
with &lt;code&gt;Box&amp;lt;[T]&amp;gt;&lt;/code&gt;. The physical difference is that &lt;code&gt;Vec&lt;/code&gt; may have allocated more space than it is currently using, to
amortize buffer grow on inserts, so it uses an additional &lt;code&gt;capacity&lt;/code&gt; field. A &lt;code&gt;Box&lt;/code&gt; uses exactly the bufffer&apos;s size, so
length and capacity are the same. Converting from a &lt;code&gt;Vec&lt;/code&gt; that is &amp;quot;full&amp;quot; (that is, for which capacity is the same as
length) is cheap because the underlying buffer is reused:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/02/understanding-bytes-in-rust-one-bit-at-a-time/vec_to_box.png&quot; alt=&quot;Converting a Vec to Box&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Here&apos;s a Box. Give-me Bytes&lt;/h2&gt;
&lt;p&gt;The implementation of &lt;code&gt;Bytes::from(Vec&amp;lt;u8&amp;gt;)&lt;/code&gt; will first check if it&apos;s a &amp;quot;full&amp;quot; Vec to convert it into a
&lt;code&gt;Box&amp;lt;[u8]&amp;gt;&lt;/code&gt; and build a &lt;code&gt;Bytes&lt;/code&gt; instance like below. When the &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; it not &amp;quot;full&amp;quot;, a different logic is used, but
let&apos;s
put that aside for now.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/02/understanding-bytes-in-rust-one-bit-at-a-time/box_to_bytes.png&quot; alt=&quot;Converting a Box to Bytes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That&apos;s a lot to unpack. I&apos;ll explain what I&apos;ve understood of the design and the trade-offs of this representation:&lt;/p&gt;
&lt;p&gt;First, note that the buffer is reused, as expected.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;length&lt;/code&gt; fields simply represent the slice information. It seems a bit redundant with the &lt;code&gt;data&lt;/code&gt;
field, that also points to the buffer. However, this distinction will be useful when we slice this data: the start
of our &amp;quot;window&amp;quot; will be different from the buffer&apos;s start, and we need to keep the buffer&apos;s start around so that we
can deallocate it when the moment comes.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;vtable&lt;/code&gt; field implements the &amp;quot;dynamic dispatch&amp;quot; pattern. Rust has &amp;quot;trait objects&amp;quot; to implement this pattern with
&lt;code&gt;&amp;amp;dyn SomeTrait&lt;/code&gt; or &lt;code&gt;Box&amp;lt;dyn SomeTrait&amp;gt;&lt;/code&gt;, but I guess the designers of the crate didn&apos;t use them because this pattern
has restrictions that are a deal-breaker for this use. If you know more why, please tell me!&lt;/p&gt;
&lt;p&gt;Note that &lt;code&gt;vtable&lt;/code&gt; points to a statically declared instance of &lt;code&gt;VTable&lt;/code&gt; called &amp;quot;promotable&amp;quot;. Aha! This name is related
to how &lt;code&gt;Bytes&lt;/code&gt; deferr allocation as we observed earlier.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;data&lt;/code&gt; field is funny-looking (no judgements!): it&apos;s an &lt;code&gt;AtomicPtr&amp;lt;_&amp;gt;&lt;/code&gt;, that unlike a normal pointer, uses atomic
operations to read/write to it. But why do we need atomic operations here? Spoiler alert: it&apos;s the deferred allocation
again. Somehow (we&apos;ll see next), this pointer will point to something else in the future.&lt;/p&gt;
&lt;p&gt;Continuing on the &lt;code&gt;data&lt;/code&gt; field, it&apos;s not just a pointer. It uses the pattern of &amp;quot;tagged pointer&amp;quot; to store one bit of
information there. I think you can already guess it: it&apos;s the deferred allocation again. This trick avoids having a
separate field for this, but it requires the code to ensure and check that the pointer to the buffer is always a
multiple of 2, so that the least bit carries no useful information and can be co-opted as a tag.&lt;/p&gt;
&lt;h2&gt;The other side of the river&lt;/h2&gt;
&lt;p&gt;Let&apos;s spring straight into action: what happens when we slice our newly-created &lt;code&gt;Bytes&lt;/code&gt;? I&apos;ve tried my best to
convey everything that happens in the diagram below without making it too busy. Take some time to navigate it first.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/02/understanding-bytes-in-rust-one-bit-at-a-time/slice_promotable.png&quot; alt=&quot;Slicing a promotable Bytes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;First, note that calling &lt;code&gt;.slice(&amp;amp;self)&lt;/code&gt; the first time actually modifies the original &lt;code&gt;Bytes&lt;/code&gt;: the previous state of
the instance is represented in a dashed countour, the new state uses red on the changed field: &lt;code&gt;data&lt;/code&gt;. Naturally, only
this field can be modified, because &lt;code&gt;.slice(&amp;amp;self)&lt;/code&gt; takes a shared reference, so only internal mutation is possible,
through the
&lt;code&gt;AtomicPtr&amp;lt;_&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As we&apos;ve observed earlier, a new piece of data is allocated: an instance of the &lt;code&gt;Shared&lt;/code&gt; struct with 24 bytes. It
carries information about the original buffer that will be used to deallocate (&lt;code&gt;buffer&lt;/code&gt; and &lt;code&gt;capacity&lt;/code&gt;). I was surprised
to learn that in Rust the original size of the buffer is actually necessary to deallocate it. I was used to C&apos;s
&lt;code&gt;free(ptr)&lt;/code&gt; that clearly doesn&apos;t need it.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Shared&lt;/code&gt; struct also carries an atomic counter with the number of &lt;code&gt;Bytes&lt;/code&gt; instances referencing this buffer. The
implementation doesn&apos;t use Rust&apos;s native &lt;code&gt;Arc&lt;/code&gt;. Instead the designers preferred re-implementing it. I could not find a
definitive answer to why, but I guess that&apos;s because they wanted to avoid the overhead in &lt;code&gt;Arc&lt;/code&gt; related to the
implementation of &amp;quot;weak references&amp;quot;, that is not necessary for &lt;code&gt;Bytes&lt;/code&gt;&apos; use case. To implement it, &lt;code&gt;Arc&lt;/code&gt; uses an
additional atomic counter and executes some extra atomic instructions when cloning and droping.&lt;/p&gt;
&lt;p&gt;Finally, note how the &lt;code&gt;vtable&lt;/code&gt; pointer does not change: it&apos;s still the same &amp;quot;promotable&amp;quot; method list. The code uses the
tag in the &lt;code&gt;data&lt;/code&gt; pointer to distinguish a not-yet-promoted &lt;code&gt;data&lt;/code&gt; (tag = 1) from a promoted &lt;code&gt;data&lt;/code&gt; (tag = 0).&lt;/p&gt;
&lt;p&gt;The deferred allocation feature is a trade-off: the code is more complex because we need an &lt;code&gt;AtomicPtr&lt;/code&gt; and a tag to
implement it. However, for code that creates a &lt;code&gt;Bytes&lt;/code&gt; without actually slicing and sharing them, it avoids the &lt;code&gt;Shared&lt;/code&gt;
allocation entirely.&lt;/p&gt;
&lt;h2&gt;It&apos;s a lie&lt;/h2&gt;
&lt;p&gt;Let&apos;s step back a bit: the behavior above only happens when we create a &lt;code&gt;Bytes&lt;/code&gt; from a &lt;code&gt;Box&amp;lt;[u8]&amp;gt;&lt;/code&gt; or &amp;quot;full&amp;quot; &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;.
When we start with a non-full &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;, the code will not use the deferred mechanism and instead allocates a &lt;code&gt;Shared&lt;/code&gt;
struct right away. In this case, there&apos;s no tagging of the &lt;code&gt;data&lt;/code&gt; field and a dedicated &lt;code&gt;vtable&lt;/code&gt; is used that will not
check for the tag:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/02/understanding-bytes-in-rust-one-bit-at-a-time/vec_to_bytes.png&quot; alt=&quot;Converting a Vec to Bytes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is very similar to how the simpler &lt;code&gt;SharedBytes&lt;/code&gt; work, except that &lt;code&gt;Bytes&lt;/code&gt; has 8 more bytes for the &lt;code&gt;vtable&lt;/code&gt; and
&lt;code&gt;Shared&lt;/code&gt; has 8 fewer bytes than &lt;code&gt;Arc&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt; for the lack of weak references.&lt;/p&gt;
&lt;h2&gt;Bytes are versatile&lt;/h2&gt;
&lt;p&gt;The dynamic dispatch implemented by &lt;code&gt;Bytes&lt;/code&gt; with the &lt;code&gt;vtable&lt;/code&gt; and &lt;code&gt;data&lt;/code&gt; fields effectively allows it to use custom
implementation for different backing storages of the data.&lt;/p&gt;
&lt;p&gt;We saw what happens to &lt;code&gt;Box&lt;/code&gt; and &lt;code&gt;Vec&lt;/code&gt;. Here&apos;s what happens when you create a &lt;code&gt;Bytes&lt;/code&gt; from an existing statically
allocated slice of bytes (in Rust represented as &lt;code&gt;&amp;amp;&apos;static [u8]&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/02/understanding-bytes-in-rust-one-bit-at-a-time/static_to_bytes.png&quot; alt=&quot;Converting static slice to Bytes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note that there is no reference counting, because in this mode, &lt;code&gt;Bytes&lt;/code&gt; does not own the data: instead it borrows data
that outlives it, as indicated by the &lt;code&gt;&apos;static&lt;/code&gt; lifetime.&lt;/p&gt;
&lt;p&gt;Another interesting usage of the dynamic dispatch is to create &lt;code&gt;Bytes&lt;/code&gt; from anything else that somehow owns a buffer.
For example, from a &lt;code&gt;memmap2::Mmap&lt;/code&gt; instance that represents a memory-mapped region:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/02/understanding-bytes-in-rust-one-bit-at-a-time/owned_to_bytes.png&quot; alt=&quot;Converting owned struct to Bytes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note that the owned struct is copied over into &lt;code&gt;Onwed&lt;/code&gt; that, just like &lt;code&gt;Shared&lt;/code&gt;, acts like an &lt;code&gt;Arc&amp;lt;_&amp;gt;&lt;/code&gt; without the weak
reference feature. The major distinction between &lt;code&gt;Owned&lt;/code&gt; and &lt;code&gt;Shared&lt;/code&gt; is that &lt;code&gt;Bytes&lt;/code&gt; does not know how to take
ownership of the buffer, as it only requires that the given type implements &lt;code&gt;AsRef&amp;lt;[u8]&amp;gt;&lt;/code&gt; to produces a
borrowed slice of bytes.&lt;/p&gt;
&lt;h2&gt;Micro benchmarks&lt;/h2&gt;
&lt;p&gt;Never trust a benchmark you see online. So here&apos;s one more for you to ignore:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;lt;B: BytesStorage&amp;gt;() {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; data = &lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;black_box&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;example_request_package&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;());
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; request = parse_request::&amp;lt;B&amp;gt;(data);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;assert_example_request&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;amp;request);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;&lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;&lt;code&gt;SharedBytes&lt;/code&gt;&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;&lt;code&gt;bytes::Bytes&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Allocated (bytes)&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;298&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;232&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;280&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution time (ns)&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;115&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;115&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;150&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So &lt;code&gt;Bytes&lt;/code&gt; seems both chunkier and slower than my half-backed &lt;code&gt;SharedBytes&lt;/code&gt; on this synthetic benchmark 🫣. But of
course, my implementation does not offer the same versatility.&lt;/p&gt;
&lt;p&gt;I searched for public benchmarks that justified some design decisions on the &lt;code&gt;bytes&lt;/code&gt; crate, but could not find them. If
you know any, please tell me!&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;end-of-page&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content></entry><entry><title>Web server auto-reload in Rust</title><id>76221558-958b-4072-afa3-3e602f92ac9a</id><updated>2026-02-14T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="programming"/><category term="rust"/><category term="axum"/><link href="https://sitegui.dev/post/2026/02/web-server-auto-reload-in-rust" rel="alternate"/><published>2026-02-14T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/02/web-server-auto-reload-in-rust" type="html">&lt;p&gt;This blog is written in Rust, and I wanted a way to reload the web pages automatically while I change the posts&apos;
contents, styles, etc. This is common-place with JavaScript frameworks, but not automatic in the Rust land. So I&apos;ve
embarked on a side quest to achieve just that: the &amp;quot;type and auto-reload&amp;quot; experience. In the end, I was surprised to
learn a bit more about sockets and processes in Linux.&lt;/p&gt;
&lt;p&gt;This post is a note to myself about these nuggets that I&apos;ve learned and to share the solution. It may be helpful for
future me and I hope for someonelse out there.&lt;/p&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;You can check the solution
here:  &lt;a href=&quot;https://git.sitegui.dev/sitegui/axum-web-auto-reload-example/src/branch/main/src/main.rs&quot;&gt;https://git.sitegui.dev/sitegui/axum-web-auto-reload-example/src/branch/main/src/main.rs&lt;/a&gt;. The README in that
repo has some nice diagrams as well.&lt;/p&gt;
&lt;h2&gt;Shopping list&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;continue-reading&quot; class=&quot;continue-reading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To reload the browser page on a source file change, you will need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;your server: I&apos;m developing mine with &lt;a href=&quot;https://docs.rs/axum/latest/axum/&quot;&gt;axum&lt;/a&gt; in Rust&lt;/li&gt;
&lt;li&gt;a tool to listen to a port, pass down the socket to the server, detect file changes, and restart the server. I&apos;m using
&lt;a href=&quot;https://github.com/watchexec/watchexec&quot;&gt;&lt;code&gt;watchexec&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;a browser&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To run it all, I use this command:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;watchexec \
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  --socket 8080 \
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  --restart \
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  --stop-signal SIGINT -- \
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  cargo run
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Accepting the passed socket&lt;/h2&gt;
&lt;p&gt;This part is very important for smooth reloads: when the browser reloads, it will try to restablish a connection with
your server. However, at the same time your server is reloading and probably not yet available on the localhost
port, causing the browser to fail immediately with a passive-aggressive message telling it was ghostest by its dearest
friend localhost.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/watchexec/watchexec/blob/main/doc/socket.md&quot;&gt;The solution is a bit complex but quite brilliant&lt;/a&gt;:
let&apos;s leave the socket connection to the &lt;code&gt;watchexec&lt;/code&gt; process, which will stay alive through the session. When the
browser tries to connect, the connection will not fail immediately because no process was listening. Of course,
&lt;code&gt;watchexec&lt;/code&gt; has no idea what to do with that incomming connection: it is &lt;em&gt;your&lt;/em&gt; server that knows. So &lt;code&gt;watchexec&lt;/code&gt; spawns
your server and pass it the socket, so that it can do its serverly stuff, like accepting connections and spitting HTML
or whatever. To &amp;quot;pass the socket&amp;quot;, &lt;code&gt;watchexec&lt;/code&gt; uses two environment variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LISTEN_FDS&lt;/code&gt;: the number of sockets being passed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LISTEN_FDS_FIRST_FD&lt;/code&gt;: the file-descriptor id of the first socket being passed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The sockets are created with &lt;code&gt;ReuseAddr&lt;/code&gt; and &lt;code&gt;ReusePort&lt;/code&gt; so that the server can listen to it again.&lt;/p&gt;
&lt;p&gt;To make it work, your server should detect that it is called by something like &lt;code&gt;watchexec&lt;/code&gt; and that it has received a
socket. I&apos;m using the crate &lt;a href=&quot;https://crates.io/crates/listenfd&quot;&gt;&lt;code&gt;listenfd&lt;/code&gt;&lt;/a&gt; to help with that:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;listenfd::ListenFd;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;std::error::Error;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;tokio::net::TcpListener;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;#[&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;tokio&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#f2777a;&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;]
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;async &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn Error&amp;gt;&amp;gt; {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;// Use the crate `listenfd` to get the socket passed by `watchexec`
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; inherited_socket = ListenFd::from_env().&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;take_tcp_listener&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;)?;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(auto_refresh, listener) = &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;if let &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;Some(inherited_socket) = inherited_socket {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        println!(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;Listening on inherit socket&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        inherited_socket.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;set_nonblocking&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;)?; &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;// required by TcpListener::from_std()
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, TcpListener::from_std(inherited_socket)?)
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;else &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;// Fallback to the typical way of listening to a new port
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; listener = TcpListener::bind((&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;127.0.0.1&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, &lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;8000&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;)).await?;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        println!(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;Listening on http://&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;{}&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, listener.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;local_addr&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;()?);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;, listener)
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    };
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Add a page script to reload&lt;/h2&gt;
&lt;p&gt;When &lt;code&gt;auto_refresh&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; in the code above, it means that we&apos;re in &amp;quot;let&apos;s reload guys!&amp;quot; territory.&lt;/p&gt;
&lt;p&gt;In my server, I&apos;m using &lt;a href=&quot;https://crates.io/crates/minijinja&quot;&gt;&lt;code&gt;minijinja&lt;/code&gt;&lt;/a&gt; to render Jinja2 templates, so I do something
like this:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-jinja&quot;&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{% if AUTO_REFRESH %}
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;lt;script&amp;gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  // Connect to the server and reload when a new message is received
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  const eventSource = new EventSource(`/auto_refresh`)
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;  eventSource.addEventListener(&amp;quot;message&amp;quot;, () =&amp;gt; location.reload())
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{% endif %}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and in my main:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;() {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    jinja_env.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;add_global&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;AUTO_REFRESH&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;, auto_refresh);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To tell that the browser should reload, I&apos;m
using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events&quot;&gt;server-sent events (SSE)&lt;/a&gt;
which are pretty cool in fact! My use case here is very simple: whenever the server sends an event, reload.&lt;/p&gt;
&lt;h2&gt;Please call me&lt;/h2&gt;
&lt;p&gt;The last piece of the puzzle is to implement the &lt;code&gt;/auto_refresh&lt;/code&gt; SSE endpoint in the server:&lt;/p&gt;
&lt;pre style=&quot;background-color:#2d2d2d;&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;axum::response::sse::{Event, KeepAlive};
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;axum::response::Sse;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;std::convert::Infallible;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;tokio::signal;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;tokio::sync::mpsc;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;tokio_stream::Stream;
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;use &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;tokio_stream::wrappers::UnboundedReceiverStream;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// Create a server-sent event stream that will send a single &amp;quot;goodbye&amp;quot; event when the server is
&lt;/span&gt;&lt;span style=&quot;color:#747369;&quot;&gt;/// stopped.
&lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; async &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get_auto_refresh&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;() -&amp;gt; Sse&amp;lt;impl Stream&amp;lt;Item=Result&amp;lt;Event, Infallible&amp;gt;&amp;gt;&amp;gt; {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    println!(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;GET /auto_refresh&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(tx, rx) = mpsc::unbounded_channel();
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    tokio::spawn(async &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;move &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;wait_ctrl_c&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;().await;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        println!(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;SSE: sending goodbye&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt; event = Event::default().&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#99cc99;&quot;&gt;goodbye&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;&amp;quot;);
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;_ = tx.&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(Ok(event));
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    });
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    Sse::new(UnboundedReceiverStream::new(rx)).&lt;/span&gt;&lt;span style=&quot;color:#66cccc;&quot;&gt;keep_alive&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;(KeepAlive::new())
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;async &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;wait_ctrl_c&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;() {
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#cc99cc;&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;_ = signal::ctrl_c().await;
&lt;/span&gt;&lt;span style=&quot;color:#d3d0c8;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&apos;s it! For sure, the JavaScript ecosystem has perfected this integration of different pieces into a better
experience, but now I can enjoy it also. Maybe as a next step I can package all the different pieces into a single
crate?&lt;/p&gt;
</content></entry><entry><title>The 15-game blew my mind</title><id>7a0ba4ae-54da-4400-9940-28ff4ae721fb</id><updated>2026-02-11T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="maths"/><link href="https://sitegui.dev/post/2026/02/the-15-game-blew-my-mind" rel="alternate"/><published>2026-02-11T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/02/the-15-game-blew-my-mind" type="html">&lt;p&gt;I&apos;ve just watched the calmly titled &lt;a href=&quot;https://www.youtube.com/watch?v=UafhPUOCM1E&quot;&gt;&amp;quot;The 15-game&amp;quot;&lt;/a&gt; in the Numberphile
Youtube channel and boy oh boy, I cannot stop thinking about it!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/02/the-15-game-blew-my-mind.png&quot; alt=&quot;The video thumbnail&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I will not spoil the end, but if you ever got intrigued by games, maths, brain-teasers, just take a 15-minute break and
watch it. At school, I loved to solve the same problem from different angles, not only to increase my chances of getting
the good answer, but also because it was fun. This video hit right there in my heart.&lt;/p&gt;
&lt;p&gt;One of my first programs was a game of &lt;span style=&quot;color:black;background-color:black&quot;&gt;tic-tac-toe&lt;/span&gt;. If only
someone had shown me the 15-game back then!&lt;/p&gt;
&lt;p&gt;I choose 5. Your turn!&lt;/p&gt;
</content></entry><entry><title>Welcome Framework Laptop</title><id>23c517d2-1021-4df4-bb6a-d9ba95f8f649</id><updated>2026-01-29T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="hardware"/><link href="https://sitegui.dev/post/2026/01/welcome-framework" rel="alternate"/><published>2026-01-29T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/01/welcome-framework" type="html">&lt;p&gt;Around 3 years ago my phone&apos;s screen broke and changing the screen would cost close to a half the device&apos;s original
price. It was cheaper to throw away and buy a new one.&lt;/p&gt;
&lt;p&gt;This is clearly wasteful, but I guess this is a typical experience with well-known consumer brands.
But hey, I don&apos;t want to move to a new home because my sink broke!&lt;/p&gt;
&lt;p&gt;In Europe, new regulations
like &lt;a href=&quot;https://commission.europa.eu/law/law-topic/consumer-protection-law/directive-repair-goods_en&quot;&gt;Right-to-Repair Directive&lt;/a&gt;
represent a smart step to force the hand of manufacturers. It requires them, for example, to keep spare parts stocked
for at least 10 years. In France, I always check
the &lt;a href=&quot;https://www.ecologie.gouv.fr/politiques-publiques/indice-reparabilite&quot;&gt;repairability index&lt;/a&gt; before buying. I&apos;m more
than willing to pay a bonus price for good product design and engineering, that respects the resources and costumers.&lt;/p&gt;
&lt;p&gt;So I&apos;ve begrudgingly replaced my phone. But this time I wanted to break the wasteful cycle, so I&apos;ve
bought &lt;a href=&quot;https://www.fairphone.com/&quot;&gt;a Fairphone&lt;/a&gt;, which promises 10 year software and hardware support. Also, if
something breaks I can just order a replacement and repair it myself. So far, I&apos;m pretty happy with the experience! It
works, it just does. I feel soon I&apos;ll replace my battery, and that&apos;s it: it will probably stick with me a bunch more
years.&lt;/p&gt;
&lt;p&gt;Since my old laptop became my home server, I needed a new one for hacking on the loose. Following a similar strategy,
I&apos;m going with
&lt;a href=&quot;https://frame.work/&quot;&gt;Framework&lt;/a&gt; for my new rig.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;continue-reading&quot; class=&quot;continue-reading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;They send the machine in a very simple DIY kit. It&apos;s a bit too easy for those used to tinker: snap, screw, click, boom!
But it&apos;s a genius brand
move to prove less experienced people that they can do it too.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/01/welcome-framework-2.jpg&quot; alt=&quot;Framework DYI laptop in fragments&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The final result is pretty good. Come back in 3 years and I&apos;ll share my thoughts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/01/welcome-framework-3.jpg&quot; alt=&quot;Framework 13 laptop&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: smaller&quot;&gt;sent from my framework&lt;/span&gt;&lt;/p&gt;
</content></entry><entry><title>Nice people don&apos;t play board games for 27 straight hours</title><id>6f2ab63e-a870-4a63-91f1-e79352a15c11</id><updated>2026-01-22T00:00:00+00:00</updated><author><name>sitegui</name></author><category term="boardgame"/><link href="https://sitegui.dev/post/2026/01/28h-board-games" rel="alternate"/><published>2026-01-22T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/01/28h-board-games" type="html">&lt;p&gt;They play for &lt;strong&gt;28&lt;/strong&gt; hours! We did it and it was pretty cool o/&lt;/p&gt;
&lt;p&gt;Well... not all those hours were spent actually playing, we also had to decide what to play next! With more than 500
games available at the event, this sometimes took a while :)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/01/28h-board-games-2.jpeg&quot; alt=&quot;a display with lots of board games&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can picture 3 rooms like the one below, full of meeples, cards, coins and fun.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sitegui.dev/post/2026/01/28h-board-games-1.jpeg&quot; alt=&quot;a room full of people playing board games&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;continue-reading&quot; class=&quot;continue-reading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It was the 16th edition of the festival &lt;em&gt;24h Chaud les Jeux!&lt;/em&gt; in &lt;a href=&quot;https://www.cholet.fr/&quot;&gt;Cholet&lt;/a&gt;.
The festival name (and the association behind it) is a well-chosen self-referential pun! It translates to something like
&amp;quot;hot for games&amp;quot;, and the &lt;em&gt;chaud les&lt;/em&gt; sounds exactly the same as the city&apos;s name. French people, especially &lt;a href=&quot;https://tif.hair/&quot;&gt;hairdressing
owners&lt;/a&gt;, love their &lt;em&gt;jeu de mots&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The festival is made not only for hardcore gamers: it&apos;s open for anyone that passes by. I always find it heart-warming
to see friends and family sitting around, just enjoying their company on a Sunday afternoon, while they fight furiously
for the control of Tokyo, or accuse each other of being a Moriarty malfeasant ready to explode the Big Ben, or just
casually arranging their azulejos the best they can.&lt;/p&gt;
&lt;p&gt;For my part, I&apos;ve managed to discover 9 new games and play other 2 that I already knew. The big ones were Endeavor: age
of sail, Coming of age, Nemesis. I like them and would happily play another time.&lt;/p&gt;
&lt;p&gt;Playing Nemesis for the first time at 08:00 was a difficult task hhaha! After 3 hours, we all exploded in space before
coming back to Earth because one of the players decided that it HAD to auto-destroy the spaceship in order to content
their desire for chaos! It was a nice play though, we were all dead and happy (and ready
for lunch).&lt;/p&gt;
&lt;p&gt;We made good choices of lighter party games to cool down: Decrypto, Nosferatu and Cabanga!.&lt;/p&gt;
&lt;p&gt;A big Thank You! for the game club &lt;a href=&quot;https://sectionjeuxasptt.wordpress.com/&quot;&gt;Chaud les jeux&lt;/a&gt; for all the time and effort
you&apos;ve put into this event. I&apos;ll be happy to see you again next time.&lt;/p&gt;
</content></entry><entry><title>Hello world 2026</title><id>e654d01f-b5f6-49c8-9417-f09b06d011a2</id><updated>2026-01-01T00:00:00+00:00</updated><author><name>sitegui</name></author><link href="https://sitegui.dev/post/2026/01/hello-world-2026" rel="alternate"/><published>2026-01-01T00:00:00+00:00</published><content xml:base="https://sitegui.dev/post/2026/01/hello-world-2026" type="html">&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20110715000000*/sitegui.com.br/blog&quot;&gt;I used to blog as a kid back in the 2010s&lt;/a&gt;, and for a
long time I wanted to get back to it. Now it&apos;s the time! This is my personal space on the web, it&apos;s not the shiniest,
not the most visited, but it is mine :)&lt;/p&gt;
&lt;p&gt;For now there isn&apos;t much to see here, this first post is mostly a hack to get me going.&lt;/p&gt;
&lt;p class=&quot;fun&quot;&gt;See you next time!&lt;/p&gt;
</content></entry></feed>