<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rusted Pieces]]></title><description><![CDATA[The residue of Rusted Pieces: ideas, code, and scraps of reverse engineering, left to corrode in public.]]></description><link>https://dust.rustedpieces.io</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1758722714851/943809b6-5593-4c4a-92ba-ee9fcc329baa.png</url><title>Rusted Pieces</title><link>https://dust.rustedpieces.io</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Apr 2026 23:31:26 GMT</lastBuildDate><atom:link href="https://dust.rustedpieces.io/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Janus Array: When Your Data Structure Has Two Faces]]></title><description><![CDATA[The Monster in the File
Picture this: you're reverse-engineering some binary format, and you discover a nightmare hierarchy that makes onions jealous. Files split into slices. Slices containing commands. Commands holding elements. And oh, did I menti...]]></description><link>https://dust.rustedpieces.io/the-janus-array-when-your-data-structure-has-two-faces</link><guid isPermaLink="true">https://dust.rustedpieces.io/the-janus-array-when-your-data-structure-has-two-faces</guid><category><![CDATA[adt]]></category><category><![CDATA[rust lang]]></category><category><![CDATA[Mach-O]]></category><category><![CDATA[elf]]></category><category><![CDATA[reverse engineering]]></category><dc:creator><![CDATA[Gabriele Biondo]]></dc:creator><pubDate>Wed, 24 Sep 2025 14:25:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1758723805812/45d939db-ab33-4fdd-b4df-27f9c8e735db.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-monster-in-the-file">The Monster in the File</h2>
<p>Picture this: you're reverse-engineering some binary format, and you discover a nightmare hierarchy that makes onions jealous. Files split into slices. Slices containing commands. Commands holding elements. And oh, did I mention? It's <strong>recursive</strong> - those elements can contain more elements, going arbitrarily deep.</p>
<p>But here's the kicker: everything lives in <strong>dual address spaces</strong>. You have absolute offsets from byte zero of the file, AND relative offsets from the start of each container. It's like having a building where every room has both a street address AND a "third door on the left from the lobby" description.</p>
<p>Welcome to the world of load commands in executable files. Where sanity goes to die.</p>
<h2 id="heading-the-obvious-wrong-solutions">The Obvious (Wrong) Solutions</h2>
<p>Your first instinct? "Throw it in a HashMap!" Map every absolute offset to its structure path. Simple, right?</p>
<p><strong>Wrong.</strong> You'd need to map <em>every single byte</em>. For a 100MB file, that's 100 million entries. Your "optimization" just consumed more RAM than Chrome on a Tuesday.</p>
<p>Next attempt: "Fine, I'll use ranges!" Build a lookup table with <code>(start, end, path)</code> tuples. Better, but now you need interval trees, overlapping ranges, complex maintenance...</p>
<p><strong>Still wrong.</strong> You're solving a problem that doesn't exist. These aren't arbitrary intervals - they're a clean hierarchical partition of the file.</p>
<h2 id="heading-the-janus-insight">The Janus Insight</h2>
<p>Then it hit me: <em>Why fight the duality? Embrace it.</em></p>
<p>Enter the <strong>Janus Array</strong> - named after the Roman god with two faces. One face looks forward through the hierarchy (give me <code>slice[i].commands[j].elements[k]</code>). The other face looks backward from absolute addresses (where does offset 0x47382 live?).</p>
<p><strong>Two access patterns. One unified structure.</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// The "forward" face - direct hierarchical access</span>
file.slices[i].commands[j].elements[k]  <span class="hljs-comment">// O(1)</span>

<span class="hljs-comment">// The "backward" face - recursive binary search  </span>
file.find_address(<span class="hljs-number">0x47382</span>)  <span class="hljs-comment">// O(log n)</span>
</code></pre>
<h2 id="heading-the-implementation-beauty">The Implementation Beauty</h2>
<p>Here's where Rust shines. One trait, multiple implementations, recursive elegance:</p>
<pre><code class="lang-rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DiskOffsets</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">find_address</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, addr: <span class="hljs-built_in">u64</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;Coordinates, Error&gt;;
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get_absolute_range</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; Range&lt;<span class="hljs-built_in">u64</span>&gt;;
    <span class="hljs-comment">// ... other methods</span>
}
</code></pre>
<p>Every level <code>(File, Slice, Command, Element)</code> implements this trait. The search algorithm? <strong>Recursive binary search</strong> that delegates down the hierarchy:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// In each level's find_address():</span>
<span class="hljs-keyword">while</span> start &lt;= end {
    <span class="hljs-keyword">let</span> mid = (start + end) / <span class="hljs-number">2</span>;
    <span class="hljs-keyword">let</span> range = children[mid].get_absolute_range();

    <span class="hljs-keyword">if</span> range.contains(&amp;addr) {
        <span class="hljs-comment">// Found it! Delegate to the child</span>
        <span class="hljs-keyword">return</span> children[mid].find_address(addr);
    }
    <span class="hljs-comment">// ... binary search logic</span>
}
</code></pre>
<p>Beautiful. No separate index structure. No memory duplication. The hierarchy <strong>IS</strong> the index.</p>
<h2 id="heading-when-theory-meets-reality">When Theory Meets Reality</h2>
<p>The textbook says "O(log n) per level, so O(log n × levels)."</p>
<p><strong>Textbooks lie.</strong></p>
<p>In practice, with load commands:</p>
<ul>
<li><p>Files rarely have more than 3 slices</p>
</li>
<li><p>Many commands are leaf nodes (no elements)</p>
</li>
<li><p>Elements are uniformly distributed when present</p>
</li>
</ul>
<p>So the real complexity? <strong>T(S,C,E) = ln(S) + ln(C) + ln(E)</strong>, where <strong>S+C+E=n</strong></p>
<p>But S ≤ 3 in practice, so ln(S) ≈ 1.1 = constant.</p>
<p><strong>Real complexity: O(ln(C) + ln(E))≤O(ln(n))</strong></p>
<p>Most of the time you don't even reach the element level, so it's just <strong>O(ln(C))</strong>.</p>
<p><em>This is what happens when you analyze algorithms in context, not in isolation.</em></p>
<h2 id="heading-the-payoff">The Payoff</h2>
<p>The Janus Array gives you:</p>
<ul>
<li><p><strong>O(1) hierarchical navigation</strong> for the common case</p>
</li>
<li><p><strong>O(log n) reverse lookup</strong> when you need it</p>
</li>
<li><p><strong>Zero memory overhead</strong> (no auxiliary structures)</p>
</li>
<li><p><strong>Type-safe error handling</strong> throughout</p>
</li>
<li><p><strong>Recursive elegance</strong> that scales naturally</p>
</li>
</ul>
<p>All wrapped in Rust's zero-cost abstractions and memory safety guarantees.</p>
<h2 id="heading-conclusion-architecture-over-algorithms">Conclusion: Architecture Over Algorithms</h2>
<p>This isn't about being clever with data structures. It's about <strong>understanding your problem domain</strong> well enough to design solutions that feel inevitable.</p>
<p>The binary format had duality baked in. Instead of fighting it with complex indexing schemes, I embraced it with a structure that naturally supports both access patterns.</p>
<p>Sometimes the best optimization is realizing you don't need to optimize at all. You need to <strong>design better.</strong></p>
<p><em>And yes, all the Python developers can kiss my gluteus maximus. 😉</em></p>
<hr />
<p>GitHub: <a target="_blank" href="https://github.com/gb-at-r3/janus-array">https://github.com/gb-at-r3/janus-array</a></p>
]]></content:encoded></item></channel></rss>