<?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>DEV Community: knut</title>
    <description>The latest articles on DEV Community by knut (@knutaf).</description>
    <link>https://dev.to/knutaf</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%2F23947%2Fae357088-f39e-4a25-ad39-d012e17f8bdb.png</url>
      <title>DEV Community: knut</title>
      <link>https://dev.to/knutaf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/knutaf"/>
    <language>en</language>
    <item>
      <title>Rust, WASM, and LOK</title>
      <dc:creator>knut</dc:creator>
      <pubDate>Thu, 02 May 2024 04:42:29 +0000</pubDate>
      <link>https://dev.to/knutaf/rust-wasm-and-lok-1dfi</link>
      <guid>https://dev.to/knutaf/rust-wasm-and-lok-1dfi</guid>
      <description>&lt;p&gt;(Cross-posted from &lt;a href="https://knutaf.com/blog/posts/20240502-rust-wasm-and-lok/" rel="noopener noreferrer"&gt;my main blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Have you played &lt;a href="https://letibus.itch.io/lok" rel="noopener noreferrer"&gt;LOK&lt;/a&gt;? It's a puzzle game in the form of a grid of squares with letters in them. You pick cells from the board in certain orders to activate "keywords" and then execute the effect of the keyword. The objective of each puzzle is to black out all the squares on the grid.&lt;/p&gt;

&lt;p&gt;I purchased the physical book a while back and finished it recently. I had a lot of fun, especially with some of the later levels that have pretty cool reveals. There's also a &lt;a href="https://store.steampowered.com/app/2207440/LOK_Digital/" rel="noopener noreferrer"&gt;video game version of LOK&lt;/a&gt; in development, which got me thinking about how someone would make that, given the types of puzzles in the book. I love developing in Rust and had been thinking about experimenting with WASM to see its capabilities and how Rust works with it, so decided to combine these two together into a side project I uncreatively called LOK-Wasm.&lt;/p&gt;

&lt;p&gt;I'll give a little overview of the architecture, but I think the more interesting parts are the game design aspects and how to set up the interface for the player.&lt;/p&gt;

&lt;p&gt;These are of course spoilers for the game's mechanics, but if you want to try it out, with the author's permission, I have it hosted at &lt;a href="https://knutaf.com/lok_wasm" rel="noopener noreferrer"&gt;https://knutaf.com/lok_wasm&lt;/a&gt;. And if you want to see the source code, I have it hosted on my &lt;a href="https://github.com/knutaf/lok_wasm" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rust + WASM
&lt;/h2&gt;

&lt;p&gt;First of all, a quick rundown of what WASM is. It stands for &lt;a href="https://webassembly.org/" rel="noopener noreferrer"&gt;Web Assembly&lt;/a&gt;. In essence, similar to how Java compiles down to a bytecode that is interpreted by a Java Virtual Machine, Web Assembly is a different bytecode interpreted by the browser. Many different languages can compile into WASM, and Javascript can interface with it like a module. In my case, I wrote a lot of the source code in Rust and compiled it down to a WASM module, then called into it from Javascript.&lt;/p&gt;

&lt;p&gt;Could I have written the whole thing in Javascript? Sure, but what would be the fun in that?&lt;/p&gt;

&lt;p&gt;Like many Rust features, there is a &lt;a href="https://rustwasm.github.io/docs/book/" rel="noopener noreferrer"&gt;Rust/WASM tutorial and book&lt;/a&gt; that was very good. I was able to follow the tutorial to get up and running with development pretty easily... though this entire ecosystem kind of has way too many moving parts. Webpack? NPM? Nodejs? Just for this? It's a bit heavyweight, but I guess like 90% of the development in the world uses stuff like this now, huh?&lt;/p&gt;

&lt;p&gt;Using that tutorial, I was also able to run &lt;code&gt;npm run build&lt;/code&gt; to produce the distribution package of static files I then uploaded to my web site for sharing. I wrote this paragraph in here as much for me as for you, dear readers, because I had to go looking on the Internet to figure out this magic incantation. Probably someone well-versed in NPM packages would have known this right away, but alas, I'm not that guy.&lt;/p&gt;

&lt;p&gt;I was able to easily upload the few files it generated to my web hosting provider. I did have to set up the MIME type for the WASM file there, by adding the following to my &lt;code&gt;.htaccess&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AddType application/wasm .wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Rust/WASM interface
&lt;/h2&gt;

&lt;p&gt;Having gone through the tutorial, I needed to decide what the architecture of my game was going to be. My main guiding principle was that I wanted &lt;em&gt;most&lt;/em&gt; of the code to be in Rust, especially the logic that implemented the rule checking. I decided I was OK having some static HTML and CSS for display, and Javascript to "render" objects exposed by the WASM module into the right HTML.&lt;/p&gt;

&lt;p&gt;The LOK puzzles are all grid-based, so I grabbed an old Grid object I had previously written in Rust. Following the Rust tutorial, I exposed the contents of the grid as an array of &lt;code&gt;u8&lt;/code&gt; so that the Javascript side could map the region of memory for fast, direct access. I like this sort of thing in general, so I wanted to see if I could make it work.&lt;/p&gt;

&lt;p&gt;Well, I was able to keep that for a while, but as I implemented more of the LOK rules, I ended up having too much state I needed to store on each &lt;code&gt;BoardCell&lt;/code&gt; object, so I abandoned the mapped &lt;code&gt;u8&lt;/code&gt; array idea and just exposed a &lt;code&gt;Board::get&lt;/code&gt; accessor to fetch a cell by row/col. I'm sure this incurs marshaling overhead, but my &lt;em&gt;n&lt;/em&gt; is so small that there's no way it matters--LOK puzzles just aren't that big. Maybe 10x10 at most.&lt;/p&gt;

&lt;p&gt;So I had just two classes exposed: &lt;code&gt;Board&lt;/code&gt; and &lt;code&gt;BoardCell&lt;/code&gt;. &lt;code&gt;Board&lt;/code&gt; had getters like the height and width and of course fetching each cell. It also had actions, the verbs allowable by LOK puzzles. &lt;code&gt;BoardCell&lt;/code&gt; only exposed getters necessary for rendering the puzzle in HTML: things like whether a cell should be interactable, what letter to draw on it, and how it should be styled.&lt;/p&gt;

&lt;p&gt;The Javascript code iterates over all the cells in the Board, fetches each one, and generates a &lt;code&gt;table&lt;/code&gt; with &lt;code&gt;tr&lt;/code&gt; and &lt;code&gt;td&lt;/code&gt; in it, applying different CSS classes to the cells depending on the current state of each one.&lt;/p&gt;

&lt;p&gt;One important part of the UI is the ability for the player to enter a puzzle. I just used a &lt;code&gt;textarea&lt;/code&gt; and let the player write in monospace font in it. If you want a 2x3 puzzle, just enter two rows of characters with 3 characters in each row. Underscore indicates a blank cell and a dash is a gap in the board. That's all you need to represent LOK puzzles. I thought about having a grid-based editor, but my I thought about wrangling all the event listeners and CSS styles and just felt tired, so I didn't.&lt;/p&gt;

&lt;p&gt;The other verbs I implemented in the UI were the different actions you can take in the LOK puzzle, an undo button, and of course one to submit for checking if you have a proper solution entered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Undo
&lt;/h2&gt;

&lt;p&gt;The undo functionality was implemented in an inefficient but simple way. Every time the player makes a move in the puzzle, I copy the entire grid plus the move they made, modify the new copy of the board, and push them onto a stack. Undo simply pops the top of the stack, so the previous board state is now the thing to render. That also pops the latest move, keeping the board state consistent with the list of moves that got it there.&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="cd"&gt;/// Marks the specified cell as blackened and tracks this move in the solution.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;blacken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&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;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.grid&lt;/span&gt;&lt;span class="nf"&gt;.height&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.grid&lt;/span&gt;&lt;span class="nf"&gt;.width&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// Make a copy of the entire board and store that with the move, for easy undo.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;target_rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;RC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;new_grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.get_latest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;new_grid&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;target_rc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.blacken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.moves&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BoardStep&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Blacken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_rc&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_grid&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="cd"&gt;/// Removes the latest move from the solution.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;undo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.moves&lt;/span&gt;&lt;span class="nf"&gt;.pop&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;
  
  
  Checking the solution
&lt;/h2&gt;

&lt;p&gt;I did most of the game logic for checking the solution with test-driven development. It was easy to do it this way because of the simple nature of the code with no external dependencies. My tests set up a puzzle and then enter in the moves to solve it. The checker returns a result that indicates if it was successful, or if not, specifically why it failed and at which move number in the attempted solution. This let me author tests for every specific failure type.&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="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lok1x4_correct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Board&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LOK_"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.blacken&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.blacken&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.blacken&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.blacken&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.check_solution&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;SR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Correct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lok1x5_unsolvable_out_of_order&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Board&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LKO_"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.blacken&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.blacken&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.blacken&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.blacken&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="nf"&gt;.check_solution&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nn"&gt;SR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ErrorOnMove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ME&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BlackenNotConnectedForKeyword&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;In whatever projects I work on, especially at work, I'm a big fan of having different error codes for different codepaths. If I had it my way, every error would uniquey identify which call-site it came from. I managed to do that with this project, so the test code very nicely describes the expectation in each case.&lt;/p&gt;

&lt;p&gt;The checker iterates over the ordered list of moves that the player (or test) entered and modifies a copy of the puzzle grid for each step. It also keeps track of what state it's in--like gathering a keyword or executing a specific keyword, accompanied by any extra info about the current state. Rust's enum types are great for this.&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;enum&lt;/span&gt; &lt;span class="n"&gt;BoardState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;GatheringKeyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Move&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;ExecutingLOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="cd"&gt;/// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each move is checked against the current state to decide if it's permitted or not. Finally, after all the moves are done, it checks the overall grid remaining to make sure it's complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Coverage
&lt;/h2&gt;

&lt;p&gt;As of the time I'm writing this blog, the main file is 2665 lines long, of which 63% of it is tests. I wrote a lot of tests! Towards the end, I also tried out Rust's code coverage support to see what tests I'd missed. It was cool because it found some tests that were not catching the conditions I expected and areas I'd forgotten to cover.&lt;/p&gt;

&lt;p&gt;Rust's code coverage support is heavily based on what LLVM provides. There's a pretty decent suite of tools, and they're runnable from cargo (Rust's build tool). There's a good &lt;a href="https://doc.rust-lang.org/rustc/instrument-coverage.html" rel="noopener noreferrer"&gt;section of the book on code coverage&lt;/a&gt;, including how to instrument your code, generate data, and analyze data. I was able to follow it pretty easily.&lt;/p&gt;

&lt;p&gt;One tweak is that the book suggests &lt;code&gt;cargo-binutils&lt;/code&gt; is optional, but I couldn't really find a good way to run the tools without it.&lt;/p&gt;

&lt;p&gt;Here's the script I ended up using for generating a coverage report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;del&lt;/span&gt; &lt;span class="kd"&gt;default_&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;.prof&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kd"&gt;cargo&lt;/span&gt; &lt;span class="kd"&gt;test&lt;/span&gt;
&lt;span class="kd"&gt;cargo&lt;/span&gt; &lt;span class="kd"&gt;profdata&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="kd"&gt;merge&lt;/span&gt; &lt;span class="na"&gt;-sparse &lt;/span&gt;&lt;span class="kd"&gt;default_&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;.profraw &lt;span class="na"&gt;-o &lt;/span&gt;&lt;span class="kd"&gt;default&lt;/span&gt;.profdata
&lt;span class="kd"&gt;cargo&lt;/span&gt; &lt;span class="kd"&gt;cov&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="kd"&gt;show&lt;/span&gt; &lt;span class="na"&gt;-Xdemangler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kd"&gt;rustfilt&lt;/span&gt; &lt;span class="kd"&gt;target&lt;/span&gt;\debug\deps\lok_wasm&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="kd"&gt;d4ce0b412053771&lt;/span&gt;&lt;span class="err"&gt;.exe&lt;/span&gt; &lt;span class="na"&gt;--instr-profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kd"&gt;default&lt;/span&gt;.profdata &lt;span class="na"&gt;--output-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kd"&gt;coverage&lt;/span&gt; &lt;span class="na"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kd"&gt;html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a few iterations of generating reports and adding tests, I managed to get 98% line coverage, and the only remaining parts were bits exclusive to the UI. Not bad at all. I don't think I've ever managed that with a project at work, heh.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile version
&lt;/h2&gt;

&lt;p&gt;I was pleasantly surprised to find that it Just Worked on my phone's browser. I guess that's the power of WASM?? I don't know. Actually I had to add one magical incantation:&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;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apparently this fixes font scaling on mobile browsers? I don't know. Without it, the font sizes were all over the place, way too big in some places, way too small in others.&lt;/p&gt;

&lt;h2&gt;
  
  
  SPOILER WARNING for next section
&lt;/h2&gt;

&lt;p&gt;The sections after this one are full of spoilers for the LOK mechanics. The original game does a great job of guiding you through learning how to play, gradually ramping you up on more complicated mechanics. It's masterfully done, so if you were thinking of playing, I recommend not reading the next section onwards.&lt;/p&gt;

&lt;p&gt;Without spoilers, I can say that my number one &lt;strong&gt;guiding principle&lt;/strong&gt; was to have my version &lt;strong&gt;reveal as little as possible about how the mechanics work&lt;/strong&gt;. It is an oracle: you can submit a solution and get a result of yes/no, but if you want to know how the mechanics work, well, I've deliberately tried to make that difficult.&lt;/p&gt;

&lt;p&gt;OK, now go away. Unless you've aready beaten the game, in which case, welcome!&lt;/p&gt;

&lt;h2&gt;
  
  
  Gathering Keywords
&lt;/h2&gt;

&lt;p&gt;Reminder: spoilers from here on out. OK, let's go. So a big part of LOK is keywords. The main verb that players have for a good chunk of the book is "blackening" out a cell. If you black out all the squares in the right order that make up a keyword, you then get to (have to, actually) use the keyword's effect.&lt;/p&gt;

&lt;p&gt;So of course I had to add a "blacken" verb to the UI. However, unless I took care with it, I could accidenetally leak information about how the mechanics work.&lt;/p&gt;

&lt;p&gt;When you blacken a square, it does change color. However, you can click on it multiple times. Normally this would be a no-op, but I don't want to reveal that, so instead the letter in the cell gets a little superscript showing how many times it was clicked on. This lets the player know their click was registered and also lets them know their "undo" was registered, if they hit Undo.&lt;/p&gt;

&lt;p&gt;I guess I could have made it a toggle, even though that's not valid in the game either, but that would be misleading, because a player might think the toggle acts like an Undo, but I didn't implement it that way.&lt;/p&gt;

&lt;p&gt;When the player has successfully blackened out a keyword, there's no feedback in the UI that they've done something interesting. The solution checker is &lt;em&gt;expecting&lt;/em&gt; the player to execute the keyword correctly next, but the only feedback the player gets is a "yes" or "no" when they click the "Check!" button, not when they interact with the board.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conductors and Paths
&lt;/h2&gt;

&lt;p&gt;Implementing the keywords LOK, TLAK, and TA were pretty straightforward. They all only use the "blacken" verb. The next choice came when implementing the conductor, 'X'. Conductors allow you to gather a keyword by selecting cells that aren''t in a straight line; you can "bend" the line at right angles.&lt;/p&gt;

&lt;p&gt;In terms of interface, I could have chosen to just let the player pick the cells of the keyword without requiring them to enter any additional information about what path they're using. In fact that was my first thought. But then I started thinking about having to write the pathfinding code to verify that the player chose a valid path and realized: hey! why should &lt;em&gt;I&lt;/em&gt; do this? The player should be telling &lt;em&gt;me&lt;/em&gt; what path they want to use, and I can verify if it's valid. Let them do the hard work, right?&lt;/p&gt;

&lt;p&gt;The other advantage of forcing the player to enter the path is that there's less chance of accidentally getting a puzzle correct without fully demonstrating an understanding of the solution. In other words, the player could enter an underspecified solution and get lucky.&lt;/p&gt;

&lt;p&gt;The downside is that it reveals information about the mechanics, namely that the concept of a "path" exists. The way I presented it in the UI is there is a set of radio buttons corresponding to different modes, like "blacken" and "mark path". When the player uses "mark path" on a cell, it gets a border and increments the superscript on it--just enough to let them know that their click had &lt;em&gt;some&lt;/em&gt; effect, but probably not too much about what it means.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editing Cells
&lt;/h2&gt;

&lt;p&gt;The final major UI change I had to make was to allow players to edit the contents of cells. Why? Because the BE keyword and the ? wildcard allow for it. I added a new mode, called "Edit". The player can freely edit whenever they want. I only allow changing characters to a single other character, but without much restriction on what they can change into. I hoped the freedom conferred by this mode didn't give them any information about why they'd want to do this or what the rules around it are.&lt;/p&gt;

&lt;p&gt;I had a lot of fun with this project. It was simple and I learned some things. And of course I enjoy writing things in Rust. Go buy and play &lt;a href="https://letibus.itch.io/lok" rel="noopener noreferrer"&gt;LOK&lt;/a&gt;! It's really good!!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>javascript</category>
    </item>
    <item>
      <title>netcrab: a networking tool</title>
      <dc:creator>knut</dc:creator>
      <pubDate>Sat, 14 Oct 2023 07:10:00 +0000</pubDate>
      <link>https://dev.to/knutaf/netcrab-a-networking-tool-503h</link>
      <guid>https://dev.to/knutaf/netcrab-a-networking-tool-503h</guid>
      <description>&lt;p&gt;(Cross-posted from &lt;a href="https://knutaf.com/blog/posts/20231014-netcrab-a-networking-tool/" rel="noopener noreferrer"&gt;my main blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Before I get to this project known as &lt;a href="https://github.com/knutaf/netcrab" rel="noopener noreferrer"&gt;netcrab&lt;/a&gt;, I thought it'd be fun to share some history from Xbox's past... call it the origin story of this tool. Let's go back in time a little bit. The year was 2012 and I had joined the Xbox console operating system team a year or so before. We'd wrapped up working on one of the last major updates for the Xbox 360 and were well underway with the next project, the thing that would eventually release as the Xbox One.&lt;/p&gt;

&lt;p&gt;I worked on the networking team, and the architecture of the Xbox One was wildly different than the 360. The Xbox One consisted of three virtual machines: a host, a shared partition (for the system UI, etc.), and an exclusive game partition. They all had to share a single network adapter, and so they had a whole lot of new code for this virtualized networking system to multiplex 3 VMs' worth of network traffic through a single adapter. To make matters even more fun, the host VM, the one that actually had access to the physical network adapters and ran their drivers, was (and still is) relatively lightweight. A lot of networking features are just not there, like, uh DHCP, IP fragmentation, and more.&lt;/p&gt;

&lt;p&gt;All of that to say, back then I was doing a lot of debugging of extremely simple networking things, like whether the box even gets an IP address, did it send a packet, did it receive a packet, why did the firewall reject this, and why did the firewall allow this? I had a need for a simple networking tool I could use as a TCP client, TCP server, UDP listener, or UDP sender.&lt;/p&gt;

&lt;p&gt;Well, such a tool already exists, of course, and has existed for a million years. It's called &lt;a href="https://nmap.org/ncat/" rel="noopener noreferrer"&gt;netcat&lt;/a&gt; and is well known to Unix people. There were two problems with it for me, though:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I didn't want to deal with license issues integrating it into tooling at work. I'm not sure what the license is, but Microsoft was much more wary about open source projects back then.&lt;/li&gt;
&lt;li&gt;That Xbox host VM I mentioned earlier does not run off-the-shelf programs. You have to recompile your program from source for it to work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I took matters into my own hands--because I love having tools--and wrote my own replacement called &lt;em&gt;netfeline&lt;/em&gt;. It got the job done for me at work, but it had just one problem: it is private to Microsoft, so I can't share it with anyone. And I'm not sure I'd want to; it's not my best work by a long shot. Now we arrive at April 2023 and I'm trying to get better at Rust and finally got the itch to rewrite netcat as an open source project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Early Choices
&lt;/h2&gt;

&lt;p&gt;Right from the beginning I knew I wanted to try using Rust's async/await functionality, but the current state of async programming in Rust is a bit weird. The language and compiler have support for certain keywords like &lt;code&gt;async&lt;/code&gt;, but there's no standard library that provides an async &lt;em&gt;runtime&lt;/em&gt;, which is needed to actually execute asynchronous tasks. The Rust async book has a good chapter on the &lt;a href="https://rust-lang.github.io/async-book/08_ecosystem/00_chapter.html" rel="noopener noreferrer"&gt;state of the ecosystem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So I started by using &lt;a href="https://tokio.rs/" rel="noopener noreferrer"&gt;Tokio&lt;/a&gt;, a popular async runtime. The docs and samples helped me get a simple outbound TCP connection working. The Rust async book also had a lot of good explanations, both practical and digging into the details of what a runtime does.&lt;/p&gt;

&lt;p&gt;When I work on projects, I like to add breadth first before depth. I want to stretch the code as broadly as it needs to go and see minimal functionality across all the features I want before I polish them. I find this helps me sort out all the structural questions I have. As I'll describe, I had to do a lot of stretching and restructuring throughout this project.&lt;/p&gt;

&lt;p&gt;To start with, I got a TCP client and server and UDP listener and sender all minimally working. This set up the code to handle four major, simple scenarios: TCP/UDP and listener/sender, all of which have slightly different ways to work with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrestling with user input
&lt;/h2&gt;

&lt;p&gt;The Tokio library I am using also provides wrappers for stdin and stdout. Unfortunately I found that this didn't work well for me. From &lt;a href="https://docs.rs/tokio/1.33/tokio/io/struct.Stdin.html" rel="noopener noreferrer"&gt;Tokio's stdin docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This handle is best used for non-interactive uses, such as when a file is piped into the application. For technical reasons, stdin is implemented by using an ordinary blocking read on a separate thread, and it is impossible to cancel that read. This can make shutdown of the runtime hang until the user presses enter.&lt;br&gt;
For interactive uses, it is recommended to spawn a thread dedicated to user input and use blocking IO directly in that thread.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, I wanted an interactive mode to work. You should be able to start a server on one end and a client on the other, and if you push a key on your keyboard, the other end should see it pop up. Tokio's stdin implementation had two problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;if the program was about to exit due to, say, the socket being closed by the peer, it wouldn't exit until you pressed a key. Unacceptable.&lt;/li&gt;
&lt;li&gt;if you pushed a key, it didn't get transmitted until you hit Enter. Boooo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To address first problem, I ended up having to take the docs' advice and put those blocking reads on my own thread. By "blocking read", I mean my thread calls a &lt;code&gt;read&lt;/code&gt; function that will sit there and wait (A.K.A. "block") until there is data available to be read (because the user pressed a key). The first problem exists because the Tokio runtime won't shut down until all the tasks it's waiting on complete, and one of them will be stuck with a blocking &lt;code&gt;read&lt;/code&gt; call until the user hits a key. But by putting it on a &lt;a href="https://doc.rust-lang.org/std/thread/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;std::thread&lt;/code&gt;&lt;/a&gt;, it's not managed by the Tokio runtime, and Rust is happy with tearing it down in the middle of a blocking call at process exit time.&lt;/p&gt;

&lt;p&gt;For the second problem, I found a useful crate called &lt;a href="https://crates.io/crates/console" rel="noopener noreferrer"&gt;console&lt;/a&gt;. This gives the ability to read one character at a time without the user needing to hit Enter. It has a &lt;a href="https://github.com/console-rs/console/issues/172" rel="noopener noreferrer"&gt;weird bug on Unix-type systems&lt;/a&gt; though, so it currently defaults to the &lt;code&gt;-i stdin-nochar&lt;/code&gt; input mode there.&lt;/p&gt;

&lt;h2&gt;
  
  
  All these arguments
&lt;/h2&gt;

&lt;p&gt;By this time I had already gotten tired of parsing arguments by myself and had looked for something to help with that. I found a really dang good &lt;a href="https://crates.io/crates/clap" rel="noopener noreferrer"&gt;argument parsing library called clap&lt;/a&gt;. What makes it so cool is it's largely declarative for common uses. You simply mark up a struct with attributes, and the parser automatically generates the usage and all the argument parsing code.&lt;/p&gt;

&lt;p&gt;Here's a snippet of one of the parts of netcrab's args as an example. This lets the user configure the random number generator for producing random byte streams sent to connected sockets. It exposes three arguments that the user could pass: &lt;code&gt;--rsizemin NUM&lt;/code&gt;, &lt;code&gt;--rsizemax NUM&lt;/code&gt;, and &lt;code&gt;--rvals binary&lt;/code&gt; or &lt;code&gt;--rvals ascii&lt;/code&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="nd"&gt;#[derive(Copy,&lt;/span&gt; &lt;span class="nd"&gt;Clone,&lt;/span&gt; &lt;span class="nd"&gt;PartialEq,&lt;/span&gt; &lt;span class="nd"&gt;Eq,&lt;/span&gt; &lt;span class="nd"&gt;PartialOrd,&lt;/span&gt; &lt;span class="nd"&gt;Ord,&lt;/span&gt; &lt;span class="nd"&gt;Debug,&lt;/span&gt; &lt;span class="nd"&gt;clap::ValueEnum)]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;RandValueType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/// Binary data&lt;/span&gt;
    &lt;span class="nd"&gt;#[value(name&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"binary"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;alias&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Binary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="cd"&gt;/// ASCII data&lt;/span&gt;
    &lt;span class="nd"&gt;#[value(name&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ascii"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;alias&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Ascii&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Args,&lt;/span&gt; &lt;span class="nd"&gt;Clone)]&lt;/span&gt;
&lt;span class="nd"&gt;#[group(required&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;multiple&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;RandConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/// Min size for random sends&lt;/span&gt;
    &lt;span class="nd"&gt;#[arg(long&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rsizemin"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;default_value_t&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;size_min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="cd"&gt;/// Max size for random sends&lt;/span&gt;
    &lt;span class="nd"&gt;#[arg(long&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rsizemax"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;default_value_t&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1450&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;size_max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="cd"&gt;/// Random value selection&lt;/span&gt;
    &lt;span class="nd"&gt;#[arg(long&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rvals"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;value_enum,&lt;/span&gt; &lt;span class="nd"&gt;default_value_t&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;RandValueType::Binary)]&lt;/span&gt;
    &lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RandValueType&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;Here are a few tips about clap, for me to remember and for you to maybe learn.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's not super straightforward what attributes are available to apply. If you have a &lt;code&gt;#[group(...)]&lt;/code&gt;, you can use attributes that match any of the getters in &lt;a href="https://docs.rs/clap/4.4/clap/struct.ArgGroup.html" rel="noopener noreferrer"&gt;&lt;code&gt;ArgGroup&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you have an &lt;code&gt;#[arg(...)]&lt;/code&gt; you can use ones from &lt;a href="https://docs.rs/clap/4.4/clap/struct.Arg.html" rel="noopener noreferrer"&gt;&lt;code&gt;Arg&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;#[command(...)]&lt;/code&gt; corresponds to &lt;a href="https://docs.rs/clap/4.4/clap/struct.Command.html" rel="noopener noreferrer"&gt;&lt;code&gt;Command&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you want to use an enum value in args, remember to add the attribute &lt;code&gt;#[derive(clap::ValueEnum)]&lt;/code&gt; or else you'll get cryptic compiler errors.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#[command(flatten)]&lt;/code&gt; can be applied to pull in all of a struct's fields into the usage while retaining the nested nature in the struct.&lt;/li&gt;
&lt;li&gt;If you want to add your own line to the usage for &lt;code&gt;-h&lt;/code&gt; you can add &lt;code&gt;#[command(disable_help_flag = true)]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you are using the "derive" parser like I did, but you want to execute one of the methods on &lt;code&gt;Command&lt;/code&gt;, you can call &lt;code&gt;YourArgs::command().whatever()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Customizing your sockets
&lt;/h2&gt;

&lt;p&gt;One of the useful things to do when testing a networking stack is customizing various &lt;a href="https://learn.microsoft.com/en-us/windows/win32/winsock/socket-options" rel="noopener noreferrer"&gt;socket options&lt;/a&gt;. If you click on the link you'll see that there are quite a lot of them. Tokio exposes a few to customize on the &lt;a href="https://docs.rs/tokio/1.33/tokio/net/struct.TcpSocket.html" rel="noopener noreferrer"&gt;&lt;code&gt;TcpSocket&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://docs.rs/tokio/1.33/tokio/net/struct.TcpStream.html" rel="noopener noreferrer"&gt;&lt;code&gt;TcpStream&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://docs.rs/tokio/1.33/tokio/net/struct.UdpSocket.html" rel="noopener noreferrer"&gt;&lt;code&gt;UdpSocket&lt;/code&gt;&lt;/a&gt; objects, but by no means all of them.&lt;/p&gt;

&lt;p&gt;On the other hand, the socket2 object, upon which Tokio's sockets are built, exposes quite a lot of them from many of the different option groups. Joining multicast groups, enabling broadcast, setting TTL, etc.. I didn't quite know which all I wanted to expose in command line args, but I wanted to set myself up for success by getting access to the &lt;code&gt;socket2::Socket&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, I didn't see a clear way to convert from a Tokio socket to that underlying socket2 socket. All I could see were &lt;code&gt;FromRawFd&lt;/code&gt; and &lt;code&gt;FromRawSocket&lt;/code&gt;, which could be joined up with the Tokio socket's &lt;code&gt;AsRawFd&lt;/code&gt;/&lt;code&gt;AsRawSocket&lt;/code&gt;. Well, I pushed forward with this and committed the following crime:&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;let&lt;/span&gt; &lt;span class="n"&gt;socket2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ManuallyDrop&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[cfg(windows)]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;socket2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_raw_socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="nf"&gt;.as_raw_socket&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="nd"&gt;#[cfg(unix)]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;socket2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_raw_fd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="nf"&gt;.as_raw_fd&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the point I take the raw FD/socket and create a &lt;code&gt;socket2::Socket&lt;/code&gt; on top of it, the &lt;code&gt;socket2::Socket&lt;/code&gt; takes ownership of the handle and will close it when it goes out of scope. That would be bad, because it would shut down my original Tokio socket. So I had to work around it with an object that Rust provides called &lt;a href="https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html" rel="noopener noreferrer"&gt;&lt;code&gt;ManuallyDrop&lt;/code&gt;&lt;/a&gt;, which inhibits running the object's destructor.&lt;/p&gt;

&lt;p&gt;This solution was ugly but worked, and I had to dip into &lt;code&gt;unsafe&lt;/code&gt; APIs for the first time in the project, which made me a bit sad. Is it impossible to write a program of any reasonable complexity in Rust without resorting to &lt;code&gt;unsafe&lt;/code&gt; calls?&lt;/p&gt;

&lt;p&gt;I tried to hint just now that there actually &lt;em&gt;is&lt;/em&gt; a clean way to do this. A recurring theme in this project is &lt;strong&gt;finding the right tool for the job&lt;/strong&gt;. The right tool in this case is &lt;a href="https://docs.rs/socket2/0.5/socket2/struct.SockRef.html" rel="noopener noreferrer"&gt;&lt;code&gt;socket2::SockRef&lt;/code&gt;&lt;/a&gt;, which lets you call all those socket options on a &lt;code&gt;AsFd&lt;/code&gt;/&lt;code&gt;AsSocket&lt;/code&gt; without taking ownership or even requiring a mutable reference. No more &lt;code&gt;unsafe&lt;/code&gt; calls either. It's exactly what I needed. I stumbled on it like five minutes ago as part of writing this blog.&lt;/p&gt;

&lt;p&gt;The moral of the story is: if you find yourself banging your head against the wall fighting the borrow checker or bringing in &lt;code&gt;unsafe&lt;/code&gt; code, look a little harder: most mature libraries have fixes for these rough edges already.&lt;/p&gt;

&lt;h2&gt;
  
  
  Group talk expansion
&lt;/h2&gt;

&lt;p&gt;At some point I thought I was nearly done: I had the main scenarios of inbound and outbound traffic working, and a bunch of extra scenarios like being an echo server, generating random data to send out, sending broadcast and multicast traffic...&lt;/p&gt;

&lt;p&gt;I was just about to call it "feature complete", but then I was browsing around that netcat web page and noticed an interesting feature: they called it &lt;a href="https://nmap.org/ncat/guide/ncat-broker.html" rel="noopener noreferrer"&gt;"broker mode"&lt;/a&gt;. It allows multiple clients to be connected at the same time and forwards traffic between all of them.&lt;/p&gt;

&lt;p&gt;Suddenly I had a vision of a problem I wanted to be able to solve. I do a lot of my work on a laptop in my home, VPN'd into Microsoft corpnet. I have an Xbox devkit and a test PC at home. Sometimes I hit a bug on a device at home and want to have someone at work take a look at it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.microsoft.com/store/detail/windbg-preview/9PGJGD53TN86?hl=en-us&amp;amp;gl=us" rel="noopener noreferrer"&gt;Windbg&lt;/a&gt; has a feature called debugger remotes. One end, which is actually attached to the thing being debugged, is called the "debugger server". It can open a port and allow a "debugger client" to connect to it and operate it remotely. In the context of my home/work setup, my home PC can connect to work resources but not the other way around (restrictive VPN firewall), so I thought it would be cool if netcrab could support working around that and allowing a debugger client at work to connect to a debugger server at home.&lt;/p&gt;

&lt;p&gt;What we need is a listener at work (since it can accept connections from home) that can accept connections from both home and the remote side of the debugger, and a connector at home that connects to both the local debugger session and the work listener. These two forwarders should be able to send traffic between the debugger session and the remote debugger.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than one connection
&lt;/h2&gt;

&lt;p&gt;There was a big hurdle to making this work: everything in the program up to this point was narrowly focused on only one remote connection. The program structure had no notion of more than one remote endpoint being connected. In order to expand the breadth of scenarios it can cover, I had to rewrite most of the guts of netcrab.&lt;/p&gt;

&lt;p&gt;To work on it in phases, I started off bringing up support for just having more than one incoming connection at a time, postponing the feature of traffic forwarding. The code would create a listening socket and call &lt;a href="https://docs.rs/tokio/1.33/tokio/net/struct.TcpListener.html#method.accept" rel="noopener noreferrer"&gt;&lt;code&gt;accept&lt;/code&gt;&lt;/a&gt; on it. With async programming, the &lt;code&gt;accept&lt;/code&gt; call is put in a separate task, and I wait until it completes.&lt;/p&gt;

&lt;p&gt;Before that rewrite, when the first &lt;code&gt;accept&lt;/code&gt; completes because of an incoming connection, that socket is handled until it closes, then the program either issues another &lt;code&gt;accept&lt;/code&gt; to handle another client or exits, depending on user preference. With only one TCP connection at a time to manage, I didn't have to think about having multiple tasks for handling different connections at the same time. Life was good.&lt;/p&gt;

&lt;p&gt;My first attempt to expand this went very poorly. I already had a function called &lt;code&gt;handle_tcp_stream&lt;/code&gt; that created an async "task" object called a "future" that drove the input and output of the socket to completion, so I figured all I needed to do was call &lt;code&gt;handle_tcp_stream&lt;/code&gt; on any new listening socket and stuff the future into a &lt;code&gt;Vec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I had the right idea but had not yet found &lt;em&gt;the right tool for the job&lt;/em&gt;. Putting these futures in a &lt;code&gt;Vec&lt;/code&gt; doesn't work because Rust doesn't let you both modify the list for adding to it and asynchronously modify it for removing completed futures from it. This requires mutably borrowing the &lt;code&gt;Vec&lt;/code&gt; twice, and that's disallowed by Rust. By the way, around this time I found this &lt;a href="https://limpet.net/mbrubeck/2019/02/07/rust-a-unique-perspective.html" rel="noopener noreferrer"&gt;good article about ways of thinking about mutability in Rust&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At some point I got the feeling I was barking up the wrong tree, and so with some searching I stumbled upon the &lt;strong&gt;right tool for the job&lt;/strong&gt;, &lt;a href="https://docs.rs/futures/0.3/futures/stream/struct.FuturesUnordered.html" rel="noopener noreferrer"&gt;&lt;code&gt;FuturesUnordered&lt;/code&gt;&lt;/a&gt;. Let's see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a set of futures that can complete in any order&lt;/li&gt;
&lt;li&gt;can add to it without a mutable borrow (wow)&lt;/li&gt;
&lt;li&gt;automatically removes a future from the list when one completes (wow)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suddenly my original idea was simple: every time I accept a new connection, I stuff it in a &lt;code&gt;FuturesUnordered&lt;/code&gt; collection of ongoing connections and just await the next one finishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything is sinks and streams
&lt;/h2&gt;

&lt;p&gt;Once I had multiple connections at the same time, the next step was to enable forwarding between them, and by the way I can't forget local input and output, which also should go to and from all connections.&lt;/p&gt;

&lt;p&gt;Before we get too deep into the router's guts, it's worth explaining about streams and sinks, which feature prominently in Rust async programming and consequently in netcrab. A &lt;a href="https://docs.rs/futures/0.3/futures/stream/trait.Stream.html" rel="noopener noreferrer"&gt;Stream&lt;/a&gt; is basically an asynchronous Iterator. While &lt;a href="https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next" rel="noopener noreferrer"&gt;&lt;code&gt;Iterator::next&lt;/code&gt;&lt;/a&gt; produces subsequent items synchronously, &lt;code&gt;Stream::next&lt;/code&gt; returns a future that asynchronously produces the next item. Just like an Iterator, there are many convenience methods to modify the data as it emerges from the Stream (e.g. &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://docs.rs/futures/0.3/futures/sink/trait.Sink.html" rel="noopener noreferrer"&gt;Sink&lt;/a&gt; is the opposite: an object that can receive values and asynchronously handle them. You call &lt;code&gt;Sink::send&lt;/code&gt; and await the transmission completing. The Sink is templated with the type of value it accepts. You can call &lt;code&gt;with&lt;/code&gt; to add an "adapter" before the sink in order to change the data type the Sink accepts or to intercept and process items before the Sink handles them.&lt;/p&gt;

&lt;p&gt;A common thing to do is send all the data from some stream to some other sink. That is done using the &lt;a href="https://docs.rs/futures/0.3/futures/sink/trait.SinkExt.html#method.send_all" rel="noopener noreferrer"&gt;&lt;code&gt;send_all&lt;/code&gt;&lt;/a&gt; method.&lt;/p&gt;

&lt;p&gt;Where, in Rust, do sinks and streams come from? Well, anything that implements the &lt;a href="https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html" rel="noopener noreferrer"&gt;&lt;code&gt;AsyncWrite&lt;/code&gt;&lt;/a&gt; trait can be turned into a &lt;code&gt;Sink&lt;/code&gt; by using the &lt;a href="https://docs.rs/tokio-util/0.7/tokio_util/codec/struct.FramedWrite.html" rel="noopener noreferrer"&gt;&lt;code&gt;FramedWrite&lt;/code&gt;&lt;/a&gt; helper. Likewise with &lt;a href="https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html" rel="noopener noreferrer"&gt;&lt;code&gt;AsyncRead&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.rs/tokio-util/0.7/tokio_util/codec/struct.FramedRead.html" rel="noopener noreferrer"&gt;&lt;code&gt;FramedRead&lt;/code&gt;&lt;/a&gt; producing a &lt;code&gt;Stream&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And where do you get an &lt;code&gt;AsyncWrite&lt;/code&gt; or &lt;code&gt;AsyncRead&lt;/code&gt; from? Well, Tokio provides them in many places. For example, you can call &lt;a href="https://docs.rs/tokio/1.33.0/tokio/net/struct.TcpStream.html#method.split" rel="noopener noreferrer"&gt;&lt;code&gt;TcpStream::split&lt;/code&gt;&lt;/a&gt;, and you get one for each direction: writing to or reading from the socket asynchronously.&lt;/p&gt;

&lt;p&gt;In practice, it looks a little like this:&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;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket_writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket_reader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tcp_socket&lt;/span&gt;&lt;span class="nf"&gt;.split&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;socket_sink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;FramedWrite&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket_sink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;BytesCodec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Call `freeze` to convert a BytesMut to a Bytes so it can be easily copied around.&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;socket_stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;FramedRead&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket_reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;BytesCodec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="nf"&gt;.freeze&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="n"&gt;router_sink&lt;/span&gt;&lt;span class="nf"&gt;.send_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;socket_stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to get a sink and stream is to use an &lt;a href="https://docs.rs/futures/0.3/futures/channel/mpsc/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;mpsc&lt;/code&gt; channel&lt;/a&gt;. You get a sink and stream, either with a fixed limit of data it can carry or unbounded that allocates from the heap as needed. MPSC stands for "Multiple-Producer, Single-Consumer", and so one of the coolest properties is that the sink part can be cloned and you can have many parts of your program all feeding data into the same channel. This was a tool I reached for &lt;em&gt;a lot&lt;/em&gt;. Maybe too much, but we'll get to that later.&lt;/p&gt;

&lt;p&gt;This isn't strictly about sinks and streams, but I want to talk about &lt;a href="https://docs.rs/bytes/1.5/bytes/struct.Bytes.html" rel="noopener noreferrer"&gt;&lt;code&gt;Bytes&lt;/code&gt;&lt;/a&gt; for a second, since it's such a simple and cool object. In its most common case, it's a reference-counted, heap-allocated buffer, so it's cheap to copy around. It has other fancy things like avoiding reference counting for static allocations, but in the context of netcrab, a &lt;code&gt;FramedRead&lt;/code&gt; with the &lt;code&gt;BytesCodec&lt;/code&gt; produces &lt;code&gt;BytesMut&lt;/code&gt; instances (which can be converted to a read-only &lt;code&gt;Bytes&lt;/code&gt; cheaply), so all of the channels use them to pass data around without incurring buffer copies everywhere.&lt;/p&gt;

&lt;p&gt;An aside: I am a big fan of writing technical blogs because the process of writing makes me think about things to change or improvements to make. Expounding about &lt;code&gt;BytesMut&lt;/code&gt; above helped me remember that I had several places where I made a temporary buffer, filled it, and then created a &lt;code&gt;Bytes&lt;/code&gt; from it, incurring a buffer copy.&lt;/p&gt;

&lt;p&gt;I made a &lt;a href="https://github.com/knutaf/netcrab/commit/1280741c7d1286bedde72e52059918befb48a522" rel="noopener noreferrer"&gt;change&lt;/a&gt; to instead fill a &lt;code&gt;BytesMut&lt;/code&gt; directly, then &lt;code&gt;freeze&lt;/code&gt; it, to remove that buffer copy. Unfortunately, profiling didn't show any change.&lt;/p&gt;

&lt;h2&gt;
  
  
  The router is also sinks and streams
&lt;/h2&gt;

&lt;p&gt;I started conceptualizing a "router". At its core it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a single &lt;code&gt;Stream&lt;/code&gt; of data from various sources (local I/O and multiple sockets)&lt;/li&gt;
&lt;li&gt;a piece of code that examines the source of each chunk of data and decides where to forward it&lt;/li&gt;
&lt;li&gt;a collection of &lt;code&gt;Sink&lt;/code&gt;s so that it could forward data wherever it should go&lt;/li&gt;
&lt;li&gt;a way for the rest of the program to tell the router that a new socket has just connected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used this blog post to finally get around to learning a tiny bit of &lt;a href="https://mermaid.js.org/intro/" rel="noopener noreferrer"&gt;Mermaid&lt;/a&gt; so I could make this chart. It's neat but does not give you enough control to make diagrams look just like you want.&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%2F80t5pbvxtd839maloac9.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%2F80t5pbvxtd839maloac9.png" alt="netcrab router architecture showing local IO, two sockets, and the router" width="800" height="644"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://mermaid.live/edit#pako:eNqtVUFLwzAU_islh6HgkOWYgQfxoKAIreDBeojJ2xaWJiN9VYb4302TbW23sjlsT3l53_ve-z7S5JsIK4EwMtP2Syy4w-TlLjeJ_8rqY-74apFkViwBExq3QyrsZOiAF5O3nKBdKsuYAWQMxYqxFLi853rWlFyUAX2Zk_cDHmWWvSyvTiHs03hwl8RPbQzoLUuxKgVjGRgJbqRxertGKEdznP6JZCcp0qQgQH2CO6XD86XRpr4RMls5AfL0JL4iN_3uT_rdp4O4Twdxnw7jPv2H-3Ro9x-t4Dp5uH5utUOpTBPqGlHL32t9VHAsClrOVovSVuir4uJom-BMkDC0L6lv3R41TpqeK0bbuRLpW82mzNy77cOerrXjyXh805gdE7uwlQxdtpWtrQBpDNkwty-yBrH5lQ8xdB9DdzPumJNRh6QbbgiiW1t1IdhIqP1opq-jkGhfcseS9CC5PSYx0ebp1EYLuqC2Lc1N3UNFD6loDxXdo_IYckUKcAVX0r9B33WNv4kWUEBOmF9KmPFKY05y8-OhvEKbrY0gDF0FV6RaSY5wp7g_lAVhM65LvwtSoXVP8V0Lz9vPL_nQLD0" rel="noopener noreferrer"&gt;mermaid&lt;/a&gt; / &lt;a href="https://gist.github.com/knutaf/d398232330aead7897e0aa0116ce3841" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The input type to the router is &lt;code&gt;SourcedBytes&lt;/code&gt;. What is that? It's something I added: a &lt;code&gt;Bytes&lt;/code&gt; plus a &lt;a href="https://doc.rust-lang.org/std/net/enum.SocketAddr.html" rel="noopener noreferrer"&gt;&lt;code&gt;SocketAddr&lt;/code&gt;&lt;/a&gt;. The reason I need that is the mpsc Sink can be cloned so multiple things can feed into it, but the Stream side of it doesn't indicate which one of the Sink clones inserted each element; I have to bundle that in myself.&lt;/p&gt;

&lt;p&gt;By the way, the diagram says &lt;code&gt;Sender&lt;/code&gt; and &lt;code&gt;Receiver&lt;/code&gt; for readability, but I actually used &lt;code&gt;UnboundedSender&lt;/code&gt; and &lt;code&gt;UnboundedReceiver&lt;/code&gt; because I was OK with spending more memory for higher throughput and to avoid handling errors with the fixed-size channels being full.&lt;/p&gt;

&lt;p&gt;Just like the router requires the remote address to accompany each data buffer, it also stores each socket sink in a map indexed by the remote address. The router can now implement some simple forwarding logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;examine the source address of a data buffer&lt;/li&gt;
&lt;li&gt;enumerate all the known socket sinks and send to each one that doesn't have the same remote address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. That's "hub" mode.&lt;/p&gt;
&lt;h2&gt;
  
  
  Revisiting windbg remotes with hub mode
&lt;/h2&gt;

&lt;p&gt;Equipped with hub mode, I tested out my idea. I'll show the spew just from a test all on localhost. This is the point of view of the work PC, not the home PC. I'll also add annotations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Listen on port 55001. Use forwarding mode "hub". Squash output
&amp;gt;nc -L *:55001 --fm hub -o none

// Successfully listening on that port.
Listening on [::]:55001, protocol TCP, family IPv6
Listening on 0.0.0.0:55001, protocol TCP, family IPv4

// Incoming connection from the "home" machine's netcrab instance, which is also connected to the debugger server and doing hub mode.
Accepted connection from 192.168.1.150:60667, protocol TCP, family IPv6

// Incoming connection from windbg debugging client, running on this same machine.
Accepted connection from 127.0.0.1:60668, protocol TCP, family IPv4

// Wait, what's this? Another?
Accepted connection from 127.0.0.1:60669, protocol TCP, family IPv4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I discovered is that windbg (and many other programs) make &lt;em&gt;multiple connections&lt;/em&gt;, for some application-specific reason. Whatever the reason, hub mode won't work for them, because traffic from one socket is forwarded to all other sockets. You end up with cross-talk that will surely confuse any application.&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%2Fsd7v1a1wxy8ouy6brysk.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%2Fsd7v1a1wxy8ouy6brysk.png" alt="diagram showing multiple sockets all connected to each other" width="800" height="356"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://mermaid.live/edit#pako:eNqdlLFOwzAQhl_F8sCUCOExQixUgoF2oEMlMIMTX5OoiR05NgVVfXfOcdMqbVQCmRzf959_n0-3o5mWQBMaxzFXtrQVJESBzYxIiYTU5TkYklUlKHvbgvnEv21pC1K4lNQo5apTcrWu9DYrhLHk5ZUrgl_r0tyIBlldw5Np3jl9xhWnHyF-wczSHJlZf-qyO26A--9ALnW2AXuHgrAid9dJdiLZgAQlvf9RR4sMVX058MqjRyyyiV568LqVM09HP1ttNqGKK1yNV9EzZ1V87N7uws-BnOB8QP65il79axUDNNHLv6o4bBpyH8cPw7ebhrERjE3LxkazncCj1nMIDmoyDWOXGJuWjY1nC-BAG24x7B5yc9Yklzo2UUcjWoOpRSlxJu18Hk5tAX5qJLiUsBauwm6OQug4dHx4F47l1LUwF1-rUtrC71vjwIf2XO0xv3BWL79VRhMfiKhrpLAwKwU2bE2Ttaha3AVZWm3mYTh2MzKijVBvWvfM_geFQqgG" rel="noopener noreferrer"&gt;mermaid&lt;/a&gt; / &lt;a href="https://gist.github.com/knutaf/725d83debe6b96e8e74c46ded3350a43" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Introducing "channels" mode
&lt;/h2&gt;

&lt;p&gt;What you really want is something more like a tunnel. Two sockets to remote machines are associated with each other as two ends of a tunnel (or "channel", as I called the feature). Traffic is forwarded between these two endpoints without any cross-talk with other sockets.&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%2Fzfj68imwi896bhg7uyty.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%2Fzfj68imwi896bhg7uyty.png" alt="diagram of multiple sockets each connected to just one other in an orderly fashion" width="800" height="306"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://mermaid.live/edit#pako:eNqdlD1vgzAQhv-K5RlUhRFVXRqpHZoMzRCpdQdjXwIK2MjYTaso_71nHIIIKE3r6fA9r-_l_HGgQkugKY3jmClb2BJSosAKwzMiIXPbLRgiygKUvWvAfOLXvrA5ETlXCsqGVKhnqpUztSn1HjPGkpdXpgiOxmVbw-uc5LqCJ1O_M_qMEaMfIT9i5tkWmXlXetXWHOB-nMiVFjuwMxSEiMyuk0lPJgMSlPT-Jx0tBaq6nuQumyyxFDd66cDrVi48nf3stdmFLq4xmu6iZy66-Nhu4MjPibzB-YD8cxe9-tcuBuhGL__q4vDQkPs4fhju3QSWjLGkx87KQA1-YUwlYyrpvA2kPdfbHWPJBIbr0YhWYCpeSLzWBy9j1Obg71yKoYQNdyWehSikzlfWpw-hCqOugQX_WhfS5n7eGgc-dWTqiOtzZ_XqWwma-kREXS25hXnBcbsrmm542eAsyMJqswjvS_vMRLTm6k3rjjn-ADUFauw" rel="noopener noreferrer"&gt;mermaid&lt;/a&gt; / &lt;a href="https://gist.github.com/knutaf/857e84213fcefe6f218cc5beecabf65d" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I already had the code to manage multiple sockets and decide which ones should be forwarded data. For hub mode, I had a broadcast-type policy implemented, and now I needed to add the necessary bookkeeping to use a different forwarding policy. A channel at its core is just a grouping of two remote endpoints, so I created this thing called a &lt;code&gt;ChannelMap&lt;/code&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;struct&lt;/span&gt; &lt;span class="n"&gt;ChannelMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="o"&gt;&amp;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;When a new socket showed up, the router would try to add it to the channel map by passing in the new &lt;code&gt;SocketAddr&lt;/code&gt;. The criteria for selecting the socket at the other end of a new channel are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the other socket must not be part of a channel aready, and&lt;/li&gt;
&lt;li&gt;the other socket must be from a different IP address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That second criterion is a bit weird, but without it you can create channels contained within this machine, which aren't useful.&lt;/p&gt;

&lt;p&gt;And of course, the router had to choose to consult the channel map instead of using the broadcast policy when in channel mode.&lt;/p&gt;

&lt;p&gt;To support the debugger client scenario where you need multiple outbound connections, I added a convenienece feature to create multiple outbound connections easily. The user can suffix "xNNN" to the port number, like &lt;code&gt;localhost:55000x13&lt;/code&gt; to create 13 outbound connections to the same host.&lt;/p&gt;

&lt;p&gt;Applications that connect to a channel socket are expecting them to be transparent, meaning if they disconnect, it should disconnect all the way to the "server" end of the socket, so a socket in a channel disconnecting needs to "forward" that disconnection onwards to the other end of the channel. To allow connecting to the channel again, I had to add the ability to automatically reconnect closed outbound connections: the new &lt;code&gt;-r&lt;/code&gt; argument, which is the analog of &lt;code&gt;-L&lt;/code&gt; (listen again after client disconnection).&lt;/p&gt;

&lt;p&gt;With these features, the channels scenario worked smoothly with windbg.&lt;/p&gt;

&lt;h2&gt;
  
  
  Socket address to route address
&lt;/h2&gt;

&lt;p&gt;The ability to create multiple outgoing connections actually threw a wrinkle into the router. Above I pasted code that used a &lt;code&gt;SocketAddr&lt;/code&gt; (the remote address) as a shorthand for a socket identifier, a way to figure out which socket produced an incoming piece of data. That doesn't work if you make multiple outgoing connections to the same remote host. See this spew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;nc localhost:55000x3
Targets:
3x localhost:55000
    [::1]:55000
    127.0.0.1:55000

Connected from [::1]:50844 to [::1]:55000, protocol TCP, family IPv6
Connected from [::1]:50845 to [::1]:55000, protocol TCP, family IPv6
Connected from [::1]:50846 to [::1]:55000, protocol TCP, family IPv6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I'm connecting three times to the same remote host. Notice that the &lt;em&gt;remote address&lt;/em&gt; is the same for all of them. If all I'm tracking on each socket is the remote address, how do I tell the difference between any packet originating from the remote address &lt;code&gt;[::1]:55000&lt;/code&gt;? Right, I can't. I need to store a tuple of the local &lt;em&gt;and&lt;/em&gt; remote addresses to uniquely identify a socket.&lt;/p&gt;

&lt;p&gt;Not a big deal. I created a new type and used it in any place a &lt;code&gt;SocketAddr&lt;/code&gt; was previously used to uniquely identify a socket.&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;struct&lt;/span&gt; &lt;span class="n"&gt;RouteAddr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SocketAddr&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;It did mean that now every piece of traffic flowing through the router had, umm, SIXTY FOUR BYTES extra attached to it!? Hold on, I'll be right back.&lt;/p&gt;

&lt;p&gt;...&lt;em&gt;four nights later&lt;/em&gt;...&lt;/p&gt;

&lt;p&gt;Whew, &lt;a href="https://github.com/knutaf/netcrab/commit/680e91765a0cace370e6b70f20d79f3e671d5943" rel="noopener noreferrer"&gt;I fixed that&lt;/a&gt;. I replaced it with a 2-byte route identifier. Though, as much as I was hoping it would show some improvement, especially when handling small packets, I wasn't able to measure any real difference. Either my laptop is too fast or it doesn't end up mattering.&lt;/p&gt;

&lt;p&gt;It did add some complexity, since now I have to maintain a mapping between these short route IDs and the real route address, but I like having an identifier that doesn't also double as the socket address, so I'm going to keep it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing mpsc channel per socket
&lt;/h2&gt;

&lt;p&gt;Going back to the diagram of the router from before, you might have noticed an overabundance of mpsc channels. I wasn't kidding when I said I used them a lot. They were a very convenient tool for creating sinks and streams without fighting Rust too much: every socket got one, the router got one, and local input got one.&lt;/p&gt;

&lt;p&gt;The router sends into each socket's channel, and the Stream side of it is sent to the socket using &lt;code&gt;send_all&lt;/code&gt;. That's a pipe that only exists to make Rust happy. Each socket exposes a sink, called &lt;a href="https://docs.rs/tokio/1.33/tokio/net/tcp/struct.WriteHalf.html" rel="noopener noreferrer"&gt;&lt;code&gt;tokio::net::tcp::WriteHalf&lt;/code&gt;&lt;/a&gt;. It feels like it should be possible just to bypass the middleman and have the router send directly to the &lt;code&gt;WriteHalf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So I tried that. And promptly got suplexed by the borrow checker and/or lifetime errors (can't remember exactly what error I hit). I was passing the &lt;code&gt;ReadHalf&lt;/code&gt; and &lt;code&gt;WriteHalf&lt;/code&gt; to two different futures, and both require mutably borrowing the &lt;code&gt;TcpStream&lt;/code&gt;. Just like when I tried to store futures in a &lt;code&gt;Vec&lt;/code&gt;, it was never gonna work.&lt;/p&gt;

&lt;p&gt;This was yet another case of not having the &lt;strong&gt;right tool for the job&lt;/strong&gt;, which turned out to be &lt;a href="https://docs.rs/tokio/1.33/tokio/net/struct.TcpStream.html#method.into_split" rel="noopener noreferrer"&gt;&lt;code&gt;TcpStream::into_split&lt;/code&gt;&lt;/a&gt;, which consumes the &lt;code&gt;TcpStream&lt;/code&gt; instance and gives you "owned" versions of the read and write half. These can be passed around freely, since the original &lt;code&gt;TcpStream&lt;/code&gt; object they came from has been consumed rather than borrowed. With that, I could remove a nice layer of queuing from the architecture.&lt;/p&gt;

&lt;p&gt;With the removal of a queue, it of course also reduced memory usage in cases where the socket was producing data at a faster rate than the router could consume it. The "unbounded" version of the mspc channel allocates extra storage in this case, and, true to its name, memory usage sometimes grew to over 1 GB. Big yikes. Anyway, here's the new diagram.&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%2Fbjlwdhiqk9z3vvnsi0xx.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%2Fbjlwdhiqk9z3vvnsi0xx.png" alt="netcrab router architecture showing the mpsc channels between the router and two sockets removed" width="800" height="555"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://mermaid.live/edit#pako:eNqtVF1rwjAU_SslD7KBMuxjhD2MPWywIbSDPdg9ZMm1BtukpLcTEf_7krS2VaswZp96P87JuSfk7gjXAggly0xv-IoZDD6eExXYr6y-U8OKVRBrvgYMwjrtSz4TowGWTxcJQb2WmlIFSCnygtL5RoGIgIkXli073F3pIfcJ-Tojk2p9merTSIRTLos4YbKZqFbrqPKi5JTGoASYUYazWFeGg3jaIpSjFGcXuSwiUcMmTIdNCG9nQng7E8Jbm_CmOcuC14d57zgUUnVh5jrcGCdHX5Vcg7wrLSwCDvLnGDjkm5vWy_rPrM0gusLLs0e22pdTq4n-KjjTqeTRwrFJlVpHbTjguHM1mEweO0PrQhv2iv6UA7KX8i2dQQ1z_-l2Hc2rOe8JT3vCVmPLHIyOSI7DhqB26zCdD5oRnB-dehfViG4tXKmF57XmHsmY5GByJoXdbjvXZt_VCnJICLW_ApasyjAhidrbVlahjreKE4qmgjGpCsEQniWzl58fkiAkavNeL0y_N_e_kQipWg" rel="noopener noreferrer"&gt;mermaid&lt;/a&gt; / &lt;a href="https://gist.github.com/knutaf/b467d1009e7d3763d85ce64919e847f9" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh, and making this change increased throughput about 2x.&lt;/p&gt;
&lt;h2&gt;
  
  
  Removing mpsc channel for local input
&lt;/h2&gt;

&lt;p&gt;While writing this blog, that mpsc channel for local input also kept bugging me. Surely there has to be a way to remove that one too, right? The reason it is a channel is to have a unified model for all input modes. Each input mode is represented by a stream: the "random data" input mode is an iterator that produces random bytes, wrapped in a stream. The "fixed data" input mode is an iterator that produces the same value, wrapped as a stream. Likewise, reading from stdin came from a stream. So the router code would just do a &lt;code&gt;send_all&lt;/code&gt; from the local input stream to its main sink and process local input just like any other socket.&lt;/p&gt;

&lt;p&gt;In other words, I've been saying "all local input modes are a stream", but really what I've done is construct a model where I have to &lt;em&gt;force&lt;/em&gt; all local input through streams. What if I can find a different commonality that fits more naturally without forcing?&lt;/p&gt;

&lt;p&gt;What if I said "all local input is a future that sends to the router sink and ends when the local input is done?" Before, I had the type &lt;code&gt;LocalIoStream&lt;/code&gt; that was defined like this:&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;type&lt;/span&gt; &lt;span class="n"&gt;LocalIoStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&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="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;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;In short, that's a stream that produces &lt;code&gt;Bytes&lt;/code&gt; objects. I tried to change it from a stream to a future, like this:&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="c1"&gt;// A future that represents work that drives the local input to completion. It is used with any `InputMode`, regardless&lt;/span&gt;
&lt;span class="c1"&gt;// of how input is obtained (stdin, random generation, etc.).&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;LocalInputDriver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;FusedFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&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="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="o"&gt;&amp;gt;&amp;gt;&amp;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 is a future (task that completes asynchronously) with no result at the end, just notification that it completed.&lt;/p&gt;

&lt;p&gt;It's slightly unfortunate that the type of &lt;code&gt;LocalInputDriver&lt;/code&gt; doesn't imply anything about its functionality, like the fact that it's supposed to send data to the router sink. But it's all in the name of performance, so I can live with it.&lt;/p&gt;

&lt;p&gt;In practice, creating a local input driver is usually the same as creating a local input stream, just with an added &lt;code&gt;router_sink.send_all(&amp;amp;mut stream)&lt;/code&gt; call at the end of the future.&lt;/p&gt;

&lt;p&gt;I mean, except for reading from actual stdin, which is done on a separate task with individual &lt;code&gt;router_sink.send()&lt;/code&gt; calls, and the future ends when stdin hits EOF.&lt;/p&gt;

&lt;p&gt;And with that, here's the final diagram of how netcrab is. Not a lot of cruft to cut out anymore.&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%2Fj3vat3ijmjbzsyl0j8fb.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%2Fj3vat3ijmjbzsyl0j8fb.png" alt="netcrab router architecture showing all mspc channels removed except the router's" width="800" height="403"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://mermaid.live/edit#pako:eNqtVE1rwzAM_SvGh7JBy2iOLuwwdthgo5AMdmh28GwlMU3s4CgrpfS_z3HSNG3TXdZcYklPT9Lzx44KI4EymuRmIzJukXw8x5q4r6q_U8vLjERGrAFJ0Lp9yHsitMCL-SqmaNbKMKYBGUNRMrbcaJAhcPnC8-SYd1f5lPuYfl2QKb2-TvVpFcI5l8s4Y3KesO22oSrKSjAWgZZgJzkuIlNbAfJpi1BNUlxc5XIZsR4XYT4uQnA7EYLbiRDcWoQ3I3hOXh-Wg3IolXZ1_H9A5kZ0FQlmzWKkQ0_1n_664qbG6_2GLgp2iG-UD_uyIQhQP0PE6ObkJlUiXDVsSqdOBWeOqNQoQGazx8GAfWx4YY6Y7qxeYoJzTNAz9dxkckJyanYE7byH_rzhA-1EB87WajOOl_GPWHAZ63aCTmkBtuBKujdl18Dcac6ggJgyt5SQ8DrHmMZ676C8RhNttaAMbQ1TWpeSIzwr7ravoCzheeW8IBUa-96-U_652v8CBDh-wQ" rel="noopener noreferrer"&gt;mermaid&lt;/a&gt; / &lt;a href="https://gist.github.com/knutaf/4d65ae287feb1d90cb846002241d38a2" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What about UDP?
&lt;/h2&gt;

&lt;p&gt;I may not have said it explicitly, but everything I talked about before with the router was actually in the context of TCP sockets. UDP works a little differently, enough so that I annoyingly couldn't reuse the &lt;code&gt;TcpRouter&lt;/code&gt; object and instead had to write &lt;em&gt;almost&lt;/em&gt; the same router functionality again.&lt;/p&gt;

&lt;p&gt;The main difference is that the &lt;code&gt;TcpStream&lt;/code&gt; object implicitly embeds the local and remote addresses, whereas a &lt;code&gt;UdpSocket&lt;/code&gt; only includes the local address. Well, you could constrain a UDP socket to have a single implicit destination by calling &lt;a href="https://docs.rs/tokio/1.33/tokio/net/struct.UdpSocket.html#method.connect" rel="noopener noreferrer"&gt;&lt;code&gt;connect&lt;/code&gt;&lt;/a&gt;, but it prevents you from receiving from other destinations, so that's no good.&lt;/p&gt;

&lt;p&gt;So whereas with TCP you have one separated stream for each remote endpoint and each one can end when a disconnect happens, with UDP you have a small set of locally-bound sockets that never end, and an association with a remote peer can be created at any time when the first packet arrives from it. Also, every UDP send needs to include the destination address.&lt;/p&gt;

&lt;p&gt;It's &lt;em&gt;just&lt;/em&gt; different enough that all the TCP router code can't be reused. I had to write analogous code for the UDP paths, heavily patterned off of the TCP router. Frustrating.&lt;/p&gt;
&lt;h2&gt;
  
  
  Listening and connecting
&lt;/h2&gt;

&lt;p&gt;When I first started this project, there were four major "modes":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;do_tcp_connect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;do_udp_connect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;do_tcp_listen&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;do_udp_listen&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, I had a clean separation between scenarios with outbound connections and ones with inbound connections. Once I had the router in place and a much better structure to the code, I could re-examine that. In an outbound connection scenario I basically resolve some hostnames, establish connections, and then throw the resultant streams into the router to manager. For inbound, I create some listening sockets and throw any inbound connection into the router.&lt;/p&gt;

&lt;p&gt;Why not have one function that does both inbound and outbound, depending on arguments? If the user asks to listen on some port (&lt;code&gt;-L&lt;/code&gt;), then start up the listening sockets. If the user passes hostnames to resolve, then do that. Feed everything into the router. With this change, I now have just &lt;code&gt;do_tcp&lt;/code&gt; and &lt;code&gt;do_udp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example of a place where this proxy-like feature could come in handy. Say you want to force a certain application's connection to go out of a certain network adapter. Let's say that adapter has the local address 192.168.1.150. You can do &lt;code&gt;netcrab --fm channels -s 192.168.1.150 -L *:55000 target-host:55000&lt;/code&gt;. That will set up a channel listening on port 55000 and forwarding to &lt;code&gt;target-host&lt;/code&gt; over port 55000 using the local adapter with address 192.168.1.150.&lt;/p&gt;
&lt;h2&gt;
  
  
  Iterators are too fast
&lt;/h2&gt;

&lt;p&gt;An interesting problem I ran into was that my input modes that involved iterators like rand and fixed produced data so quickly that they stalled all other processing. I don't quite know what policy caused this, but I found one workaround is inserting a &lt;a href="https://docs.rs/tokio/1.33/tokio/task/fn.yield_now.html" rel="noopener noreferrer"&gt;&lt;code&gt;yield_now&lt;/code&gt;&lt;/a&gt; call in every iterator step.&lt;/p&gt;
&lt;h2&gt;
  
  
  How big a &lt;code&gt;BytesMut&lt;/code&gt; do I want?
&lt;/h2&gt;

&lt;p&gt;I'll end with one last topic, which is kind of fun. Earlier I talked about using &lt;code&gt;BytesMut&lt;/code&gt; to create a buffer, fill it, then turn it immutable and pass it around. One place this is used is when reading from stdin. The program lets the user choose what size of data to accumulate from stdin before sending it to the network: the "chunk size" or "send size".&lt;/p&gt;

&lt;p&gt;In theory I could allocate a single &lt;code&gt;BytesMut&lt;/code&gt; with enough space for several chunks and freeze only the most recently filled chunk, then start writing into the next chunk. &lt;a href="https://docs.rs/bytes/1.5/bytes/struct.BytesMut.html#method.split" rel="noopener noreferrer"&gt;&lt;code&gt;BytesMut::split&lt;/code&gt;&lt;/a&gt; lets you do that. It costs CPU to allocate and free memory, so this would reduce the number of allocations. Here's basically what the scratch buffer then looks like.&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="c1"&gt;// Allocate 4 chunks of space at a time.&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;alloc_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;read_buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BytesMut&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alloc_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Manually set the length because we know we're going to fill it.&lt;/span&gt;
&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;read_buf&lt;/span&gt;&lt;span class="nf"&gt;.set_len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// ... Later, when sending a chunk:&lt;/span&gt;

&lt;span class="c1"&gt;// split() makes a Bytes with just the valid length but retains the remaining capacity in the BytesMut.&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;next_chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_buf&lt;/span&gt;&lt;span class="nf"&gt;.split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.freeze&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// If we've used up all the capacity, allocate a new one.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;read_buf&lt;/span&gt;&lt;span class="nf"&gt;.capacity&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="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;read_buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BytesMut&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alloc_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;read_buf&lt;/span&gt;&lt;span class="nf"&gt;.set_len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&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;I had this working fine. The memory usage looks big, but I was pushing hundreds of MB/sec read from disk, so that was expected.&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%2Fux8yp31nw72lvisq7aww.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%2Fux8yp31nw72lvisq7aww.png" alt="graph of memory usage, goes up to about 800 MB" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I noticed a function called &lt;a href="https://docs.rs/bytes/1.5/bytes/struct.BytesMut.html#method.reserve" rel="noopener noreferrer"&gt;&lt;code&gt;reserve&lt;/code&gt;&lt;/a&gt;. It has an interesting note that it will try to reclaim space from the buffer if it can find a region of the buffer that has no further &lt;code&gt;Bytes&lt;/code&gt; objects still alive referring to it.&lt;/p&gt;

&lt;p&gt;I thought this was pretty cool. Imagine not having to reallocate new chunks of memory, but instead automatically getting to reuse space you had previously allocated. So I swapped the &lt;code&gt;with_capacity&lt;/code&gt; calls above for &lt;code&gt;reserve&lt;/code&gt; to see if that trick ever kicked in.&lt;/p&gt;

&lt;p&gt;Well, uh, the graph ended up looking like this instead.&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%2F15mny4ictin0jcejz7jl.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%2F15mny4ictin0jcejz7jl.png" alt="graph of memory usage, goes up to about 1.4 GB" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So clearly my calling pattern with this buffer is such that I always have some overlapping use of it when it comes time to reserve more space, so it just grows and grows. And of course it didn't come with any perf benefit either, so I had to fall back to using &lt;code&gt;with_capacity&lt;/code&gt;, which was just fine.&lt;/p&gt;

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

&lt;p&gt;I wrote a lot. It kind of meandered. It was a tour through a bunch of the internals. It was written as much for me as for you (I fixed at least three things due to writing this). Am I a good Rust programmer now? Absolutely not. Did I learn something in the course of making netcrab? Absolutely yes. And I got a useful tool out of it, too.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>tokio</category>
      <category>networking</category>
      <category>performance</category>
    </item>
    <item>
      <title>A Cautionary Rust Tale About IO Redirection</title>
      <dc:creator>knut</dc:creator>
      <pubDate>Sun, 24 Sep 2023 05:43:38 +0000</pubDate>
      <link>https://dev.to/knutaf/a-cautionary-rust-tale-about-io-redirection-1i21</link>
      <guid>https://dev.to/knutaf/a-cautionary-rust-tale-about-io-redirection-1i21</guid>
      <description>&lt;p&gt;(Cross-posted from &lt;a href="https://knutaf.com/blog/posts/20230924-child-process-io-redirection-in-rust/" rel="noopener noreferrer"&gt;my main blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Yesterday I hit an absolutely baffling bug with some Rust code I'm working on. Illustrating it is a bit contrived, but it's a cautionary tale, and there's not much I like more than sharing cautionary tales. I'm going to illustrate the bug I hit in a toy executable instead of in the context of netcrab, which is unnecessarily complicated.&lt;/p&gt;

&lt;p&gt;So there I was, trying to add a feature to &lt;a href="https://github.com/knutaf/netcrab" rel="noopener noreferrer"&gt;netcrab&lt;/a&gt; to allow piping input and output to an executed command on the local machine. I'm getting input from a network socket and want to transfer that over to the child process's stdin; and the child process is producing stdout output that I want to send back over the network.&lt;/p&gt;

&lt;p&gt;So I start by creating the child process. In my case I was trying to use an interactive program like cmd.exe.&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;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cmd.exe"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.stdin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Stdio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;piped&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.stdout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Stdio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;piped&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.stderr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Stdio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;null&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to execute command"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set the input and output to be &lt;a href="https://doc.rust-lang.org/std/process/struct.Stdio.html#method.piped" rel="noopener noreferrer"&gt;&lt;code&gt;piped&lt;/code&gt;&lt;/a&gt; so I can get access to them. Next I spawn a thread to read from the child process's stdin and write it out somewhere--in the example it'll just go to the main program's stdout.&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="c1"&gt;// Extract the ChildStdout object and send it to be drained.&lt;/span&gt;
&lt;span class="nf"&gt;spawn_child_stdout_reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="py"&gt;.stdout&lt;/span&gt;&lt;span class="nf"&gt;.take&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;spawn_child_stdout_reader&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;child_stdout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ChildStdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;JoinHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="c1"&gt;// Create a new thread.&lt;/span&gt;
    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c1"&gt;// In the thread, loop forever.&lt;/span&gt;
        &lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="c1"&gt;// Keep trying to read from the child's stdout.&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;child_stdout&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// If we get a 0-byte read, the child's stdout ended.&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&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="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Child stdout exited!"&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="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_num_bytes_read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// If we got data, write it to the main stdout.&lt;/span&gt;
                    &lt;span class="nn"&gt;std&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="nf"&gt;stdout&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"err reading from child stdout: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&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="nf"&gt;.unwrap&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;I ran this, and the child stdout exited right after printing out the initial prompt. It didn't make sense why it would close stdout so early.&lt;/p&gt;

&lt;p&gt;At some point I decided to try adding in the stdin handling to see if it would help. Yes, I know this could be written much better with templating. I'll include that at the end.&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="nf"&gt;spawn_child_stdin_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="py"&gt;.stdin&lt;/span&gt;&lt;span class="nf"&gt;.take&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;spawn_child_stdin_writer&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;child_stdin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ChildStdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;JoinHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;std&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="nf"&gt;stdin&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&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="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"stdin exited!"&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="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_num_bytes_read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;child_stdin&lt;/span&gt;&lt;span class="nf"&gt;.write&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;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"err writing to child stdin: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&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="nf"&gt;.unwrap&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;And this actually helped. Why?? In my quest to figure it out, I started commenting out bits and pieces to narrow it down. But I was sleepy, so I didn't do a good job of it. I ended up at some point with this slight change.&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;fn&lt;/span&gt; &lt;span class="nf"&gt;spawn_child_stdin_writer&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;child_stdin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ChildStdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;JoinHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;std&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="nf"&gt;stdin&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&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="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"stdin exited!"&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="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_num_bytes_read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// TODO: re-enable&lt;/span&gt;
                    &lt;span class="c1"&gt;//child_stdin.write(&amp;amp;buf);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"err writing to child stdin: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&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="nf"&gt;.unwrap&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;And this version of the code I sort of forgot about and left in, becoming more and more confused about why my child stdout was exiting so fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reveal
&lt;/h2&gt;

&lt;p&gt;So what was happening here? Here's what's up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fundamentally&lt;/strong&gt;, if you close the child's stdin stream and the child process tries to read from it, then the child process will close the &lt;em&gt;stdout&lt;/em&gt; stream too. This should only happen with programs that actually try to read from stdin. Or maybe programs that gracefully accept stdin being closed without responding by also closing stdout. I admit I don't have 100% understanding of this. With interactive programs, you typically get both streams or neither, bucko.&lt;/p&gt;

&lt;p&gt;So how is the child stdin getting dropped? This is is where it gets contrived and painful.&lt;/p&gt;

&lt;p&gt;First, I started off only implementing the stdout reader portion, so when the child process object went out of scope, the stdin handle, not having been extracted using &lt;code&gt;take()&lt;/code&gt;, got dropped along with it. Oops.&lt;/p&gt;

&lt;p&gt;Next, when I added the stdin writer thread, it started working again, because something was now using the child stdin.&lt;/p&gt;

&lt;p&gt;Finally, in my blundering attempts to understand what was happening, when I commented out the &lt;code&gt;child_stdin.write(&amp;amp;buf);&lt;/code&gt; line in &lt;code&gt;spawn_child_stdin_writer&lt;/code&gt;, even though &lt;code&gt;child_stdin&lt;/code&gt; was moved into the function as an argument, because I didn't &lt;em&gt;reference&lt;/em&gt; it in the thread's closure, the &lt;code&gt;move ||&lt;/code&gt; syntax didn't move it, so it got dropped outside of the thread. OH COME ON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caution
&lt;/h2&gt;

&lt;p&gt;You know, when I write C++ code, I want to be real explicit about everything. For example, I insist on avoiding &lt;code&gt;auto&lt;/code&gt; at all times because I've seen how it makes code ambiguous. But in Rust, I trust the language a lot and happily let it type deduce and do all kinds of other things automatically because it seems to have its act together.&lt;/p&gt;

&lt;p&gt;But dang now Rust, I'm going to have to start being more caref--what? What's that? Huh? No, I didn't see any warning messag--yeah, of course I look at the warning messages. Fine, I'll go look.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;warning: unused variable: "child_stdin"
  --&amp;gt; src\main.rs:24:33
   |
24 | fn spawn_child_stdin_writer(mut child_stdin: ChildStdin) -&amp;gt; JoinHandle&amp;lt;()&amp;gt; {
   |                                 ^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: "_child_stdin"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shut up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Better reader/writer thread
&lt;/h2&gt;

&lt;p&gt;As promised, here's a cleaner way to express the concept of spinning up a thread that drains a source and sends it all to a sink. Not efficient, but illustrative. We could all use some illustrations in our lives.&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;fn&lt;/span&gt; &lt;span class="n"&gt;spawn_read_write_thread&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TSink&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;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TSource&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;sink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TSink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;JoinHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;TSource&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;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TSink&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;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&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="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Source closed!"&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="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sink&lt;/span&gt;&lt;span class="nf"&gt;.write&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;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Err reading from source: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&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="nf"&gt;.unwrap&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;



</description>
      <category>rust</category>
      <category>debugging</category>
    </item>
    <item>
      <title>A Framework Laptop Hacking Story</title>
      <dc:creator>knut</dc:creator>
      <pubDate>Thu, 22 Jun 2023 17:32:29 +0000</pubDate>
      <link>https://dev.to/knutaf/a-framework-laptop-hacking-story-39lf</link>
      <guid>https://dev.to/knutaf/a-framework-laptop-hacking-story-39lf</guid>
      <description>&lt;p&gt;(Cross-posted from &lt;a href="https://knutaf.com/blog/posts/20230622-a-framework-laptop-hacking-story/" rel="noopener noreferrer"&gt;my main blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I bought a &lt;a href="https://frame.work/" rel="noopener noreferrer"&gt;Framework Laptop&lt;/a&gt; a few months ago. I was really drawn to the idea of a laptop that I could customize and which was built with repairability in mind. That's a really great stance to take, and it didn't hurt that the laptop build and specs looked good, so I went for it. It was also cool that I could buy it with no memory, no hard drive, no power adapter, and no operating system, and supply those things separately. In general I was very happy with it, but it had one little behavior that bugged me. This is a long journey, but I hope it contains some useful information.&lt;/p&gt;

&lt;h2&gt;
  
  
  The keyboard problem
&lt;/h2&gt;

&lt;p&gt;I noticed sometimes when typing or editing files, keys wouldn't repeat properly when held down. I noticed it most when pressing and releasing combinations of keys in quick succession. Eventually I figured out a simple and concise repro. You can try this at home on your computer and compare the behavior:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Press and hold a key, say 'A'&lt;/li&gt;
&lt;li&gt;Press and hold another key, say 'B'. Now you have two keys held down.&lt;/li&gt;
&lt;li&gt;Release 'A' but keep 'B' held down&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Expected:&lt;/em&gt; since you still have the 'B' key held down, it should continue emitting repeated keystrokes.&lt;br&gt;
&lt;em&gt;Actual:&lt;/em&gt; no keystrokes are being emitted.&lt;/p&gt;

&lt;p&gt;I noticed this reproed not only in all programs in Windows, but even when navigating BIOS settings, which gave me a strong feeling that it was either a hardware or firmware issue.&lt;/p&gt;
&lt;h2&gt;
  
  
  Support response
&lt;/h2&gt;

&lt;p&gt;Framework support engaged readily with me on it and immediately replaced my &lt;a href="https://guides.frame.work/Guide/Input+Cover+Replacement+Guide/93?lang=en" rel="noopener noreferrer"&gt;Input Cover&lt;/a&gt; free of charge, no questions asked. I was already sort of skeptical about whether this would work, but I didn't think it was worth turning down this free troubleshooting.&lt;/p&gt;

&lt;p&gt;Sure enough, the problem kept happening with the new Input Cover. By this point I had read some of the source code and was pretty sure I had spotted the bug. In &lt;a href="https://github.com/FrameworkComputer/EmbeddedController/blob/72748b7d0aa4ac18204f00608ed61451b10314a3/common/keyboard_8042.c#LL433C6-L433C28" rel="noopener noreferrer"&gt;keyboard_8042.c, there's a function called &lt;code&gt;keyboard_state_changed&lt;/code&gt;&lt;/a&gt; that is called for each key when it changes state between being pressed or not pressed. It has the following code in it:&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_pressed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;keyboard_wakeup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;set_typematic_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scan_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;task_wake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TASK_ID_KEYPROTO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;clear_typematic_key&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;If a key is pressed, set it up as the new "typematic" (i.e. repeated) key. That sets up timers so that it is automatically sent at some configured interval to do the repeating. However, if it is released, clear the typematic configuration so that no key is being repeated, &lt;em&gt;irrespective of which key that was&lt;/em&gt;. In other words, releasing any key will stop any other key repeating.&lt;/p&gt;

&lt;p&gt;Bug or no bug, this isn't how basically every other computer I've used in my life works. I reported the code behavior to Framework support, who checked with their firmware engineer and unsurprisingly said "by design", because &lt;a href="https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/main/common/keyboard_8042.c#491" rel="noopener noreferrer"&gt;the same code is present in the upstream ChromeOS Embedded Controller repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So every ChromeOS based computer will have this behavior. That's a lot of devices. Is it a bug? At this point, who knows. It's definitely a behavior difference. Is it desirable? Some people are probably used to it by now, so there isn't a clear cut answer. I doubt this behavior will get changed at this point. So, what am I to do?&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework Hacking
&lt;/h2&gt;

&lt;p&gt;One of the coolest things about the Framework laptop is that because they published their embedded controller (EC) source code, it's possible to rebuild and reflash the firmware, which includes this keyboard code. Although Framework itself doesn't publish guides on this (more thoughts on this later), members of the community like DHowett have. He has &lt;a href="https://www.howett.net/tags/framework-ec/" rel="noopener noreferrer"&gt;a whole series of posts on how to rebuild the EC and flash it&lt;/a&gt;. In fact I haven't found any other comprehensive resources like that. It's really amazing stuff, and I'm thankful for it.&lt;/p&gt;

&lt;p&gt;I started working my way through the &lt;a href="https://www.howett.net/posts/2022-04-adding-an-ec-feature-1/" rel="noopener noreferrer"&gt;guide on modifying the EC firmware&lt;/a&gt;. It was going pretty smoothly. As an aside, I installed &lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install" rel="noopener noreferrer"&gt;WSL&lt;/a&gt; so that I had a reasonable dev environment, because I'm a scrub who does all his dev work on Windows. I'd never used WSL before, and I was amazed at how easy it was to follow Linux-centric instructions pretty much to the letter. You get a fully fledged Linux environment running in a Windows command prompt. It's actually magic.&lt;/p&gt;

&lt;p&gt;Anyway, with the success there, I was able to spit out "ec.bin", which I could flash to my device. At this point, I promptly chickened out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hacking with safety
&lt;/h2&gt;

&lt;p&gt;I am way too risk-averse to potentially brick my $2000+ laptop due to a coding bug or some issue with the way the firmware image I produce. I had to have a backup plan in case something went wrong. I took to the Framework forums to &lt;a href="https://community.frame.work/t/how-to-prepare-for-reflashing-firmware/31001" rel="noopener noreferrer"&gt;ask if others had tools or suggestions on a backup plan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It turns out I might be more risk averse than anyone else...? Haha... or everyone else already has the knowhow to fix a problem like this on their own if it arises. I have just dabbled in embedded programming, so that is probably the case.&lt;/p&gt;

&lt;p&gt;There are basically three potential backup routes that I could see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use the JECDB header to connect a SWD probe like a &lt;a href="https://github.com/raspberrypi/picoprobe" rel="noopener noreferrer"&gt;Picoprobe&lt;/a&gt; and debug the firmware&lt;/li&gt;
&lt;li&gt;use a UART debug adapter from the USB-C port&lt;/li&gt;
&lt;li&gt;connect a flash programmer directly to the flash chip that holds the EC firmware and write a backup to it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The JECDB header (labeled JSWDB) is not populated and is so tiny that it would require microsoldering. I don't have a hot air station or any experience here, so I wasn't too keen to try that, although it would involve some new toys. Who doesn't love new toys?&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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800020%26authkey%3D%2521ABoAs5c5vwcVNW0%26width%3D4608%26height%3D2080" 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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800020%26authkey%3D%2521ABoAs5c5vwcVNW0%26width%3D4608%26height%3D2080" alt="Upper-right corner of the top side of the Framework 12th gen mainboard, showing the JECDB header, labeled JSWDB" width="720" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The UART console would be cool and useful. Someone actually used to make a thing called a &lt;a href="https://www.sparkfun.com/products/retired/14746" rel="noopener noreferrer"&gt;SuzyQable&lt;/a&gt; that would do this. Actually DHowett made a limited run of similar functionality in a &lt;a href="https://www.howett.net/posts/2023-04-framework-ec-card/" rel="noopener noreferrer"&gt;Framework laptop expansion card&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But most people on the forum suggested getting a flash programmer and interfacing directly with the flash chip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Equipment for chip flashing
&lt;/h2&gt;

&lt;p&gt;I didn't realize this before, but many flash chips speak protocols that are well known by tools, so you can take an unpowered one, touch the pins with a connector to the programmer, and read, erase, and write contents. There is a very good write-up I referenced a lot about &lt;a href="https://wiki.mrchromebox.tech/Unbricking" rel="noopener noreferrer"&gt;unbricking a Chromebook&lt;/a&gt; (which this laptop sort of is, in this respect).&lt;/p&gt;

&lt;p&gt;From that guide, I learned some of the equipment I would need: the CH341a flash programmer itself, potentially a voltage adjuster, and a chip clip or other type of connection to the exposed pins on the mainboard. Not too surprisingly, it's possible to find &lt;a href="https://smile.amazon.com/gp/product/B07V2M5MVH/" rel="noopener noreferrer"&gt;a bundle with all of this flash programming equipment together&lt;/a&gt;. The bundle came with a &lt;a href="https://madpcb.com/glossary/soic" rel="noopener noreferrer"&gt;SOIC-8&lt;/a&gt; chip clip. I wasn't sure yet if that would be the right type.&lt;/p&gt;

&lt;p&gt;Here's a useful pic of the programmer, taken from that Unbricking page. I had to consult it a bunch of times to remember the orientation of the pins.&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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800018%26authkey%3D%2521AL1SaeBWo2wB8Js%26width%3D1000%26height%3D410" 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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800018%26authkey%3D%2521AL1SaeBWo2wB8Js%26width%3D1000%26height%3D410" alt="A CH341a usb flash programmer annotated showing pin order 4/3/2/1 starting at the top-left pin and 5/6/7/8 in the bottom-left pin, with the USB connector on the left side" width="1000" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the flash chip, part 1
&lt;/h2&gt;

&lt;p&gt;The unbricking guide of course didn't tell me where specifically to find the flash chip on my laptop's motherboard, so I had to go hunting. Its advice was to look for Winbond chips, and I found one on the top side.&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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800021%26authkey%3D%2521AHChWNO5j0luwyI%26width%3D4096%26height%3D1848" 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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800021%26authkey%3D%2521AHChWNO5j0luwyI%26width%3D4096%26height%3D1848" alt="Framework 12th gen mainboard close up photo of the battery receptacle and a Winbond 25R256JVEN chip" width="600" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one looks like a &lt;a href="https://www.winbond.com/hq/product/code-storage-flash-memory/serial-nor-flash/?__locale=en&amp;amp;partNo=W25R256JV" rel="noopener noreferrer"&gt;Winbond 25R256JVEN&lt;/a&gt;. The chip package is a &lt;a href="https://madpcb.com/glossary/wson/" rel="noopener noreferrer"&gt;WSON-8&lt;/a&gt; 8mm x 6mm. The SOIC-8 clip, while it had the right dimensions and spacing between pins, isn't physically compatible with the extremely flush mounting that WSON-8 has; the chip's leads don't stick out far enough for a chip clip to attach to it. I would need to buy a test probe instead.&lt;/p&gt;

&lt;p&gt;Another important point is the voltage level that the chip requires. From the Winbond web page I saw that Vcc is 2.7V - 3.6V, so it would accept 3.3V from the flash programmer and I wouldn't need to use the 1.8V voltage adjuster.&lt;/p&gt;

&lt;p&gt;I found a ton of test probes on AliExpress. Here's the one I ended up buying. Description is &lt;a href="https://www.aliexpress.us/item/3256805155067798.html" rel="noopener noreferrer"&gt;"2023 DFN8 QFN8 WSON8 Chip Probe Line Read Write Burning Test Adapter Socket 1.27 6x8 5x6 for CH341A TL866 RT809H/F Programmer"&lt;/a&gt;. I actually had ordered another one from eBay before that, and it came damaged with one pin bent, which made it effectively useless. You know, it's really frustrating that all this test probe does is hold 8 pins in a particular shape. It has no logic, nothing complicated at all about it. But I don't have any other way of holding 8 wires touching the chip's pins all at the same time, so I'm beholden to this. Curse these inadequate human hands! And it's doubly frustrating because the only place I can get one of these is overseas, so I had to wait 2-3 weeks for it to arrive.&lt;/p&gt;

&lt;p&gt;When it finally did arrive, I was able to use a program called &lt;a href="https://github.com/nofeletru/UsbAsp-flash" rel="noopener noreferrer"&gt;AsProgrammer&lt;/a&gt; to use the CH341a flash programmer to read the contents of the flash chip. Here are some things I noticed about using this tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if you don't have the test probe firmly touching the flash chip's leads, it won't detect the flash chip type correctly&lt;/li&gt;
&lt;li&gt;however, once you start the "Read IC" operation to dump the contents, if your test probe doesn't have firm contact, it will silently read zeroes&lt;/li&gt;
&lt;/ul&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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800012%26authkey%3D%2521ACBdbmOpjd55VdU%26width%3D1285%26height%3D864" 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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800012%26authkey%3D%2521ACBdbmOpjd55VdU%26width%3D1285%26height%3D864" alt="Screenshot of the AsProgrammer tool, showing a hex dump mostly filled with 0xFF and the IC identified as W25Q80DV" width="1285" height="864"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore after dumping the flash it's important to execute a "Verify IC" command, which compares the dumped buffer with the flash contents. If it fails, it either means you moved the test probe during the "Read IC" or the "Verify IC" operation. Likewise, whenever you write to the flash chip, you need to do a "Verify IC" command after to make sure that you didn't lose contact during the writing process. Even with a pretty steady hand, I messed up the read operation a couple times. Honestly, it's terrible, and I would really rather have a better way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top-side 32 MB flash chip
&lt;/h2&gt;

&lt;p&gt;Anyway, on to the chip contents. Firstly, I'm sharing &lt;a href="https://bit.ly/framework_laptop_12th_gen_intel_files" rel="noopener noreferrer"&gt;all the things I dumped from the mainboard, plus close up pictures&lt;/a&gt; online. I encountered a real dearth of pictures of the under-side of the mainboard, so I took a bunch of close-up pics so people can look at what ICs are found there. They're not the best quality but it's better than nothing.&lt;/p&gt;

&lt;p&gt;I wasn't quite able to figure out what this flash was. It was &lt;em&gt;much&lt;/em&gt; bigger than the 524,288 bytes that the EC firmware image normally is. I was able to find a copy of the EC firmware image in it at offset 0x1000, but not sure why. I uploaded my backup of this chip under the name "top-side_near-cmos-battery_Winbond_25R256JVEN.bin", but I didn't feel this is the important backup to make.&lt;/p&gt;

&lt;p&gt;So if this isn't the EC firmware flash, where is that? I peeked under all the components on the top side of the board that I could (fan, etc.) but couldn't find any other flash chips. I tried looking at the &lt;a href="https://github.com/FrameworkComputer/Framework-Laptop-13/tree/main/Mainboard" rel="noopener noreferrer"&gt;schematic&lt;/a&gt;, and while it mentions two flash ROMs, it doesn't mention where to find them. Reluctantly, I took out the mainboard and checked the under-side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Under-side flash chips
&lt;/h2&gt;

&lt;p&gt;I found &lt;em&gt;three&lt;/em&gt; identical &lt;a href="https://www.winbond.com/hq/product/code-storage-flash-memory/serial-nor-flash/?__locale=en&amp;amp;partNo=W25Q80DV" rel="noopener noreferrer"&gt;Winbond 25Q80DVIG&lt;/a&gt; chips on the under side. One of them was quite close to the MEC1521 embedded controller, so I took a guess that this one contains the EC firmware.&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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800022%26authkey%3D%2521AEMLXtTBvbNg9Tk%26width%3D4608%26height%3D2080" 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%2Fonedrive.live.com%2Fembed%3Fresid%3D34E0B07D960510FA%2521800022%26authkey%3D%2521AEMLXtTBvbNg9Tk%26width%3D4608%26height%3D2080" alt="Close up photo of an MEC1521 chip and a Winbond W25Q80DVIG chip on the under side of a Framework laptop 12th gen Intel motherboard" width="600" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These ones are WSON-8 6mm x 5mm package, different than 6mm x 8mm that the upper-side chip was. It's a good thing I had both size test probes. This one also accepts 3.3V. I pulled the contents of this one off under the name "under-side_bottom-left_Winbond_25Q80DVIG.bin". Finally, when I compared its contents to the dump I got from ECTool, it was an exact match.&lt;/p&gt;

&lt;p&gt;I also dumped and uploaded the other two flash chips on the under side, though I couldn't quite tell what they're for. Just from looking at strings, they seem to be firmware for other components, but I couldn't quite tell what. I also couldn't find these flash parts in the schematic, so I'm not sure what's up with that.&lt;/p&gt;

&lt;p&gt;At this point I felt like I had a route to restoring a backup if I had to, so I felt ready to proceed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detour - other problems
&lt;/h2&gt;

&lt;p&gt;At this point I reassembled the laptop and... it didn't turn on and wouldn't charge. I saw forum posts relating to the 11th gen Intel mainboard having some issue, but since I have a 12th gen Intel laptop I didn't think it would apply. I tried stuff like trickle charging with a non-PD USB-C adapter, but it didn't help.&lt;/p&gt;

&lt;p&gt;I saw a suggestion to try popping out the CMOS battery and popping it back in. I uh, tried to do that, but managed to snap the receptacle. Within a day, Framework shipped out a replacement mainboard free of charge and shipping. I was blown away by the quality of customer service.&lt;/p&gt;

&lt;h2&gt;
  
  
  A good baseline
&lt;/h2&gt;

&lt;p&gt;When I was getting ready to flash again, I noticed an &lt;a href="https://github.com/FrameworkComputer/EmbeddedController/issues/21" rel="noopener noreferrer"&gt;issue about the compiler version used to build the firmware binary&lt;/a&gt;. I followed the advice, but more importantly I noticed that the issue has been recently &lt;em&gt;fixed&lt;/em&gt;, and in the resolution, the maintainer says "Next release (hx20 3.19, hx30 3.07) will include them". It reminded me of something crucial: the Framework EC firmware source code repo doesn't have any particular indication of its level of stability at any given commit. Which commits could be considered fully tested releases? What if the head of the branch introduces a bug that they're working on fixing?&lt;/p&gt;

&lt;p&gt;When I build my fix, I want to apply it as a delta on top of something I know is fully tested, or at good enough for them to release. As part of my spelunking through firmware images earlier, I pulled all the strings out of the different firmware images to get clues about what they were. The very first string in the EC firmware image is "hx30_v0.0.1-&lt;strong&gt;7a61a89&lt;/strong&gt;". That looks suspiciously like a commit hash. Can I look it up in the Framework EC repo? Hey, look, it sure is a &lt;a href="https://github.com/FrameworkComputer/EmbeddedController/commit/7a61a89ae7930b16ded9318e2da878a617484427" rel="noopener noreferrer"&gt;valid commit&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;With this I could &lt;code&gt;git checkout 7a61a89&lt;/code&gt; and then create my topic branch with my fix from here. This version was clearly good enough for them to ship in-box, and that's a pretty good quality bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flashing with ECTool
&lt;/h2&gt;

&lt;p&gt;Finally with the new mainboard and the laptop operational again, I was ready to use &lt;a href="https://www.howett.net/posts/2022-03-announcing-ectool-efi/" rel="noopener noreferrer"&gt;ECTool&lt;/a&gt; to flash my bug fixed firmware. It actually flashed correctly without a hitch. All the stuff I did before was to prepare for the worst, but it didn't happen.&lt;/p&gt;

&lt;p&gt;Well, it wasn't &lt;em&gt;completely&lt;/em&gt; without an issue. I spent so much time worrying about the flashing procedure that I was surprised to find my bug fix not only did not fix the bug, it almost made it impossible to reflash without using my backup. What I found is that whenever I pressed a key (not held it down), it would have a slight delay and then start repeating and not stop repeating. Ittt madeeee itttt harddd tooo typeeee stufffff.....&lt;/p&gt;

&lt;p&gt;No problem, let me just use ECTool to reflash the backup. Uh oh, the first thing ECTool does is say "press any key to abort". Due to my keys repeating, it kept aborting! Finally after some panicking, I figured out I could press Shift after hitting Enter, and it wouldn't count for its "press any key" logic. With that, I was able to reflash a backup. As a side note, I have a fork of ECTool that adds &lt;a href="https://github.com/knutaf/FrameworkHacksPkg/commit/d3bea0ee530b3c93a62eaaad4721576b05fb67c0" rel="noopener noreferrer"&gt;an option to avoid the "press any key to abort" behavior&lt;/a&gt;. I'll see about getting this feature into ECTool proper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging the keyboard fix
&lt;/h2&gt;

&lt;p&gt;Now that I've managed to dig myself out of the hole my bug created, I need to debug and figure out why my fix didn't work. Let's dig into the code. Here is the entirety of the fix:&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_pressed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;keyboard_wakeup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;set_typematic_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scan_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;task_wake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TASK_ID_KEYPROTO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// FIX STARTS HERE&lt;/span&gt;
    &lt;span class="c1"&gt;// Only clear typematic key if that is the key being released. This fixes&lt;/span&gt;
    &lt;span class="c1"&gt;// an issue where if keys A and then B are both held down at the same time,&lt;/span&gt;
    &lt;span class="c1"&gt;// and the user releases A, B will also stop repeating.&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;len&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;typematic_len&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="n"&gt;memcmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scan_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typematic_scan_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;clear_typematic_key&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// FIX ENDS HERE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a key is pressed, &lt;code&gt;set_typematic_key&lt;/code&gt; is called, which sets global variables to store the scancode of the key that should be repeated. My thinking was when the key is released, I should be able to compare the scancode that's being released and only clear the typematic scancode if it's the one being repeated.&lt;/p&gt;

&lt;p&gt;From the observed behavior with this buggy change, &lt;code&gt;clear_typematic_key&lt;/code&gt; is somehow not being called on key release. Here were the first level causes I could think of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the function containing this code isn't being called at all&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;len&lt;/code&gt; doesn't match&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scan_code&lt;/code&gt; doesn't match&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I was able to rule out #1 pretty quickly by looking through the code that calls this. Also I was pretty sure this code was being called before when a key is pressed and then again when released.&lt;/p&gt;

&lt;p&gt;Options 2 and 3 are interesting. I don't know what &lt;code&gt;scan_code&lt;/code&gt; really is. Is it identical when a key is pressed versus released? I need to look at the code that creates the scancode value:&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="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matrix_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_pressed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scancode_set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scan_code&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;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I notice right away that &lt;code&gt;is_pressed&lt;/code&gt; is a parameter. It's possible that the resultant scancode embeds a pressed/released bit inside. Let's look deeper.&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;static&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ec_error_list&lt;/span&gt;
&lt;span class="nf"&gt;matrix_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;int8_t&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;int8_t&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;int8_t&lt;/span&gt; &lt;span class="n"&gt;pressed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;scancode_set_list&lt;/span&gt; &lt;span class="n"&gt;code_set&lt;/span&gt;&lt;span class="p"&gt;,&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;scan_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;make_code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;scancode_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;make_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pressed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code_set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scan_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still plumbing through &lt;code&gt;pressed&lt;/code&gt;...&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;static&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;scancode_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;make_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;int8_t&lt;/span&gt; &lt;span class="n"&gt;pressed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;scancode_set_list&lt;/span&gt; &lt;span class="n"&gt;code_set&lt;/span&gt;&lt;span class="p"&gt;,&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;scan_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&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;pressed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;scan_code&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;len&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;scan_code&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;len&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="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0xf0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;scan_code&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;len&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aha, my suspicion was correct! The scancode for a key being pressed is different than a key being released, so my attempted fix will never work.&lt;/p&gt;

&lt;h2&gt;
  
  
  A working fix
&lt;/h2&gt;

&lt;p&gt;Instead of comparing the scancode directly (which won't work, because it's different when releasing a key), I can use the &lt;code&gt;make_code&lt;/code&gt; value, which seems to more directly indicate the key without incorporating the pressed/released state.&lt;/p&gt;

&lt;p&gt;Here's a link to a &lt;a href="https://github.com/knutaf/FrameworkEmbeddedController/commit/61a3707a3543bda91fa9d39cf1af6958506a8c62" rel="noopener noreferrer"&gt;working fix&lt;/a&gt;. I also threw in a defensive measure for testing, to clear the typematic settings after the key has been repeating for N seconds straight. I built it, flashed it with ECTool, and my laptop keyboard behavior is now perfect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback for Framework Computers
&lt;/h2&gt;

&lt;p&gt;This process was a real journey. My laptop was out of commission for like three months off and on while waiting for equipment or replacement parts. Framework did an amazing job with their customer service, but there are still things I'd like to see them do to make life easier for customers like me who want to customize it in the future.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Publish an official &lt;a href="https://guides.frame.work/c/Framework_Laptop" rel="noopener noreferrer"&gt;guide&lt;/a&gt; to safe EC firmware flashing. This probably requires also doing some of the other things in this list.&lt;/li&gt;
&lt;li&gt;Populate the JECDB/JSWDB header on the mainboard out of the box, so that if we brick a laptop we can more easily debug and fix it.&lt;/li&gt;
&lt;li&gt;Productize and sell a UART debugger expansion port card, like the &lt;a href="https://www.howett.net/posts/2023-04-framework-ec-card/" rel="noopener noreferrer"&gt;one DHowett made&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Publish official pictures of the mainboard for reference.&lt;/li&gt;
&lt;li&gt;Update the schematics to include all the components on the mainboard, for example all the 4 flash chips I found.&lt;/li&gt;
&lt;li&gt;Add tags or branches for releases in the EC firmware repo, so we can know which commits are good places to make deltas on top of.&lt;/li&gt;
&lt;li&gt;Allow certain kinds of modifications under warranty. I acknowledge this is a bit of a stretch though.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm hoping this blog post contains some useful information in this niche space, and that the &lt;a href="https://bit.ly/framework_laptop_12th_gen_intel_files" rel="noopener noreferrer"&gt;flash chip dumps and mainboard pics&lt;/a&gt; I uploaded may help others who don't feel like taking apart their laptops.&lt;/p&gt;

</description>
      <category>embedded</category>
      <category>debugging</category>
      <category>firmware</category>
    </item>
    <item>
      <title>Java and Console Character Encodings</title>
      <dc:creator>knut</dc:creator>
      <pubDate>Sun, 26 Mar 2023 07:48:12 +0000</pubDate>
      <link>https://dev.to/knutaf/java-and-console-character-sets-3dpl</link>
      <guid>https://dev.to/knutaf/java-and-console-character-sets-3dpl</guid>
      <description>&lt;p&gt;(Cross-posted from &lt;a href="https://knutaf.com/blog/posts/20230326-java-and-console-character-encodings/" rel="noopener noreferrer"&gt;my main blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;So I got nerd sniped by my buddy Snoopy the other daaaaay...&lt;/p&gt;

&lt;p&gt;He's studying CS in Europe and is writing a program for an assignment where he has to input some characters from the command line on Windows and process them. The relevant part of the program is pretty simple. It's 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;Scanner sc = new Scanner(System.in);
String input = sc.next();
for (int i = 0; i &amp;lt; input.length(); i++) {
    System.out.print(String.format("%02x", (int)input.charAt(i)));
    System.out.println();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So he runs it and enters a non-ANSI character: š (that's &lt;a href="https://codepoints.net/U+0161?lang=en" rel="noopener noreferrer"&gt;U+0161&lt;/a&gt;). The output he gave me is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;java PrintBytes
š
00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that's weird. I am pretty sure this is not a null character. I expected to see either a Unicode or UTF-8 representation of this. This was about the time I felt the uncontrollable urge to get involved.&lt;/p&gt;

&lt;h1&gt;
  
  
  Default Codepage Issues
&lt;/h1&gt;

&lt;p&gt;I downloaded the JDK and tried it on my machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;java PrintBytes
š
73
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, that's weird. Oh, my system codepage is set to Windows, whereas his was set to UTF-8. I used &lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/chcp" rel="noopener noreferrer"&gt;&lt;code&gt;chcp&lt;/code&gt;&lt;/a&gt; to change it to 65001, which is UTF-8, and got the same odd zero result.&lt;/p&gt;

&lt;h1&gt;
  
  
  Redirected input from a file
&lt;/h1&gt;

&lt;p&gt;Next test: what if I read the same input from a file instead?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;java PrintBytes &amp;lt; input.txt
c5
a1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hey, that's correct. That's the UTF-8 representation of it. So something is weird with how Java is reading from an interactive command line compared to file input, even when both come through stdin.&lt;/p&gt;

&lt;h1&gt;
  
  
  How does Rust do it?
&lt;/h1&gt;

&lt;p&gt;Next test, let's see how it does in Rust.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use std::io::Read;
fn main() {
    for b in std::io::stdin().bytes() {
        let val = b.unwrap();
        match val {
            0xd =&amp;gt; println!(""),
            0xa =&amp;gt; (),
            _ =&amp;gt; println!("{:#02x}", val),
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is good:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;target\debug\printbytes.exe
š
0xc5
0xa1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So Rust is doing it right interactively. The Rust code actually checks if stdin is currently a console handle and calls &lt;code&gt;ReadConsoleW&lt;/code&gt;, otherwise calling &lt;code&gt;ReadFile&lt;/code&gt;, which handles regular file I/O just fine.&lt;/p&gt;

&lt;p&gt;Snoopy also tried writing the equivalent program in Python, and it also did it right. So Java seems to be doing something wrong under certain conditions... but what's the reason?&lt;/p&gt;

&lt;h1&gt;
  
  
  Finding the answer
&lt;/h1&gt;

&lt;p&gt;A good starting point might be to check the &lt;a href="https://github.com/rust-lang/rust/blob/master/library/std/src/sys/windows/stdio.rs#L358" rel="noopener noreferrer"&gt;Rust source&lt;/a&gt;. My first guess was that somewhere I'd see a call to &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile" rel="noopener noreferrer"&gt;&lt;code&gt;ReadFile&lt;/code&gt;&lt;/a&gt; on the stdin handle, but instead I see the lowest level Windows call it makes is to a function I'm not familiar with, &lt;a href="https://learn.microsoft.com/en-us/windows/console/readconsole" rel="noopener noreferrer"&gt;&lt;code&gt;ReadConsoleW&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Reading the docs, it references something about ANSI compatibility:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;ReadConsole&lt;/code&gt; reads keyboard input from a console's input buffer. It behaves like the &lt;code&gt;ReadFile&lt;/code&gt; function, except that it can read in either Unicode (wide-character) or ANSI mode.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I found another link that gives &lt;a href="https://learn.microsoft.com/en-us/windows/console/high-level-console-input-and-output-functions" rel="noopener noreferrer"&gt;a good comparison between &lt;code&gt;ReadFile&lt;/code&gt; and &lt;code&gt;ReadConsole&lt;/code&gt;&lt;/a&gt;. It confirms that &lt;code&gt;ReadConsoleA&lt;/code&gt; (the ANSI version) only reads ANSI characters, but &lt;code&gt;ReadConsoleW&lt;/code&gt; can read Unicode characters. Rust is reading Unicode characters (hopefully UTF-16 but I'm not really sure), then translating them internally into UTF-8, since its string type is natively UTF-8.&lt;/p&gt;

&lt;h1&gt;
  
  
  Confirming with C++
&lt;/h1&gt;

&lt;p&gt;Easiest way to confirm was write a little C++ program, going straight to the source. In different modes it can try &lt;code&gt;ReadFile&lt;/code&gt; or &lt;code&gt;ReadConsoleW&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint16_t c;
if (argc == 1) {
    ReadFile(GetStdHandle(STD_INPUT_HANDLE), reinterpret_cast&amp;lt;uint8_t*&amp;gt;(&amp;amp;c), 1, nullptr, nullptr);
} else {
    DWORD numRead;
    ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), &amp;amp;c, 1, &amp;amp;numRead, nullptr);
}

printf("%04x\n", c);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First here's &lt;code&gt;ReadFile&lt;/code&gt; mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;printbytes_c.exe
š
0000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then &lt;code&gt;ReadConsoleW&lt;/code&gt; mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;printbytes_c.exe -c
š
0161
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;U+0161 is the UTF-16 encoding of the character, so that seems to be showing some Unicode support. Interesting to note that &lt;code&gt;ReadConsoleA&lt;/code&gt; showed the same behavior as &lt;code&gt;ReadFile&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The behavior is a little unfortunate in Windows, but it seems to be fairly well documented. Most languages seem to be doing a proper job of handling this, but Java isn't. We can even see it in the debugger. I don't have proper symbols, but at least the top of the stack seems to resolve pretty clearly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0:004&amp;gt; k
 # Child-SP          RetAddr               Call Site
00 00000016`03ffce28 00007fff`7157c7f4     KERNEL32!ReadFile
01 00000016`03ffce30 00007fff`7157bd76     java!handleRead+0x20
02 00000016`03ffce70 00007fff`71572641     java!JNI_OnLoad+0x196
03 00000016`03ffef00 00000171`9146a02e     java!Java_java_io_FileInputStream_readBytes+0x1d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So Java... do better. Have a way to properly handle Unicode interactive console input. Maybe it does...? A Java expert would probably know, but I can't find it on the Internet with any obvious searches. But also this problem is Windows-specific, so Windows... why you gotta be this way? In conclusion, computers are bad.&lt;/p&gt;

</description>
      <category>java</category>
      <category>windows</category>
      <category>unicode</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
