<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Junxiao Shi</title>
    <description>The latest articles on Forem by Junxiao Shi (@yoursunny).</description>
    <link>https://forem.com/yoursunny</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242407%2Fac8c4533-e9f9-4cbc-980b-b0bb5acc2797.jpg</url>
      <title>Forem: Junxiao Shi</title>
      <link>https://forem.com/yoursunny</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/yoursunny"/>
    <language>en</language>
    <item>
      <title>Deep Atlantic Storage: Rewriting in Rust</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Mon, 25 May 2026 04:00:00 +0000</pubDate>
      <link>https://forem.com/yoursunny/deep-atlantic-storage-rewriting-in-rust-49hp</link>
      <guid>https://forem.com/yoursunny/deep-atlantic-storage-rewriting-in-rust-49hp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally published on yoursunny.com blog &lt;a href="https://yoursunny.com/t/2026/das-rust/" rel="noopener noreferrer"&gt;https://yoursunny.com/t/2026/das-rust/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have been coding in &lt;a href="https://yoursunny.com/t/2020/NDNph-intro/" rel="noopener noreferrer"&gt;C++&lt;/a&gt;, &lt;a href="https://yoursunny.com/t/2021/ndpresponder/" rel="noopener noreferrer"&gt;Go&lt;/a&gt;, and &lt;a href="https://yoursunny.com/t/2019/NDNts-intro/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; for many years, but recently I started learning the &lt;a href="https://rust-lang.org/" rel="noopener noreferrer"&gt;Rust Programming Language&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust
&lt;/h2&gt;

&lt;p&gt;I choose to study the Rust programming language because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rust is one of the major system languages that I do not know.&lt;/li&gt;
&lt;li&gt;Rust, just like Go, is a memory safe language, apparently now a &lt;a href="https://stackoverflow.blog/2024/12/30/in-rust-we-trust-white-house-office-urges-memory-safety/" rel="noopener noreferrer"&gt;national security issue&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Rust, unlike Go, does not have a garbage collector.&lt;/li&gt;
&lt;li&gt;Rust, unlike Go, does not have a nil pointer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last point is particularly important.&lt;br&gt;
In Go, I often find myself writing a method with pointer receiver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;V&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pointer receiver is required, if the method needs to modify a field on its receiver.&lt;br&gt;
However, more often than not, I forget to check whether the receiver itself is a &lt;code&gt;nil&lt;/code&gt; pointer, &lt;a href="https://github.com/usnistgov/ndn-dpdk/blob/67f84648ea71c18aae7e6306f872a235207f5c5d/ndn/data.go#L172-L175" rel="noopener noreferrer"&gt;even in production code&lt;/a&gt;.&lt;br&gt;
If the above function was called like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would experience a panic at &lt;em&gt;runtime&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x46f383]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In contrast, Rust's rigid rules can prevent writing such careless code.&lt;br&gt;
A function/method is allowed to modify its arguments, including &lt;code&gt;self&lt;/code&gt;, only if the argument is passed as a &lt;em&gt;mutable borrow&lt;/em&gt;, signified with the &lt;code&gt;mut&lt;/code&gt; keyword.&lt;br&gt;
Moreover, a &lt;em&gt;borrow&lt;/em&gt; can only be created from a real value, allocated in either the stack or the heap.&lt;br&gt;
It is generally impossible to have a nil or NULL pointer, or a &lt;em&gt;borrow&lt;/em&gt; that points to invalid memory.&lt;br&gt;
This different, as I perceive, elevates Rust to have a higher level of memory safety.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Book
&lt;/h2&gt;

&lt;p&gt;The official &lt;a href="https://rust-lang.org/learn/" rel="noopener noreferrer"&gt;Learn Rust&lt;/a&gt; webpage gives three options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Rust Programming Language, "the book" that covers the language principles and some small exercises.&lt;/li&gt;
&lt;li&gt;Rust By Examples, a bunch of code with minimal explanation.&lt;/li&gt;
&lt;li&gt;Rustlings, an interactive tutorial of the Rust syntax in my environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two decades ago, I &lt;a href="https://yoursunny.com/t/2009/my-webdev-timeline/" rel="noopener noreferrer"&gt;learned HTML and CSS&lt;/a&gt; from the official W3C specifications.&lt;br&gt;
While I would not go as deep as learning from the language and compiler specifications, I chose the deepest option among the three, &lt;a href="https://doc.rust-lang.org/book/" rel="noopener noreferrer"&gt;"the book"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During the &lt;a href="https://en.wikipedia.org/wiki/2025_United_States_federal_government_shutdown" rel="noopener noreferrer"&gt;43-day government shutdown&lt;/a&gt;, I studied the book on each weekday.&lt;br&gt;
I setup my local environment, pasted the sample snippets, and made small changes to see what would happen.&lt;/p&gt;

&lt;p&gt;I feel that, Rust is a difficult language to learn.&lt;br&gt;
Not in a university taking programming class, I don't have a professor and teaching assistants to assign my homework, grade my code, and answer my questions.&lt;br&gt;
I am mostly on my own, against the Visual Studio Code console, judged by the compiler output when I invoke the &lt;code&gt;cargo run&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The last chapter of the book is &lt;a href="https://doc.rust-lang.org/book/ch21-00-final-project-a-web-server.html" rel="noopener noreferrer"&gt;building a multithreaded web server&lt;/a&gt;.&lt;br&gt;
I immediately noted that, the exercise is making me handle the network traffic at TCP level.&lt;br&gt;
In contrast, Go has a built-in &lt;code&gt;net/http&lt;/code&gt; package, and I used &lt;a href="https://learn.microsoft.com/en-us/windows/win32/http/http-server-api-overview" rel="noopener noreferrer"&gt;Win32 HTTP Server API&lt;/a&gt; for my &lt;a href="https://yoursunny.com/study/IS494/" rel="noopener noreferrer"&gt;hProxyN&lt;/a&gt; project in college.&lt;br&gt;
I'm surprised that Rust, being a modern programming language, lacks an HTTP server in its standard library.&lt;/p&gt;

&lt;p&gt;Here I learned a different philosophy: Rust is designed with a minimal standard library that only contains the essentials.&lt;br&gt;
Most "batteries" are provided as crates, including random number generation, command line parsing, Async I/O, HTTP server, and many others.&lt;/p&gt;
&lt;h2&gt;
  
  
  Re-build the Deep Atlantic Storage app
&lt;/h2&gt;

&lt;p&gt;As I finish the book, I wanted to build something real.&lt;br&gt;
While I had a few new project ideas, I decided against building a completely new project, using a language that I barely know, because it would be too complicated to architect the software, design the protocols, and deal with an unfamiliar language, all at the same time.&lt;br&gt;
Instead, I wanted to re-build something existing, but better.&lt;/p&gt;

&lt;p&gt;Many learners would choose a tutorial project, often seen on &lt;a href="https://dev.to/t/clone"&gt;DEV #clone tag&lt;/a&gt;.&lt;br&gt;
However, I feel wasteful building these projects, because I do not see myself ever deploy and operating the resulting program, which means the code would be written once and never used again.&lt;br&gt;
Instead, I wanted to build something that I would deploy, but with low stakes.&lt;/p&gt;

&lt;p&gt;Looking over my website, now in its 20th year, I found the perfect candidate: &lt;a href="https://yoursunny.com/p/summer-host/storage/" rel="noopener noreferrer"&gt;Deep Atlantic Storage&lt;/a&gt;, a wacky webpage I made on July 4th holiday in 2021.&lt;br&gt;
It has fewer than 300 lines of code, originally written in JavaScript and Go, encompassing many aspects of system programming: file access, stdio usage, byte slices, bit operators, HTTP servers.&lt;br&gt;
I shall re-build this application, in Rust.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Process and What I Learned
&lt;/h2&gt;

&lt;p&gt;The codebase is hosted on GitHub: &lt;a href="https://github.com/yoursunny/summer-host-storage" rel="noopener noreferrer"&gt;yoursunny/summer-host-storage&lt;/a&gt;.&lt;br&gt;
It took me four days to build the initial version.&lt;/p&gt;

&lt;p&gt;On my first day, I established the package structure and enabled GitHub Actions, even before writing any code logic.&lt;br&gt;
Then, I wrote these functions along with unit tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Download a file from the Atlantic Ocean.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;BitCounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// Upload a file to the Atlantic Ocean.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BitCounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tried a few command line parsing crates on my second day and picked &lt;a href="https://crates.io/crates/clap" rel="noopener noreferrer"&gt;clap&lt;/a&gt;.&lt;br&gt;
I wrote the necessary declarations, to make the download and upload functionality available on the command line.&lt;/p&gt;

&lt;p&gt;I spent the third day researching HTTP server crates.&lt;br&gt;
I initially used &lt;a href="https://crates.io/crates/hyper" rel="noopener noreferrer"&gt;hyper&lt;/a&gt; but was very confused on its &lt;em&gt;body&lt;/em&gt; concept and had great difficulty writing a test case.&lt;br&gt;
Then I switched to &lt;a href="https://crates.io/crates/axum" rel="noopener noreferrer"&gt;axum&lt;/a&gt;, which seems to have more &lt;em&gt;natural&lt;/em&gt; APIs similar to what I used in Go, Node.js, and Arduino.&lt;/p&gt;

&lt;p&gt;However, I hit a snag when I tried to integrated my &lt;code&gt;download&lt;/code&gt; and &lt;code&gt;upload&lt;/code&gt; functions: I wrote these as &lt;em&gt;synchronous&lt;/em&gt; functions, so that the HTTP server must buffer the entire file in memory during each download or upload.&lt;br&gt;
The &lt;code&gt;io::Write&lt;/code&gt; and &lt;code&gt;io::Read&lt;/code&gt; traits feel like streams, but they are not the same streams as Node.js or Go.&lt;/p&gt;

&lt;p&gt;On my fourth day, I transformed the core functions to be &lt;em&gt;asynchronous&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncRead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWrite&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Download a file from the Atlantic Ocean.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AsyncWrite&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Unpin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;BitCounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// Upload a file to the Atlantic Ocean.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AsyncRead&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Unpin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BitCounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This refactoring enabled me to unclog the HTTP pipes and realized request and response streaming, finishing the build.&lt;/p&gt;

&lt;p&gt;My main takeaway from this process is that, for network applications, I should use the industry standard &lt;a href="https://crates.io/crates/tokio" rel="noopener noreferrer"&gt;tokio&lt;/a&gt; crate and build as asynchronous APIs, which would perform better than the standard library traits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;I attended &lt;a href="https://www.meetup.com/rustdc/events/312489197/" rel="noopener noreferrer"&gt;Rust DC meetup&lt;/a&gt; and briefly introduced my app.&lt;br&gt;
Audience was interested.&lt;br&gt;
During the same meetup, I learned about another command line parsing library &lt;a href="https://crates.io/crates/bpaf" rel="noopener noreferrer"&gt;bpaf&lt;/a&gt; and decided to make the switch.&lt;br&gt;
The code became a few lines shorter as its syntax is less verbose.&lt;/p&gt;

&lt;p&gt;I haven't deployed the app yet, because it still lacks a few important features such as the gzip compression that is available in the Deep Atlantic Storage app written in Go.&lt;br&gt;
I shall further refine my first Rust application and get it read for deployment.&lt;/p&gt;

</description>
      <category>rust</category>
    </item>
    <item>
      <title>New Avatar</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Fri, 22 May 2026 03:14:06 +0000</pubDate>
      <link>https://forem.com/yoursunny/new-avatar-59e6</link>
      <guid>https://forem.com/yoursunny/new-avatar-59e6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally published on yoursunny.com blog &lt;a href="https://yoursunny.com/t/2026/avatar/" rel="noopener noreferrer"&gt;https://yoursunny.com/t/2026/avatar/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0cjwjo26f19vl8rvas9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0cjwjo26f19vl8rvas9.jpg" alt="2025 avatar" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In computing, an &lt;a href="https://en.wikipedia.org/wiki/Avatar_%28computing%29" rel="noopener noreferrer"&gt;avatar&lt;/a&gt; is a graphical representation of a user, the user's character, or persona.&lt;br&gt;
An avatar can be a two-dimensional picture, also known as a profile picture or userpic, or a three-dimensional digital representation, often used in games.&lt;/p&gt;

&lt;p&gt;My existing avatar has been in use since 2010.&lt;br&gt;
It was created from a selfie taken at Expo 2010 Shanghai, where I proudly visited every pavilion.&lt;br&gt;
I stood in front of the Expo Axis, placed my Canon E1000D atop a trash can near the Hong Kong pavilion, and used the self-timer feature of the DSLR camera to capture the selfie.&lt;br&gt;
The picture depicts me wearing a white sleeveless shirt and a black backpack, in front of background of trees, people, and the Expo Axis.&lt;br&gt;
Subsequently, I cropped the image with Adobe Photoshop, blurred the background, and produced the final image.&lt;/p&gt;

&lt;p&gt;Over the years, as I expanded my online presence, readers from dozens of social media websites and forums have seen my avatar.&lt;br&gt;
It has become part of my online identity, my personal brand.&lt;/p&gt;

&lt;p&gt;In 2016, I realized that I still have the same shirt as shown in my avatar.&lt;br&gt;
On the 6-year anniversary of the original picture, I took &lt;a href="https://x.com/yoursunny/status/748996988616577024?s=20" rel="noopener noreferrer"&gt;a new photo of me in the same outfit&lt;/a&gt;, in front of a cactus behind the university.&lt;br&gt;
In 2020, I &lt;a href="https://x.com/yoursunny/status/1278494906479190017?s=20" rel="noopener noreferrer"&gt;did it again&lt;/a&gt; for the 10-year anniversary, but indoors due to COVID-19 pandemic.&lt;br&gt;
Nevertheless, I did not adopt either picture as my avatar, because they have drastically different backgrounds and would make me unrecognizable online.&lt;/p&gt;

&lt;p&gt;By 2025, I grew unhappy with my avatar.&lt;br&gt;
Firstly, the 2010 avatar was produced as 750x750 image resolution, which is insufficient by today's standards.&lt;br&gt;
Secondly, my real life appearance has changed over the years, and I wanted to showcase my more recent look.&lt;br&gt;
I wanted to remake my avatar, but in the same style so that it remains recognizable.&lt;/p&gt;

&lt;p&gt;On a cool summer day, I put on the shirt (yes, the same one) and a black backpack, and set out to find the perfect background for my new avatar.&lt;br&gt;
I took a bus to Rock Creek Park in Washington DC, hiked the north floodplain area, and found a patch of woods with enough trees that resembled my 2010 avatar.&lt;br&gt;
I placed my iPhone 16e on a fence post, turned on the Portrait mode, and captured the perfect image after multiple tries waiting for the perfect sunlight.&lt;br&gt;
It depicts me wearing the same white sleeveless shirt and a similar black backpack, in front of some trees but no people or building.&lt;br&gt;
Apple Intelligence has already blurred the background for me, so that I do not need to use Photoshop again.&lt;/p&gt;

&lt;p&gt;After the trip, I quickly forgot that I took the new picture for my avatar, so that it wasn't uploaded.&lt;br&gt;
Today, as I worked on archiving my photos by moving out from Dropbox, I re-discovered this image, and decided to finish the effort.&lt;br&gt;
I cropped the image with &lt;a href="https://picpick.app/en/" rel="noopener noreferrer"&gt;PicPick&lt;/a&gt; application, optimized the file with &lt;a href="https://ezgif.com/" rel="noopener noreferrer"&gt;Ezgif&lt;/a&gt;, and then uploaded the final result to &lt;a href="https://gravatar.com/" rel="noopener noreferrer"&gt;Gravatar&lt;/a&gt; so that it would appear in all the forums where I have a presence.&lt;br&gt;
With that, my new avatar is launched.&lt;/p&gt;




&lt;p&gt;Note: the &lt;a href="https://yoursunny.com/t/2026/avatar/" rel="noopener noreferrer"&gt;original article&lt;/a&gt; contains a CSS slider that compares 2010 and 2025 avatars.&lt;/p&gt;

</description>
      <category>art</category>
    </item>
    <item>
      <title>Today I Learned: openat()</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Sat, 28 Aug 2021 17:53:27 +0000</pubDate>
      <link>https://forem.com/yoursunny/today-i-learned-openat-8h1</link>
      <guid>https://forem.com/yoursunny/today-i-learned-openat-8h1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally published on yoursunny.com blog &lt;a href="https://yoursunny.com/t/2021/openat/" rel="noopener noreferrer"&gt;https://yoursunny.com/t/2021/openat/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  fopen and open
&lt;/h2&gt;

&lt;p&gt;In C programming language, the &lt;code&gt;&amp;lt;stdio.h&amp;gt;&lt;/code&gt; header supplies functions for file input and output.&lt;br&gt;
To open a file, we usually use the &lt;a href="https://en.cppreference.com/w/c/io/fopen" rel="noopener noreferrer"&gt;fopen&lt;/a&gt; function.&lt;br&gt;
It is defined by the C language standard and works in every operating system.&lt;/p&gt;

&lt;p&gt;Working at a lower level, there's also the &lt;a href="https://man7.org/linux/man-pages/man2/open.2.html" rel="noopener noreferrer"&gt;open&lt;/a&gt; function.&lt;br&gt;
It is a system call provided by the Linux kernel and exposed through glibc.&lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;fopen&lt;/code&gt; and &lt;code&gt;open&lt;/code&gt; have an input parameter: the file pathname, as a NUL-terminated string.&lt;br&gt;
These two functions are declared like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the file we want to access is in the &lt;a href="https://en.wikipedia.org/wiki/Working_directory" rel="noopener noreferrer"&gt;current working directory&lt;/a&gt;, or we have the full pathname of the file as a string, this is easy to use.&lt;br&gt;
However, sometimes we want to access a file relative to another directory, and the above API isn't so easy to use.&lt;/p&gt;
&lt;h2&gt;
  
  
  Directory Path + Filename
&lt;/h2&gt;

&lt;p&gt;One such occasion is in my &lt;a href="https://yoursunny.com/t/2020/NDNph-intro/" rel="noopener noreferrer"&gt;NDNph&lt;/a&gt; library: I wanted to use a directory in the filesystem as a persistent key-value store, where object keys are used as filenames, and object value is written as file content.&lt;br&gt;
The API for this key-value store looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;KV&lt;/span&gt; &lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @brief Open a key-value store at specified path
 * @param[out] kv key-value store object
 * @param dir directory pathname
 * @return whether success
 */&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;KV_Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @brief Write @p value to file @p dir "/" @p key
 * @param kv key-value store object
 * @param key filename
 * @param value file content
 * @param size size of file content
 * @return whether success
 */&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;KV_Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since &lt;code&gt;fopen&lt;/code&gt; and &lt;code&gt;open&lt;/code&gt; want the file pathname as a single string, I have to concatenate &lt;code&gt;dir&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; in &lt;code&gt;KV_Save&lt;/code&gt;.&lt;br&gt;
This in turn requires saving a copy of &lt;code&gt;dir&lt;/code&gt; in &lt;code&gt;KV_Open&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;KV&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;KV_Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;S_ISDIR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strdup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;KV_Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PATH_MAX&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;snprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"%s/%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;O_WRONLY&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;O_CREAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;0644&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// TODO write and close file&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  openat
&lt;/h2&gt;

&lt;p&gt;This week, I came across a new function: &lt;a href="https://man7.org/linux/man-pages/man2/openat.2.html" rel="noopener noreferrer"&gt;openat&lt;/a&gt;.&lt;br&gt;
It operates in the same way as &lt;code&gt;open&lt;/code&gt;, except that it supports specifying a &lt;em&gt;relative&lt;/em&gt; pathname interpreted relative to another directory, which is represented by a file descriptor.&lt;/p&gt;

&lt;p&gt;The function signature of &lt;strong&gt;openat&lt;/strong&gt; is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;openat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;dirfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows me to simplify the key-value store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;KV&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;dirfd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;KV_Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dirfd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;O_RDONLY&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;O_DIRECTORY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dirfd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;KV_Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dirfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;O_WRONLY&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;O_CREAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;0644&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// TODO write and close file&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;KV_Open&lt;/code&gt; opens the directory as a file descriptor with the &lt;code&gt;open&lt;/code&gt; function.&lt;br&gt;
The &lt;code&gt;O_DIRECTORY&lt;/code&gt; flag ensures we are opening a directory instead of a regular file.&lt;br&gt;
It's no longer necessary to save a copy of the directory path.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;KV_Save&lt;/code&gt; calls &lt;code&gt;openat&lt;/code&gt; with the directory file descriptor and the filename &lt;code&gt;key&lt;/code&gt;.&lt;br&gt;
It's no longer necessary to perform string concatenation.&lt;br&gt;
The code is 5 lines shorter than the &lt;code&gt;open&lt;/code&gt;-based solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Code Download
&lt;/h2&gt;

&lt;p&gt;This article introduces Linux &lt;a href="https://man7.org/linux/man-pages/man2/openat.2.html" rel="noopener noreferrer"&gt;openat&lt;/a&gt; syscall that I recently discovered.&lt;br&gt;
The &lt;code&gt;openat&lt;/code&gt; function enables resolving a filename or relative path, relative to another directory that is not the current working directory.&lt;br&gt;
It can do so without requiring manual string concatenation.&lt;/p&gt;

&lt;p&gt;Code samples (whole program including load/save/delete functions):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://yoursunny.com/t/2021/openat/open.c" rel="noopener noreferrer"&gt;open.c&lt;/a&gt;: key-value store implemented with &lt;code&gt;open&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://yoursunny.com/t/2021/openat/openat.c" rel="noopener noreferrer"&gt;openat.c&lt;/a&gt;: key-value store implemented with &lt;code&gt;openat&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/yoursunny/530750611f951941886ea82160c1927d" rel="noopener noreferrer"&gt;view on GitHub Gist&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caution: this proof-of-concept code assumes that &lt;code&gt;key&lt;/code&gt; is a valid filename.&lt;br&gt;
It cannot safely handle untrusted and potentially malicious input.&lt;/p&gt;

</description>
      <category>c</category>
      <category>linux</category>
      <category>syscall</category>
    </item>
    <item>
      <title>Deep Atlantic Storage: Streaming Bits</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Sun, 11 Jul 2021 19:42:27 +0000</pubDate>
      <link>https://forem.com/yoursunny/deep-atlantic-storage-streaming-bits-33mp</link>
      <guid>https://forem.com/yoursunny/deep-atlantic-storage-streaming-bits-33mp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally published on yoursunny.com blog &lt;a href="https://yoursunny.com/t/2021/das-stream-bits/" rel="noopener noreferrer"&gt;https://yoursunny.com/t/2021/das-stream-bits/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm bored on 4th of July holiday, so I made a wacky webpage: &lt;a href="https://yoursunny.com/p/summer-host/storage/" rel="noopener noreferrer"&gt;Deep Atlantic Storage&lt;/a&gt;.&lt;br&gt;
It is described as a free file storage service, where you can upload any file to be stored deep in the Atlantic Ocean, with no size limit and content restriction whatsoever.&lt;br&gt;
How does it work, and how can I afford to provide it?&lt;/p&gt;

&lt;p&gt;This article is the third of a 3-part series that reveals the secrets behind &lt;em&gt;Deep Atlantic Storage&lt;/em&gt;.&lt;br&gt;
The &lt;a href="https://yoursunny.com/t/2021/das-sort-bits/" rel="noopener noreferrer"&gt;first part&lt;/a&gt; revealed that the uploaded file is sorted which drastically reduces its storage demand, and introduced the bit sorting algorithm.&lt;br&gt;
The &lt;a href="https://yoursunny.com/t/2021/das-file-worker/" rel="noopener noreferrer"&gt;second part&lt;/a&gt; covered how to process the upload in the browser using Web Workers.&lt;br&gt;
Now I'd continue from there, and explain where I store the files and how I offer downloads with reasonable costs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Storage in the URL
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Deep Atlantic Storage&lt;/em&gt; sorts the bits in every uploaded file.&lt;br&gt;
After sorting, each file can be represented by two numbers: the number of &lt;code&gt;0&lt;/code&gt; bits, the number of &lt;code&gt;1&lt;/code&gt; bits.&lt;br&gt;
Given these two numbers, the sorted file can be reconstructed.&lt;/p&gt;

&lt;p&gt;I could make a database or use one of those fancy NoSQL thingy to store those numbers that represent the files, but I prefer my websites stateless so that &lt;a href="https://yoursunny.com/t/2021/disaster-recovery-no-tears/" rel="noopener noreferrer"&gt;I don't need to take backups&lt;/a&gt;.&lt;br&gt;
Therefore, I decided to encode those numbers in the URI.&lt;/p&gt;

&lt;p&gt;Each URI created by the &lt;em&gt;Deep Atlantic Storage&lt;/em&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://summer-host-storage.yoursunny.dev/6705fb/18fa05/%E2%98%80%EF%B8%8F.bin
                                          ^        ^          ^
                                          zeros    ones       filename
                                          (in hexadecimal)    (URI encoded)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever someone requests this URI, the server program can extract the number of &lt;code&gt;0&lt;/code&gt; bits and &lt;code&gt;1&lt;/code&gt; bits from the URI, and reconstruct the sorted file accordingly.&lt;br&gt;
By having all the information in the URI itself, I can run a storage service without maintaining any server-side storage.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bit Counts » Byte Array
&lt;/h2&gt;

&lt;p&gt;Given the number of &lt;code&gt;0&lt;/code&gt; bits and &lt;code&gt;1&lt;/code&gt; bits in a file, how to generate a file with those bits?&lt;br&gt;
The naive way is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a string of &lt;code&gt;"0"&lt;/code&gt; and &lt;code&gt;"1"&lt;/code&gt; repeated for requested length.&lt;/li&gt;
&lt;li&gt;Parse the string into an array of bytes.&lt;/li&gt;
&lt;li&gt;Write those bytes to a file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;makeFileFromBitCounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bitString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cnt0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt;
    &lt;span class="n"&gt;byteCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cnt0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
    &lt;span class="n"&gt;arrayOfBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;byteCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;byteorder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;big&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arrayOfBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this approach is vastly inefficient in memory usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The array of bytes has the length of the entire file, and it is allocated in memory.&lt;/li&gt;
&lt;li&gt;Even worse, the bit string has 8x the file size, demanding even more memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bit Counts » Stream
&lt;/h2&gt;

&lt;p&gt;Instead of reconstructing the file as a byte array in the memory, I can write the bits to an output stream (which could be a file or a network socket) as I generate them.&lt;br&gt;
However, there's one little problem: in most programming languages, the stream I/O functions are byte-oriented, not bit-oriented.&lt;br&gt;
In other words, I can only write bytes, not bits, to the stream.&lt;/p&gt;

&lt;p&gt;Looking at the structure of a sorted file in bytes, it has three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A consecutive run of 0x00 bytes.&lt;/li&gt;
&lt;li&gt;Possibly, one byte that is neither 0x00 nor 0xFF.&lt;/li&gt;
&lt;li&gt;A consecutive run of 0xFF bytes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to write the file to a stream using byte-oriented API, I need to calculate three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The number of 0x00 bytes.&lt;/li&gt;
&lt;li&gt;Whether the &lt;em&gt;middle&lt;/em&gt; byte exists, and its value.&lt;/li&gt;
&lt;li&gt;The number of 0xFF bytes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since each byte has 8 bits, suppose there are &lt;em&gt;cnt0&lt;/em&gt; zero bits and &lt;em&gt;cnt1&lt;/em&gt; one bits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of 0x00 bytes should be &lt;em&gt;cnt0 ÷ 8&lt;/em&gt;, rounded down.&lt;/li&gt;
&lt;li&gt;The number of 0xFF bytes should be &lt;em&gt;cnt1 ÷ 8&lt;/em&gt;, rounded down.&lt;/li&gt;
&lt;li&gt;If the &lt;em&gt;whole&lt;/em&gt; bytes did not cover all the bits, there would be a &lt;em&gt;middle&lt;/em&gt; byte.

&lt;ul&gt;
&lt;li&gt;This byte should contain &lt;em&gt;cnt0 mod 8&lt;/em&gt; zeros and &lt;em&gt;cnt1 mod 8&lt;/em&gt; ones.&lt;/li&gt;
&lt;li&gt;Since &lt;em&gt;cnt0 + cnt1&lt;/em&gt; is divisible by 8, &lt;em&gt;(cnt0 mod 8) + (cnt1 mod 8)&lt;/em&gt; is expected to equal 8.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Bit_numbering" rel="noopener noreferrer"&gt;Bit numbering&lt;/a&gt; within a byte can go either direction, but I picked "Most Significant Bit first" order.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following code does the calculations and writes the bits to an output stream (&lt;a href="https://coliru.stacked-crooked.com/a/b04fdef39d36ad67" rel="noopener noreferrer"&gt;try on Coliru&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;streamFileFromBitCounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ostream&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;cnt0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;bytes0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cnt0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;bytes1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;middleByte&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;middle0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cnt0&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;middle1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middle0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;middle1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middle0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;middle1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;middleByte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0xFF&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;middle0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;bytes0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleByte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_value&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleByte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;bytes1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xFF&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Deep Atlantic Storage&lt;/em&gt; runs this logic on a server rather than in the browser.&lt;br&gt;
While &lt;code&gt;Blob&lt;/code&gt; API and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL" rel="noopener noreferrer"&gt;URL.createObjectURL()&lt;/a&gt; allow generating a downloadable file from JavaScript, the entire file must be allocated in memory.&lt;br&gt;
Browser support for streaming from JavaScript is still &lt;a href="https://stackoverflow.com/a/62887531/3729203" rel="noopener noreferrer"&gt;at an early stage&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Stream » HTTP Server
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Deep Atlantic Storage&lt;/em&gt; offers file downloads from an HTTP server.&lt;br&gt;
The server, dubbed &lt;em&gt;Deep Atlantic Storage app&lt;/em&gt;, is built upon the logic above, with a few improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The response is piped through a gzip compressor, in order to save network egress from my server.&lt;/li&gt;
&lt;li&gt;For long runs of 0x00 and 0xFF bytes, instead of writing one byte at a time, the program writes them in &lt;em&gt;pages&lt;/em&gt; of 1MB each.&lt;/li&gt;
&lt;li&gt;A small delay is added after writing each page to throttle the downloads and avoid consuming too much CPU.&lt;/li&gt;
&lt;li&gt;If the client has disconnected, stop writing and return right away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the HTTP handler implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;buf0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1048576&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="c"&gt;// create 1MB page filled with 0x00 bytes&lt;/span&gt;
  &lt;span class="n"&gt;buf1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Repeat&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;0xFF&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;1048576&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// create 1MB page filled with 0xFF bytes&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// parse URI and extract number of zeros and ones&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sscanf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/%x/%x/%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// tell browser/client that:&lt;/span&gt;
  &lt;span class="c"&gt;// (1) the response is binary file, not text or HTML&lt;/span&gt;
  &lt;span class="c"&gt;// (2) the response is gzip compressed&lt;/span&gt;
  &lt;span class="c"&gt;// (3) browser should prompt for a download instead of display the file&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/octet-stream"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Encoding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gzip"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Disposition"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"attachment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;// calculate number of 0x00 and 0xFF pages and leftover bytes, and the middle byte&lt;/span&gt;
  &lt;span class="n"&gt;bytes0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cnt0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;
  &lt;span class="n"&gt;middle0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;middle1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cnt0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cnt1&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;
  &lt;span class="n"&gt;pages0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pages1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bytes0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;bytes1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;bytes0&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;pages0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;bytes1&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;pages1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewWriterLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// make gzip compressor, fastest mode&lt;/span&gt;
  &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                   &lt;span class="c"&gt;// close and flush when it's done&lt;/span&gt;

  &lt;span class="n"&gt;writePages&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;// subroutine to write some pages&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// pipe one page to the gzip compressor&lt;/span&gt;
      &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="c"&gt;// client disconnected&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// return non-nil error&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="c"&gt;// delay 1ms and continue writing&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;writePages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pages0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;// write 0x00 pages&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="c"&gt;// stop if client has disconnected&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bytes0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c"&gt;// write leftover 0x00 bytes&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;middle0&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;middle1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;// write the middle byte, if there is one&lt;/span&gt;
    &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;0xFF&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;middle0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bytes1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c"&gt;// write leftover 0xFF bytes&lt;/span&gt;
  &lt;span class="n"&gt;writePages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pages1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// write 0xFF pages&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual server has some additional logic for verifying the HTTP request verb and &lt;code&gt;Accept-Encoding: gzip&lt;/code&gt; header.&lt;br&gt;
You can see its source code &lt;a href="https://gist.github.com/yoursunny/173acdc792409f946f9d990055c2dfc0" rel="noopener noreferrer"&gt;in this Gist&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  HTTP Server » HTTPS Server
&lt;/h2&gt;

&lt;p&gt;It's 2021 and every website is expected to have HTTPS.&lt;br&gt;
Moreover, the &lt;code&gt;.dev&lt;/code&gt; domain I'm using is designated as a "secure namespace" such that most browsers won't even connect to it over plain HTTP.&lt;br&gt;
Therefore, I need to deploy &lt;em&gt;Deep Atlantic Storage app&lt;/em&gt; over HTTPS.&lt;/p&gt;

&lt;p&gt;I turned to my favorite TLS termination solution, &lt;a href="https://yoursunny.com/t/2021/yoursunny-com-caddy/" rel="noopener noreferrer"&gt;Caddy server&lt;/a&gt;, for this task.&lt;br&gt;
The &lt;code&gt;Caddyfile&lt;/code&gt; uses a &lt;code&gt;reverse_proxy&lt;/code&gt; directive to the HTTP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://summer-host-storage.yoursunny.dev {
  reverse_proxy http://127.0.0.1:3333 {
    flush_interval 1s
    transport http {
      compression off
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caddy by default would make request to the HTTP backend server with &lt;code&gt;Accept-Encoding: gzip&lt;/code&gt; request header, and then decompress the response if the incoming request does not accept gzip compression.&lt;br&gt;
This is undesirable for &lt;em&gt;Deep Atlantic Storage&lt;/em&gt; because my server can possibly send large responses, consuming too much egress bandwidth.&lt;br&gt;
Therefore, I explicitly disabled HTTP transport compression, so that the incoming request is served only if the client accepts gzip compression, and the outgoing response is always compressed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This article is the final installment of a 3-part series that reveals the secrets behind &lt;a href="https://yoursunny.com/p/summer-host/storage/" rel="noopener noreferrer"&gt;Deep Atlantic Storage&lt;/a&gt;.&lt;br&gt;
Given the &lt;a href="https://yoursunny.com/t/2021/das-sort-bits/" rel="noopener noreferrer"&gt;bit counts&lt;/a&gt; generated by &lt;a href="https://yoursunny.com/t/2021/das-file-worker/" rel="noopener noreferrer"&gt;Web Workers&lt;/a&gt;, I built and deployed a server that reconstructs the file as gzip'ed HTTP response.&lt;/p&gt;

&lt;p&gt;While &lt;em&gt;Deep Atlantic Storage&lt;/em&gt; started as a joke, it is a nice exercise to learn the algorithms and APIs around bits, bytes, and blobs.&lt;br&gt;
I enjoyed building the app, and I hope you learned something from these articles.&lt;/p&gt;

</description>
      <category>go</category>
      <category>python</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Deep Atlantic Storage: Reading File Upload in Web Workers</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Sun, 11 Jul 2021 19:39:30 +0000</pubDate>
      <link>https://forem.com/yoursunny/deep-atlantic-storage-reading-file-upload-in-web-workers-1hj6</link>
      <guid>https://forem.com/yoursunny/deep-atlantic-storage-reading-file-upload-in-web-workers-1hj6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally published on yoursunny.com blog &lt;a href="https://yoursunny.com/t/2021/das-file-worker/" rel="noopener noreferrer"&gt;https://yoursunny.com/t/2021/das-file-worker/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm bored on 4th of July holiday, so I made a wacky webpage: &lt;a href="https://yoursunny.com/p/summer-host/storage/" rel="noopener noreferrer"&gt;Deep Atlantic Storage&lt;/a&gt;.&lt;br&gt;
It is described as a free file storage service, where you can upload any file to be stored deep in the Atlantic Ocean, with no size limit and content restriction whatsoever.&lt;br&gt;
How does it work, and how can I afford to provide it?&lt;/p&gt;

&lt;p&gt;This article is the second of a 3-part series that reveals the secrets behind &lt;em&gt;Deep Atlantic Storage&lt;/em&gt;.&lt;br&gt;
The &lt;a href="https://yoursunny.com/t/2021/das-sort-bits/" rel="noopener noreferrer"&gt;previous part&lt;/a&gt; introduced the algorithm I use to sort all the bits in a &lt;code&gt;Uint8Array&lt;/code&gt;.&lt;br&gt;
Now I'd continue from there, and explain how the webpage accepts and processes file uploads.&lt;/p&gt;
&lt;h2&gt;
  
  
  File Upload
&lt;/h2&gt;

&lt;p&gt;File upload has always been a part of HTML standard as long as I remembered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"upload.php"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"upload"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would create a &lt;em&gt;Browse&lt;/em&gt; button that allows the user to select a local file.&lt;br&gt;
When the form is submitted, the file name and content are sent to the server, and a server-side script can &lt;a href="https://www.php.net/manual/en/features.file-upload.post-method.php" rel="noopener noreferrer"&gt;process the upload&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's straightforward, but not ideal for &lt;em&gt;Deep Atlantic Storage&lt;/em&gt;.&lt;br&gt;
As explained in the last article, regardless of how large a file is, the result of sorting all the bits could be represented by just two numbers: how many &lt;code&gt;0&lt;/code&gt; bits and &lt;code&gt;1&lt;/code&gt; bits are in the file.&lt;br&gt;
It is unnecessary to send the whole file to the server; instead, counting in the browser would be a lot faster.&lt;/p&gt;
&lt;h2&gt;
  
  
  File and Blob
&lt;/h2&gt;

&lt;p&gt;Fast forward to 2021, JavaScript can do everything.&lt;/p&gt;

&lt;p&gt;In JavaScript, given the DOM object corresponding to the &lt;code&gt;&amp;lt;input type="file"&amp;gt;&lt;/code&gt; element, I can access the (first) selected file via &lt;code&gt;.files[0]&lt;/code&gt; property.&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications" rel="noopener noreferrer"&gt;Using files from web applications&lt;/a&gt; has further explanation of these APIs.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.files[0]&lt;/code&gt; returns a &lt;code&gt;File&lt;/code&gt; object, which is a subclass of &lt;code&gt;Blob&lt;/code&gt;.&lt;br&gt;
Then, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer" rel="noopener noreferrer"&gt;Blob.prototype.arrayBuffer()&lt;/a&gt; function asynchronously reads the entire file into an &lt;code&gt;ArrayBuffer&lt;/code&gt;, providing access to its content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"demo_form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"demo_upload"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#demo_form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#demo_upload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`file size &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;countBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// from the previous article&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`file has &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; zeros and &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ones`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code adds an event listener to the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;.&lt;br&gt;
When the form is submitted, the callback function reads the file into an &lt;code&gt;ArrayBuffer&lt;/code&gt; and passes it as a &lt;code&gt;Uint8Array&lt;/code&gt; to the bit counting function (&lt;code&gt;countBits&lt;/code&gt; from the previous article).&lt;/p&gt;
&lt;h2&gt;
  
  
  ReadableStream
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;file.arrayBuffer()&lt;/code&gt; works, but there's a problem: if the user selects a huge file, the entire file has to be read into the memory all at once, causing considerable memory stress.&lt;br&gt;
To solve this problem, I can use &lt;a href="https://web.dev/streams/" rel="noopener noreferrer"&gt;Streams API&lt;/a&gt; to read the file in smaller chunks, and process each chunk before reading the next.&lt;/p&gt;

&lt;p&gt;From a &lt;code&gt;Blob&lt;/code&gt; object (such as the &lt;code&gt;file&lt;/code&gt; in the snippet above), I can call &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob/stream" rel="noopener noreferrer"&gt;&lt;code&gt;.stream().getReader()&lt;/code&gt;&lt;/a&gt; to create a &lt;code&gt;ReadableStreamDefaultReader&lt;/code&gt;.&lt;br&gt;
Then, I can repeatedly call &lt;code&gt;reader.read()&lt;/code&gt;, which returns a Promise that resolves to either a chunk of data or an end-of-file (EOF) indication.&lt;/p&gt;

&lt;p&gt;To process the file chunk by chunk and count how many &lt;code&gt;1&lt;/code&gt; bits are there, my strategy is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;reader.read()&lt;/code&gt; in a loop to obtain the next chunk.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;done&lt;/code&gt; is true, indicating EOF has been reached, break the loop.&lt;/li&gt;
&lt;li&gt;Add the number of &lt;code&gt;1&lt;/code&gt; bits in each byte of the chunk into the overall counter.&lt;/li&gt;
&lt;li&gt;Finally, calculate how many &lt;code&gt;0&lt;/code&gt; bits are there from the file size, accessible via &lt;code&gt;blob.size&lt;/code&gt; property.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;countBitsBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ReadableStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;ONES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Web Worker
&lt;/h2&gt;

&lt;p&gt;In a web application, it's best to execute complex computations on a background thread, so that the main thread can react to user interactions swiftly.&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer"&gt;Web Workers&lt;/a&gt; are a simple means for web content to run scripts in background threads.&lt;br&gt;
In &lt;em&gt;Deep Atlantic Storage&lt;/em&gt;, I delegated the task of sorting or counting bits in the file to a web worker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw91r3zzyc5qy4sxbmtc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw91r3zzyc5qy4sxbmtc8.png" alt="webpage sends File to worker.js, worker.js sends result or error to webpage" width="600" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the user selects a file and submits the form, the form event handler creates a &lt;code&gt;Worker&lt;/code&gt; (if it has not done so), and calls &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage" rel="noopener noreferrer"&gt;Worker.prototype.postMessage()&lt;/a&gt; to pass the &lt;code&gt;File&lt;/code&gt; object to the background thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#demo_form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#demo_upload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;handleWorkerMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// described later&lt;/span&gt;
  &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;worker.js&lt;/code&gt; runs in the background.&lt;br&gt;
It receives the message (a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent" rel="noopener noreferrer"&gt;MessageEvent&lt;/a&gt; enclosing a &lt;code&gt;File&lt;/code&gt; object) in a function assigned to the global &lt;code&gt;onmessage&lt;/code&gt; variable.&lt;br&gt;
This function then calls &lt;code&gt;countBitsBlob&lt;/code&gt; to count how many zeros and ones are in the file, then calls the global &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/postMessage" rel="noopener noreferrer"&gt;postMessage&lt;/a&gt; function to pass the result back to the webpage main thread.&lt;br&gt;
It also catches any errors that might have been thrown, and passes those to the main thread as well.&lt;br&gt;
I've included &lt;code&gt;type: "result"&lt;/code&gt; and &lt;code&gt;type: "error"&lt;/code&gt; in these two types of messages, so that the main thread can distinguish between them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;countBitsBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that in the &lt;code&gt;catch&lt;/code&gt; clause, the &lt;code&gt;Error&lt;/code&gt; object is converted to a string before being passed to &lt;code&gt;postMessage&lt;/code&gt;.&lt;br&gt;
This is necessary because only &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm" rel="noopener noreferrer"&gt;a handful of types&lt;/a&gt; can pass through &lt;code&gt;postMessage&lt;/code&gt;, but &lt;code&gt;Error&lt;/code&gt; is not one of them.&lt;/p&gt;

&lt;p&gt;Back in the main thread, the &lt;code&gt;handleWorkerMessage&lt;/code&gt; function, which was assigned to &lt;code&gt;worker.onmessage&lt;/code&gt; property, receives messages from the worker thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleWorkerMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`file has &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; zeros and &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ones`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combined with some user interface magic (not described in this article, but you can look at the webpage source code), this makes up the &lt;em&gt;Deep Atlantic Storage&lt;/em&gt; webpage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This article is the second of a 3-part series that reveals the secrets behind &lt;a href="https://yoursunny.com/p/summer-host/storage/" rel="noopener noreferrer"&gt;Deep Atlantic Storage&lt;/a&gt;.&lt;br&gt;
Building upon the bit counting algorithm designed in the &lt;a href="https://yoursunny.com/t/2021/das-sort-bits/" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, I turned it into a web application that reads an uploaded file chunk by chunk via Streams API, and moved the heavy lifting to a background thread via Web Workers.&lt;br&gt;
The &lt;a href="https://yoursunny.com/t/2021/das-stream-bits/" rel="noopener noreferrer"&gt;next part&lt;/a&gt; in this series will explain how I made a server to reconstruct the file from the bit counts.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webworker</category>
    </item>
    <item>
      <title>Deep Atlantic Storage: Sorting Bits</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Sun, 11 Jul 2021 19:34:11 +0000</pubDate>
      <link>https://forem.com/yoursunny/deep-atlantic-storage-sorting-bits-4co2</link>
      <guid>https://forem.com/yoursunny/deep-atlantic-storage-sorting-bits-4co2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally published on yoursunny.com blog &lt;a href="https://yoursunny.com/t/2021/das-sort-bits/" rel="noopener noreferrer"&gt;https://yoursunny.com/t/2021/das-sort-bits/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm bored on 4th of July holiday, so I made a wacky webpage: &lt;a href="https://yoursunny.com/p/summer-host/storage/" rel="noopener noreferrer"&gt;Deep Atlantic Storage&lt;/a&gt;.&lt;br&gt;
It is described as a free file storage service, where you can upload any file to be stored deep in the Atlantic Ocean, with no size limit and content restriction whatsoever.&lt;br&gt;
Since &lt;a href="https://en.wikipedia.org/wiki/Chia_%28cryptocurrency%29" rel="noopener noreferrer"&gt;Chia currency farming&lt;/a&gt; became popular in May, &lt;a href="https://www.tomshardware.com/news/analysis-hdd-prices-skyrocket-high-capacity-hdds-sold-out" rel="noopener noreferrer"&gt;hard drive prices went up significantly&lt;/a&gt;.&lt;br&gt;
How can I afford to operate an unlimited free storage service?&lt;/p&gt;
&lt;h2&gt;
  
  
  "Advanced Sorting Technology"
&lt;/h2&gt;

&lt;p&gt;One of the benefits listed on &lt;em&gt;Deep Atlantic Storage&lt;/em&gt; webpage is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced sorting technology keeps your data neatly ordered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What this means is that, content in the uploaded file would be sorted before being stored.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://en.wikipedia.org/wiki/Sorting_algorithm" rel="noopener noreferrer"&gt;sorting algorithm&lt;/a&gt; is an algorithm that puts elements of a list in a certain order, such as numerical order or lexicographical order.&lt;br&gt;
Every coder knows a few sorting algorithms, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;quick sort&lt;/li&gt;
&lt;li&gt;bubble sort&lt;/li&gt;
&lt;li&gt;merge sort&lt;/li&gt;
&lt;li&gt;insertion sort&lt;/li&gt;
&lt;li&gt;selection sort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most sorting algorithms are &lt;em&gt;comparison sorts&lt;/em&gt; that rely on a comparison function to determine the relative order between two elements.&lt;br&gt;
For example, the program below (&lt;a href="https://godbolt.org/z/he4q8s8nr" rel="noopener noreferrer"&gt;try on Compiler Explorer&lt;/a&gt;) sorts a list of points on a two-dimensional Euclidean plane by its distance from the origin.&lt;br&gt;
It uses &lt;a href="https://en.cppreference.com/w/cpp/algorithm/sort" rel="noopener noreferrer"&gt;&lt;code&gt;std::sort&lt;/code&gt;&lt;/a&gt; function from the C++ standard library, passing a custom comparison function that returns &lt;code&gt;true&lt;/code&gt; if the first point is closer to the origin point &lt;code&gt;(0,0)&lt;/code&gt; than the second point, or &lt;code&gt;false&lt;/code&gt; otherwise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;algorithm&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;cmath&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;cstdio&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mf"&gt;0.9&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%+0.1f, %+0.1f&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The input and output of a sorting algorithm are both a list of &lt;em&gt;elements&lt;/em&gt;.&lt;br&gt;
&lt;em&gt;Deep Atlantic Storage&lt;/em&gt; deals with files.&lt;br&gt;
A file must first be turned into a list of elements before it can be sorted.&lt;/p&gt;

&lt;p&gt;There are many ways to interpret a file as a list of elements.&lt;br&gt;
If the file is a database, following the database structure, each table in the database is a list of rows that can be sorted.&lt;br&gt;
If the file is plain text, the &lt;a href="https://man7.org/linux/man-pages/man1/sort.1.html" rel="noopener noreferrer"&gt;Unix sort command&lt;/a&gt; can read it as a list of text lines that can be sorted.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;Deep Atlantic Storage&lt;/em&gt;, I decided to use the most basic unit of information: &lt;a href="https://en.wikipedia.org/wiki/Bit" rel="noopener noreferrer"&gt;bit&lt;/a&gt;.&lt;br&gt;
When you upload a file to my unlimited storage service, the bits contained in the file are sorted in ascending order.&lt;br&gt;
For example, suppose the file has the text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@yoursunny
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In binary form it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@        y        o        u        r        s        n        n        n        y
01000000 01111001 01101111 01110101 01110010 01110011 01110101 01101110 01101110 01111001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I sort all the bits, it becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000000 00000000 00000000 00000000 00111111 11111111 11111111 11111111 11111111 11111111
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sorting Bits » Counting Bits
&lt;/h2&gt;

&lt;p&gt;Naively, I can collect every bit in the input file into a list of bits, and sort them using a "normal" sort algorithm (&lt;a href="https://runkit.com/embed/008512pzpm2y" rel="noopener noreferrer"&gt;try on RunKit&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@yoursunny&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;compares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;compares&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; elements`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;compares&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; compares`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort" rel="noopener noreferrer"&gt;Array.prototype.sort()&lt;/a&gt; is a comparison sort algorithm.&lt;br&gt;
Theoretically, a comparison sort algorithm cannot perform better than &lt;em&gt;O(n log n)&lt;/em&gt; comparisons, where &lt;em&gt;n&lt;/em&gt; is the number of elements in the input list.&lt;br&gt;
For my 80-bit input, Node.js v16.3.0 invoked the comparison function 322 times.&lt;br&gt;
If the input was longer, considerably more comparisons would be needed.&lt;/p&gt;

&lt;p&gt;Since there are only two possible values, &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;, for each bit, there is a better algorithm: counting sort.&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Counting_sort" rel="noopener noreferrer"&gt;Counting sort&lt;/a&gt; is an integer sorting algorithm suitable for a list of small non-negative integers.&lt;br&gt;
It does not use a comparison function and hence is a non-comparison sorting algorithm.&lt;br&gt;
Instead, counting sort first counts how many elements possess each distinct key value, then uses these counts to determine the positions of each key value in the output list.&lt;br&gt;
Its time complexity is &lt;em&gt;O(n+k)&lt;/em&gt;, where &lt;em&gt;n&lt;/em&gt; is the number of elements and &lt;em&gt;k&lt;/em&gt; is the maximum integer key value in the list.&lt;/p&gt;

&lt;p&gt;A counting sort on the same input can be written as (&lt;a href="https://play.golang.org/p/uLmphjlJK72" rel="noopener noreferrer"&gt;try on Go Playground&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;sortBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sorted&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sorted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sorted&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@yoursunny"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;0x01&lt;/span&gt;
            &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;sorted&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sortBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sorted Bits » Bit Counts
&lt;/h2&gt;

&lt;p&gt;A sorting algorithm does not change the size of the list being sorted.&lt;br&gt;
Suppose a 1GB file is uploaded to &lt;em&gt;Deep Atlantic Storage&lt;/em&gt;, there are 8589934592 bits in this file before sorting, and there would be still 8589934592 bits after sorting.&lt;br&gt;
Storing a sorted file takes as much disk space as storing the original unsorted file.&lt;/p&gt;

&lt;p&gt;Looking at the sorted bits, there is an important observation:&lt;br&gt;
after sorting, all the &lt;code&gt;0&lt;/code&gt; bits are together, and all the &lt;code&gt;1&lt;/code&gt; bits are together!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000000 00000000 00000000 00000000 00111111 11111111 11111111 11111111 11111111 11111111
\_____________ 34 zeros _____________/\____________________ 46 ones ____________________/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of storing the same bit repeatedly, I only need to remember: "there are 34 zeros followed by 46 ones".&lt;br&gt;
This allows &lt;em&gt;Deep Atlantic Storage&lt;/em&gt; to store sorted large files with considerably less disk space than the original files: any file, regardless of its size, can be represented by two numbers.&lt;/p&gt;

&lt;p&gt;Given a list of sorted bits, I can iterate over the list and count the number of consecutive zeros and ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;groupby&lt;/span&gt;

&lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;00000000000000000000000000000000001111111111111111111111111111111111111111111111&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is, in fact, the basic idea of &lt;a href="https://en.wikipedia.org/wiki/Run-length_encoding" rel="noopener noreferrer"&gt;run-length encoding&lt;/a&gt;, a lossless data compression method.&lt;/p&gt;

&lt;p&gt;However, it's unnecessary to run a sorting algorithm followed by a compression algorithm.&lt;br&gt;
Instead, I can let the counting sort algorithm return the counters for zeros and ones directly, skipping the unnecessary step of constructing a sorted list of bits.&lt;/p&gt;

&lt;p&gt;Well, actually I don't even need to count both zeros and ones.&lt;br&gt;
Since there are 8 bits in every byte, it suffices to only count the &lt;code&gt;1&lt;/code&gt; bits, and I can calculate the number &lt;code&gt;0&lt;/code&gt; bits to be &lt;code&gt;8 * bytes - ones&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that, our bit &lt;del&gt;sorting&lt;/del&gt; counting algorithm becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;countBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bit Counting » Byte Counting
&lt;/h2&gt;

&lt;p&gt;Looking at the bit counting algorithm, the inner loop that iterates over the bits within a byte would be executed once for every byte, which is a hot spot worth optimization.&lt;br&gt;
To optimize this code, I aim at eliminating the loop.&lt;/p&gt;

&lt;p&gt;In a byte, there are 256 possible values between 0x00 and 0xFF.&lt;br&gt;
The number of zeros and ones in each byte value never changes.&lt;br&gt;
Therefore, it is unnecessary to loop over the bits every time.&lt;br&gt;
Instead, I can build a lookup table that maps a byte value into the number of &lt;del&gt;zeros and&lt;/del&gt; ones in that byte.&lt;/p&gt;

&lt;p&gt;This code, executed during initialization, prepares the lookup table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ONES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mh"&gt;0xFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;ONES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this lookup table, I can count bits in a file more efficiently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;countBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cnt0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cnt1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;ONES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As measured on &lt;a href="https://jsben.ch/Ut9lc" rel="noopener noreferrer"&gt;JSBEN.CH&lt;/a&gt;, the lookup table approach is 3~5x faster than the previous algorithm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, I reviewed commonly used sorting algorithms, explained why counting sort is more efficient on a list of bits where each bit is either &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;, explored how to compactly store sorted bits as two numbers, and finally optimized the algorithm using a lookup table.&lt;/p&gt;

&lt;p&gt;This article is the first of a 3-part series that reveals the secrets behind &lt;a href="https://yoursunny.com/p/summer-host/storage/" rel="noopener noreferrer"&gt;Deep Atlantic Storage&lt;/a&gt;.&lt;br&gt;
The &lt;a href="https://yoursunny.com/t/2021/das-file-worker/" rel="noopener noreferrer"&gt;next part&lt;/a&gt; in this series will explain how the bit sorting aka byte counting algorithm is used in a web application.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>javascript</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Google Code Jam 2021 is Coming</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Fri, 26 Mar 2021 12:31:10 +0000</pubDate>
      <link>https://forem.com/yoursunny/google-code-jam-2021-is-coming-2m48</link>
      <guid>https://forem.com/yoursunny/google-code-jam-2021-is-coming-2m48</guid>
      <description>&lt;p&gt;Code Jam is an annual programming competition organized by Google, with a focus on algorithms.&lt;/p&gt;

&lt;p&gt;The Qualifications Round of Code Jam 2021 starts today, and continues for 30 hours.&lt;br&gt;
You can register and participate at the Code Jam website: &lt;a href="https://g.co/codejam" rel="noopener noreferrer"&gt;https://g.co/codejam&lt;/a&gt;&lt;/p&gt;

</description>
      <category>news</category>
      <category>competition</category>
    </item>
    <item>
      <title>How Many Servers Do You Need for a Crucial Website?</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Fri, 19 Mar 2021 04:55:30 +0000</pubDate>
      <link>https://forem.com/yoursunny/how-many-servers-do-you-need-for-a-crucial-website-51np</link>
      <guid>https://forem.com/yoursunny/how-many-servers-do-you-need-for-a-crucial-website-51np</guid>
      <description>&lt;p&gt;A crucial website ought to have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a primary server: Ryzen CPU, NVMe storage.&lt;/li&gt;
&lt;li&gt;a secondary standby: Xeon CPU, SSD storage.&lt;/li&gt;
&lt;li&gt;a third replica: Atom CPU, spinning rust storage.&lt;/li&gt;
&lt;li&gt;a fourth backup: ARMv5 CPU, VHS tape.&lt;/li&gt;
&lt;li&gt;a system to automatically perform failover.&lt;/li&gt;
&lt;li&gt;a cat to look over this system.&lt;/li&gt;
&lt;li&gt;a robot to feed the cat.&lt;/li&gt;
&lt;li&gt;an engineer to repair the robot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These can get your data 99.99% secure.&lt;/p&gt;

&lt;p&gt;Now add some cold storage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;punch cards kept in California.&lt;/li&gt;
&lt;li&gt;microfilm buried in Antarctica.&lt;/li&gt;
&lt;li&gt;vinyl record launched to Jupiter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all four online servers are lost, you are looking at a lengthy downtime, but your data would be 99.9999% secure by recovering from the cold storage.&lt;/p&gt;

&lt;p&gt;To finally reach 100%, transmit your data as radio waves toward a black hole.&lt;br&gt;
If California fells into the ocean, Antarctica melts, and the sun explodes taking out Jupiter, you can travel to the black hole and access your data.&lt;/p&gt;

&lt;p&gt;According to physics, information cannot be destroyed by a black hole.&lt;br&gt;
It's the ultimate storage with infinite space.&lt;br&gt;
Once you enter the black hole, you won't be able to come back, but you are with your data forever.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>jokes</category>
    </item>
    <item>
      <title>OVH Strasbourg: Halt and Catch Fire, Data Uploaded to the Cloud</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Sat, 13 Mar 2021 01:21:15 +0000</pubDate>
      <link>https://forem.com/yoursunny/ovh-strasbourg-halt-and-catch-fire-data-uploaded-to-the-cloud-4j19</link>
      <guid>https://forem.com/yoursunny/ovh-strasbourg-halt-and-catch-fire-data-uploaded-to-the-cloud-4j19</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally published on yoursunny.com blog &lt;a href="https://yoursunny.com/t/2021/OVH-halt-and-catch-fire/" rel="noopener noreferrer"&gt;https://yoursunny.com/t/2021/OVH-halt-and-catch-fire/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;2021-03-10, an OVH Cloud data center in Strasbourg, France caught on fire.&lt;br&gt;
Thousands of servers have been destroyed by fire.&lt;br&gt;
Thousands more are currently unavailable due to power cut, and will remain offline for several more days.&lt;/p&gt;

&lt;p&gt;Data stored in those servers have been uploaded to the cloud via black smoke.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frt3eavens1syfbvutdmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frt3eavens1syfbvutdmd.png" alt="a building is burning with black smoke rising to the sky" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to Hacker News, a Dev mistakenly invoked the &lt;a href="https://en.wikipedia.org/wiki/Halt_and_Catch_Fire_%28computing%29" rel="noopener noreferrer"&gt;Halt and Catch Fire&lt;/a&gt; instruction on an Uninterrupted Power Supply unit, causing this incident.&lt;/p&gt;

&lt;p&gt;Chairman of OVH cloud advised clients to activate their disaster recovery plans, such as restoring off-site backups to a new cloud server.&lt;br&gt;
Some clients are crying because they did not have backups, or they stored their backups on another machine in the same data center.&lt;br&gt;
Other clients experienced no downtime because they designed their systems for datacenter scale redundancy.&lt;br&gt;
&lt;a href="https://www.explainxkcd.com/wiki/index.php/1737:_Datacenter_Scale" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngj87uvjbpkt0ajlnx9r.png" alt="datacenter scale" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>news</category>
      <category>datacenter</category>
      <category>jokes</category>
    </item>
    <item>
      <title>Rename WiFi Interface on Ubuntu 20.04</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Fri, 05 Mar 2021 01:43:47 +0000</pubDate>
      <link>https://forem.com/yoursunny/rename-wifi-interface-on-ubuntu-20-04-2448</link>
      <guid>https://forem.com/yoursunny/rename-wifi-interface-on-ubuntu-20-04-2448</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally published on yoursunny.com blog &lt;a href="https://yoursunny.com/t/2021/WiFi-rename/" rel="noopener noreferrer"&gt;https://yoursunny.com/t/2021/WiFi-rename/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;During an experiment, I need to use three WiFi interfaces on a Raspberry Pi running Ubuntu 20.04.&lt;br&gt;
In addition to Raspberry Pi's internal WiFi interface, I added two USB WiFi adapters.&lt;br&gt;
Three network interfaces showed up in the system (&lt;code&gt;ip link&lt;/code&gt; command), and they are named wlan0, wlan1, and wlan2 by default.&lt;/p&gt;

&lt;p&gt;I often need to capture packets with &lt;code&gt;tcpdump&lt;/code&gt;, and I often have to be type these interface names manually.&lt;br&gt;
It isn't easy to remember the purpose of each network interface, so I wanted to rename the interfaces to reflect their role in my application.&lt;br&gt;
However, this isn't as easy as it sounds.&lt;/p&gt;
&lt;h2&gt;
  
  
  🚫 Netplan
&lt;/h2&gt;

&lt;p&gt;Ubuntu 20.04 configures network interfaces using &lt;a href="https://netplan.io" rel="noopener noreferrer"&gt;Netplan&lt;/a&gt;, so my first thought was: I can write a Netplan configuration that matches network interfaces with their MAC addresses, and assigns the desired name to each network interface.&lt;/p&gt;

&lt;p&gt;The config file would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;wifis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uplink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;macaddress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ba:fe:de:f0:b9:e4&lt;/span&gt;
      &lt;span class="na"&gt;set-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uplink&lt;/span&gt;
      &lt;span class="na"&gt;access-points&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rP8jKHJ64&lt;/span&gt;
      &lt;span class="na"&gt;dhcp4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;dhcp6&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;accept-ra&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this method would not work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;netplan apply
ERROR: uplink: networkd backend does not support wifi with match:, only by interface name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚫 70-persistent-net.rules
&lt;/h2&gt;

&lt;p&gt;Many online guides refer to a file &lt;code&gt;/etc/udev/rules.d/70-persistent-net.rules&lt;/code&gt;, which is processed by udev during boot.&lt;br&gt;
The file should have been created automatically by the system upon discovery of the network interfaces, and I just need to modify the interface names to the desired names.&lt;/p&gt;

&lt;p&gt;However, this file does not exist in Ubuntu 20.04, so it's another dead end.&lt;/p&gt;
&lt;h2&gt;
  
  
  ✔️ systemd.link
&lt;/h2&gt;

&lt;p&gt;After carefully reading &lt;a href="https://wiki.debian.org/NetworkInterfaceNames" rel="noopener noreferrer"&gt;Debian's NetworkInterfaceNames guide&lt;/a&gt;, I found the correct way: &lt;a href="https://www.freedesktop.org/software/systemd/man/systemd.link.html" rel="noopener noreferrer"&gt;systemd.link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To rename WiFi interfaces, I can create a systemd configuration file for each network interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[Match]
MACAddress=ba:fe:de:f0:b9:e4
[Link]
Name=uplink'&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/systemd/network/10-uplink.link
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After rebooting, the network interface is renamed to what I wanted.&lt;/p&gt;

</description>
      <category>wifi</category>
      <category>ubuntu</category>
      <category>raspberrypi</category>
    </item>
    <item>
      <title>What would you need 64GB of RAM for?</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Sat, 27 Feb 2021 06:09:19 +0000</pubDate>
      <link>https://forem.com/yoursunny/what-would-you-need-64gb-of-ram-for-17bb</link>
      <guid>https://forem.com/yoursunny/what-would-you-need-64gb-of-ram-for-17bb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is originally &lt;a href="https://www.quora.com/What-would-you-need-64GB-of-RAM-for/answer/Junxiao-Shi" rel="noopener noreferrer"&gt;published on Quora&lt;/a&gt;, with code links at the end&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used 58GB of RAM once. NodeJS refuses to use more memory and I had to rewrite the program in C++.&lt;/p&gt;

&lt;p&gt;It's in a research project. I captured a packet trace from Network File System (NFS) server, and I want to reconstruct the complete pathnames accessed in each command. The way NFS works is that, every command only carries a component of the name (like a directory name or a filename within a directory), along with a handle that represents the directory in which the file resides. To get the complete pathname, I need a lookup table of all known handles and their corresponding pathnames. Then, for each new command that has a handle and one more name component, I can query this lookup table to find out the pathname of that handle, and append the new component to get the complete pathname. Finally, I'd insert the new pathname along with the new handle into the table.&lt;/p&gt;

&lt;p&gt;I have 24 hours of packet trace with millions of these handles. It shouldn't take so much memory, but I somehow decided to write the program in NodeJS. NodeJS struggled to allocate more and more memory as it reads the input, until it reaches 58GB after several hours. The machine has 96GB but NodeJS won't use more memory. I can tell that it's no longer making progress, because records in &lt;code&gt;/proc/*/fdinfo&lt;/code&gt; indicates that the cursor in input file isn't moving anymore.&lt;/p&gt;

&lt;p&gt;I spent four hours rewriting this part of the program in C++. More specifically, I hosted the lookup table in a C++ program, and had the NodeJS program communicate with C++ process via Unix socket. The program completed in 15 minutes with no more than 1GB of memory.&lt;/p&gt;

&lt;p&gt;Conclusion: if you need 64GB of memory just for data structures, you probably need a better programming language.&lt;/p&gt;

&lt;h2&gt;
  
  
  source code links
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/yoursunny/nfsdump" rel="noopener noreferrer"&gt;https://github.com/yoursunny/nfsdump&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pathtree/fullpath.js&lt;/code&gt; is the Node.js edition, for Node.js v0.6 on Ubuntu 12.04.&lt;br&gt;
&lt;code&gt;pathtree/fullpath.cc&lt;/code&gt; is the C++ edition implementing same functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  highlighted Quora comments
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Michael Johnson-Moore&lt;/strong&gt;&lt;br&gt;
Conclusion 2: If you need something to be fast, don't use an interpreted language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alex Bujorianu&lt;/strong&gt;&lt;br&gt;
Many more developers should heed this lesson! ... I really don’t understand why a cloud desktop client, a text editor (!) etc. need 100MB of memory per instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ubeyde Mavus&lt;/strong&gt;&lt;br&gt;
Or you need to know your way around C++ better than your way around NodeJS.&lt;/p&gt;

</description>
      <category>node</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Hacktoberfest 2020 shirt arrived</title>
      <dc:creator>Junxiao Shi</dc:creator>
      <pubDate>Sat, 20 Feb 2021 03:49:05 +0000</pubDate>
      <link>https://forem.com/yoursunny/hacktoberfest-2020-shirt-arrived-4005</link>
      <guid>https://forem.com/yoursunny/hacktoberfest-2020-shirt-arrived-4005</guid>
      <description>&lt;p&gt;I managed to finish the challenge despite that the rules changed 3 times during the month. Most of my contributions went to &lt;a href="https://definitelytyped.org/" rel="noopener noreferrer"&gt;DefinitelyTyped&lt;/a&gt;, a collection of TypeScript definitions that make my own life easier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4pyhgkb0uds3n9lyrdnq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4pyhgkb0uds3n9lyrdnq.png" alt="a blue T-shirt with some stickers on top of it" width="800" height="879"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I picked the dark-colored shirt, so that it wouldn't get ruined if I decide to roll around in the mud. Yes I still do that, because it relieves stress.&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
    </item>
  </channel>
</rss>
