ipeople.gnome.org_federico.atom.xml - sfeed_tests - sfeed tests and RSS and Atom files Err codemadness.org 70 hgit clone git://git.codemadness.org/sfeed_tests URL:git://git.codemadness.org/sfeed_tests codemadness.org 70 1Log /git/sfeed_tests/log.gph codemadness.org 70 1Files /git/sfeed_tests/files.gph codemadness.org 70 1Refs /git/sfeed_tests/refs.gph codemadness.org 70 1README /git/sfeed_tests/file/README.gph codemadness.org 70 1LICENSE /git/sfeed_tests/file/LICENSE.gph codemadness.org 70 i--- Err codemadness.org 70 ipeople.gnome.org_federico.atom.xml (1402111B) Err codemadness.org 70 i--- Err codemadness.org 70 i 1 Err codemadness.org 70 i 2 Federico's Bloghttps://people.gnome.org/~federico/blog/2021-09-01T13:57:47-05:00GNOME themes, an incomplete status report, and how you can help2021-09-01T13:57:47-05:002021-09-01T13:57:47-05:00Federico Mena Quinterotag:people.gnome.org,2021-09-01:/~federico/blog/gnome-themes.html<p>"Themes in GNOME" is a complicated topic in technical and social Err codemadness.org 70 i 3 terms. Technically there are a lot of incomplete moving parts; Err codemadness.org 70 i 4 socially there is a lot of missing documentation to be written, a lot Err codemadness.org 70 i 5 of miscommunication and mismatched expectations.</p> Err codemadness.org 70 i 6 <p>The following is a brief and incomplete, but hopefully encouraging, Err codemadness.org 70 i 7 summary …</p><p>"Themes in GNOME" is a complicated topic in technical and social Err codemadness.org 70 i 8 terms. Technically there are a lot of incomplete moving parts; Err codemadness.org 70 i 9 socially there is a lot of missing documentation to be written, a lot Err codemadness.org 70 i 10 of miscommunication and mismatched expectations.</p> Err codemadness.org 70 i 11 <p>The following is a brief and incomplete, but hopefully encouraging, Err codemadness.org 70 i 12 summary of the status of themes in GNOME. I want to give you an Err codemadness.org 70 i 13 overall picture of the status of things, and more importantly, an idea Err codemadness.org 70 i 14 of how you can help. This is not a problem that can be solved by a Err codemadness.org 70 i 15 small team of platform developers.</p> Err codemadness.org 70 i 16 <p>I wish to thank Alexander Mikhaylenko for providing most of the Err codemadness.org 70 i 17 knowledge in this post.</p> Err codemadness.org 70 i 18 <h1>Frame of reference</h1> Err codemadness.org 70 i 19 <p>First, I urge you to read Cassidy James Blaede's comprehensive "<a href="https://blog.elementary.io/the-need-for-a-freedesktop-dark-style-preference/">The Err codemadness.org 70 i 20 Need for a FreeDesktop Dark Style Err codemadness.org 70 i 21 Preference</a>". Err codemadness.org 70 i 22 That gives an excellent, well-researched introduction to the "dark Err codemadness.org 70 i 23 style" problem, the status quo on other platforms, and exploratory Err codemadness.org 70 i 24 plans for GNOME and Elementary from 2019.</p> Err codemadness.org 70 i 25 <p>Go ahead, read it. It's very good.</p> Err codemadness.org 70 i 26 <p>There is also a <a href="https://www.youtube.com/watch?v=gi_3b81eBUE&amp;list=PLkmRdYgttscEuv9v2-H9P5FBj8-td_Nri&amp;index=31">GUADEC talk about Cassidy's Err codemadness.org 70 i 27 research</a> Err codemadness.org 70 i 28 if you prefer to watch a video.</p> Err codemadness.org 70 i 29 <p>Two key take-aways from this: First, about this being a Err codemadness.org 70 i 30 <strong>preference</strong>, not a system-enforced setting:</p> Err codemadness.org 70 i 31 <blockquote> Err codemadness.org 70 i 32 <p>I’m explicitly using the language “Dark Style Preference” for a Err codemadness.org 70 i 33 reason! As you’ll read further on, it’s important that this is Err codemadness.org 70 i 34 treated as a user “preference,” not an explicit “mode” or Err codemadness.org 70 i 35 strictly-enforced “setting.” It’s also not a “theme” in the sense Err codemadness.org 70 i 36 that it just swaps out some assets, but is a way for the OS to Err codemadness.org 70 i 37 support a user expressing a preference, and apps to respond to that Err codemadness.org 70 i 38 preference.</p> Err codemadness.org 70 i 39 </blockquote> Err codemadness.org 70 i 40 <p>Second, about the <strong>accessibility</strong> implications:</p> Err codemadness.org 70 i 41 <blockquote> Err codemadness.org 70 i 42 <p>Clearly there’s an accessibility and usability angle here. And as Err codemadness.org 70 i 43 with other accessibility efforts, it’s important to not relegate a Err codemadness.org 70 i 44 dark style preference to a buried “Universal Access” or Err codemadness.org 70 i 45 “Accessibility” feature, as that makes it less discoverable, less Err codemadness.org 70 i 46 tested, and less likely to be used by folks who could greatly Err codemadness.org 70 i 47 benefit, but don’t consider themselves “disabled.”</p> Err codemadness.org 70 i 48 </blockquote> Err codemadness.org 70 i 49 <h1>Libadwaita and the rest of the ecosystem</h1> Err codemadness.org 70 i 50 <p>Read the <a href="https://discourse.gnome.org/t/libadwaita-1-0-roadmap/7415">libadwaita Err codemadness.org 70 i 51 roadmap</a>; Err codemadness.org 70 i 52 it is very short, but links to very interesting issues on gitlab.</p> Err codemadness.org 70 i 53 <p>For example, this merge request is for an <a href="https://gitlab.gnome.org/GNOME/libadwaita/-/merge_requests/224">API to query the dark style Err codemadness.org 70 i 54 and high-contrast Err codemadness.org 70 i 55 preferences</a>. Err codemadness.org 70 i 56 It has links to pending work in other parts of the platform: libhandy, Err codemadness.org 70 i 57 gsettings schemas, portals so that containerized applications can Err codemadness.org 70 i 58 query those preferences.</p> Err codemadness.org 70 i 59 <p>As far as I understand it, applications that just use GTK3 or libhandy Err codemadness.org 70 i 60 can opt in to supporting the dark style preference — it is opt-in Err codemadness.org 70 i 61 because doing that unconditionally in GTK/libhandy right now would Err codemadness.org 70 i 62 break existing applications.. If your app uses libadwaita, it is Err codemadness.org 70 i 63 assumed that you have opted into supporting that preference, since Err codemadness.org 70 i 64 libadwaita's widgets already make that assumption, and it is not Err codemadness.org 70 i 65 API-stable yet — so it can make that assumption from the beginning.</p> Err codemadness.org 70 i 66 <p>There is discussion of the accessibility implications in <a href="https://gitlab.gnome.org/Teams/Design/settings-mockups/-/issues/27#note_1257700">the design Err codemadness.org 70 i 67 mockups</a>.</p> Err codemadness.org 70 i 68 <h1>CSS parity across implementations</h1> Err codemadness.org 70 i 69 <p>In GNOME we have three implementations of CSS:</p> Err codemadness.org 70 i 70 <ul> Err codemadness.org 70 i 71 <li> Err codemadness.org 70 i 72 <p>librsvg uses servo's engine for CSS selector matching, and micro-parsers for CSS values based on servo's cssparser.</p> Err codemadness.org 70 i 73 </li> Err codemadness.org 70 i 74 <li> Err codemadness.org 70 i 75 <p>GTK has its own CSS parser and processor.</p> Err codemadness.org 70 i 76 </li> Err codemadness.org 70 i 77 <li> Err codemadness.org 70 i 78 <p>Gnome-shell uses an embedded version of libcroco for parsing, but it Err codemadness.org 70 i 79 does most of the selector matching and cascading with gnome-shell's Err codemadness.org 70 i 80 own Shell Toolkit code.</p> Err codemadness.org 70 i 81 </li> Err codemadness.org 70 i 82 </ul> Err codemadness.org 70 i 83 <p>None of those implementations supports <code>@media</code> queries nor custom Err codemadness.org 70 i 84 properties with <code>var()</code>. That is, unlike in the web platform, GNOME Err codemadness.org 70 i 85 applications cannot have this in their CSS:</p> Err codemadness.org 70 i 86 <div class="highlight"><pre><span></span><code><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">prefers-color-scheme</span><span class="o">:</span> <span class="nt">dark</span><span class="o">)</span> <span class="p">{</span> Err codemadness.org 70 i 87 <span class="c">/* styles for dark style */</span> Err codemadness.org 70 i 88 <span class="p">}</span> Err codemadness.org 70 i 89 Err codemadness.org 70 i 90 <span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">prefers-color-scheme</span><span class="o">:</span> <span class="nt">light</span><span class="o">)</span> <span class="p">{</span> Err codemadness.org 70 i 91 <span class="c">/* styles for light style */</span> Err codemadness.org 70 i 92 <span class="p">}</span> Err codemadness.org 70 i 93 </code></pre></div> Err codemadness.org 70 i 94 Err codemadness.org 70 i 95 <p>Or even declaring colors in a civilized fashion:</p> Err codemadness.org 70 i 96 <div class="highlight"><pre><span></span><code><span class="p">:</span><span class="nd">root</span> <span class="p">{</span> Err codemadness.org 70 i 97 <span class="nv">--main-bg-color</span><span class="p">:</span> <span class="kc">pink</span><span class="p">;</span> Err codemadness.org 70 i 98 <span class="p">}</span> Err codemadness.org 70 i 99 Err codemadness.org 70 i 100 <span class="nt">some_widget</span> <span class="p">{</span> Err codemadness.org 70 i 101 <span class="k">background-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="nv">--main-bg-color</span><span class="p">);</span> Err codemadness.org 70 i 102 <span class="p">}</span> Err codemadness.org 70 i 103 </code></pre></div> Err codemadness.org 70 i 104 Err codemadness.org 70 i 105 <p>Or combining the two:</p> Err codemadness.org 70 i 106 <div class="highlight"><pre><span></span><code><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">prefers-color-scheme</span><span class="o">:</span> <span class="nt">dark</span><span class="o">)</span> <span class="p">{</span> Err codemadness.org 70 i 107 <span class="p">:</span><span class="nd">root</span> <span class="p">{</span> Err codemadness.org 70 i 108 <span class="nv">--main-bg-color</span><span class="p">:</span> <span class="c">/* some nice dark background color */</span><span class="p">;</span> Err codemadness.org 70 i 109 <span class="nv">--main-fg-color</span><span class="p">:</span> <span class="c">/* a contrasty light foreground */</span><span class="p">;</span> Err codemadness.org 70 i 110 <span class="p">}</span> Err codemadness.org 70 i 111 <span class="p">}</span> Err codemadness.org 70 i 112 Err codemadness.org 70 i 113 <span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">prefers-color-scheme</span><span class="o">:</span> <span class="nt">light</span><span class="o">)</span> <span class="p">{</span> Err codemadness.org 70 i 114 <span class="p">:</span><span class="nd">root</span> <span class="p">{</span> Err codemadness.org 70 i 115 <span class="nv">--main-bg-color</span><span class="p">:</span> <span class="c">/* some nice light background color */</span><span class="p">;</span> Err codemadness.org 70 i 116 <span class="nv">--main-fg-color</span><span class="p">:</span> <span class="c">/* a contrasty dark foreground */</span><span class="p">;</span> Err codemadness.org 70 i 117 <span class="p">}</span> Err codemadness.org 70 i 118 <span class="p">}</span> Err codemadness.org 70 i 119 Err codemadness.org 70 i 120 <span class="nt">some_widget</span> <span class="p">{</span> Err codemadness.org 70 i 121 <span class="k">background-color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="nv">--main-bg-color</span><span class="p">);</span> Err codemadness.org 70 i 122 <span class="p">}</span> Err codemadness.org 70 i 123 </code></pre></div> Err codemadness.org 70 i 124 Err codemadness.org 70 i 125 <p>Boom. I think this would remove some workarounds we have right now:</p> Err codemadness.org 70 i 126 <ul> Err codemadness.org 70 i 127 <li> Err codemadness.org 70 i 128 <p>Just like GTK, libadwaita generates four variants of the system's Err codemadness.org 70 i 129 stylesheet using scss (regular, dark, high-contrast, Err codemadness.org 70 i 130 high-contrast-dark). This would be obviated with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media"><code>@media</code> Err codemadness.org 70 i 131 queries</a> Err codemadness.org 70 i 132 for <code>prefers-color-scheme</code>, <code>prefers-contrast</code>, <code>inverted-colors</code> as Err codemadness.org 70 i 133 in the web platform.</p> Err codemadness.org 70 i 134 </li> Err codemadness.org 70 i 135 <li> Err codemadness.org 70 i 136 <p>GTK has a custom <code>@define-color</code> keyword, but neither gnome-shell Err codemadness.org 70 i 137 nor librsvg support that. This would be obviated with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">CSS custom Err codemadness.org 70 i 138 properties</a> - Err codemadness.org 70 i 139 the <code>var()</code> mechanism. (I don't know if some "environmental" stuff Err codemadness.org 70 i 140 would be better done as Err codemadness.org 70 i 141 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/env()"><code>env()</code></a>, Err codemadness.org 70 i 142 but none of the three implementations support that, either.)</p> Err codemadness.org 70 i 143 </li> Err codemadness.org 70 i 144 </ul> Err codemadness.org 70 i 145 <h1>Accent colors</h1> Err codemadness.org 70 i 146 <p>They are currently implemented with GTK's <code>@define-color</code>, which is Err codemadness.org 70 i 147 not ideal if the colors have to trickle down from GTK to SVG icons, Err codemadness.org 70 i 148 since librsvg doesn't do <code>@define-color</code> - it would rather have Err codemadness.org 70 i 149 <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/459"><code>var()</code> Err codemadness.org 70 i 150 instead</a>.</p> Err codemadness.org 70 i 151 <p>Of course, gnome-shell's libcroco doesn't do <code>@define-color</code> either.</p> Err codemadness.org 70 i 152 <p>Look for <code>@accent_color</code>, <code>@accent_bg_color</code>, <code>@warning_color</code>, etc. in the <a href="https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/src/stylesheet/_defaults.scss">default Err codemadness.org 70 i 153 stylesheet</a>, Err codemadness.org 70 i 154 or better yet, <strong>write documentation!</strong></p> Err codemadness.org 70 i 155 <p>The default style:</p> Err codemadness.org 70 i 156 <p><img alt="Default blue style" src="https://people.gnome.org/~federico/blog/images/adwaita-default.png"></p> Err codemadness.org 70 i 157 <p>Accent color set to orange (e.g. tweak it in GTK's CSS inspector):</p> Err codemadness.org 70 i 158 <p><img alt="Orange accents for widgets" src="https://people.gnome.org/~federico/blog/images/adwaita-accent-orange.png"></p> Err codemadness.org 70 i 159 <div class="highlight"><pre><span></span><code><span class="c">/* Standalone, e.g. the &quot;Page 1&quot; label */</span> Err codemadness.org 70 i 160 <span class="p">@</span><span class="k">define-color</span> <span class="nt">accent_color</span> <span class="p">@</span><span class="k">orange_5</span><span class="p">;</span> Err codemadness.org 70 i 161 Err codemadness.org 70 i 162 <span class="c">/* background+text pair */</span> Err codemadness.org 70 i 163 <span class="p">@</span><span class="k">define-color</span> <span class="nt">accent_bg_color</span> <span class="p">@</span><span class="k">orange_4</span><span class="p">;</span> Err codemadness.org 70 i 164 <span class="p">@</span><span class="k">define-color</span> <span class="nt">accent_fg_color</span> <span class="nt">white</span><span class="p">;</span> Err codemadness.org 70 i 165 </code></pre></div> Err codemadness.org 70 i 166 Err codemadness.org 70 i 167 <h1>Custom widgets</h1> Err codemadness.org 70 i 168 <p>Again, your app's custom stylesheet for its custom widgets can use the Err codemadness.org 70 i 169 colors defined through <code>@define-color</code> from the system's stylesheet.</p> Err codemadness.org 70 i 170 <h1>Recoloring styles</h1> Err codemadness.org 70 i 171 <p>You will be able to do this after it gets merged into the main branch, Err codemadness.org 70 i 172 e.g. recolor everything to sepia:</p> Err codemadness.org 70 i 173 <p><img alt="Adwaita recolored to sepia" src="https://people.gnome.org/~federico/blog/images/adwaita-recolored.png"></p> Err codemadness.org 70 i 174 <div class="highlight"><pre><span></span><code><span class="p">@</span><span class="k">define-color</span> <span class="nt">headerbar_bg_color</span> <span class="p">#</span><span class="nn">eedcbf</span><span class="p">;</span> Err codemadness.org 70 i 175 <span class="p">@</span><span class="k">define-color</span> <span class="nt">headerbar_fg_color</span> <span class="p">#</span><span class="nn">483a22</span><span class="p">;</span> Err codemadness.org 70 i 176 Err codemadness.org 70 i 177 <span class="p">@</span><span class="k">define-color</span> <span class="nt">bg_color</span> <span class="p">#</span><span class="nn">f9f3e9</span><span class="p">;</span> Err codemadness.org 70 i 178 <span class="p">@</span><span class="k">define-color</span> <span class="nt">fg_color</span> <span class="p">#</span><span class="nn">483a22</span><span class="p">;</span> Err codemadness.org 70 i 179 Err codemadness.org 70 i 180 <span class="p">@</span><span class="k">define-color</span> <span class="nt">dark_fill_color</span> <span class="nt">shade</span><span class="o">(</span><span class="p">#</span><span class="nn">f9f3e9</span><span class="o">,</span> <span class="p">.</span><span class="nc">95</span><span class="o">)</span><span class="p">;</span> Err codemadness.org 70 i 181 Err codemadness.org 70 i 182 <span class="p">@</span><span class="k">define-color</span> <span class="nt">accent_bg_color</span> <span class="p">@</span><span class="k">orange_4</span><span class="p">;</span> Err codemadness.org 70 i 183 <span class="p">@</span><span class="k">define-color</span> <span class="nt">accent_color</span> <span class="p">@</span><span class="k">orange_5</span><span class="p">;</span> Err codemadness.org 70 i 184 </code></pre></div> Err codemadness.org 70 i 185 Err codemadness.org 70 i 186 <p>Of course <code>shade()</code> is not web-platform CSS, either. We could keep Err codemadness.org 70 i 187 it, or redo it by implementing <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/calc()"><code>calc()</code> Err codemadness.org 70 i 188 function</a> for Err codemadness.org 70 i 189 color values.</p> Err codemadness.org 70 i 190 <h1>Recoloring icons</h1> Err codemadness.org 70 i 191 <p>Currently GTK takes some defined colors and <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/539#note_669487">creates a chunk of CSS to Err codemadness.org 70 i 192 inject into SVG for Err codemadness.org 70 i 193 icons</a>. Err codemadness.org 70 i 194 This has <a href="https://gitlab.gnome.org/GNOME/gtk/-/issues/2314">some Err codemadness.org 70 i 195 problems</a>.</p> Err codemadness.org 70 i 196 <p>There is also some discussion about <a href="https://gitlab.gnome.org/GNOME/gtk/-/issues/1762">standardizing recolorable Err codemadness.org 70 i 197 icons</a> across Err codemadness.org 70 i 198 desktop environments.</p> Err codemadness.org 70 i 199 <h1>How you can help</h1> Err codemadness.org 70 i 200 <p>Implement support for <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries"><code>@media</code> Err codemadness.org 70 i 201 queries</a> Err codemadness.org 70 i 202 in our three CSS implementations (librsvg, gnome-shell, GTK). Decide Err codemadness.org 70 i 203 how CSS media features like <code>prefers-color-scheme</code>, Err codemadness.org 70 i 204 <code>prefers-contrast</code>, <code>inverted-colors</code> should interact with the GNOME's Err codemadness.org 70 i 205 themes and accessibility, and decide if we should use them for Err codemadness.org 70 i 206 familiarity with the web platform, or if we need media features with Err codemadness.org 70 i 207 different names.</p> Err codemadness.org 70 i 208 <p>Implement support for <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties">CSS custom properties - Err codemadness.org 70 i 209 <code>var()</code></a> Err codemadness.org 70 i 210 in our three CSS implementations. Decide if we should replace the Err codemadness.org 70 i 211 current <code>@define-color</code> with that (note that <code>@define-color</code> is only Err codemadness.org 70 i 212 in GTK, but not in librsvg or gnome-shell).</p> Err codemadness.org 70 i 213 <p>See the <a href="https://discourse.gnome.org/t/libadwaita-1-0-roadmap/7415">libadwaita Err codemadness.org 70 i 214 roadmap</a> Err codemadness.org 70 i 215 and help out!</p> Err codemadness.org 70 i 216 <p>Port applications to use the proposed APIs for querying the dark style Err codemadness.org 70 i 217 preference. There are a bunch of hacky ways of doing it right now; Err codemadness.org 70 i 218 they need to be migrated to the new system.</p> Err codemadness.org 70 i 219 <p>Personally I would love help with finishing to <a href="https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1355">port gnome-shell's Err codemadness.org 70 i 220 styles to Err codemadness.org 70 i 221 Rust</a> - Err codemadness.org 70 i 222 this is part of unifying librsvg's and gnome-shell's CSS machinery.</p>Bzip2's experimental repository is changing maintainership2021-06-03T19:21:04-05:002021-06-03T19:21:04-05:00Federico Mena Quinterotag:people.gnome.org,2021-06-03:/~federico/blog/bzip2-changing-maintainership.html<p>Bzip2's stable repository is maintained <a href="https://sourceware.org/git/?p=bzip2.git;a=summary">at Sourceware</a> by Err codemadness.org 70 i 223 Mark Wielaard. In 2019 I started maintaining an <a href="https://gitlab.com/bzip2/bzip2/">experimental Err codemadness.org 70 i 224 repository in GitLab</a>, with the intention of updating Err codemadness.org 70 i 225 the build system and starting a Rust port of bzip2. Unfortunately I Err codemadness.org 70 i 226 have left this project slip by.</p> Err codemadness.org 70 i 227 <p>The new maintainer of the <a href="https://gitlab.com/bzip2/bzip2/">experimental repository …</a></p><p>Bzip2's stable repository is maintained <a href="https://sourceware.org/git/?p=bzip2.git;a=summary">at Sourceware</a> by Err codemadness.org 70 i 228 Mark Wielaard. In 2019 I started maintaining an <a href="https://gitlab.com/bzip2/bzip2/">experimental Err codemadness.org 70 i 229 repository in GitLab</a>, with the intention of updating Err codemadness.org 70 i 230 the build system and starting a Rust port of bzip2. Unfortunately I Err codemadness.org 70 i 231 have left this project slip by.</p> Err codemadness.org 70 i 232 <p>The new maintainer of the <a href="https://gitlab.com/bzip2/bzip2/">experimental repository</a> for Err codemadness.org 70 i 233 Bzip2 is Micah Snyder. Thanks, Micah, for picking it up!</p>Librsvg, Rust, and non-mainstream architectures2021-02-24T19:14:55-06:002021-02-24T19:14:55-06:00Federico Mena Quinterotag:people.gnome.org,2021-02-24:/~federico/blog/librsvg-rust-and-non-mainstream-architectures.html<p>Almost five years ago <a href="https://people.gnome.org/~federico/news-2016-10.html#25">librsvg introduced Rust into its source Err codemadness.org 70 i 234 code</a>. Around the same time, Linux distributions Err codemadness.org 70 i 235 started shipping the first versions of Firefox that also required Err codemadness.org 70 i 236 Rust. I unashamedly wanted to ride that wave: distros would <em>have</em> to Err codemadness.org 70 i 237 integrate a new language in their build infrastructure, or they would …</p><p>Almost five years ago <a href="https://people.gnome.org/~federico/news-2016-10.html#25">librsvg introduced Rust into its source Err codemadness.org 70 i 238 code</a>. Around the same time, Linux distributions Err codemadness.org 70 i 239 started shipping the first versions of Firefox that also required Err codemadness.org 70 i 240 Rust. I unashamedly wanted to ride that wave: distros would <em>have</em> to Err codemadness.org 70 i 241 integrate a new language in their build infrastructure, or they would Err codemadness.org 70 i 242 be left without Firefox. I was hoping that having a working Rust Err codemadness.org 70 i 243 toolchain would make it easier for the rustified librsvg to get into Err codemadness.org 70 i 244 distros.</p> Err codemadness.org 70 i 245 <p>Two years after that, <a href="https://lwn.net/Articles/771355/">someone from Debian complained</a> Err codemadness.org 70 i 246 that this made it hard or impossible to build librsvg (and all the Err codemadness.org 70 i 247 software that depends on it, which is A Lot) on all the architectures Err codemadness.org 70 i 248 that Debian builds on — specifically, on things like HP PA-RISC or Err codemadness.org 70 i 249 Alpha, which <a href="https://www.debian.org/ports/">even Debian marks as "discontinued" now</a>.</p> Err codemadness.org 70 i 250 <p>Recently there was a similar kerfuffle, this time from <a href="https://lwn.net/Articles/845535/">someone from Err codemadness.org 70 i 251 Gentoo</a>, specifically about how Python's cryptography Err codemadness.org 70 i 252 package now requires Rust. So, it doesn't build for platforms that Err codemadness.org 70 i 253 Rust/LLVM don't support, like hppa, alpha, and Itanium. It also Err codemadness.org 70 i 254 doesn't build for platforms for which there are no Rust packages from Err codemadness.org 70 i 255 Gentoo yet (mips, s390x, riscv among them).</p> Err codemadness.org 70 i 256 <h2>Memories of discontinued architectures</h2> Err codemadness.org 70 i 257 <p>Let me reminisce about a couple of discontinued architectures. If I'm Err codemadness.org 70 i 258 reading <a href="https://en.wikipedia.org/wiki/DEC_Alpha">Wikipedia</a> correctly, the DEC Alpha ceased to be Err codemadness.org 70 i 259 developed in 2001, and HP, who purchased Compaq, who purchased DEC, Err codemadness.org 70 i 260 stopped selling Alpha systems in 2007. Notably, Compaq phased out the Err codemadness.org 70 i 261 Alpha in favor of the Itanium, which stopped being developed in 2017.</p> Err codemadness.org 70 i 262 <p>I <em>used</em> an Alpha machine in 1997-1998, back at the University. Err codemadness.org 70 i 263 <a href="https://twitter.com/migueldeicaza/">Miguel</a> kindly let me program and learn from him at the Institute Err codemadness.org 70 i 264 where he worked, and the computer lab there got an Alpha box to let Err codemadness.org 70 i 265 the scientists run mathematical models on a machine with really fast Err codemadness.org 70 i 266 floating-point. This was a time when people actually regularly ssh'ed Err codemadness.org 70 i 267 into machines to run X11 applications remotely — in their case, I Err codemadness.org 70 i 268 think it was Matlab and Mathematica. Good times.</p> Err codemadness.org 70 i 269 <p>The Alpha had fast floating point, much faster than Intel x86 CPUs, Err codemadness.org 70 i 270 and I was delighted to do graphics work on it. That was the first Err codemadness.org 70 i 271 64-bit machine I used, and it let me learn how to fix code that only Err codemadness.org 70 i 272 assumed 32 bits. It had a really picky floating-point unit. Whereas Err codemadness.org 70 i 273 x86 would happily throw you a NaN if you used uninitialized memory as Err codemadness.org 70 i 274 floats, the Alpha would properly fault and crash the program. I fixed Err codemadness.org 70 i 275 so many bugs thanks to that!</p> Err codemadness.org 70 i 276 <p>I also have fond memories of the 32-bit SPARC Err codemadness.org 70 i 277 boxes at the University and their flat-screen fixed-frequency CRT Err codemadness.org 70 i 278 displays, but you know, I haven't <em>seen</em> one of those machines since Err codemadness.org 70 i 279 1998. Because I was doing graphics work, I used the single SPARC Err codemadness.org 70 i 280 machine in the computer lab at the Institute that had 24-bit graphics, Err codemadness.org 70 i 281 with a humongous 21" CRT display. PCs at the time still had 8-bit video Err codemadness.org 70 i 282 cards and shitty little monitors.</p> Err codemadness.org 70 i 283 <p>At about the same time that the Institute got its Alpha, it also got Err codemadness.org 70 i 284 one of the first 64-bit UltraSPARCs from Sun — a very expensive Err codemadness.org 70 i 285 machine definitely not targeted to hobbyists. I think it had two CPUs! Err codemadness.org 70 i 286 Multicore did not exist!</p> Err codemadness.org 70 i 287 <p>I think I saw a single Itanium machine in my life, probably around Err codemadness.org 70 i 288 2002-2005. The Ximian/Novell office in Mexico City got one, for QA Err codemadness.org 70 i 289 purposes — an incredibly loud and unstable machine. I don't think we Err codemadness.org 70 i 290 ever did any actual development on that box; it was a "can you Err codemadness.org 70 i 291 reproduce this bug there" kind of thing. I think Ximian/Novell had a Err codemadness.org 70 i 292 contract with HP to test the distro there, I don't remember.</p> Err codemadness.org 70 i 293 <h2>Unsupported architectures at the LLVM level</h2> Err codemadness.org 70 i 294 <p>Platforms like the Alpha and Itanium that Rust/LLVM don't support — Err codemadness.org 70 i 295 those platforms are dead in the water. The compiler cannot target Err codemadness.org 70 i 296 them, as Rust generates machine code via LLVM, and LLVM doesn't Err codemadness.org 70 i 297 support them.</p> Err codemadness.org 70 i 298 <p>I don't know why distributions maintained by volunteers give Err codemadness.org 70 i 299 themselves the responsibility to keep their software running on Err codemadness.org 70 i 300 platforms that have not been manufactured for years, and that were Err codemadness.org 70 i 301 never even hobbyist machines.</p> Err codemadness.org 70 i 302 <p>I read the other day, and now I regret not keeping the link, something Err codemadness.org 70 i 303 like this: don't assume that your hobby computing entitles you to free Err codemadness.org 70 i 304 labor on the part of compiler writers, software maintainers, and Err codemadness.org 70 i 305 distro volunteers. (If someone helps me find the source, I'll happily Err codemadness.org 70 i 306 link to it and quote it properly.)</p> Err codemadness.org 70 i 307 <h2>Non-tier-1 platforms and "$distro does not build Rust there yet"</h2> Err codemadness.org 70 i 308 <p>I think people are discovering these once again:</p> Err codemadness.org 70 i 309 <ul> Err codemadness.org 70 i 310 <li> Err codemadness.org 70 i 311 <p>Writing and supporting a compiler for a certain architecture takes Real Work.</p> Err codemadness.org 70 i 312 </li> Err codemadness.org 70 i 313 <li> Err codemadness.org 70 i 314 <p>Supporting a distro for a certain architecture takes Real Work.</p> Err codemadness.org 70 i 315 </li> Err codemadness.org 70 i 316 <li> Err codemadness.org 70 i 317 <p>Fixing software to work on a certain architecture takes Real Work.</p> Err codemadness.org 70 i 318 </li> Err codemadness.org 70 i 319 </ul> Err codemadness.org 70 i 320 <p>Rust divides its support for different platforms into <a href="https://doc.rust-lang.org/nightly/rustc/platform-support.html">tiers</a>, going Err codemadness.org 70 i 321 from tier 1, the most supported, to tier 3, the least supported. Or, Err codemadness.org 70 i 322 I should say, <em>taken care of</em>, which is a combination of people who Err codemadness.org 70 i 323 actually have the hardware in question, and whether the general CI and Err codemadness.org 70 i 324 build tooling is prepared to deal with them as effectively as it does Err codemadness.org 70 i 325 for tier 1 platforms.</p> Err codemadness.org 70 i 326 <p>In other words: there are more people capable of paying attention to, and Err codemadness.org 70 i 327 testing things on, x86_64 PCs than there are for Err codemadness.org 70 i 328 <code>sparc-unknown-linux-gnu</code>.</p> Err codemadness.org 70 i 329 <h2>Some anecdotes from Suse</h2> Err codemadness.org 70 i 330 <p>At Suse we actually support IBM's s390x big iron; those mainframes run Err codemadness.org 70 i 331 Suse Linux Enterprise Server. You have to pay a lot of money to get a Err codemadness.org 70 i 332 machine like that and support for it. It's a room-sized beast that Err codemadness.org 70 i 333 requires professional babysitting.</p> Err codemadness.org 70 i 334 <p>When librsvg and Firefox started getting rustified, there was of Err codemadness.org 70 i 335 course concern about getting Rust to work properly on the s390x. Err codemadness.org 70 i 336 I worked sporadically with the people who made the distro work there, Err codemadness.org 70 i 337 and who had to deal with building Rust and Firefox on it (librsvg Err codemadness.org 70 i 338 was a non-issue after getting Rust and Firefox to work).</p> Err codemadness.org 70 i 339 <p>I think all the LLVM work for the s390x was done at IBM. There were Err codemadness.org 70 i 340 probably a couple of miscompilations that affected Firefox; they got fixed.</p> Err codemadness.org 70 i 341 <p>One would expect bugs in software for IBM mainframes to be fixed by Err codemadness.org 70 i 342 IBM or its contractors, not by volunteers maintaining a distro in Err codemadness.org 70 i 343 their spare time.</p> Err codemadness.org 70 i 344 <p>Giving computing time on mainframes to volunteers in distros could seem Err codemadness.org 70 i 345 like a good samaritan move, or a trap to extract free labor from Err codemadness.org 70 i 346 unsuspecting people.</p> Err codemadness.org 70 i 347 <h3>Endianness bugs</h3> Err codemadness.org 70 i 348 <p>Firefox's problems on the s390x were more around big-endian bugs than Err codemadness.org 70 i 349 anything. You see, all the common architectures these days (x86_64 Err codemadness.org 70 i 350 and arm64) are little-endian. However, s390x is <a href="https://en.wikipedia.org/wiki/Endianness">big-endian</a>, Err codemadness.org 70 i 351 which means that all multi-byte numbers in memory are stored backwards Err codemadness.org 70 i 352 from what most software expects.</p> Err codemadness.org 70 i 353 <p>It is not a problem to write software that assumes little-endian or Err codemadness.org 70 i 354 big-endian all the time, but it takes a little care to write software Err codemadness.org 70 i 355 that works on either.</p> Err codemadness.org 70 i 356 <p>Most of the software that volunteers and paid people write assumes Err codemadness.org 70 i 357 little-endian CPUs, because that is likely what they are targeting. Err codemadness.org 70 i 358 It is a pain in the ass to encounter code that works incorrectly on Err codemadness.org 70 i 359 big-endian — a pain because <em>knowing where to look</em> for evidence of Err codemadness.org 70 i 360 bugs is tricky, and <em>fixing existing code</em> to work with either Err codemadness.org 70 i 361 endianness can be either very simple, or a major adventure in Err codemadness.org 70 i 362 refactoring and testing.</p> Err codemadness.org 70 i 363 <p>Two cases in point:</p> Err codemadness.org 70 i 364 <p><strong>Firefox.</strong> When Suse started dealing with Rust and Firefox in the Err codemadness.org 70 i 365 s390x, there were endianness bugs in the graphics code in Firefox that Err codemadness.org 70 i 366 deals with pixel formats. Whether pixels get stored in memory as Err codemadness.org 70 i 367 ARGB/ABGR/RGBA/etc. is a platform-specific thing, and is generally a Err codemadness.org 70 i 368 combination of the graphics hardware for that platform, plus the Err codemadness.org 70 i 369 actual CPU architecture. At that time, it looked like the C++ code in Err codemadness.org 70 i 370 Firefox that deals with pixels had been rewritten/refactored, and had Err codemadness.org 70 i 371 lost big-endian support along the way. I don't know the current Err codemadness.org 70 i 372 status (not a single big-endian CPU in my vincinity), but I haven't Err codemadness.org 70 i 373 seen related bugs come in the Suse bug tracker? Maybe it's fixed now?</p> Err codemadness.org 70 i 374 <p><strong>Librsvg</strong> had <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues?scope=all&amp;utf8=%E2%9C%93&amp;state=closed&amp;search=s390">two root causes of bugs for Err codemadness.org 70 i 375 big-endian</a>. One was in the old code for SVG Err codemadness.org 70 i 376 filter effects that was written in C; <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/195">it never supported big-endian</a>. The Err codemadness.org 70 i 377 initial port to Rust inherited the same bug (think of a line-by-line Err codemadness.org 70 i 378 port, althought it wasn't exactly like that), but it got fixed when my Err codemadness.org 70 i 379 Summer of Code intern Ivan Molodetskikh refactored the code to have a Err codemadness.org 70 i 380 <code>Pixel</code> abstraction that works for little-endian and big-endian, and Err codemadness.org 70 i 381 wraps Cairo's funky requirements.</p> Err codemadness.org 70 i 382 <p>The other endian-related bug in librsvg was when <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/328">computing Err codemadness.org 70 i 383 masks</a>. Again, a little refactoring with that <code>Pixel</code> Err codemadness.org 70 i 384 abstraction fixed it.</p> Err codemadness.org 70 i 385 <p>I knew that the original C code for SVG filter effects didn't work on Err codemadness.org 70 i 386 big-endian. But even back then, at Suse we never got Err codemadness.org 70 i 387 reports of it producing incorrect results on the s390x... maybe people don't use Err codemadness.org 70 i 388 their mainframes to run <code>rsvg-convert</code>? I was hoping that the port to Err codemadness.org 70 i 389 Rust of that code would automatically fix that bug, and it kind of Err codemadness.org 70 i 390 happened that way through Ivan's careful work.</p> Err codemadness.org 70 i 391 <p>And the code for masks? There were two bugs reported with that same Err codemadness.org 70 i 392 root cause: <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/328">one from Err codemadness.org 70 i 393 Debian</a> as a Err codemadness.org 70 i 394 failure in librsvg's test suite (yay, it caught that bug!), and one Err codemadness.org 70 i 395 from <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/322">someone running an Apple PowerBook Err codemadness.org 70 i 396 G4</a> with a MATE Err codemadness.org 70 i 397 desktop and seeing incorrectly-rendered SVG icons.</p> Err codemadness.org 70 i 398 <p>And you know what? I am <strong>delighted</strong> to see people trying to keep Err codemadness.org 70 i 399 those lovely machines alive. A laptop that doesn't get warm enough to Err codemadness.org 70 i 400 burn your thighs, what a concept. A perfectly serviceable 32-bit Err codemadness.org 70 i 401 laptop with a maximum of about 1 GB of RAM and a 40 GB hard drive (it Err codemadness.org 70 i 402 didn't have HDMI!)... But you know, it's the same kind of delight I Err codemadness.org 70 i 403 feel when people talk about doing film photography on a Err codemadness.org 70 i 404 <a href="https://en.wikipedia.org/wiki/Rollei_35">Rollei 35</a>. A lot of Err codemadness.org 70 i 405 nostalgia for hardware of days past, and a lot of mixed feelings about Err codemadness.org 70 i 406 not throwing out working things and creating more trash.</p> Err codemadness.org 70 i 407 <p>As a graphics programmer I feel the responsibility to write code that Err codemadness.org 70 i 408 works on little-endian and big-endian, but you know, it's not exactly Err codemadness.org 70 i 409 an everyday concern anymore. The last big-endian machine I used on an Err codemadness.org 70 i 410 everyday basis was the SPARCs in the university, more than 20 years Err codemadness.org 70 i 411 ago.</p> Err codemadness.org 70 i 412 <h3>Who gets paid to fix this?</h3> Err codemadness.org 70 i 413 <p>That's the question. Suse got paid to support Firefox on the s390x; I Err codemadness.org 70 i 414 suppose IBM has an interest in fixing LLVM there; both actually have Err codemadness.org 70 i 415 people and hardware and money to that effect.</p> Err codemadness.org 70 i 416 <p>Within Suse, I am by default responsible for keeping librsvg Err codemadness.org 70 i 417 working for the s390x as well — it gets built as part of the distro, Err codemadness.org 70 i 418 after all. I have never gotten an endianness bug report from the Suse Err codemadness.org 70 i 419 side of things.</p> Err codemadness.org 70 i 420 <p>Which leads me to suspect that, probably similar to Debian and Gentoo, Err codemadness.org 70 i 421 we <em>build</em> a lot of software because it's in the build chain, but we Err codemadness.org 70 i 422 don't <em>run</em> it to its fullest extent. Do people run GNOME desktops on Err codemadness.org 70 i 423 s390x virtual machines? Maybe? Did they not notice endianness bugs Err codemadness.org 70 i 424 <strong>because they were not in the code path that most GNOME icons Err codemadness.org 70 i 425 actually use</strong>? Who knows!</p> Err codemadness.org 70 i 426 <p>I'm thankful to Simon from the Debian bug for pointing out Err codemadness.org 70 i 427 the failure in librsvg's test case for masks, and to Mingcong for Err codemadness.org 70 i 428 actually showing a screenshot of a MATE desktop running on a PPC Err codemadness.org 70 i 429 PowerBook. Those were useful things for them to do.</p> Err codemadness.org 70 i 430 <p>Also — they were kind about it. It was a pleasure to interact with them.</p>Do not use librsvg 2.40.x2020-11-27T10:28:46-06:002020-11-27T10:28:46-06:00Federico Mena Quinterotag:people.gnome.org,2020-11-27:/~federico/blog/do-not-use-librsvg-2.40.x.html<p>Please do not use librsvg 2.40.x; <strong>it cannot render recent Adwaita icon themes correctly</strong>.</p> Err codemadness.org 70 i 431 <p>The librsvg 2.40.x series is the last "C only" version of the library; Err codemadness.org 70 i 432 it was deprecated in 2017.</p> Err codemadness.org 70 i 433 <p>During the port to Rust, I rewrote the path parser to be Err codemadness.org 70 i 434 spec-compliant, and …</p><p>Please do not use librsvg 2.40.x; <strong>it cannot render recent Adwaita icon themes correctly</strong>.</p> Err codemadness.org 70 i 435 <p>The librsvg 2.40.x series is the last "C only" version of the library; Err codemadness.org 70 i 436 it was deprecated in 2017.</p> Err codemadness.org 70 i 437 <p>During the port to Rust, I rewrote the path parser to be Err codemadness.org 70 i 438 spec-compliant, and fixed a few cases that the C version did not Err codemadness.org 70 i 439 handle. One of this cases is for compact Arc data.</p> Err codemadness.org 70 i 440 <p>The <a href="https://www.w3.org/TR/SVG11/paths.html#PathDataBNF">SVG path grammar</a> allows Err codemadness.org 70 i 441 one to remove whitespace between numbers if the next number starts Err codemadness.org 70 i 442 with a sign. For example, <code>23-45</code> gets parsed as two numbers <code>23 Err codemadness.org 70 i 443 -45</code>.</p> Err codemadness.org 70 i 444 <p>In addition, the arguments of the Arc commands have two flags in the Err codemadness.org 70 i 445 middle of a bunch of numbers. The flags can be <code>0</code> or <code>1</code>, and there Err codemadness.org 70 i 446 may be no whitespace between the flags and the next number. For Err codemadness.org 70 i 447 example, <code>A1.98 1.98 0 0015 13.96</code> gets parsed as <code>A1.98 1.98 0 0 0 15 Err codemadness.org 70 i 448 13.96</code> — note the two <code>0 0</code> flags before the <code>15</code>.</p> Err codemadness.org 70 i 449 <p>Librsvg 2.40.x does not parse this correctly. Err codemadness.org 70 i 450 Adwaita-icon-theme-3.36, and possibly earlier versions, uses minimized Err codemadness.org 70 i 451 SVG files with compressed whitespace, and will not render correctly Err codemadness.org 70 i 452 with the C-only version of librsvg.</p> Err codemadness.org 70 i 453 <p>This is <code>help-contents-symbolic.svg</code> rendered with librsvg 2.40.21:</p> Err codemadness.org 70 i 454 <p><img alt="icon rendered incorrectly" src="https://people.gnome.org/~federico/blog/images/help-contents-symbolic-2.40.21.png"></p> Err codemadness.org 70 i 455 <p>And this is <code>help-contents-symbolic.svg</code> rendered with librsvg 2.50.2:</p> Err codemadness.org 70 i 456 <p><img alt="icon rendered correctly" src="https://people.gnome.org/~federico/blog/images/help-contents-symbolic-2.50.2.png"></p> Err codemadness.org 70 i 457 <p>This is not the only icon with compact Arc commands; there are many Err codemadness.org 70 i 458 others that will also be mis-rendered in 2.40.x.</p> Err codemadness.org 70 i 459 <p>I don't know when Adwaita started using SVGs with compressed Err codemadness.org 70 i 460 whitespace; probably it didn't when librsvg 2.40.x was the latest Err codemadness.org 70 i 461 version, or everyone would have noticed mis-rendered icons.</p> Err codemadness.org 70 i 462 <p><strong>Background:</strong> Someone recently filed a Err codemadness.org 70 i 463 <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/654">bug</a> about Err codemadness.org 70 i 464 memory unsafety in librsvg 2.40.x's path parser, which mysteriously Err codemadness.org 70 i 465 enough only manifests itself in big-endian platforms. I wouldn't be Err codemadness.org 70 i 466 surprised if this had latent bugs on little-endian as well.</p> Err codemadness.org 70 i 467 <p>Please use at least librsvg 2.48.x; any earlier versions are not Err codemadness.org 70 i 468 supported. Generally I keep an eye on the last two stable release Err codemadness.org 70 i 469 sets (2.48.x and 2.50.x as of this writing), but only commit fixes to Err codemadness.org 70 i 470 the latest stable series (2.50.x currently).</p>Librsvg's test suite is now in Rust2020-10-26T10:38:12-06:002020-10-26T10:38:12-06:00Federico Mena Quinterotag:people.gnome.org,2020-10-26:/~federico/blog/librsvg-test-suite-is-now-in-rust.html<p>Some important changes are afoot in librsvg.</p> Err codemadness.org 70 i 471 <h2>Changes to continuous integration</h2> Err codemadness.org 70 i 472 <p>Some days ago, <a href="https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/398">Dunja Lalic rewrote the continuous integration Err codemadness.org 70 i 473 scripts</a> to be much faster. A complete pipeline used to take Err codemadness.org 70 i 474 about 90 minutes to run, now it takes about 15 minutes on average.</p> Err codemadness.org 70 i 475 <p><img alt="Graph with pipeline timings, which shrink drastically" src="https://people.gnome.org/~federico/blog/images/librsvg-fast-ci.png" title="Guess when the CI changed"></p> Err codemadness.org 70 i 476 <p>The <a href="https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/398">description of changes</a> is interesting …</p><p>Some important changes are afoot in librsvg.</p> Err codemadness.org 70 i 477 <h2>Changes to continuous integration</h2> Err codemadness.org 70 i 478 <p>Some days ago, <a href="https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/398">Dunja Lalic rewrote the continuous integration Err codemadness.org 70 i 479 scripts</a> to be much faster. A complete pipeline used to take Err codemadness.org 70 i 480 about 90 minutes to run, now it takes about 15 minutes on average.</p> Err codemadness.org 70 i 481 <p><img alt="Graph with pipeline timings, which shrink drastically" src="https://people.gnome.org/~federico/blog/images/librsvg-fast-ci.png" title="Guess when the CI changed"></p> Err codemadness.org 70 i 482 <p>The <a href="https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/398">description of changes</a> is interesting. The idea is to make tests fail as fast as possible, close to the beginning of the pipeline. To speed up the whole pipeline, Dunja did the following:</p> Err codemadness.org 70 i 483 <ul> Err codemadness.org 70 i 484 <li> Err codemadness.org 70 i 485 <p>Move the <code>cargo check</code> stage to the beginning. This test means, Err codemadness.org 70 i 486 "does this even have a chance of compiling?".</p> Err codemadness.org 70 i 487 </li> Err codemadness.org 70 i 488 <li> Err codemadness.org 70 i 489 <p>Have the code style and formatting tests, <code>cargo clippy</code> and <code>cargo Err codemadness.org 70 i 490 fmt</code>, run in parallel with the unit tests. These lints can fail, Err codemadness.org 70 i 491 but they are easy to fix after one is finished modifying the main Err codemadness.org 70 i 492 code.</p> Err codemadness.org 70 i 493 </li> Err codemadness.org 70 i 494 <li> Err codemadness.org 70 i 495 <p>Run the unit tests and the smoke tests in debug mode, so they compile Err codemadness.org 70 i 496 quickly.</p> Err codemadness.org 70 i 497 </li> Err codemadness.org 70 i 498 <li> Err codemadness.org 70 i 499 <p>Run the complete integration test suite in release mode. This takes Err codemadness.org 70 i 500 longer to compile, but there are some slow tests that benefit a lot Err codemadness.org 70 i 501 from faster execution.</p> Err codemadness.org 70 i 502 </li> Err codemadness.org 70 i 503 <li> Err codemadness.org 70 i 504 <p>Move the release tests until the end, and only run them once a week Err codemadness.org 70 i 505 — or whenever, by hand. These take a good amount of time to run, Err codemadness.org 70 i 506 because they do a full <code>make distcheck</code> and autotools is slow. Even Err codemadness.org 70 i 507 then, now these tests take 30-40 minutes, instead of the 90 from Err codemadness.org 70 i 508 before.</p> Err codemadness.org 70 i 509 </li> Err codemadness.org 70 i 510 <li> Err codemadness.org 70 i 511 <p>Between each stage of the pipeline, don't cache what doesn't help Err codemadness.org 70 i 512 reduce compilation time. It seems that keeping around a big cache, Err codemadness.org 70 i 513 with the whole build <code>target</code>, between each pipeline stage can be Err codemadness.org 70 i 514 worse than not having one at all.</p> Err codemadness.org 70 i 515 </li> Err codemadness.org 70 i 516 </ul> Err codemadness.org 70 i 517 <p><img alt="Complete pipeline with all the stages" src="https://people.gnome.org/~federico/blog/images/librsvg-complete-pipeline.png"></p> Err codemadness.org 70 i 518 <h2>Test suite in Rust</h2> Err codemadness.org 70 i 519 <p>Beteen Sven Neumann, Dunja Lalic, and myself we have finally <a href="https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/408">ported Err codemadness.org 70 i 520 the test suite to Rust</a>: all of librsvg's tests are Err codemadness.org 70 i 521 now in Rust, except for the C API tests. We had to do a few things:</p> Err codemadness.org 70 i 522 <ul> Err codemadness.org 70 i 523 <li> Err codemadness.org 70 i 524 <p>Review the old tests and remove some obsolete ones.</p> Err codemadness.org 70 i 525 </li> Err codemadness.org 70 i 526 <li> Err codemadness.org 70 i 527 <p>Port each of the test modules to Rust. They are small, but each one Err codemadness.org 70 i 528 has special little things — test for crashes in the XML loading Err codemadness.org 70 i 529 code, test for crashes during rendering, test the library's security Err codemadness.org 70 i 530 limits.</p> Err codemadness.org 70 i 531 </li> Err codemadness.org 70 i 532 <li> Err codemadness.org 70 i 533 <p>Fix the small tests that come as part of the documentation.</p> Err codemadness.org 70 i 534 </li> Err codemadness.org 70 i 535 <li> Err codemadness.org 70 i 536 <p>Untangle the reference tests and port them to Rust.</p> Err codemadness.org 70 i 537 </li> Err codemadness.org 70 i 538 <li> Err codemadness.org 70 i 539 <p>Move little chunks of code around so the unit tests and integration Err codemadness.org 70 i 540 tests can share utilities to compare images, compute file paths for Err codemadness.org 70 i 541 test fixtures, etc.</p> Err codemadness.org 70 i 542 </li> Err codemadness.org 70 i 543 </ul> Err codemadness.org 70 i 544 <p>The most complicated thing to port was the reference tests. These are Err codemadness.org 70 i 545 the most important ones; each test loads an SVG document, renders it, Err codemadness.org 70 i 546 and compares the result to a reference PNG image. There are some Err codemadness.org 70 i 547 complications in the tests; they have to create a special Err codemadness.org 70 i 548 configuration for Fontconfig and Pango, so as to have reproducible Err codemadness.org 70 i 549 font rendering. The pango-rs bindings do not cover this part of Err codemadness.org 70 i 550 Pango, so we had to do some things by hand.</p> Err codemadness.org 70 i 551 <p>Anyway, <a href="https://gitlab.gnome.org/GNOME/librsvg/-/tree/master/tests/src">the tests are now in Rust</a>. One nice thing is that Err codemadness.org 70 i 552 now the tests run automatically in parallel, across all CPU cores, so Err codemadness.org 70 i 553 we save on total testing time.</p> Err codemadness.org 70 i 554 <h2>What's next: cargo-c and publish to crates.io</h2> Err codemadness.org 70 i 555 <p>We want to be able to <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/635">publish librsvg in crates.io</a> as a Err codemadness.org 70 i 556 normal crate; this implies being able to compile, test, and publish Err codemadness.org 70 i 557 entirely from Cargo. The compilation and testing part is done.</p> Err codemadness.org 70 i 558 <p>Now, we have to reorganize the code so it can be published to Err codemadness.org 70 i 559 crates.io. Librsvg comes in three parts, <code>rsvg_internals</code> with the Err codemadness.org 70 i 560 implementation of the library, <code>librsvg</code> with the traditional C API, Err codemadness.org 70 i 561 and <code>librsvg_crate</code> with the Rust API. However, to publish the Rust Err codemadness.org 70 i 562 API to crates.io, it would be more convenient to have a single crate Err codemadness.org 70 i 563 instead of one with the internals and one with the API.</p> Err codemadness.org 70 i 564 <p>The next step is thus to reorganize the code:</p> Err codemadness.org 70 i 565 <ul> Err codemadness.org 70 i 566 <li> Err codemadness.org 70 i 567 <p>Make it possible to implement the C API as a compile-time option on Err codemadness.org 70 i 568 top of the normal Rust code. We want to <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/552">use cargo-c</a> to Err codemadness.org 70 i 569 compile the traditional shared library <code>librsvg.so</code>, instead of Err codemadness.org 70 i 570 depending on C tools for compiling and linking.</p> Err codemadness.org 70 i 571 </li> Err codemadness.org 70 i 572 <li> Err codemadness.org 70 i 573 <p>Combine <code>rsvg_internals</code> and <code>librsvg_crate</code> in a single crate, to Err codemadness.org 70 i 574 publish them together. Crates.io has a 10 MB limit per crate; now Err codemadness.org 70 i 575 that the test suite lives in a separate <code>tests</code> crate, this Err codemadness.org 70 i 576 shouldn't be a problem.</p> Err codemadness.org 70 i 577 </li> Err codemadness.org 70 i 578 <li> Err codemadness.org 70 i 579 <p>I would like to <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/597">polish the public error types</a> before Err codemadness.org 70 i 580 publishing the Rust API; right now they expose some implementation Err codemadness.org 70 i 581 details that are of no interest to callers of the library.</p> Err codemadness.org 70 i 582 </li> Err codemadness.org 70 i 583 </ul> Err codemadness.org 70 i 584 <h2>What remains to be ported to Rust?</h2> Err codemadness.org 70 i 585 <p>Only two things, which amount to less than 900 lines of C code:</p> Err codemadness.org 70 i 586 <ul> Err codemadness.org 70 i 587 <li> Err codemadness.org 70 i 588 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/534">rsvg-convert</a> - the command-line program that everyone uses to Err codemadness.org 70 i 589 convert SVG to PNG and other formats. Fortunately, Sven Neumann Err codemadness.org 70 i 590 wrote some <a href="https://gitlab.gnome.org/GNOME/librsvg/-/blob/master/tests/src/cmdline/rsvg_convert.rs">fantastic tests</a> for rsvg-convert, Err codemadness.org 70 i 591 as it is like an API that we need to keep stable: if we change the Err codemadness.org 70 i 592 command-line options or the program's behavior, we would break Err codemadness.org 70 i 593 everyone's scripts.</p> Err codemadness.org 70 i 594 </li> Err codemadness.org 70 i 595 <li> Err codemadness.org 70 i 596 <p>The gdk-pixbuf module for loading SVG. Alberto Ruiz <a href="https://gitlab.gnome.org/GNOME/librsvg/-/tree/wip/aruiz/rust-pixbuf-loader">has started Err codemadness.org 70 i 597 porting it to Rust</a>. The generic part of this code Err codemadness.org 70 i 598 could later serve to wrap other Rust image codecs and plug them to Err codemadness.org 70 i 599 gdk-pixbuf.</p> Err codemadness.org 70 i 600 </li> Err codemadness.org 70 i 601 </ul>Librsvg is accepting interns for Outreachy's December 2020 round2020-10-05T09:52:27-05:002020-10-05T09:52:27-05:00Federico Mena Quinterotag:people.gnome.org,2020-10-05:/~federico/blog/librsvg-accepting-outreachy-interns-2020.html<p>There are two projects in librsvg available for <a href="https://www.outreachy.org">Outreachy</a> applicants Err codemadness.org 70 i 602 in the December 2020 / March 2021 round:</p> Err codemadness.org 70 i 603 <ul> Err codemadness.org 70 i 604 <li> Err codemadness.org 70 i 605 <p><strong>Revamp the text engine</strong> - Do you know about international text Err codemadness.org 70 i 606 layout? Can you read a right-to-left language, or do you write in a Err codemadness.org 70 i 607 language that requires complex shaping? Would you like to implement …</p></li></ul><p>There are two projects in librsvg available for <a href="https://www.outreachy.org">Outreachy</a> applicants Err codemadness.org 70 i 608 in the December 2020 / March 2021 round:</p> Err codemadness.org 70 i 609 <ul> Err codemadness.org 70 i 610 <li> Err codemadness.org 70 i 611 <p><strong>Revamp the text engine</strong> - Do you know about international text Err codemadness.org 70 i 612 layout? Can you read a right-to-left language, or do you write in a Err codemadness.org 70 i 613 language that requires complex shaping? Would you like to implement Err codemadness.org 70 i 614 the <a href="https://www.w3.org/TR/SVG2/text.html">SVG 2 text specification</a> in a <a href="https://gitlab.gnome.org/GNOME/librsvg/-/blob/master/rsvg_internals/src/text.rs">pleasant Rust code Err codemadness.org 70 i 615 base</a>? This project requires someone who can write Rust Err codemadness.org 70 i 616 comfortably; it will require reading and refactoring some Err codemadness.org 70 i 617 existing code. You don't need to be an expert in exotic lifetimes Err codemadness.org 70 i 618 and trait bounds and such; the code doesn't use them.</p> Err codemadness.org 70 i 619 </li> Err codemadness.org 70 i 620 <li> Err codemadness.org 70 i 621 <p><strong>Implement SVG2/CSS3 features</strong> - Are you excited by all the <a href="https://www.w3.org/TR/SVG2/changes.html">SVG2 Err codemadness.org 70 i 622 features</a> in Inkscape, and would like to add support for them Err codemadness.org 70 i 623 in librsvg? Would you like to do small changes to many parts of the Err codemadness.org 70 i 624 code to implement small features, one at a time? Do you like Err codemadness.org 70 i 625 test-driven development? This project requires someone who can Err codemadness.org 70 i 626 write Rust code at a medium level; you'll learn a lot by Err codemadness.org 70 i 627 cutting&amp;pasting from existing code and refactoring things to Err codemadness.org 70 i 628 implement SVG2 features.</p> Err codemadness.org 70 i 629 </li> Err codemadness.org 70 i 630 </ul> Err codemadness.org 70 i 631 <p><strong>Important:</strong> Outreachy's December 2020 / March 2021 round is available <a href="https://www.outreachy.org/docs/applicant/#eligibility">only Err codemadness.org 70 i 632 for students in the Southern hemisphere</a>. People in the Err codemadness.org 70 i 633 Northern hemisphere can wait until the 2021 mid-year round.</p> Err codemadness.org 70 i 634 <p>You can see <a href="https://www.outreachy.org/apply/project-selection/#gnome">GNOME's projects in Outreachy</a> for this round. Err codemadness.org 70 i 635 <strong>The deadline for initial contributions and project applications is Err codemadness.org 70 i 636 October 31, 2020 at 16:00 UTC.</strong></p>"Rust does not have a stable ABI"2020-08-12T22:01:44-05:002020-08-12T22:01:44-05:00Federico Mena Quinterotag:people.gnome.org,2020-08-12:/~federico/blog/rust-stable-abi.html<p>I've seen GNOME people (often, people who have been working for a long Err codemadness.org 70 i 637 time on C libraries) express concerns along the following lines:</p> Err codemadness.org 70 i 638 <ol> Err codemadness.org 70 i 639 <li>Compiled Rust code doesn't have a stable ABI (application binary interface).</li> Err codemadness.org 70 i 640 <li>So, we can't have shared libraries in the traditional fashion of Err codemadness.org 70 i 641 Linux distributions.</li> Err codemadness.org 70 i 642 <li>Also Rust bundles …</li></ol><p>I've seen GNOME people (often, people who have been working for a long Err codemadness.org 70 i 643 time on C libraries) express concerns along the following lines:</p> Err codemadness.org 70 i 644 <ol> Err codemadness.org 70 i 645 <li>Compiled Rust code doesn't have a stable ABI (application binary interface).</li> Err codemadness.org 70 i 646 <li>So, we can't have shared libraries in the traditional fashion of Err codemadness.org 70 i 647 Linux distributions.</li> Err codemadness.org 70 i 648 <li>Also Rust bundles its entire standard library with every binary it compiles, which makes Rust-built libraries huge.</li> Err codemadness.org 70 i 649 </ol> Err codemadness.org 70 i 650 <p>These are extremely valid concerns to be addressed by people like Err codemadness.org 70 i 651 myself who propose that chunks of infrastructural libraries Err codemadness.org 70 i 652 should be done in Rust.</p> Err codemadness.org 70 i 653 <p>So, let's begin.</p> Err codemadness.org 70 i 654 <p>The first part of this article is a super-quick introduction to shared Err codemadness.org 70 i 655 libraries and how Linux distributions use them. If you already know Err codemadness.org 70 i 656 those things, feel free to skip to the "<a href="#rust_does_not_have_a_stable_abi">Rust does not have a stable Err codemadness.org 70 i 657 ABI</a>" section.</p> Err codemadness.org 70 i 658 <h2>How do distributions use shared libraries?</h2> Err codemadness.org 70 i 659 <p>If several programs run at the same time and use the same shared library Err codemadness.org 70 i 660 (say, <code>libgtk-3.so</code>), the operating system can load a single copy of Err codemadness.org 70 i 661 the library in memory and share the read-only parts of the code/data Err codemadness.org 70 i 662 through the magic of virtual memory.</p> Err codemadness.org 70 i 663 <p><em>In theory</em>, if a library gets a bugfix but does not change its Err codemadness.org 70 i 664 interface, one can just recompile the library, stick the new <code>.so</code> in Err codemadness.org 70 i 665 <code>/usr/lib</code> or whatever, and be done with it. Programs that depend on Err codemadness.org 70 i 666 the library do not need to be recompiled.</p> Err codemadness.org 70 i 667 <p>If libraries limit their public interface to a plain C ABI Err codemadness.org 70 i 668 (application binary interface), they are relatively easy to consume Err codemadness.org 70 i 669 from other programming languages. Those languages don't have to deal Err codemadness.org 70 i 670 with name mangling of C++ symbols, exception handlers, constructors, Err codemadness.org 70 i 671 and all that complexity. Pretty much every language has some form of Err codemadness.org 70 i 672 C FFI (foreign function interface), which roughly means "call C Err codemadness.org 70 i 673 functions without too much trouble".</p> Err codemadness.org 70 i 674 <p>For the purposes of a library, what's an Err codemadness.org 70 i 675 <a href="https://en.wikipedia.org/wiki/Application_binary_interface">ABI</a>? Err codemadness.org 70 i 676 Wikipedia says, "An ABI defines how data structures or computational Err codemadness.org 70 i 677 routines are accessed in machine code [...] A common aspect of an Err codemadness.org 70 i 678 ABI is the calling convention", which means that to call a function in Err codemadness.org 70 i 679 machine code you need to frob the call and stack pointers, pass some Err codemadness.org 70 i 680 function arguments in registers or push some others to the stack, etc. Err codemadness.org 70 i 681 Really low-level stuff. Each machine architecture or operating system Err codemadness.org 70 i 682 usually defines a C standard ABI.</p> Err codemadness.org 70 i 683 <p>For libraries, we commonly understand an ABI to mean the machine-code Err codemadness.org 70 i 684 implications of their programming interface. Which functions are Err codemadness.org 70 i 685 available as public symbols in the <code>.so</code> file? To which numeric Err codemadness.org 70 i 686 values do C enum values correspond, so that they can be passed to Err codemadness.org 70 i 687 those functions? What is the exact order and type of arguments that Err codemadness.org 70 i 688 the functions take? What are the struct sizes, and the order and Err codemadness.org 70 i 689 types and padding of the fields that those functions take? Does one Err codemadness.org 70 i 690 pass arguments in CPU registers or on the stack? Does the caller or Err codemadness.org 70 i 691 the callee clean up the stack after a function call?</p> Err codemadness.org 70 i 692 <h2>Bug fixes and security fixes</h2> Err codemadness.org 70 i 693 <p>Linux distributions generally try <em>really hard</em> to have a single Err codemadness.org 70 i 694 version of each shared library installed in the system: a single Err codemadness.org 70 i 695 <code>libjpeg.so</code>, a single <code>libpng.so</code>, a single <code>libc.so</code>, etc.</p> Err codemadness.org 70 i 696 <p>This is helpful when there needs to be an update to fix a bug, Err codemadness.org 70 i 697 security-related or not: users can just download the updated package Err codemadness.org 70 i 698 for the library, which when installed will just stick in a new <code>.so</code> Err codemadness.org 70 i 699 in the right place, and the calling software won't need to be updated.</p> Err codemadness.org 70 i 700 <p>This is possible only if the bug <em>really only changes the internal Err codemadness.org 70 i 701 code</em> without changing behavior or interface. If a bug fix requires Err codemadness.org 70 i 702 part of the public API or ABI to change, then you are screwed; all Err codemadness.org 70 i 703 calling software needs to be recompiled. "Irresponsible" library Err codemadness.org 70 i 704 authors either learn really fast when distros complain loudly about Err codemadness.org 70 i 705 this sort of change, or they don't learn and get forever marked by Err codemadness.org 70 i 706 distros as "that irresponsible library" which always requires special Err codemadness.org 70 i 707 handling in order not to break other software.</p> Err codemadness.org 70 i 708 <p>Sidenote: sometimes it's more complicated. Poppler (the PDF Err codemadness.org 70 i 709 rendering library) ships at least two stable APIs, one Glib-based in Err codemadness.org 70 i 710 C, and one Qt-based in C++. However, some software like texlive uses Err codemadness.org 70 i 711 Poppler's internals library directly, which of course does not have a Err codemadness.org 70 i 712 stable API, and thus texlive breaks frequently as Poppler evolves. Err codemadness.org 70 i 713 Someone should extend the public, stable API so that texlive doesn't Err codemadness.org 70 i 714 have to use the library's internals!</p> Err codemadness.org 70 i 715 <h2>Bundled libraries</h2> Err codemadness.org 70 i 716 <p>Sometimes it is not irresponsible authors of libraries, but rather Err codemadness.org 70 i 717 that people who use the libraries find out that over time the behavior Err codemadness.org 70 i 718 of the library changes subtly, maybe without breaking the API or ABI, Err codemadness.org 70 i 719 and they are better off bundling a specific version of the library Err codemadness.org 70 i 720 with their software. That version is what they test their software Err codemadness.org 70 i 721 against, and they try to learn its quirks.</p> Err codemadness.org 70 i 722 <p>Distros inevitably complain about this, and either patch the calling Err codemadness.org 70 i 723 software by hand to force it to use the system's shared library, or Err codemadness.org 70 i 724 succeed in getting patches accepted by the software so that they have Err codemadness.org 70 i 725 a <code>--use-system-libjpeg</code> option or similar.</p> Err codemadness.org 70 i 726 <p>This doesn't work very well if the bundled version of the library has Err codemadness.org 70 i 727 extra patches that are not in a distro's usual patches. Or Err codemadness.org 70 i 728 vice-versa; it may actually work better to use the distro's version of Err codemadness.org 70 i 729 the library, if it has extra fixes that the bundled library doesn't. Err codemadness.org 70 i 730 Who knows! It's a case-by-case situation.</p> Err codemadness.org 70 i 731 <h2 id="rust_does_not_have_a_stable_abi">Rust does not have a stable ABI</h2> Err codemadness.org 70 i 732 <p>By default indeed it doesn't, because the compiler team wants to have Err codemadness.org 70 i 733 the freedom to change the data layout and Rust-to-Rust calling Err codemadness.org 70 i 734 conventions, often for performance reasons, at any time. For example, Err codemadness.org 70 i 735 it is not guaranteed that struct fields will be laid out in memory in Err codemadness.org 70 i 736 the same order as they are written in the code:</p> Err codemadness.org 70 i 737 <div class="highlight"><pre><span></span><code><span class="k">struct</span> <span class="nc">Foo</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 738 <span class="w"> </span><span class="n">bar</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 739 <span class="w"> </span><span class="n">baz</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 740 <span class="w"> </span><span class="n">beep</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 741 <span class="w"> </span><span class="n">qux</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 742 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 743 </code></pre></div> Err codemadness.org 70 i 744 Err codemadness.org 70 i 745 <p>The compiler is free to rearrange the struct fields in memory as it Err codemadness.org 70 i 746 sees fit. Maybe it decides to put the two <code>bool</code> fields next to each Err codemadness.org 70 i 747 other to save on inter-field padding due to alignment requirements; Err codemadness.org 70 i 748 maybe it does static analysis or profile-guided optimizations and Err codemadness.org 70 i 749 picks an optmal ordering.</p> Err codemadness.org 70 i 750 <p>But we can override this! Let's look at data layout first, and then Err codemadness.org 70 i 751 calling conventions.</p> Err codemadness.org 70 i 752 <h3>Data layout for C versus Rust</h3> Err codemadness.org 70 i 753 <p>The following is the same struct as above, but with an extra <code>#[repr(C)]</code> attribute:</p> Err codemadness.org 70 i 754 <div class="highlight"><pre><span></span><code><span class="cp">#[repr(C)]</span><span class="w"></span> Err codemadness.org 70 i 755 <span class="k">struct</span> <span class="nc">Foo</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 756 <span class="w"> </span><span class="n">bar</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 757 <span class="w"> </span><span class="n">baz</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 758 <span class="w"> </span><span class="n">beep</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 759 <span class="w"> </span><span class="n">qux</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 760 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 761 </code></pre></div> Err codemadness.org 70 i 762 Err codemadness.org 70 i 763 <p>With that attribute, the struct will be laid out just as this C struct:</p> Err codemadness.org 70 i 764 <div class="highlight"><pre><span></span><code><span class="cp">#include</span> <span class="cpf">&lt;stdbool.h&gt;</span><span class="cp"></span> Err codemadness.org 70 i 765 <span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp"></span> Err codemadness.org 70 i 766 Err codemadness.org 70 i 767 <span class="k">struct</span> <span class="nc">Foo</span> <span class="p">{</span> Err codemadness.org 70 i 768 <span class="kt">bool</span> <span class="n">bar</span><span class="p">;</span> Err codemadness.org 70 i 769 <span class="kt">double</span> <span class="n">baz</span><span class="p">;</span> Err codemadness.org 70 i 770 <span class="kt">bool</span> <span class="n">beep</span><span class="p">;</span> Err codemadness.org 70 i 771 <span class="kt">int32_t</span> <span class="n">qux</span><span class="p">;</span> Err codemadness.org 70 i 772 <span class="p">}</span> Err codemadness.org 70 i 773 </code></pre></div> Err codemadness.org 70 i 774 Err codemadness.org 70 i 775 <p>(Aside: it is unfortunate that <a href="https://people.gnome.org/~federico/news-2017-04.html#gboolean-is-not-rust-bool"><code>gboolean</code> is not <code>bool</code></a>, Err codemadness.org 70 i 776 but that's because <code>gboolean</code> predates C99, and clearly standards from Err codemadness.org 70 i 777 20 years ago are <em>too new</em> to use. (Aside aside: since I wrote that Err codemadness.org 70 i 778 other post, Rust's repr(C) for bool is actually defined as C99's bool; Err codemadness.org 70 i 779 it's no longer undefined.))</p> Err codemadness.org 70 i 780 <p>Even Rust's data-carrying enums can be laid out in a manner friendly Err codemadness.org 70 i 781 to C and C++:</p> Err codemadness.org 70 i 782 <div class="highlight"><pre><span></span><code><span class="cp">#[repr(C, u8)]</span><span class="w"></span> Err codemadness.org 70 i 783 <span class="k">enum</span> <span class="nc">MyEnum</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 784 <span class="w"> </span><span class="n">A</span><span class="p">(</span><span class="kt">u32</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 785 <span class="w"> </span><span class="n">B</span><span class="p">(</span><span class="kt">f32</span><span class="p">,</span><span class="w"> </span><span class="kt">bool</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 786 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 787 </code></pre></div> Err codemadness.org 70 i 788 Err codemadness.org 70 i 789 <p>This means, use C layout, and a <code>u8</code> for the enum's discriminant. It Err codemadness.org 70 i 790 will be laid out like this:</p> Err codemadness.org 70 i 791 <div class="highlight"><pre><span></span><code><span class="cp">#include</span> <span class="cpf">&lt;stdbool.h&gt;</span><span class="cp"></span> Err codemadness.org 70 i 792 <span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp"></span> Err codemadness.org 70 i 793 Err codemadness.org 70 i 794 <span class="k">enum</span> <span class="n">MyEnumTag</span> <span class="p">{</span> Err codemadness.org 70 i 795 <span class="n">A</span><span class="p">,</span> Err codemadness.org 70 i 796 <span class="n">B</span> Err codemadness.org 70 i 797 <span class="p">};</span> Err codemadness.org 70 i 798 Err codemadness.org 70 i 799 <span class="k">typedef</span> <span class="kt">uint32_t</span> <span class="n">MyEnumPayloadA</span><span class="p">;</span> Err codemadness.org 70 i 800 Err codemadness.org 70 i 801 <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 802 <span class="kt">float</span> <span class="n">x</span><span class="p">;</span> Err codemadness.org 70 i 803 <span class="kt">bool</span> <span class="n">y</span><span class="p">;</span> Err codemadness.org 70 i 804 <span class="p">}</span> <span class="n">MyEnumPayloadB</span><span class="p">;</span> Err codemadness.org 70 i 805 Err codemadness.org 70 i 806 <span class="k">typedef</span> <span class="k">union</span> <span class="p">{</span> Err codemadness.org 70 i 807 <span class="n">MyEnumPayloadA</span> <span class="n">a</span><span class="p">;</span> Err codemadness.org 70 i 808 <span class="n">MyEnumPayloadB</span> <span class="n">b</span><span class="p">;</span> Err codemadness.org 70 i 809 <span class="p">}</span> <span class="n">MyEnumPayload</span><span class="p">;</span> Err codemadness.org 70 i 810 Err codemadness.org 70 i 811 <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 812 <span class="kt">uint8_t</span> <span class="n">tag</span><span class="p">;</span> Err codemadness.org 70 i 813 <span class="n">MyEnumPayload</span> <span class="n">payload</span><span class="p">;</span> Err codemadness.org 70 i 814 <span class="p">}</span> <span class="n">MyEnum</span><span class="p">;</span> Err codemadness.org 70 i 815 </code></pre></div> Err codemadness.org 70 i 816 Err codemadness.org 70 i 817 <p>The gory details of data layout are in the <a href="https://doc.rust-lang.org/nomicon/other-reprs.html">Alternative Representations section of the Err codemadness.org 70 i 818 Rustonomicon</a> and Err codemadness.org 70 i 819 the <a href="https://rust-lang.github.io/unsafe-code-guidelines/introduction.html">Unsafe Code Err codemadness.org 70 i 820 Guidelines</a>.</p> Err codemadness.org 70 i 821 <h3>Calling conventions</h3> Err codemadness.org 70 i 822 <p>An ABI's calling conventions detail things like how to call functions Err codemadness.org 70 i 823 in machine code, and how to lay out function arguments in registers or Err codemadness.org 70 i 824 the stack. <a href="https://en.wikipedia.org/wiki/X86_calling_conventions">The wikipedia page on X86 calling Err codemadness.org 70 i 825 conventions</a> Err codemadness.org 70 i 826 has a good cheat-sheet, useful when you are looking at assembly code Err codemadness.org 70 i 827 and registers in a low-level debugger.</p> Err codemadness.org 70 i 828 <p>I've already written about how it is possible to write Rust code to Err codemadness.org 70 i 829 export functions callable from C; one uses the <code>extern "C"</code> in the Err codemadness.org 70 i 830 function definition and a <code>#[no_mangle]</code> attribute to keep the symbol Err codemadness.org 70 i 831 name pristine. This is how librsvg is able to have the following:</p> Err codemadness.org 70 i 832 <div class="highlight"><pre><span></span><code><span class="cp">#[no_mangle]</span><span class="w"></span> Err codemadness.org 70 i 833 <span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_handle_new_from_file</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 834 <span class="w"> </span><span class="n">filename</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">libc</span>::<span class="n">c_char</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 835 <span class="w"> </span><span class="n">error</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">glib_sys</span>::<span class="n">GError</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 836 <span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">RsvgHandle</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 837 <span class="w"> </span><span class="c1">// ...</span> Err codemadness.org 70 i 838 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 839 </code></pre></div> Err codemadness.org 70 i 840 Err codemadness.org 70 i 841 <p>Which compiles to what a C compiler would produce for this:</p> Err codemadness.org 70 i 842 <div class="highlight"><pre><span></span><code><span class="n">RsvgHandle</span> <span class="o">*</span><span class="nf">rsvg_handle_new_from_file</span> <span class="p">(</span><span class="k">const</span> <span class="n">gchar</span> <span class="o">*</span><span class="n">filename</span><span class="p">,</span> <span class="n">GError</span> <span class="o">**</span><span class="n">error</span><span class="p">);</span> Err codemadness.org 70 i 843 </code></pre></div> Err codemadness.org 70 i 844 Err codemadness.org 70 i 845 <p>(Aside: librsvg <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/416">still uses an intermediate C library full of Err codemadness.org 70 i 846 stubs</a> that just Err codemadness.org 70 i 847 call the Rust-exported functions, but there is now <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/552">tooling to produce a .so Err codemadness.org 70 i 848 directly from Err codemadness.org 70 i 849 Rust</a> which I Err codemadness.org 70 i 850 just haven't had time to investigate. Help is appreciated!)</p> Err codemadness.org 70 i 851 <h3>Summary of ABI so far</h3> Err codemadness.org 70 i 852 <p>It is <em>one's decision</em> to export a stable C ABI from a Rust library. Err codemadness.org 70 i 853 There is some awkwardness in how types are laid out in C, because the Err codemadness.org 70 i 854 Rust type system is richer, but things can be made to work well with a Err codemadness.org 70 i 855 little thought. Certainly no more thought than the burden of Err codemadness.org 70 i 856 designing and maintaining a stable API/ABI in plain C.</p> Err codemadness.org 70 i 857 <p>I'll fold the second concern into here — "we can't have shared Err codemadness.org 70 i 858 libraries in traditional distro fashion". Yes, we can, API/ABI-wise, Err codemadness.org 70 i 859 but read on.</p> Err codemadness.org 70 i 860 <h2>Rust bundles its entire standard library with Rust-built .so's</h2> Err codemadness.org 70 i 861 <p>I.e. it statically links all the Rust dependencies. This produces a Err codemadness.org 70 i 862 large .so:</p> Err codemadness.org 70 i 863 <ul> Err codemadness.org 70 i 864 <li>librsvg-2.so (version 2.40.21, C only) - 1408840 bytes</li> Err codemadness.org 70 i 865 <li>librsvg-2.so (version 2.49.3, Rust only) - 9899120 bytes</li> Err codemadness.org 70 i 866 </ul> Err codemadness.org 70 i 867 <p>Holy crap! What's all that?</p> Err codemadness.org 70 i 868 <p>(And I'm cheating: this is both with link-time optimization turned on, Err codemadness.org 70 i 869 and by running <code>strip(1)</code> on the .so. If you just <code>autogen.sh &amp;&amp; make</code> Err codemadness.org 70 i 870 it will be bigger.)</p> Err codemadness.org 70 i 871 <p>This has Rust's standard library statically linked (or at least the Err codemadness.org 70 i 872 bits of that librsvg actually uses), plus all the Rust dependencies Err codemadness.org 70 i 873 (cssparser, selectors, nalgebra, glib-rs, cairo-rs, locale_config, Err codemadness.org 70 i 874 rayon, xml5ever, and an assload of crates). I could explain why each Err codemadness.org 70 i 875 one is needed:</p> Err codemadness.org 70 i 876 <ul> Err codemadness.org 70 i 877 <li>cssparser - librsvg needs to parse CSS.</li> Err codemadness.org 70 i 878 <li>selectors - librsvg needs to run the CSS selector matching Err codemadness.org 70 i 879 algorithm.</li> Err codemadness.org 70 i 880 <li>nalgebra - the code for SVG filter effects uses vectors and Err codemadness.org 70 i 881 matrices.</li> Err codemadness.org 70 i 882 <li>glib-rs, cairo-rs - draw to Cairo and export GObject types.</li> Err codemadness.org 70 i 883 <li>locale_config - so that localized SVG files can work.</li> Err codemadness.org 70 i 884 <li>rayon - so filters can use all your CPU cores instead of processing Err codemadness.org 70 i 885 one pixel at a time.</li> Err codemadness.org 70 i 886 <li>Etcetera. SVG is big and requires a lot of helper code!</li> Err codemadness.org 70 i 887 </ul> Err codemadness.org 70 i 888 <p>Is this a problem?</p> Err codemadness.org 70 i 889 <p>Or more exactly, why does this happen, and why do people perceive it Err codemadness.org 70 i 890 as a problem?</p> Err codemadness.org 70 i 891 <h3>Stable APIs/ABIs and distros</h3> Err codemadness.org 70 i 892 <p>Many Linux distributions have worked <em>really hard</em> to ensure that Err codemadness.org 70 i 893 there is a single copy of "system libraries" in an installation. Err codemadness.org 70 i 894 There is Just One Copy of <code>/usr/lib/libc.so</code>, <code>/usr/lib/libjpeg.so</code>, Err codemadness.org 70 i 895 etc., and packages are compiled with special options to tell them to Err codemadness.org 70 i 896 really use the sytem libraries instead of their bundled versions, or Err codemadness.org 70 i 897 patched to do so if they don't provide build-time options for that.</p> Err codemadness.org 70 i 898 <p>In a way, this works well for distros:</p> Err codemadness.org 70 i 899 <ul> Err codemadness.org 70 i 900 <li> Err codemadness.org 70 i 901 <p>A bug in a library can be fixed in a single place, and all Err codemadness.org 70 i 902 applications that use it get the fix automatically.</p> Err codemadness.org 70 i 903 </li> Err codemadness.org 70 i 904 <li> Err codemadness.org 70 i 905 <p>A security bug can be patched in a single place, and in theory Err codemadness.org 70 i 906 applications don't need to be audited further.</p> Err codemadness.org 70 i 907 </li> Err codemadness.org 70 i 908 </ul> Err codemadness.org 70 i 909 <p>If you maintain a library that is shipped in Linux distros, and you Err codemadness.org 70 i 910 break the ABI, you'll get complaints from distros very quickly.</p> Err codemadness.org 70 i 911 <p>This is good because it creates responsible maintainers for libraries Err codemadness.org 70 i 912 that can be depended on. It's how Inkscape/GIMP can have a stable Err codemadness.org 70 i 913 toolkit to be written in.</p> Err codemadness.org 70 i 914 <p>This is bad because it encourages stagnation in the long term. It's Err codemadness.org 70 i 915 how we get a horrible, unsafe, error-prone API in libjpeg that can Err codemadness.org 70 i 916 never ever be improved because it would requires changes in tons of Err codemadness.org 70 i 917 software; it's why <code>gboolean</code> is still a 32-bit <code>int</code> after Err codemadness.org 70 i 918 twenty-something years, even though everything else close to C has Err codemadness.org 70 i 919 decided that booleans are 1 byte. It's how Inkscape/GIMP take many Err codemadness.org 70 i 920 years to move from GTK2 to GTK3 (okay, that's lack of paid developers Err codemadness.org 70 i 921 to do the grunt work, but it is enabled by having forever-stable APIs).</p> Err codemadness.org 70 i 922 <p>However, a long-term stable API/ABI has a <strong>lot of value</strong>. It is why Err codemadness.org 70 i 923 the Windows API is the crown jewels; it is why people can rely on glib Err codemadness.org 70 i 924 and glibc to not break their code for many years and take them for granted.</p> Err codemadness.org 70 i 925 <h3>But we only have a single stable ABI anyway</h3> Err codemadness.org 70 i 926 <p>And that is the C ABI. Even C++ libraries have trouble with this, and Err codemadness.org 70 i 927 people sometimes write the internals of a library in C++ for Err codemadness.org 70 i 928 convenience, but export a stable C API/ABI from it.</p> Err codemadness.org 70 i 929 <p>High level languages like Python have <em>real trouble</em> calling C++ code Err codemadness.org 70 i 930 precisely because of ABI issues.</p> Err codemadness.org 70 i 931 <h3>Actually, in GNOME we have gone further than that</h3> Err codemadness.org 70 i 932 <p>In GNOME we have constructed a sweet little universe where <a href="https://people.gnome.org/~federico/blog/magic-of-gobject-introspection.html">GObject Err codemadness.org 70 i 933 Introspection</a> is Err codemadness.org 70 i 934 basically a C ABI with a ton of machine-generated annotations to make Err codemadness.org 70 i 935 it friendly to language bindings.</p> Err codemadness.org 70 i 936 <p>Still, we rely on a C ABI underneath. See <a href="https://twitter.com/federicomena/status/1286447929880801280">this exploratory twitter Err codemadness.org 70 i 937 thread on advancing the C ABI from Rust</a> for Err codemadness.org 70 i 938 lots of food for thought.</p> Err codemadness.org 70 i 939 <h3>Single copies of libraries with a C ABI</h3> Err codemadness.org 70 i 940 <p>Okay, let's go back to this. What price do we pay for single copies Err codemadness.org 70 i 941 of libraries that, by necessity, must export a C ABI?</p> Err codemadness.org 70 i 942 <ul> Err codemadness.org 70 i 943 <li> Err codemadness.org 70 i 944 <p>Code that can be conveniently called from C, maybe from C++, and Err codemadness.org 70 i 945 moderately to very inconvently from ANYTHING ELSE. With most new Err codemadness.org 70 i 946 application code being written definitely not in C, maybe we should Err codemadness.org 70 i 947 reconsider our priorities here.</p> Err codemadness.org 70 i 948 </li> Err codemadness.org 70 i 949 <li> Err codemadness.org 70 i 950 <p>No language facilities like generics or field visibility, which are Err codemadness.org 70 i 951 not even "modern language" features. Even C++ templates get Err codemadness.org 70 i 952 compiled and statically linked into the calling code, because Err codemadness.org 70 i 953 there's no way to pass information like the size of <code>T</code> in Err codemadness.org 70 i 954 <code>Array&lt;T&gt;</code> across a C ABI. You wanted to make some struct fields Err codemadness.org 70 i 955 public and some private? You are out of luck.</p> Err codemadness.org 70 i 956 </li> Err codemadness.org 70 i 957 <li> Err codemadness.org 70 i 958 <p>No knowledge of data ownership except by careful reading of the C Err codemadness.org 70 i 959 function's documentation. Does the function free its arguments? Err codemadness.org 70 i 960 How - with <code>free()</code> or <code>g_free()</code> or <code>my_thing_free()</code>? Or does the Err codemadness.org 70 i 961 caller just lend it a reference? Can the data be copied bit-by-bit Err codemadness.org 70 i 962 or must a special function be called to make a copy? Err codemadness.org 70 i 963 GObject-Introspection carries this information in its annotations, Err codemadness.org 70 i 964 while the C ABI has no idea and just ships raw pointers around.</p> Err codemadness.org 70 i 965 </li> Err codemadness.org 70 i 966 </ul> Err codemadness.org 70 i 967 <p>More food for thought note: <a href="https://twitter.com/hsivonen/status/1232204147740508162">this twitter Err codemadness.org 70 i 968 thread</a> says Err codemadness.org 70 i 969 this about the C++ ABI: "Also, the ABI matters for whether the actual Err codemadness.org 70 i 970 level of practicality of complying with LGPL matches the level of Err codemadness.org 70 i 971 practicality intended years ago when some project picked LGPL as its Err codemadness.org 70 i 972 license. Of course, the standard does not talk about LGPL, either. Err codemadness.org 70 i 973 LGPL has rather different implications for Rust and Go than it does Err codemadness.org 70 i 974 for C and Java. It was obviously written with C in mind."</p> Err codemadness.org 70 i 975 <h2>Monomorphization and template bloat</h2> Err codemadness.org 70 i 976 <p>While C++ had the problem of "lots of template code in header files", Err codemadness.org 70 i 977 Rust has the problem that <a href="https://pingcap.com/blog/generics-and-compile-time-in-rust#monomorphized-generics">monomorphization of generics creates a lot Err codemadness.org 70 i 978 of compiled Err codemadness.org 70 i 979 code</a>. Err codemadness.org 70 i 980 There are tricks to avoid this and they are all the decision of the Err codemadness.org 70 i 981 library/crate author. Both share the root cause that templated or Err codemadness.org 70 i 982 generic code must be recompiled for every specific use, and thus Err codemadness.org 70 i 983 cannot live in a shared library.</p> Err codemadness.org 70 i 984 <p>Also, see this wonderful <a href="https://thume.ca/2019/07/14/a-tour-of-metaprogramming-models-for-generics/">article on how different languages implement Err codemadness.org 70 i 985 generics</a>, Err codemadness.org 70 i 986 and think that a plain C ABI means we have NOTHING of the sort.</p> Err codemadness.org 70 i 987 <p>Also, see <a href="https://gankra.github.io/blah/swift-abi/">How Swift Achieved Dynamic Linking Where Rust Err codemadness.org 70 i 988 Couldn't</a> for more food for Err codemadness.org 70 i 989 thought. This is extremely roughly equivalent to GObject's boxed Err codemadness.org 70 i 990 types; callers keep values on the heap but know the type layout via Err codemadness.org 70 i 991 annotation magic, while the library's actual implementation Err codemadness.org 70 i 992 is free to have the values on the stack or wherever for its own use.</p> Err codemadness.org 70 i 993 <h2>Should all libraries export APIs with generics and exotic types?</h2> Err codemadness.org 70 i 994 <p>No!</p> Err codemadness.org 70 i 995 <p>You probably want something like a low-level array of values, Err codemadness.org 70 i 996 <code>Vec&lt;T&gt;</code>, to be inlined everywhere and with code that knows the Err codemadness.org 70 i 997 type of the vector's elements. Element accesses can be inlined to a Err codemadness.org 70 i 998 single machine instruction in the best case.</p> Err codemadness.org 70 i 999 <p>But not everything requires this absolute raw performance with Err codemadness.org 70 i 1000 everything inlined everywhere. It is fine to pass references or Err codemadness.org 70 i 1001 pointers to things and do dynamic dispatch from a vtable if you are Err codemadness.org 70 i 1002 not in a super-tight loop, as we love to do in the GObject world.</p> Err codemadness.org 70 i 1003 <h2>Library sizes</h2> Err codemadness.org 70 i 1004 <p>I don't have a good answer to librsvg's compiled size. If gnome-shell Err codemadness.org 70 i 1005 merges my branch to rustify the CSS code, it will also grow its binary Err codemadness.org 70 i 1006 size by quite a bit.</p> Err codemadness.org 70 i 1007 <p>It is my intention to have a Rust crate that both librsvg and Err codemadness.org 70 i 1008 gnome-shell share for their CSS styling needs, but right now I have no Err codemadness.org 70 i 1009 idea if this would be a shared library or just a normal Rust crate. Err codemadness.org 70 i 1010 Maybe it's possible to have a very general CSS library, and the Err codemadness.org 70 i 1011 application registers which properties it can parse and how? Is it Err codemadness.org 70 i 1012 possible to do this as a shared library without essentially Err codemadness.org 70 i 1013 reinventing libcroco? I don't know yet. We'll see.</p> Err codemadness.org 70 i 1014 <h2>A metaphor which I haven't fully explored</h2> Err codemadness.org 70 i 1015 <p>If every application or end-user package is kind of like a living Err codemadness.org 70 i 1016 organism, with its own cycles and behaviors and organs (dependent Err codemadness.org 70 i 1017 libraries) that make it possible...</p> Err codemadness.org 70 i 1018 <p>Why do distros expect all the living organisms on your machine to Err codemadness.org 70 i 1019 share The World's Single Lungs Service, and The World's Single Stomach Err codemadness.org 70 i 1020 Service, and The World's Single Liver Service?</p> Err codemadness.org 70 i 1021 <p>You know, instead of letting every organism have its own slightly Err codemadness.org 70 i 1022 different version of those organs, customized for it? We humans know Err codemadness.org 70 i 1023 how to do vaccination campaigns and everything; maybe we need better Err codemadness.org 70 i 1024 tools to apply bug fixes where they are needed?</p> Err codemadness.org 70 i 1025 <p>I know this metaphor is extremely imperfect and not how things work in Err codemadness.org 70 i 1026 software, but it makes me wonder.</p>Looking for candidates for the 2020 GNOME Foundation elections2020-05-26T17:05:31-05:002020-05-26T17:05:31-05:00Federico Mena Quinterotag:people.gnome.org,2020-05-26:/~federico/blog/looking-for-candidates-2020.html<p>I forgot to write this a few days ago; I hope it is not too late.</p> Err codemadness.org 70 i 1027 <p>The GNOME Foundation's <a href="https://mail.gnome.org/archives/foundation-announce/2020-May/msg00000.html">elections for the Board</a> are coming Err codemadness.org 70 i 1028 up, and we are looking for candidates. Of the 7 directors, we are Err codemadness.org 70 i 1029 replacing 4, and the 3 remaining positions remain for another year. Err codemadness.org 70 i 1030 You …</p><p>I forgot to write this a few days ago; I hope it is not too late.</p> Err codemadness.org 70 i 1031 <p>The GNOME Foundation's <a href="https://mail.gnome.org/archives/foundation-announce/2020-May/msg00000.html">elections for the Board</a> are coming Err codemadness.org 70 i 1032 up, and we are looking for candidates. Of the 7 directors, we are Err codemadness.org 70 i 1033 replacing 4, and the 3 remaining positions remain for another year. Err codemadness.org 70 i 1034 You could be one of those four.</p> Err codemadness.org 70 i 1035 <p>I would like it very much if there were candidates and directors that Err codemadness.org 70 i 1036 fall outside the box of "white male programmer"; it is unfortunate Err codemadness.org 70 i 1037 that for the current Board we ended up with all dudes. GNOME has a Err codemadness.org 70 i 1038 <a href="https://wiki.gnome.org/Foundation/CodeOfConduct">Code of Conduct</a> to make it a good place to be.</p> Err codemadness.org 70 i 1039 <p>Allan Day wrote a <a href="https://blogs.gnome.org/aday/2020/05/26/gnome-foundation-board-of-directors-a-year-in-review/">review of the Board's activies for the last Err codemadness.org 70 i 1040 year</a>. We are moving from a model where the Board does a Err codemadness.org 70 i 1041 little bit of everything, to one with a more strategic role — now that Err codemadness.org 70 i 1042 the Foundation has full-time employees, they take care of most of the Err codemadness.org 70 i 1043 executive work.</p> Err codemadness.org 70 i 1044 <p><strong>The call-for-candidates is open until May 29, so hurry up!</strong></p> Err codemadness.org 70 i 1045 <ul> Err codemadness.org 70 i 1046 <li><a href="https://blogs.gnome.org/aday/2020/05/26/gnome-foundation-board-of-directors-a-year-in-review/">GNOME Foundation Board of Directors: a Year in Review</a></li> Err codemadness.org 70 i 1047 <li><a href="https://mail.gnome.org/archives/foundation-announce/2020-May/msg00000.html">Call for candidates and elections</a></li> Err codemadness.org 70 i 1048 </ul>Bringing my Emacs from the past2020-04-28T18:03:22-05:002020-04-28T18:03:22-05:00Federico Mena Quinterotag:people.gnome.org,2020-04-28:/~federico/blog/bringing-my-emacs-from-the-past.html<p>I started using Emacs in 1995, and since then I have been carrying a <code>.emacs</code> Err codemadness.org 70 i 1049 that by now has a lot of accumulated crap. It is such an old configuration that Err codemadness.org 70 i 1050 it didn't even use the modern convention of <code>~/.emacs.d/init.el</code> (and it looks Err codemadness.org 70 i 1051 like a newer Emacs …</p><p>I started using Emacs in 1995, and since then I have been carrying a <code>.emacs</code> Err codemadness.org 70 i 1052 that by now has a lot of accumulated crap. It is such an old configuration that Err codemadness.org 70 i 1053 it didn't even use the modern convention of <code>~/.emacs.d/init.el</code> (and it looks Err codemadness.org 70 i 1054 like a newer Emacs version will allow <code>.config/emacs</code> as per the XDG Err codemadness.org 70 i 1055 standard... at last).</p> Err codemadness.org 70 i 1056 <p>I have wanted to change my Emacs configuration for some time, and give it all Err codemadness.org 70 i 1057 the pretty and modern toys.</p> Err codemadness.org 70 i 1058 <p>The things that matter the most to me:</p> Err codemadness.org 70 i 1059 <ul> Err codemadness.org 70 i 1060 <li>Not have a random dumpster in <code>~/.emacs</code> if possible.</li> Err codemadness.org 70 i 1061 <li>Pretty colors.</li> Err codemadness.org 70 i 1062 <li>Magit.</li> Err codemadness.org 70 i 1063 <li>Rust-mode or whatever the new thing is for rust-analyzer and the Language Server.</li> Err codemadness.org 70 i 1064 </ul> Err codemadness.org 70 i 1065 <p>After looking at several examples of configurations that mention <code>use-package</code> Err codemadness.org 70 i 1066 as a unified way of loading packages and configuring them, I found <a href="https://github.com/alhassy/emacs.d">this Err codemadness.org 70 i 1067 configuration</a> which is extremely well Err codemadness.org 70 i 1068 documented. The author does literate programming with org-mode and elisp — Err codemadness.org 70 i 1069 something which I'm casually interested in, but not just now — but that way Err codemadness.org 70 i 1070 everything ends up very well explained and easy to read.</p> Err codemadness.org 70 i 1071 <p>I extracted bits of that configuration and ended up with the following.</p> Err codemadness.org 70 i 1072 <h2>Everything in <code>~/.emacs/init.el</code> and with <code>use-package</code></h2> Err codemadness.org 70 i 1073 <div class="highlight"><pre><span></span><code><span class="c1">;; Initialize package system</span> Err codemadness.org 70 i 1074 Err codemadness.org 70 i 1075 <span class="p">(</span><span class="nb">require</span> <span class="ss">&#39;package</span><span class="p">)</span> Err codemadness.org 70 i 1076 Err codemadness.org 70 i 1077 <span class="p">(</span><span class="k">setq</span> <span class="nv">package-archives</span> Err codemadness.org 70 i 1078 <span class="o">&#39;</span><span class="p">((</span><span class="s">&quot;org&quot;</span> <span class="o">.</span> <span class="s">&quot;https://orgmode.org/elpa/&quot;</span><span class="p">)</span> Err codemadness.org 70 i 1079 <span class="p">(</span><span class="s">&quot;gnu&quot;</span> <span class="o">.</span> <span class="s">&quot;https://elpa.gnu.org/packages/&quot;</span><span class="p">)</span> Err codemadness.org 70 i 1080 <span class="p">(</span><span class="s">&quot;melpa&quot;</span> <span class="o">.</span> <span class="s">&quot;https://melpa.org/packages/&quot;</span><span class="p">)))</span> Err codemadness.org 70 i 1081 Err codemadness.org 70 i 1082 <span class="p">(</span><span class="nv">package-initialize</span><span class="p">)</span> Err codemadness.org 70 i 1083 <span class="c1">;(package-refresh-contents)</span> Err codemadness.org 70 i 1084 Err codemadness.org 70 i 1085 <span class="c1">;; Use-package for civilized configuration</span> Err codemadness.org 70 i 1086 Err codemadness.org 70 i 1087 <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">package-installed-p</span> <span class="ss">&#39;use-package</span><span class="p">)</span> Err codemadness.org 70 i 1088 <span class="p">(</span><span class="nv">package-install</span> <span class="ss">&#39;use-package</span><span class="p">))</span> Err codemadness.org 70 i 1089 <span class="p">(</span><span class="nb">require</span> <span class="ss">&#39;use-package</span><span class="p">)</span> Err codemadness.org 70 i 1090 Err codemadness.org 70 i 1091 <span class="p">(</span><span class="k">setq</span> <span class="nv">use-package-always-ensure</span> <span class="no">t</span><span class="p">)</span> Err codemadness.org 70 i 1092 </code></pre></div> Err codemadness.org 70 i 1093 Err codemadness.org 70 i 1094 <h2><code>~/.emacs.d/custom.el</code> for <code>M-x customize</code> stuff</h2> Err codemadness.org 70 i 1095 <div class="highlight"><pre><span></span><code><span class="c1">;; Set customization data in a specific file, without littering</span> Err codemadness.org 70 i 1096 <span class="c1">;; my init files.</span> Err codemadness.org 70 i 1097 Err codemadness.org 70 i 1098 <span class="p">(</span><span class="k">setq</span> <span class="nv">custom-file</span> <span class="s">&quot;~/.emacs.d/custom.el&quot;</span><span class="p">)</span> Err codemadness.org 70 i 1099 <span class="p">(</span><span class="nf">load</span> <span class="nv">custom-file</span><span class="p">)</span> Err codemadness.org 70 i 1100 </code></pre></div> Err codemadness.org 70 i 1101 Err codemadness.org 70 i 1102 <h2>Which-key to get hints when typing command prefixes</h2> Err codemadness.org 70 i 1103 <div class="highlight"><pre><span></span><code><span class="c1">;; Make it easier to discover key shortcuts</span> Err codemadness.org 70 i 1104 Err codemadness.org 70 i 1105 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">which-key</span> Err codemadness.org 70 i 1106 <span class="nb">:diminish</span> Err codemadness.org 70 i 1107 <span class="nb">:config</span> Err codemadness.org 70 i 1108 <span class="p">(</span><span class="nv">which-key-mode</span><span class="p">)</span> Err codemadness.org 70 i 1109 <span class="p">(</span><span class="nv">which-key-setup-side-window-bottom</span><span class="p">)</span> Err codemadness.org 70 i 1110 <span class="p">(</span><span class="k">setq</span> <span class="nv">which-key-idle-delay</span> <span class="mf">0.1</span><span class="p">))</span> Err codemadness.org 70 i 1111 </code></pre></div> Err codemadness.org 70 i 1112 Err codemadness.org 70 i 1113 <h2>Don't pollute the modeline with common modes</h2> Err codemadness.org 70 i 1114 <div class="highlight"><pre><span></span><code><span class="c1">;; Do not show some common modes in the modeline, to save space</span> Err codemadness.org 70 i 1115 Err codemadness.org 70 i 1116 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">diminish</span> Err codemadness.org 70 i 1117 <span class="nb">:defer</span> <span class="mi">5</span> Err codemadness.org 70 i 1118 <span class="nb">:config</span> Err codemadness.org 70 i 1119 <span class="p">(</span><span class="nv">diminish</span> <span class="ss">&#39;org-indent-mode</span><span class="p">))</span> Err codemadness.org 70 i 1120 </code></pre></div> Err codemadness.org 70 i 1121 Err codemadness.org 70 i 1122 <h2>Magit to use git in a civilized fashion</h2> Err codemadness.org 70 i 1123 <div class="highlight"><pre><span></span><code><span class="c1">;; Magit</span> Err codemadness.org 70 i 1124 Err codemadness.org 70 i 1125 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">magit</span> Err codemadness.org 70 i 1126 <span class="nb">:config</span> Err codemadness.org 70 i 1127 <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&quot;C-x g&quot;</span><span class="p">)</span> <span class="ss">&#39;magit-status</span><span class="p">))</span> Err codemadness.org 70 i 1128 </code></pre></div> Err codemadness.org 70 i 1129 Err codemadness.org 70 i 1130 <h2>Move between windows with Shift-arrows</h2> Err codemadness.org 70 i 1131 <div class="highlight"><pre><span></span><code><span class="c1">;; Let me switch windows with shift-arrows instead of &quot;C-x o&quot; all the time</span> Err codemadness.org 70 i 1132 <span class="p">(</span><span class="nv">windmove-default-keybindings</span><span class="p">)</span> Err codemadness.org 70 i 1133 </code></pre></div> Err codemadness.org 70 i 1134 Err codemadness.org 70 i 1135 <h2>Pretty colors</h2> Err codemadness.org 70 i 1136 <p>I was using <code>solarized-dark</code> but I like this one even better:</p> Err codemadness.org 70 i 1137 <div class="highlight"><pre><span></span><code><span class="c1">;; Pretty colors</span> Err codemadness.org 70 i 1138 Err codemadness.org 70 i 1139 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">flatland-theme</span> Err codemadness.org 70 i 1140 <span class="nb">:config</span> Err codemadness.org 70 i 1141 <span class="p">(</span><span class="nv">custom-theme-set-faces</span> <span class="ss">&#39;flatland</span> Err codemadness.org 70 i 1142 <span class="o">&#39;</span><span class="p">(</span><span class="nv">show-paren-match</span> <span class="p">((</span><span class="no">t</span> <span class="p">(</span><span class="nb">:background</span> <span class="s">&quot;dark gray&quot;</span> <span class="nb">:foreground</span> <span class="s">&quot;black&quot;</span> <span class="nb">:weight</span> <span class="nv">bold</span><span class="p">))))</span> Err codemadness.org 70 i 1143 <span class="o">&#39;</span><span class="p">(</span><span class="nv">show-paren-mismatch</span> <span class="p">((</span><span class="no">t</span> <span class="p">(</span><span class="nb">:background</span> <span class="s">&quot;firebrick&quot;</span> <span class="nb">:foreground</span> <span class="s">&quot;orange&quot;</span> <span class="nb">:weight</span> <span class="nv">bold</span><span class="p">))))))</span> Err codemadness.org 70 i 1144 </code></pre></div> Err codemadness.org 70 i 1145 Err codemadness.org 70 i 1146 <h2>Nyan cat instead of scrollbars</h2> Err codemadness.org 70 i 1147 <div class="highlight"><pre><span></span><code><span class="c1">;; Nyan cat instead of scrollbar</span> Err codemadness.org 70 i 1148 <span class="c1">;; scroll-bar-mode is turned off in custom.el</span> Err codemadness.org 70 i 1149 Err codemadness.org 70 i 1150 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">nyan-mode</span> Err codemadness.org 70 i 1151 <span class="nb">:config</span> Err codemadness.org 70 i 1152 <span class="p">(</span><span class="nv">nyan-mode</span> <span class="mi">1</span><span class="p">))</span> Err codemadness.org 70 i 1153 </code></pre></div> Err codemadness.org 70 i 1154 Err codemadness.org 70 i 1155 <h2>Move buffers to adjacent windows</h2> Err codemadness.org 70 i 1156 <div class="highlight"><pre><span></span><code><span class="c1">;; Move buffers between windows</span> Err codemadness.org 70 i 1157 Err codemadness.org 70 i 1158 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">buffer-move</span> Err codemadness.org 70 i 1159 <span class="nb">:config</span> Err codemadness.org 70 i 1160 <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&quot;&lt;C-S-up&gt;&quot;</span><span class="p">)</span> <span class="ss">&#39;buf-move-up</span><span class="p">)</span> Err codemadness.org 70 i 1161 <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&quot;&lt;C-S-down&gt;&quot;</span><span class="p">)</span> <span class="ss">&#39;buf-move-down</span><span class="p">)</span> Err codemadness.org 70 i 1162 <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&quot;&lt;C-S-left&gt;&quot;</span><span class="p">)</span> <span class="ss">&#39;buf-move-left</span><span class="p">)</span> Err codemadness.org 70 i 1163 <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&quot;&lt;C-S-right&gt;&quot;</span><span class="p">)</span> <span class="ss">&#39;buf-move-right</span><span class="p">))</span> Err codemadness.org 70 i 1164 </code></pre></div> Err codemadness.org 70 i 1165 Err codemadness.org 70 i 1166 <h2>Change buffer names for files with the same name</h2> Err codemadness.org 70 i 1167 <div class="highlight"><pre><span></span><code><span class="c1">;; Note that ‘uniquify’ is builtin.</span> Err codemadness.org 70 i 1168 <span class="p">(</span><span class="nb">require</span> <span class="ss">&#39;uniquify</span><span class="p">)</span> Err codemadness.org 70 i 1169 <span class="p">(</span><span class="k">setq</span> <span class="nv">uniquify-separator</span> <span class="s">&quot;/&quot;</span> <span class="c1">;; The separator in buffer names.</span> Err codemadness.org 70 i 1170 <span class="nv">uniquify-buffer-name-style</span> <span class="ss">&#39;forward</span><span class="p">)</span> <span class="c1">;; names/in/this/style</span> Err codemadness.org 70 i 1171 </code></pre></div> Err codemadness.org 70 i 1172 Err codemadness.org 70 i 1173 <h2>Helm to auto-complete in grand style</h2> Err codemadness.org 70 i 1174 <div class="highlight"><pre><span></span><code><span class="p">(</span><span class="nb">use-package</span> <span class="nv">helm</span> Err codemadness.org 70 i 1175 <span class="nb">:diminish</span> Err codemadness.org 70 i 1176 <span class="nb">:init</span> <span class="p">(</span><span class="nv">helm-mode</span> <span class="no">t</span><span class="p">)</span> Err codemadness.org 70 i 1177 <span class="nb">:bind</span> <span class="p">((</span><span class="s">&quot;M-x&quot;</span> <span class="o">.</span> <span class="nv">helm-M-x</span><span class="p">)</span> Err codemadness.org 70 i 1178 <span class="p">(</span><span class="s">&quot;C-x C-f&quot;</span> <span class="o">.</span> <span class="nv">helm-find-files</span><span class="p">)</span> Err codemadness.org 70 i 1179 <span class="p">(</span><span class="s">&quot;C-x b&quot;</span> <span class="o">.</span> <span class="nv">helm-mini</span><span class="p">)</span> <span class="c1">;; See buffers &amp; recent files; more useful.</span> Err codemadness.org 70 i 1180 <span class="p">(</span><span class="s">&quot;C-x r b&quot;</span> <span class="o">.</span> <span class="nv">helm-filtered-bookmarks</span><span class="p">)</span> Err codemadness.org 70 i 1181 <span class="p">(</span><span class="s">&quot;C-x C-r&quot;</span> <span class="o">.</span> <span class="nv">helm-recentf</span><span class="p">)</span> <span class="c1">;; Search for recently edited files</span> Err codemadness.org 70 i 1182 <span class="p">(</span><span class="s">&quot;C-c i&quot;</span> <span class="o">.</span> <span class="nv">helm-imenu</span><span class="p">)</span> Err codemadness.org 70 i 1183 <span class="p">(</span><span class="s">&quot;C-h a&quot;</span> <span class="o">.</span> <span class="nv">helm-apropos</span><span class="p">)</span> Err codemadness.org 70 i 1184 <span class="c1">;; Look at what was cut recently &amp; paste it in.</span> Err codemadness.org 70 i 1185 <span class="p">(</span><span class="s">&quot;M-y&quot;</span> <span class="o">.</span> <span class="nv">helm-show-kill-ring</span><span class="p">)</span> Err codemadness.org 70 i 1186 Err codemadness.org 70 i 1187 <span class="nb">:map</span> <span class="nv">helm-map</span> Err codemadness.org 70 i 1188 <span class="c1">;; We can list ‘actions’ on the currently selected item by C-z.</span> Err codemadness.org 70 i 1189 <span class="p">(</span><span class="s">&quot;C-z&quot;</span> <span class="o">.</span> <span class="nv">helm-select-action</span><span class="p">)</span> Err codemadness.org 70 i 1190 <span class="c1">;; Let&#39;s keep tab-completetion anyhow.</span> Err codemadness.org 70 i 1191 <span class="p">(</span><span class="s">&quot;TAB&quot;</span> <span class="o">.</span> <span class="nv">helm-execute-persistent-action</span><span class="p">)</span> Err codemadness.org 70 i 1192 <span class="p">(</span><span class="s">&quot;&lt;tab&gt;&quot;</span> <span class="o">.</span> <span class="nv">helm-execute-persistent-action</span><span class="p">)))</span> Err codemadness.org 70 i 1193 </code></pre></div> Err codemadness.org 70 i 1194 Err codemadness.org 70 i 1195 <h2>Ripgrep to search in grand style</h2> Err codemadness.org 70 i 1196 <div class="highlight"><pre><span></span><code><span class="c1">;; Ripgrep</span> Err codemadness.org 70 i 1197 Err codemadness.org 70 i 1198 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">rg</span> Err codemadness.org 70 i 1199 <span class="nb">:config</span> Err codemadness.org 70 i 1200 <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&quot;M-s g&quot;</span><span class="p">)</span> <span class="ss">&#39;rg</span><span class="p">)</span> Err codemadness.org 70 i 1201 <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&quot;M-s d&quot;</span><span class="p">)</span> <span class="ss">&#39;rg-dwim</span><span class="p">))</span> Err codemadness.org 70 i 1202 Err codemadness.org 70 i 1203 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">helm-rg</span><span class="p">)</span> Err codemadness.org 70 i 1204 </code></pre></div> Err codemadness.org 70 i 1205 Err codemadness.org 70 i 1206 <h2>Rust mode and Language Server</h2> Err codemadness.org 70 i 1207 <p>Now that RLS is in the process of being deprecated, it's getting substituted Err codemadness.org 70 i 1208 with rust-analyzer. Also, rust-mode goes away in favor of rustic.</p> Err codemadness.org 70 i 1209 <div class="highlight"><pre><span></span><code><span class="c1">;; Rustic, LSP</span> Err codemadness.org 70 i 1210 Err codemadness.org 70 i 1211 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">flycheck</span><span class="p">)</span> Err codemadness.org 70 i 1212 Err codemadness.org 70 i 1213 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">rustic</span><span class="p">)</span> Err codemadness.org 70 i 1214 Err codemadness.org 70 i 1215 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">lsp-ui</span><span class="p">)</span> Err codemadness.org 70 i 1216 Err codemadness.org 70 i 1217 <span class="p">(</span><span class="nb">use-package</span> <span class="nv">helm-lsp</span> Err codemadness.org 70 i 1218 <span class="nb">:config</span> Err codemadness.org 70 i 1219 <span class="p">(</span><span class="nf">define-key</span> <span class="nv">lsp-mode-map</span> <span class="p">[</span><span class="nv">remap</span> <span class="nv">xref-find-apropos</span><span class="p">]</span> <span class="nf">#&#39;</span><span class="nv">helm-lsp-workspace-symbol</span><span class="p">))</span> Err codemadness.org 70 i 1220 </code></pre></div> Err codemadness.org 70 i 1221 Err codemadness.org 70 i 1222 <h2>Performatively not get distracted</h2> Err codemadness.org 70 i 1223 <div class="highlight"><pre><span></span><code><span class="p">;;;</span><span class="w"> </span><span class="n">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">notification</span><span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="n">compilation</span><span class="w"> </span><span class="n">finishes</span><span class="w"></span> Err codemadness.org 70 i 1224 Err codemadness.org 70 i 1225 <span class="p">(</span><span class="n">setq</span><span class="w"> </span><span class="n">compilation</span><span class="o">-</span><span class="n">finish</span><span class="o">-</span><span class="n">functions</span><span class="w"></span> Err codemadness.org 70 i 1226 <span class="w"> </span><span class="p">(</span><span class="n">append</span><span class="w"> </span><span class="n">compilation</span><span class="o">-</span><span class="n">finish</span><span class="o">-</span><span class="n">functions</span><span class="w"></span> Err codemadness.org 70 i 1227 <span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="n">fmq</span><span class="o">-</span><span class="n">compilation</span><span class="o">-</span><span class="n">finish</span><span class="p">)))</span><span class="w"></span> Err codemadness.org 70 i 1228 Err codemadness.org 70 i 1229 <span class="p">(</span><span class="n">defun</span><span class="w"> </span><span class="n">fmq</span><span class="o">-</span><span class="n">compilation</span><span class="o">-</span><span class="n">finish</span><span class="w"> </span><span class="p">(</span><span class="n">buffer</span><span class="w"> </span><span class="n">status</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 1230 <span class="w"> </span><span class="p">(</span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">not</span><span class="w"> </span><span class="p">(</span><span class="n">member</span><span class="w"> </span><span class="n">mode</span><span class="o">-</span><span class="n">name</span><span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="s">&quot;Grep&quot;</span><span class="w"> </span><span class="s">&quot;rg&quot;</span><span class="p">)))</span><span class="w"></span> Err codemadness.org 70 i 1231 <span class="w"> </span><span class="p">(</span><span class="n">call</span><span class="o">-</span><span class="n">process</span><span class="w"> </span><span class="s">&quot;notify-send&quot;</span><span class="w"> </span><span class="n">nil</span><span class="w"> </span><span class="n">nil</span><span class="w"> </span><span class="n">nil</span><span class="w"></span> Err codemadness.org 70 i 1232 <span class="w"> </span><span class="s">&quot;-t&quot;</span><span class="w"> </span><span class="s">&quot;0&quot;</span><span class="w"></span> Err codemadness.org 70 i 1233 <span class="w"> </span><span class="s">&quot;-i&quot;</span><span class="w"> </span><span class="s">&quot;emacs&quot;</span><span class="w"></span> Err codemadness.org 70 i 1234 <span class="w"> </span><span class="s">&quot;Compilation finished in Emacs&quot;</span><span class="w"></span> Err codemadness.org 70 i 1235 <span class="w"> </span><span class="n">status</span><span class="p">)))</span><span class="w"></span> Err codemadness.org 70 i 1236 </code></pre></div> Err codemadness.org 70 i 1237 Err codemadness.org 70 i 1238 <h2>Stuff from custom.el</h2> Err codemadness.org 70 i 1239 <p>The interesting bits here are making LSP work; everything else is preferences.</p> Err codemadness.org 70 i 1240 <div class="highlight"><pre><span></span><code><span class="p">(</span><span class="nv">custom-set-variables</span> Err codemadness.org 70 i 1241 <span class="c1">;; custom-set-variables was added by Custom.</span> Err codemadness.org 70 i 1242 <span class="c1">;; If you edit it by hand, you could mess it up, so be careful.</span> Err codemadness.org 70 i 1243 <span class="c1">;; Your init file should contain only one such instance.</span> Err codemadness.org 70 i 1244 <span class="c1">;; If there is more than one, they won&#39;t work right.</span> Err codemadness.org 70 i 1245 <span class="o">&#39;</span><span class="p">(</span><span class="nv">column-number-mode</span> <span class="no">t</span><span class="p">)</span> Err codemadness.org 70 i 1246 <span class="o">&#39;</span><span class="p">(</span><span class="nv">custom-safe-themes</span> Err codemadness.org 70 i 1247 <span class="p">(</span><span class="k">quote</span> Err codemadness.org 70 i 1248 <span class="p">(</span><span class="s">&quot;2540689fd0bc5d74c4682764ff6c94057ba8061a98be5dd21116bf7bf301acfb&quot;</span> <span class="s">&quot;bffa9739ce0752a37d9b1eee78fc00ba159748f50dc328af4be661484848e476&quot;</span> <span class="s">&quot;0fffa9669425ff140ff2ae8568c7719705ef33b7a927a0ba7c5e2ffcfac09b75&quot;</span> <span class="s">&quot;2809bcb77ad21312897b541134981282dc455ccd7c14d74cc333b6e549b824f3&quot;</span> <span class="nv">default</span><span class="p">)))</span> Err codemadness.org 70 i 1249 <span class="o">&#39;</span><span class="p">(</span><span class="nv">delete-selection-mode</span> <span class="no">nil</span><span class="p">)</span> Err codemadness.org 70 i 1250 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-rust-analyzer-display-chaining-hints</span> <span class="no">t</span><span class="p">)</span> Err codemadness.org 70 i 1251 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-rust-analyzer-display-parameter-hints</span> <span class="no">nil</span><span class="p">)</span> Err codemadness.org 70 i 1252 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-rust-analyzer-macro-expansion-method</span> <span class="p">(</span><span class="k">quote</span> <span class="nv">rustic-analyzer-macro-expand</span><span class="p">))</span> Err codemadness.org 70 i 1253 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-rust-analyzer-server-command</span> <span class="p">(</span><span class="k">quote</span> <span class="p">(</span><span class="s">&quot;/home/federico/.cargo/bin/rust-analyzer&quot;</span><span class="p">)))</span> Err codemadness.org 70 i 1254 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-rust-analyzer-server-display-inlay-hints</span> <span class="no">nil</span><span class="p">)</span> Err codemadness.org 70 i 1255 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-rust-full-docs</span> <span class="no">t</span><span class="p">)</span> Err codemadness.org 70 i 1256 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-rust-server</span> <span class="p">(</span><span class="k">quote</span> <span class="nv">rust-analyzer</span><span class="p">))</span> Err codemadness.org 70 i 1257 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-ui-doc-alignment</span> <span class="p">(</span><span class="k">quote</span> <span class="nv">window</span><span class="p">))</span> Err codemadness.org 70 i 1258 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-ui-doc-position</span> <span class="p">(</span><span class="k">quote</span> <span class="nv">top</span><span class="p">))</span> Err codemadness.org 70 i 1259 <span class="o">&#39;</span><span class="p">(</span><span class="nv">lsp-ui-sideline-enable</span> <span class="no">nil</span><span class="p">)</span> Err codemadness.org 70 i 1260 <span class="o">&#39;</span><span class="p">(</span><span class="nv">menu-bar-mode</span> <span class="no">nil</span><span class="p">)</span> Err codemadness.org 70 i 1261 <span class="o">&#39;</span><span class="p">(</span><span class="nv">package-selected-packages</span> Err codemadness.org 70 i 1262 <span class="p">(</span><span class="k">quote</span> Err codemadness.org 70 i 1263 <span class="p">(</span><span class="nv">helm-lsp</span> <span class="nv">lsp-ui</span> <span class="nv">lsp-mode</span> <span class="nv">flycheck</span> <span class="nv">rustic</span> <span class="nv">rg</span> <span class="nv">helm-rg</span> <span class="nv">ripgrep</span> <span class="nv">helm-projectile</span> <span class="nv">helm</span> <span class="nv">buffer-move</span> <span class="nv">nyan-mode</span> <span class="nv">flatland-black-theme</span> <span class="nv">flatland-theme</span> <span class="nv">afternoon-theme</span> <span class="nv">spacemacs-theme</span> <span class="nv">solarized-theme</span> <span class="nv">magit</span> <span class="nv">diminish</span> <span class="nv">which-key</span> <span class="nb">use-package</span><span class="p">)))</span> Err codemadness.org 70 i 1264 <span class="o">&#39;</span><span class="p">(</span><span class="nv">rustic-lsp-server</span> <span class="p">(</span><span class="k">quote</span> <span class="nv">rust-analyzer</span><span class="p">))</span> Err codemadness.org 70 i 1265 <span class="o">&#39;</span><span class="p">(</span><span class="nv">scroll-bar-mode</span> <span class="no">nil</span><span class="p">)</span> Err codemadness.org 70 i 1266 <span class="o">&#39;</span><span class="p">(</span><span class="nv">scroll-step</span> <span class="mi">0</span><span class="p">)</span> Err codemadness.org 70 i 1267 <span class="o">&#39;</span><span class="p">(</span><span class="nv">tool-bar-mode</span> <span class="no">nil</span><span class="p">))</span> Err codemadness.org 70 i 1268 <span class="p">(</span><span class="nv">custom-set-faces</span> Err codemadness.org 70 i 1269 <span class="c1">;; custom-set-faces was added by Custom.</span> Err codemadness.org 70 i 1270 <span class="c1">;; If you edit it by hand, you could mess it up, so be careful.</span> Err codemadness.org 70 i 1271 <span class="c1">;; Your init file should contain only one such instance.</span> Err codemadness.org 70 i 1272 <span class="c1">;; If there is more than one, they won&#39;t work right.</span> Err codemadness.org 70 i 1273 <span class="p">)</span> Err codemadness.org 70 i 1274 </code></pre></div> Err codemadness.org 70 i 1275 Err codemadness.org 70 i 1276 <h2>Results</h2> Err codemadness.org 70 i 1277 <p>I am very happy with rustic / rust-analyzer and the Language Server. Having Err codemadness.org 70 i 1278 documentation on each thing when one moves the cursor around code is something Err codemadness.org 70 i 1279 that I never thought would work well in Emacs. I haven't decided if I love <code>M-x Err codemadness.org 70 i 1280 lsp-rust-analyzer-inlay-hints-mode</code> or if it drives me nuts; it shows you the Err codemadness.org 70 i 1281 names of function arguments and inferred types among the code. I suppose I'll Err codemadness.org 70 i 1282 turn it off and on as needed.</p> Err codemadness.org 70 i 1283 <p>Some days ago, before using helm, I had projectile-mode to work with git Err codemadness.org 70 i 1284 checkouts and I was quite liking it. I haven't found how to configure Err codemadness.org 70 i 1285 helm-projectile to work; I'll have to keep experimenting.</p>Reducing memory consumption in librsvg, part 4: compact representation for Bézier paths2020-03-26T18:58:36-06:002020-03-26T18:58:36-06:00Federico Mena Quinterotag:people.gnome.org,2020-03-26:/~federico/blog/reducing-memory-consumption-in-librsvg-4.html<p>Let's continue with the <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/574">enormous SVG</a> from the last time, a Err codemadness.org 70 i 1286 map extracted from OpenStreetMap.</p> Err codemadness.org 70 i 1287 <p>According to <a href="https://valgrind.org/docs/manual/ms-manual.html">Massif</a>, peak memory consumption for that file occurs at Err codemadness.org 70 i 1288 the following point during the execution of rsvg-convert. I pasted Err codemadness.org 70 i 1289 only the part that refers to Bézier paths:</p> Err codemadness.org 70 i 1290 <div class="highlight"><pre><span></span><code> <span class="nt">--------------------------------------------------------------------------------</span> Err codemadness.org 70 i 1291 <span class="nt">n</span> <span class="nt">time</span><span class="o">(</span><span class="nt">i</span><span class="o">)</span> <span class="nt">total</span><span class="o">(</span><span class="nt">B</span><span class="o">)</span> <span class="nt">useful-heap …</span></code></pre></div><p>Let's continue with the <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/574">enormous SVG</a> from the last time, a Err codemadness.org 70 i 1292 map extracted from OpenStreetMap.</p> Err codemadness.org 70 i 1293 <p>According to <a href="https://valgrind.org/docs/manual/ms-manual.html">Massif</a>, peak memory consumption for that file occurs at Err codemadness.org 70 i 1294 the following point during the execution of rsvg-convert. I pasted Err codemadness.org 70 i 1295 only the part that refers to Bézier paths:</p> Err codemadness.org 70 i 1296 <div class="highlight"><pre><span></span><code> <span class="nt">--------------------------------------------------------------------------------</span> Err codemadness.org 70 i 1297 <span class="nt">n</span> <span class="nt">time</span><span class="o">(</span><span class="nt">i</span><span class="o">)</span> <span class="nt">total</span><span class="o">(</span><span class="nt">B</span><span class="o">)</span> <span class="nt">useful-heap</span><span class="o">(</span><span class="nt">B</span><span class="o">)</span> <span class="nt">extra-heap</span><span class="o">(</span><span class="nt">B</span><span class="o">)</span> <span class="nt">stacks</span><span class="o">(</span><span class="nt">B</span><span class="o">)</span> Err codemadness.org 70 i 1298 <span class="nt">--------------------------------------------------------------------------------</span> Err codemadness.org 70 i 1299 <span class="nt">1</span> <span class="nt">33</span> <span class="nt">24</span><span class="o">,</span><span class="nt">139</span><span class="o">,</span><span class="nt">598</span><span class="o">,</span><span class="nt">653</span> <span class="nt">1</span><span class="o">,</span><span class="nt">416</span><span class="o">,</span><span class="nt">831</span><span class="o">,</span><span class="nt">176</span> <span class="nt">1</span><span class="o">,</span><span class="nt">329</span><span class="o">,</span><span class="nt">943</span><span class="o">,</span><span class="nt">212</span> <span class="nt">86</span><span class="o">,</span><span class="nt">887</span><span class="o">,</span><span class="nt">964</span> <span class="nt">0</span> Err codemadness.org 70 i 1300 <span class="nt">2</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x4A2727E</span><span class="o">:</span> <span class="nt">alloc</span> <span class="o">(</span><span class="nt">alloc</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">84</span><span class="o">)</span> Err codemadness.org 70 i 1301 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x4A2727E</span><span class="o">:</span> <span class="nt">alloc</span> <span class="o">(</span><span class="nt">alloc</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">172</span><span class="o">)</span> Err codemadness.org 70 i 1302 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x4A2727E</span><span class="o">:</span> <span class="nt">allocate_in</span><span class="o">&lt;</span><span class="nt">rsvg_internals</span><span class="p">::</span><span class="nd">path_builder</span><span class="p">::</span><span class="nd">PathCommand</span><span class="o">,</span><span class="nt">alloc</span><span class="p">::</span><span class="nd">alloc</span><span class="p">::</span><span class="nd">Global</span><span class="o">&gt;</span> <span class="o">(</span><span class="nt">raw_vec</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">98</span><span class="o">)</span> Err codemadness.org 70 i 1303 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x4A2727E</span><span class="o">:</span> <span class="nt">with_capacity</span><span class="o">&lt;</span><span class="nt">rsvg_internals</span><span class="p">::</span><span class="nd">path_builder</span><span class="p">::</span><span class="nd">PathCommand</span><span class="o">&gt;</span> <span class="o">(</span><span class="nt">raw_vec</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">167</span><span class="o">)</span> Err codemadness.org 70 i 1304 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x4A2727E</span><span class="o">:</span> <span class="nt">with_capacity</span><span class="o">&lt;</span><span class="nt">rsvg_internals</span><span class="p">::</span><span class="nd">path_builder</span><span class="p">::</span><span class="nd">PathCommand</span><span class="o">&gt;</span> <span class="o">(</span><span class="nt">vec</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">358</span><span class="o">)</span> Err codemadness.org 70 i 1305 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x4A2727E</span><span class="o">:</span> <span class="o">&lt;</span><span class="nt">alloc</span><span class="p">::</span><span class="nd">vec</span><span class="p">::</span><span class="nd">Vec</span><span class="o">&lt;</span><span class="nt">T</span><span class="o">&gt;</span> <span class="nt">as</span> <span class="nt">alloc</span><span class="p">::</span><span class="nd">vec</span><span class="p">::</span><span class="nd">SpecExtend</span><span class="o">&lt;</span><span class="nt">T</span><span class="o">,</span><span class="nt">I</span><span class="o">&gt;&gt;</span><span class="p">::</span><span class="nd">from_iter</span> <span class="o">(</span><span class="nt">vec</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">1992</span><span class="o">)</span> Err codemadness.org 70 i 1306 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x49D212C</span><span class="o">:</span> <span class="nt">from_iter</span><span class="o">&lt;</span><span class="nt">rsvg_internals</span><span class="p">::</span><span class="nd">path_builder</span><span class="p">::</span><span class="nd">PathCommand</span><span class="o">,</span><span class="nt">smallvec</span><span class="p">::</span><span class="nd">IntoIter</span><span class="o">&lt;</span><span class="cp">[</span><span class="nx">rsvg_internals</span><span class="nl">::path_builder::PathCommand</span><span class="p">;</span> <span class="mi">32</span><span class="cp">]</span><span class="o">&gt;&gt;</span> <span class="o">(</span><span class="nt">vec</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">1901</span><span class="o">)</span> Err codemadness.org 70 i 1307 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x49D212C</span><span class="o">:</span> <span class="nt">collect</span><span class="o">&lt;</span><span class="nt">smallvec</span><span class="p">::</span><span class="nd">IntoIter</span><span class="o">&lt;</span><span class="cp">[</span><span class="nx">rsvg_internals</span><span class="nl">::path_builder::PathCommand</span><span class="p">;</span> <span class="mi">32</span><span class="cp">]</span><span class="o">&gt;,</span><span class="nt">alloc</span><span class="p">::</span><span class="nd">vec</span><span class="p">::</span><span class="nd">Vec</span><span class="o">&lt;</span><span class="nt">rsvg_internals</span><span class="p">::</span><span class="nd">path_builder</span><span class="p">::</span><span class="nd">PathCommand</span><span class="o">&gt;&gt;</span> <span class="o">(</span><span class="nt">iterator</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">1493</span><span class="o">)</span> Err codemadness.org 70 i 1308 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x49D212C</span><span class="o">:</span> <span class="nt">into_vec</span><span class="o">&lt;</span><span class="cp">[</span><span class="nx">rsvg_internals</span><span class="nl">::path_builder::PathCommand</span><span class="p">;</span> <span class="mi">32</span><span class="cp">]</span><span class="o">&gt;</span> <span class="o">(</span><span class="nt">lib</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">893</span><span class="o">)</span> Err codemadness.org 70 i 1309 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">448B</span><span class="o">)</span> <span class="nt">0x49D212C</span><span class="o">:</span> <span class="nt">smallvec</span><span class="p">::</span><span class="nd">SmallVec</span><span class="o">&lt;</span><span class="nt">A</span><span class="o">&gt;</span><span class="p">::</span><span class="nd">into_boxed_slice</span> <span class="o">(</span><span class="nt">lib</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">902</span><span class="o">)</span> Err codemadness.org 70 i 1310 <span class="nt">3</span> <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">24</span><span class="p">.</span><span class="nc">88</span><span class="o">%</span> <span class="o">(</span><span class="nt">352</span><span class="o">,</span><span class="nt">523</span><span class="o">,</span><span class="nt">016B</span><span class="o">)</span> <span class="nt">0x4A0394C</span><span class="o">:</span> <span class="nt">into_path</span> <span class="o">(</span><span class="nt">path_builder</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">320</span><span class="o">)</span> Err codemadness.org 70 i 1311 <span class="o">|</span> Err codemadness.org 70 i 1312 <span class="nt">4</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">03</span><span class="p">.</span><span class="nc">60</span><span class="o">%</span> <span class="o">(</span><span class="nt">50</span><span class="o">,</span><span class="nt">990</span><span class="o">,</span><span class="nt">328B</span><span class="o">)</span> <span class="nt">0x4A242F0</span><span class="o">:</span> <span class="nt">realloc</span> <span class="o">(</span><span class="nt">alloc</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">128</span><span class="o">)</span> Err codemadness.org 70 i 1313 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">03</span><span class="p">.</span><span class="nc">60</span><span class="o">%</span> <span class="o">(</span><span class="nt">50</span><span class="o">,</span><span class="nt">990</span><span class="o">,</span><span class="nt">328B</span><span class="o">)</span> <span class="nt">0x4A242F0</span><span class="o">:</span> <span class="nt">realloc</span> <span class="o">(</span><span class="nt">alloc</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">187</span><span class="o">)</span> Err codemadness.org 70 i 1314 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">03</span><span class="p">.</span><span class="nc">60</span><span class="o">%</span> <span class="o">(</span><span class="nt">50</span><span class="o">,</span><span class="nt">990</span><span class="o">,</span><span class="nt">328B</span><span class="o">)</span> <span class="nt">0x4A242F0</span><span class="o">:</span> <span class="nt">shrink_to_fit</span><span class="o">&lt;</span><span class="nt">rsvg_internals</span><span class="p">::</span><span class="nd">path_builder</span><span class="p">::</span><span class="nd">PathCommand</span><span class="o">,</span><span class="nt">alloc</span><span class="p">::</span><span class="nd">alloc</span><span class="p">::</span><span class="nd">Global</span><span class="o">&gt;</span> <span class="o">(</span><span class="nt">raw_vec</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">633</span><span class="o">)</span> Err codemadness.org 70 i 1315 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">03</span><span class="p">.</span><span class="nc">60</span><span class="o">%</span> <span class="o">(</span><span class="nt">50</span><span class="o">,</span><span class="nt">990</span><span class="o">,</span><span class="nt">328B</span><span class="o">)</span> <span class="nt">0x4A242F0</span><span class="o">:</span> <span class="nt">shrink_to_fit</span><span class="o">&lt;</span><span class="nt">rsvg_internals</span><span class="p">::</span><span class="nd">path_builder</span><span class="p">::</span><span class="nd">PathCommand</span><span class="o">&gt;</span> <span class="o">(</span><span class="nt">vec</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">623</span><span class="o">)</span> Err codemadness.org 70 i 1316 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">03</span><span class="p">.</span><span class="nc">60</span><span class="o">%</span> <span class="o">(</span><span class="nt">50</span><span class="o">,</span><span class="nt">990</span><span class="o">,</span><span class="nt">328B</span><span class="o">)</span> <span class="nt">0x4A242F0</span><span class="o">:</span> <span class="nt">alloc</span><span class="p">::</span><span class="nd">vec</span><span class="p">::</span><span class="nd">Vec</span><span class="o">&lt;</span><span class="nt">T</span><span class="o">&gt;</span><span class="p">::</span><span class="nd">into_boxed_slice</span> <span class="o">(</span><span class="nt">vec</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">679</span><span class="o">)</span> Err codemadness.org 70 i 1317 <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">03</span><span class="p">.</span><span class="nc">60</span><span class="o">%</span> <span class="o">(</span><span class="nt">50</span><span class="o">,</span><span class="nt">990</span><span class="o">,</span><span class="nt">328B</span><span class="o">)</span> <span class="nt">0x49D2136</span><span class="o">:</span> <span class="nt">smallvec</span><span class="p">::</span><span class="nd">SmallVec</span><span class="o">&lt;</span><span class="nt">A</span><span class="o">&gt;</span><span class="p">::</span><span class="nd">into_boxed_slice</span> <span class="o">(</span><span class="nt">lib</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">902</span><span class="o">)</span> Err codemadness.org 70 i 1318 <span class="nt">5</span> <span class="o">|</span> <span class="nt">-</span><span class="o">&gt;</span><span class="nt">03</span><span class="p">.</span><span class="nc">60</span><span class="o">%</span> <span class="o">(</span><span class="nt">50</span><span class="o">,</span><span class="nt">990</span><span class="o">,</span><span class="nt">328B</span><span class="o">)</span> <span class="nt">0x4A0394C</span><span class="o">:</span> <span class="nt">into_path</span> <span class="o">(</span><span class="nt">path_builder</span><span class="p">.</span><span class="nc">rs</span><span class="p">:</span><span class="nd">320</span><span class="o">)</span> Err codemadness.org 70 i 1319 </code></pre></div> Err codemadness.org 70 i 1320 Err codemadness.org 70 i 1321 <p>Line 1 has the totals, and we see that at that point the program uses Err codemadness.org 70 i 1322 1,329,943,212 bytes on the heap.</p> Err codemadness.org 70 i 1323 <p>Lines 3 and 5 give us a hint that <code>into_path</code> is being called; this is Err codemadness.org 70 i 1324 the function that converts a temporary/mutable <code>PathBuilder</code> into a Err codemadness.org 70 i 1325 permanent/immutable <code>Path</code>.</p> Err codemadness.org 70 i 1326 <p>Lines 2 and 4 indicate that the arrays of <code>PathCommand</code>, which are Err codemadness.org 70 i 1327 inside those immutable <code>Path</code>s, use 24.88% + 3.60% = 28.48% of the Err codemadness.org 70 i 1328 program's memory; between both they use Err codemadness.org 70 i 1329 352,523,448 + 50,990,328 = 403,513,776 bytes.</p> Err codemadness.org 70 i 1330 <p>That is about 400 MB of <code>PathCommand</code>. Let's see what's going on.</p> Err codemadness.org 70 i 1331 <h2>What is in a PathCommand?</h2> Err codemadness.org 70 i 1332 <p>A <code>Path</code> is a list of commands similar to PostScript, which get used Err codemadness.org 70 i 1333 in SVG to draw Bézier paths. It is a flat array of <code>PathCommand</code>:</p> Err codemadness.org 70 i 1334 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Path</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1335 <span class="w"> </span><span class="n">path_commands</span>: <span class="nb">Box</span><span class="o">&lt;</span><span class="p">[</span><span class="n">PathCommand</span><span class="p">]</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1336 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1337 Err codemadness.org 70 i 1338 <span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">PathCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1339 <span class="w"> </span><span class="n">MoveTo</span><span class="p">(</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="kt">f64</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1340 <span class="w"> </span><span class="n">LineTo</span><span class="p">(</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="kt">f64</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1341 <span class="w"> </span><span class="n">CurveTo</span><span class="p">(</span><span class="n">CubicBezierCurve</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1342 <span class="w"> </span><span class="n">Arc</span><span class="p">(</span><span class="n">EllipticalArc</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1343 <span class="w"> </span><span class="n">ClosePath</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1344 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1345 </code></pre></div> Err codemadness.org 70 i 1346 Err codemadness.org 70 i 1347 <p>Let's see the variants of <code>PathCommand</code>:</p> Err codemadness.org 70 i 1348 <ul> Err codemadness.org 70 i 1349 <li><code>MoveTo</code>: 2 double-precision floating-point numbers.</li> Err codemadness.org 70 i 1350 <li><code>LineTo</code>: same.</li> Err codemadness.org 70 i 1351 <li><code>CurveTo</code>: 6 double-precision floating-point numbers.</li> Err codemadness.org 70 i 1352 <li><code>EllipticalArc</code>: 7 double-precision floating-point numbers, plus 2 Err codemadness.org 70 i 1353 flags (see below).</li> Err codemadness.org 70 i 1354 <li><code>ClosePath</code>: no extra data.</li> Err codemadness.org 70 i 1355 </ul> Err codemadness.org 70 i 1356 <p>These variants vary a lot in terms of size, and each element of the Err codemadness.org 70 i 1357 <code>Path.path_commands</code> array occupies the maximum of their sizes Err codemadness.org 70 i 1358 (i.e. <code>sizeof::&lt;EllipticalArc&gt;</code>).</p> Err codemadness.org 70 i 1359 <h2>A more compact representation</h2> Err codemadness.org 70 i 1360 <p>Ideally, each command in the array would only occupy as much space as Err codemadness.org 70 i 1361 it needs.</p> Err codemadness.org 70 i 1362 <p>We can represent a <code>Path</code> in a different way, as two separate arrays:</p> Err codemadness.org 70 i 1363 <ul> Err codemadness.org 70 i 1364 <li>A very compact array of commands without coordinates.</li> Err codemadness.org 70 i 1365 <li>An array with coordinates only.</li> Err codemadness.org 70 i 1366 </ul> Err codemadness.org 70 i 1367 <p>That is, the following:</p> Err codemadness.org 70 i 1368 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Path</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1369 <span class="w"> </span><span class="n">commands</span>: <span class="nb">Box</span><span class="o">&lt;</span><span class="p">[</span><span class="n">PackedCommand</span><span class="p">]</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1370 <span class="w"> </span><span class="n">coords</span>: <span class="nb">Box</span><span class="o">&lt;</span><span class="p">[</span><span class="kt">f64</span><span class="p">]</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1371 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1372 </code></pre></div> Err codemadness.org 70 i 1373 Err codemadness.org 70 i 1374 <p>The <code>coords</code> array is obvious; it is just a flat array with all the Err codemadness.org 70 i 1375 coordinates in the <code>Path</code> in the order in which they appear.</p> Err codemadness.org 70 i 1376 <p>And the <code>commands</code> array?</p> Err codemadness.org 70 i 1377 <h3>PackedCommand</h3> Err codemadness.org 70 i 1378 <p>We saw above that the biggest variant in <code>PathCommand</code> is Err codemadness.org 70 i 1379 <code>Arc(EllipticalArc)</code>. Let's look inside it:</p> Err codemadness.org 70 i 1380 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">EllipticalArc</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1381 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">r</span>: <span class="p">(</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="kt">f64</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1382 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">x_axis_rotation</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1383 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">large_arc</span>: <span class="nc">LargeArc</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1384 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">sweep</span>: <span class="nc">Sweep</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1385 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">from</span>: <span class="p">(</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="kt">f64</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1386 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">to</span>: <span class="p">(</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="kt">f64</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1387 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1388 </code></pre></div> Err codemadness.org 70 i 1389 Err codemadness.org 70 i 1390 <p>There are 7 <code>f64</code> floating-point numbers there. The other two fields, Err codemadness.org 70 i 1391 <code>large_arc</code> and <code>sweep</code>, are effectively booleans (they are just enums Err codemadness.org 70 i 1392 with two variants, with pretty names instead of just <code>true</code> and Err codemadness.org 70 i 1393 <code>false</code>).</p> Err codemadness.org 70 i 1394 <p>Thus, we have 7 doubles and two flags. Between the two flags there Err codemadness.org 70 i 1395 are 4 possibilities.</p> Err codemadness.org 70 i 1396 <p>Since no other <code>PathCommand</code> variant has flags, we can have the Err codemadness.org 70 i 1397 following enum, which fits in a single byte:</p> Err codemadness.org 70 i 1398 <div class="highlight"><pre><span></span><code><span class="cp">#[repr(u8)]</span><span class="w"></span> Err codemadness.org 70 i 1399 <span class="k">enum</span> <span class="nc">PackedCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1400 <span class="w"> </span><span class="n">MoveTo</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1401 <span class="w"> </span><span class="n">LineTo</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1402 <span class="w"> </span><span class="n">CurveTo</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1403 <span class="w"> </span><span class="n">ArcSmallNegative</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1404 <span class="w"> </span><span class="n">ArcSmallPositive</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1405 <span class="w"> </span><span class="n">ArcLargeNegative</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1406 <span class="w"> </span><span class="n">ArcLargePositive</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1407 <span class="w"> </span><span class="n">ClosePath</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1408 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1409 </code></pre></div> Err codemadness.org 70 i 1410 Err codemadness.org 70 i 1411 <p>That is, simple values for <code>MoveTo</code>/etc. and four special values for Err codemadness.org 70 i 1412 the different types of <code>Arc</code>.</p> Err codemadness.org 70 i 1413 <h2>Packing a PathCommand into a PackedCommand</h2> Err codemadness.org 70 i 1414 <p>In order to pack the array of <code>PathCommand</code>, we must first know how Err codemadness.org 70 i 1415 many coordinates each of its variants will produce:</p> Err codemadness.org 70 i 1416 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">PathCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1417 <span class="w"> </span><span class="k">fn</span> <span class="nf">num_coordinates</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">usize</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1418 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="o">*</span><span class="bp">self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1419 <span class="w"> </span><span class="n">PathCommand</span>::<span class="n">MoveTo</span><span class="p">(</span><span class="o">..</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1420 <span class="w"> </span><span class="n">PathCommand</span>::<span class="n">LineTo</span><span class="p">(</span><span class="o">..</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1421 <span class="w"> </span><span class="n">PathCommand</span>::<span class="n">CurveTo</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1422 <span class="w"> </span><span class="n">PathCommand</span>::<span class="n">Arc</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1423 <span class="w"> </span><span class="n">PathCommand</span>::<span class="n">ClosePath</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1424 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1425 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1426 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1427 </code></pre></div> Err codemadness.org 70 i 1428 Err codemadness.org 70 i 1429 <p>Then, we need to convert each <code>PathCommand</code> into a <code>PackedCommand</code> and Err codemadness.org 70 i 1430 write its coordinates into an array:</p> Err codemadness.org 70 i 1431 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">PathCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1432 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_packed</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">coords</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="p">[</span><span class="kt">f64</span><span class="p">])</span><span class="w"> </span>-&gt; <span class="nc">PackedCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1433 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="o">*</span><span class="bp">self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1434 <span class="w"> </span><span class="n">PathCommand</span>::<span class="n">MoveTo</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1435 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">x</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1436 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1437 <span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">MoveTo</span><span class="w"></span> Err codemadness.org 70 i 1438 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1439 Err codemadness.org 70 i 1440 <span class="w"> </span><span class="c1">// etc. for the other simple commands</span> Err codemadness.org 70 i 1441 Err codemadness.org 70 i 1442 <span class="w"> </span><span class="n">PathCommand</span>::<span class="n">Arc</span><span class="p">(</span><span class="k">ref</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">to_packed_and_coords</span><span class="p">(</span><span class="n">coords</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1443 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1444 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1445 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1446 </code></pre></div> Err codemadness.org 70 i 1447 Err codemadness.org 70 i 1448 <p>Let's look at that <code>to_packed_and_coords</code> more closely:</p> Err codemadness.org 70 i 1449 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">EllipticalArc</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1450 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_packed_and_coords</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">coords</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="p">[</span><span class="kt">f64</span><span class="p">])</span><span class="w"> </span>-&gt; <span class="nc">PackedCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1451 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">r</span><span class="p">.</span><span class="mi">0</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1452 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">r</span><span class="p">.</span><span class="mi">1</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1453 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">x_axis_rotation</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1454 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">from</span><span class="p">.</span><span class="mi">0</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1455 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">from</span><span class="p">.</span><span class="mi">1</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1456 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">to</span><span class="p">.</span><span class="mi">0</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1457 <span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">to</span><span class="p">.</span><span class="mi">1</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1458 Err codemadness.org 70 i 1459 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">large_arc</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">sweep</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1460 <span class="w"> </span><span class="p">(</span><span class="n">LargeArc</span><span class="p">(</span><span class="kc">false</span><span class="p">),</span><span class="w"> </span><span class="n">Sweep</span>::<span class="n">Negative</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">ArcSmallNegative</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1461 <span class="w"> </span><span class="p">(</span><span class="n">LargeArc</span><span class="p">(</span><span class="kc">false</span><span class="p">),</span><span class="w"> </span><span class="n">Sweep</span>::<span class="n">Positive</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">ArcSmallPositive</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1462 <span class="w"> </span><span class="p">(</span><span class="n">LargeArc</span><span class="p">(</span><span class="kc">true</span><span class="p">),</span><span class="w"> </span><span class="n">Sweep</span>::<span class="n">Negative</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">ArcLargeNegative</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1463 <span class="w"> </span><span class="p">(</span><span class="n">LargeArc</span><span class="p">(</span><span class="kc">true</span><span class="p">),</span><span class="w"> </span><span class="n">Sweep</span>::<span class="n">Positive</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">ArcLargePositive</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1464 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1465 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1466 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1467 </code></pre></div> Err codemadness.org 70 i 1468 Err codemadness.org 70 i 1469 <h2>Creating the compact Path</h2> Err codemadness.org 70 i 1470 <p>Let's look at <code>PathBuilder::into_path</code> line by line:</p> Err codemadness.org 70 i 1471 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">PathBuilder</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1472 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">into_path</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Path</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1473 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">num_commands</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">path_commands</span><span class="p">.</span><span class="n">len</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 1474 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">num_coords</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="w"></span> Err codemadness.org 70 i 1475 <span class="w"> </span><span class="p">.</span><span class="n">path_commands</span><span class="w"></span> Err codemadness.org 70 i 1476 <span class="w"> </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 1477 <span class="w"> </span><span class="p">.</span><span class="n">fold</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="o">|</span><span class="n">acc</span><span class="p">,</span><span class="w"> </span><span class="n">cmd</span><span class="o">|</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">cmd</span><span class="p">.</span><span class="n">num_coordinates</span><span class="p">());</span><span class="w"></span> Err codemadness.org 70 i 1478 </code></pre></div> Err codemadness.org 70 i 1479 Err codemadness.org 70 i 1480 <p>First we compute the total number of coordinates using <code>fold</code>; we ask Err codemadness.org 70 i 1481 each command <code>cmd</code> its <code>num_coordinates()</code> and add it into the <code>acc</code> Err codemadness.org 70 i 1482 accumulator.</p> Err codemadness.org 70 i 1483 <p>Now we know how much memory to allocate:</p> Err codemadness.org 70 i 1484 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">packed_commands</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="n">num_commands</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 1485 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">coords</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mf">0.0</span><span class="p">;</span><span class="w"> </span><span class="n">num_coords</span><span class="p">];</span><span class="w"></span> Err codemadness.org 70 i 1486 </code></pre></div> Err codemadness.org 70 i 1487 Err codemadness.org 70 i 1488 <p>We use <code>Vec::with_capacity</code> to allocate exactly as much memory as we will Err codemadness.org 70 i 1489 need for the <code>packed_commands</code>; adding elements will not need a Err codemadness.org 70 i 1490 <code>realloc()</code>, since we already know how many elements we will have.</p> Err codemadness.org 70 i 1491 <p>We use the <code>vec!</code> macro to create an array of <code>0.0</code> repeated Err codemadness.org 70 i 1492 <code>num_coords</code> times; that macro uses <code>with_capacity</code> internally. That is the Err codemadness.org 70 i 1493 array we will use to store the coordinates for all the commands.</p> Err codemadness.org 70 i 1494 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">coords_slice</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">coords</span><span class="p">.</span><span class="n">as_mut_slice</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 1495 </code></pre></div> Err codemadness.org 70 i 1496 Err codemadness.org 70 i 1497 <p>We get a mutable slice out of the whole array of coordinates.</p> Err codemadness.org 70 i 1498 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">path_commands</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1499 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">c</span><span class="p">.</span><span class="n">num_coordinates</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 1500 <span class="w"> </span><span class="n">packed_commands</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">to_packed</span><span class="p">(</span><span class="n">coords_slice</span><span class="p">.</span><span class="n">get_mut</span><span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="n">n</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()));</span><span class="w"></span> Err codemadness.org 70 i 1501 <span class="w"> </span><span class="n">coords_slice</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">coords_slice</span><span class="p">[</span><span class="n">n</span><span class="o">..</span><span class="p">];</span><span class="w"></span> Err codemadness.org 70 i 1502 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1503 </code></pre></div> Err codemadness.org 70 i 1504 Err codemadness.org 70 i 1505 <p>For each command, we see how many coordinates it will generate and we Err codemadness.org 70 i 1506 put that number in <code>n</code>. We get a mutable sub-slice from Err codemadness.org 70 i 1507 <code>coords_slice</code> with only that number of elements, and pass it to Err codemadness.org 70 i 1508 <code>to_packed</code> for each command.</p> Err codemadness.org 70 i 1509 <p>At the end of each iteration we move the mutable slice to where the Err codemadness.org 70 i 1510 next command's coordinates will go.</p> Err codemadness.org 70 i 1511 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="n">Path</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1512 <span class="w"> </span><span class="n">commands</span>: <span class="nc">packed_commands</span><span class="p">.</span><span class="n">into_boxed_slice</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 1513 <span class="w"> </span><span class="n">coords</span>: <span class="nc">coords</span><span class="p">.</span><span class="n">into_boxed_slice</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 1514 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1515 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1516 </code></pre></div> Err codemadness.org 70 i 1517 Err codemadness.org 70 i 1518 <p>At the end, we create the final and immutable <code>Path</code> by converting Err codemadness.org 70 i 1519 each array <code>into_boxed_slice</code> like the last time. That way each of Err codemadness.org 70 i 1520 the two arrays, the one with <code>PackedCommand</code>s and the one with Err codemadness.org 70 i 1521 coordinates, occupy the minimum space they need.</p> Err codemadness.org 70 i 1522 <h2>An iterator for Path</h2> Err codemadness.org 70 i 1523 <p>This is all very well, but we also want it to be easy to iterate on Err codemadness.org 70 i 1524 that compact representation; the <code>PathCommand</code> enums from the Err codemadness.org 70 i 1525 beginning are very convenient to use and that's what the rest of the Err codemadness.org 70 i 1526 code already uses. Let's make an iterator that unpacks what is inside Err codemadness.org 70 i 1527 a <code>Path</code> and produces a <code>PathCommand</code> for each element.</p> Err codemadness.org 70 i 1528 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">PathIter</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1529 <span class="w"> </span><span class="n">commands</span>: <span class="nc">slice</span>::<span class="n">Iter</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">PackedCommand</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1530 <span class="w"> </span><span class="n">coords</span>: <span class="kp">&amp;</span><span class="o">&#39;</span><span class="na">a</span> <span class="p">[</span><span class="kt">f64</span><span class="p">],</span><span class="w"></span> Err codemadness.org 70 i 1531 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1532 </code></pre></div> Err codemadness.org 70 i 1533 Err codemadness.org 70 i 1534 <p>We need an iterator over the array of <code>PackedCommand</code> so we can visit Err codemadness.org 70 i 1535 each command. However, to get elements of <code>coords</code>, I am going to Err codemadness.org 70 i 1536 use a slice of <code>f64</code> instead of an iterator.</p> Err codemadness.org 70 i 1537 <p>Let's look at the implementation of the iterator:</p> Err codemadness.org 70 i 1538 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="o">&gt;</span><span class="w"> </span><span class="nb">Iterator</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">PathIter</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1539 <span class="w"> </span><span class="k">type</span> <span class="nc">Item</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PathCommand</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1540 Err codemadness.org 70 i 1541 <span class="w"> </span><span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="bp">Self</span>::<span class="n">Item</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1542 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">commands</span><span class="p">.</span><span class="n">next</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1543 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">cmd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PathCommand</span>::<span class="n">from_packed</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">coords</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 1544 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">num_coords</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cmd</span><span class="p">.</span><span class="n">num_coordinates</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 1545 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">coords</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="n">coords</span><span class="p">[</span><span class="n">num_coords</span><span class="o">..</span><span class="p">];</span><span class="w"></span> Err codemadness.org 70 i 1546 <span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 1547 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1548 <span class="w"> </span><span class="nb">None</span><span class="w"></span> Err codemadness.org 70 i 1549 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1550 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1551 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1552 </code></pre></div> Err codemadness.org 70 i 1553 Err codemadness.org 70 i 1554 <p>Since we want each iteration to produce a <code>PathCommand</code>, we declare it Err codemadness.org 70 i 1555 as having the associated <code>type Item =  PathCommand</code>.</p> Err codemadness.org 70 i 1556 <p>If the <code>self.commands</code> iterator has another element, it means there is Err codemadness.org 70 i 1557 another <code>PackedCommand</code> available.</p> Err codemadness.org 70 i 1558 <p>We call <code>PathCommand::from_packed</code> with the <code>self.coords</code> slice to Err codemadness.org 70 i 1559 unpack a command and its coordinates. We see how many coordinates the Err codemadness.org 70 i 1560 command consumed and re-slice <code>self.coords</code> according to the number of Err codemadness.org 70 i 1561 commands, so that it now points to the coordinates for the next Err codemadness.org 70 i 1562 command.</p> Err codemadness.org 70 i 1563 <p>We return <code>Some(cmd)</code> if there was an element, or <code>None</code> if the Err codemadness.org 70 i 1564 iterator is empty.</p> Err codemadness.org 70 i 1565 <p>The implementation of <code>from_packed</code> is obvious and I'll just paste a Err codemadness.org 70 i 1566 bit from it:</p> Err codemadness.org 70 i 1567 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">PathCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1568 <span class="w"> </span><span class="k">fn</span> <span class="nf">from_packed</span><span class="p">(</span><span class="n">packed</span>: <span class="kp">&amp;</span><span class="nc">PackedCommand</span><span class="p">,</span><span class="w"> </span><span class="n">coords</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">f64</span><span class="p">])</span><span class="w"> </span>-&gt; <span class="nc">PathCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1569 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="o">*</span><span class="n">packed</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1570 <span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">MoveTo</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1571 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span><span class="w"></span> Err codemadness.org 70 i 1572 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span><span class="w"></span> Err codemadness.org 70 i 1573 <span class="w"> </span><span class="n">PathCommand</span>::<span class="n">MoveTo</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 1574 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1575 Err codemadness.org 70 i 1576 <span class="w"> </span><span class="c1">// etc. for the other variants in PackedCommand</span> Err codemadness.org 70 i 1577 Err codemadness.org 70 i 1578 <span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">ArcSmallNegative</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PathCommand</span>::<span class="n">Arc</span><span class="p">(</span><span class="n">EllipticalArc</span>::<span class="n">from_coords</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 1579 <span class="w"> </span><span class="n">LargeArc</span><span class="p">(</span><span class="kc">false</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1580 <span class="w"> </span><span class="n">Sweep</span>::<span class="n">Negative</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1581 <span class="w"> </span><span class="n">coords</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1582 <span class="w"> </span><span class="p">)),</span><span class="w"></span> Err codemadness.org 70 i 1583 Err codemadness.org 70 i 1584 <span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">ArcSmallPositive</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="c1">// etc.</span> Err codemadness.org 70 i 1585 Err codemadness.org 70 i 1586 <span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">ArcLargeNegative</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="c1">// etc.</span> Err codemadness.org 70 i 1587 Err codemadness.org 70 i 1588 <span class="w"> </span><span class="n">PackedCommand</span>::<span class="n">ArcLargePositive</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="c1">// etc.</span> Err codemadness.org 70 i 1589 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1590 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1591 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1592 </code></pre></div> Err codemadness.org 70 i 1593 Err codemadness.org 70 i 1594 <h2>Results</h2> Err codemadness.org 70 i 1595 <p>Before the changes (this is the same Massif heading as above):</p> Err codemadness.org 70 i 1596 <div class="highlight"><pre><span></span><code><span class="nb">--------------------------------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 1597 <span class="c"> n time(i) total(B) useful</span><span class="nb">-</span><span class="c">heap(B) extra</span><span class="nb">-</span><span class="c">heap(B) stacks(B)</span> Err codemadness.org 70 i 1598 <span class="nb">--------------------------------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 1599 <span class="c"> 33 24</span><span class="nt">,</span><span class="c">139</span><span class="nt">,</span><span class="c">598</span><span class="nt">,</span><span class="c">653 1</span><span class="nt">,</span><span class="c">416</span><span class="nt">,</span><span class="c">831</span><span class="nt">,</span><span class="c">176 1</span><span class="nt">,</span><span class="c">329</span><span class="nt">,</span><span class="c">943</span><span class="nt">,</span><span class="c">212 86</span><span class="nt">,</span><span class="c">887</span><span class="nt">,</span><span class="c">964 0</span> Err codemadness.org 70 i 1600 <span class="c"> ^^^^^^^^^^^^^</span> Err codemadness.org 70 i 1601 <span class="c"> boo</span> Err codemadness.org 70 i 1602 </code></pre></div> Err codemadness.org 70 i 1603 Err codemadness.org 70 i 1604 <p>After:</p> Err codemadness.org 70 i 1605 <div class="highlight"><pre><span></span><code><span class="nb">--------------------------------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 1606 <span class="c"> n time(i) total(B) useful</span><span class="nb">-</span><span class="c">heap(B) extra</span><span class="nb">-</span><span class="c">heap(B) stacks(B)</span> Err codemadness.org 70 i 1607 <span class="nb">--------------------------------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 1608 <span class="c"> 28 26</span><span class="nt">,</span><span class="c">611</span><span class="nt">,</span><span class="c">886</span><span class="nt">,</span><span class="c">993 1</span><span class="nt">,</span><span class="c">093</span><span class="nt">,</span><span class="c">747</span><span class="nt">,</span><span class="c">888 1</span><span class="nt">,</span><span class="c">023</span><span class="nt">,</span><span class="c">147</span><span class="nt">,</span><span class="c">907 70</span><span class="nt">,</span><span class="c">599</span><span class="nt">,</span><span class="c">981 0</span> Err codemadness.org 70 i 1609 <span class="c"> ^^^^^^^^^^^^^</span> Err codemadness.org 70 i 1610 <span class="c"> oh yeah</span> Err codemadness.org 70 i 1611 </code></pre></div> Err codemadness.org 70 i 1612 Err codemadness.org 70 i 1613 <p>We went from using 1,329,943,212 bytes down to 1,023,147,907 bytes, Err codemadness.org 70 i 1614 that is, we knocked it down by 300 MB.</p> Err codemadness.org 70 i 1615 <p>However, that is for the whole program. Above we saw that <code>Path</code> data Err codemadness.org 70 i 1616 occupies 403,513,776 bytes; how about now?</p> Err codemadness.org 70 i 1617 <div class="highlight"><pre><span></span><code><span class="o">-&gt;</span><span class="mf">07.45</span><span class="o">%</span> <span class="p">(</span><span class="mi">81</span><span class="p">,</span><span class="mi">525</span><span class="p">,</span><span class="mi">328</span><span class="n">B</span><span class="p">)</span> <span class="mh">0x4A34C6F</span><span class="o">:</span> <span class="n">alloc</span> <span class="p">(</span><span class="n">alloc</span><span class="p">.</span><span class="n">rs</span><span class="o">:</span><span class="mi">84</span><span class="p">)</span> Err codemadness.org 70 i 1618 <span class="o">|</span> <span class="o">-&gt;</span><span class="mf">07.45</span><span class="o">%</span> <span class="p">(</span><span class="mi">81</span><span class="p">,</span><span class="mi">525</span><span class="p">,</span><span class="mi">328</span><span class="n">B</span><span class="p">)</span> <span class="mh">0x4A34C6F</span><span class="o">:</span> <span class="n">alloc</span> <span class="p">(</span><span class="n">alloc</span><span class="p">.</span><span class="n">rs</span><span class="o">:</span><span class="mi">172</span><span class="p">)</span> Err codemadness.org 70 i 1619 <span class="o">|</span> <span class="o">-&gt;</span><span class="mf">07.45</span><span class="o">%</span> <span class="p">(</span><span class="mi">81</span><span class="p">,</span><span class="mi">525</span><span class="p">,</span><span class="mi">328</span><span class="n">B</span><span class="p">)</span> <span class="mh">0x4A34C6F</span><span class="o">:</span> <span class="n">allocate_in</span><span class="o">&lt;</span><span class="n">f64</span><span class="p">,</span><span class="n">alloc</span><span class="o">::</span><span class="n">alloc</span><span class="o">::</span><span class="kr">Global</span><span class="o">&gt;</span> <span class="p">(</span><span class="n">raw_vec</span><span class="p">.</span><span class="n">rs</span><span class="o">:</span><span class="mi">98</span><span class="p">)</span> Err codemadness.org 70 i 1620 <span class="o">|</span> <span class="o">-&gt;</span><span class="mf">07.45</span><span class="o">%</span> <span class="p">(</span><span class="mi">81</span><span class="p">,</span><span class="mi">525</span><span class="p">,</span><span class="mi">328</span><span class="n">B</span><span class="p">)</span> <span class="mh">0x4A34C6F</span><span class="o">:</span> <span class="n">with_capacity</span><span class="o">&lt;</span><span class="n">f64</span><span class="o">&gt;</span> <span class="p">(</span><span class="n">raw_vec</span><span class="p">.</span><span class="n">rs</span><span class="o">:</span><span class="mi">167</span><span class="p">)</span> Err codemadness.org 70 i 1621 <span class="o">|</span> <span class="o">-&gt;</span><span class="mf">07.45</span><span class="o">%</span> <span class="p">(</span><span class="mi">81</span><span class="p">,</span><span class="mi">525</span><span class="p">,</span><span class="mi">328</span><span class="n">B</span><span class="p">)</span> <span class="mh">0x4A34C6F</span><span class="o">:</span> <span class="n">with_capacity</span><span class="o">&lt;</span><span class="n">f64</span><span class="o">&gt;</span> <span class="p">(</span><span class="n">vec</span><span class="p">.</span><span class="n">rs</span><span class="o">:</span><span class="mi">358</span><span class="p">)</span> Err codemadness.org 70 i 1622 <span class="o">|</span> <span class="o">-&gt;</span><span class="mf">07.45</span><span class="o">%</span> <span class="p">(</span><span class="mi">81</span><span class="p">,</span><span class="mi">525</span><span class="p">,</span><span class="mi">328</span><span class="n">B</span><span class="p">)</span> <span class="mh">0x4A34C6F</span><span class="o">:</span> <span class="n">rsvg_internals</span><span class="o">::</span><span class="n">path_builder</span><span class="o">::</span><span class="n">PathBuilder</span><span class="o">::</span><span class="n">into_path</span> <span class="p">(</span><span class="n">path_builder</span><span class="p">.</span><span class="n">rs</span><span class="o">:</span><span class="mi">486</span><span class="p">)</span> Err codemadness.org 70 i 1623 </code></pre></div> Err codemadness.org 70 i 1624 Err codemadness.org 70 i 1625 <p>Perfect. We went from occupying 403,513,776 bytes to just Err codemadness.org 70 i 1626 81,525,328 bytes. Instead of <code>Path</code> data amounting to 28.48% of the Err codemadness.org 70 i 1627 heap, it is just 7.45%.</p> Err codemadness.org 70 i 1628 <p>I think we can stop worrying about <code>Path</code> data for now. I like how Err codemadness.org 70 i 1629 this turned out without having to use <code>unsafe</code>.</p> Err codemadness.org 70 i 1630 <h2>References</h2> Err codemadness.org 70 i 1631 <ul> Err codemadness.org 70 i 1632 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/e9db621cdc79d31b8694d0f42cee4e02628ee145">Refactoring to use an iterator</a></li> Err codemadness.org 70 i 1633 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/cb4cde7140cd6ecfd8a78483278dcb1ab8217612">Adding tests for Path/PathBuilder</a></li> Err codemadness.org 70 i 1634 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/b183ac1e3207bd8110d60ded8878a22daed3f891">Using a more compact representation for Path</a></li> Err codemadness.org 70 i 1635 </ul>Reducing memory consumption in librsvg, part 3: slack space in Bézier paths2020-03-24T17:14:55-06:002020-03-24T17:14:55-06:00Federico Mena Quinterotag:people.gnome.org,2020-03-24:/~federico/blog/reducing-memory-consumption-in-librsvg-3.html<p>We got a <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/574">bug with a gigantic SVG</a> of a map extracted from Err codemadness.org 70 i 1636 OpenStreetMap, and it has about 600,000 elements. Most of them are Err codemadness.org 70 i 1637 <code>&lt;path&gt;</code>, that is, specifications for Bézier paths.</p> Err codemadness.org 70 i 1638 <p>A <code>&lt;path&gt;</code> can look like this:</p> Err codemadness.org 70 i 1639 <div class="highlight"><pre><span></span><code><span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">&quot;m 2239.05,1890.28 5.3,-1.81&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 1640 </code></pre></div> Err codemadness.org 70 i 1641 Err codemadness.org 70 i 1642 <p>The …</p><p>We got a <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/574">bug with a gigantic SVG</a> of a map extracted from Err codemadness.org 70 i 1643 OpenStreetMap, and it has about 600,000 elements. Most of them are Err codemadness.org 70 i 1644 <code>&lt;path&gt;</code>, that is, specifications for Bézier paths.</p> Err codemadness.org 70 i 1645 <p>A <code>&lt;path&gt;</code> can look like this:</p> Err codemadness.org 70 i 1646 <div class="highlight"><pre><span></span><code><span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">&quot;m 2239.05,1890.28 5.3,-1.81&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 1647 </code></pre></div> Err codemadness.org 70 i 1648 Err codemadness.org 70 i 1649 <p>The <code>d</code> attribute contains a <a href="https://www.w3.org/TR/SVG2/paths.html#TheDProperty">list of commands</a> to Err codemadness.org 70 i 1650 create a Bézier path, very similar to PostScript's operators. Librsvg Err codemadness.org 70 i 1651 has the following to represent those commands:</p> Err codemadness.org 70 i 1652 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">PathCommand</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1653 <span class="w"> </span><span class="n">MoveTo</span><span class="p">(</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="kt">f64</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1654 <span class="w"> </span><span class="n">LineTo</span><span class="p">(</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="kt">f64</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1655 <span class="w"> </span><span class="n">CurveTo</span><span class="p">(</span><span class="n">CubicBezierCurve</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1656 <span class="w"> </span><span class="n">Arc</span><span class="p">(</span><span class="n">EllipticalArc</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1657 <span class="w"> </span><span class="n">ClosePath</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1658 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1659 </code></pre></div> Err codemadness.org 70 i 1660 Err codemadness.org 70 i 1661 <p>Those commands get stored in an array, a <code>Vec</code> inside a <code>PathBuilder</code>:</p> Err codemadness.org 70 i 1662 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">PathBuilder</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1663 <span class="w"> </span><span class="n">path_commands</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PathCommand</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1664 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1665 </code></pre></div> Err codemadness.org 70 i 1666 Err codemadness.org 70 i 1667 <p>Librsvg translates each of the commands inside a <code>&lt;path d="..."/&gt;</code> Err codemadness.org 70 i 1668 into a <code>PathCommand</code> and pushes it into the <code>Vec</code> in the Err codemadness.org 70 i 1669 <code>PathBuilder</code>. When it is done parsing the attribute, the Err codemadness.org 70 i 1670 <code>PathBuilder</code> remains as the final version of the path.</p> Err codemadness.org 70 i 1671 <p>To let a <code>Vec</code> grow efficiently as items are pushed into Err codemadness.org 70 i 1672 it, Rust makes the <code>Vec</code> grow by powers of 2. When we add an item, if Err codemadness.org 70 i 1673 the <em>capacity</em> of the <code>Vec</code> is full, its buffer gets <code>realloc()</code>ed to Err codemadness.org 70 i 1674 twice its capacity. That way there are only O(log₂n) calls to Err codemadness.org 70 i 1675 <code>realloc()</code>, where <code>n</code> is the total number of items in the array.</p> Err codemadness.org 70 i 1676 <p>However, this means that once we are done adding items to the <code>Vec</code>, Err codemadness.org 70 i 1677 there may still be some free space in it: <em>the capacity exceeds the Err codemadness.org 70 i 1678 length of the array</em>. The invariant is that Err codemadness.org 70 i 1679 <code>vec.capacity() &gt;= vec.len()</code>.</p> Err codemadness.org 70 i 1680 <p>First I wanted to shrink the <code>PathBuilder</code>s so that they have no extra Err codemadness.org 70 i 1681 capacity in the end.</p> Err codemadness.org 70 i 1682 <h2>First step: convert to Box&lt;[T]&gt;</h2> Err codemadness.org 70 i 1683 <p>A "boxed slice" is a contiguous array in the heap, that cannot grow or Err codemadness.org 70 i 1684 shrink. That is, it has no extra capacity, only a length.</p> Err codemadness.org 70 i 1685 <p><code>Vec</code> has a method <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_boxed_slice"><code>into_boxed_slice</code></a> which does Err codemadness.org 70 i 1686 eactly that: it consumes the vector and converts it into a boxed Err codemadness.org 70 i 1687 slice without extra capacity. In its innards, it does a <code>realloc()</code> Err codemadness.org 70 i 1688 on the <code>Vec</code>'s buffer to match its length.</p> Err codemadness.org 70 i 1689 <p>Let's see the numbers that Massif reports:</p> Err codemadness.org 70 i 1690 <div class="highlight"><pre><span></span><code><span class="nb">--------------------------------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 1691 <span class="c"> n time(i) total(B) useful</span><span class="nb">-</span><span class="c">heap(B) extra</span><span class="nb">-</span><span class="c">heap(B) stacks(B)</span> Err codemadness.org 70 i 1692 <span class="nb">--------------------------------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 1693 <span class="c"> 23 22</span><span class="nt">,</span><span class="c">751</span><span class="nt">,</span><span class="c">613</span><span class="nt">,</span><span class="c">855 1</span><span class="nt">,</span><span class="c">560</span><span class="nt">,</span><span class="c">916</span><span class="nt">,</span><span class="c">408 1</span><span class="nt">,</span><span class="c">493</span><span class="nt">,</span><span class="c">746</span><span class="nt">,</span><span class="c">540 67</span><span class="nt">,</span><span class="c">169</span><span class="nt">,</span><span class="c">868 0</span> Err codemadness.org 70 i 1694 <span class="c"> ^^^^^^^^^^^^^</span> Err codemadness.org 70 i 1695 <span class="c"> before</span> Err codemadness.org 70 i 1696 Err codemadness.org 70 i 1697 <span class="c"> 30 22</span><span class="nt">,</span><span class="c">796</span><span class="nt">,</span><span class="c">106</span><span class="nt">,</span><span class="c">012 1</span><span class="nt">,</span><span class="c">553</span><span class="nt">,</span><span class="c">581</span><span class="nt">,</span><span class="c">072 1</span><span class="nt">,</span><span class="c">329</span><span class="nt">,</span><span class="c">943</span><span class="nt">,</span><span class="c">324 223</span><span class="nt">,</span><span class="c">637</span><span class="nt">,</span><span class="c">748 0</span> Err codemadness.org 70 i 1698 <span class="c"> ^^^^^^^^^^^^^</span> Err codemadness.org 70 i 1699 <span class="c"> after</span> Err codemadness.org 70 i 1700 </code></pre></div> Err codemadness.org 70 i 1701 Err codemadness.org 70 i 1702 <p>That is, we went from using 1,493,746,540 bytes on the heap to using Err codemadness.org 70 i 1703 1,329,943,324 bytes. Simply removing extra capacity from the path Err codemadness.org 70 i 1704 commands saves about 159 MB for this particular file.</p> Err codemadness.org 70 i 1705 <h2>Second step: make the allocator do less work</h2> Err codemadness.org 70 i 1706 <p>However, the <code>extra-heap</code> column in that table has a number I don't Err codemadness.org 70 i 1707 like: there are 223,637,748 bytes in <code>malloc()</code> metadata and unused Err codemadness.org 70 i 1708 space in the heap.</p> Err codemadness.org 70 i 1709 <p>I suppose that so many calls to <code>realloc()</code> make the heap a bit Err codemadness.org 70 i 1710 fragmented.</p> Err codemadness.org 70 i 1711 <p>It would be good to be able to read most of the <code>&lt;path d="..."/&gt;</code> to Err codemadness.org 70 i 1712 temporary buffers that don't need so many calls to <code>realloc()</code>, and Err codemadness.org 70 i 1713 that in the end get copied to exact-sized buffers, without extra Err codemadness.org 70 i 1714 capacity.</p> Err codemadness.org 70 i 1715 <p>We can do just that with the <a href="https://docs.rs/smallvec/1.2.0/smallvec/">smallvec</a> crate. A <code>SmallVec</code> has the Err codemadness.org 70 i 1716 same API as <code>Vec</code>, but it can store small arrays directly in the Err codemadness.org 70 i 1717 stack, without an extra heap allocation. Once the capacity is full, Err codemadness.org 70 i 1718 the stack buffer "spills" into a heap buffer automatically.</p> Err codemadness.org 70 i 1719 <p>Most of the <code>d</code> attributes in the huge file in the <a href="https://gitlab.gnome.org/GNOME/librsvg/-/issues/574">bug</a> have Err codemadness.org 70 i 1720 fewer than 32 commands. That is, if we use the following:</p> Err codemadness.org 70 i 1721 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">PathBuilder</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1722 <span class="w"> </span><span class="n">path_commands</span>: <span class="nc">SmallVec</span><span class="o">&lt;</span><span class="p">[</span><span class="n">PathCommand</span><span class="p">;</span><span class="w"> </span><span class="mi">32</span><span class="p">]</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1723 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1724 </code></pre></div> Err codemadness.org 70 i 1725 Err codemadness.org 70 i 1726 <p>We are saying that there can be up to 32 items in the <code>SmallVec</code> Err codemadness.org 70 i 1727 without causing a heap allocation; once that is exceeded, it will work Err codemadness.org 70 i 1728 like a normal <code>Vec</code>.</p> Err codemadness.org 70 i 1729 <p>At the end we still do <code>into_boxed_slice</code> to turn it into an Err codemadness.org 70 i 1730 independent heap allocation with an exact size.</p> Err codemadness.org 70 i 1731 <p>This reduces the <code>extra-heap</code> quite a bit:</p> Err codemadness.org 70 i 1732 <div class="highlight"><pre><span></span><code><span class="nb">--------------------------------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 1733 <span class="c"> n time(i) total(B) useful</span><span class="nb">-</span><span class="c">heap(B) extra</span><span class="nb">-</span><span class="c">heap(B) stacks(B)</span> Err codemadness.org 70 i 1734 <span class="nb">--------------------------------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 1735 <span class="c"> 33 24</span><span class="nt">,</span><span class="c">139</span><span class="nt">,</span><span class="c">598</span><span class="nt">,</span><span class="c">653 1</span><span class="nt">,</span><span class="c">416</span><span class="nt">,</span><span class="c">831</span><span class="nt">,</span><span class="c">176 1</span><span class="nt">,</span><span class="c">329</span><span class="nt">,</span><span class="c">943</span><span class="nt">,</span><span class="c">212 86</span><span class="nt">,</span><span class="c">887</span><span class="nt">,</span><span class="c">964 0</span> Err codemadness.org 70 i 1736 <span class="c"> ^^^^^^^^^^</span> Err codemadness.org 70 i 1737 </code></pre></div> Err codemadness.org 70 i 1738 Err codemadness.org 70 i 1739 <p>Also, the total bytes shrink from 1,553,581,072 to Err codemadness.org 70 i 1740 1,416,831,176 — we have a smaller heap because there is not so much Err codemadness.org 70 i 1741 work for the allocator, and there are a lot fewer temporary blocks Err codemadness.org 70 i 1742 when parsing the <code>d</code> attributes.</p> Err codemadness.org 70 i 1743 <h2>Making the code prettier</h2> Err codemadness.org 70 i 1744 <p>I put in the following:</p> Err codemadness.org 70 i 1745 <div class="highlight"><pre><span></span><code><span class="sd">/// This one is mutable</span> Err codemadness.org 70 i 1746 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">PathBuilder</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1747 <span class="w"> </span><span class="n">path_commands</span>: <span class="nc">SmallVec</span><span class="o">&lt;</span><span class="p">[</span><span class="n">PathCommand</span><span class="p">;</span><span class="w"> </span><span class="mi">32</span><span class="p">]</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1748 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1749 Err codemadness.org 70 i 1750 <span class="sd">/// This one is immutable</span> Err codemadness.org 70 i 1751 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Path</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1752 <span class="w"> </span><span class="n">path_commands</span>: <span class="nb">Box</span><span class="o">&lt;</span><span class="p">[</span><span class="n">PathCommand</span><span class="p">]</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1753 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1754 Err codemadness.org 70 i 1755 <span class="k">impl</span><span class="w"> </span><span class="n">PathBuilder</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1756 <span class="w"> </span><span class="sd">/// Consumes the PathBuilder and converts it into an immutable Path</span> Err codemadness.org 70 i 1757 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">into_path</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Path</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1758 <span class="w"> </span><span class="n">Path</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1759 <span class="w"> </span><span class="n">path_commands</span>: <span class="nc">self</span><span class="p">.</span><span class="n">path_commands</span><span class="p">.</span><span class="n">into_boxed_slice</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 1760 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1761 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1762 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1763 </code></pre></div> Err codemadness.org 70 i 1764 Err codemadness.org 70 i 1765 <p>With that, <code>PathBuilder</code> is just a temporary struct that turns into an Err codemadness.org 70 i 1766 immutable <code>Path</code> once we are done feeding it. <code>Path</code> contains a boxed Err codemadness.org 70 i 1767 slice of the exact size, without any extra capacity.</p> Err codemadness.org 70 i 1768 <h2>Next steps</h2> Err codemadness.org 70 i 1769 <p>All the coordinates in librsvg are stored as <code>f64</code>, double-precision Err codemadness.org 70 i 1770 floating point numbers. The SVG/CSS spec says that single-precision Err codemadness.org 70 i 1771 floats are enough, and that 64-bit floats should be used only for Err codemadness.org 70 i 1772 geometric transformations.</p> Err codemadness.org 70 i 1773 <p>I'm a bit scared to make that change; I'll have to look closely at the Err codemadness.org 70 i 1774 results of the test suite to see if rendered files change very much. Err codemadness.org 70 i 1775 I suppose even big maps require only as much precision as <code>f32</code> — Err codemadness.org 70 i 1776 after all, that is what OpenStreetMap uses.</p> Err codemadness.org 70 i 1777 <h2>References</h2> Err codemadness.org 70 i 1778 <ul> Err codemadness.org 70 i 1779 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/fc44abd1a8a85b7d8c474260e315cf4c73e4ac01">Convert the Vec into a Err codemadness.org 70 i 1780 Box&lt;[T]&gt;</a></li> Err codemadness.org 70 i 1781 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/ee63041d012406a9b05204ef604eb5411a8cf7ae">Convert to SmallVec</a></li> Err codemadness.org 70 i 1782 </ul>Reducing memory consumption in librsvg, part 2: SpecifiedValues2020-03-20T14:29:16-06:002020-03-20T14:29:16-06:00Federico Mena Quinterotag:people.gnome.org,2020-03-20:/~federico/blog/reducing-memory-consumption-in-librsvg-2.html<p>To continue with <a href="https://people.gnome.org/~federico/blog/reducing-memory-consumption-in-librsvg-1-es.html">last time's topic</a>, let's see how to make Err codemadness.org 70 i 1783 librsvg's DOM nodes smaller in memory. Since that time, there have Err codemadness.org 70 i 1784 been some changes to the code; that is why in this post some of the Err codemadness.org 70 i 1785 type names are different from last time's.</p> Err codemadness.org 70 i 1786 <p>Every SVG element is represented with …</p><p>To continue with <a href="https://people.gnome.org/~federico/blog/reducing-memory-consumption-in-librsvg-1-es.html">last time's topic</a>, let's see how to make Err codemadness.org 70 i 1787 librsvg's DOM nodes smaller in memory. Since that time, there have Err codemadness.org 70 i 1788 been some changes to the code; that is why in this post some of the Err codemadness.org 70 i 1789 type names are different from last time's.</p> Err codemadness.org 70 i 1790 <p>Every SVG element is represented with this struct:</p> Err codemadness.org 70 i 1791 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Element</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1792 <span class="w"> </span><span class="n">element_type</span>: <span class="nc">ElementType</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1793 <span class="w"> </span><span class="n">element_name</span>: <span class="nc">QualName</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1794 <span class="w"> </span><span class="n">id</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1795 <span class="w"> </span><span class="n">class</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1796 <span class="w"> </span><span class="n">specified_values</span>: <span class="nc">SpecifiedValues</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1797 <span class="w"> </span><span class="n">important_styles</span>: <span class="nc">HashSet</span><span class="o">&lt;</span><span class="n">QualName</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1798 <span class="w"> </span><span class="n">result</span>: <span class="nc">ElementResult</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1799 <span class="w"> </span><span class="n">transform</span>: <span class="nc">Transform</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1800 <span class="w"> </span><span class="n">values</span>: <span class="nc">ComputedValues</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1801 <span class="w"> </span><span class="n">cond</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1802 <span class="w"> </span><span class="n">style_attr</span>: <span class="nb">String</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1803 <span class="w"> </span><span class="n">element_impl</span>: <span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span><span class="w"> </span><span class="n">ElementTrait</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1804 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1805 </code></pre></div> Err codemadness.org 70 i 1806 Err codemadness.org 70 i 1807 <p>The two biggest fields are the ones with types <code>SpecifiedValues</code> and Err codemadness.org 70 i 1808 <code>ComputedValues</code>. These are the sizes of the whole <code>Element</code> struct Err codemadness.org 70 i 1809 and those two types:</p> Err codemadness.org 70 i 1810 <div class="highlight"><pre><span></span><code>sizeof Element: 1808 Err codemadness.org 70 i 1811 sizeof SpecifiedValues: 824 Err codemadness.org 70 i 1812 sizeof ComputedValues: 704 Err codemadness.org 70 i 1813 </code></pre></div> Err codemadness.org 70 i 1814 Err codemadness.org 70 i 1815 <p>In this post, we'll reduce the size of <code>SpecifiedValues</code>.</p> Err codemadness.org 70 i 1816 <h2>What is SpecifiedValues?</h2> Err codemadness.org 70 i 1817 <p>If we have an element like this:</p> Err codemadness.org 70 i 1818 <div class="highlight"><pre><span></span><code><span class="nt">&lt;circle</span> <span class="na">cx=</span><span class="s">&quot;10&quot;</span> <span class="na">cy=</span><span class="s">&quot;10&quot;</span> <span class="na">r=</span><span class="s">&quot;10&quot;</span> <span class="na">stroke-width=</span><span class="s">&quot;4&quot;</span> <span class="na">stroke=</span><span class="s">&quot;blue&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 1819 </code></pre></div> Err codemadness.org 70 i 1820 Err codemadness.org 70 i 1821 <p>The values of the style properties <code>stroke-width</code> and <code>stroke</code> get Err codemadness.org 70 i 1822 stored in a <code>SpecifiedValues</code> struct. This struct has a bunch of Err codemadness.org 70 i 1823 fields, one for each possible style property:</p> Err codemadness.org 70 i 1824 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">SpecifiedValues</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1825 <span class="w"> </span><span class="n">baseline_shift</span>: <span class="nc">SpecifiedValue</span><span class="o">&lt;</span><span class="n">BaselineShift</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1826 <span class="w"> </span><span class="n">clip_path</span>: <span class="nc">SpecifiedValue</span><span class="o">&lt;</span><span class="n">ClipPath</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1827 <span class="w"> </span><span class="n">clip_rule</span>: <span class="nc">SpecifiedValue</span><span class="o">&lt;</span><span class="n">ClipRule</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1828 <span class="w"> </span><span class="sd">/// ...</span> Err codemadness.org 70 i 1829 <span class="w"> </span><span class="n">stroke</span>: <span class="nc">SpecifiedValue</span><span class="o">&lt;</span><span class="n">Stroke</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1830 <span class="w"> </span><span class="n">stroke_width</span>: <span class="nc">SpecifiedValue</span><span class="o">&lt;</span><span class="n">StrokeWidth</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1831 <span class="w"> </span><span class="sd">/// ...</span> Err codemadness.org 70 i 1832 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1833 </code></pre></div> Err codemadness.org 70 i 1834 Err codemadness.org 70 i 1835 <p>Each field is a <code>SpecifiedValue&lt;T&gt;</code> for the following reason. In Err codemadness.org 70 i 1836 CSS/SVG, a style property can be unspecified, or it can have an Err codemadness.org 70 i 1837 <code>inherit</code> value to force the property to be copied from the element's Err codemadness.org 70 i 1838 parent, or it can actually have a specified value. Librsvg represents Err codemadness.org 70 i 1839 these as follows:</p> Err codemadness.org 70 i 1840 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">SpecifiedValue</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"></span> Err codemadness.org 70 i 1841 <span class="k">where</span><span class="w"></span> Err codemadness.org 70 i 1842 <span class="w"> </span><span class="n">T</span>: <span class="c1">// some trait bounds here</span> Err codemadness.org 70 i 1843 <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1844 <span class="w"> </span><span class="n">Unspecified</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1845 <span class="w"> </span><span class="n">Inherit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1846 <span class="w"> </span><span class="n">Specified</span><span class="p">(</span><span class="n">T</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1847 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1848 </code></pre></div> Err codemadness.org 70 i 1849 Err codemadness.org 70 i 1850 <p>Now, <code>SpecifiedValues</code> has a bunch of fields, 47 of them to be exact — Err codemadness.org 70 i 1851 one for each of the style properties that librsvg supports. That is Err codemadness.org 70 i 1852 why <code>SpecifiedValues</code> has a size of 824 bytes; it is the largest Err codemadness.org 70 i 1853 sub-structure within <code>Element</code>, and it would be good to reduce its Err codemadness.org 70 i 1854 size.</p> Err codemadness.org 70 i 1855 <h2>Not all properties are specified</h2> Err codemadness.org 70 i 1856 <p>Let's go back to the chunk of SVG from above:</p> Err codemadness.org 70 i 1857 <div class="highlight"><pre><span></span><code><span class="nt">&lt;circle</span> <span class="na">cx=</span><span class="s">&quot;10&quot;</span> <span class="na">cy=</span><span class="s">&quot;10&quot;</span> <span class="na">r=</span><span class="s">&quot;10&quot;</span> <span class="na">stroke-width=</span><span class="s">&quot;4&quot;</span> <span class="na">stroke=</span><span class="s">&quot;blue&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 1858 </code></pre></div> Err codemadness.org 70 i 1859 Err codemadness.org 70 i 1860 <p>Here we only have two specified properties, so the <code>stroke_width</code> and Err codemadness.org 70 i 1861 <code>stroke</code> fields of <code>SpecifiedValues</code> will be set as Err codemadness.org 70 i 1862 <code>SpecifiedValue::Specified(something)</code> and all the other fields will Err codemadness.org 70 i 1863 be left as <code>SpecifiedValue::Unspecified</code>.</p> Err codemadness.org 70 i 1864 <p>It would be good to store only complete values for the properties that Err codemadness.org 70 i 1865 are specified, and just a small flag for unset properties.</p> Err codemadness.org 70 i 1866 <h2>Another way to represent the set of properties</h2> Err codemadness.org 70 i 1867 <p>Since there is a maximum of 47 properties per element (or more if Err codemadness.org 70 i 1868 librsvg adds support for extra ones), we can have a small array of Err codemadness.org 70 i 1869 47 bytes. Each byte contains the index within another array that Err codemadness.org 70 i 1870 contains only the values of specified properties, or a sentinel value Err codemadness.org 70 i 1871 for properties that are unset.</p> Err codemadness.org 70 i 1872 <p>First, I made an enum that fits in a <code>u8</code> for all the properties, plus Err codemadness.org 70 i 1873 the sentinel value, which also gives us the total number of Err codemadness.org 70 i 1874 properties. The <code>#[repr(u8)]</code> guarantees that this enum fits in a Err codemadness.org 70 i 1875 byte.</p> Err codemadness.org 70 i 1876 <div class="highlight"><pre><span></span><code><span class="cp">#[repr(u8)]</span><span class="w"></span> Err codemadness.org 70 i 1877 <span class="k">enum</span> <span class="nc">PropertyId</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1878 <span class="w"> </span><span class="n">BaselineShift</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1879 <span class="w"> </span><span class="n">ClipPath</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1880 <span class="w"> </span><span class="n">ClipRule</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1881 <span class="w"> </span><span class="n">Color</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1882 <span class="w"> </span><span class="c1">// ...</span> Err codemadness.org 70 i 1883 <span class="w"> </span><span class="n">WritingMode</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1884 <span class="w"> </span><span class="n">XmlLang</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1885 <span class="w"> </span><span class="n">XmlSpace</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1886 <span class="w"> </span><span class="n">UnsetProperty</span><span class="p">,</span><span class="w"> </span><span class="c1">// the number of properties and also the sentinel value</span> Err codemadness.org 70 i 1887 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1888 </code></pre></div> Err codemadness.org 70 i 1889 Err codemadness.org 70 i 1890 <p>Also, since before these changes there was the following monster to Err codemadness.org 70 i 1891 represent "which property is this" plus the property's value:</p> Err codemadness.org 70 i 1892 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">ParsedProperty</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1893 <span class="w"> </span><span class="n">BaselineShift</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">BaselineShift</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1894 <span class="w"> </span><span class="n">ClipPath</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">ClipPath</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1895 <span class="w"> </span><span class="n">ClipRule</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">ClipRule</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1896 <span class="w"> </span><span class="n">Color</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">Color</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 1897 <span class="w"> </span><span class="c1">// ...</span> Err codemadness.org 70 i 1898 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1899 </code></pre></div> Err codemadness.org 70 i 1900 Err codemadness.org 70 i 1901 <p>I changed the definition of <code>SpecifiedValues</code> to have two arrays, one Err codemadness.org 70 i 1902 to store which properties are specified, and another only with the Err codemadness.org 70 i 1903 values for the properties that are actually specified:</p> Err codemadness.org 70 i 1904 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">SpecifiedValues</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1905 <span class="w"> </span><span class="n">indices</span>: <span class="p">[</span><span class="kt">u8</span><span class="p">;</span><span class="w"> </span><span class="n">PropertyId</span>::<span class="n">UnsetProperty</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">],</span><span class="w"></span> Err codemadness.org 70 i 1906 <span class="w"> </span><span class="n">props</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">ParsedProperty</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1907 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1908 </code></pre></div> Err codemadness.org 70 i 1909 Err codemadness.org 70 i 1910 <p>There is a thing that is awkward in Rust, or which I haven't found how Err codemadness.org 70 i 1911 to solve in a nicer way: given a <code>ParsedProperty</code>, find the Err codemadness.org 70 i 1912 corresponding <code>PropertyId</code> for its discriminant. I did the obvious Err codemadness.org 70 i 1913 thing:</p> Err codemadness.org 70 i 1914 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">ParsedProperty</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1915 <span class="w"> </span><span class="k">fn</span> <span class="nf">get_property_id</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PropertyId</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1916 <span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="n">ParsedProperty</span>::<span class="o">*</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1917 Err codemadness.org 70 i 1918 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="o">*</span><span class="bp">self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1919 <span class="w"> </span><span class="n">BaselineShift</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PropertyId</span>::<span class="n">BaselineShift</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1920 <span class="w"> </span><span class="n">ClipPath</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PropertyId</span>::<span class="n">ClipPath</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1921 <span class="w"> </span><span class="n">ClipRule</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PropertyId</span>::<span class="n">ClipRule</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1922 <span class="w"> </span><span class="n">Color</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">PropertyId</span>::<span class="n">Color</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 1923 <span class="w"> </span><span class="c1">// ...</span> Err codemadness.org 70 i 1924 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1925 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1926 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1927 </code></pre></div> Err codemadness.org 70 i 1928 Err codemadness.org 70 i 1929 <h2>Initialization</h2> Err codemadness.org 70 i 1930 <p>First, we want to initialize an empty <code>SpecifiedValues</code>, where every Err codemadness.org 70 i 1931 element of the the <code>indices</code> array is set to the sentinel value that Err codemadness.org 70 i 1932 means that the corresponding property is not set:</p> Err codemadness.org 70 i 1933 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="nb">Default</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">SpecifiedValues</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1934 <span class="w"> </span><span class="k">fn</span> <span class="nf">default</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1935 <span class="w"> </span><span class="n">SpecifiedValues</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1936 <span class="w"> </span><span class="n">indices</span>: <span class="p">[</span><span class="n">PropertyId</span>::<span class="n">UnsetProperty</span><span class="p">.</span><span class="n">as_u8</span><span class="p">();</span><span class="w"> </span><span class="n">PropertyId</span>::<span class="n">UnsetProperty</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">],</span><span class="w"></span> Err codemadness.org 70 i 1937 <span class="w"> </span><span class="n">props</span>: <span class="nb">Vec</span>::<span class="n">new</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 1938 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1939 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1940 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1941 </code></pre></div> Err codemadness.org 70 i 1942 Err codemadness.org 70 i 1943 <p>That sets the <code>indices</code> field to an array full of the same Err codemadness.org 70 i 1944 <code>PropertyId::UnsetProperty</code> sentinel value. Also, the <code>props</code> array Err codemadness.org 70 i 1945 is empty; it hasn't even had a block of memory allocated for it yet. Err codemadness.org 70 i 1946 That way, SVG elements without style properties don't use any extra Err codemadness.org 70 i 1947 memory.</p> Err codemadness.org 70 i 1948 <h2>Which properties are specified and what are their indices?</h2> Err codemadness.org 70 i 1949 <p>Second, we want a function that will give us the index in <code>props</code> for Err codemadness.org 70 i 1950 some property, or that will tell us if the property has not been set Err codemadness.org 70 i 1951 yet:</p> Err codemadness.org 70 i 1952 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">SpecifiedValues</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1953 <span class="w"> </span><span class="k">fn</span> <span class="nf">property_index</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">id</span>: <span class="nc">PropertyId</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="kt">usize</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1954 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">indices</span><span class="p">[</span><span class="n">id</span><span class="p">.</span><span class="n">as_usize</span><span class="p">()];</span><span class="w"></span> Err codemadness.org 70 i 1955 Err codemadness.org 70 i 1956 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">PropertyId</span>::<span class="n">UnsetProperty</span><span class="p">.</span><span class="n">as_u8</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1957 <span class="w"> </span><span class="nb">None</span><span class="w"></span> Err codemadness.org 70 i 1958 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1959 <span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">v</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 1960 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1961 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1962 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1963 </code></pre></div> Err codemadness.org 70 i 1964 Err codemadness.org 70 i 1965 <p>(If someone passes <code>id = PropertyId::UnsetProperty</code>, the array access Err codemadness.org 70 i 1966 to <code>indices</code> will panic, which is what we want, since <em>that</em> is not a Err codemadness.org 70 i 1967 valid property id.)</p> Err codemadness.org 70 i 1968 <h2>Change a property's value</h2> Err codemadness.org 70 i 1969 <p>Third, we want to set the value of a property that has not been set, Err codemadness.org 70 i 1970 or change the value of one that was already specified:</p> Err codemadness.org 70 i 1971 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">SpecifiedValues</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1972 <span class="w"> </span><span class="k">fn</span> <span class="nf">replace_property</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">prop</span>: <span class="kp">&amp;</span><span class="nc">ParsedProperty</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1973 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">prop</span><span class="p">.</span><span class="n">get_property_id</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 1974 Err codemadness.org 70 i 1975 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">index</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">property_index</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1976 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">props</span><span class="p">[</span><span class="n">index</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">prop</span><span class="p">.</span><span class="n">clone</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 1977 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 1978 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">prop</span><span class="p">.</span><span class="n">clone</span><span class="p">());</span><span class="w"></span> Err codemadness.org 70 i 1979 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">pos</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">len</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1980 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">indices</span><span class="p">[</span><span class="n">id</span><span class="p">.</span><span class="n">as_usize</span><span class="p">()]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pos</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u8</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 1981 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1982 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1983 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 1984 </code></pre></div> Err codemadness.org 70 i 1985 Err codemadness.org 70 i 1986 <p>In the first case in the <code>if</code>, the property was already set and we Err codemadness.org 70 i 1987 just replace its value. In the second case, the property was not set; Err codemadness.org 70 i 1988 we add it to the <code>props</code> array and store its resulting index in Err codemadness.org 70 i 1989 <code>indices</code>.</p> Err codemadness.org 70 i 1990 <h2>Results</h2> Err codemadness.org 70 i 1991 <p>Before:</p> Err codemadness.org 70 i 1992 <div class="highlight"><pre><span></span><code>sizeof Element: 1808 Err codemadness.org 70 i 1993 sizeof SpecifiedValues: 824 Err codemadness.org 70 i 1994 </code></pre></div> Err codemadness.org 70 i 1995 Err codemadness.org 70 i 1996 <p>After:</p> Err codemadness.org 70 i 1997 <div class="highlight"><pre><span></span><code>sizeof Element: 1056 Err codemadness.org 70 i 1998 sizeof SpecifiedValues: 72 Err codemadness.org 70 i 1999 </code></pre></div> Err codemadness.org 70 i 2000 Err codemadness.org 70 i 2001 <p>The pathological file <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/42">from the last time</a> used Err codemadness.org 70 i 2002 463,412,720 bytes in memory before these changes. After the changes, Err codemadness.org 70 i 2003 it uses 314,526,136 bytes.</p> Err codemadness.org 70 i 2004 <p>I also measured memory consumption for a normal file, in this case Err codemadness.org 70 i 2005 <a href="https://gitlab.gnome.org/Teams/Design/icon-development-kit/-/blob/master/src/icons.svg">one with a bunch of GNOME's symbolic icons</a>. The old version Err codemadness.org 70 i 2006 uses 17 MB; the new version only 13 MB.</p> Err codemadness.org 70 i 2007 <h2>How to keep fine-tuning this</h2> Err codemadness.org 70 i 2008 <p>For now, I am satisfied with <code>SpecifiedValues</code>, although it could Err codemadness.org 70 i 2009 still be made smaller:</p> Err codemadness.org 70 i 2010 <ul> Err codemadness.org 70 i 2011 <li> Err codemadness.org 70 i 2012 <p>The crate <a href="https://lib.rs/crates/tagged-box">tagged-box</a> converts an enum like <code>ParsedProperty</code> into Err codemadness.org 70 i 2013 an enum-of-boxes, and codifies the enum's discriminant into the Err codemadness.org 70 i 2014 box's pointer. This way each variant occupies the minimum possible Err codemadness.org 70 i 2015 memory, although in a separately-allocated block, and the container Err codemadness.org 70 i 2016 itself uses only a pointer. I am not sure if this is worth it; each Err codemadness.org 70 i 2017 <code>ParsedProperty</code> is 64 bytes, but the flat array <code>props: Err codemadness.org 70 i 2018 Vec&lt;ParsedProperty&gt;</code> is very appealing in a single block of memory. Err codemadness.org 70 i 2019 I have not checked the sizes of each individual property to see if Err codemadness.org 70 i 2020 they vary a lot among them.</p> Err codemadness.org 70 i 2021 </li> Err codemadness.org 70 i 2022 <li> Err codemadness.org 70 i 2023 <p>Look for a crate that lets us have the properties in a single memory Err codemadness.org 70 i 2024 block, a kind of arena with variable types. This can be implemented Err codemadness.org 70 i 2025 with a bit of <code>unsafe</code>, but one has to be careful with the alignment Err codemadness.org 70 i 2026 of different types.</p> Err codemadness.org 70 i 2027 </li> Err codemadness.org 70 i 2028 <li> Err codemadness.org 70 i 2029 <p>The crate <a href="https://lib.rs/crates/enum_set2">enum_set2</a> represents an array of field-less enums as a Err codemadness.org 70 i 2030 compact bit array. If we changed the representation of Err codemadness.org 70 i 2031 <code>SpecifiedValue</code>, this would reduce the <code>indices</code> array to a Err codemadness.org 70 i 2032 minimum.</p> Err codemadness.org 70 i 2033 </li> Err codemadness.org 70 i 2034 </ul> Err codemadness.org 70 i 2035 <p>If someone wants to dedicate some time to implement and measure this, Err codemadness.org 70 i 2036 I would be very grateful.</p> Err codemadness.org 70 i 2037 <h2>Next steps</h2> Err codemadness.org 70 i 2038 <p>According to Massif, the next thing is to keep making <code>Element</code> Err codemadness.org 70 i 2039 smaller. The next thing to shrink is <code>ComputedValues</code>. The obvious Err codemadness.org 70 i 2040 route is to do exactly the same as I did for <code>SpecifiedValues</code>. I am Err codemadness.org 70 i 2041 not sure if it would be better to try to <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/570">share the style Err codemadness.org 70 i 2042 structs</a> between elements.</p>Librsvg accepting interns for Summer of Code 20202020-03-16T17:53:17-06:002020-03-16T17:53:17-06:00Federico Mena Quinterotag:people.gnome.org,2020-03-16:/~federico/blog/librsvg-soc-2020.html<p>Are you a student qualified to run for Summer of Code 2020? I'm Err codemadness.org 70 i 2043 willing to mentor the following project for librsvg.</p> Err codemadness.org 70 i 2044 <h2>Project: Revamp the text engine in librsvg</h2> Err codemadness.org 70 i 2045 <p>Librsvg supports only a few features of the <a href="https://www.w3.org/TR/SVG2/text.html">SVG Text Err codemadness.org 70 i 2046 specification</a>. It requires Err codemadness.org 70 i 2047 extra features to be really useful:</p> Err codemadness.org 70 i 2048 <ul> Err codemadness.org 70 i 2049 <li> Err codemadness.org 70 i 2050 <p><strong>Proper bidirectional support …</strong></p></li></ul><p>Are you a student qualified to run for Summer of Code 2020? I'm Err codemadness.org 70 i 2051 willing to mentor the following project for librsvg.</p> Err codemadness.org 70 i 2052 <h2>Project: Revamp the text engine in librsvg</h2> Err codemadness.org 70 i 2053 <p>Librsvg supports only a few features of the <a href="https://www.w3.org/TR/SVG2/text.html">SVG Text Err codemadness.org 70 i 2054 specification</a>. It requires Err codemadness.org 70 i 2055 extra features to be really useful:</p> Err codemadness.org 70 i 2056 <ul> Err codemadness.org 70 i 2057 <li> Err codemadness.org 70 i 2058 <p><strong>Proper bidirectional support.</strong> Librsvg supports the <code>direction</code> and Err codemadness.org 70 i 2059 <code>unicode-bidi</code> properties for text elements, among others, but in a Err codemadness.org 70 i 2060 very rudimentary fashion. It just translates those properties to Err codemadness.org 70 i 2061 Pango terminology and asks <code>PangoLayout</code> to lay out the text. SVG Err codemadness.org 70 i 2062 really wants finer control of that, for which...</p> Err codemadness.org 70 i 2063 </li> Err codemadness.org 70 i 2064 <li> Err codemadness.org 70 i 2065 <p>... ideally you would make librsvg <strong>use Harfbuzz directly</strong>, or a Err codemadness.org 70 i 2066 wrapper that is close to its level of operation. Pango is a bit too high Err codemadness.org 70 i 2067 level for the needs of SVG.</p> Err codemadness.org 70 i 2068 </li> Err codemadness.org 70 i 2069 <li> Err codemadness.org 70 i 2070 <p>Manual layout of text glyphs. After a text engine like Harfbuzz Err codemadness.org 70 i 2071 does the shaping, librsvg would need to lay out the produced glyphs in Err codemadness.org 70 i 2072 the way of the SVG attributes <code>dx, dy, x, y</code>, etc. The SVG Text Err codemadness.org 70 i 2073 specification has the algorithms for this.</p> Err codemadness.org 70 i 2074 </li> Err codemadness.org 70 i 2075 <li> Err codemadness.org 70 i 2076 <p>The cherry on top: text-on-a-path. Again, the spec has the details. Err codemadness.org 70 i 2077 You would make Wikimedia content creators very happy with this!</p> Err codemadness.org 70 i 2078 </li> Err codemadness.org 70 i 2079 </ul> Err codemadness.org 70 i 2080 <p><strong>Requirements:</strong> Rust for programming language; some familiarity with Err codemadness.org 70 i 2081 Unicode concepts and text layout. Familiarity with Cairo and Harfbuzz Err codemadness.org 70 i 2082 would help a lot. Preference will be given to people who can write a Err codemadness.org 70 i 2083 right-to-left human language, <strong>or</strong> a language that requires complex Err codemadness.org 70 i 2084 shaping.</p> Err codemadness.org 70 i 2085 <p><a href="https://wiki.gnome.org/Outreach/SummerOfCode/Students">Details for students</a></p>Reducing memory consumption in librsvg, part 1: text nodes2020-03-12T18:57:35-06:002020-03-12T18:57:35-06:00Federico Mena Quinterotag:people.gnome.org,2020-03-12:/~federico/blog/reducing-memory-consumption-in-librsvg-1.html<p>Librsvg's memory consumption has not been a problem so far for GNOME's Err codemadness.org 70 i 2086 use cases, which is basically rendering icons. But for SVG files with Err codemadness.org 70 i 2087 thousands of elements, it could do a lot better.</p> Err codemadness.org 70 i 2088 <h2>Memory consumption in the DOM</h2> Err codemadness.org 70 i 2089 <p>Librsvg shares some common problems with web browsers: it must Err codemadness.org 70 i 2090 construct a …</p><p>Librsvg's memory consumption has not been a problem so far for GNOME's Err codemadness.org 70 i 2091 use cases, which is basically rendering icons. But for SVG files with Err codemadness.org 70 i 2092 thousands of elements, it could do a lot better.</p> Err codemadness.org 70 i 2093 <h2>Memory consumption in the DOM</h2> Err codemadness.org 70 i 2094 <p>Librsvg shares some common problems with web browsers: it must Err codemadness.org 70 i 2095 construct a DOM tree in memory with SVG elements, and keep a bunch of Err codemadness.org 70 i 2096 information for each of the tree's nodes. For example, each SVG Err codemadness.org 70 i 2097 element may have an <code>id</code> attribute, or a <code>class</code>; each one has a Err codemadness.org 70 i 2098 transformation matrix; etc.</p> Err codemadness.org 70 i 2099 <p>Apart from the tree node metadata (pointers to sibling and parent Err codemadness.org 70 i 2100 nodes), each node has this:</p> Err codemadness.org 70 i 2101 <div class="highlight"><pre><span></span><code><span class="sd">/// Contents of a tree node</span> Err codemadness.org 70 i 2102 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">NodeData</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2103 <span class="w"> </span><span class="n">node_type</span>: <span class="nc">NodeType</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2104 <span class="w"> </span><span class="n">element_name</span>: <span class="nc">QualName</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2105 <span class="w"> </span><span class="n">id</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="c1">// id attribute from XML element</span> Err codemadness.org 70 i 2106 <span class="w"> </span><span class="n">class</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="c1">// class attribute from XML element</span> Err codemadness.org 70 i 2107 <span class="w"> </span><span class="n">specified_values</span>: <span class="nc">SpecifiedValues</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2108 <span class="w"> </span><span class="n">important_styles</span>: <span class="nc">HashSet</span><span class="o">&lt;</span><span class="n">QualName</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2109 <span class="w"> </span><span class="n">result</span>: <span class="nc">NodeResult</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2110 <span class="w"> </span><span class="n">transform</span>: <span class="nc">Transform</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2111 <span class="w"> </span><span class="n">values</span>: <span class="nc">ComputedValues</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2112 <span class="w"> </span><span class="n">cond</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2113 <span class="w"> </span><span class="n">style_attr</span>: <span class="nb">String</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2114 Err codemadness.org 70 i 2115 <span class="w"> </span><span class="n">node_impl</span>: <span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span><span class="w"> </span><span class="n">NodeTrait</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="c1">// concrete struct for node types</span> Err codemadness.org 70 i 2116 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2117 </code></pre></div> Err codemadness.org 70 i 2118 Err codemadness.org 70 i 2119 <p>On a 64-bit box, that <code>NodeData</code> struct is 1808 bytes. And the biggest fields Err codemadness.org 70 i 2120 are the <code>SpecifiedValues</code> (824 bytes) and <code>ComputedValues</code> (704 bytes).</p> Err codemadness.org 70 i 2121 <p>Librsvg represents <em>all</em> tree nodes with that struct. Consider an SVG Err codemadness.org 70 i 2122 like this:</p> Err codemadness.org 70 i 2123 <div class="highlight"><pre><span></span><code><span class="nt">&lt;svg</span> <span class="na">xmlns=</span><span class="s">&quot;http://www.w3.org/2000/svg&quot;</span> <span class="na">width=</span><span class="s">&quot;100&quot;</span> <span class="na">height=</span><span class="s">&quot;100&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 2124 <span class="nt">&lt;rect</span> <span class="na">x=</span><span class="s">&quot;10&quot;</span> <span class="na">y=</span><span class="s">&quot;20&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 2125 <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">&quot;...&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 2126 <span class="nt">&lt;text</span> <span class="na">x=</span><span class="s">&quot;10&quot;</span> <span class="na">y=</span><span class="s">&quot;20&quot;</span><span class="nt">&gt;</span>Hello<span class="nt">&lt;/text&gt;</span> Err codemadness.org 70 i 2127 <span class="c">&lt;!-- etc --&gt;</span> Err codemadness.org 70 i 2128 <span class="nt">&lt;/svg&gt;</span> Err codemadness.org 70 i 2129 </code></pre></div> Err codemadness.org 70 i 2130 Err codemadness.org 70 i 2131 <p>There are 4 elements in that file. However, there are also tree nodes Err codemadness.org 70 i 2132 for the XML text nodes, that is, the whitespace between tags and the Err codemadness.org 70 i 2133 "<code>Hello</code>" inside the <code>&lt;text&gt;</code> element.</p> Err codemadness.org 70 i 2134 <p>The contents of each of those text nodes is tiny (a newline and maybe Err codemadness.org 70 i 2135 a couple of spaces), but each node still takes up at least 1808 bytes Err codemadness.org 70 i 2136 from the <code>NodeData</code> struct, plus the size of the text string.</p> Err codemadness.org 70 i 2137 <p>Let's refactor this to make it easier to remove that overhead.</p> Err codemadness.org 70 i 2138 <h2>First step: separate text nodes from element nodes</h2> Err codemadness.org 70 i 2139 <p>Internally, librsvg represents XML text nodes with a <code>NodeChars</code> struct Err codemadness.org 70 i 2140 which is basically a string with some extra stuff. All the concrete Err codemadness.org 70 i 2141 structs for tree node types must implement a trait called <code>NodeTrait</code>, Err codemadness.org 70 i 2142 and <code>NodeChars</code> is no exception:</p> Err codemadness.org 70 i 2143 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">NodeChars</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2144 <span class="w"> </span><span class="c1">// a string with the text node&#39;s contents</span> Err codemadness.org 70 i 2145 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2146 Err codemadness.org 70 i 2147 <span class="k">impl</span><span class="w"> </span><span class="n">NodeTrait</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">NodeChars</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2148 <span class="w"> </span><span class="c1">// a mostly empty impl with methods that do nothing</span> Err codemadness.org 70 i 2149 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2150 </code></pre></div> Err codemadness.org 70 i 2151 Err codemadness.org 70 i 2152 <p>You don't see it in the definition of <code>NodeData</code> in the previous Err codemadness.org 70 i 2153 section, but for a text node, the <code>NodeData.node_impl</code> field would Err codemadness.org 70 i 2154 point to a heap-allocated <code>NodeChars</code> (it can do that, since Err codemadness.org 70 i 2155 <code>NodeChars</code> implements <code>NodeTrait</code>, so it can go into <code>node_impl: Err codemadness.org 70 i 2156 Box&lt;dyn NodeTrait&gt;</code>).</p> Err codemadness.org 70 i 2157 <p>First, I turned the <code>NodeData</code> struct into an enum with two variants, Err codemadness.org 70 i 2158 and moved all of its previous fields to an <code>Element</code> struct:</p> Err codemadness.org 70 i 2159 <div class="highlight"><pre><span></span><code><span class="c1">// This one is new</span> Err codemadness.org 70 i 2160 <span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">NodeData</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2161 <span class="w"> </span><span class="n">Element</span><span class="p">(</span><span class="n">Element</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 2162 <span class="w"> </span><span class="n">Text</span><span class="p">(</span><span class="n">NodeChars</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 2163 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2164 Err codemadness.org 70 i 2165 <span class="c1">// This is the old struct with a different name</span> Err codemadness.org 70 i 2166 <span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">Element</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2167 <span class="w"> </span><span class="n">node_type</span>: <span class="nc">NodeType</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2168 <span class="w"> </span><span class="n">element_name</span>: <span class="nc">QualName</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2169 <span class="w"> </span><span class="n">id</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2170 <span class="w"> </span><span class="n">class</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2171 <span class="w"> </span><span class="n">specified_values</span>: <span class="nc">SpecifiedValues</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2172 <span class="w"> </span><span class="n">important_styles</span>: <span class="nc">HashSet</span><span class="o">&lt;</span><span class="n">QualName</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2173 <span class="w"> </span><span class="n">result</span>: <span class="nc">NodeResult</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2174 <span class="w"> </span><span class="n">transform</span>: <span class="nc">Transform</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2175 <span class="w"> </span><span class="n">values</span>: <span class="nc">ComputedValues</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2176 <span class="w"> </span><span class="n">cond</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2177 <span class="w"> </span><span class="n">style_attr</span>: <span class="nb">String</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2178 <span class="w"> </span><span class="n">node_impl</span>: <span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span><span class="w"> </span><span class="n">NodeTrait</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2179 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2180 </code></pre></div> Err codemadness.org 70 i 2181 Err codemadness.org 70 i 2182 <p>The size of a Rust enum is the maximum of the sizes of its variants, Err codemadness.org 70 i 2183 plus a little extra for the discriminant (you can think of a C struct Err codemadness.org 70 i 2184 with an int for the discriminant, and a union of variants).</p> Err codemadness.org 70 i 2185 <p>The code <a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/741a0c0bb4c9bc5dc4b246a92d9ba2e26275d45d">needed a few Err codemadness.org 70 i 2186 changes</a> Err codemadness.org 70 i 2187 to split <code>NodeData</code> in this way, by adding accessor Err codemadness.org 70 i 2188 functions to each of the <code>Element</code> or <code>Text</code> cases conveniently. This Err codemadness.org 70 i 2189 is one of those refactors where you can just change the declaration, Err codemadness.org 70 i 2190 and walk down the compiler's errors to make each case use the accesors Err codemadness.org 70 i 2191 instead of whatever was done before.</p> Err codemadness.org 70 i 2192 <h2>Second step: move the Element variant to a separate allocation</h2> Err codemadness.org 70 i 2193 <p>Now, <a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/8312bc00b6a4abbac82fef0596fb25cad3a56eaf">we turn <code>NodeData</code> into Err codemadness.org 70 i 2194 this</a>:</p> Err codemadness.org 70 i 2195 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">NodeData</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2196 <span class="w"> </span><span class="n">Element</span><span class="p">(</span><span class="nb">Box</span><span class="o">&lt;</span><span class="n">Element</span><span class="o">&gt;</span><span class="p">),</span><span class="w"> </span><span class="c1">// This goes inside a Box</span> Err codemadness.org 70 i 2197 <span class="w"> </span><span class="n">Text</span><span class="p">(</span><span class="n">NodeChars</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 2198 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2199 </code></pre></div> Err codemadness.org 70 i 2200 Err codemadness.org 70 i 2201 <p>That way, the <code>Element</code> variant is the size of a pointer (i.e. a Err codemadness.org 70 i 2202 pointer to the heap-allocated <code>Box</code>), and the <code>Text</code> variant is as big Err codemadness.org 70 i 2203 as <code>NodeChars</code> as usual.</p> Err codemadness.org 70 i 2204 <p>This means that <code>Element</code> nodes are just as big as before, plus an Err codemadness.org 70 i 2205 extra pointer, plus an extra heap allocation.</p> Err codemadness.org 70 i 2206 <p>However, the <code>Text</code> nodes get a lot smaller!</p> Err codemadness.org 70 i 2207 <ul> Err codemadness.org 70 i 2208 <li>Before: <code>sizeof::&lt;NodeData&gt;() = 1808</code></li> Err codemadness.org 70 i 2209 <li>After: <code>sizeof::&lt;NodeData&gt;() = 72</code></li> Err codemadness.org 70 i 2210 </ul> Err codemadness.org 70 i 2211 <p>By making the <code>Element</code> variant a lot smaller (the size of a <code>Box</code>, Err codemadness.org 70 i 2212 which is just a pointer), it has no extra overhead on the <code>Text</code> Err codemadness.org 70 i 2213 variant.</p> Err codemadness.org 70 i 2214 <p>This means that in the SVG file, all the whitespace between XML Err codemadness.org 70 i 2215 elements now takes a lot less memory.</p> Err codemadness.org 70 i 2216 <h2>Some numbers from a pathological file</h2> Err codemadness.org 70 i 2217 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/issues/42">Issue 42</a> is about Err codemadness.org 70 i 2218 an SVG file that is just a <code>&lt;use&gt;</code> element repeated many times, once Err codemadness.org 70 i 2219 per line:</p> Err codemadness.org 70 i 2220 <div class="highlight"><pre><span></span><code><span class="nt">&lt;svg</span> <span class="na">xmlns=</span><span class="s">&quot;http://www.w3.org/2000/svg&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 2221 <span class="nt">&lt;defs&gt;</span> Err codemadness.org 70 i 2222 <span class="nt">&lt;symbol</span> <span class="na">id=</span><span class="s">&quot;glyph0-0&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 2223 <span class="c">&lt;!-- a few elements here --&gt;</span> Err codemadness.org 70 i 2224 <span class="nt">&lt;/symbol&gt;</span> Err codemadness.org 70 i 2225 <span class="nt">&lt;/defs&gt;</span> Err codemadness.org 70 i 2226 Err codemadness.org 70 i 2227 <span class="nt">&lt;use</span> <span class="na">xlink:href=</span><span class="s">&quot;#glyph0-0&quot;</span> <span class="na">x=</span><span class="s">&quot;1&quot;</span> <span class="na">y=</span><span class="s">&quot;10&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 2228 <span class="nt">&lt;use</span> <span class="na">xlink:href=</span><span class="s">&quot;#glyph0-0&quot;</span> <span class="na">x=</span><span class="s">&quot;1&quot;</span> <span class="na">y=</span><span class="s">&quot;10&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 2229 <span class="nt">&lt;use</span> <span class="na">xlink:href=</span><span class="s">&quot;#glyph0-0&quot;</span> <span class="na">x=</span><span class="s">&quot;1&quot;</span> <span class="na">y=</span><span class="s">&quot;10&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 2230 <span class="c">&lt;!-- about 196,000 similar lines --&gt;</span> Err codemadness.org 70 i 2231 <span class="nt">&lt;/svg&gt;</span> Err codemadness.org 70 i 2232 </code></pre></div> Err codemadness.org 70 i 2233 Err codemadness.org 70 i 2234 <p>So we have around 196,000 elements. According to <a href="https://valgrind.org/docs/manual/ms-manual.html">Valgrind's Massif Err codemadness.org 70 i 2235 tool</a>, this makes <code>rsvg-convert</code> allocate 800,501,568 bytes in the Err codemadness.org 70 i 2236 old version, versus 463,412,720 bytes in the new version, or about 60% Err codemadness.org 70 i 2237 of the space.</p> Err codemadness.org 70 i 2238 <h2>Next steps</h2> Err codemadness.org 70 i 2239 <p>There is a lot of repetition in the text nodes of a typical SVG file. Err codemadness.org 70 i 2240 For example, in that pathological file above, most of the whitespace is Err codemadness.org 70 i 2241 identical: between each element there is a newline and two spaces. Err codemadness.org 70 i 2242 Instead of having thousands of little allocations, all with the same Err codemadness.org 70 i 2243 string, there could be a pool of shared strings. Files with "real" Err codemadness.org 70 i 2244 indentation could get benefits from sharing the whitespace-only text Err codemadness.org 70 i 2245 nodes.</p> Err codemadness.org 70 i 2246 <p>Real browser engines are very careful to share the style structs Err codemadness.org 70 i 2247 across elements if possible. Look for "style struct sharing" in Err codemadness.org 70 i 2248 <a href="https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/">"Inside a super fast CSS engine: Quantum CSS"</a>. This is Err codemadness.org 70 i 2249 going to take some good work in librsvg, but we can get there Err codemadness.org 70 i 2250 gradually.</p> Err codemadness.org 70 i 2251 <h2>References</h2> Err codemadness.org 70 i 2252 <ul> Err codemadness.org 70 i 2253 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/302/commits">Commits for the whole Err codemadness.org 70 i 2254 refactoring</a></li> Err codemadness.org 70 i 2255 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/741a0c0bb4c9bc5dc4b246a92d9ba2e26275d45d">Turn the NodeData struct into an enum with variants for Element and Err codemadness.org 70 i 2256 Text</a></li> Err codemadness.org 70 i 2257 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/-/commit/8312bc00b6a4abbac82fef0596fb25cad3a56eaf">Box the Element variant to make Text nodes Err codemadness.org 70 i 2258 smaller</a>. Err codemadness.org 70 i 2259 The commit message has parts of the massif log with all the Err codemadness.org 70 i 2260 interesting numbers.</li> Err codemadness.org 70 i 2261 <li><a href="https://gitlab.gnome.org/GNOME/librsvg/issues/528">Tracker bug for memory Err codemadness.org 70 i 2262 consumption</a></li> Err codemadness.org 70 i 2263 </ul>Exposing C and Rust APIs: some thoughts from librsvg2020-01-15T11:15:06-06:002020-01-15T11:15:06-06:00Federico Mena Quinterotag:people.gnome.org,2020-01-15:/~federico/blog/exposing-c-and-rust-apis.html<p>Librsvg exports two public APIs: the <a href="https://developer.gnome.org/rsvg/stable/">C API</a> that is in turn available Err codemadness.org 70 i 2264 to other languages through <a href="https://people.gnome.org/~federico/blog/magic-of-gobject-introspection.html">GObject Introspection</a>, and the <a href="https://gnome.pages.gitlab.gnome.org/librsvg/doc/librsvg/">Rust API</a>.</p> Err codemadness.org 70 i 2265 <p>You could call this a use of the <a href="https://en.wikipedia.org/wiki/Facade_pattern">facade pattern</a> on top of the Err codemadness.org 70 i 2266 <a href="https://gnome.pages.gitlab.gnome.org/librsvg/doc/rsvg_internals/index.html">rsvg_internals crate</a>. That crate <em>is</em> the actual Err codemadness.org 70 i 2267 implementation of librsvg, and exports an …</p><p>Librsvg exports two public APIs: the <a href="https://developer.gnome.org/rsvg/stable/">C API</a> that is in turn available Err codemadness.org 70 i 2268 to other languages through <a href="https://people.gnome.org/~federico/blog/magic-of-gobject-introspection.html">GObject Introspection</a>, and the <a href="https://gnome.pages.gitlab.gnome.org/librsvg/doc/librsvg/">Rust API</a>.</p> Err codemadness.org 70 i 2269 <p>You could call this a use of the <a href="https://en.wikipedia.org/wiki/Facade_pattern">facade pattern</a> on top of the Err codemadness.org 70 i 2270 <a href="https://gnome.pages.gitlab.gnome.org/librsvg/doc/rsvg_internals/index.html">rsvg_internals crate</a>. That crate <em>is</em> the actual Err codemadness.org 70 i 2271 implementation of librsvg, and exports an interface with many knobs Err codemadness.org 70 i 2272 that are not exposed from the public APIs. The knobs are to allow for Err codemadness.org 70 i 2273 the variations in each of those APIs.</p> Err codemadness.org 70 i 2274 <p>This post is about some interesting things that have come up during Err codemadness.org 70 i 2275 the creation/separation of those public APIs, and the implications of Err codemadness.org 70 i 2276 having an internals library that implements both.</p> Err codemadness.org 70 i 2277 <h2>Initial code organization</h2> Err codemadness.org 70 i 2278 <p>When librsvg was being ported to Rust, it just had an <code>rsvg_internals</code> Err codemadness.org 70 i 2279 crate that compiled as a <code>staticlib</code> to a <code>.a</code> library, which was Err codemadness.org 70 i 2280 later linked into the final <code>librsvg.so</code>.</p> Err codemadness.org 70 i 2281 <p>Eventually the code got to the point where it was feasible to port the Err codemadness.org 70 i 2282 toplevel C API to Rust. This was relatively easy to do, since Err codemadness.org 70 i 2283 everything else underneath was already in Rust. At that point I Err codemadness.org 70 i 2284 became interested <a href="https://people.gnome.org/~federico/blog/a-rust-api-for-librsvg.html">in also having a Rust API</a> for librsvg — Err codemadness.org 70 i 2285 first to port the test suite to Rust and be able to run tests in Err codemadness.org 70 i 2286 parallel, and then to actually have a public API in Rust with more Err codemadness.org 70 i 2287 modern idioms than the historical, GObject-based API in C.</p> Err codemadness.org 70 i 2288 <p>Version <a href="https://gitlab.gnome.org/GNOME/librsvg/-/tags/2.45.5">2.45.5</a>, from February 2019, is the last release that only had Err codemadness.org 70 i 2289 a C API.</p> Err codemadness.org 70 i 2290 <p>Most of the C API of librsvg is in the <code>RsvgHandle</code> class. An Err codemadness.org 70 i 2291 <code>RsvgHandle</code> gets loaded with SVG data from a file or a stream, and Err codemadness.org 70 i 2292 then gets rendered to a Cairo context. The naming of Rust source Err codemadness.org 70 i 2293 files more or less matched the C source files, so where there was Err codemadness.org 70 i 2294 <code>rsvg-handle.c</code> initially, later we had <code>handle.rs</code> with the Rustified Err codemadness.org 70 i 2295 part of that code.</p> Err codemadness.org 70 i 2296 <p>So, <code>handle.rs</code> had the Rust internals of the <code>RsvgHandle</code> class, and Err codemadness.org 70 i 2297 a bunch of <code>extern "C"</code> functions callable from C. For example, for Err codemadness.org 70 i 2298 this function in the public C API:</p> Err codemadness.org 70 i 2299 <div class="highlight"><pre><span></span><code><span class="kt">void</span> <span class="nf">rsvg_handle_set_base_gfile</span> <span class="p">(</span><span class="n">RsvgHandle</span> <span class="o">*</span><span class="n">handle</span><span class="p">,</span> Err codemadness.org 70 i 2300 <span class="n">GFile</span> <span class="o">*</span><span class="n">base_file</span><span class="p">);</span> Err codemadness.org 70 i 2301 </code></pre></div> Err codemadness.org 70 i 2302 Err codemadness.org 70 i 2303 <p>The corresponding Rust implementation <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/3d6d42fe1387588354291595585fbf498a89704e/rsvg_internals/src/handle.rs#L614-626">was this</a>:</p> Err codemadness.org 70 i 2304 <div class="highlight"><pre><span></span><code><span class="cp">#[no_mangle]</span><span class="w"></span> Err codemadness.org 70 i 2305 <span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_handle_rust_set_base_gfile</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 2306 <span class="w"> </span><span class="n">raw_handle</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">RsvgHandle</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2307 <span class="w"> </span><span class="n">raw_gfile</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">gio_sys</span>::<span class="n">GFile</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2308 <span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2309 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">rhandle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_rust_handle</span><span class="p">(</span><span class="n">raw_handle</span><span class="p">);</span><span class="w"> </span><span class="c1">// 1</span> Err codemadness.org 70 i 2310 Err codemadness.org 70 i 2311 <span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">raw_gfile</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"> </span><span class="c1">// 2</span> Err codemadness.org 70 i 2312 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">file</span>: <span class="nc">gio</span>::<span class="n">File</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">from_glib_none</span><span class="p">(</span><span class="n">raw_gfile</span><span class="p">);</span><span class="w"> </span><span class="c1">// 3</span> Err codemadness.org 70 i 2313 Err codemadness.org 70 i 2314 <span class="w"> </span><span class="n">rhandle</span><span class="p">.</span><span class="n">set_base_gfile</span><span class="p">(</span><span class="o">&amp;</span><span class="n">file</span><span class="p">);</span><span class="w"> </span><span class="c1">// 4</span> Err codemadness.org 70 i 2315 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2316 </code></pre></div> Err codemadness.org 70 i 2317 Err codemadness.org 70 i 2318 <ol> Err codemadness.org 70 i 2319 <li>Get the Rust struct corresponding to the C GObject.</li> Err codemadness.org 70 i 2320 <li>Check the arguments.</li> Err codemadness.org 70 i 2321 <li>Convert from C GObject reference to Rust reference.</li> Err codemadness.org 70 i 2322 <li>Call the actual implementation of <code>set_base_gfile</code> in the Rust Err codemadness.org 70 i 2323 struct.</li> Err codemadness.org 70 i 2324 </ol> Err codemadness.org 70 i 2325 <p>You can see that this function takes in arguments with C types, and Err codemadness.org 70 i 2326 converts them to Rust types. It's basically just glue between the C Err codemadness.org 70 i 2327 code and the actual implementation.</p> Err codemadness.org 70 i 2328 <p>Then, the actual implementation of <code>set_base_gfile</code> looked <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/3d6d42fe1387588354291595585fbf498a89704e/rsvg_internals/src/handle.rs#L202-208">like Err codemadness.org 70 i 2329 this</a>:</p> Err codemadness.org 70 i 2330 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">Handle</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2331 <span class="w"> </span><span class="k">fn</span> <span class="nf">set_base_gfile</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">file</span>: <span class="kp">&amp;</span><span class="nc">gio</span>::<span class="n">File</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2332 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">file</span><span class="p">.</span><span class="n">get_uri</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2333 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">set_base_url</span><span class="p">(</span><span class="o">&amp;</span><span class="n">uri</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2334 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2335 <span class="w"> </span><span class="n">rsvg_g_warning</span><span class="p">(</span><span class="s">&quot;file has no URI; will not set the base URI&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2336 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2337 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2338 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2339 </code></pre></div> Err codemadness.org 70 i 2340 Err codemadness.org 70 i 2341 <p>This is an actual method for a Rust <code>Handle</code> struct, and takes Rust Err codemadness.org 70 i 2342 types as arguments — no conversions are necessary here. However, Err codemadness.org 70 i 2343 there is a pesky call to <code>rsvg_g_warning</code>, about which I'll talk later.</p> Err codemadness.org 70 i 2344 <p>I found it cleanest, although not the shortest code, to structure Err codemadness.org 70 i 2345 things like this:</p> Err codemadness.org 70 i 2346 <ul> Err codemadness.org 70 i 2347 <li> Err codemadness.org 70 i 2348 <p>C code: bunch of stub functions where <code>rsvg_blah</code> just calls a Err codemadness.org 70 i 2349 corresponding <code>rsvg_rust_blah</code>.</p> Err codemadness.org 70 i 2350 </li> Err codemadness.org 70 i 2351 <li> Err codemadness.org 70 i 2352 <p>Toplevel Rust code: bunch of <code>#[no_mangle] unsafe extern "C" fn rust_blah()</code> that Err codemadness.org 70 i 2353 convert from C argument types to Rust types, and call safe Rust Err codemadness.org 70 i 2354 functions — for librsvg, these happened to be methods for a struct. Err codemadness.org 70 i 2355 Before returning, the toplevel functions convert Rust return values Err codemadness.org 70 i 2356 to C return values, and do things like converting the <code>Err(E)</code> of a Err codemadness.org 70 i 2357 <code>Result&lt;&gt;</code> into a <code>GError</code> or a boolean or whatever the traditional Err codemadness.org 70 i 2358 C API required.</p> Err codemadness.org 70 i 2359 </li> Err codemadness.org 70 i 2360 </ul> Err codemadness.org 70 i 2361 <p>In the very first versions of the code where the public API was Err codemadness.org 70 i 2362 implemented in Rust, the <code>extern "C"</code> functions actually contained Err codemadness.org 70 i 2363 their implementation. However, after some refactoring, it turned out Err codemadness.org 70 i 2364 to be cleaner to leave those functions just with the task of Err codemadness.org 70 i 2365 converting C to Rust types and vice-versa, and put the actual Err codemadness.org 70 i 2366 implementation in very Rust-y code. This made it easier to keep the Err codemadness.org 70 i 2367 <code>unsafe</code> conversion code (unsafe because it deals with raw pointers Err codemadness.org 70 i 2368 coming from C) only in the toplevel functions.</p> Err codemadness.org 70 i 2369 <h2>Growing out a Rust API</h2> Err codemadness.org 70 i 2370 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/commit/cd848439e01378461647dd848918262bf6639193">This commit</a> is where the new, public Rust API Err codemadness.org 70 i 2371 started. That commit just created a <a href="https://doc.rust-lang.org/cargo/reference/manifest.html#the-workspace-section">Cargo workspace</a> Err codemadness.org 70 i 2372 with two crates; the <code>rsvg_internals</code> crate that we already had, and a Err codemadness.org 70 i 2373 <code>librsvg_crate</code> with the public Rust API.</p> Err codemadness.org 70 i 2374 <p>The commits over the subsequent couple of months are of intense Err codemadness.org 70 i 2375 refactoring:</p> Err codemadness.org 70 i 2376 <ul> Err codemadness.org 70 i 2377 <li> Err codemadness.org 70 i 2378 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/commit/d01f75ed310b0a7b8d62fc7de65465357f20f535">This commit</a> moves the <code>unsafe extern "C"</code> Err codemadness.org 70 i 2379 functions to a separate <code>c_api.rs</code> source file. This leaves Err codemadness.org 70 i 2380 <code>handle.rs</code> with only the safe Rust implementation of the toplevel Err codemadness.org 70 i 2381 API, and <code>c_api.rs</code> with the unsafe entry points that mostly just Err codemadness.org 70 i 2382 convert argument types, return values, and errors.</p> Err codemadness.org 70 i 2383 </li> Err codemadness.org 70 i 2384 <li> Err codemadness.org 70 i 2385 <p>The API primitives get expanded to allow for a public Rust API that Err codemadness.org 70 i 2386 is "hard to misuse" unlike the C API, which needs to be called Err codemadness.org 70 i 2387 in a certain order.</p> Err codemadness.org 70 i 2388 </li> Err codemadness.org 70 i 2389 </ul> Err codemadness.org 70 i 2390 <h2>Needing to call a C macro</h2> Err codemadness.org 70 i 2391 <p>However, there was a little problem. The Rust code cannot call Err codemadness.org 70 i 2392 <a href="https://developer.gnome.org/glib/stable/glib-Message-Logging.html#g-warning"><code>g_warning</code></a>, a C macro in glib that prints a message to Err codemadness.org 70 i 2393 stderr or uses structured logging. Librsvg used that to signal Err codemadness.org 70 i 2394 conditions where something went (recoverably) wrong, but there was no Err codemadness.org 70 i 2395 way to return a proper error code to the caller — it's mainly used as Err codemadness.org 70 i 2396 a debugging aid.</p> Err codemadness.org 70 i 2397 <p>This is what the <code>rsvg_internals</code> used to be able to call that C macro:</p> Err codemadness.org 70 i 2398 <p>First, the C code exports a function that just calls the macro:</p> Err codemadness.org 70 i 2399 <div class="highlight"><pre><span></span><code><span class="cm">/* This function exists just so that we can effectively call g_warning() from Rust,</span> Err codemadness.org 70 i 2400 <span class="cm"> * since glib-rs doesn&#39;t bind the g_log functions yet.</span> Err codemadness.org 70 i 2401 <span class="cm"> */</span> Err codemadness.org 70 i 2402 <span class="kt">void</span> Err codemadness.org 70 i 2403 <span class="nf">rsvg_g_warning_from_c</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">msg</span><span class="p">)</span> Err codemadness.org 70 i 2404 <span class="p">{</span> Err codemadness.org 70 i 2405 <span class="n">g_warning</span> <span class="p">(</span><span class="s">&quot;%s&quot;</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span> Err codemadness.org 70 i 2406 <span class="p">}</span> Err codemadness.org 70 i 2407 </code></pre></div> Err codemadness.org 70 i 2408 Err codemadness.org 70 i 2409 <p>Second, the Rust code binds that function to be callable from Rust:</p> Err codemadness.org 70 i 2410 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_g_warning</span><span class="p">(</span><span class="n">msg</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2411 <span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2412 <span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_g_warning_from_c</span><span class="p">(</span><span class="n">msg</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">libc</span>::<span class="n">c_char</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2413 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2414 Err codemadness.org 70 i 2415 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2416 <span class="w"> </span><span class="n">rsvg_g_warning_from_c</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">to_glib_none</span><span class="p">().</span><span class="mi">0</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2417 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2418 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2419 </code></pre></div> Err codemadness.org 70 i 2420 Err codemadness.org 70 i 2421 <p>However! Since the standalone <code>librsvg_crate</code> does not link to the C Err codemadness.org 70 i 2422 code from the public <code>librsvg.so</code>, the helper <code>rsvg_g_warning_from_c</code> Err codemadness.org 70 i 2423 is not available!</p> Err codemadness.org 70 i 2424 <h3>A configuration feature for the internals library</h3> Err codemadness.org 70 i 2425 <p>And yet! Those warnings are only meaningful for the C API, which is Err codemadness.org 70 i 2426 not able to return error codes from all situations. However, the Rust Err codemadness.org 70 i 2427 API <em>is</em> able to do that, and so doesn't need the warnings printed to Err codemadness.org 70 i 2428 stderr. My first solution was to add a build-time option for whether Err codemadness.org 70 i 2429 the <code>rsvg_internals</code> library is being build for the C library, or for Err codemadness.org 70 i 2430 the Rust one.</p> Err codemadness.org 70 i 2431 <p>In case we are building for the C library, the code calls Err codemadness.org 70 i 2432 <code>rsvg_g_warning_from_c</code> as usual.</p> Err codemadness.org 70 i 2433 <p>But in case we are building for the Rust library, that code is a Err codemadness.org 70 i 2434 no-op.</p> Err codemadness.org 70 i 2435 <p>This is the <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/4df0ef3d6dd8c277ef484b33104e9666ea6ea38c/rsvg_internals/Cargo.toml#L81-83">bit in rsvg_internals/Cargo.toml</a> to declare the feature:</p> Err codemadness.org 70 i 2436 <div class="highlight"><pre><span></span><code><span class="k">[features]</span> Err codemadness.org 70 i 2437 <span class="c1"># Enables calling g_warning() when built as part of librsvg.so</span> Err codemadness.org 70 i 2438 <span class="n">c-library</span> <span class="o">=</span> <span class="k">[]</span> Err codemadness.org 70 i 2439 </code></pre></div> Err codemadness.org 70 i 2440 Err codemadness.org 70 i 2441 <p>And <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/4df0ef3d6dd8c277ef484b33104e9666ea6ea38c/rsvg_internals/src/util.rs#L31-48">this is the corresponding code</a>:</p> Err codemadness.org 70 i 2442 <div class="highlight"><pre><span></span><code><span class="cp">#[cfg(feature = </span><span class="s">&quot;c-library&quot;</span><span class="cp">)]</span><span class="w"></span> Err codemadness.org 70 i 2443 <span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_g_warning</span><span class="p">(</span><span class="n">msg</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2444 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2445 <span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2446 <span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_g_warning_from_c</span><span class="p">(</span><span class="n">msg</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">libc</span>::<span class="n">c_char</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2447 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2448 Err codemadness.org 70 i 2449 <span class="w"> </span><span class="n">rsvg_g_warning_from_c</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">to_glib_none</span><span class="p">().</span><span class="mi">0</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2450 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2451 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2452 Err codemadness.org 70 i 2453 <span class="cp">#[cfg(not(feature = </span><span class="s">&quot;c-library&quot;</span><span class="cp">))]</span><span class="w"></span> Err codemadness.org 70 i 2454 <span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_g_warning</span><span class="p">(</span><span class="n">_msg</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2455 <span class="w"> </span><span class="c1">// The only callers of this are in handle.rs. When those functions</span> Err codemadness.org 70 i 2456 <span class="w"> </span><span class="c1">// are called from the Rust API, they are able to return a</span> Err codemadness.org 70 i 2457 <span class="w"> </span><span class="c1">// meaningful error code, but the C API isn&#39;t - so they issues a</span> Err codemadness.org 70 i 2458 <span class="w"> </span><span class="c1">// g_warning() instead.</span> Err codemadness.org 70 i 2459 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2460 </code></pre></div> Err codemadness.org 70 i 2461 Err codemadness.org 70 i 2462 <p>The first function is the one that is compiled when the <code>c-library</code> Err codemadness.org 70 i 2463 feature is enabled; this happens when building <code>rsvg_internals</code> to Err codemadness.org 70 i 2464 link into <code>librsvg.so</code>.</p> Err codemadness.org 70 i 2465 <p>The second function does nothing; it is what is compiled when Err codemadness.org 70 i 2466 <code>rsvg_internals</code> is being used just from the <code>librsvg_crate</code> crate Err codemadness.org 70 i 2467 with the Rust API.</p> Err codemadness.org 70 i 2468 <p>While this worked well, it meant that <strong>the internals library was Err codemadness.org 70 i 2469 built twice</strong> on each compilation run of the whole librsvg module: Err codemadness.org 70 i 2470 once for <code>librsvg.so</code>, and once for <code>librsvg_crate</code>.</p> Err codemadness.org 70 i 2471 <h2>Making programming errors a <code>g_critical</code></h2> Err codemadness.org 70 i 2472 <p>While <code>g_warning()</code> means "something went wrong, but the program will Err codemadness.org 70 i 2473 continue", <code>g_critical()</code> means "there is a programming error". For Err codemadness.org 70 i 2474 historical reasons Glib does not abort when <code>g_critical()</code> is called, Err codemadness.org 70 i 2475 except by setting <a href="https://developer.gnome.org/glib/stable/glib-running.html#G-DEBUG:CAPS"><code>G_DEBUG=fatal-criticals</code></a>, or by Err codemadness.org 70 i 2476 running a development version of Glib.</p> Err codemadness.org 70 i 2477 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/commit/9d26e03dc64342978ab8273dcf6a474af3843b9f">This commit</a> turned warnings into critical errors when the Err codemadness.org 70 i 2478 C API was called out of order, by using a similar Err codemadness.org 70 i 2479 <code>rsvg_g_critical_from_c()</code> wrapper for a C macro.</p> Err codemadness.org 70 i 2480 <h2>Separating the C-callable code into yet another crate</h2> Err codemadness.org 70 i 2481 <p>To recapitulate, at that point we had this:</p> Err codemadness.org 70 i 2482 <div class="highlight"><pre><span></span><code><span class="nv">librsvg</span><span class="o">/</span> Err codemadness.org 70 i 2483 <span class="o">|</span> <span class="nv">Cargo</span>.<span class="nv">toml</span> <span class="o">-</span> <span class="nv">declares</span> <span class="nv">the</span> <span class="nv">Cargo</span> <span class="nv">workspace</span> Err codemadness.org 70 i 2484 <span class="o">|</span> Err codemadness.org 70 i 2485 <span class="o">+-</span> <span class="nv">rsvg_internals</span><span class="o">/</span> Err codemadness.org 70 i 2486 <span class="o">|</span> <span class="o">|</span> <span class="nv">Cargo</span>.<span class="nv">toml</span> Err codemadness.org 70 i 2487 <span class="o">|</span> <span class="o">+-</span> <span class="nv">src</span><span class="o">/</span> Err codemadness.org 70 i 2488 <span class="o">|</span> <span class="nv">c_api</span>.<span class="nv">rs</span> <span class="o">-</span> <span class="nv">convert</span> <span class="nv">types</span> <span class="nv">and</span> <span class="k">return</span> <span class="nv">values</span>, <span class="k">call</span> <span class="nl">into</span> <span class="nv">implementation</span> Err codemadness.org 70 i 2489 <span class="o">|</span> <span class="nv">handle</span>.<span class="nv">rs</span> <span class="o">-</span> <span class="nv">actual</span> <span class="nv">implementation</span> Err codemadness.org 70 i 2490 <span class="o">|</span> <span class="o">*</span>.<span class="nv">rs</span> <span class="o">-</span> <span class="nv">all</span> <span class="nv">the</span> <span class="nv">other</span> <span class="nv">internals</span> Err codemadness.org 70 i 2491 <span class="o">|</span> Err codemadness.org 70 i 2492 <span class="o">+-</span> <span class="nv">librsvg</span><span class="o">/</span> Err codemadness.org 70 i 2493 <span class="o">|</span> <span class="o">*</span>.<span class="nv">c</span> <span class="o">-</span> <span class="nv">stub</span> <span class="nv">functions</span> <span class="nv">that</span> <span class="k">call</span> <span class="nl">into</span> <span class="nv">Rust</span> Err codemadness.org 70 i 2494 <span class="o">|</span> <span class="nv">rsvg</span><span class="o">-</span><span class="nv">base</span>.<span class="nv">c</span> <span class="o">-</span> <span class="nv">contains</span> <span class="nv">rsvg_g_warning_from_c</span><span class="ss">()</span> <span class="nv">among</span> <span class="nv">others</span> Err codemadness.org 70 i 2495 <span class="o">|</span> Err codemadness.org 70 i 2496 <span class="o">+-</span> <span class="nv">librsvg_crate</span><span class="o">/</span> Err codemadness.org 70 i 2497 <span class="o">|</span> <span class="nv">Cargo</span>.<span class="nv">toml</span> Err codemadness.org 70 i 2498 <span class="o">+-</span> <span class="nv">src</span><span class="o">/</span> Err codemadness.org 70 i 2499 <span class="o">|</span> <span class="nv">lib</span>.<span class="nv">rs</span> <span class="o">-</span> <span class="nv">public</span> <span class="nv">Rust</span> <span class="nv">API</span> Err codemadness.org 70 i 2500 <span class="o">+-</span> <span class="nv">tests</span><span class="o">/</span> <span class="o">-</span> <span class="nv">tests</span> <span class="k">for</span> <span class="nv">the</span> <span class="nv">public</span> <span class="nv">Rust</span> <span class="nv">API</span> Err codemadness.org 70 i 2501 <span class="o">*</span>.<span class="nv">rs</span> Err codemadness.org 70 i 2502 </code></pre></div> Err codemadness.org 70 i 2503 Err codemadness.org 70 i 2504 <p>At this point <code>c_api.rs</code> with all the <code>unsafe</code> functions looked out of Err codemadness.org 70 i 2505 place. That code is only relevant to <code>librsvg.so</code> — the public C API Err codemadness.org 70 i 2506 —, not to the Rust API in <code>librsvg_crate</code>.</p> Err codemadness.org 70 i 2507 <p>I started moving the C API glue to a separate <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/78219898d17440a41d21a206afa5a5d982dcbf9f"><code>librsvg_c_api</code> crate</a> that lives Err codemadness.org 70 i 2508 along with the C stubs:</p> Err codemadness.org 70 i 2509 <div class="highlight"><pre><span></span><code>+- librsvg/ Err codemadness.org 70 i 2510 | *.c - stub functions that call into Rust Err codemadness.org 70 i 2511 | rsvg-base.c - contains rsvg_g_warning_from_c() among others Err codemadness.org 70 i 2512 | Cargo.toml Err codemadness.org 70 i 2513 | c_api.rs - what we had before Err codemadness.org 70 i 2514 </code></pre></div> Err codemadness.org 70 i 2515 Err codemadness.org 70 i 2516 <p>This made the dependencies look like the following:</p> Err codemadness.org 70 i 2517 <div class="highlight"><pre><span></span><code> rsvg_internals Err codemadness.org 70 i 2518 ^ ^ Err codemadness.org 70 i 2519 | \ Err codemadness.org 70 i 2520 | \ Err codemadness.org 70 i 2521 librsvg_crate librsvg_c_api Err codemadness.org 70 i 2522 (Rust API) ^ Err codemadness.org 70 i 2523 | Err codemadness.org 70 i 2524 librsvg.so Err codemadness.org 70 i 2525 (C API) Err codemadness.org 70 i 2526 </code></pre></div> Err codemadness.org 70 i 2527 Err codemadness.org 70 i 2528 <p>And also, this made it possible to <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/38fe68c2c3165a923dee9cfaa9a9f57960fb9b95">remove the configuration feature</a> Err codemadness.org 70 i 2529 for <code>rsvg_internals</code>, since the code that calls Err codemadness.org 70 i 2530 <code>rsvg_g_warning_from_c</code> now lives in <code>librsvg_c_api</code>.</p> Err codemadness.org 70 i 2531 <p>With that, <code>rsvg_internals</code> is compiled only once, as it should be.</p> Err codemadness.org 70 i 2532 <p>This also helped clean up some code in the internals library. Err codemadness.org 70 i 2533 Deprecated functions that render SVGs directly to <code>GdkPixbuf</code> are now Err codemadness.org 70 i 2534 in <code>librsvg_c_api</code> and don't clutter the <code>rsvg_internals</code> library. Err codemadness.org 70 i 2535 All the GObject boilerplate is there as well now; <code>rsvg_internals</code> is Err codemadness.org 70 i 2536 mostly safe code except for the glue to libxml2.</p> Err codemadness.org 70 i 2537 <h2>Summary</h2> Err codemadness.org 70 i 2538 <p>It was useful to move all the code that dealt with incoming C types, Err codemadness.org 70 i 2539 our outgoing C return values and errors, into the same place, and Err codemadness.org 70 i 2540 separate it from the "pure Rust" code.</p> Err codemadness.org 70 i 2541 <p>This took gradual refactoring and was not done in a single step, but Err codemadness.org 70 i 2542 it left the resulting Rust code rather nice and clean.</p> Err codemadness.org 70 i 2543 <p>When we added a new public Rust API, we had to shuffle some code Err codemadness.org 70 i 2544 around that could only be linked in the context of a C library.</p> Err codemadness.org 70 i 2545 <p>Compile-time configuration features are useful (like <code>#ifdef</code> in the C Err codemadness.org 70 i 2546 world), but they do cause double compilation if you need a C-internals Err codemadness.org 70 i 2547 and a Rust-internals library from the same code.</p> Err codemadness.org 70 i 2548 <p>Having proper error reporting throughout the Rust code is a lot of Err codemadness.org 70 i 2549 work, but pretty much invaluable. The glue code to C can then convert Err codemadness.org 70 i 2550 and expose those errors as needed.</p> Err codemadness.org 70 i 2551 <p>If you need both C and Rust APIs into the same code base, you may end Err codemadness.org 70 i 2552 up naturally using a facade pattern for each. It helps to gradually Err codemadness.org 70 i 2553 refactor the internals to be as "pure idiomatic Rust" as possible, Err codemadness.org 70 i 2554 while letting API idiosyncrasies bubble up to each individual facade.</p>Moving gnome-shell's styles to Rust2019-11-25T21:10:06-06:002019-11-25T21:10:06-06:00Federico Mena Quinterotag:people.gnome.org,2019-11-25:/~federico/blog/moving-gnome-shell-styles-to-rust.html<p>Gnome-shell uses CSS processing code that dates from Err codemadness.org 70 i 2555 <a href="https://blog.ometer.com/2006/10/14/text-layout-that-works-properly/">HippoCanvas</a>, Err codemadness.org 70 i 2556 a CSS-aware canvas from around 2006. It uses libcroco to parse CSS, Err codemadness.org 70 i 2557 and implements selector matching by hand in C.</p> Err codemadness.org 70 i 2558 <p>This code is getting rather dated, and libcroco is unmaintained.</p> Err codemadness.org 70 i 2559 <p>I've been reading the code for Err codemadness.org 70 i 2560 <a href="https://gitlab.gnome.org/GNOME/gnome-shell/blob/66fc5c07/src/st/st-theme.c"><code>StTheme</code></a> Err codemadness.org 70 i 2561 and Err codemadness.org 70 i 2562 <a href="https://gitlab.gnome.org/GNOME/gnome-shell/blob/66fc5c07/src/st/st-theme-node.c"><code>StThemeNode</code></a>, Err codemadness.org 70 i 2563 and it …</p><p>Gnome-shell uses CSS processing code that dates from Err codemadness.org 70 i 2564 <a href="https://blog.ometer.com/2006/10/14/text-layout-that-works-properly/">HippoCanvas</a>, Err codemadness.org 70 i 2565 a CSS-aware canvas from around 2006. It uses libcroco to parse CSS, Err codemadness.org 70 i 2566 and implements selector matching by hand in C.</p> Err codemadness.org 70 i 2567 <p>This code is getting rather dated, and libcroco is unmaintained.</p> Err codemadness.org 70 i 2568 <p>I've been reading the code for Err codemadness.org 70 i 2569 <a href="https://gitlab.gnome.org/GNOME/gnome-shell/blob/66fc5c07/src/st/st-theme.c"><code>StTheme</code></a> Err codemadness.org 70 i 2570 and Err codemadness.org 70 i 2571 <a href="https://gitlab.gnome.org/GNOME/gnome-shell/blob/66fc5c07/src/st/st-theme-node.c"><code>StThemeNode</code></a>, Err codemadness.org 70 i 2572 and it looks very feasible to port it gradually to Rust, by using the Err codemadness.org 70 i 2573 same crates that librsvg uses, and eventually removing libcroco Err codemadness.org 70 i 2574 altogether: <strong>gnome-shell is the last module that uses libcroco in Err codemadness.org 70 i 2575 distro packages</strong>.</p> Err codemadness.org 70 i 2576 <h2>Strategy</h2> Err codemadness.org 70 i 2577 <p><code>StTheme</code> and <code>StThemeNode</code> use libcroco to load CSS stylesheets and Err codemadness.org 70 i 2578 keep them in memory. The values of individual properties are just Err codemadness.org 70 i 2579 tokenized and kept around as a linked list of <code>CRTerm</code>; this struct Err codemadness.org 70 i 2580 represents a single token.</p> Err codemadness.org 70 i 2581 <p>Later, the drawing code uses functions like Err codemadness.org 70 i 2582 <code>st_theme_node_lookup_color(node, "property_name")</code> or Err codemadness.org 70 i 2583 <code>st_theme_node_lookup_length()</code> to query the various properties that Err codemadness.org 70 i 2584 it needs. It is <em>then</em> that the type of each property gets Err codemadness.org 70 i 2585 determined: prior to that step, property values are just tokenized, Err codemadness.org 70 i 2586 not parsed into usable values.</p> Err codemadness.org 70 i 2587 <p>I am going to start by porting the individual parsers to Rust, similar Err codemadness.org 70 i 2588 to what Paolo and I did for librsvg. It turns out that there's some Err codemadness.org 70 i 2589 code we can share.</p> Err codemadness.org 70 i 2590 <p>So far I have the <a href="https://gitlab.gnome.org/federico/stylish/blob/master/src/color.rs">parser for Err codemadness.org 70 i 2591 colors</a> Err codemadness.org 70 i 2592 implemented in Rust. This <a href="https://gitlab.gnome.org/federico/gnome-shell/commit/f1bc7b8ece4dd3384a76c46492d61de70b3d670a">removes a little bunch of Err codemadness.org 70 i 2593 code</a> Err codemadness.org 70 i 2594 from the C parsers, and replaces it with a little Rust code, since the Err codemadness.org 70 i 2595 cssparser crate can already parse CSS colors with alpha with no extra Err codemadness.org 70 i 2596 work — libcroco didn't support alpha.</p> Err codemadness.org 70 i 2597 <p>As a bonus, this supports <code>hsl()</code> colors in addition to <code>rgb()</code> ones Err codemadness.org 70 i 2598 out of the box!</p> Err codemadness.org 70 i 2599 <p>After all the parsers are done, the next step would be to convert the Err codemadness.org 70 i 2600 representation of complete stylesheets into pure Rust code.</p> Err codemadness.org 70 i 2601 <h2>What can we expect?</h2> Err codemadness.org 70 i 2602 <p><strong>A well-maintained CSS stack.</strong> Firefox and Servo both use the Err codemadness.org 70 i 2603 crates in question, so librsvg and gnome-shell should get maintenance Err codemadness.org 70 i 2604 of a robust CSS stack "for free", for the foreseeable future.</p> Err codemadness.org 70 i 2605 <p><strong>Speed.</strong> Caveat: I have no profile data for gnome-shell yet, so I don't Err codemadness.org 70 i 2606 know how much time it spends doing CSS parsing and cascading, but it Err codemadness.org 70 i 2607 looks like the Rust version has a good chance of being more efficient.</p> Err codemadness.org 70 i 2608 <p>The <a href="https://docs.rs/selectors/">selectors crate</a> has some very Err codemadness.org 70 i 2609 interesting Err codemadness.org 70 i 2610 <a href="https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/">optimizations</a> Err codemadness.org 70 i 2611 from Mozilla Servo, and it is also now used in Firefox. It supports Err codemadness.org 70 i 2612 doing selector matching using Bloom filters, and can also avoid Err codemadness.org 70 i 2613 re-cascading child nodes if a change to a parent would not cause its Err codemadness.org 70 i 2614 children to change.</p> Err codemadness.org 70 i 2615 <p>All the parsing is done with zero-copy parsers thanks to Rust's string Err codemadness.org 70 i 2616 slices; without so many <code>malloc()</code> calls in the parsing code path, Err codemadness.org 70 i 2617 the parsing stage should really fly.</p> Err codemadness.org 70 i 2618 <p><strong>More CSS features.</strong> The selectors crate can do matching on Err codemadness.org 70 i 2619 basically all kinds of selectors as defined by recent CSS specs; one Err codemadness.org 70 i 2620 just has to provide the correct hooks into the calling code's Err codemadness.org 70 i 2621 representation of the DOM tree. The kind of matching that <code>StTheme</code> Err codemadness.org 70 i 2622 can do is somewhat limited; the rustification should make it match Err codemadness.org 70 i 2623 much more closely to what people expect from CSS engines in web Err codemadness.org 70 i 2624 browsers.</p> Err codemadness.org 70 i 2625 <p><strong>A well-defined model of property inheritance.</strong> <code>StThemeNode</code>'s Err codemadness.org 70 i 2626 model for CSS property inheritance is a bit ad-hoc and inconsistent. Err codemadness.org 70 i 2627 I haven't quite tested it, but from looking at the code, it seems that Err codemadness.org 70 i 2628 not all properties get inherited in the same way. I hope to move it Err codemadness.org 70 i 2629 to something closer to what librsvg already does, which should make it Err codemadness.org 70 i 2630 match people's expectations from the web.</p> Err codemadness.org 70 i 2631 <h2>In the meantime</h2> Err codemadness.org 70 i 2632 <p>I have a merge request ready to simply move the libcroco source code Err codemadness.org 70 i 2633 directly inside gnome-shell's source tree. This should let distros Err codemadness.org 70 i 2634 remove their libcroco package as soon as possible. That MR does not Err codemadness.org 70 i 2635 require Rust yet.</p> Err codemadness.org 70 i 2636 <p>My playground is here:</p> Err codemadness.org 70 i 2637 <ul> Err codemadness.org 70 i 2638 <li><a href="https://gitlab.gnome.org/federico/gnome-shell/commits/rustify-styles">Gnome-shell branch to rustify the Err codemadness.org 70 i 2639 styles</a></li> Err codemadness.org 70 i 2640 <li><a href="https://gitlab.gnome.org/federico/stylish">Stylish</a>, a Rust Err codemadness.org 70 i 2641 library that will implement gnome-shell's styling code.</li> Err codemadness.org 70 i 2642 </ul> Err codemadness.org 70 i 2643 <p>This does not compile yet! I'll plug things together tomorrow.</p> Err codemadness.org 70 i 2644 <p>(Oh, yes, the project to redo Firefox's CSS stack in Rust used to be Err codemadness.org 70 i 2645 called Stylo. I'm calling this Stylish, as in Styles for the Shell.)</p>Refactoring the Length type2019-11-19T10:01:14-06:002019-11-19T10:01:14-06:00Federico Mena Quinterotag:people.gnome.org,2019-11-19:/~federico/blog/refactoring-the-length-type.html<p><a href="https://www.w3.org/TR/css3-values/#lengths">CSS length values</a> have a number and a unit, e.g. <code>5cm</code> Err codemadness.org 70 i 2646 or <code>6px</code>. Sometimes the unit is a <strong>percentage</strong>, like <code>50%</code>, and SVG Err codemadness.org 70 i 2647 says that lengths with percentage units should be resolved with Err codemadness.org 70 i 2648 respect to a certain rectangle. For example, consider this circle Err codemadness.org 70 i 2649 element:</p> Err codemadness.org 70 i 2650 <div class="highlight"><pre><span></span><code><span class="nt">&lt;circle</span> <span class="na">cx=</span><span class="s">&quot;50%&quot;</span> <span class="na">cy=</span><span class="s">&quot;75 …</span></code></pre></div><p><a href="https://www.w3.org/TR/css3-values/#lengths">CSS length values</a> have a number and a unit, e.g. <code>5cm</code> Err codemadness.org 70 i 2651 or <code>6px</code>. Sometimes the unit is a <strong>percentage</strong>, like <code>50%</code>, and SVG Err codemadness.org 70 i 2652 says that lengths with percentage units should be resolved with Err codemadness.org 70 i 2653 respect to a certain rectangle. For example, consider this circle Err codemadness.org 70 i 2654 element:</p> Err codemadness.org 70 i 2655 <div class="highlight"><pre><span></span><code><span class="nt">&lt;circle</span> <span class="na">cx=</span><span class="s">&quot;50%&quot;</span> <span class="na">cy=</span><span class="s">&quot;75%&quot;</span> <span class="na">r=</span><span class="s">&quot;4px&quot;</span> <span class="na">fill=</span><span class="s">&quot;black&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 2656 </code></pre></div> Err codemadness.org 70 i 2657 Err codemadness.org 70 i 2658 <p>This means, draw a solid black circle whose center is at 50% of the Err codemadness.org 70 i 2659 width and 75% of the height of the current viewport. The circle Err codemadness.org 70 i 2660 should have a 4-pixel radius.</p> Err codemadness.org 70 i 2661 <p>The process of converting that kind of units into absolute pixels for Err codemadness.org 70 i 2662 the final drawing is called <strong>normalization</strong>. In SVG, percentage Err codemadness.org 70 i 2663 units sometimes need to be normalized with respect to the current Err codemadness.org 70 i 2664 viewport (a local coordinate system), or with respect to the size of Err codemadness.org 70 i 2665 another object (e.g. when a clipping path is used to cut the current Err codemadness.org 70 i 2666 shape in half).</p> Err codemadness.org 70 i 2667 <p>One detail about normalization is that it can be with respect to the Err codemadness.org 70 i 2668 horizontal dimension of the current viewport, the vertical dimension, Err codemadness.org 70 i 2669 or both. Keep this in mind: at normalization time, we need to be able Err codemadness.org 70 i 2670 to distinguish between those three modes.</p> Err codemadness.org 70 i 2671 <h2>The original C version</h2> Err codemadness.org 70 i 2672 <p>I have <a href="https://people.gnome.org/~federico/news-2016-11.html#03">talked about the original C code for lengths</a> before; the Err codemadness.org 70 i 2673 following is a small summary.</p> Err codemadness.org 70 i 2674 <p>The original C code had <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/ef720eeabf6a1bf2bca9b31756398578d75998a6/rsvg-private.h#L256-259">this struct</a> to represent lengths:</p> Err codemadness.org 70 i 2675 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 2676 <span class="kt">double</span> <span class="n">length</span><span class="p">;</span> Err codemadness.org 70 i 2677 <span class="kt">char</span> <span class="n">factor</span><span class="p">;</span> Err codemadness.org 70 i 2678 <span class="p">}</span> <span class="n">RsvgLength</span><span class="p">;</span> Err codemadness.org 70 i 2679 </code></pre></div> Err codemadness.org 70 i 2680 Err codemadness.org 70 i 2681 <p>The <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/ef720eeabf6a1bf2bca9b31756398578d75998a6/rsvg-css.c#L181-205">parsing code</a> would set the <code>factor</code> field to a Err codemadness.org 70 i 2682 character depending on the length's unit: <code>'p'</code> for percentages, <code>'i'</code> Err codemadness.org 70 i 2683 for inches, etc., and <code>'\0'</code> for the default unit, which is pixels.</p> Err codemadness.org 70 i 2684 <p>Along with that, the <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/ef720eeabf6a1bf2bca9b31756398578d75998a6/rsvg-css.c#L181-205">normalization code</a> needed to know Err codemadness.org 70 i 2685 the direction (horizontal, vertical, both) to which the length in Err codemadness.org 70 i 2686 question refers. It did this by taking another character as an Err codemadness.org 70 i 2687 argument to the normalization function:</p> Err codemadness.org 70 i 2688 <div class="highlight"><pre><span></span><code><span class="kt">double</span> Err codemadness.org 70 i 2689 <span class="nf">_rsvg_css_normalize_length</span> <span class="p">(</span><span class="k">const</span> <span class="n">RsvgLength</span> <span class="o">*</span> <span class="n">in</span><span class="p">,</span> <span class="n">RsvgDrawingCtx</span> <span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> <span class="kt">char</span> <span class="n">dir</span><span class="p">)</span> Err codemadness.org 70 i 2690 <span class="p">{</span> Err codemadness.org 70 i 2691 <span class="k">if</span> <span class="p">(</span><span class="n">in</span><span class="o">-&gt;</span><span class="n">factor</span> <span class="o">==</span> <span class="sc">&#39;\0&#39;</span><span class="p">)</span> <span class="cm">/* pixels, no need to normalize */</span> Err codemadness.org 70 i 2692 <span class="k">return</span> <span class="n">in</span><span class="o">-&gt;</span><span class="n">length</span><span class="p">;</span> Err codemadness.org 70 i 2693 <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">in</span><span class="o">-&gt;</span><span class="n">factor</span> <span class="o">==</span> <span class="sc">&#39;p&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* percentages; need to consider direction */</span> Err codemadness.org 70 i 2694 <span class="k">if</span> <span class="p">(</span><span class="n">dir</span> <span class="o">==</span> <span class="sc">&#39;h&#39;</span><span class="p">)</span> <span class="cm">/* horizontal */</span> Err codemadness.org 70 i 2695 <span class="k">return</span> <span class="n">in</span><span class="o">-&gt;</span><span class="n">length</span> <span class="o">*</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">vb</span><span class="p">.</span><span class="n">rect</span><span class="p">.</span><span class="n">width</span><span class="p">;</span> Err codemadness.org 70 i 2696 <span class="k">if</span> <span class="p">(</span><span class="n">dir</span> <span class="o">==</span> <span class="sc">&#39;v&#39;</span><span class="p">)</span> <span class="cm">/* vertical */</span> Err codemadness.org 70 i 2697 <span class="k">return</span> <span class="n">in</span><span class="o">-&gt;</span><span class="n">length</span> <span class="o">*</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">vb</span><span class="p">.</span><span class="n">rect</span><span class="p">.</span><span class="n">height</span><span class="p">;</span> Err codemadness.org 70 i 2698 <span class="k">if</span> <span class="p">(</span><span class="n">dir</span> <span class="o">==</span> <span class="sc">&#39;o&#39;</span><span class="p">)</span> <span class="cm">/* both */</span> Err codemadness.org 70 i 2699 <span class="k">return</span> <span class="n">in</span><span class="o">-&gt;</span><span class="n">length</span> <span class="o">*</span> <span class="n">rsvg_viewport_percentage</span> <span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">vb</span><span class="p">.</span><span class="n">rect</span><span class="p">.</span><span class="n">width</span><span class="p">,</span> Err codemadness.org 70 i 2700 <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">vb</span><span class="p">.</span><span class="n">rect</span><span class="p">.</span><span class="n">height</span><span class="p">);</span> Err codemadness.org 70 i 2701 <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> Err codemadness.org 70 i 2702 <span class="p">}</span> Err codemadness.org 70 i 2703 </code></pre></div> Err codemadness.org 70 i 2704 Err codemadness.org 70 i 2705 <p><a href="https://people.gnome.org/~federico/news-2016-11.html#03">The original post</a> talks about how I found a couple of bugs with Err codemadness.org 70 i 2706 how the directions are identified at normalization time. The function Err codemadness.org 70 i 2707 above expects one of <code>'h'/'v'/'o'</code> for horizontal/vertical/both, and one or Err codemadness.org 70 i 2708 two places in the code passed the wrong character.</p> Err codemadness.org 70 i 2709 <h2>Making the C version cleaner</h2> Err codemadness.org 70 i 2710 <p>Before converting that code to Rust, I <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/b7768db1a9cd129298737f0d0ea9fd7cd7d444a0">removed the pesky Err codemadness.org 70 i 2711 characters</a> and made the code use proper enums to Err codemadness.org 70 i 2712 identify a length's units.</p> Err codemadness.org 70 i 2713 <div class="highlight"><pre><span></span><code><span class="o">+</span><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span> Err codemadness.org 70 i 2714 <span class="o">+</span> <span class="n">LENGTH_UNIT_DEFAULT</span><span class="p">,</span> Err codemadness.org 70 i 2715 <span class="o">+</span> <span class="n">LENGTH_UNIT_PERCENT</span><span class="p">,</span> Err codemadness.org 70 i 2716 <span class="o">+</span> <span class="n">LENGTH_UNIT_FONT_EM</span><span class="p">,</span> Err codemadness.org 70 i 2717 <span class="o">+</span> <span class="n">LENGTH_UNIT_FONT_EX</span><span class="p">,</span> Err codemadness.org 70 i 2718 <span class="o">+</span> <span class="n">LENGTH_UNIT_INCH</span><span class="p">,</span> Err codemadness.org 70 i 2719 <span class="o">+</span> <span class="n">LENGTH_UNIT_RELATIVE_LARGER</span><span class="p">,</span> Err codemadness.org 70 i 2720 <span class="o">+</span> <span class="n">LENGTH_UNIT_RELATIVE_SMALLER</span> Err codemadness.org 70 i 2721 <span class="o">+</span><span class="p">}</span> <span class="n">LengthUnit</span><span class="p">;</span> Err codemadness.org 70 i 2722 <span class="o">+</span> Err codemadness.org 70 i 2723 <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 2724 <span class="kt">double</span> <span class="n">length</span><span class="p">;</span> Err codemadness.org 70 i 2725 <span class="o">-</span> <span class="kt">char</span> <span class="n">factor</span><span class="p">;</span> Err codemadness.org 70 i 2726 <span class="o">+</span> <span class="n">LengthUnit</span> <span class="n">unit</span><span class="p">;</span> Err codemadness.org 70 i 2727 <span class="p">}</span> <span class="n">RsvgLength</span><span class="p">;</span> Err codemadness.org 70 i 2728 </code></pre></div> Err codemadness.org 70 i 2729 Err codemadness.org 70 i 2730 <p>Then, <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/cb166d90d1b4370108ce57b8651a6a7f61ccd89d">do the same for the normalization function</a>, so it will get the Err codemadness.org 70 i 2731 direction in which to normalize as an enum instead of a char.</p> Err codemadness.org 70 i 2732 <div class="highlight"><pre><span></span><code><span class="o">+</span><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span> Err codemadness.org 70 i 2733 <span class="o">+</span> <span class="n">LENGTH_DIR_HORIZONTAL</span><span class="p">,</span> Err codemadness.org 70 i 2734 <span class="o">+</span> <span class="n">LENGTH_DIR_VERTICAL</span><span class="p">,</span> Err codemadness.org 70 i 2735 <span class="o">+</span> <span class="n">LENGTH_DIR_BOTH</span> Err codemadness.org 70 i 2736 <span class="o">+</span><span class="p">}</span> <span class="n">LengthDir</span><span class="p">;</span> Err codemadness.org 70 i 2737 Err codemadness.org 70 i 2738 <span class="kt">double</span> Err codemadness.org 70 i 2739 <span class="o">-</span><span class="n">_rsvg_css_normalize_length</span> <span class="p">(</span><span class="k">const</span> <span class="n">RsvgLength</span> <span class="o">*</span> <span class="n">in</span><span class="p">,</span> <span class="n">RsvgDrawingCtx</span> <span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> <span class="kt">char</span> <span class="n">dir</span><span class="p">)</span> Err codemadness.org 70 i 2740 <span class="o">+</span><span class="n">_rsvg_css_normalize_length</span> <span class="p">(</span><span class="k">const</span> <span class="n">RsvgLength</span> <span class="o">*</span> <span class="n">in</span><span class="p">,</span> <span class="n">RsvgDrawingCtx</span> <span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">LengthDir</span> <span class="n">dir</span><span class="p">)</span> Err codemadness.org 70 i 2741 </code></pre></div> Err codemadness.org 70 i 2742 Err codemadness.org 70 i 2743 <h2>Making the C version easier to get right</h2> Err codemadness.org 70 i 2744 <p>While doing the last change above, I found a place in the code that Err codemadness.org 70 i 2745 used the wrong direction by mistake, probably due to a cut&amp;paste Err codemadness.org 70 i 2746 error. Part of the problem here is that the code was specifying the Err codemadness.org 70 i 2747 direction at normalization time.</p> Err codemadness.org 70 i 2748 <p>I decided to change it so that <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/5a85e7cf2ecc90486346debc9a5a426163348f52">each direction value carried its own Err codemadness.org 70 i 2749 direction since initialization</a>, so that subsequent Err codemadness.org 70 i 2750 code wouldn't have to worry about that. Hopefully, initializing a Err codemadness.org 70 i 2751 <code>width</code> field should make it obvious that it needed Err codemadness.org 70 i 2752 <code>LENGTH_DIR_HORIZONTAL</code>.</p> Err codemadness.org 70 i 2753 <div class="highlight"><pre><span></span><code> <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 2754 <span class="kt">double</span> <span class="n">length</span><span class="p">;</span> Err codemadness.org 70 i 2755 <span class="n">LengthUnit</span> <span class="n">unit</span><span class="p">;</span> Err codemadness.org 70 i 2756 <span class="o">+</span> <span class="n">LengthDir</span> <span class="n">dir</span><span class="p">;</span> Err codemadness.org 70 i 2757 <span class="p">}</span> <span class="n">RsvgLength</span><span class="p">;</span> Err codemadness.org 70 i 2758 </code></pre></div> Err codemadness.org 70 i 2759 Err codemadness.org 70 i 2760 <p>That is, so that instead of</p> Err codemadness.org 70 i 2761 <div class="highlight"><pre><span></span><code> <span class="cm">/* at initialization time */</span> Err codemadness.org 70 i 2762 <span class="n">foo</span><span class="p">.</span><span class="n">width</span> <span class="o">=</span> <span class="n">_rsvg_css_parse_length</span> <span class="p">(</span><span class="n">str</span><span class="p">);</span> Err codemadness.org 70 i 2763 Err codemadness.org 70 i 2764 <span class="p">...</span> Err codemadness.org 70 i 2765 Err codemadness.org 70 i 2766 <span class="cm">/* at rendering time */</span> Err codemadness.org 70 i 2767 <span class="kt">double</span> <span class="n">final_width</span> <span class="o">=</span> <span class="n">_rsvg_css_normalize_length</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">foo</span><span class="p">.</span><span class="n">width</span><span class="p">,</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">LENGTH_DIR_HORIZONTAL</span><span class="p">);</span> Err codemadness.org 70 i 2768 </code></pre></div> Err codemadness.org 70 i 2769 Err codemadness.org 70 i 2770 <p>we would instead do this:</p> Err codemadness.org 70 i 2771 <div class="highlight"><pre><span></span><code> <span class="cm">/* at initialization time */</span> Err codemadness.org 70 i 2772 <span class="n">foo</span><span class="p">.</span><span class="n">width</span> <span class="o">=</span> <span class="n">_rsvg_css_parse_length</span> <span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">LENGTH_DIR_HORIZONTAL</span><span class="p">);</span> Err codemadness.org 70 i 2773 Err codemadness.org 70 i 2774 <span class="p">...</span> Err codemadness.org 70 i 2775 Err codemadness.org 70 i 2776 <span class="cm">/* at rendering time */</span> Err codemadness.org 70 i 2777 <span class="kt">double</span> <span class="n">final_width</span> <span class="o">=</span> <span class="n">_rsvg_css_normalize_length</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">foo</span><span class="p">.</span><span class="n">width</span><span class="p">,</span> <span class="n">ctx</span><span class="p">);</span> Err codemadness.org 70 i 2778 </code></pre></div> Err codemadness.org 70 i 2779 Err codemadness.org 70 i 2780 <p>This made the drawing code, which deals with a lot of coordinates at Err codemadness.org 70 i 2781 the same time, a lot less noisy.</p> Err codemadness.org 70 i 2782 <h2>Initial port to Rust</h2> Err codemadness.org 70 i 2783 <p>To recap, this was the state of the structs after the initial Err codemadness.org 70 i 2784 refactoring in C:</p> Err codemadness.org 70 i 2785 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span> Err codemadness.org 70 i 2786 <span class="n">LENGTH_UNIT_DEFAULT</span><span class="p">,</span> Err codemadness.org 70 i 2787 <span class="n">LENGTH_UNIT_PERCENT</span><span class="p">,</span> Err codemadness.org 70 i 2788 <span class="n">LENGTH_UNIT_FONT_EM</span><span class="p">,</span> Err codemadness.org 70 i 2789 <span class="n">LENGTH_UNIT_FONT_EX</span><span class="p">,</span> Err codemadness.org 70 i 2790 <span class="n">LENGTH_UNIT_INCH</span><span class="p">,</span> Err codemadness.org 70 i 2791 <span class="n">LENGTH_UNIT_RELATIVE_LARGER</span><span class="p">,</span> Err codemadness.org 70 i 2792 <span class="n">LENGTH_UNIT_RELATIVE_SMALLER</span> Err codemadness.org 70 i 2793 <span class="p">}</span> <span class="n">LengthUnit</span><span class="p">;</span> Err codemadness.org 70 i 2794 Err codemadness.org 70 i 2795 <span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span> Err codemadness.org 70 i 2796 <span class="n">LENGTH_DIR_HORIZONTAL</span><span class="p">,</span> Err codemadness.org 70 i 2797 <span class="n">LENGTH_DIR_VERTICAL</span><span class="p">,</span> Err codemadness.org 70 i 2798 <span class="n">LENGTH_DIR_BOTH</span> Err codemadness.org 70 i 2799 <span class="p">}</span> <span class="n">LengthDir</span><span class="p">;</span> Err codemadness.org 70 i 2800 Err codemadness.org 70 i 2801 <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 2802 <span class="kt">double</span> <span class="n">length</span><span class="p">;</span> Err codemadness.org 70 i 2803 <span class="n">LengthUnit</span> <span class="n">unit</span><span class="p">;</span> Err codemadness.org 70 i 2804 <span class="n">LengthDir</span> <span class="n">dir</span><span class="p">;</span> Err codemadness.org 70 i 2805 <span class="p">}</span> <span class="n">RsvgLength</span><span class="p">;</span> Err codemadness.org 70 i 2806 </code></pre></div> Err codemadness.org 70 i 2807 Err codemadness.org 70 i 2808 <p>This <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/03d7716b4dd2e4e7cb04f58b14f00d2bff42c0d4">ported to Rust</a> in a straightforward fashion:</p> Err codemadness.org 70 i 2809 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">LengthUnit</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2810 <span class="w"> </span><span class="nb">Default</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2811 <span class="w"> </span><span class="n">Percent</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2812 <span class="w"> </span><span class="n">FontEm</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2813 <span class="w"> </span><span class="n">FontEx</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2814 <span class="w"> </span><span class="n">Inch</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2815 <span class="w"> </span><span class="n">RelativeLarger</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2816 <span class="w"> </span><span class="n">RelativeSmaller</span><span class="w"></span> Err codemadness.org 70 i 2817 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2818 Err codemadness.org 70 i 2819 <span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">LengthDir</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2820 <span class="w"> </span><span class="n">Horizontal</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2821 <span class="w"> </span><span class="n">Vertical</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2822 <span class="w"> </span><span class="n">Both</span><span class="w"></span> Err codemadness.org 70 i 2823 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2824 Err codemadness.org 70 i 2825 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">RsvgLength</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2826 <span class="w"> </span><span class="n">length</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2827 <span class="w"> </span><span class="n">unit</span>: <span class="nc">LengthUnit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2828 <span class="w"> </span><span class="n">dir</span>: <span class="nc">LengthDir</span><span class="w"></span> Err codemadness.org 70 i 2829 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2830 </code></pre></div> Err codemadness.org 70 i 2831 Err codemadness.org 70 i 2832 <p>It <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/822459f29b74e4e154d721c80f6e16fe6f05e0f2">got a similar constructor</a> that took the Err codemadness.org 70 i 2833 direction and produced an <code>RsvgLength</code>:</p> Err codemadness.org 70 i 2834 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">RsvgLength</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2835 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="w"> </span><span class="p">(</span><span class="n">string</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">dir</span>: <span class="nc">LengthDir</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">RsvgLength</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2836 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2837 </code></pre></div> Err codemadness.org 70 i 2838 Err codemadness.org 70 i 2839 <p>(This was <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/e299ef0e285f7d1267528a38d8c41596b89da526">before using <code>Result</code></a>; remember that the original Err codemadness.org 70 i 2840 C code did very little error checking!)</p> Err codemadness.org 70 i 2841 <h2>The initial Parse trait</h2> Err codemadness.org 70 i 2842 <p>It was at that point that it seemed convenient to <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/5510e4c3f9c9c84bac4a05f6d93a09c9b4862544">introduce a <code>Parse</code> Err codemadness.org 70 i 2843 trait</a>, which all CSS value types would implement to Err codemadness.org 70 i 2844 parse themselves from string.</p> Err codemadness.org 70 i 2845 <p>However, parsing an <code>RsvgLength</code> also needed an extra piece of data, Err codemadness.org 70 i 2846 the <code>LengthDir</code>. My initial version of the <code>Parse</code> trait had an Err codemadness.org 70 i 2847 associated called <code>Data</code>, through which one could pass an extra piece Err codemadness.org 70 i 2848 of data during parsing/initialization:</p> Err codemadness.org 70 i 2849 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">Parse</span>: <span class="nb">Sized</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2850 <span class="w"> </span><span class="k">type</span> <span class="nc">Data</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2851 <span class="w"> </span><span class="k">type</span> <span class="nb">Err</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2852 Err codemadness.org 70 i 2853 <span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="w"> </span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">data</span>: <span class="nc">Self</span>::<span class="n">Data</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="bp">Self</span>::<span class="nb">Err</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2854 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2855 </code></pre></div> Err codemadness.org 70 i 2856 Err codemadness.org 70 i 2857 <p>This was explicitly to be able to <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/0cd2fb2145c25898f296fe2c268f7527f58834c8">pass a <code>LengthDir</code> to the parser</a> for Err codemadness.org 70 i 2858 <code>RsvgLength</code>:</p> Err codemadness.org 70 i 2859 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">Parse</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">RsvgLength</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2860 <span class="w"> </span><span class="k">type</span> <span class="nc">Data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">LengthDir</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2861 <span class="w"> </span><span class="k">type</span> <span class="nb">Err</span> <span class="o">=</span><span class="w"> </span><span class="n">AttributeError</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2862 Err codemadness.org 70 i 2863 <span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="w"> </span><span class="p">(</span><span class="n">string</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">dir</span>: <span class="nc">LengthDir</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span> <span class="o">&lt;</span><span class="n">RsvgLength</span><span class="p">,</span><span class="w"> </span><span class="n">AttributeError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2864 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2865 </code></pre></div> Err codemadness.org 70 i 2866 Err codemadness.org 70 i 2867 <p>This was okay for lengths, but <em>very noisy</em> for everything else that Err codemadness.org 70 i 2868 didn't require an extra bit of data. In the rest of the code, the Err codemadness.org 70 i 2869 helper type was <code>Data = ()</code> and there was a pair of extra parentheses <code>()</code> Err codemadness.org 70 i 2870 in every place that <code>parse()</code> was called.</p> Err codemadness.org 70 i 2871 <h2>Removing the helper Data type</h2> Err codemadness.org 70 i 2872 <h3>Introducing one type per direction</h3> Err codemadness.org 70 i 2873 <p>Over a year later, that <code>()</code> bit of data everywhere was driving me Err codemadness.org 70 i 2874 nuts. I started refactoring the <code>Length</code> module to remove it.</p> Err codemadness.org 70 i 2875 <p>First, I <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/cab989b9f749ca71551ad1fb68411bb2e96d96b8">introduced three newtypes</a> to wrap <code>Length</code>, and indicate Err codemadness.org 70 i 2876 their direction at the same time:</p> Err codemadness.org 70 i 2877 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">LengthHorizontal</span><span class="p">(</span><span class="n">Length</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2878 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">LengthVertical</span><span class="p">(</span><span class="n">Length</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2879 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">LengthBoth</span><span class="p">(</span><span class="n">Length</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2880 </code></pre></div> Err codemadness.org 70 i 2881 Err codemadness.org 70 i 2882 <p>This was <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/cab989b9f749ca71551ad1fb68411bb2e96d96b8">done with a macro</a> because now each wrapper type Err codemadness.org 70 i 2883 needed to know the relevant <code>LengthDir</code>.</p> Err codemadness.org 70 i 2884 <p>Now, for example, the declaration for the <code>&lt;circle&gt;</code> element looked Err codemadness.org 70 i 2885 like this:</p> Err codemadness.org 70 i 2886 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">NodeCircle</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2887 <span class="w"> </span><span class="n">cx</span>: <span class="nc">Cell</span><span class="o">&lt;</span><span class="n">LengthHorizontal</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2888 <span class="w"> </span><span class="n">cy</span>: <span class="nc">Cell</span><span class="o">&lt;</span><span class="n">LengthVertical</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2889 <span class="w"> </span><span class="n">r</span>: <span class="nc">Cell</span><span class="o">&lt;</span><span class="n">LengthBoth</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2890 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2891 </code></pre></div> Err codemadness.org 70 i 2892 Err codemadness.org 70 i 2893 <p>(Ignore the <code>Cell</code> everywhere; we got rid of that later.)</p> Err codemadness.org 70 i 2894 <h3>Removing the <code>dir</code> field</h3> Err codemadness.org 70 i 2895 <p>Since now the information about the length's direction is embodied in Err codemadness.org 70 i 2896 the <code>LengthHorizontal/LengthVertical/LengthBoth</code> types, this made it Err codemadness.org 70 i 2897 possible to <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/78f39807ce75e176840ec049539d9897f53316d5">remove the <code>dir</code> field</a> from the inner Err codemadness.org 70 i 2898 <code>Length</code> struct.</p> Err codemadness.org 70 i 2899 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">RsvgLength</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2900 <span class="w"> </span><span class="n">length</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2901 <span class="w"> </span><span class="n">unit</span>: <span class="nc">LengthUnit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2902 <span class="o">-</span><span class="w"> </span><span class="n">dir</span>: <span class="nc">LengthDir</span><span class="w"></span> Err codemadness.org 70 i 2903 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2904 Err codemadness.org 70 i 2905 <span class="o">+</span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">LengthHorizontal</span><span class="p">(</span><span class="n">Length</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2906 <span class="o">+</span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">LengthVertical</span><span class="p">(</span><span class="n">Length</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2907 <span class="o">+</span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">LengthBoth</span><span class="p">(</span><span class="n">Length</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2908 <span class="o">+</span><span class="w"></span> Err codemadness.org 70 i 2909 <span class="o">+</span><span class="n">define_length_type</span><span class="o">!</span><span class="p">(</span><span class="n">LengthHorizontal</span><span class="p">,</span><span class="w"> </span><span class="n">LengthDir</span>::<span class="n">Horizontal</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2910 <span class="o">+</span><span class="n">define_length_type</span><span class="o">!</span><span class="p">(</span><span class="n">LengthVertical</span><span class="p">,</span><span class="w"> </span><span class="n">LengthDir</span>::<span class="n">Vertical</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2911 <span class="o">+</span><span class="n">define_length_type</span><span class="o">!</span><span class="p">(</span><span class="n">LengthBoth</span><span class="p">,</span><span class="w"> </span><span class="n">LengthDir</span>::<span class="n">Both</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 2912 </code></pre></div> Err codemadness.org 70 i 2913 Err codemadness.org 70 i 2914 <p>Note the use of a <code>define_length_type!</code> macro to generate code for Err codemadness.org 70 i 2915 those three newtypes.</p> Err codemadness.org 70 i 2916 <h3>Removing the <code>Data</code> associated type</h3> Err codemadness.org 70 i 2917 <p>And finally, this made it possible to <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/0532d20d92971e7346fade2c48483b06dd49762c">remove the <code>Data</code> associated Err codemadness.org 70 i 2918 type</a> from the <code>Parse</code> trait.</p> Err codemadness.org 70 i 2919 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">Parse</span>: <span class="nb">Sized</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2920 <span class="o">-</span><span class="w"> </span><span class="k">type</span> <span class="nc">Data</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2921 <span class="w"> </span><span class="k">type</span> <span class="nb">Err</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2922 Err codemadness.org 70 i 2923 <span class="o">-</span><span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="p">(</span><span class="n">parser</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">Parser</span><span class="o">&lt;&#39;</span><span class="nb">_</span><span class="p">,</span><span class="w"> </span><span class="o">&#39;</span><span class="nb">_</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="n">data</span>: <span class="nc">Self</span>::<span class="n">Data</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="bp">Self</span>::<span class="nb">Err</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2924 <span class="o">+</span><span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="p">(</span><span class="n">parser</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">Parser</span><span class="o">&lt;&#39;</span><span class="nb">_</span><span class="p">,</span><span class="w"> </span><span class="o">&#39;</span><span class="nb">_</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="bp">Self</span>::<span class="nb">Err</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2925 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2926 </code></pre></div> Err codemadness.org 70 i 2927 Err codemadness.org 70 i 2928 <p>The resulting mega-commit removed a bunch of stray parentheses <code>()</code> Err codemadness.org 70 i 2929 from all calls to <code>parse()</code>, and the code ended up a lot easier to Err codemadness.org 70 i 2930 read.</p> Err codemadness.org 70 i 2931 <h2>Removing the newtypes</h2> Err codemadness.org 70 i 2932 <p>This was fine for a while. Recently, however, I figured out that it Err codemadness.org 70 i 2933 would be possible to embody the information for a length's direction Err codemadness.org 70 i 2934 in a different way.</p> Err codemadness.org 70 i 2935 <p>But to get there, I first needed a temporary refactor.</p> Err codemadness.org 70 i 2936 <h3>Replacing the macro with a trait with a default implementation</h3> Err codemadness.org 70 i 2937 <p>Deep in the guts of <code>length.rs</code>, the key function that does something Err codemadness.org 70 i 2938 different based on <code>LengthDir</code> is its <code>scaling_factor</code> method:</p> Err codemadness.org 70 i 2939 <div class="highlight"><pre><span></span><code><span class="k">enum</span> <span class="nc">LengthDir</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2940 <span class="w"> </span><span class="n">Horizontal</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2941 <span class="w"> </span><span class="n">Vertical</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2942 <span class="w"> </span><span class="n">Both</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2943 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2944 Err codemadness.org 70 i 2945 <span class="k">impl</span><span class="w"> </span><span class="n">LengthDir</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2946 <span class="w"> </span><span class="k">fn</span> <span class="nf">scaling_factor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">x</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="kt">f64</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2947 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="bp">self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2948 <span class="w"> </span><span class="n">LengthDir</span>::<span class="n">Horizontal</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2949 <span class="w"> </span><span class="n">LengthDir</span>::<span class="n">Vertical</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 2950 <span class="w"> </span><span class="n">LengthDir</span>::<span class="n">Both</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">viewport_percentage</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 2951 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2952 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2953 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2954 </code></pre></div> Err codemadness.org 70 i 2955 Err codemadness.org 70 i 2956 <p>That method gets passed, for example, the <code>width/height</code> of the Err codemadness.org 70 i 2957 current viewport for the <code>x/y</code> arguments. The method decides whether Err codemadness.org 70 i 2958 to use the width, height, or a combination of both.</p> Err codemadness.org 70 i 2959 <p>And of course, the interesting part of the <code>define_length_type!</code> macro Err codemadness.org 70 i 2960 was to generate code for calling <code>LengthDir::Horizontal::scaling_factor()</code>/etc. as Err codemadness.org 70 i 2961 appropriate depending on the <code>LengthDir</code> in question.</p> Err codemadness.org 70 i 2962 <p>First I made a trait called <code>Orientation</code> with a <code>scaling_factor</code> Err codemadness.org 70 i 2963 method, and three zero-sized types that implement that trait. Note Err codemadness.org 70 i 2964 how each of these three implementations corresponds to one of the Err codemadness.org 70 i 2965 <code>match</code> arms above:</p> Err codemadness.org 70 i 2966 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">Orientation</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2967 <span class="w"> </span><span class="k">fn</span> <span class="nf">scaling_factor</span><span class="p">(</span><span class="n">x</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="kt">f64</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">f64</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2968 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2969 Err codemadness.org 70 i 2970 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Horizontal</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2971 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Vertical</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2972 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Both</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 2973 Err codemadness.org 70 i 2974 <span class="k">impl</span><span class="w"> </span><span class="n">Orientation</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Horizontal</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2975 <span class="w"> </span><span class="k">fn</span> <span class="nf">scaling_factor</span><span class="p">(</span><span class="n">x</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="n">_y</span>: <span class="kt">f64</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2976 <span class="w"> </span><span class="n">x</span><span class="w"></span> Err codemadness.org 70 i 2977 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2978 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2979 Err codemadness.org 70 i 2980 <span class="k">impl</span><span class="w"> </span><span class="n">Orientation</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Vertical</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2981 <span class="w"> </span><span class="k">fn</span> <span class="nf">scaling_factor</span><span class="p">(</span><span class="n">_x</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="kt">f64</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2982 <span class="w"> </span><span class="n">y</span><span class="w"></span> Err codemadness.org 70 i 2983 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2984 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2985 Err codemadness.org 70 i 2986 <span class="k">impl</span><span class="w"> </span><span class="n">Orientation</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Both</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2987 <span class="w"> </span><span class="k">fn</span> <span class="nf">scaling_factor</span><span class="p">(</span><span class="n">x</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="kt">f64</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2988 <span class="w"> </span><span class="n">viewport_percentage</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 2989 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2990 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 2991 </code></pre></div> Err codemadness.org 70 i 2992 Err codemadness.org 70 i 2993 <p>Now most of the contents of the <code>define_length_type!</code> macro can go in Err codemadness.org 70 i 2994 the <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/21a40f4ef801eecfe58c7602835c37f436f0e926">default implementation of a new trait Err codemadness.org 70 i 2995 <code>LengthTrait</code></a>. Crucially, this trait has an Err codemadness.org 70 i 2996 <code>Orientation</code> associated type, <strong>which it uses to call into the Err codemadness.org 70 i 2997 Orientation trait</strong>:</p> Err codemadness.org 70 i 2998 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">LengthTrait</span>: <span class="nb">Sized</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 2999 <span class="w"> </span><span class="k">type</span> <span class="nc">Orientation</span>: <span class="nc">Orientation</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3000 Err codemadness.org 70 i 3001 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 3002 Err codemadness.org 70 i 3003 <span class="w"> </span><span class="k">fn</span> <span class="nf">normalize</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">values</span>: <span class="kp">&amp;</span><span class="nc">ComputedValues</span><span class="p">,</span><span class="w"> </span><span class="n">params</span>: <span class="kp">&amp;</span><span class="nc">ViewParams</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3004 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">unit</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3005 <span class="w"> </span><span class="n">LengthUnit</span>::<span class="n">Px</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">length</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 3006 Err codemadness.org 70 i 3007 <span class="w"> </span><span class="n">LengthUnit</span>::<span class="n">Percent</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3008 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">length</span><span class="p">()</span><span class="w"> </span><span class="o">*</span><span class="w"></span> Err codemadness.org 70 i 3009 <span class="w"> </span><span class="o">&lt;</span><span class="bp">Self</span>::<span class="n">Orientation</span><span class="o">&gt;</span>::<span class="n">scaling_factor</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">view_box_width</span><span class="p">,</span><span class="w"> </span><span class="n">params</span><span class="p">.</span><span class="n">view_box_height</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 3010 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3011 Err codemadness.org 70 i 3012 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 3013 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3014 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3015 </code></pre></div> Err codemadness.org 70 i 3016 Err codemadness.org 70 i 3017 <p>Note that the incantation is Err codemadness.org 70 i 3018 <code>&lt;Self::Orientation&gt;::scaling_factor(...)</code> to call that method on the Err codemadness.org 70 i 3019 associated type.</p> Err codemadness.org 70 i 3020 <p>Now the <code>define_length_type!</code> macro is shrunk a lot, with the Err codemadness.org 70 i 3021 interesting part being just this:</p> Err codemadness.org 70 i 3022 <div class="highlight"><pre><span></span><code><span class="fm">macro_rules!</span><span class="w"> </span><span class="n">define_length_type</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3023 <span class="w"> </span><span class="p">{</span><span class="cp">$name</span>:<span class="nc">ident</span><span class="p">,</span><span class="w"> </span><span class="cp">$orient</span>:<span class="nc">ty</span><span class="p">}</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3024 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="cp">$name</span><span class="p">(</span><span class="n">Length</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 3025 Err codemadness.org 70 i 3026 <span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">LengthTrait</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="cp">$name</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3027 <span class="w"> </span><span class="k">type</span> <span class="nc">Orientation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="cp">$orient</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3028 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3029 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3030 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3031 Err codemadness.org 70 i 3032 <span class="n">define_length_type</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">LengthHorizontal</span><span class="p">,</span><span class="w"> </span><span class="n">Horizontal</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3033 <span class="n">define_length_type</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">LengthVertical</span><span class="p">,</span><span class="w"> </span><span class="n">Vertical</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3034 <span class="n">define_length_type</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">LengthBoth</span><span class="p">,</span><span class="w"> </span><span class="n">Both</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3035 </code></pre></div> Err codemadness.org 70 i 3036 Err codemadness.org 70 i 3037 <p>We moved from having three newtypes of length-with-LengthDir to three Err codemadness.org 70 i 3038 newtypes with dir-as-associated-type.</p> Err codemadness.org 70 i 3039 <h3>Removing the newtypes and the macro</h3> Err codemadness.org 70 i 3040 <p>After that temporary refactoring, we had the <code>Orientation</code> trait and Err codemadness.org 70 i 3041 the three zero-sized types <code>Horizontal</code>, <code>Vertical</code>, <code>Both</code>.</p> Err codemadness.org 70 i 3042 <p>I figured out that one can use <a href="https://doc.rust-lang.org/std/marker/struct.PhantomData.html"><code>PhantomData</code></a> as a way to carry Err codemadness.org 70 i 3043 around the type that <code>Length</code> needs to normalize itself, instead of Err codemadness.org 70 i 3044 using an associated type in an extra <code>LengthTrait</code>. Behold!</p> Err codemadness.org 70 i 3045 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Length</span><span class="o">&lt;</span><span class="n">O</span>: <span class="nc">Orientation</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3046 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">length</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3047 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">unit</span>: <span class="nc">LengthUnit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3048 <span class="w"> </span><span class="n">orientation</span>: <span class="nc">PhantomData</span><span class="o">&lt;</span><span class="n">O</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3049 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3050 Err codemadness.org 70 i 3051 <span class="k">impl</span><span class="o">&lt;</span><span class="n">O</span>: <span class="nc">Orientation</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Length</span><span class="o">&lt;</span><span class="n">O</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3052 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">normalize</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">values</span>: <span class="kp">&amp;</span><span class="nc">ComputedValues</span><span class="p">,</span><span class="w"> </span><span class="n">params</span>: <span class="kp">&amp;</span><span class="nc">ViewParams</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3053 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">unit</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3054 <span class="w"> </span><span class="n">LengthUnit</span>::<span class="n">Px</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">length</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3055 Err codemadness.org 70 i 3056 <span class="w"> </span><span class="n">LengthUnit</span>::<span class="n">Percent</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3057 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">length</span><span class="w"> </span> Err codemadness.org 70 i 3058 <span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="o">&lt;</span><span class="n">O</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">Orientation</span><span class="o">&gt;</span>::<span class="n">scaling_factor</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">view_box_width</span><span class="p">,</span><span class="w"> </span><span class="n">params</span><span class="p">.</span><span class="n">view_box_height</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 3059 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3060 Err codemadness.org 70 i 3061 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 3062 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3063 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3064 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3065 </code></pre></div> Err codemadness.org 70 i 3066 Err codemadness.org 70 i 3067 <p>Now the incantation is <code>&lt;O as Orientation&gt;::scaling_factor()</code> to call Err codemadness.org 70 i 3068 the method on the generic type; it is no longer an associated type in Err codemadness.org 70 i 3069 a trait.</p> Err codemadness.org 70 i 3070 <p>With that, users of lengths look like this; here, our <code>&lt;circle&gt;</code> Err codemadness.org 70 i 3071 element from before:</p> Err codemadness.org 70 i 3072 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Circle</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3073 <span class="w"> </span><span class="n">cx</span>: <span class="nc">Length</span><span class="o">&lt;</span><span class="n">Horizontal</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3074 <span class="w"> </span><span class="n">cy</span>: <span class="nc">Length</span><span class="o">&lt;</span><span class="n">Vertical</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3075 <span class="w"> </span><span class="n">r</span>: <span class="nc">Length</span><span class="o">&lt;</span><span class="n">Both</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3076 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3077 </code></pre></div> Err codemadness.org 70 i 3078 Err codemadness.org 70 i 3079 <p>I'm very happy with the readability of all the code now. I used to Err codemadness.org 70 i 3080 think of <code>PhantomData</code> as a way to deal with <a href="https://doc.rust-lang.org/nomicon/phantom-data.html">wrapping pointers from Err codemadness.org 70 i 3081 C</a>, but it turns out that it is also useful to keep a generic Err codemadness.org 70 i 3082 type around should one need it.</p> Err codemadness.org 70 i 3083 <p>The final <code>Length</code> struct is this:</p> Err codemadness.org 70 i 3084 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Length</span><span class="o">&lt;</span><span class="n">O</span>: <span class="nc">Orientation</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3085 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">length</span>: <span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3086 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">unit</span>: <span class="nc">LengthUnit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3087 <span class="w"> </span><span class="n">orientation</span>: <span class="nc">PhantomData</span><span class="o">&lt;</span><span class="n">O</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3088 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3089 </code></pre></div> Err codemadness.org 70 i 3090 Err codemadness.org 70 i 3091 <p>And it only takes up as much space as its <code>length</code> and <code>unit</code> fields; Err codemadness.org 70 i 3092 <code>PhantomData</code> is zero-sized after all.</p> Err codemadness.org 70 i 3093 <p>(Later, we renamed <code>Orientation</code> to <code>Normalize</code>, but the code's Err codemadness.org 70 i 3094 structure remained the same.)</p> Err codemadness.org 70 i 3095 <h2>Summary</h2> Err codemadness.org 70 i 3096 <p>Over a couple of years, librsvg's type that represents CSS lengths Err codemadness.org 70 i 3097 went from a C representation along the lines of "all data in the world Err codemadness.org 70 i 3098 is an int", to a Rust representation that uses some interesting type Err codemadness.org 70 i 3099 trickery:</p> Err codemadness.org 70 i 3100 <ul> Err codemadness.org 70 i 3101 <li> Err codemadness.org 70 i 3102 <p>C struct with <code>char</code> for units.</p> Err codemadness.org 70 i 3103 </li> Err codemadness.org 70 i 3104 <li> Err codemadness.org 70 i 3105 <p>C struct with a <code>LengthUnits</code> enum.</p> Err codemadness.org 70 i 3106 </li> Err codemadness.org 70 i 3107 <li> Err codemadness.org 70 i 3108 <p>C struct without an embodied direction; each place that needs to Err codemadness.org 70 i 3109 normalize needs to get the orientation right.</p> Err codemadness.org 70 i 3110 </li> Err codemadness.org 70 i 3111 <li> Err codemadness.org 70 i 3112 <p>C struct with a built-in direction as an extra field, done at Err codemadness.org 70 i 3113 initialization time.</p> Err codemadness.org 70 i 3114 </li> Err codemadness.org 70 i 3115 <li> Err codemadness.org 70 i 3116 <p>Same struct but in Rust.</p> Err codemadness.org 70 i 3117 </li> Err codemadness.org 70 i 3118 <li> Err codemadness.org 70 i 3119 <p>An ugly but workable <code>Parse</code> trait so that the direction can be set Err codemadness.org 70 i 3120 at parse/initialization time.</p> Err codemadness.org 70 i 3121 </li> Err codemadness.org 70 i 3122 <li> Err codemadness.org 70 i 3123 <p>Three newtypes <code>LengthHorizontal</code>, <code>LengthVertical</code>, Err codemadness.org 70 i 3124 <code>LengthBoth</code> with a common core. A cleaned-up <code>Parse</code> trait. A Err codemadness.org 70 i 3125 macro to generate those newtypes.</p> Err codemadness.org 70 i 3126 </li> Err codemadness.org 70 i 3127 <li> Err codemadness.org 70 i 3128 <p>Replace the <code>LengthDir</code> enum with an <code>Orientation</code> Err codemadness.org 70 i 3129 trait, and three zero-sized types <code>Horizontal/Vertical/Both</code> that Err codemadness.org 70 i 3130 implement the trait.</p> Err codemadness.org 70 i 3131 </li> Err codemadness.org 70 i 3132 <li> Err codemadness.org 70 i 3133 <p>Replace most of the macro with a helper trait <code>LengthTrait</code> that has Err codemadness.org 70 i 3134 an <code>Orientation</code> associated type.</p> Err codemadness.org 70 i 3135 </li> Err codemadness.org 70 i 3136 <li> Err codemadness.org 70 i 3137 <p>Replace the helper trait with a single <code>Length&lt;T: Orientation&gt;</code> Err codemadness.org 70 i 3138 type, which puts the orientation as a generic parameter. The macro Err codemadness.org 70 i 3139 disappears and there is a single implementation for everything.</p> Err codemadness.org 70 i 3140 </li> Err codemadness.org 70 i 3141 </ul> Err codemadness.org 70 i 3142 <p>Refactoring never ends!</p>CSS in librsvg is now in Rust, courtesy of Mozilla Servo2019-11-11T19:36:04-06:002019-11-11T19:36:04-06:00Federico Mena Quinterotag:people.gnome.org,2019-11-11:/~federico/blog/css-in-librsvg-is-now-in-rust.html<p>Summary: after an <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/237">epic amount of Err codemadness.org 70 i 3143 refactoring</a>, Err codemadness.org 70 i 3144 librsvg now does all CSS parsing and matching in Rust, <strong>without using Err codemadness.org 70 i 3145 libcroco</strong>. In addition, the CSS engine comes from Mozilla Servo, so Err codemadness.org 70 i 3146 it should be able to handle much more complex CSS than librsvg ever Err codemadness.org 70 i 3147 could before.</p> Err codemadness.org 70 i 3148 <p>This is the story of …</p><p>Summary: after an <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/237">epic amount of Err codemadness.org 70 i 3149 refactoring</a>, Err codemadness.org 70 i 3150 librsvg now does all CSS parsing and matching in Rust, <strong>without using Err codemadness.org 70 i 3151 libcroco</strong>. In addition, the CSS engine comes from Mozilla Servo, so Err codemadness.org 70 i 3152 it should be able to handle much more complex CSS than librsvg ever Err codemadness.org 70 i 3153 could before.</p> Err codemadness.org 70 i 3154 <p>This is the story of CSS support in librsvg.</p> Err codemadness.org 70 i 3155 <h2>Introduction</h2> Err codemadness.org 70 i 3156 <p>The <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/c5cbb842b25980321a09c427531279d56097b646">first commit to introduce CSS Err codemadness.org 70 i 3157 parsing</a> Err codemadness.org 70 i 3158 in librsvg dates from 2002. It was as minimal as possible, written to Err codemadness.org 70 i 3159 support a small subset of what was then Err codemadness.org 70 i 3160 <a href="https://www.w3.org/TR/1998/REC-CSS2-19980512/">CSS2</a>.</p> Err codemadness.org 70 i 3161 <p>Librsvg handled CSS stylesheets more "piecing them apart" than Err codemadness.org 70 i 3162 "parsing them". You know, when <code>g_strsplit()</code> is your best friend. Err codemadness.org 70 i 3163 The basic parsing algorithm was to turn a stylesheet like this:</p> Err codemadness.org 70 i 3164 <div class="highlight"><pre><span></span><code><span class="nt">rect</span> <span class="p">{</span> <span class="n">fill</span><span class="p">:</span> <span class="kc">blue</span><span class="p">;</span> <span class="p">}</span> Err codemadness.org 70 i 3165 Err codemadness.org 70 i 3166 <span class="p">.</span><span class="nc">classname</span> <span class="p">{</span> Err codemadness.org 70 i 3167 <span class="n">fill</span><span class="p">:</span> <span class="kc">green</span><span class="p">;</span> Err codemadness.org 70 i 3168 <span class="n">stroke-width</span><span class="p">:</span> <span class="mi">4</span><span class="p">;</span> Err codemadness.org 70 i 3169 <span class="p">}</span> Err codemadness.org 70 i 3170 </code></pre></div> Err codemadness.org 70 i 3171 Err codemadness.org 70 i 3172 <p>Into a hash table whose keys are strings like <code>rect</code> and <code>.classname</code>, Err codemadness.org 70 i 3173 and whose values are everything inside curly braces.</p> Err codemadness.org 70 i 3174 <p>The selector matching phase was equally simple. The code only handled Err codemadness.org 70 i 3175 a few possible match types as follows. If it wanted to match a Err codemadness.org 70 i 3176 certain kind of CSS selector, it would say, "what would this selector Err codemadness.org 70 i 3177 look like in CSS syntax", it would make up a string with that syntax, Err codemadness.org 70 i 3178 and compare it to the key strings it had stored in the hash table from Err codemadness.org 70 i 3179 above.</p> Err codemadness.org 70 i 3180 <p>So, to match an <strong>element name selector</strong>, it would <code>sprintf("%s", Err codemadness.org 70 i 3181 element-&gt;name)</code>, obtain something like <code>rect</code> and see if the hash Err codemadness.org 70 i 3182 table had such a key.</p> Err codemadness.org 70 i 3183 <p>To match a <strong>class selector</strong>, it would <code>sprintf(".%s", Err codemadness.org 70 i 3184 element-&gt;class)</code>, obtain something like <code>.classname</code>, and look it up Err codemadness.org 70 i 3185 in the hash table.</p> Err codemadness.org 70 i 3186 <p>This scheme supported only a few combinations. It handled <code>tag</code>, Err codemadness.org 70 i 3187 <code>.class</code>, <code>tag.class</code>, and a few combinations with <code>#id</code> in them. Err codemadness.org 70 i 3188 This was enough to support very simple stylesheets.</p> Err codemadness.org 70 i 3189 <p>The value corresponding to each key in the hash table was the stuff Err codemadness.org 70 i 3190 between curly braces in the stylesheet, so the second rule from the Err codemadness.org 70 i 3191 example above would contain <code>fill: green; stroke-width: 4;</code>. Once Err codemadness.org 70 i 3192 librsvg decided that an SVG element matched that CSS rule, it would Err codemadness.org 70 i 3193 re-parse the string with the CSS properties and apply them to the Err codemadness.org 70 i 3194 element's style.</p> Err codemadness.org 70 i 3195 <p>I'm amazed that so little code was enough to deal with a good number Err codemadness.org 70 i 3196 of SVG files with stylesheets. I suspect that this was due to a few Err codemadness.org 70 i 3197 things:</p> Err codemadness.org 70 i 3198 <ul> Err codemadness.org 70 i 3199 <li> Err codemadness.org 70 i 3200 <p>While people were using complex CSS in HTML all the time, it was Err codemadness.org 70 i 3201 less common for SVG...</p> Err codemadness.org 70 i 3202 </li> Err codemadness.org 70 i 3203 <li> Err codemadness.org 70 i 3204 <p>... because CSS2 was somewhat new, and the SVG spec was still being Err codemadness.org 70 i 3205 written...</p> Err codemadness.org 70 i 3206 </li> Err codemadness.org 70 i 3207 <li> Err codemadness.org 70 i 3208 <p>... and SVGs created with illustration programs don't really use Err codemadness.org 70 i 3209 stylesheets; they include the full style information inside each Err codemadness.org 70 i 3210 element instead of symbolically referencing it from a stylesheet.</p> Err codemadness.org 70 i 3211 </li> Err codemadness.org 70 i 3212 </ul> Err codemadness.org 70 i 3213 <p>From the kinds of bugs that librsvg has gotten around "CSS support is Err codemadness.org 70 i 3214 too limited", it feels like SVGs which use CSS features are either Err codemadness.org 70 i 3215 hand-written, or machine-generated from custom programs like data Err codemadness.org 70 i 3216 plotting software. Illustration programs tend to list all style Err codemadness.org 70 i 3217 properties explicitly in each SVG element, and don't use CSS.</p> Err codemadness.org 70 i 3218 <h2>Libcroco appears</h2> Err codemadness.org 70 i 3219 <p>The first commit to <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/99de937717129fdf8539904618b918f6119f43a1">introduce Err codemadness.org 70 i 3220 libcroco</a> Err codemadness.org 70 i 3221 was to do CSS parsing, from March 2003.</p> Err codemadness.org 70 i 3222 <p>At the same time, libcroco was introducing code to do CSS matching. Err codemadness.org 70 i 3223 However, this code never got used in librsvg; it still kept its simple Err codemadness.org 70 i 3224 string-based matcher. Maybe libcroco's API was not ready?</p> Err codemadness.org 70 i 3225 <p>Libcroco fell out of maintainership around the first half of 2005, and Err codemadness.org 70 i 3226 volunteers have kept fixing it since then.</p> Err codemadness.org 70 i 3227 <h2>Problems with librsvg's string matcher for CSS</h2> Err codemadness.org 70 i 3228 <p>The C implementation of CSS matching in librsvg remained basically Err codemadness.org 70 i 3229 untouched until 2018, when Paolo Borelli and I started porting the Err codemadness.org 70 i 3230 surrounding code to Rust.</p> Err codemadness.org 70 i 3231 <p>I had a lot of trouble figuring out the concepts from the code. I Err codemadness.org 70 i 3232 didn't know all the <a href="https://gnome.pages.gitlab.gnome.org/librsvg/doc/rsvg_internals/css/index.html#terminology">terminology of CSS Err codemadness.org 70 i 3233 implementations</a>, Err codemadness.org 70 i 3234 and librsvg didn't use it, either.</p> Err codemadness.org 70 i 3235 <p>I think that librsvg's code suffered from what the refactoring Err codemadness.org 70 i 3236 literature calls <a href="https://refactoring.guru/smells/primitive-obsession"><strong>primitive Err codemadness.org 70 i 3237 obsession</strong></a>. Err codemadness.org 70 i 3238 Instead of having a parsed representation of CSS selectors, librsvg Err codemadness.org 70 i 3239 just stored a stringified version of them. So, a selector like Err codemadness.org 70 i 3240 <code>rect#classname</code> really was stored with a string like that, instead of Err codemadness.org 70 i 3241 an actual decomposition into structs.</p> Err codemadness.org 70 i 3242 <p>Moreover, things were misnamed. This is the field that stored Err codemadness.org 70 i 3243 stylesheet data inside an RsvgHandle:</p> Err codemadness.org 70 i 3244 <div class="highlight"><pre><span></span><code> <span class="n">GHashTable</span> <span class="o">*</span><span class="n">css_props</span><span class="p">;</span> Err codemadness.org 70 i 3245 </code></pre></div> Err codemadness.org 70 i 3246 Err codemadness.org 70 i 3247 <p>From just looking at the field declaration, this doesn't tell me Err codemadness.org 70 i 3248 anything about what kind of data is stored there. One has to grep the Err codemadness.org 70 i 3249 source code for where that field is used:</p> Err codemadness.org 70 i 3250 <div class="highlight"><pre><span></span><code><span class="k">static</span> <span class="kt">void</span> Err codemadness.org 70 i 3251 <span class="nf">rsvg_css_define_style</span> <span class="p">(</span><span class="n">RsvgHandle</span> <span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> Err codemadness.org 70 i 3252 <span class="k">const</span> <span class="n">gchar</span> <span class="o">*</span> <span class="n">selector</span><span class="p">,</span> Err codemadness.org 70 i 3253 <span class="k">const</span> <span class="n">gchar</span> <span class="o">*</span> <span class="n">style_name</span><span class="p">,</span> Err codemadness.org 70 i 3254 <span class="k">const</span> <span class="n">gchar</span> <span class="o">*</span> <span class="n">style_value</span><span class="p">,</span> Err codemadness.org 70 i 3255 <span class="n">gboolean</span> <span class="n">important</span><span class="p">)</span> Err codemadness.org 70 i 3256 <span class="p">{</span> Err codemadness.org 70 i 3257 <span class="n">GHashTable</span> <span class="o">*</span><span class="n">styles</span><span class="p">;</span> Err codemadness.org 70 i 3258 Err codemadness.org 70 i 3259 <span class="n">styles</span> <span class="o">=</span> <span class="n">g_hash_table_lookup</span> <span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">priv</span><span class="o">-&gt;</span><span class="n">css_props</span><span class="p">,</span> <span class="n">selector</span><span class="p">);</span> Err codemadness.org 70 i 3260 </code></pre></div> Err codemadness.org 70 i 3261 Err codemadness.org 70 i 3262 <p>Okay, it looks up a <code>selector</code> by name in the <code>css_props</code>, and it Err codemadness.org 70 i 3263 gives back... another hash table <code>styles</code>? What's in there?</p> Err codemadness.org 70 i 3264 <div class="highlight"><pre><span></span><code> <span class="n">g_hash_table_insert</span> <span class="p">(</span><span class="n">styles</span><span class="p">,</span> Err codemadness.org 70 i 3265 <span class="n">g_strdup</span> <span class="p">(</span><span class="n">style_name</span><span class="p">),</span> Err codemadness.org 70 i 3266 <span class="n">style_value_data_new</span> <span class="p">(</span><span class="n">style_value</span><span class="p">,</span> <span class="n">important</span><span class="p">));</span> Err codemadness.org 70 i 3267 </code></pre></div> Err codemadness.org 70 i 3268 Err codemadness.org 70 i 3269 <p>Another string key called <code>style_name</code>, whose key is a Err codemadness.org 70 i 3270 <code>StyleValueData</code>; what's in it?</p> Err codemadness.org 70 i 3271 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="nc">_StyleValueData</span> <span class="p">{</span> Err codemadness.org 70 i 3272 <span class="n">gchar</span> <span class="o">*</span><span class="n">value</span><span class="p">;</span> Err codemadness.org 70 i 3273 <span class="n">gboolean</span> <span class="n">important</span><span class="p">;</span> Err codemadness.org 70 i 3274 <span class="p">}</span> <span class="n">StyleValueData</span><span class="p">;</span> Err codemadness.org 70 i 3275 </code></pre></div> Err codemadness.org 70 i 3276 Err codemadness.org 70 i 3277 <p>The <code>value</code> is another string. Strings all the way!</p> Err codemadness.org 70 i 3278 <p>At the time, I didn't really figure out what each level of nested hash Err codemadness.org 70 i 3279 tables was supposed to mean. I didn't understand why we handled style Err codemadness.org 70 i 3280 properties in a completely different part of the code, and yet this Err codemadness.org 70 i 3281 part had a <code>css_props</code> field that didn't seem to store properties at all.</p> Err codemadness.org 70 i 3282 <p>It took a while to realize that <code>css_props</code> was misnamed. It wasn't Err codemadness.org 70 i 3283 storing a mapping of selector names to properties; it was storing a Err codemadness.org 70 i 3284 mapping of selector names to <strong>declaration lists</strong>, which are lists of Err codemadness.org 70 i 3285 property/value pairs.</p> Err codemadness.org 70 i 3286 <p>So, when I started <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/08f28816868878962647651118d5a1eb95d06d17">porting the CSS parsing code to Err codemadness.org 70 i 3287 Rust</a>, Err codemadness.org 70 i 3288 I started to create real types with for each concept.</p> Err codemadness.org 70 i 3289 <div class="highlight"><pre><span></span><code><span class="c1">// Maps property_name -&gt; Declaration</span> Err codemadness.org 70 i 3290 <span class="k">type</span> <span class="nc">DeclarationList</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">Declaration</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3291 Err codemadness.org 70 i 3292 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">CssStyles</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3293 <span class="w"> </span><span class="n">selectors_to_declarations</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">DeclarationList</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3294 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3295 </code></pre></div> Err codemadness.org 70 i 3296 Err codemadness.org 70 i 3297 <p>Even though the keys of those HashMaps are still strings, because Err codemadness.org 70 i 3298 librsvg didn't have a better way to represent their corresponding Err codemadness.org 70 i 3299 concepts, at least those declarations let one see what the hell is Err codemadness.org 70 i 3300 being stored without grepping the rest of the code. This is a part of Err codemadness.org 70 i 3301 the code that I didn't really touch very much, so it was nice to have Err codemadness.org 70 i 3302 that reminder.</p> Err codemadness.org 70 i 3303 <p>The <a href="https://gitlab.gnome.org/GNOME/librsvg/commit/12dd9b1bb039484ce273228049f96f8e39d2f339">first port of the CSS matching code to Err codemadness.org 70 i 3304 Rust</a> Err codemadness.org 70 i 3305 kept the same algorithm as the C code, the one that created strings Err codemadness.org 70 i 3306 with <code>element.class</code> and compared them to the stored selector names. Err codemadness.org 70 i 3307 Ugly, but it still worked in the same limited fashion.</p> Err codemadness.org 70 i 3308 <h2>Rustifying the CSS parsers</h2> Err codemadness.org 70 i 3309 <p>It turns out that CSS parsing is divided in two parts. One can have a Err codemadness.org 70 i 3310 <code>style</code> attribute inside an element, for example</p> Err codemadness.org 70 i 3311 <div class="highlight"><pre><span></span><code><span class="nt">&lt;rect</span> <span class="na">x=</span><span class="s">&quot;0&quot;</span> <span class="na">y=</span><span class="s">&quot;0&quot;</span> <span class="na">width=</span><span class="s">&quot;100&quot;</span> <span class="na">height=</span><span class="s">&quot;100&quot;</span> Err codemadness.org 70 i 3312 <span class="na">style=</span><span class="s">&quot;fill: green; stroke: magenta; stroke-width: 4;&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 3313 </code></pre></div> Err codemadness.org 70 i 3314 Err codemadness.org 70 i 3315 <p>This is a plain declaration list which is not associated to any Err codemadness.org 70 i 3316 selectors, and which is applied directly to just the element in which it Err codemadness.org 70 i 3317 appears.</p> Err codemadness.org 70 i 3318 <p>Then, there is the <code>&lt;style&gt;</code> element itself, with a normal-looking CSS stylesheet</p> Err codemadness.org 70 i 3319 <div class="highlight"><pre><span></span><code><span class="nt">&lt;style</span> <span class="na">type=</span><span class="s">&quot;text/css&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 3320 rect { Err codemadness.org 70 i 3321 fill: green; Err codemadness.org 70 i 3322 stroke: magenta; Err codemadness.org 70 i 3323 stroke-width: 4; Err codemadness.org 70 i 3324 } Err codemadness.org 70 i 3325 <span class="nt">&lt;/style&gt;</span> Err codemadness.org 70 i 3326 </code></pre></div> Err codemadness.org 70 i 3327 Err codemadness.org 70 i 3328 <p>This means that all <code>&lt;rect&gt;</code> elements will get that style applied.</p> Err codemadness.org 70 i 3329 <p>I started to look for existing Rust crates to parse and handle CSS Err codemadness.org 70 i 3330 data. The <a href="https://docs.rs/cssparser/">cssparser</a> and Err codemadness.org 70 i 3331 <a href="https://docs.rs/selectors/">selectors</a> crates come from Mozilla, so Err codemadness.org 70 i 3332 I thought they should do a pretty good job of things.</p> Err codemadness.org 70 i 3333 <p>And they do! Except that they are not a drop-in replacement for Err codemadness.org 70 i 3334 anything. They are what gets used in Mozilla's Servo browser engine, Err codemadness.org 70 i 3335 so they are optimized to hell, and the code can be pretty intimidating.</p> Err codemadness.org 70 i 3336 <p>Out of the box, cssparser provides a CSS tokenizer, but it does Err codemadness.org 70 i 3337 not know how to handle any properties/values in particular. One must Err codemadness.org 70 i 3338 use the tokenizer to implement a parser for each kind of CSS property Err codemadness.org 70 i 3339 one wants to support — Servo has mountains of code for all of HTML's Err codemadness.org 70 i 3340 style properties, and librsvg had to provide a smaller mountain of code Err codemadness.org 70 i 3341 for SVG style properties.</p> Err codemadness.org 70 i 3342 <p>Thus started the big task of porting librsvg's string-based parsers Err codemadness.org 70 i 3343 for CSS properties into ones based on cssparser tokens. Cssparser Err codemadness.org 70 i 3344 provides a <code>Parser</code> struct, which extracts tokens out of a CSS Err codemadness.org 70 i 3345 stream. Out of this, librsvg defines a <code>Parse</code> trait for parsable Err codemadness.org 70 i 3346 things:</p> Err codemadness.org 70 i 3347 <div class="highlight"><pre><span></span><code><span class="k">use</span><span class="w"> </span><span class="n">cssparser</span>::<span class="n">Parser</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3348 Err codemadness.org 70 i 3349 <span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">Parse</span>: <span class="nb">Sized</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3350 <span class="w"> </span><span class="k">type</span> <span class="nb">Err</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3351 Err codemadness.org 70 i 3352 <span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="p">(</span><span class="n">parser</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">Parser</span><span class="o">&lt;&#39;</span><span class="nb">_</span><span class="p">,</span><span class="w"> </span><span class="o">&#39;</span><span class="nb">_</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="bp">Self</span>::<span class="nb">Err</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3353 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3354 </code></pre></div> Err codemadness.org 70 i 3355 Err codemadness.org 70 i 3356 <p>What's with those two default lifetimes in <code>Parser&lt;'_, '_&gt;</code>? Err codemadness.org 70 i 3357 Cssparser tries very hard to be a zero-copy tokenizer. One of the Err codemadness.org 70 i 3358 lifetimes refers to the input string which is wrapped in a Err codemadness.org 70 i 3359 <code>Tokenizer</code>, which is wrapped in a <code>ParserInput</code>. The other lifetime Err codemadness.org 70 i 3360 is for the <code>ParserInput</code> itself.</p> Err codemadness.org 70 i 3361 <p>In the actual implementation of that trait, the <code>Err</code> type also uses Err codemadness.org 70 i 3362 the lifetime that refers to the input string. For example, there is a Err codemadness.org 70 i 3363 <code>BasicParseErrorKind::UnexpectedToken(Token&lt;'i&gt;)</code>, which one returns Err codemadness.org 70 i 3364 when there is an unexpected token. And to avoid copying the substring Err codemadness.org 70 i 3365 into the error, one returns a slice reference into the original Err codemadness.org 70 i 3366 string, thus the lifetime.</p> Err codemadness.org 70 i 3367 <p>I was more of a Rust newbie back then, and it was very hard to make Err codemadness.org 70 i 3368 sense of how cssparser was meant to be used.</p> Err codemadness.org 70 i 3369 <p>The process was more or less this:</p> Err codemadness.org 70 i 3370 <ul> Err codemadness.org 70 i 3371 <li> Err codemadness.org 70 i 3372 <p>Port the C parsers to Rust; implement types for each CSS property.</p> Err codemadness.org 70 i 3373 </li> Err codemadness.org 70 i 3374 <li> Err codemadness.org 70 i 3375 <p>Port the <code>&amp;str</code>-based parsers into ones that use <code>cssparser</code>.</p> Err codemadness.org 70 i 3376 </li> Err codemadness.org 70 i 3377 <li> Err codemadness.org 70 i 3378 <p>Fix the error handling scheme to match what cssparser's high-level Err codemadness.org 70 i 3379 traits expect.</p> Err codemadness.org 70 i 3380 </li> Err codemadness.org 70 i 3381 </ul> Err codemadness.org 70 i 3382 <p>This last point was... hard. Again, I wasn't comfortable enough with Err codemadness.org 70 i 3383 Rust lifetimes and nested generics; in the end it was all right.</p> Err codemadness.org 70 i 3384 <h2>Moving declaration lists to Rust</h2> Err codemadness.org 70 i 3385 <p>With the individual parsers for CSS properties done, and with them Err codemadness.org 70 i 3386 already using a different type for each property, the next thing was Err codemadness.org 70 i 3387 to implement cssparser's traits to parse declaration lists.</p> Err codemadness.org 70 i 3388 <p>Again, a declaration list looks like this:</p> Err codemadness.org 70 i 3389 <div class="highlight"><pre><span></span><code><span class="nt">fill</span><span class="o">:</span> <span class="nt">blue</span><span class="o">;</span> Err codemadness.org 70 i 3390 <span class="nt">stroke-width</span><span class="o">:</span> <span class="nt">4</span><span class="o">;</span> Err codemadness.org 70 i 3391 </code></pre></div> Err codemadness.org 70 i 3392 Err codemadness.org 70 i 3393 <p>It's essentially a key/value list.</p> Err codemadness.org 70 i 3394 <p>The trait that cssparser wants us to implement is this:</p> Err codemadness.org 70 i 3395 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">DeclarationParser</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3396 <span class="w"> </span><span class="k">type</span> <span class="nc">Declaration</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3397 <span class="w"> </span><span class="k">type</span> <span class="nc">Error</span>: <span class="o">&#39;</span><span class="na">i</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3398 Err codemadness.org 70 i 3399 <span class="w"> </span><span class="k">fn</span> <span class="nf">parse_value</span><span class="o">&lt;&#39;</span><span class="na">t</span><span class="o">&gt;</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 3400 <span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3401 <span class="w"> </span><span class="n">name</span>: <span class="nc">CowRcStr</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3402 <span class="w"> </span><span class="n">input</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">Parser</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="p">,</span><span class="w"> </span><span class="o">&#39;</span><span class="na">t</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3403 <span class="w"> </span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="bp">Self</span>::<span class="n">Declaration</span><span class="p">,</span><span class="w"> </span><span class="n">ParseError</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="p">,</span><span class="w"> </span><span class="bp">Self</span>::<span class="n">Error</span><span class="o">&gt;&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3404 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3405 </code></pre></div> Err codemadness.org 70 i 3406 Err codemadness.org 70 i 3407 <p>That is, define a type for a <code>Declaration</code>, and implement a Err codemadness.org 70 i 3408 <code>parse_value()</code> method that takes a <code>name</code> and a <code>Parser</code>, and outputs Err codemadness.org 70 i 3409 a <code>Declaration</code> or an error.</p> Err codemadness.org 70 i 3410 <p>What this <em>really</em> means is that the type you implement for Err codemadness.org 70 i 3411 <code>Declaration</code> needs to be able to represent all the CSS property types Err codemadness.org 70 i 3412 that you care about. Thus, a struct plus a big enum like this:</p> Err codemadness.org 70 i 3413 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Declaration</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3414 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">prop_name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3415 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">property</span>: <span class="nc">ParsedProperty</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3416 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">important</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3417 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3418 Err codemadness.org 70 i 3419 <span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">ParsedProperty</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3420 <span class="w"> </span><span class="n">BaselineShift</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">BaselineShift</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 3421 <span class="w"> </span><span class="n">ClipPath</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">ClipPath</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 3422 <span class="w"> </span><span class="n">ClipRule</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">ClipRule</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 3423 <span class="w"> </span><span class="n">Color</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">Color</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 3424 <span class="w"> </span><span class="n">ColorInterpolationFilters</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">ColorInterpolationFilters</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 3425 <span class="w"> </span><span class="n">Direction</span><span class="p">(</span><span class="n">SpecifiedValue</span><span class="o">&lt;</span><span class="n">Direction</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 3426 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 3427 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3428 </code></pre></div> Err codemadness.org 70 i 3429 Err codemadness.org 70 i 3430 <p>This gives us declaration lists (the stuff inside curly braces in a Err codemadness.org 70 i 3431 CSS stylesheet), but it doesn't give us qualified rules, which are Err codemadness.org 70 i 3432 composed of selector names plus a declaration list.</p> Err codemadness.org 70 i 3433 <h2>Refactoring towards real CSS concepts</h2> Err codemadness.org 70 i 3434 <p>Paolo Borelli has been steadily refactoring librsvg and fixing things Err codemadness.org 70 i 3435 like the primitive obsession I mentioned above. We now have real Err codemadness.org 70 i 3436 concepts like a Document, Stylesheet, QualifiedRule, Rule, AtRule.</p> Err codemadness.org 70 i 3437 <p>This refactoring took a long time, because it involved redoing the XML Err codemadness.org 70 i 3438 loading code and its interaction with the CSS parser a few times.</p> Err codemadness.org 70 i 3439 <h2>Implementing traits from the selectors crate</h2> Err codemadness.org 70 i 3440 <p>The <a href="https://docs.rs/selectors">selectors</a> crate Err codemadness.org 70 i 3441 contains Servo's code for parsing CSS selectors and doing matching. Err codemadness.org 70 i 3442 However, it is <em>extremely</em> generic. Using it involves implementing a Err codemadness.org 70 i 3443 good number of concepts.</p> Err codemadness.org 70 i 3444 <p>For example, this <code>SelectorImpl</code> trait has no methods, and is just a Err codemadness.org 70 i 3445 collection of types that refer to your implementation of an element Err codemadness.org 70 i 3446 tree. How do you represent an attribute/value? How do you represent Err codemadness.org 70 i 3447 an identifier? How do you represent a namespace and a local name?</p> Err codemadness.org 70 i 3448 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">SelectorImpl</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3449 <span class="w"> </span><span class="k">type</span> <span class="nc">ExtraMatchingData</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3450 <span class="w"> </span><span class="k">type</span> <span class="nc">AttrValue</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3451 <span class="w"> </span><span class="k">type</span> <span class="nc">Identifier</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3452 <span class="w"> </span><span class="k">type</span> <span class="nc">ClassName</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3453 <span class="w"> </span><span class="k">type</span> <span class="nc">PartName</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3454 <span class="w"> </span><span class="k">type</span> <span class="nc">LocalName</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3455 <span class="w"> </span><span class="k">type</span> <span class="nc">NamespaceUrl</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3456 <span class="w"> </span><span class="k">type</span> <span class="nc">NamespacePrefix</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3457 <span class="w"> </span><span class="k">type</span> <span class="nc">BorrowedNamespaceUrl</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3458 <span class="w"> </span><span class="k">type</span> <span class="nc">BorrowedLocalName</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3459 <span class="w"> </span><span class="k">type</span> <span class="nc">NonTSPseudoClass</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3460 <span class="w"> </span><span class="k">type</span> <span class="nc">PseudoElement</span>: <span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 3461 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3462 </code></pre></div> Err codemadness.org 70 i 3463 Err codemadness.org 70 i 3464 <p>A lot of those can be <code>String</code>, but Servo has smarter things in store. Err codemadness.org 70 i 3465 I ended up using the <a href="https://docs.rs/markup5ever"><code>markup5ever</code></a> crate, which provides a string Err codemadness.org 70 i 3466 interning framework for markup and XML concepts like a <code>LocalName</code>, a Err codemadness.org 70 i 3467 <code>Namespace</code>, etc. This reduces memory consumption, because instead of Err codemadness.org 70 i 3468 storing string copies of element names everywhere, one just stores Err codemadness.org 70 i 3469 tokens for interned strings.</p> Err codemadness.org 70 i 3470 <p>(In the meantime I had to implement support for XML namespaces, which Err codemadness.org 70 i 3471 the selectors code really wants, but which librsvg never supported.)</p> Err codemadness.org 70 i 3472 <p>Then, the selectors crate wants you to say how your code implements an Err codemadness.org 70 i 3473 element tree. It has a monster trait <code>Element</code>:</p> Err codemadness.org 70 i 3474 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">Element</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 3475 <span class="w"> </span><span class="k">type</span> <span class="nc">Impl</span>: <span class="nc">SelectorImpl</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3476 Err codemadness.org 70 i 3477 <span class="w"> </span><span class="k">fn</span> <span class="nf">opaque</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">OpaqueElement</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3478 Err codemadness.org 70 i 3479 <span class="w"> </span><span class="k">fn</span> <span class="nf">parent_element</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3480 Err codemadness.org 70 i 3481 <span class="w"> </span><span class="k">fn</span> <span class="nf">parent_node_is_shadow_root</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3482 Err codemadness.org 70 i 3483 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 3484 Err codemadness.org 70 i 3485 <span class="w"> </span><span class="k">fn</span> <span class="nf">prev_sibling_element</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3486 <span class="w"> </span><span class="k">fn</span> <span class="nf">next_sibling_element</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3487 Err codemadness.org 70 i 3488 <span class="w"> </span><span class="k">fn</span> <span class="nf">has_local_name</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 3489 <span class="w"> </span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span> Err codemadness.org 70 i 3490 <span class="w"> </span><span class="n">local_name</span>: <span class="kp">&amp;</span><span class="o">&lt;</span><span class="bp">Self</span>::<span class="n">Impl</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">SelectorImpl</span><span class="o">&gt;</span>::<span class="n">BorrowedLocalName</span><span class="w"></span> Err codemadness.org 70 i 3491 <span class="w"> </span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3492 Err codemadness.org 70 i 3493 <span class="w"> </span><span class="k">fn</span> <span class="nf">has_id</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 3494 <span class="w"> </span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3495 <span class="w"> </span><span class="n">id</span>: <span class="kp">&amp;</span><span class="o">&lt;</span><span class="bp">Self</span>::<span class="n">Impl</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">SelectorImpl</span><span class="o">&gt;</span>::<span class="n">Identifier</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3496 <span class="w"> </span><span class="n">case_sensitivity</span>: <span class="nc">CaseSensitivity</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 3497 <span class="w"> </span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 3498 Err codemadness.org 70 i 3499 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 3500 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 3501 </code></pre></div> Err codemadness.org 70 i 3502 Err codemadness.org 70 i 3503 <p>That is, when you provide an implementation of <code>Element</code> and Err codemadness.org 70 i 3504 <code>SelectorImpl</code>, the selectors crate will know how to navigate your Err codemadness.org 70 i 3505 element tree and ask it questions like, "does this element have the id Err codemadness.org 70 i 3506 <code>#foo</code>?"; "does this element have the name <code>rect</code>?". It makes perfect Err codemadness.org 70 i 3507 sense in the end, but it is quite intimidating when you are not 100% Err codemadness.org 70 i 3508 comfortable with webs of traits and associated types and generics with Err codemadness.org 70 i 3509 a bunch of trait bounds!</p> Err codemadness.org 70 i 3510 <p>I tried implementing that trait twice in the last year, and failed. Err codemadness.org 70 i 3511 It turns out that its API <a href="https://github.com/servo/servo/issues/22972">needed a key Err codemadness.org 70 i 3512 fix</a> that landed last Err codemadness.org 70 i 3513 June, but I didn't notice until a couple of weeks ago.</p> Err codemadness.org 70 i 3514 <h2>So?</h2> Err codemadness.org 70 i 3515 <p>Two days ago, Paolo and I committed the <a href="https://gitlab.gnome.org/federico/librsvg/merge_requests/6">last code to be able to Err codemadness.org 70 i 3516 completely replace Err codemadness.org 70 i 3517 libcroco</a>.</p> Err codemadness.org 70 i 3518 <p>And, after implementing CSS <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/525">specificity</a> (which was easy now that we Err codemadness.org 70 i 3519 have real CSS concepts and a good pipeline for the CSS cascade), a Err codemadness.org 70 i 3520 bunch of very old bugs started falling down Err codemadness.org 70 i 3521 (<a href="https://gitlab.gnome.org/GNOME/librsvg/issues/336">1</a> Err codemadness.org 70 i 3522 <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/466">2</a> Err codemadness.org 70 i 3523 <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/428">3</a> Err codemadness.org 70 i 3524 <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/167">4</a> Err codemadness.org 70 i 3525 <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/79">5</a> Err codemadness.org 70 i 3526 <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/441">6</a>).</p> Err codemadness.org 70 i 3527 <p>Now it is going to be easy to implement things like <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/379">letting the Err codemadness.org 70 i 3528 application specify a user Err codemadness.org 70 i 3529 stylesheet</a>. In Err codemadness.org 70 i 3530 particular, this should let GTK remove the <a href="https://gitlab.gnome.org/GNOME/gtk/blob/ad48bbb8/gtk/gtkicontheme.c#L3728-3750">rather egregious Err codemadness.org 70 i 3531 hack</a> Err codemadness.org 70 i 3532 it has to recolor SVG icons while using librsvg indirectly.</p> Err codemadness.org 70 i 3533 <h2>Conclusion</h2> Err codemadness.org 70 i 3534 <p>This will appear in librsvg 2.47.1 — that version will no longer Err codemadness.org 70 i 3535 require libcroco.</p> Err codemadness.org 70 i 3536 <p>As far as I know, the only module that still depends on libcroco (in Err codemadness.org 70 i 3537 GNOME or otherwise) is <strong>gnome-shell</strong>. It uses libcroco to parse CSS Err codemadness.org 70 i 3538 and get the basic structure of selectors so it can implement matching Err codemadness.org 70 i 3539 by hand.</p> Err codemadness.org 70 i 3540 <p>Gnome-shell has some code which looks awfully similar to what librsvg Err codemadness.org 70 i 3541 had when it was written in C:</p> Err codemadness.org 70 i 3542 <ul> Err codemadness.org 70 i 3543 <li> Err codemadness.org 70 i 3544 <p><a href="https://gitlab.gnome.org/GNOME/gnome-shell/blob/66fc5c07/src/st/st-theme.c">StTheme</a> Err codemadness.org 70 i 3545 has the high-level CSS stylesheet parser and the selector matching code.</p> Err codemadness.org 70 i 3546 </li> Err codemadness.org 70 i 3547 <li> Err codemadness.org 70 i 3548 <p><a href="https://gitlab.gnome.org/GNOME/gnome-shell/blob/66fc5c07/src/st/st-theme-node.c">StThemeNode</a> Err codemadness.org 70 i 3549 has the low-level CSS property parsers.</p> Err codemadness.org 70 i 3550 </li> Err codemadness.org 70 i 3551 </ul> Err codemadness.org 70 i 3552 <p>... and it turns out that those files come all the way from Err codemadness.org 70 i 3553 <a href="https://blog.ometer.com/2006/10/14/text-layout-that-works-properly/">HippoCanvas</a>, Err codemadness.org 70 i 3554 the CSS-aware canvas that Mugshot used! Mugshot was a circa-2006 Err codemadness.org 70 i 3555 pre-Facebook aggregator for social media data like blogs, Flickr Err codemadness.org 70 i 3556 pictures, etc. HippoCanvas also got used in Sugar, the GUI for Err codemadness.org 70 i 3557 One Laptop Per Child. Yes, our code is <em>that</em> old.</p> Err codemadness.org 70 i 3558 <p>Libcroco is unmaintained, and has outstanding CVEs. I would be very Err codemadness.org 70 i 3559 happy to assist someone in porting gnome-shell's CSS code to Rust :)</p>Gdk-pixbuf modules - call for help2019-09-11T17:54:03-05:002019-09-12T13:15:17-05:00Federico Mena Quinterotag:people.gnome.org,2019-09-11:/~federico/blog/gdk-pixbuf-modules.html<p>I've been doing a little refactoring of gdk-pixbuf's crufty code, to Err codemadness.org 70 i 3560 see if the gripes from my <a href="https://people.gnome.org/~federico/blog/my-gdk-pixbuf-braindump.html">braindump</a> can be solved. For Err codemadness.org 70 i 3561 things where it is not obvious how to proceed, I've started taking Err codemadness.org 70 i 3562 more detailed notes in a <a href="https://gitlab.gnome.org/federico/gdk-pixbuf-survey">gdk-pixbuf survey</a>.</p> Err codemadness.org 70 i 3563 <p>Today I was looking at which <a href="https://gitlab.gnome.org/federico/gdk-pixbuf-survey/blob/master/src/modules.md">gdk-pixbuf modules</a> are …</p><p>I've been doing a little refactoring of gdk-pixbuf's crufty code, to Err codemadness.org 70 i 3564 see if the gripes from my <a href="https://people.gnome.org/~federico/blog/my-gdk-pixbuf-braindump.html">braindump</a> can be solved. For Err codemadness.org 70 i 3565 things where it is not obvious how to proceed, I've started taking Err codemadness.org 70 i 3566 more detailed notes in a <a href="https://gitlab.gnome.org/federico/gdk-pixbuf-survey">gdk-pixbuf survey</a>.</p> Err codemadness.org 70 i 3567 <p>Today I was looking at which <a href="https://gitlab.gnome.org/federico/gdk-pixbuf-survey/blob/master/src/modules.md">gdk-pixbuf modules</a> are Err codemadness.org 70 i 3568 implemented by third parties, that is, which external projects provide Err codemadness.org 70 i 3569 their own image codecs pluggable into gdk-pixbuf.</p> Err codemadness.org 70 i 3570 <p>And there are not that many!</p> Err codemadness.org 70 i 3571 <p>The only four that I found are <strong>libheif, libopenraw, libwmf, Err codemadness.org 70 i 3572 librsvg</strong> (this last one, of course).</p> Err codemadness.org 70 i 3573 <p><em>Update 2019/Sep/12</em> - Added <strong>apng, exif-raw, psd, pvr, vtf, webp, xcf</strong>.</p> Err codemadness.org 70 i 3574 <p>All of those use the gdk-pixbuf module API in a remarkably similar Err codemadness.org 70 i 3575 fashion. Did they cut&amp;paste each other's code? Did they do the Err codemadness.org 70 i 3576 simplest thing that didn't crash in gdk-pixbuf's checks for buggy Err codemadness.org 70 i 3577 loaders, which happens to be exactly what they do? Who knows! Either Err codemadness.org 70 i 3578 way, this makes future API changes in the modules a lot easier, since Err codemadness.org 70 i 3579 they all do the same right now.</p> Err codemadness.org 70 i 3580 <p>I'm trying to decide between these:</p> Err codemadness.org 70 i 3581 <ul> Err codemadness.org 70 i 3582 <li> Err codemadness.org 70 i 3583 <p>Keep modules as they are; find a way to sandbox them from gdk-pixbuf Err codemadness.org 70 i 3584 itself. This is hard because the API is "chatty"; modules and Err codemadness.org 70 i 3585 calling code go back and forth peeking at each other's structures.</p> Err codemadness.org 70 i 3586 </li> Err codemadness.org 70 i 3587 <li> Err codemadness.org 70 i 3588 <p>Decide that third-party modules are only useful for thumbnailers; Err codemadness.org 70 i 3589 modify them to <em>be</em> thumbnailers instead of generic gdk-pixbuf Err codemadness.org 70 i 3590 modules. This would mean that those formats would stop working Err codemadness.org 70 i 3591 automatically in gdk-pixbuf based viewers like EOG.</p> Err codemadness.org 70 i 3592 </li> Err codemadness.org 70 i 3593 <li> Err codemadness.org 70 i 3594 <p>Have "blessed" codecs inside gdk-pixbuf which are not modules so Err codemadness.org 70 i 3595 their no longer have API/ABI stability constraints. Keep Err codemadness.org 70 i 3596 third-party modules separate. Sandbox the internal ones with a Err codemadness.org 70 i 3597 non-chatty API.</p> Err codemadness.org 70 i 3598 </li> Err codemadness.org 70 i 3599 <li> Err codemadness.org 70 i 3600 <p>If all third-party modules work indeed as <a href="https://gitlab.gnome.org/federico/gdk-pixbuf-survey/blob/master/src/modules.md">I found</a>, the Err codemadness.org 70 i 3601 module API can be simplified quite a lot since no third-party Err codemadness.org 70 i 3602 modules implement animations or saving. If so, simplify the module Err codemadness.org 70 i 3603 API and the gdk-pixbuf internals rather drastically.</p> Err codemadness.org 70 i 3604 </li> Err codemadness.org 70 i 3605 </ul> Err codemadness.org 70 i 3606 <p>Do you know any other image formats which provide <a href="https://gitlab.gnome.org/federico/gdk-pixbuf-survey/blob/master/src/modules.md">gdk-pixbuf Err codemadness.org 70 i 3607 modules</a>? <a href="mailto:federico@gnome.org">Mail me, please!</a></p>On responsible vulnerability disclosure2019-08-10T21:05:34-05:002019-08-10T21:05:34-05:00Federico Mena Quinterotag:people.gnome.org,2019-08-10:/~federico/blog/on-responsible-vulnerability-disclosure.html<p>Recently KDE had an unfortunate event. Someone <a href="https://gist.github.com/zeropwn/630832df151029cb8f22d5b6b9efaefb">found a Err codemadness.org 70 i 3608 vulnerability</a> in the code that processes <code>.desktop</code> and Err codemadness.org 70 i 3609 <code>.directory</code> files, through which an attacker could create a malicious Err codemadness.org 70 i 3610 file that causes shell command execution (<a href="https://paper.seebug.org/1008/">analysis</a>). They went for immediate, Err codemadness.org 70 i 3611 full disclosure, where KDE didn't even get a chance of fixing the …</p><p>Recently KDE had an unfortunate event. Someone <a href="https://gist.github.com/zeropwn/630832df151029cb8f22d5b6b9efaefb">found a Err codemadness.org 70 i 3612 vulnerability</a> in the code that processes <code>.desktop</code> and Err codemadness.org 70 i 3613 <code>.directory</code> files, through which an attacker could create a malicious Err codemadness.org 70 i 3614 file that causes shell command execution (<a href="https://paper.seebug.org/1008/">analysis</a>). They went for immediate, Err codemadness.org 70 i 3615 full disclosure, where KDE didn't even get a chance of fixing the bug Err codemadness.org 70 i 3616 before it was published.</p> Err codemadness.org 70 i 3617 <p>There are many protocols for <a href="https://en.wikipedia.org/wiki/Responsible_disclosure">disclosing vulnerabilities in a Err codemadness.org 70 i 3618 coordinated, responsible fashion</a>, but the gist of them is this:</p> Err codemadness.org 70 i 3619 <ol> Err codemadness.org 70 i 3620 <li> Err codemadness.org 70 i 3621 <p>Someone finds a vulnerability in some software through studying Err codemadness.org 70 i 3622 some code, or some other mechanism.</p> Err codemadness.org 70 i 3623 </li> Err codemadness.org 70 i 3624 <li> Err codemadness.org 70 i 3625 <p>They report the vulnerability to the software's author through some Err codemadness.org 70 i 3626 private channel. For free softare in particular, researchers can Err codemadness.org 70 i 3627 use <a href="https://oss-security.openwall.org/wiki/disclosure/researcher">Openwall's recommended process for Err codemadness.org 70 i 3628 researchers</a>, which includes <strong>notifying the Err codemadness.org 70 i 3629 author/maintainer and distros and security groups.</strong> Free Err codemadness.org 70 i 3630 software projects can <a href="https://alexgaynor.net/2013/oct/19/security-process-open-source-projects/">follow a well-established process</a>.</p> Err codemadness.org 70 i 3631 </li> Err codemadness.org 70 i 3632 <li> Err codemadness.org 70 i 3633 <p>The author and reporter agree on a deadline for releasing a public Err codemadness.org 70 i 3634 report of the vulnerability, or in semi-automated systems like Err codemadness.org 70 i 3635 Google Zero, a deadline is automatically established.</p> Err codemadness.org 70 i 3636 </li> Err codemadness.org 70 i 3637 <li> Err codemadness.org 70 i 3638 <p>The author works on fixing the vulnerability.</p> Err codemadness.org 70 i 3639 </li> Err codemadness.org 70 i 3640 <li> Err codemadness.org 70 i 3641 <p>The deadline is reached; the patch has been publically released, Err codemadness.org 70 i 3642 the appropriate people have been notified, systems have been Err codemadness.org 70 i 3643 patched. If there is no patch, the author and reporter can agree Err codemadness.org 70 i 3644 on postponing the date, or the reporter can publish the Err codemadness.org 70 i 3645 vulnerability report, thus creating public pressure for a fix.</p> Err codemadness.org 70 i 3646 </li> Err codemadness.org 70 i 3647 </ol> Err codemadness.org 70 i 3648 <p>The steps above gloss over many practicalities and issues from the Err codemadness.org 70 i 3649 real world, but the idea is basically this: the author or maintainer Err codemadness.org 70 i 3650 of the software is given a chance to fix a security bug before Err codemadness.org 70 i 3651 information on the vulnerability is released to the hostile world. Err codemadness.org 70 i 3652 The idea is to <strong>keep harm from being done</strong> by not publishing Err codemadness.org 70 i 3653 unpatched vulnerabilities until there is a fix for them (... or until Err codemadness.org 70 i 3654 the deadline expires).</p> Err codemadness.org 70 i 3655 <h2>What happened instead</h2> Err codemadness.org 70 i 3656 <p>Around the beginning of July, the reporter <a href="https://twitter.com/zer0pwn/status/1146554083056193536">posts about looking for Err codemadness.org 70 i 3657 bugs in KDE</a>.</p> Err codemadness.org 70 i 3658 <p>On <a href="https://twitter.com/zer0pwn/status/1156312405472923648">July 30</a>, he posts a video with the proof of concept.</p> Err codemadness.org 70 i 3659 <p>On August 3, he <a href="https://twitter.com/zer0pwn/status/1157760759289528321">makes a Twitter poll</a> about what to do with the Err codemadness.org 70 i 3660 vulnerability.</p> Err codemadness.org 70 i 3661 <p>On August 4, he <a href="https://twitter.com/zer0pwn/status/1158167374799020039">publishes the vulnerability</a>.</p> Err codemadness.org 70 i 3662 <p>KDE is left with having to patch this in emergency mode. On August 7, Err codemadness.org 70 i 3663 KDE releases a <a href="https://kde.org/info/security/advisory-20190807-1.txt">security advisory</a> in perfect form:</p> Err codemadness.org 70 i 3664 <ul> Err codemadness.org 70 i 3665 <li> Err codemadness.org 70 i 3666 <p>Description of exactly what causes the vulnerability.</p> Err codemadness.org 70 i 3667 </li> Err codemadness.org 70 i 3668 <li> Err codemadness.org 70 i 3669 <p>Description of how it was solved.</p> Err codemadness.org 70 i 3670 </li> Err codemadness.org 70 i 3671 <li> Err codemadness.org 70 i 3672 <p>Instructions on what to do for users of various versions of KDE Err codemadness.org 70 i 3673 libraries.</p> Err codemadness.org 70 i 3674 </li> Err codemadness.org 70 i 3675 <li> Err codemadness.org 70 i 3676 <p>Links to easy-to-cherry-pick patches for distro vendors.</p> Err codemadness.org 70 i 3677 </li> Err codemadness.org 70 i 3678 </ul> Err codemadness.org 70 i 3679 <p>Now, distro vendors are, in turn, in emergency mode, as they must Err codemadness.org 70 i 3680 apply the patch, run it through QA, release their own advisories, Err codemadness.org 70 i 3681 etc.</p> Err codemadness.org 70 i 3682 <h2>What if this had been done with coordinated disclosure?</h2> Err codemadness.org 70 i 3683 <p>The bug would have been fixed, probably in the same way, <em>but it would Err codemadness.org 70 i 3684 not be in emergency mode</em>. <a href="https://kde.org/info/security/advisory-20190807-1.txt">KDE's advisory</a> contains this:</p> Err codemadness.org 70 i 3685 <blockquote> Err codemadness.org 70 i 3686 <p>Thanks to Dominik Penner for finding and documenting this issue (we wish however that he would Err codemadness.org 70 i 3687 have contacted us before making the issue public) and to David Faure for the fix.</p> Err codemadness.org 70 i 3688 </blockquote> Err codemadness.org 70 i 3689 <p>This is an extremely gracious way of thanking the reporter.</p> Err codemadness.org 70 i 3690 <h2>I am not an infosec person...</h2> Err codemadness.org 70 i 3691 <p>... but some behaviors in the infosec sphere are deeply uncomfortable Err codemadness.org 70 i 3692 to me. I don't like it when security "research" is hard to tell from Err codemadness.org 70 i 3693 vandalism. "Excuse me, you left your car door unlocked" vs. "Hey Err codemadness.org 70 i 3694 everyone, this car is unlocked, have at it".</p> Err codemadness.org 70 i 3695 <p>I don't know the details of the discourse in the infosec sphere around Err codemadness.org 70 i 3696 full disclosure against irresponsible vendors of proprietary software or Err codemadness.org 70 i 3697 services. However, <strong>KDE is free software</strong>! There is no need to be Err codemadness.org 70 i 3698 an asshole to them.</p>Constructors2019-07-24T13:59:01-05:002019-07-24T13:59:01-05:00Federico Mena Quinterotag:people.gnome.org,2019-07-24:/~federico/blog/constructors.html<p>Have you ever had these annoyances with GObject-style constructors?</p> Err codemadness.org 70 i 3699 <ul> Err codemadness.org 70 i 3700 <li> Err codemadness.org 70 i 3701 <p>From a constructor, calling a method on a partially-constructed Err codemadness.org 70 i 3702 object is dangerous.</p> Err codemadness.org 70 i 3703 </li> Err codemadness.org 70 i 3704 <li> Err codemadness.org 70 i 3705 <p>A constructor needs to set up "not quite initialized" values in the Err codemadness.org 70 i 3706 instance struct until a construct-time property is set.</p> Err codemadness.org 70 i 3707 </li> Err codemadness.org 70 i 3708 <li> Err codemadness.org 70 i 3709 <p>You actually need to override <code>GObjectClass::constructed</code> (or was …</p></li></ul><p>Have you ever had these annoyances with GObject-style constructors?</p> Err codemadness.org 70 i 3710 <ul> Err codemadness.org 70 i 3711 <li> Err codemadness.org 70 i 3712 <p>From a constructor, calling a method on a partially-constructed Err codemadness.org 70 i 3713 object is dangerous.</p> Err codemadness.org 70 i 3714 </li> Err codemadness.org 70 i 3715 <li> Err codemadness.org 70 i 3716 <p>A constructor needs to set up "not quite initialized" values in the Err codemadness.org 70 i 3717 instance struct until a construct-time property is set.</p> Err codemadness.org 70 i 3718 </li> Err codemadness.org 70 i 3719 <li> Err codemadness.org 70 i 3720 <p>You actually need to override <code>GObjectClass::constructed</code> (or was it Err codemadness.org 70 i 3721 <code>::constructor</code>?) to take care of construct-only properties which Err codemadness.org 70 i 3722 need to be considered together, not individually.</p> Err codemadness.org 70 i 3723 </li> Err codemadness.org 70 i 3724 <li> Err codemadness.org 70 i 3725 <p>Constructors can't report an error, unless you derive from Err codemadness.org 70 i 3726 <code>GInitable</code>, which is not in gobject, but in gio instead. (Also, Err codemadness.org 70 i 3727 why does <em>that</em> force the constructor to take a <code>GCancellable</code>...?)</p> Err codemadness.org 70 i 3728 </li> Err codemadness.org 70 i 3729 <li> Err codemadness.org 70 i 3730 <p>You need more than one constructor, but that needs to be done with Err codemadness.org 70 i 3731 helper functions.</p> Err codemadness.org 70 i 3732 </li> Err codemadness.org 70 i 3733 </ul> Err codemadness.org 70 i 3734 <p>This article, <a href="https://matklad.github.io/2019/07/16/perils-of-constructors.html">Perils of Err codemadness.org 70 i 3735 Constructors</a>, Err codemadness.org 70 i 3736 explains all of these problems very well. It is not centered on Err codemadness.org 70 i 3737 GObject, but rather on constructors in object-oriented languages in Err codemadness.org 70 i 3738 general.</p> Err codemadness.org 70 i 3739 <p>(Spoiler: Rust does not have constructors or partially-initialized structs, so Err codemadness.org 70 i 3740 these problems don't really exist there.)</p> Err codemadness.org 70 i 3741 <p>(Addendum: <em>that</em> makes it somewhat awkward to convert GObject code in Err codemadness.org 70 i 3742 C to Rust, but librsvg was able to solve it nicely with Err codemadness.org 70 i 3743 <code>&lt;buzzword&gt;</code><a href="http://cliffle.com/blog/rust-typestate/">the typestate pattern</a><code>&lt;/buzzword&gt;</code>.)</p>Gtk-rs tutorial2019-07-08T18:36:11-05:002019-07-08T18:36:11-05:00Federico Mena Quinterotag:people.gnome.org,2019-07-08:/~federico/blog/gtk-rs-tutorial.html<p><a href="https://cybre.space/@tindall">Leonora Tindall</a> has written a Err codemadness.org 70 i 3744 very nice tutorial on <a href="https://nora.codes/tutorial/speedy-desktop-apps-with-gtk-and-rust/">Speedy Desktop Apps With GTK and Err codemadness.org 70 i 3745 Rust</a>. Err codemadness.org 70 i 3746 It covers prototyping a dice roller app with Glade, writing the code with Err codemadness.org 70 i 3747 Rust and the gtk-rs bindings, and integrating the app into the desktop with Err codemadness.org 70 i 3748 a <code>.desktop</code> file.</p>Removing rsvg-view2019-07-02T11:36:59-05:002019-07-02T11:36:59-05:00Federico Mena Quinterotag:people.gnome.org,2019-07-02:/~federico/blog/removing-rsvg-view.html<p>I am preparing the 2.46.0 librsvg release. <strong>This will no longer have Err codemadness.org 70 i 3749 the rsvg-view-3 program.</strong></p> Err codemadness.org 70 i 3750 <h2>History of rsvg-view</h2> Err codemadness.org 70 i 3751 <p>Rsvg-view <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/c56e236c5b5fa89c365b87ac59bd476d67c5ee27/test-display.c">started Err codemadness.org 70 i 3752 out</a> Err codemadness.org 70 i 3753 as a 71-line C program to aid development of librsvg. It would just Err codemadness.org 70 i 3754 render an SVG file to a pixbuf, stick that pixbuf in a <code>GtkImage</code> Err codemadness.org 70 i 3755 widget …</p><p>I am preparing the 2.46.0 librsvg release. <strong>This will no longer have Err codemadness.org 70 i 3756 the rsvg-view-3 program.</strong></p> Err codemadness.org 70 i 3757 <h2>History of rsvg-view</h2> Err codemadness.org 70 i 3758 <p>Rsvg-view <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/c56e236c5b5fa89c365b87ac59bd476d67c5ee27/test-display.c">started Err codemadness.org 70 i 3759 out</a> Err codemadness.org 70 i 3760 as a 71-line C program to aid development of librsvg. It would just Err codemadness.org 70 i 3761 render an SVG file to a pixbuf, stick that pixbuf in a <code>GtkImage</code> Err codemadness.org 70 i 3762 widget, and show a window with that.</p> Err codemadness.org 70 i 3763 <p>Over time, it slowly acquired most of the command-line options that Err codemadness.org 70 i 3764 <code>rsvg-convert</code> supports. And I suppose, as a way of testing the Err codemadness.org 70 i 3765 Cairo-ification of librsvg, it also got the ability to print SVG files Err codemadness.org 70 i 3766 to a <code>GtkPrintContext</code>. At last count, it was a 784-line C program Err codemadness.org 70 i 3767 that is not really the best code in the world.</p> Err codemadness.org 70 i 3768 <h2>What makes rsvg-view awkward?</h2> Err codemadness.org 70 i 3769 <p>Rsvg-view requires GTK. But GTK requires librsvg, indirectly, through Err codemadness.org 70 i 3770 gdk-pixbuf! There is not a hard circular dependency because GTK goes, Err codemadness.org 70 i 3771 "gdk-pixbuf, load me this SVG file" without knowing how it will be Err codemadness.org 70 i 3772 loaded. In turn, gdk-pixbuf initializes the SVG loader provided by Err codemadness.org 70 i 3773 librsvg, and that loader reads/renders the SVG file.</p> Err codemadness.org 70 i 3774 <p>Ideally librsvg would only depend on gdk-pixbuf, so it would be able Err codemadness.org 70 i 3775 to provide the SVG loader.</p> Err codemadness.org 70 i 3776 <p>The rsvg-view source code still has a few calls to GTK functions which Err codemadness.org 70 i 3777 are now deprecated. The program emits GTK warnings during normal use.</p> Err codemadness.org 70 i 3778 <p>Rsvg-view is... not a very good SVG viewer. It doesn't even start up Err codemadness.org 70 i 3779 with the window scaled properly to the SVG's dimensions! If used for Err codemadness.org 70 i 3780 quick testing during development, it cannot even aid in viewing the Err codemadness.org 70 i 3781 transparent background regions which the SVG does not cover. It just Err codemadness.org 70 i 3782 sticks a lousy custom widget inside a <code>GtkScrolledWindow</code>, and does Err codemadness.org 70 i 3783 not have the conventional niceties to view images like zooming with Err codemadness.org 70 i 3784 the scroll wheel.</p> Err codemadness.org 70 i 3785 <p><a href="https://wiki.gnome.org/Apps/EyeOfGnome/">EOG</a> is a much better SVG viewer than rsvg-view, and people actually Err codemadness.org 70 i 3786 invest effort in making it pleasant to use.</p> Err codemadness.org 70 i 3787 <h2>Removal of rsvg-view</h2> Err codemadness.org 70 i 3788 <p>So, the next version of librsvg will not provide the <code>rsvg-view-3</code> Err codemadness.org 70 i 3789 binary. Please update your packages accordingly. Distros may be able to Err codemadness.org 70 i 3790 move the compilation of librsvg to a more sensible place in the Err codemadness.org 70 i 3791 platform stack, now that it doesn't depend on GTK being available.</p> Err codemadness.org 70 i 3792 <p>What can you use instead? Any other image viewer. <a href="https://wiki.gnome.org/Apps/EyeOfGnome/">EOG</a> works fine; Err codemadness.org 70 i 3793 there are dozens of other good viewers, too.</p>Bzip2 1.0.7 is released2019-06-27T13:55:38-05:002019-06-27T13:55:38-05:00Federico Mena Quinterotag:people.gnome.org,2019-06-27:/~federico/blog/bzip2-107-is-released.html<p><a href="https://sourceware.org/ml/bzip2-devel/2019-q2/msg00022.html">Bzip2 1.0.7 has been released</a> by Mark Wielaard. We have a Err codemadness.org 70 i 3794 slight change of plans since <a href="https://people.gnome.org/~federico/blog/preparing-the-bzip2-107-release.html">my last post</a>:</p> Err codemadness.org 70 i 3795 <ul> Err codemadness.org 70 i 3796 <li> Err codemadness.org 70 i 3797 <p>The 1.0.x series is in strict maintenance mode and will not change Err codemadness.org 70 i 3798 build systems. <strong>This is targeted towards embedded use</strong>, as in Err codemadness.org 70 i 3799 projects which already embed the …</p></li></ul><p><a href="https://sourceware.org/ml/bzip2-devel/2019-q2/msg00022.html">Bzip2 1.0.7 has been released</a> by Mark Wielaard. We have a Err codemadness.org 70 i 3800 slight change of plans since <a href="https://people.gnome.org/~federico/blog/preparing-the-bzip2-107-release.html">my last post</a>:</p> Err codemadness.org 70 i 3801 <ul> Err codemadness.org 70 i 3802 <li> Err codemadness.org 70 i 3803 <p>The 1.0.x series is in strict maintenance mode and will not change Err codemadness.org 70 i 3804 build systems. <strong>This is targeted towards embedded use</strong>, as in Err codemadness.org 70 i 3805 projects which already embed the bzip2-1.0.6 sources and undoubtedly Err codemadness.org 70 i 3806 patch the build system. Right now this series, and the tagged 1.0.7 Err codemadness.org 70 i 3807 release, live in the <a href="https://sourceware.org/git/?p=bzip2.git">sourceware repository for bzip2</a>.</p> Err codemadness.org 70 i 3808 </li> Err codemadness.org 70 i 3809 <li> Err codemadness.org 70 i 3810 <p>The 1.1.x series has Meson and CMake build systems, and a couple of Err codemadness.org 70 i 3811 extra changes to modernize the C code but which were not fit for the Err codemadness.org 70 i 3812 1.0.7 release. <strong>This is targeted towards operating system Err codemadness.org 70 i 3813 distributions</strong>. This lives in the master branch of the <a href="https://gitlab.com/federicomenaquintero/bzip2">gitlab Err codemadness.org 70 i 3814 repository for bzip2</a>.</p> Err codemadness.org 70 i 3815 </li> Err codemadness.org 70 i 3816 </ul> Err codemadness.org 70 i 3817 <p><strong>Distros and embedded users</strong> should start using bzip2-1.0.7 Err codemadness.org 70 i 3818 immediately. The patches they already have for the bzip2's Err codemadness.org 70 i 3819 traditional build system should still apply. The release includes bug Err codemadness.org 70 i 3820 fixes and security fixes that have accumulated over the years, Err codemadness.org 70 i 3821 including the new <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/74de1e2e6ffc9d51ef9824db71a8ffee5962cdbc">CVE-2019-12900</a>.</p> Err codemadness.org 70 i 3822 <p>Once 1.1.0 is released, distributions should be able to remove their Err codemadness.org 70 i 3823 patches to the build system and just start using Meson or CMake. You Err codemadness.org 70 i 3824 may want to monitor the <a href="https://gitlab.com/federicomenaquintero/bzip2/-/milestones/1">1.1.0 milestone</a> — help is Err codemadness.org 70 i 3825 appreciated fixing the issues there so we can make the first release Err codemadness.org 70 i 3826 with the new build systems!</p>Preparing the bzip2-1.0.7 release2019-06-20T11:47:26-05:002019-06-20T11:47:26-05:00Federico Mena Quinterotag:people.gnome.org,2019-06-20:/~federico/blog/preparing-the-bzip2-107-release.html<p><strong>ATTENTION ALL DISTRIBUTIONS</strong>: this is for you. <strong>THE SONAME MAY CHANGE!</strong></p> Err codemadness.org 70 i 3827 <p>I am preparing a bzip2-1.0.7 release. You can see the <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/master/NEWS">release Err codemadness.org 70 i 3828 notes</a>, which should be of interest:</p> Err codemadness.org 70 i 3829 <ul> Err codemadness.org 70 i 3830 <li> Err codemadness.org 70 i 3831 <p>Many historical patches from various distributions are integrated Err codemadness.org 70 i 3832 now.</p> Err codemadness.org 70 i 3833 </li> Err codemadness.org 70 i 3834 <li> Err codemadness.org 70 i 3835 <p>We have a new fix for the just-published <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12900">CVE-2019-12900</a>, courtesy of …</p></li></ul><p><strong>ATTENTION ALL DISTRIBUTIONS</strong>: this is for you. <strong>THE SONAME MAY CHANGE!</strong></p> Err codemadness.org 70 i 3836 <p>I am preparing a bzip2-1.0.7 release. You can see the <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/master/NEWS">release Err codemadness.org 70 i 3837 notes</a>, which should be of interest:</p> Err codemadness.org 70 i 3838 <ul> Err codemadness.org 70 i 3839 <li> Err codemadness.org 70 i 3840 <p>Many historical patches from various distributions are integrated Err codemadness.org 70 i 3841 now.</p> Err codemadness.org 70 i 3842 </li> Err codemadness.org 70 i 3843 <li> Err codemadness.org 70 i 3844 <p>We have a new fix for the just-published <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12900">CVE-2019-12900</a>, courtesy of Err codemadness.org 70 i 3845 Albert Astals Cid.</p> Err codemadness.org 70 i 3846 </li> Err codemadness.org 70 i 3847 <li> Err codemadness.org 70 i 3848 <p><strong>Bzip2 has moved to Meson</strong> for its preferred build system, Err codemadness.org 70 i 3849 courtesy of Dylan Baker. For special situations, a CMake build Err codemadness.org 70 i 3850 system is also provided, courtesy of Micah Snyder.</p> Err codemadness.org 70 i 3851 </li> Err codemadness.org 70 i 3852 </ul> Err codemadness.org 70 i 3853 <h2>What's with the soname?</h2> Err codemadness.org 70 i 3854 <p>From bzip2-1.0.1 (from the year 2000), until bzip2-1.0.6 (from 2010), Err codemadness.org 70 i 3855 release tarballs came with a special Err codemadness.org 70 i 3856 <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/962d60610cb31e0f294a834e55ebb355be55d05a/Makefile-libbz2_so#L38"><code>Makefile-libbz2_so</code></a> to generate a shared library Err codemadness.org 70 i 3857 instead of a static one.</p> Err codemadness.org 70 i 3858 <p>This never used libtool or anything; it specified linker flags by Err codemadness.org 70 i 3859 hand. Various distributions either patched this special makefile, or Err codemadness.org 70 i 3860 replaced it by another one, or outright replaced the complete build Err codemadness.org 70 i 3861 system for a different one.</p> Err codemadness.org 70 i 3862 <p>Some things to note:</p> Err codemadness.org 70 i 3863 <ul> Err codemadness.org 70 i 3864 <li> Err codemadness.org 70 i 3865 <p>This hand-written <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/962d60610cb31e0f294a834e55ebb355be55d05a/Makefile-libbz2_so#L38"><code>Makefile-libbz2_so</code></a> used a link Err codemadness.org 70 i 3866 line like <code>$(CC) -shared -Wl,-soname -Wl,libbz2.so.1.0 -o Err codemadness.org 70 i 3867 libbz2.so.1.0.6</code>. This means, make the DT_SONAME field inside the Err codemadness.org 70 i 3868 ELF file be <code>libbz2.so.1.0</code> (note the two digits in <code>1.0</code>), and make Err codemadness.org 70 i 3869 the filename of the shared library be <code>libbz2.so.1.0.6</code>.</p> Err codemadness.org 70 i 3870 </li> Err codemadness.org 70 i 3871 <li> Err codemadness.org 70 i 3872 <p>Fedora patched the soname in a patch called "saneso" to just be Err codemadness.org 70 i 3873 <code>libbz2.so.1</code>.</p> Err codemadness.org 70 i 3874 </li> Err codemadness.org 70 i 3875 <li> Err codemadness.org 70 i 3876 <p>Stanislav Brabec, from openSUSE, <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/70ec984159c8263fdd4aac7c4670977aff0fe5b3#a4d1bfa62791f4ba7465f19c7f7da6b3b78714a5_0_30">replaced the hand-written Err codemadness.org 70 i 3877 makefiles with autotools</a>, which meant using libtool. It Err codemadness.org 70 i 3878 has this interesting note:</p> Err codemadness.org 70 i 3879 </li> Err codemadness.org 70 i 3880 </ul> Err codemadness.org 70 i 3881 <blockquote> Err codemadness.org 70 i 3882 <p>Incompatible changes:</p> Err codemadness.org 70 i 3883 <p>soname change. Libtool has no support for two parts soname suffix (e. g. Err codemadness.org 70 i 3884 libbz2.so.1.0). It must be a single number (e. g. libbz2.so.1). That is Err codemadness.org 70 i 3885 why soname must change. But I see not a big problem with it. Several Err codemadness.org 70 i 3886 distributions already use the new number instead of the non-standard Err codemadness.org 70 i 3887 number from Makefile-libbz2_so.</p> Err codemadness.org 70 i 3888 </blockquote> Err codemadness.org 70 i 3889 <p>(In fact, if I do <code>objdump -x /usr/lib64/*.so | grep SONAME</code>, I see Err codemadness.org 70 i 3890 that most libraries have single-digit sonames.)</p> Err codemadness.org 70 i 3891 <p>In my experience, both Fedora and openSUSE are very strict, and Err codemadness.org 70 i 3892 correct, about obscure things like library sonames.</p> Err codemadness.org 70 i 3893 <p>With the switch to Meson, bzip2 no longer uses libtool. It will have Err codemadness.org 70 i 3894 a single-digit soname — this is not in the <code>meson.build</code> yet, but Err codemadness.org 70 i 3895 expect it to be there within the next couple of days.</p> Err codemadness.org 70 i 3896 <p>I don't know what distros which decided to preserve the <code>1.0</code> soname Err codemadness.org 70 i 3897 will need to do; maybe they will need to patch <code>meson.build</code> on their Err codemadness.org 70 i 3898 own.</p> Err codemadness.org 70 i 3899 <p>Fortunately, <strong>the API/ABI are still exactly the same</strong>. You can Err codemadness.org 70 i 3900 preserve the old soname which your distro was using and linking libbz2 Err codemadness.org 70 i 3901 will probably keep working as usual.</p> Err codemadness.org 70 i 3902 <p>(This is a C-only release as usual. The Rust branch is still Err codemadness.org 70 i 3903 experimental.)</p>Bzip2 in Rust: porting the randomization table2019-06-11T14:30:17-05:002019-06-11T14:30:17-05:00Federico Mena Quinterotag:people.gnome.org,2019-06-11:/~federico/blog/bzip2-in-rust-randomization-table.html<p>Here is a straightforward port of some easy code.</p> Err codemadness.org 70 i 3904 <p><a href="https://gitlab.com/federicomenaquintero/bzip2/blob/7bd2dc3c13d01a963ef80ae96727ce247acb77fa/randtable.c#L26"><code>randtable.c</code></a> has a lookup table with seemingly-random Err codemadness.org 70 i 3905 numbers. This table is used by <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/5f8f075a554fc593be827cff5f8bc6ce880b08f7/bzlib_private.h#L131-149">the following macros in Err codemadness.org 70 i 3906 bzlib_private.h</a>:</p> Err codemadness.org 70 i 3907 <div class="highlight"><pre><span></span><code><span class="k">extern</span> <span class="n">Int32</span> <span class="n">BZ2_rNums</span><span class="p">[</span><span class="mi">512</span><span class="p">];</span> Err codemadness.org 70 i 3908 Err codemadness.org 70 i 3909 <span class="cp">#define BZ_RAND_DECLS \</span> Err codemadness.org 70 i 3910 <span class="cp"> Int32 rNToGo; \</span> Err codemadness.org 70 i 3911 <span class="cp"> Int32 rTPos \</span> Err codemadness.org 70 i 3912 Err codemadness.org 70 i 3913 <span class="cp">#define BZ_RAND_INIT_MASK \</span> Err codemadness.org 70 i 3914 <span class="cp"> s-&gt;rNToGo = 0; \</span> Err codemadness.org 70 i 3915 <span class="cp"> s-&gt;rTPos = 0 \</span> Err codemadness.org 70 i 3916 Err codemadness.org 70 i 3917 <span class="cp">#define BZ_RAND_MASK ((s- …</span></code></pre></div><p>Here is a straightforward port of some easy code.</p> Err codemadness.org 70 i 3918 <p><a href="https://gitlab.com/federicomenaquintero/bzip2/blob/7bd2dc3c13d01a963ef80ae96727ce247acb77fa/randtable.c#L26"><code>randtable.c</code></a> has a lookup table with seemingly-random Err codemadness.org 70 i 3919 numbers. This table is used by <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/5f8f075a554fc593be827cff5f8bc6ce880b08f7/bzlib_private.h#L131-149">the following macros in Err codemadness.org 70 i 3920 bzlib_private.h</a>:</p> Err codemadness.org 70 i 3921 <div class="highlight"><pre><span></span><code><span class="k">extern</span> <span class="n">Int32</span> <span class="n">BZ2_rNums</span><span class="p">[</span><span class="mi">512</span><span class="p">];</span> Err codemadness.org 70 i 3922 Err codemadness.org 70 i 3923 <span class="cp">#define BZ_RAND_DECLS \</span> Err codemadness.org 70 i 3924 <span class="cp"> Int32 rNToGo; \</span> Err codemadness.org 70 i 3925 <span class="cp"> Int32 rTPos \</span> Err codemadness.org 70 i 3926 Err codemadness.org 70 i 3927 <span class="cp">#define BZ_RAND_INIT_MASK \</span> Err codemadness.org 70 i 3928 <span class="cp"> s-&gt;rNToGo = 0; \</span> Err codemadness.org 70 i 3929 <span class="cp"> s-&gt;rTPos = 0 \</span> Err codemadness.org 70 i 3930 Err codemadness.org 70 i 3931 <span class="cp">#define BZ_RAND_MASK ((s-&gt;rNToGo == 1) ? 1 : 0)</span> Err codemadness.org 70 i 3932 Err codemadness.org 70 i 3933 <span class="cp">#define BZ_RAND_UPD_MASK \</span> Err codemadness.org 70 i 3934 <span class="cp"> if (s-&gt;rNToGo == 0) { \</span> Err codemadness.org 70 i 3935 <span class="cp"> s-&gt;rNToGo = BZ2_rNums[s-&gt;rTPos]; \</span> Err codemadness.org 70 i 3936 <span class="cp"> s-&gt;rTPos++; \</span> Err codemadness.org 70 i 3937 <span class="cp"> if (s-&gt;rTPos == 512) s-&gt;rTPos = 0; \</span> Err codemadness.org 70 i 3938 <span class="cp"> } \</span> Err codemadness.org 70 i 3939 <span class="cp"> s-&gt;rNToGo--;</span> Err codemadness.org 70 i 3940 </code></pre></div> Err codemadness.org 70 i 3941 Err codemadness.org 70 i 3942 <p>Here, <code>BZ_RAND_DECLS</code> is used to declare two fields, <code>rNToGo</code> and Err codemadness.org 70 i 3943 <code>rTPos</code>, into two structs (<a href="https://gitlab.com/federicomenaquintero/bzip2/blob/5f8f075a554fc593be827cff5f8bc6ce880b08f7/bzlib_private.h#L213">1</a>, <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/5f8f075a554fc593be827cff5f8bc6ce880b08f7/bzlib_private.h#L345">2</a>). Both are similar to this:</p> Err codemadness.org 70 i 3944 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 3945 <span class="p">...</span> Err codemadness.org 70 i 3946 <span class="n">Bool</span> <span class="n">blockRandomised</span><span class="p">;</span> Err codemadness.org 70 i 3947 <span class="n">BZ_RAND_DECLS</span> Err codemadness.org 70 i 3948 <span class="p">...</span> Err codemadness.org 70 i 3949 <span class="p">}</span> <span class="n">DState</span><span class="p">;</span> Err codemadness.org 70 i 3950 </code></pre></div> Err codemadness.org 70 i 3951 Err codemadness.org 70 i 3952 <p>Then, the code that needs to initialize those fields calls Err codemadness.org 70 i 3953 <code>BZ_RAND_INIT_MASK</code>, which expands into code to set the two fields to Err codemadness.org 70 i 3954 zero.</p> Err codemadness.org 70 i 3955 <p>At several points in the code, <code>BZ_RAND_UPD_MASK</code> gets called, which Err codemadness.org 70 i 3956 expands into code that updates the randomization state, or something Err codemadness.org 70 i 3957 like that, and uses <code>BZ_RAND_MASK</code> to get a useful value out of the Err codemadness.org 70 i 3958 randomization state.</p> Err codemadness.org 70 i 3959 <p>I have no idea yet what the state is about, but let's port it Err codemadness.org 70 i 3960 directly.</p> Err codemadness.org 70 i 3961 <h2>Give things a name</h2> Err codemadness.org 70 i 3962 <p>It's interesting to see that <strong>no code except for those macros</strong> uses Err codemadness.org 70 i 3963 the fields <code>rNToGo</code> and <code>rTPos</code>, which are declared via Err codemadness.org 70 i 3964 <code>BZ_RAND_DECLS</code>. So, let's make up a <strong>type with a name</strong> for that. Err codemadness.org 70 i 3965 Since I have no better name for it, I shall call it just Err codemadness.org 70 i 3966 <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/7bd2dc3c13d01a963ef80ae96727ce247acb77fa"><code>RandState</code></a>. I added that type definition in the C code, Err codemadness.org 70 i 3967 and replaced the macro-which-creates-struct-fields with a Err codemadness.org 70 i 3968 <code>RandState</code>-typed field:</p> Err codemadness.org 70 i 3969 <div class="highlight"><pre><span></span><code>-#define BZ_RAND_DECLS \ Err codemadness.org 70 i 3970 - Int32 rNToGo; \ Err codemadness.org 70 i 3971 - Int32 rTPos \ Err codemadness.org 70 i 3972 +typedef struct { Err codemadness.org 70 i 3973 + Int32 rNToGo; Err codemadness.org 70 i 3974 + Int32 rTPos; Err codemadness.org 70 i 3975 +} RandState; Err codemadness.org 70 i 3976 Err codemadness.org 70 i 3977 ... Err codemadness.org 70 i 3978 Err codemadness.org 70 i 3979 - BZ_RAND_DECLS; Err codemadness.org 70 i 3980 + RandState rand; Err codemadness.org 70 i 3981 </code></pre></div> Err codemadness.org 70 i 3982 Err codemadness.org 70 i 3983 <p>Since the fields now live inside a sub-struct, I changed the other Err codemadness.org 70 i 3984 macros to use <code>s-&gt;rand.rNToGo</code> instead of <code>s-&gt;rNToGo</code>, and similarly Err codemadness.org 70 i 3985 for the other field.</p> Err codemadness.org 70 i 3986 <h2>Turn macros into functions</h2> Err codemadness.org 70 i 3987 <p>Now, three commits (<a href="https://gitlab.com/federicomenaquintero/bzip2/commit/95d3e979a4ac69d382b67d0d4c97e956002797b4">1</a>, <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/35242daa8f760c68e0b358f4ee38488374f440db">2</a>, <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/5d376c13abb891d311a00dc1ce0533390fc20b51">3</a>) to turn the Err codemadness.org 70 i 3988 macros <code>BZ_RAND_INIT_MASK</code>, <code>BZ_RAND_MASK</code>, and <code>BZ_RAND_UPD_MASK</code> Err codemadness.org 70 i 3989 into functions.</p> Err codemadness.org 70 i 3990 <p>And now that the functions live in the same C source file as the Err codemadness.org 70 i 3991 lookup table they reference, <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/5d5f98ab88526498a20969d565bf2f3831b4467a">the table can be made <code>static const</code></a> to Err codemadness.org 70 i 3992 avoid having it as read/write unshared data in the linked binary.</p> Err codemadness.org 70 i 3993 <p>Premature optimization concern: doesn't de-inlining those macros Err codemadness.org 70 i 3994 cause performance problems? At first, we will get the added overhead Err codemadness.org 70 i 3995 from a function call. When the whole code is ported to Rust, the Rust Err codemadness.org 70 i 3996 compiler will probably be able to figure out that those tiny functions Err codemadness.org 70 i 3997 can be inlined (or we can <code>#[inline]</code> them by hand if we have proof, Err codemadness.org 70 i 3998 or if we have more hubris than faith in LLVM).</p> Err codemadness.org 70 i 3999 <h2>Port functions and table to Rust</h2> Err codemadness.org 70 i 4000 <p>The functions are so tiny, and the table so cut-and-pasteable, that Err codemadness.org 70 i 4001 it's easy to <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/dc01d95a704d02a810072a0aab5fb11e58150f78">port them to Rust</a> in a single shot:</p> Err codemadness.org 70 i 4002 <div class="highlight"><pre><span></span><code><span class="cp">#[no_mangle]</span><span class="w"></span> Err codemadness.org 70 i 4003 <span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">BZ2_rand_init</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">RandState</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4004 <span class="w"> </span><span class="n">RandState</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4005 <span class="w"> </span><span class="n">rNToGo</span>: <span class="mi">0</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4006 <span class="w"> </span><span class="n">rTPos</span>: <span class="mi">0</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4007 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4008 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4009 Err codemadness.org 70 i 4010 <span class="cp">#[no_mangle]</span><span class="w"></span> Err codemadness.org 70 i 4011 <span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">BZ2_rand_mask</span><span class="p">(</span><span class="n">r</span>: <span class="kp">&amp;</span><span class="nc">RandState</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4012 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">rNToGo</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4013 <span class="w"> </span><span class="mi">1</span><span class="w"></span> Err codemadness.org 70 i 4014 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4015 <span class="w"> </span><span class="mi">0</span><span class="w"></span> Err codemadness.org 70 i 4016 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4017 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4018 Err codemadness.org 70 i 4019 <span class="cp">#[no_mangle]</span><span class="w"></span> Err codemadness.org 70 i 4020 <span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">BZ2_rand_update_mask</span><span class="p">(</span><span class="n">r</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">RandState</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4021 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">rNToGo</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4022 <span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">rNToGo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RAND_TABLE</span><span class="p">[</span><span class="n">r</span><span class="p">.</span><span class="n">rTPos</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">];</span><span class="w"></span> Err codemadness.org 70 i 4023 <span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">rTPos</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 4024 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">rTPos</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">512</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4025 <span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">rTPos</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 4026 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4027 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4028 <span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">rNToGo</span><span class="w"> </span><span class="o">-=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 4029 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4030 </code></pre></div> Err codemadness.org 70 i 4031 Err codemadness.org 70 i 4032 <p>Also, we define the <code>RandState</code> type as a Rust struct with a Err codemadness.org 70 i 4033 <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/dc01d95a704d02a810072a0aab5fb11e58150f78/bzlib_rust/src/rand_table.rs#L56-61">C-compatible representation</a>, so it will have the same layout in memory Err codemadness.org 70 i 4034 as the C struct. <strong>This is what allows us to have a <code>RandState</code> in Err codemadness.org 70 i 4035 the C struct</strong>, while in reality the C code doesn't access it Err codemadness.org 70 i 4036 directly; it is just used as a struct field.</p> Err codemadness.org 70 i 4037 <div class="highlight"><pre><span></span><code><span class="c1">// Keep this in sync with bzlib_private.h:</span> Err codemadness.org 70 i 4038 <span class="cp">#[repr(C)]</span><span class="w"></span> Err codemadness.org 70 i 4039 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">RandState</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4040 <span class="w"> </span><span class="n">rNToGo</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4041 <span class="w"> </span><span class="n">rTPos</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4042 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4043 </code></pre></div> Err codemadness.org 70 i 4044 Err codemadness.org 70 i 4045 <p><a href="https://gitlab.com/federicomenaquintero/bzip2/commit/dc01d95a704d02a810072a0aab5fb11e58150f78">See the commit</a> for the corresponding <code>extern</code> Err codemadness.org 70 i 4046 declarations in <code>bzlib_private.h</code>. With those functions and the table Err codemadness.org 70 i 4047 ported to Rust, we can remove <code>randtable.c</code>. Yay!</p> Err codemadness.org 70 i 4048 <h2>A few cleanups</h2> Err codemadness.org 70 i 4049 <p>After moving to another house one throws away useless boxes; we have Err codemadness.org 70 i 4050 to do some cleanup in the Rust code after the initial port, too.</p> Err codemadness.org 70 i 4051 <p>Rust prefers snake_case fields rather than camelCase ones, and I Err codemadness.org 70 i 4052 agree. I <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/d13a9fbc51f5c49b859485d6097ee0dd2b3410f0">renamed the fields</a> to <code>n_to_go</code> and <code>table_pos</code>.</p> Err codemadness.org 70 i 4053 <p>Then, I discovered that the <code>EState</code> struct doesn't actually use the Err codemadness.org 70 i 4054 fields for the randomization state. I just <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/b73c6a507de373dbb0cc8deab0767b41967e678b">removed them</a>.</p> Err codemadness.org 70 i 4055 <h2>Exegesis</h2> Err codemadness.org 70 i 4056 <p>What is that randomization state all about?</p> Err codemadness.org 70 i 4057 <p>And why does <code>DState</code> (the struct used during decompression) need the Err codemadness.org 70 i 4058 randomization state, but <code>EState</code> (used during compression) doesn't Err codemadness.org 70 i 4059 need it?</p> Err codemadness.org 70 i 4060 <p>I found <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/rustify/compress.c#L639-648">this interesting comment</a>:</p> Err codemadness.org 70 i 4061 <div class="highlight"><pre><span></span><code> <span class="cm">/*-- </span> Err codemadness.org 70 i 4062 <span class="cm"> Now a single bit indicating (non-)randomisation. </span> Err codemadness.org 70 i 4063 <span class="cm"> As of version 0.9.5, we use a better sorting algorithm</span> Err codemadness.org 70 i 4064 <span class="cm"> which makes randomisation unnecessary. So always set</span> Err codemadness.org 70 i 4065 <span class="cm"> the randomised bit to &#39;no&#39;. Of course, the decoder</span> Err codemadness.org 70 i 4066 <span class="cm"> still needs to be able to handle randomised blocks</span> Err codemadness.org 70 i 4067 <span class="cm"> so as to maintain backwards compatibility with</span> Err codemadness.org 70 i 4068 <span class="cm"> older versions of bzip2.</span> Err codemadness.org 70 i 4069 <span class="cm"> --*/</span> Err codemadness.org 70 i 4070 <span class="n">bsW</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span> Err codemadness.org 70 i 4071 </code></pre></div> Err codemadness.org 70 i 4072 Err codemadness.org 70 i 4073 <p>Okay! So <em>compression</em> no longer uses randomization, but Err codemadness.org 70 i 4074 <em>decompression</em> has to support files which were compressed with Err codemadness.org 70 i 4075 randomization. Here, <code>bsW(s,1,0)</code> always writes a 0 bit to the file.</p> Err codemadness.org 70 i 4076 <p>However, the decompression code <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/b73c6a507de373dbb0cc8deab0767b41967e678b/decompress.c#L251">actually reads the <code>blockRandomised</code> Err codemadness.org 70 i 4077 bit</a> from the file so that it can see whether it is Err codemadness.org 70 i 4078 dealing with an old-format file:</p> Err codemadness.org 70 i 4079 <div class="highlight"><pre><span></span><code><span class="n">GET_BITS</span><span class="p">(</span><span class="n">BZ_X_RANDBIT</span><span class="p">,</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">blockRandomised</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> Err codemadness.org 70 i 4080 </code></pre></div> Err codemadness.org 70 i 4081 Err codemadness.org 70 i 4082 <p>Later in the code, this <code>s-&gt;blockRandomised</code> field gets consulted; if Err codemadness.org 70 i 4083 the bit is on, the code calls <code>BZ2_rand_update_mask()</code> and friends as Err codemadness.org 70 i 4084 appropriate. If one is using files compressed with Bzip2 0.9.5 or Err codemadness.org 70 i 4085 later, those randomization functions are not even called.</p> Err codemadness.org 70 i 4086 <p>Talk about preserving compatibility with the past.</p> Err codemadness.org 70 i 4087 <h2>Explanation, or building my headcanon</h2> Err codemadness.org 70 i 4088 <p>Bzip2's compression starts by running a <a href="https://en.wikipedia.org/wiki/Burrows%E2%80%93Wheeler_transform">Burrows-Wheeler Err codemadness.org 70 i 4089 Transform</a> on a block of data to compress, which is a wonderful Err codemadness.org 70 i 4090 algorithm that I'm trying to fully understand. Part of the BWT Err codemadness.org 70 i 4091 involves sorting all the string rotations of the block in question.</p> Err codemadness.org 70 i 4092 <p>Per <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/rustify/compress.c#L639-648">the comment I cited</a>, really old versions of bzip2 used a Err codemadness.org 70 i 4093 randomization helper to make sorting perform well in extreme cases, Err codemadness.org 70 i 4094 but not-so-old versions fixed this.</p> Err codemadness.org 70 i 4095 <p>This explains why the decompression struct <code>DState</code> has a Err codemadness.org 70 i 4096 <code>blockRandomised</code> bit, but the compression struct <code>EState</code> doesn't Err codemadness.org 70 i 4097 need one. The fields that the original macro was pasting into Err codemadness.org 70 i 4098 <code>EState</code> were just a vestige from 1999, which is when Bzip2 0.9.5 was Err codemadness.org 70 i 4099 released.</p>Bzip2 uses Meson and Autotools now — and a plea for help2019-06-07T11:01:44-05:002019-06-07T11:01:44-05:00Federico Mena Quinterotag:people.gnome.org,2019-06-07:/~federico/blog/bzip2-uses-meson-and-autotools.html<p>There is a lot of activity in the <a href="https://gitlab.com/federicomenaquintero/bzip2/activity">bzip2 repository</a>!</p> Err codemadness.org 70 i 4100 <p>Perhaps the most exciting thing is that Dylan Baker made a merge Err codemadness.org 70 i 4101 request to add <a href="https://gitlab.com/federicomenaquintero/bzip2/merge_requests/6">Meson as a build system for bzip2</a>; this is Err codemadness.org 70 i 4102 merged now into the master branch.</p> Err codemadness.org 70 i 4103 <p>The current status is this:</p> Err codemadness.org 70 i 4104 <ul> Err codemadness.org 70 i 4105 <li>Both Meson and Autotools are …</li></ul><p>There is a lot of activity in the <a href="https://gitlab.com/federicomenaquintero/bzip2/activity">bzip2 repository</a>!</p> Err codemadness.org 70 i 4106 <p>Perhaps the most exciting thing is that Dylan Baker made a merge Err codemadness.org 70 i 4107 request to add <a href="https://gitlab.com/federicomenaquintero/bzip2/merge_requests/6">Meson as a build system for bzip2</a>; this is Err codemadness.org 70 i 4108 merged now into the master branch.</p> Err codemadness.org 70 i 4109 <p>The current status is this:</p> Err codemadness.org 70 i 4110 <ul> Err codemadness.org 70 i 4111 <li>Both Meson and Autotools are supported.</li> Err codemadness.org 70 i 4112 <li>We have CI runs for both build systems.</li> Err codemadness.org 70 i 4113 </ul> Err codemadness.org 70 i 4114 <h2>A plea for help: add CI runners for other platforms!</h2> Err codemadness.org 70 i 4115 <p>Do you use *BSD / Windows / Solaris / etc. and know how to make Err codemadness.org 70 i 4116 Gitlab's CI work for them?</p> Err codemadness.org 70 i 4117 <p>The only runners we have now for bzip2 are for well-known Linux Err codemadness.org 70 i 4118 distros. I would really like to keep bzip2 working on non-Linux Err codemadness.org 70 i 4119 platforms. If you know how to make Gitlab CI runners for other Err codemadness.org 70 i 4120 systems, <a href="https://gitlab.com/federicomenaquintero/bzip2/merge_requests">please send a merge request</a>!</p> Err codemadness.org 70 i 4121 <h2>Why two build systems?</h2> Err codemadness.org 70 i 4122 <p>Mainly uncertainty on my part. I haven't used Meson extensively; Err codemadness.org 70 i 4123 people tell me that it works better than Autotools out of the box for Err codemadness.org 70 i 4124 Windows.</p> Err codemadness.org 70 i 4125 <p>Bzip2 runs on all sorts of ancient systems, and I don't know whether Err codemadness.org 70 i 4126 Meson or Autotools will be a better fit for them. Time will tell. Err codemadness.org 70 i 4127 Hopefully in the future we can have only a single supported build Err codemadness.org 70 i 4128 system for bzip2.</p>Bzip2 repository reconstructed2019-06-05T19:13:05-05:002019-06-05T19:13:05-05:00Federico Mena Quinterotag:people.gnome.org,2019-06-05:/~federico/blog/bzip2-repository-reconstructed.html<p>I have just done a <code>git push --force-with-lease</code> to <a href="https://gitlab.com/federicomenaquintero/bzip2/commits/master">bzip2's master Err codemadness.org 70 i 4129 branch</a>, which means that if you had a previous clone of this Err codemadness.org 70 i 4130 repository, you'll have to re-fetch it and rebase any changes you may Err codemadness.org 70 i 4131 have on top.</p> Err codemadness.org 70 i 4132 <p>I apologize for the inconvenience!</p> Err codemadness.org 70 i 4133 <p>But I have a good excuse: Julian …</p><p>I have just done a <code>git push --force-with-lease</code> to <a href="https://gitlab.com/federicomenaquintero/bzip2/commits/master">bzip2's master Err codemadness.org 70 i 4134 branch</a>, which means that if you had a previous clone of this Err codemadness.org 70 i 4135 repository, you'll have to re-fetch it and rebase any changes you may Err codemadness.org 70 i 4136 have on top.</p> Err codemadness.org 70 i 4137 <p>I apologize for the inconvenience!</p> Err codemadness.org 70 i 4138 <p>But I have a good excuse: Julian Seward pointed me to a <a href="https://sourceware.org/git/?p=bzip2.git;a=summary">repository Err codemadness.org 70 i 4139 at sourceware</a> where Mark Wielaard reconstructed a commit Err codemadness.org 70 i 4140 history for bzip2, based on the historical tarballs starting from Err codemadness.org 70 i 4141 bzip2-0.1. Bzip2 was never maintained under revision control, so the Err codemadness.org 70 i 4142 reconstructed repository should be used mostly for historical Err codemadness.org 70 i 4143 reference (go look for <code>bzip2.exe</code> in the initial commit!).</p> Err codemadness.org 70 i 4144 <p>I have rebased all the post-1.0.6 commits on top of Mark's repository; Err codemadness.org 70 i 4145 this is what is in the <a href="https://gitlab.com/federicomenaquintero/bzip2/commits/master">master</a> branch now.</p> Err codemadness.org 70 i 4146 <p>There is a new <a href="https://gitlab.com/federicomenaquintero/bzip2/commits/rustify">rustify</a> branch as well, based on master, which is Err codemadness.org 70 i 4147 where I will do the gradual port to Rust.</p> Err codemadness.org 70 i 4148 <p>I foresee no other force-pushes to the master branch in the future. Err codemadness.org 70 i 4149 Apologies again if this disrupts your workflow.</p> Err codemadness.org 70 i 4150 <p><strong>Update:</strong> <a href="https://gitlab.com/federicomenaquintero/bzip2/issues/7">Someone did another reconstruction</a>. If they Err codemadness.org 70 i 4151 weave the histories together, I'll do another force-push, the very Err codemadness.org 70 i 4152 last one, I promise. If you send merge requests, I'll rebase them Err codemadness.org 70 i 4153 myself if that happens.</p>Maintaining bzip22019-06-04T19:41:57-05:002019-06-04T19:41:57-05:00Federico Mena Quinterotag:people.gnome.org,2019-06-04:/~federico/blog/maintaining-bzip2.html<p>Today I had a very pleasant conversation with Julian Seward, of bzip2 Err codemadness.org 70 i 4154 and <a href="http://valgrind.org/">Valgrind</a> fame. Julian has kindly agreed Err codemadness.org 70 i 4155 to cede the maintainership of <a href="https://sourceware.org/bzip2/">bzip2</a> Err codemadness.org 70 i 4156 to me.</p> Err codemadness.org 70 i 4157 <p>Bzip2 has not had a release since 2010. In the meantime, Linux Err codemadness.org 70 i 4158 distros have accumulated a number of bug/security fixes for it …</p><p>Today I had a very pleasant conversation with Julian Seward, of bzip2 Err codemadness.org 70 i 4159 and <a href="http://valgrind.org/">Valgrind</a> fame. Julian has kindly agreed Err codemadness.org 70 i 4160 to cede the maintainership of <a href="https://sourceware.org/bzip2/">bzip2</a> Err codemadness.org 70 i 4161 to me.</p> Err codemadness.org 70 i 4162 <p>Bzip2 has not had a release since 2010. In the meantime, Linux Err codemadness.org 70 i 4163 distros have accumulated a number of bug/security fixes for it. Err codemadness.org 70 i 4164 Seemingly every distributor of bzip2 patches its build system. The Err codemadness.org 70 i 4165 documentation generation step is a bit creaky. There is no source Err codemadness.org 70 i 4166 control repository, nor bug tracker. I hope to fix these things Err codemadness.org 70 i 4167 gradually.</p> Err codemadness.org 70 i 4168 <p>This is the new <a href="https://gitlab.com/federicomenaquintero/bzip2">repository for bzip2</a>.</p> Err codemadness.org 70 i 4169 <p>Ways in which you can immediately help by submitting merge requests:</p> Err codemadness.org 70 i 4170 <ul> Err codemadness.org 70 i 4171 <li> Err codemadness.org 70 i 4172 <p>Look at the <a href="https://gitlab.com/federicomenaquintero/bzip2/issues">issues</a>; currently they are around auto-generating the Err codemadness.org 70 i 4173 version number.</p> Err codemadness.org 70 i 4174 </li> Err codemadness.org 70 i 4175 <li> Err codemadness.org 70 i 4176 <p>Create a basic <a href="https://gitlab.com/help/ci/README.md">continuous integration</a> pipeline that at least Err codemadness.org 70 i 4177 builds the code and runs the tests.</p> Err codemadness.org 70 i 4178 </li> Err codemadness.org 70 i 4179 <li> Err codemadness.org 70 i 4180 <p>Test the autotools setup, courtesy of Stanislav Brabec, and improve Err codemadness.org 70 i 4181 it as you see fit.</p> Err codemadness.org 70 i 4182 </li> Err codemadness.org 70 i 4183 </ul> Err codemadness.org 70 i 4184 <p>The <a href="https://people.gnome.org/~federico/blog/bzip2-in-rust-basic-infra.html">rustification</a> will happen in a separate branch for now, at least Err codemadness.org 70 i 4185 until the Autotools setup settles down.</p> Err codemadness.org 70 i 4186 <p>I hope to have a 1.0.7 release soon, but this really needs <em>your</em> Err codemadness.org 70 i 4187 help. Let's revive this awesome little project.</p>Bzip2 in Rust - Basic infrastructure and CRC32 computation2019-05-30T10:36:19-05:002019-05-30T10:36:19-05:00Federico Mena Quinterotag:people.gnome.org,2019-05-30:/~federico/blog/bzip2-in-rust-basic-infra.html<p>I have started a little experiment in porting bits of the widely-used Err codemadness.org 70 i 4188 <a href="https://sourceware.org/bzip2/">bzip2/bzlib</a> to Rust. I hope this can Err codemadness.org 70 i 4189 serve to refresh bzip2, which had its last release in 2010 and has Err codemadness.org 70 i 4190 been nominally unmaintained for years.</p> Err codemadness.org 70 i 4191 <p>I hope to make several posts detailing how this port is done …</p><p>I have started a little experiment in porting bits of the widely-used Err codemadness.org 70 i 4192 <a href="https://sourceware.org/bzip2/">bzip2/bzlib</a> to Rust. I hope this can Err codemadness.org 70 i 4193 serve to refresh bzip2, which had its last release in 2010 and has Err codemadness.org 70 i 4194 been nominally unmaintained for years.</p> Err codemadness.org 70 i 4195 <p>I hope to make several posts detailing how this port is done. In this Err codemadness.org 70 i 4196 post, I'll talk about setting up a Rust infrastructure for bzip2 and Err codemadness.org 70 i 4197 my experiments in replacing the C code that does CRC32 computations.</p> Err codemadness.org 70 i 4198 <h2>Super-quick summary of how librsvg was ported to Rust</h2> Err codemadness.org 70 i 4199 <ul> Err codemadness.org 70 i 4200 <li> Err codemadness.org 70 i 4201 <p>Add the necessary autotools infrastructure to build a Rust Err codemadness.org 70 i 4202 sub-library that gets linked into the main public library.</p> Err codemadness.org 70 i 4203 </li> Err codemadness.org 70 i 4204 <li> Err codemadness.org 70 i 4205 <p>Port bit by bit to Rust. Add unit tests as appropriate. Refactor Err codemadness.org 70 i 4206 endlessly.</p> Err codemadness.org 70 i 4207 </li> Err codemadness.org 70 i 4208 <li> Err codemadness.org 70 i 4209 <p><strong>MAINTAIN THE PUBLIC API/ABI AT ALL COSTS</strong> so callers don't Err codemadness.org 70 i 4210 notice that the library is being rewritten under their feet.</p> Err codemadness.org 70 i 4211 </li> Err codemadness.org 70 i 4212 </ul> Err codemadness.org 70 i 4213 <p>I have no idea of how bzip2 works internally, but I do know how to Err codemadness.org 70 i 4214 maintain ABIs, so let's get started.</p> Err codemadness.org 70 i 4215 <h2>Bzip2's source tree</h2> Err codemadness.org 70 i 4216 <p>As a very small project that just builds a library and couple of Err codemadness.org 70 i 4217 executables, bzip2 was structured with all the source files directly Err codemadness.org 70 i 4218 under a toplevel directory.</p> Err codemadness.org 70 i 4219 <p>The only tests in there are three reference files that get compressed, Err codemadness.org 70 i 4220 then uncompressed, and then compared to the original ones.</p> Err codemadness.org 70 i 4221 <p>As the rustification proceeds, I'll move the files around to better Err codemadness.org 70 i 4222 places. The scheme from librsvg worked well in this respect, so I'll Err codemadness.org 70 i 4223 probably be copying many of the techniques and organization from Err codemadness.org 70 i 4224 there.</p> Err codemadness.org 70 i 4225 <h2>Deciding what to port first</h2> Err codemadness.org 70 i 4226 <p>I looked a bit at the bzip2 sources, and the code to do CRC32 Err codemadness.org 70 i 4227 computations seemed isolated enough from the rest of the code to port Err codemadness.org 70 i 4228 easily.</p> Err codemadness.org 70 i 4229 <p>The CRC32 code was arranged like this. First, a lookup table in Err codemadness.org 70 i 4230 <code>crc32table.c</code>:</p> Err codemadness.org 70 i 4231 <div class="highlight"><pre><span></span><code><span class="n">UInt32</span> <span class="n">BZ2_crc32Table</span><span class="p">[</span><span class="mi">256</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> Err codemadness.org 70 i 4232 <span class="mh">0x00000000L</span><span class="p">,</span> <span class="mh">0x04c11db7L</span><span class="p">,</span> <span class="mh">0x09823b6eL</span><span class="p">,</span> <span class="mh">0x0d4326d9L</span><span class="p">,</span> Err codemadness.org 70 i 4233 <span class="mh">0x130476dcL</span><span class="p">,</span> <span class="mh">0x17c56b6bL</span><span class="p">,</span> <span class="mh">0x1a864db2L</span><span class="p">,</span> <span class="mh">0x1e475005L</span><span class="p">,</span> Err codemadness.org 70 i 4234 <span class="p">...</span> Err codemadness.org 70 i 4235 <span class="p">}</span> Err codemadness.org 70 i 4236 </code></pre></div> Err codemadness.org 70 i 4237 Err codemadness.org 70 i 4238 <p>And then, three macros in <code>bzlib_private.h</code> which make up all the Err codemadness.org 70 i 4239 CRC32 code in the library:</p> Err codemadness.org 70 i 4240 <div class="highlight"><pre><span></span><code><span class="k">extern</span> <span class="n">UInt32</span> <span class="n">BZ2_crc32Table</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span> Err codemadness.org 70 i 4241 Err codemadness.org 70 i 4242 <span class="cp">#define BZ_INITIALISE_CRC(crcVar) \</span> Err codemadness.org 70 i 4243 <span class="cp">{ \</span> Err codemadness.org 70 i 4244 <span class="cp"> crcVar = 0xffffffffL; \</span> Err codemadness.org 70 i 4245 <span class="cp">}</span> Err codemadness.org 70 i 4246 Err codemadness.org 70 i 4247 <span class="cp">#define BZ_FINALISE_CRC(crcVar) \</span> Err codemadness.org 70 i 4248 <span class="cp">{ \</span> Err codemadness.org 70 i 4249 <span class="cp"> crcVar = ~(crcVar); \</span> Err codemadness.org 70 i 4250 <span class="cp">}</span> Err codemadness.org 70 i 4251 Err codemadness.org 70 i 4252 <span class="cp">#define BZ_UPDATE_CRC(crcVar,cha) \</span> Err codemadness.org 70 i 4253 <span class="cp">{ \</span> Err codemadness.org 70 i 4254 <span class="cp"> crcVar = (crcVar &lt;&lt; 8) ^ \</span> Err codemadness.org 70 i 4255 <span class="cp"> BZ2_crc32Table[(crcVar &gt;&gt; 24) ^ \</span> Err codemadness.org 70 i 4256 <span class="cp"> ((UChar)cha)]; \</span> Err codemadness.org 70 i 4257 <span class="cp">}</span> Err codemadness.org 70 i 4258 </code></pre></div> Err codemadness.org 70 i 4259 Err codemadness.org 70 i 4260 <p>Initially I wanted to just remove this code and replace it with one of Err codemadness.org 70 i 4261 the existing Rust crates to do CRC32 computations, but first I needed Err codemadness.org 70 i 4262 to know which variant of CRC32 this is.</p> Err codemadness.org 70 i 4263 <h2>Preparing the CRC32 port so it will not break</h2> Err codemadness.org 70 i 4264 <p>I needed to set up tests for the CRC32 code so the replacement code Err codemadness.org 70 i 4265 would compute exactly the same values as the original:</p> Err codemadness.org 70 i 4266 <ul> Err codemadness.org 70 i 4267 <li><a href="https://gitlab.com/federicomenaquintero/bzip2/commit/bd79e7adf4274eef376111404a40ef3bd6836f06">Rename crc32table.c to Err codemadness.org 70 i 4268 crc32.c</a> - Err codemadness.org 70 i 4269 that file is going to hold all the CRC32 code, not only the lookup table.</li> Err codemadness.org 70 i 4270 <li><a href="https://gitlab.com/federicomenaquintero/bzip2/commit/fb284d38c6f45382c5f6acde69631caed6589f27">Turn the CRC32 macros into Err codemadness.org 70 i 4271 functions</a> - Err codemadness.org 70 i 4272 so I can move them to Rust and have the C code call them.</li> Err codemadness.org 70 i 4273 </ul> Err codemadness.org 70 i 4274 <p>Then I needed a test that computed the CRC32 values of several Err codemadness.org 70 i 4275 strings, so I could capture the results and make them part of the Err codemadness.org 70 i 4276 test.</p> Err codemadness.org 70 i 4277 <div class="highlight"><pre><span></span><code><span class="k">static</span> <span class="k">const</span> <span class="n">UChar</span> <span class="n">buf1</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="p">;</span> Err codemadness.org 70 i 4278 <span class="k">static</span> <span class="k">const</span> <span class="n">UChar</span> <span class="n">buf2</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&quot; &quot;</span><span class="p">;</span> Err codemadness.org 70 i 4279 <span class="k">static</span> <span class="k">const</span> <span class="n">UChar</span> <span class="n">buf3</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&quot;hello world&quot;</span><span class="p">;</span> Err codemadness.org 70 i 4280 <span class="k">static</span> <span class="k">const</span> <span class="n">UChar</span> <span class="n">buf4</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&quot;Lorem ipsum dolor sit amet, consectetur adipiscing elit, &quot;</span><span class="p">;</span> Err codemadness.org 70 i 4281 Err codemadness.org 70 i 4282 <span class="kt">int</span> Err codemadness.org 70 i 4283 <span class="nf">main</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span> Err codemadness.org 70 i 4284 <span class="p">{</span> Err codemadness.org 70 i 4285 <span class="n">printf</span> <span class="p">(</span><span class="s">&quot;buf1: %x</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">crc32_buffer</span><span class="p">(</span><span class="n">buf1</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">buf1</span><span class="p">)));</span> Err codemadness.org 70 i 4286 <span class="n">printf</span> <span class="p">(</span><span class="s">&quot;buf2: %x</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">crc32_buffer</span><span class="p">(</span><span class="n">buf2</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">buf2</span><span class="p">)));</span> Err codemadness.org 70 i 4287 <span class="n">printf</span> <span class="p">(</span><span class="s">&quot;buf3: %x</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">crc32_buffer</span><span class="p">(</span><span class="n">buf3</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">buf3</span><span class="p">)));</span> Err codemadness.org 70 i 4288 <span class="n">printf</span> <span class="p">(</span><span class="s">&quot;buf4: %x</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">crc32_buffer</span><span class="p">(</span><span class="n">buf4</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">buf4</span><span class="p">)));</span> Err codemadness.org 70 i 4289 <span class="c1">// ...</span> Err codemadness.org 70 i 4290 <span class="p">}</span> Err codemadness.org 70 i 4291 </code></pre></div> Err codemadness.org 70 i 4292 Err codemadness.org 70 i 4293 <p>This computes the CRC32 values of some strings using the original Err codemadness.org 70 i 4294 algorithm, and prints their results. Then I could cut&amp;paste those Err codemadness.org 70 i 4295 results, and turn the <code>printf</code> into <code>assert</code> — and that gives me a Err codemadness.org 70 i 4296 test.</p> Err codemadness.org 70 i 4297 <div class="highlight"><pre><span></span><code><span class="kt">int</span> Err codemadness.org 70 i 4298 <span class="nf">main</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span> Err codemadness.org 70 i 4299 <span class="p">{</span> Err codemadness.org 70 i 4300 <span class="n">assert</span> <span class="p">(</span><span class="n">crc32_buffer</span> <span class="p">(</span><span class="n">buf1</span><span class="p">,</span> <span class="n">strlen</span> <span class="p">(</span><span class="n">buf1</span><span class="p">))</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">);</span> Err codemadness.org 70 i 4301 <span class="n">assert</span> <span class="p">(</span><span class="n">crc32_buffer</span> <span class="p">(</span><span class="n">buf2</span><span class="p">,</span> <span class="n">strlen</span> <span class="p">(</span><span class="n">buf2</span><span class="p">))</span> <span class="o">==</span> <span class="mh">0x29d4f6ab</span><span class="p">);</span> Err codemadness.org 70 i 4302 <span class="n">assert</span> <span class="p">(</span><span class="n">crc32_buffer</span> <span class="p">(</span><span class="n">buf3</span><span class="p">,</span> <span class="n">strlen</span> <span class="p">(</span><span class="n">buf3</span><span class="p">))</span> <span class="o">==</span> <span class="mh">0x44f71378</span><span class="p">);</span> Err codemadness.org 70 i 4303 <span class="n">assert</span> <span class="p">(</span><span class="n">crc32_buffer</span> <span class="p">(</span><span class="n">buf4</span><span class="p">,</span> <span class="n">strlen</span> <span class="p">(</span><span class="n">buf4</span><span class="p">))</span> <span class="o">==</span> <span class="mh">0xd31de6c9</span><span class="p">);</span> Err codemadness.org 70 i 4304 <span class="c1">// ...</span> Err codemadness.org 70 i 4305 <span class="p">}</span> Err codemadness.org 70 i 4306 </code></pre></div> Err codemadness.org 70 i 4307 Err codemadness.org 70 i 4308 <h2>Setting up a Rust infrastructure for bzip2</h2> Err codemadness.org 70 i 4309 <p>Two things made this reasonably easy:</p> Err codemadness.org 70 i 4310 <ul> Err codemadness.org 70 i 4311 <li>A patch from Stanislav Brabec, from Suse, to <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/72ece1146d6de1d2e6d2bb2cc96a1c3c0f307d7b">add an autotools Err codemadness.org 70 i 4312 framework to bzip2</a></li> Err codemadness.org 70 i 4313 <li>The existing <a href="https://people.gnome.org/~federico/blog/librsvg-build-infrastructure.html">Autotools + Rust machinery in librsvg</a>.</li> Err codemadness.org 70 i 4314 </ul> Err codemadness.org 70 i 4315 <p>I.e. "copy and paste from somewhere that I know works well". Err codemadness.org 70 i 4316 Wonderful!</p> Err codemadness.org 70 i 4317 <p>This is the <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/dfa0b88df8b518c4b76d0f3660b6487c67e93d52">commit that adds a Rust infrastructure for Err codemadness.org 70 i 4318 bzip2</a>. It does the following:</p> Err codemadness.org 70 i 4319 <ol> Err codemadness.org 70 i 4320 <li>Create a Cargo workspace (a <code>Cargo.toml</code> in the toplevel) with a Err codemadness.org 70 i 4321 single member, a <code>bzlib_rust</code> directory where the Rustified parts Err codemadness.org 70 i 4322 of the code will live.</li> Err codemadness.org 70 i 4323 <li>Create <code>bzlib_rust/Cargo.toml</code> and <code>bzlib_rust/src</code> for the Rust Err codemadness.org 70 i 4324 sources. This will generate a <code>staticlib</code> for <code>libbzlib_rust.a</code>, that Err codemadness.org 70 i 4325 can be linked into the main <code>libbz2.la</code>.</li> Err codemadness.org 70 i 4326 <li>Puts in automake hooks so that <code>make clean</code>, <code>make check</code>, etc. all Err codemadness.org 70 i 4327 do what you expect for the Rust part.</li> Err codemadness.org 70 i 4328 </ol> Err codemadness.org 70 i 4329 <p>As a side benefit, librsvg's Autotools+Rust infrastructure already Err codemadness.org 70 i 4330 handled things like cross-compilation correctly, so I have high hopes Err codemadness.org 70 i 4331 that this will be good enough for bzip2.</p> Err codemadness.org 70 i 4332 <h2>Can I use a Rust crate for CRC32?</h2> Err codemadness.org 70 i 4333 <p>There are <a href="https://crates.io/search?q=crc&amp;sort=downloads">many Rust crates to do CRC computations</a>. I was Err codemadness.org 70 i 4334 hoping especially to be able to use <a href="https://github.com/srijs/rust-crc32fast">crc32fast</a>, which is Err codemadness.org 70 i 4335 SIMD-accelerated.</p> Err codemadness.org 70 i 4336 <p>I wrote a Rust version of the "CRC me a buffer" test from above to see Err codemadness.org 70 i 4337 if crc32fast produced the same values as the C code, and of course it Err codemadness.org 70 i 4338 didn't. Eventually, after <a href="https://mstdn.mx/@federicomena/102170914494787056">asking on Mastodon</a>, Kepstin <a href="https://glitch.social/@kepstin/102171203800281388">figured Err codemadness.org 70 i 4339 out</a> what variant of CRC32 is being used in the original Err codemadness.org 70 i 4340 code.</p> Err codemadness.org 70 i 4341 <p>It turns out that this is directly doable in Rust with the <a href="https://github.com/mrhooray/crc-rs">git version Err codemadness.org 70 i 4342 of the <code>crc</code> crate</a>. This crate lets one configure the CRC32 Err codemadness.org 70 i 4343 polynomial and the mode of computation; there are <a href="https://github.com/Michaelangel007/crc32">many variants of Err codemadness.org 70 i 4344 CRC32</a> and I wasn't fully aware of them.</p> Err codemadness.org 70 i 4345 <p>The magic incantation is this:</p> Err codemadness.org 70 i 4346 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">digest</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">crc32</span>::<span class="n">Digest</span>::<span class="n">new_custom</span><span class="p">(</span><span class="n">crc32</span>::<span class="n">IEEE</span><span class="p">,</span><span class="w"> </span><span class="o">!</span><span class="mi">0</span><span class="k">u32</span><span class="p">,</span><span class="w"> </span><span class="o">!</span><span class="mi">0</span><span class="k">u32</span><span class="p">,</span><span class="w"> </span><span class="n">crc</span>::<span class="n">CalcType</span>::<span class="n">Normal</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 4347 </code></pre></div> Err codemadness.org 70 i 4348 Err codemadness.org 70 i 4349 <p>With that, <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/6ef10026a74af85437a4deca6957889d9e8bdbf8">the Rust Err codemadness.org 70 i 4350 test</a> Err codemadness.org 70 i 4351 produces the same values as the C code. Yay!</p> Err codemadness.org 70 i 4352 <h2>But it can't be that easy</h2> Err codemadness.org 70 i 4353 <p>Bzlib stores its internal state in the <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/60be65f9/bzlib_private.h#L182-252">EState Err codemadness.org 70 i 4354 struct</a>, Err codemadness.org 70 i 4355 defined in Err codemadness.org 70 i 4356 <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/60be65f9/bzlib_private.h">bzlib_private.h</a>.</p> Err codemadness.org 70 i 4357 <p>That struct stores several running CRC32 computations, and the state Err codemadness.org 70 i 4358 for each one of those is a single <code>UInt32</code> value. However, I cannot Err codemadness.org 70 i 4359 just replace those struct fields with something that comes from Rust, Err codemadness.org 70 i 4360 since the C code does not know the size of a <code>crc32::Digest</code> from Err codemadness.org 70 i 4361 Rust.</p> Err codemadness.org 70 i 4362 <p>The normal way to do this (say, like in librsvg) would be to turn Err codemadness.org 70 i 4363 <code>UInt32 some_crc</code> into <code>void *some_crc</code> and heap-allocate that on the Err codemadness.org 70 i 4364 Rust side, with whatever size it needs.</p> Err codemadness.org 70 i 4365 <p><strong>However!</strong></p> Err codemadness.org 70 i 4366 <p>It turns out that bzlib lets the caller <a href="https://gitlab.com/federicomenaquintero/bzip2/blob/60be65f9/bzlib.h#L62-64">define a custom Err codemadness.org 70 i 4367 allocator</a> Err codemadness.org 70 i 4368 so that bzlib doesn't use plain <code>malloc()</code> by default.</p> Err codemadness.org 70 i 4369 <p>Rust lets one define a <a href="https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html">global, custom allocator</a>. Err codemadness.org 70 i 4370 However, bzlib's concept of a custom allocator includes a bit of Err codemadness.org 70 i 4371 context:</p> Err codemadness.org 70 i 4372 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 4373 <span class="c1">// ...</span> Err codemadness.org 70 i 4374 Err codemadness.org 70 i 4375 <span class="kt">void</span> <span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="n">bzalloc</span><span class="p">)(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int</span> <span class="n">m</span><span class="p">);</span> Err codemadness.org 70 i 4376 <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">bzfree</span><span class="p">)(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">);</span> Err codemadness.org 70 i 4377 <span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">;</span> Err codemadness.org 70 i 4378 <span class="p">}</span> <span class="n">bz_stream</span><span class="p">;</span> Err codemadness.org 70 i 4379 </code></pre></div> Err codemadness.org 70 i 4380 Err codemadness.org 70 i 4381 <p>The caller sets up <code>bzalloc/bzfree</code> callbacks and an optional <code>opaque</code> Err codemadness.org 70 i 4382 context for the allocator. However, Rust's <code>GlobalAlloc</code> is set up at Err codemadness.org 70 i 4383 compilation time, and we can't pass that context in a good, Err codemadness.org 70 i 4384 thread-safe fashion to it.</p> Err codemadness.org 70 i 4385 <h2>Who uses the bzlib custom allocator, anyway?</h2> Err codemadness.org 70 i 4386 <p>If one sets <code>bzalloc/bzfree</code> to <code>NULL</code>, bzlib will use the system's Err codemadness.org 70 i 4387 plain <code>malloc()/free()</code> by default. Most software does this.</p> Err codemadness.org 70 i 4388 <p>I am looking in <a href="https://codesearch.debian.net/search?q=bzalloc">Debian's codesearch</a> for where <code>bzalloc</code> Err codemadness.org 70 i 4389 gets set, hoping that I can figure out if that software really needs a Err codemadness.org 70 i 4390 custom allocator, or if they are just dressing up <code>malloc()</code> with Err codemadness.org 70 i 4391 logging code or similar (ImageMagick seems to do this; Python seems to Err codemadness.org 70 i 4392 have a genuine concern about the Global Interpreter Lock). Debian's Err codemadness.org 70 i 4393 codesearch is a fantastic tool!</p> Err codemadness.org 70 i 4394 <h2>The first rustified code</h2> Err codemadness.org 70 i 4395 <p>I <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/2fb746e887d0fff705f5c82c961407b8539d96a2">cut&amp;pasted the CRC32 lookup Err codemadness.org 70 i 4396 table</a> Err codemadness.org 70 i 4397 and fixed it up for Rust's syntax, and also ported the CRC32 Err codemadness.org 70 i 4398 computation functions. I gave them the same names as the original C Err codemadness.org 70 i 4399 ones, and exported them, e.g.</p> Err codemadness.org 70 i 4400 <div class="highlight"><pre><span></span><code><span class="k">const</span><span class="w"> </span><span class="n">TABLE</span>: <span class="p">[</span><span class="kt">u32</span><span class="p">;</span><span class="w"> </span><span class="mi">256</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="w"></span> Err codemadness.org 70 i 4401 <span class="w"> </span><span class="mh">0x00000000</span><span class="p">,</span><span class="w"> </span><span class="mh">0x04c11db7</span><span class="p">,</span><span class="w"> </span><span class="mh">0x09823b6e</span><span class="p">,</span><span class="w"> </span><span class="mh">0x0d4326d9</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4402 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 4403 <span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 4404 Err codemadness.org 70 i 4405 <span class="cp">#[no_mangle]</span><span class="w"></span> Err codemadness.org 70 i 4406 <span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">BZ2_update_crc</span><span class="p">(</span><span class="n">crc_var</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="kt">u32</span><span class="p">,</span><span class="w"> </span><span class="n">cha</span>: <span class="kt">u8</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4407 <span class="w"> </span><span class="o">*</span><span class="n">crc_var</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">crc_var</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">8</span><span class="p">)</span><span class="w"> </span><span class="o">^</span><span class="w"> </span><span class="n">TABLE</span><span class="p">[((</span><span class="o">*</span><span class="n">crc_var</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="mi">24</span><span class="p">)</span><span class="w"> </span><span class="o">^</span><span class="w"> </span><span class="kt">u32</span>::<span class="n">from</span><span class="p">(</span><span class="n">cha</span><span class="p">))</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">];</span><span class="w"></span> Err codemadness.org 70 i 4408 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4409 </code></pre></div> Err codemadness.org 70 i 4410 Err codemadness.org 70 i 4411 <p>This is a straight port of the C code. Rust is very strict about Err codemadness.org 70 i 4412 integer sizes, and arrays can only be indexed with a <code>usize</code>, not any Err codemadness.org 70 i 4413 random integer — hence the explicit conversions.</p> Err codemadness.org 70 i 4414 <p>And with this, <a href="https://gitlab.com/federicomenaquintero/bzip2/commit/c4c071059f98cfc2e4bb528bde499ba2d41a8b24">and after fixing the Err codemadness.org 70 i 4415 linkage</a>, Err codemadness.org 70 i 4416 the tests pass!</p> Err codemadness.org 70 i 4417 <p>First pass at rustifying CRC32: <strong>done</strong>.</p> Err codemadness.org 70 i 4418 <h2>But that does one byte at a time</h2> Err codemadness.org 70 i 4419 <p>Indeed; the original C code to do CRC32 only handled one byte at a Err codemadness.org 70 i 4420 time. If I replace this with a SIMD-enabled Rust crate, it will want Err codemadness.org 70 i 4421 to process whole buffers at once. I hope the code in bzlib can be Err codemadness.org 70 i 4422 refactored to do that. We'll see!</p> Err codemadness.org 70 i 4423 <h2>How to use an existing Rust crate for this</h2> Err codemadness.org 70 i 4424 <p>I just found out that one does not in fact need to use a complete Err codemadness.org 70 i 4425 <code>crc32::Digest</code> to do equivalent computations; one can call Err codemadness.org 70 i 4426 <a href="https://docs.rs/crc/1.8.1/crc/crc32/fn.update.html">crc32::update()</a> Err codemadness.org 70 i 4427 by hand and maintain a single <code>u32</code> state, just like the original Err codemadness.org 70 i 4428 <code>UInt32</code> from the C code.</p> Err codemadness.org 70 i 4429 <p>So, I may not need to mess around with a custom allocator just yet. Err codemadness.org 70 i 4430 Stay tuned.</p> Err codemadness.org 70 i 4431 <p>In the meantime, I've <a href="https://github.com/srijs/rust-crc32fast/issues/9">filed a bug against Err codemadness.org 70 i 4432 crc32fast</a> to make Err codemadness.org 70 i 4433 it possible to use a custom polynomial and order and still get the Err codemadness.org 70 i 4434 benefits of SIMD.</p>Containing mutability in GObjects2019-04-16T17:04:33-05:002019-04-16T17:04:40-05:00Federico Mena Quinterotag:people.gnome.org,2019-04-16:/~federico/blog/containing-mutability-in-gobjects.html<p>Traditionally, GObject implementations in C are mutable: you Err codemadness.org 70 i 4435 instantiate a GObject and then change its state via method calls. Err codemadness.org 70 i 4436 Sometimes this is expected and desired; a <code>GtkCheckButton</code> widget Err codemadness.org 70 i 4437 certainly can change its internal state from pressed to not pressed, Err codemadness.org 70 i 4438 for example.</p> Err codemadness.org 70 i 4439 <p>Other times, objects are mutable while they are being …</p><p>Traditionally, GObject implementations in C are mutable: you Err codemadness.org 70 i 4440 instantiate a GObject and then change its state via method calls. Err codemadness.org 70 i 4441 Sometimes this is expected and desired; a <code>GtkCheckButton</code> widget Err codemadness.org 70 i 4442 certainly can change its internal state from pressed to not pressed, Err codemadness.org 70 i 4443 for example.</p> Err codemadness.org 70 i 4444 <p>Other times, objects are mutable while they are being "assembled" or Err codemadness.org 70 i 4445 "configured", and only yield a final immutable result until later. Err codemadness.org 70 i 4446 This is the case for <code>RsvgHandle</code> from librsvg.</p> Err codemadness.org 70 i 4447 <p>Please bear with me while I write about the history of the Err codemadness.org 70 i 4448 <code>RsvgHandle</code> API and why it ended up with different ways of doing the Err codemadness.org 70 i 4449 same thing.</p> Err codemadness.org 70 i 4450 <h2>The traditional RsvgHandle API</h2> Err codemadness.org 70 i 4451 <p>The final purpose of an <code>RsvgHandle</code> is to represent an SVG document Err codemadness.org 70 i 4452 loaded in memory. Once it is loaded, the SVG document does not Err codemadness.org 70 i 4453 change, as librsvg does not support animation or creating/removing SVG Err codemadness.org 70 i 4454 elements; it is a static renderer.</p> Err codemadness.org 70 i 4455 <p>However, before an <code>RsvgHandle</code> achieves its immutable state, it has Err codemadness.org 70 i 4456 to be loaded first. Loading can be done in two ways:</p> Err codemadness.org 70 i 4457 <ul> Err codemadness.org 70 i 4458 <li>The historical/deprecated way, using the <a href="https://developer.gnome.org/rsvg/unstable/rsvg-RsvgHandle.html#rsvg-handle-write"><code>rsvg_handle_write()</code></a> and Err codemadness.org 70 i 4459 <code>rsvg_handle_close()</code> APIs. Plenty of code in GNOME used this Err codemadness.org 70 i 4460 <code>write/close</code> idiom before GLib got a good abstraction for Err codemadness.org 70 i 4461 streams; you can see another example in <a href="https://developer.gnome.org/gdk-pixbuf/unstable/GdkPixbufLoader.html"><code>GdkPixbufLoader</code></a>. Err codemadness.org 70 i 4462 The idea is that applications do this:</li> Err codemadness.org 70 i 4463 </ul> Err codemadness.org 70 i 4464 <div class="highlight"><pre><span></span><code><span class="n">file</span> <span class="o">=</span> <span class="n">open</span> <span class="n">a</span> <span class="n">file</span><span class="o">...</span><span class="p">;</span> Err codemadness.org 70 i 4465 <span class="n">handle</span> <span class="o">=</span> <span class="n">rsvg_handle_new</span> <span class="p">();</span> Err codemadness.org 70 i 4466 Err codemadness.org 70 i 4467 <span class="k">while</span> <span class="p">(</span><span class="n">file</span> <span class="n">has</span> <span class="n">more</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span> Err codemadness.org 70 i 4468 <span class="n">rsvg_handle_write</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">a</span> <span class="n">bit</span> <span class="n">of</span> <span class="n">data</span><span class="p">);</span> Err codemadness.org 70 i 4469 <span class="p">}</span> Err codemadness.org 70 i 4470 Err codemadness.org 70 i 4471 <span class="n">rsvg_handle_close</span> <span class="p">(</span><span class="n">handle</span><span class="p">);</span> Err codemadness.org 70 i 4472 Err codemadness.org 70 i 4473 <span class="o">//</span> <span class="n">now</span> <span class="n">the</span> <span class="n">handle</span> <span class="k">is</span> <span class="n">fully</span> <span class="n">loaded</span> <span class="ow">and</span> <span class="n">immutable</span> Err codemadness.org 70 i 4474 Err codemadness.org 70 i 4475 <span class="n">rsvg_handle_render</span> <span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="o">...</span><span class="p">);</span> Err codemadness.org 70 i 4476 </code></pre></div> Err codemadness.org 70 i 4477 Err codemadness.org 70 i 4478 <ul> Err codemadness.org 70 i 4479 <li>The streaming way, with <a href="https://developer.gnome.org/rsvg/unstable/rsvg-Using-RSVG-with-GIO.html#rsvg-handle-read-stream-sync"><code>rsvg_handle_read_stream_sync()</code></a>, Err codemadness.org 70 i 4480 which takes a <a href="https://developer.gnome.org/gio/unstable/GInputStream.html"><code>GInputStream</code></a>, or one of the convenience functions Err codemadness.org 70 i 4481 which take a <a href="https://developer.gnome.org/gio/unstable/GFile.html#GFile-struct"><code>GFile</code></a> and produce a stream from it.</li> Err codemadness.org 70 i 4482 </ul> Err codemadness.org 70 i 4483 <div class="highlight"><pre><span></span><code><span class="n">file</span> <span class="o">=</span> <span class="n">g_file_new_for_path</span> <span class="p">(</span><span class="s2">&quot;/foo/bar.svg&quot;</span><span class="p">);</span> Err codemadness.org 70 i 4484 <span class="n">stream</span> <span class="o">=</span> <span class="n">g_file_read</span> <span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="o">...</span><span class="p">);</span> Err codemadness.org 70 i 4485 <span class="n">handle</span> <span class="o">=</span> <span class="n">rsvg_handle_new</span> <span class="p">();</span> Err codemadness.org 70 i 4486 Err codemadness.org 70 i 4487 <span class="n">rsvg_handle_read_stream_sync</span> <span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">stream</span><span class="p">,</span> <span class="o">...</span><span class="p">);</span> Err codemadness.org 70 i 4488 Err codemadness.org 70 i 4489 <span class="o">//</span> <span class="n">now</span> <span class="n">the</span> <span class="n">handle</span> <span class="k">is</span> <span class="n">fully</span> <span class="n">loaded</span> <span class="ow">and</span> <span class="n">immutable</span> Err codemadness.org 70 i 4490 Err codemadness.org 70 i 4491 <span class="n">rsvg_handle_render</span> <span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="o">...</span><span class="p">);</span> Err codemadness.org 70 i 4492 </code></pre></div> Err codemadness.org 70 i 4493 Err codemadness.org 70 i 4494 <h2>A bit of history</h2> Err codemadness.org 70 i 4495 <p>Let's consider a few of <code>RsvgHandle</code>'s functions.</p> Err codemadness.org 70 i 4496 <p><strong>Constructors:</strong></p> Err codemadness.org 70 i 4497 <ul> Err codemadness.org 70 i 4498 <li><code>rsvg_handle_new()</code></li> Err codemadness.org 70 i 4499 <li><code>rsvg_handle_new_with_flags()</code></li> Err codemadness.org 70 i 4500 </ul> Err codemadness.org 70 i 4501 <p><strong>Configure the handle for loading:</strong></p> Err codemadness.org 70 i 4502 <ul> Err codemadness.org 70 i 4503 <li><code>rsvg_handle_set_base_uri()</code></li> Err codemadness.org 70 i 4504 <li><code>rsvg_handle_set_base_gfile()</code></li> Err codemadness.org 70 i 4505 </ul> Err codemadness.org 70 i 4506 <p><strong>Deprecated loading API:</strong></p> Err codemadness.org 70 i 4507 <ul> Err codemadness.org 70 i 4508 <li><code>rsvg_handle_write()</code></li> Err codemadness.org 70 i 4509 <li><code>rsvg_handle_close()</code></li> Err codemadness.org 70 i 4510 </ul> Err codemadness.org 70 i 4511 <p><strong>Streaming API:</strong></p> Err codemadness.org 70 i 4512 <ul> Err codemadness.org 70 i 4513 <li><code>rsvg_handle_read_stream_sync()</code></li> Err codemadness.org 70 i 4514 </ul> Err codemadness.org 70 i 4515 <p>When librsvg first acquired the concept of an <code>RsvgHandle</code>, it just Err codemadness.org 70 i 4516 had <code>rsvg_handle_new()</code> with no arguments. About 9 years later, it Err codemadness.org 70 i 4517 got <code>rsvg_handle_new_with_flags()</code> to allow more options, but it took Err codemadness.org 70 i 4518 another 2 years to actually add some usable flags — the first one was Err codemadness.org 70 i 4519 to configure the parsing limits in the underlying calls to libxml2.</p> Err codemadness.org 70 i 4520 <p>About 3 years after <code>RsvgHandle</code> appeared, it got Err codemadness.org 70 i 4521 <code>rsvg_handle_set_base_uri()</code> to configure the "base URI" against which Err codemadness.org 70 i 4522 relative references in the SVG document get resolved. For example, if Err codemadness.org 70 i 4523 you are reading <code>/foo/bar.svg</code> and it contains an element like <code>&lt;image Err codemadness.org 70 i 4524 xlink:ref="smiley.png"/&gt;</code>, then librsvg needs to be able to produce Err codemadness.org 70 i 4525 the path <code>/foo/smiley.png</code> and that is done relative to the base URI. Err codemadness.org 70 i 4526 (The base URI is implicit when reading from a specific SVG file, but Err codemadness.org 70 i 4527 it needs to be provided when reading from an arbitrary stream that may Err codemadness.org 70 i 4528 not even come from a file.)</p> Err codemadness.org 70 i 4529 <p>Initially <code>RsvgHandle</code> had the <code>write/close</code> APIs, and 8 years later Err codemadness.org 70 i 4530 it got the streaming functions once GIO appeared. Eventually the Err codemadness.org 70 i 4531 streaming API would be the preferred one, instead of just being a Err codemadness.org 70 i 4532 convenience for those brave new apps that started using GIO.</p> Err codemadness.org 70 i 4533 <p>A summary of librsvg's API may be something like:</p> Err codemadness.org 70 i 4534 <ul> Err codemadness.org 70 i 4535 <li> Err codemadness.org 70 i 4536 <p>librsvg gets written initially; it doesn't even have an Err codemadness.org 70 i 4537 <code>RsvgHandle</code>, and just provides a single function which takes a Err codemadness.org 70 i 4538 <code>FILE *</code> and renders it to a <code>GdkPixbuf</code>.</p> Err codemadness.org 70 i 4539 </li> Err codemadness.org 70 i 4540 <li> Err codemadness.org 70 i 4541 <p>That gets replaced with <code>RsvgHandle</code>, its single <code>rsvg_handle_new()</code> Err codemadness.org 70 i 4542 constructor, and the <code>write/close</code> API to feed it data Err codemadness.org 70 i 4543 progressively.</p> Err codemadness.org 70 i 4544 </li> Err codemadness.org 70 i 4545 <li> Err codemadness.org 70 i 4546 <p>GIO appears, we get the first widespread streaming APIs in GNOME, Err codemadness.org 70 i 4547 and <code>RsvgHandle</code> gets the ability to read from streams.</p> Err codemadness.org 70 i 4548 </li> Err codemadness.org 70 i 4549 <li> Err codemadness.org 70 i 4550 <p><code>RsvgHandle</code> gets <code>rsvg_handle_new_with_flags()</code> because now apps Err codemadness.org 70 i 4551 may want to configure extra stuff for libxml2.</p> Err codemadness.org 70 i 4552 </li> Err codemadness.org 70 i 4553 <li> Err codemadness.org 70 i 4554 <p>When Cairo appears and librsvg is ported to it, <code>RsvgHandle</code> gets an Err codemadness.org 70 i 4555 extra flag so that SVGs rendered to PDF can embed image data Err codemadness.org 70 i 4556 efficiently.</p> Err codemadness.org 70 i 4557 </li> Err codemadness.org 70 i 4558 </ul> Err codemadness.org 70 i 4559 <p>It's a convoluted history, but <code>git log -- rsvg.h</code> makes it accessible.</p> Err codemadness.org 70 i 4560 <h2>Where is the mutability?</h2> Err codemadness.org 70 i 4561 <p>An <code>RsvgHandle</code> gets created, with flags or without. It's empty, and Err codemadness.org 70 i 4562 doesn't know if it will be given data with the <code>write/close</code> API or Err codemadness.org 70 i 4563 with the streaming API. Also, someone may call <code>set_base_uri()</code> on Err codemadness.org 70 i 4564 it. So, the handle must remain mutable while it is being populated Err codemadness.org 70 i 4565 with data. After that, it can say, "no more changes, I'm done".</p> Err codemadness.org 70 i 4566 <p>In C, this doesn't even have a name. Everything is mutable by default Err codemadness.org 70 i 4567 all the time. This monster was the private data of <code>RsvgHandle</code> Err codemadness.org 70 i 4568 before it got ported to Rust:</p> Err codemadness.org 70 i 4569 <div class="highlight"><pre><span></span><code><span class="k">struct</span> <span class="nc">RsvgHandlePrivate</span> <span class="p">{</span> Err codemadness.org 70 i 4570 <span class="c1">// set during construction</span> Err codemadness.org 70 i 4571 <span class="n">RsvgHandleFlags</span> <span class="n">flags</span><span class="p">;</span> Err codemadness.org 70 i 4572 Err codemadness.org 70 i 4573 <span class="c1">// GObject-ism</span> Err codemadness.org 70 i 4574 <span class="n">gboolean</span> <span class="n">is_disposed</span><span class="p">;</span> Err codemadness.org 70 i 4575 Err codemadness.org 70 i 4576 <span class="c1">// Extra crap for a deprecated API</span> Err codemadness.org 70 i 4577 <span class="n">RsvgSizeFunc</span> <span class="n">size_func</span><span class="p">;</span> Err codemadness.org 70 i 4578 <span class="n">gpointer</span> <span class="n">user_data</span><span class="p">;</span> Err codemadness.org 70 i 4579 <span class="n">GDestroyNotify</span> <span class="n">user_data_destroy</span><span class="p">;</span> Err codemadness.org 70 i 4580 Err codemadness.org 70 i 4581 <span class="c1">// Data only used while parsing an SVG</span> Err codemadness.org 70 i 4582 <span class="n">RsvgHandleState</span> <span class="n">state</span><span class="p">;</span> Err codemadness.org 70 i 4583 <span class="n">RsvgDefs</span> <span class="o">*</span><span class="n">defs</span><span class="p">;</span> Err codemadness.org 70 i 4584 <span class="n">guint</span> <span class="n">nest_level</span><span class="p">;</span> Err codemadness.org 70 i 4585 <span class="n">RsvgNode</span> <span class="o">*</span><span class="n">currentnode</span><span class="p">;</span> Err codemadness.org 70 i 4586 <span class="n">RsvgNode</span> <span class="o">*</span><span class="n">treebase</span><span class="p">;</span> Err codemadness.org 70 i 4587 <span class="n">GHashTable</span> <span class="o">*</span><span class="n">css_props</span><span class="p">;</span> Err codemadness.org 70 i 4588 <span class="n">RsvgSaxHandler</span> <span class="o">*</span><span class="n">handler</span><span class="p">;</span> Err codemadness.org 70 i 4589 <span class="kt">int</span> <span class="n">handler_nest</span><span class="p">;</span> Err codemadness.org 70 i 4590 <span class="n">GHashTable</span> <span class="o">*</span><span class="n">entities</span><span class="p">;</span> Err codemadness.org 70 i 4591 <span class="n">xmlParserCtxtPtr</span> <span class="n">ctxt</span><span class="p">;</span> Err codemadness.org 70 i 4592 <span class="n">GError</span> <span class="o">**</span><span class="n">error</span><span class="p">;</span> Err codemadness.org 70 i 4593 <span class="n">GCancellable</span> <span class="o">*</span><span class="n">cancellable</span><span class="p">;</span> Err codemadness.org 70 i 4594 <span class="n">GInputStream</span> <span class="o">*</span><span class="n">compressed_input_stream</span><span class="p">;</span> Err codemadness.org 70 i 4595 Err codemadness.org 70 i 4596 <span class="c1">// Data only used while rendering</span> Err codemadness.org 70 i 4597 <span class="kt">double</span> <span class="n">dpi_x</span><span class="p">;</span> Err codemadness.org 70 i 4598 <span class="kt">double</span> <span class="n">dpi_y</span><span class="p">;</span> Err codemadness.org 70 i 4599 Err codemadness.org 70 i 4600 <span class="c1">// The famous base URI, set before loading</span> Err codemadness.org 70 i 4601 <span class="n">gchar</span> <span class="o">*</span><span class="n">base_uri</span><span class="p">;</span> Err codemadness.org 70 i 4602 <span class="n">GFile</span> <span class="o">*</span><span class="n">base_gfile</span><span class="p">;</span> Err codemadness.org 70 i 4603 Err codemadness.org 70 i 4604 <span class="c1">// Some internal stuff</span> Err codemadness.org 70 i 4605 <span class="n">gboolean</span> <span class="n">in_loop</span><span class="p">;</span> Err codemadness.org 70 i 4606 <span class="n">gboolean</span> <span class="n">is_testing</span><span class="p">;</span> Err codemadness.org 70 i 4607 <span class="p">};</span> Err codemadness.org 70 i 4608 </code></pre></div> Err codemadness.org 70 i 4609 Err codemadness.org 70 i 4610 <p>"Single responsibility principle"? This is a horror show. That Err codemadness.org 70 i 4611 <code>RsvgHandlePrivate</code> struct has all of these:</p> Err codemadness.org 70 i 4612 <ul> Err codemadness.org 70 i 4613 <li>Data only settable during construction (flags)</li> Err codemadness.org 70 i 4614 <li>Data set after construction, but which may only be set before Err codemadness.org 70 i 4615 loading (base URI)</li> Err codemadness.org 70 i 4616 <li>Highly mutable data used only during the loading stage: state Err codemadness.org 70 i 4617 machines, XML parsers, a stack of XML elements, CSS properties...</li> Err codemadness.org 70 i 4618 <li>The DPI (dots per inch) values only used during rendering.</li> Err codemadness.org 70 i 4619 <li>Assorted fields used at various stages of the handle's life.</li> Err codemadness.org 70 i 4620 </ul> Err codemadness.org 70 i 4621 <p>It took a lot of refactoring to get the code to a point where it was Err codemadness.org 70 i 4622 clear that an <code>RsvgHandle</code> in fact has distinct stages during its Err codemadness.org 70 i 4623 lifetime, and that some of that data should only live during a Err codemadness.org 70 i 4624 particular stage. Before, everything seemed a jumble of fields, used Err codemadness.org 70 i 4625 at various unclear points in the code (for the struct listing above, Err codemadness.org 70 i 4626 I've grouped related fields together — they were somewhat shuffled in Err codemadness.org 70 i 4627 the original code!).</p> Err codemadness.org 70 i 4628 <h2>What would a better separation look like?</h2> Err codemadness.org 70 i 4629 <p>In the <a href="https://gitlab.gnome.org/GNOME/librsvg/">master branch</a>, now librsvg has this:</p> Err codemadness.org 70 i 4630 <div class="highlight"><pre><span></span><code><span class="sd">/// Contains all the interior mutability for a RsvgHandle to be called</span> Err codemadness.org 70 i 4631 <span class="sd">/// from the C API.</span> Err codemadness.org 70 i 4632 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">CHandle</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4633 <span class="w"> </span><span class="n">dpi</span>: <span class="nc">Cell</span><span class="o">&lt;</span><span class="n">Dpi</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4634 <span class="w"> </span><span class="n">load_flags</span>: <span class="nc">Cell</span><span class="o">&lt;</span><span class="n">LoadFlags</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4635 Err codemadness.org 70 i 4636 <span class="w"> </span><span class="n">base_url</span>: <span class="nc">RefCell</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="n">Url</span><span class="o">&gt;&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4637 <span class="w"> </span><span class="c1">// needed because the C api returns *const char</span> Err codemadness.org 70 i 4638 <span class="w"> </span><span class="n">base_url_cstring</span>: <span class="nc">RefCell</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="n">CString</span><span class="o">&gt;&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4639 Err codemadness.org 70 i 4640 <span class="w"> </span><span class="n">size_callback</span>: <span class="nc">RefCell</span><span class="o">&lt;</span><span class="n">SizeCallback</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4641 <span class="w"> </span><span class="n">is_testing</span>: <span class="nc">Cell</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4642 <span class="w"> </span><span class="n">load_state</span>: <span class="nc">RefCell</span><span class="o">&lt;</span><span class="n">LoadState</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4643 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4644 </code></pre></div> Err codemadness.org 70 i 4645 Err codemadness.org 70 i 4646 <p>Internally, that <code>CHandle</code> struct is now the private data of the Err codemadness.org 70 i 4647 public <code>RsvgHandle</code> object. Note that all of <code>CHandle</code>'s fields are a Err codemadness.org 70 i 4648 <code>Cell&lt;&gt;</code> or <code>RefCell&lt;&gt;</code>: in Rust terms, this means that those fields Err codemadness.org 70 i 4649 allow for "interior mutability" in the <code>CHandle</code> struct: they can be Err codemadness.org 70 i 4650 modified after intialization.</p> Err codemadness.org 70 i 4651 <p>The last field's cell, <code>load_state</code>, contains this type:</p> Err codemadness.org 70 i 4652 <div class="highlight"><pre><span></span><code><span class="k">enum</span> <span class="nc">LoadState</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4653 <span class="w"> </span><span class="n">Start</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4654 Err codemadness.org 70 i 4655 <span class="w"> </span><span class="c1">// Being loaded using the legacy write()/close() API</span> Err codemadness.org 70 i 4656 <span class="w"> </span><span class="n">Loading</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">buffer</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u8</span><span class="o">&gt;</span><span class="w"> </span><span class="p">},</span><span class="w"></span> Err codemadness.org 70 i 4657 Err codemadness.org 70 i 4658 <span class="w"> </span><span class="c1">// Fully loaded, with a Handle to an SVG document</span> Err codemadness.org 70 i 4659 <span class="w"> </span><span class="n">ClosedOk</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">handle</span>: <span class="nc">Handle</span><span class="w"> </span><span class="p">},</span><span class="w"></span> Err codemadness.org 70 i 4660 Err codemadness.org 70 i 4661 <span class="w"> </span><span class="n">ClosedError</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4662 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4663 </code></pre></div> Err codemadness.org 70 i 4664 Err codemadness.org 70 i 4665 <p>A <code>CHandle</code> starts in the <code>Start</code> state, where it doesn't know if it Err codemadness.org 70 i 4666 will be loaded with a stream, or with the legacy write/close API.</p> Err codemadness.org 70 i 4667 <p>If the caller uses the write/close API, the handle moves to the Err codemadness.org 70 i 4668 <code>Loading</code> state, which has a <code>buffer</code> where it accumulates the data Err codemadness.org 70 i 4669 being fed to it.</p> Err codemadness.org 70 i 4670 <p>But if the caller uses the stream API, the handle tries to parse an Err codemadness.org 70 i 4671 SVG document from the stream, and it moves either to the <code>ClosedOk</code> Err codemadness.org 70 i 4672 state, or to the <code>ClosedError</code> state if there is a parse error.</p> Err codemadness.org 70 i 4673 <p>Correspondingly, when using the write/close API, when the caller Err codemadness.org 70 i 4674 finally calls <code>rsvg_handle_close()</code>, the handle creates a stream for Err codemadness.org 70 i 4675 the <code>buffer</code>, parses it, and also gets either into the <code>ClosedOk</code> or Err codemadness.org 70 i 4676 <code>ClosedError</code> state.</p> Err codemadness.org 70 i 4677 <p>If you look at the variant <code>ClosedOk { handle: Handle }</code>, it contains Err codemadness.org 70 i 4678 a fully loaded <code>Handle</code> inside, which right now is just a wrapper Err codemadness.org 70 i 4679 around a reference-counted <code>Svg</code> object:</p> Err codemadness.org 70 i 4680 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Handle</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4681 <span class="w"> </span><span class="n">svg</span>: <span class="nc">Rc</span><span class="o">&lt;</span><span class="n">Svg</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4682 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4683 </code></pre></div> Err codemadness.org 70 i 4684 Err codemadness.org 70 i 4685 <p>The reason why <code>LoadState::ClosedOk</code> does not contain an <code>Rc&lt;Svg&gt;</code> Err codemadness.org 70 i 4686 directly, and instead wraps it with a <code>Handle</code>, is that this is just Err codemadness.org 70 i 4687 the first pass at refactoring. Also, <code>Handle</code> contains some Err codemadness.org 70 i 4688 API-level logic which I'm not completely sure makes sense as a Err codemadness.org 70 i 4689 lower-level <code>Svg</code> object. We'll see.</p> Err codemadness.org 70 i 4690 <h2>Couldn't you move more of <code>CHandle</code>'s fields into <code>LoadState</code>?</h2> Err codemadness.org 70 i 4691 <p>Sort of, kind of, but the public API still lets one do things like Err codemadness.org 70 i 4692 call <code>rsvg_handle_get_base_uri()</code> after the handle is fully loaded, Err codemadness.org 70 i 4693 even though its result will be of little value. So, the fields that Err codemadness.org 70 i 4694 hold the <code>base_uri</code> information are kept in the longer-lived Err codemadness.org 70 i 4695 <code>CHandle</code>, not in the individual variants of <code>LoadState</code>.</p> Err codemadness.org 70 i 4696 <h2>How does this look from the Rust API?</h2> Err codemadness.org 70 i 4697 <p><code>CHandle</code> implements the public C API of librsvg. Internally, Err codemadness.org 70 i 4698 <code>Handle</code> implements the basic "load from stream", "get the geometry of Err codemadness.org 70 i 4699 an SVG element", and "render to a Cairo context" functionality.</p> Err codemadness.org 70 i 4700 <p>This basic functionality gets exported in a cleaner way through the Err codemadness.org 70 i 4701 <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/master/librsvg_crate/src/lib.rs">Rust API</a>, discussed <a href="https://people.gnome.org/~federico/blog/a-rust-api-for-librsvg.html">previously</a>. There is no Err codemadness.org 70 i 4702 interior mutability in there at all; that API uses a builder pattern Err codemadness.org 70 i 4703 to gradually configure an SVG loader, which returns a fully loaded Err codemadness.org 70 i 4704 <code>SvgHandle</code>, out of which you can create a <code>CairoRenderer</code>.</p> Err codemadness.org 70 i 4705 <p>In fact, it may be possible to refactor all of this a bit and Err codemadness.org 70 i 4706 implement <code>CHandle</code> directly in terms of the new Rust API: in effect, Err codemadness.org 70 i 4707 use <code>CHandle</code> as the "holding space" while the SVG loader gets Err codemadness.org 70 i 4708 configured, and later turned into a fully loaded <code>SvgHandle</code> Err codemadness.org 70 i 4709 internally.</p> Err codemadness.org 70 i 4710 <h2>Conclusion</h2> Err codemadness.org 70 i 4711 <p>The C version of <code>RsvgHandle</code>'s private structure used to have a bunch Err codemadness.org 70 i 4712 of fields. Without knowing the code, it was hard to know that they Err codemadness.org 70 i 4713 belonged in groups, and each group corresponded roughtly to a stage in Err codemadness.org 70 i 4714 the handle's lifetime.</p> Err codemadness.org 70 i 4715 <p>It took plenty of refactoring to get the fields split up cleanly in Err codemadness.org 70 i 4716 librsvg's internals. The process of refactoring <code>RsvgHandle</code>'s fields, Err codemadness.org 70 i 4717 and ensuring that the various states of a handle are consistent, in Err codemadness.org 70 i 4718 fact exposed a few bugs where the state was not being checked Err codemadness.org 70 i 4719 appropriately. The public C API remains the same as always, but has Err codemadness.org 70 i 4720 better internal checks now.</p> Err codemadness.org 70 i 4721 <p>GObject APIs tend to allow for a lot of mutability via methods that Err codemadness.org 70 i 4722 change the internal state of objects. For <code>RsvgHandle</code>, it was possible Err codemadness.org 70 i 4723 to change this into a single <code>CHandle</code> that maintains the mutable data Err codemadness.org 70 i 4724 in a contained fashion, and later translates it internally into an Err codemadness.org 70 i 4725 immutable <code>Handle</code> that represents a fully-loaded SVG document. This Err codemadness.org 70 i 4726 scheme ties in well with the new Rust API for librsvg, which keeps Err codemadness.org 70 i 4727 everything immutable after creation.</p>A Rust API for librsvg2019-03-15T13:36:47-06:002019-03-15T13:36:47-06:00Federico Mena Quinterotag:people.gnome.org,2019-03-15:/~federico/blog/a-rust-api-for-librsvg.html<p>After the librsvg team <a href="https://people.gnome.org/~federico/blog/librsvg-gobject-in-rust.html">finished the rustification</a> of Err codemadness.org 70 i 4728 librsvg's main library, I wanted to start porting the high-level test Err codemadness.org 70 i 4729 suite to Rust. This is mainly to be able to run tests in parallel, Err codemadness.org 70 i 4730 which <code>cargo test</code> does automatically in order to reduce test times. Err codemadness.org 70 i 4731 However, this meant that librsvg needed …</p><p>After the librsvg team <a href="https://people.gnome.org/~federico/blog/librsvg-gobject-in-rust.html">finished the rustification</a> of Err codemadness.org 70 i 4732 librsvg's main library, I wanted to start porting the high-level test Err codemadness.org 70 i 4733 suite to Rust. This is mainly to be able to run tests in parallel, Err codemadness.org 70 i 4734 which <code>cargo test</code> does automatically in order to reduce test times. Err codemadness.org 70 i 4735 However, this meant that librsvg needed a Rust API that would exercise Err codemadness.org 70 i 4736 the same code paths as the C entry points.</p> Err codemadness.org 70 i 4737 <p>At the same time, I wanted the Rust API to make it impossible to Err codemadness.org 70 i 4738 misuse the library. From the viewpoint of the C API, an <code>RsvgHandle</code> Err codemadness.org 70 i 4739 has different stages:</p> Err codemadness.org 70 i 4740 <ul> Err codemadness.org 70 i 4741 <li>Just initialized</li> Err codemadness.org 70 i 4742 <li>Loading</li> Err codemadness.org 70 i 4743 <li>Loaded, or in an error state after a failed load</li> Err codemadness.org 70 i 4744 <li>Ready to render</li> Err codemadness.org 70 i 4745 </ul> Err codemadness.org 70 i 4746 <p>To ensure consistency, the public API checks that you cannot render an Err codemadness.org 70 i 4747 <code>RsvgHandle</code> that is not completely loaded yet, or one that resulted Err codemadness.org 70 i 4748 in a loading error. But wouldn't it be nice if it were impossible to Err codemadness.org 70 i 4749 call the API functions in the wrong order?</p> Err codemadness.org 70 i 4750 <p>This is exactly what <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/master/librsvg_crate/src/lib.rs">the Rust API</a> does. There is a <code>Loader</code>, Err codemadness.org 70 i 4751 to which you give a filename or a stream, and it will return a Err codemadness.org 70 i 4752 fully-loaded <code>SvgHandle</code> or an error. Then, you can only create a Err codemadness.org 70 i 4753 <code>CairoRenderer</code> if you have an <code>SvgHandle</code>.</p> Err codemadness.org 70 i 4754 <p>For historical reasons, the C API in librsvg is not perfectly Err codemadness.org 70 i 4755 consistent. For example, some functions which return an error will Err codemadness.org 70 i 4756 actually return a proper <a href="https://developer.gnome.org/glib/stable/glib-Error-Reporting.html"><code>GError</code></a>, but some others will just Err codemadness.org 70 i 4757 return a <code>gboolean</code> with no further explanation of what went wrong. Err codemadness.org 70 i 4758 In contrast, all the Rust API functions that can fail will actually Err codemadness.org 70 i 4759 return a <a href="https://doc.rust-lang.org/std/result/index.html"><code>Result</code></a>, and the error case will have a meaningful Err codemadness.org 70 i 4760 error value. In the Rust API, there is no "wrong order" in which the Err codemadness.org 70 i 4761 various API functions and methods can be called; it tries to do the Err codemadness.org 70 i 4762 whole "make invalid states unrepresentable".</p> Err codemadness.org 70 i 4763 <p>To implement <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/master/librsvg_crate/src/lib.rs">the Rust API</a>, I had to do some refactoring of the Err codemadness.org 70 i 4764 <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/master/rsvg_internals/src/handle.rs">internals</a> that hook to the public entry points. This made me Err codemadness.org 70 i 4765 realize that librsvg could be a lot easier to use. The C API has Err codemadness.org 70 i 4766 always forced you to call it in this fashion:</p> Err codemadness.org 70 i 4767 <ol> Err codemadness.org 70 i 4768 <li>Ask the SVG for its dimensions, or how big it is.</li> Err codemadness.org 70 i 4769 <li>Based on that, scale your Cairo context to the size you actually Err codemadness.org 70 i 4770 want.</li> Err codemadness.org 70 i 4771 <li>Render the SVG to that context's current transformation matrix.</li> Err codemadness.org 70 i 4772 </ol> Err codemadness.org 70 i 4773 <p>But first, (1) gives you inadequate information because Err codemadness.org 70 i 4774 <code>rsvg_handle_get_dimensions()</code> returns <a href="https://developer.gnome.org/rsvg/unstable/rsvg-RsvgHandle.html#RsvgDimensionData">a Err codemadness.org 70 i 4775 structure</a> with <code>int</code> fields for the width and Err codemadness.org 70 i 4776 height. The API is similar to gdk-pixbuf's in that it always wants to Err codemadness.org 70 i 4777 think in whole pixels. However, an SVG is not necessarily Err codemadness.org 70 i 4778 integer-sized.</p> Err codemadness.org 70 i 4779 <p>Then, (2) forces you to calculate some geometry in almost all cases, Err codemadness.org 70 i 4780 as most apps want to render SVG content scaled proportionally to a Err codemadness.org 70 i 4781 certain size. This is not hard to do, but it's an inconvenience.</p> Err codemadness.org 70 i 4782 <h2>SVG dimensions</h2> Err codemadness.org 70 i 4783 <p>Let's look at (1) again. The question, "how big is the SVG" is a bit Err codemadness.org 70 i 4784 meaningless when we consider that SVGs <strong>can be scaled to any size</strong>; Err codemadness.org 70 i 4785 that's the whole point of them!</p> Err codemadness.org 70 i 4786 <p>When you ask <code>RsvgHandle</code> how big it is, in reality it should look at Err codemadness.org 70 i 4787 you and whisper in your ear, "how big do you want it to be?".</p> Err codemadness.org 70 i 4788 <p>And that's the thing. The HTML/CSS/SVG model is that one embeds Err codemadness.org 70 i 4789 content into <strong>viewports</strong> of a given size. The software is Err codemadness.org 70 i 4790 responsible for scaling the content to fit into that viewport.</p> Err codemadness.org 70 i 4791 <p>In the end, what we want is a rendering function that takes a Cairo Err codemadness.org 70 i 4792 context and a Rectangle for a viewport, and that's it. The function Err codemadness.org 70 i 4793 should take care of fitting the SVG's contents within that viewport.</p> Err codemadness.org 70 i 4794 <p>There is now <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/448">an open bug</a> about exactly this sort of API. In Err codemadness.org 70 i 4795 the end, programs should just have to load their SVG handle, and Err codemadness.org 70 i 4796 directly ask it to render at whatever size they need, instead of doing Err codemadness.org 70 i 4797 the size computations by hand.</p> Err codemadness.org 70 i 4798 <h2>When will this be available?</h2> Err codemadness.org 70 i 4799 <p>I'm in the middle of a <a href="https://gitlab.gnome.org/federico/librsvg/commits/viewport-with-offsets">rather large refactor</a> Err codemadness.org 70 i 4800 to make this <em>viewport</em> concept really work. So far this involves:</p> Err codemadness.org 70 i 4801 <ul> Err codemadness.org 70 i 4802 <li> Err codemadness.org 70 i 4803 <p>Defining APIs that take a viewport.</p> Err codemadness.org 70 i 4804 </li> Err codemadness.org 70 i 4805 <li> Err codemadness.org 70 i 4806 <p>Refactoring all the geometry computation to support the semantics of the Err codemadness.org 70 i 4807 C API, plus the new <code>with_viewport</code> semantics.</p> Err codemadness.org 70 i 4808 </li> Err codemadness.org 70 i 4809 <li> Err codemadness.org 70 i 4810 <p>Fixing the code that kept track of an internal offset for all Err codemadness.org 70 i 4811 temporary images.</p> Err codemadness.org 70 i 4812 </li> Err codemadness.org 70 i 4813 <li> Err codemadness.org 70 i 4814 <p>Refactoring all the code that mucks around with the Cairo context's Err codemadness.org 70 i 4815 affine transformation matrix, which is a big mutable mess.</p> Err codemadness.org 70 i 4816 </li> Err codemadness.org 70 i 4817 <li> Err codemadness.org 70 i 4818 <p>Tests, examples, documentation.</p> Err codemadness.org 70 i 4819 </li> Err codemadness.org 70 i 4820 </ul> Err codemadness.org 70 i 4821 <p>I want to make the Rust API available for the 2.46 release, which is Err codemadness.org 70 i 4822 hopefully not too far off. It should be ready for the next GNOME Err codemadness.org 70 i 4823 release. In the meantime, you can check out the open bugs for the Err codemadness.org 70 i 4824 <a href="https://gitlab.gnome.org/GNOME/librsvg/milestones/20">2.46.0 milestone</a>. <strong>Help is appreciated; the deadline Err codemadness.org 70 i 4825 for the first 3.33 tarballs is approximately one month from now!</strong></p>Rust build scripts vs. Meson2019-02-27T12:14:12-06:002019-02-27T12:14:12-06:00Federico Mena Quinterotag:people.gnome.org,2019-02-27:/~federico/blog/rust-build-scripts.html<p>One of the pain points in trying to make the <a href="https://mesonbuild.com/">Meson</a> build system work Err codemadness.org 70 i 4826 with Rust and Cargo is Cargo's use of build scripts, i.e. the Err codemadness.org 70 i 4827 <code>build.rs</code> that many Rust programs use for doing things before the Err codemadness.org 70 i 4828 main build. This post is about my exploration of what <code>build …</code></p><p>One of the pain points in trying to make the <a href="https://mesonbuild.com/">Meson</a> build system work Err codemadness.org 70 i 4829 with Rust and Cargo is Cargo's use of build scripts, i.e. the Err codemadness.org 70 i 4830 <code>build.rs</code> that many Rust programs use for doing things before the Err codemadness.org 70 i 4831 main build. This post is about my exploration of what <code>build.rs</code> Err codemadness.org 70 i 4832 does.</p> Err codemadness.org 70 i 4833 <p>Thanks to <a href="https://nirbheek.in/">Nirbheek Chauhan</a> for his comments Err codemadness.org 70 i 4834 and additions to a draft of this article!</p> Err codemadness.org 70 i 4835 <p>TL;DR: <code>build.rs</code> is pretty ad-hoc and somewhat primitive, when Err codemadness.org 70 i 4836 compared to Meson's very nice, high-level patterns for build-time Err codemadness.org 70 i 4837 things.</p> Err codemadness.org 70 i 4838 <p>I have the intuition that giving names to the things that are Err codemadness.org 70 i 4839 usually done in <code>build.rs</code> scripts, and creating abstractions for Err codemadness.org 70 i 4840 them, can make it easier later to implement those abstractions in Err codemadness.org 70 i 4841 terms of Meson. Maybe we can eliminate <code>build.rs</code> in most cases? Err codemadness.org 70 i 4842 Maybe Cargo can acquire higher-level concepts that plug well to Meson?</p> Err codemadness.org 70 i 4843 <p>(That is... I think we can refactor our way out of this mess.)</p> Err codemadness.org 70 i 4844 <h2>What does <code>build.rs</code> do?</h2> Err codemadness.org 70 i 4845 <p>The first paragraph in the <a href="https://doc.rust-lang.org/cargo/reference/build-scripts.html">documentation for Cargo build Err codemadness.org 70 i 4846 scripts</a> tells us this:</p> Err codemadness.org 70 i 4847 <blockquote> Err codemadness.org 70 i 4848 <p>Some packages need to compile third-party non-Rust code, for example Err codemadness.org 70 i 4849 C libraries. Other packages need to link to C libraries which can Err codemadness.org 70 i 4850 either be located on the system or possibly need to be built from Err codemadness.org 70 i 4851 source. Others still need facilities for functionality such as code Err codemadness.org 70 i 4852 generation before building (think parser generators).</p> Err codemadness.org 70 i 4853 </blockquote> Err codemadness.org 70 i 4854 <p>That is,</p> Err codemadness.org 70 i 4855 <ul> Err codemadness.org 70 i 4856 <li> Err codemadness.org 70 i 4857 <p>Compiling third-party non-Rust code. For example, maybe there is a Err codemadness.org 70 i 4858 C sub-library that the Rust crate needs.</p> Err codemadness.org 70 i 4859 </li> Err codemadness.org 70 i 4860 <li> Err codemadness.org 70 i 4861 <p>Link to C libraries... located on the system... or built from Err codemadness.org 70 i 4862 source. For example, in <a href="https://gtk-rs.org">gtk-rs</a>, the <a href="https://github.com/gtk-rs/sys">sys</a> crates link to Err codemadness.org 70 i 4863 <code>libgtk-3.so</code>, <code>libcairo.so</code>, etc. and need to find a way to locate Err codemadness.org 70 i 4864 those libraries with <code>pkg-config</code>.</p> Err codemadness.org 70 i 4865 </li> Err codemadness.org 70 i 4866 <li> Err codemadness.org 70 i 4867 <p>Code generation. In the C world this could be generating a parser Err codemadness.org 70 i 4868 with <code>yacc</code>; in the Rust world there are many utilities to generate Err codemadness.org 70 i 4869 code that is later used in your actual program.</p> Err codemadness.org 70 i 4870 </li> Err codemadness.org 70 i 4871 </ul> Err codemadness.org 70 i 4872 <p>In the next sections I'll look briefly at each of these cases, but in Err codemadness.org 70 i 4873 a different order.</p> Err codemadness.org 70 i 4874 <h2>Code generation</h2> Err codemadness.org 70 i 4875 <p>Here is an example, in <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/a5c8a9ca/rsvg_internals/build.rs">how librsvg generates code</a> Err codemadness.org 70 i 4876 for a couple of things that get autogenerated before compiling the Err codemadness.org 70 i 4877 main library:</p> Err codemadness.org 70 i 4878 <ul> Err codemadness.org 70 i 4879 <li>A perfect hash function (PHF) of attributes and CSS property names.</li> Err codemadness.org 70 i 4880 <li>A pair of lookup tables for SRGB linearization and un-linearization.</li> Err codemadness.org 70 i 4881 </ul> Err codemadness.org 70 i 4882 <p>For example, this is <code>main()</code> in <code>build.rs</code>:</p> Err codemadness.org 70 i 4883 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4884 <span class="w"> </span><span class="n">generate_phf_of_svg_attributes</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 4885 <span class="w"> </span><span class="n">generate_srgb_tables</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 4886 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4887 </code></pre></div> Err codemadness.org 70 i 4888 Err codemadness.org 70 i 4889 <p>And this is the first few lines of of the first function:</p> Err codemadness.org 70 i 4890 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">generate_phf_of_svg_attributes</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4891 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Path</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">env</span>::<span class="n">var</span><span class="p">(</span><span class="s">&quot;OUT_DIR&quot;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()).</span><span class="n">join</span><span class="p">(</span><span class="s">&quot;attributes-codegen.rs&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 4892 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BufWriter</span>::<span class="n">new</span><span class="p">(</span><span class="n">File</span>::<span class="n">create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">path</span><span class="p">).</span><span class="n">unwrap</span><span class="p">());</span><span class="w"></span> Err codemadness.org 70 i 4893 Err codemadness.org 70 i 4894 <span class="w"> </span><span class="fm">writeln!</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">file</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;#[repr(C)]&quot;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 4895 Err codemadness.org 70 i 4896 <span class="w"> </span><span class="c1">// ... etc</span> Err codemadness.org 70 i 4897 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4898 </code></pre></div> Err codemadness.org 70 i 4899 Err codemadness.org 70 i 4900 <p>Generate a path like <code>$OUT_DIR/attributes-codegen.rs</code>, create a file Err codemadness.org 70 i 4901 with that name, a <code>BufWriter</code> for the file, and start outputting code Err codemadness.org 70 i 4902 to it.</p> Err codemadness.org 70 i 4903 <p>Similarly, the second function:</p> Err codemadness.org 70 i 4904 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">generate_srgb_tables</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 4905 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">linearize_table</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">compute_table</span><span class="p">(</span><span class="n">linearize</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 4906 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">unlinearize_table</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">compute_table</span><span class="p">(</span><span class="n">unlinearize</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 4907 Err codemadness.org 70 i 4908 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Path</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">env</span>::<span class="n">var</span><span class="p">(</span><span class="s">&quot;OUT_DIR&quot;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()).</span><span class="n">join</span><span class="p">(</span><span class="s">&quot;srgb-codegen.rs&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 4909 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BufWriter</span>::<span class="n">new</span><span class="p">(</span><span class="n">File</span>::<span class="n">create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">path</span><span class="p">).</span><span class="n">unwrap</span><span class="p">());</span><span class="w"></span> Err codemadness.org 70 i 4910 Err codemadness.org 70 i 4911 <span class="w"> </span><span class="c1">// ...</span> Err codemadness.org 70 i 4912 Err codemadness.org 70 i 4913 <span class="w"> </span><span class="n">print_table</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">file</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;LINEARIZE&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">linearize_table</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 4914 <span class="w"> </span><span class="n">print_table</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">file</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;UNLINEARIZE&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">unlinearize_table</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 4915 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 4916 </code></pre></div> Err codemadness.org 70 i 4917 Err codemadness.org 70 i 4918 <p>Compute two lookup tables, create a file named Err codemadness.org 70 i 4919 <code>$OUT_DIR/srgb-codegen.rs</code>, and write the lookup tables to the file.</p> Err codemadness.org 70 i 4920 <p>Later in the actual librsvg code, the generated files get included Err codemadness.org 70 i 4921 into the source code using the <code>include!</code> macro. For example, here is Err codemadness.org 70 i 4922 where <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/a5c8a9ca/rsvg_internals/src/attributes.rs#L6"><code>attributes-codegen.rs</code> gets included</a>:</p> Err codemadness.org 70 i 4923 <div class="highlight"><pre><span></span><code><span class="c1">// attributes.rs</span> Err codemadness.org 70 i 4924 Err codemadness.org 70 i 4925 <span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">phf</span><span class="p">;</span><span class="w"> </span><span class="c1">// crate for perfect hash function</span> Err codemadness.org 70 i 4926 Err codemadness.org 70 i 4927 <span class="c1">// the generated file has the declaration for enum Attribute</span> Err codemadness.org 70 i 4928 <span class="fm">include!</span><span class="p">(</span><span class="fm">concat!</span><span class="p">(</span><span class="fm">env!</span><span class="p">(</span><span class="s">&quot;OUT_DIR&quot;</span><span class="p">),</span><span class="w"> </span><span class="s">&quot;/attributes-codegen.rs&quot;</span><span class="p">));</span><span class="w"></span> Err codemadness.org 70 i 4929 </code></pre></div> Err codemadness.org 70 i 4930 Err codemadness.org 70 i 4931 <p>One thing to note here is that the generated source files Err codemadness.org 70 i 4932 (<code>attributes-codegen.rs</code>, <code>srgb-codegen.rs</code>) get put in <code>$OUT_DIR</code>, a Err codemadness.org 70 i 4933 directory that Cargo creates for the compilation artifacts. The files <strong>do Err codemadness.org 70 i 4934 not</strong> get put into the original source directories with the rest of Err codemadness.org 70 i 4935 the library's code; the idea is to keep the source directories Err codemadness.org 70 i 4936 pristine.</p> Err codemadness.org 70 i 4937 <p>At least in those terms, Meson and Cargo agree that source directories Err codemadness.org 70 i 4938 should be kept clean of autogenerated files.</p> Err codemadness.org 70 i 4939 <p>The <a href="https://doc.rust-lang.org/cargo/reference/build-scripts.html#case-study-code-generation">Code Generation</a> section of Cargo's documentation agrees:</p> Err codemadness.org 70 i 4940 <blockquote> Err codemadness.org 70 i 4941 <p>In general, build scripts should not modify any files outside of Err codemadness.org 70 i 4942 OUT_DIR. It may seem fine on the first blush, but it does cause Err codemadness.org 70 i 4943 problems when you use such crate as a dependency, because there's an Err codemadness.org 70 i 4944 implicit invariant that sources in .cargo/registry should be Err codemadness.org 70 i 4945 immutable. cargo won't allow such scripts when packaging.</p> Err codemadness.org 70 i 4946 </blockquote> Err codemadness.org 70 i 4947 <p>Now, some things to note here:</p> Err codemadness.org 70 i 4948 <ul> Err codemadness.org 70 i 4949 <li> Err codemadness.org 70 i 4950 <p>Both the <code>build.rs</code> program and the actual library sources look at Err codemadness.org 70 i 4951 the <code>$OUT_DIR</code> environment variable for the location of the Err codemadness.org 70 i 4952 generated sources.</p> Err codemadness.org 70 i 4953 </li> Err codemadness.org 70 i 4954 <li> Err codemadness.org 70 i 4955 <p>The Cargo docs say that if the code generator needs input files, it Err codemadness.org 70 i 4956 can look for them based on its current directory, which will be the Err codemadness.org 70 i 4957 toplevel of your source package (i.e. your toplevel <code>Cargo.toml</code>).</p> Err codemadness.org 70 i 4958 </li> Err codemadness.org 70 i 4959 </ul> Err codemadness.org 70 i 4960 <p><strong>Meson hates this scheme of things</strong>. In particular, Meson is very Err codemadness.org 70 i 4961 systematic about where it finds input files and sources, and where Err codemadness.org 70 i 4962 things like code generators are allowed to place their output.</p> Err codemadness.org 70 i 4963 <p>The way Meson communicates these paths to code generators is via Err codemadness.org 70 i 4964 command-line arguments to <a href="https://mesonbuild.com/Reference-manual.html#custom_target">"custom targets"</a>. Here is Err codemadness.org 70 i 4965 <a href="https://github.com/mesonbuild/meson/blob/master/test%20cases/common/145%20custom%20target%20multiple%20outputs/meson.build">an example</a> that is easier to read than the Err codemadness.org 70 i 4966 documentation:</p> Err codemadness.org 70 i 4967 <div class="highlight"><pre><span></span><code><span class="n">gen</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">find_program</span><span class="p">(</span><span class="s1">&#39;generator.py&#39;</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 4968 Err codemadness.org 70 i 4969 <span class="n">outputs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">custom_target</span><span class="p">(</span><span class="s1">&#39;generated&#39;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4970 <span class="w"> </span><span class="k">output</span><span class="w"> </span><span class="err">:</span><span class="w"> </span><span class="o">[</span><span class="n">&#39;foo.h&#39;, &#39;foo.c&#39;</span><span class="o">]</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4971 <span class="w"> </span><span class="n">command</span><span class="w"> </span><span class="err">:</span><span class="w"> </span><span class="o">[</span><span class="n">gen, &#39;@OUTDIR@&#39;</span><span class="o">]</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 4972 <span class="w"> </span><span class="p">...</span><span class="w"></span> Err codemadness.org 70 i 4973 <span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 4974 </code></pre></div> Err codemadness.org 70 i 4975 Err codemadness.org 70 i 4976 <p>This defines a target named <code>'generated'</code>, which will use the Err codemadness.org 70 i 4977 <code>generator.py</code> program to output two files, <code>foo.h</code> and <code>foo.c</code>. That Err codemadness.org 70 i 4978 Python program will get called with <code>@OUTDIR@</code> as a command-line Err codemadness.org 70 i 4979 argument; in effect, meson will call Err codemadness.org 70 i 4980 <code>/full/path/to/generator.py @OUTDIR@</code> explicitly, without any magic Err codemadness.org 70 i 4981 passed through environment variables.</p> Err codemadness.org 70 i 4982 <p>If this looks similar to what Cargo does above with <code>build.rs</code>, it's Err codemadness.org 70 i 4983 because it <strong>is</strong> similar. It's just that <strong>Meson gives a name</strong> to Err codemadness.org 70 i 4984 the concept of generating code at build time (Meson's name for this is Err codemadness.org 70 i 4985 a <strong>custom target</strong>), and provides a mechanism to say which program is Err codemadness.org 70 i 4986 the generator, which files it is expected to generate, and how to call Err codemadness.org 70 i 4987 the program with appropriate arguments to put files in the right Err codemadness.org 70 i 4988 place.</p> Err codemadness.org 70 i 4989 <p>In contrast, Cargo assumes that all of that information can be Err codemadness.org 70 i 4990 inferred from an environment variable.</p> Err codemadness.org 70 i 4991 <p>In addition, if the custom target takes other files as input (say, so Err codemadness.org 70 i 4992 it can call <code>yacc my-grammar.y</code>), the <code>custom_target()</code> command can Err codemadness.org 70 i 4993 take an <code>input:</code> argument. This way, Meson can add a dependency on Err codemadness.org 70 i 4994 those input files, so that the appropriate things will be rebuilt if Err codemadness.org 70 i 4995 the input files change.</p> Err codemadness.org 70 i 4996 <p>Now, Cargo could very well provide a small utility crate that build Err codemadness.org 70 i 4997 scripts could use to figure out all that information. Meson would Err codemadness.org 70 i 4998 tell Cargo to use its scheme of things, and pass it down to build Err codemadness.org 70 i 4999 scripts via that utility crate. I.e. to have</p> Err codemadness.org 70 i 5000 <div class="highlight"><pre><span></span><code><span class="c1">// build.rs</span> Err codemadness.org 70 i 5001 Err codemadness.org 70 i 5002 <span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">cargo_high_level</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5003 Err codemadness.org 70 i 5004 <span class="kd">let</span><span class="w"> </span><span class="n">output</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Path</span>::<span class="n">new</span><span class="p">(</span><span class="n">cargo_high_level</span>::<span class="n">get_output_path</span><span class="p">()).</span><span class="n">join</span><span class="p">(</span><span class="s">&quot;codegen.rs&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 5005 <span class="c1">// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this, instead of:</span> Err codemadness.org 70 i 5006 Err codemadness.org 70 i 5007 <span class="kd">let</span><span class="w"> </span><span class="n">output</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Path</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">env</span>::<span class="n">var</span><span class="p">(</span><span class="s">&quot;OUT_DIR&quot;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()).</span><span class="n">join</span><span class="p">(</span><span class="s">&quot;codegen.rs&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 5008 Err codemadness.org 70 i 5009 <span class="c1">// let the build system know about generated dependencies</span> Err codemadness.org 70 i 5010 <span class="n">cargo_high_level</span>::<span class="n">add_output</span><span class="p">(</span><span class="n">output</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 5011 </code></pre></div> Err codemadness.org 70 i 5012 Err codemadness.org 70 i 5013 <p>A similar mechanism could be used for the way Meson likes to pass Err codemadness.org 70 i 5014 command-line arguments to the programs that deal with custom targets.</p> Err codemadness.org 70 i 5015 <h2>Linking to C libraries on the system</h2> Err codemadness.org 70 i 5016 <p>Some Rust crates need to link to lower-level C libraries that actually Err codemadness.org 70 i 5017 do the work. For example, in <a href="https://gtk-rs.org">gtk-rs</a>, there are high-level binding Err codemadness.org 70 i 5018 crates called <code>gtk</code>, <code>gdk</code>, <code>cairo</code>, etc. These use low-level crates Err codemadness.org 70 i 5019 called <code>gtk-sys</code>, <code>gdk-sys</code>, <code>cairo-sys</code>. Those <code>-sys</code> crates are Err codemadness.org 70 i 5020 just direct wrappers on top of the C functions of the respective Err codemadness.org 70 i 5021 system libraries: <code>gtk-sys</code> makes almost every function in Err codemadness.org 70 i 5022 <code>libgtk-3.so</code> available as a Rust-callable function.</p> Err codemadness.org 70 i 5023 <p>System libraries sometimes live in a well-known part of the filesystem Err codemadness.org 70 i 5024 (<code>/usr/lib64</code>, for example); other times, like in Windows and MacOS, Err codemadness.org 70 i 5025 they could be anywhere. To find that location plus other related Err codemadness.org 70 i 5026 metadata (include paths for C header files, library version), many Err codemadness.org 70 i 5027 system libraries use <a href="https://www.freedesktop.org/wiki/Software/pkg-config/"><code>pkg-config</code></a>. At the highest Err codemadness.org 70 i 5028 level, one can run <code>pkg-config</code> on the command line, or from build Err codemadness.org 70 i 5029 scripts, to query some things about libraries. For example:</p> Err codemadness.org 70 i 5030 <div class="highlight"><pre><span></span><code># <span class="nv">what</span><span class="s1">&#39;</span><span class="s">s the system</span><span class="s1">&#39;</span><span class="nv">s</span> <span class="nv">installed</span> <span class="nv">version</span> <span class="nv">of</span> <span class="nv">GTK</span>? Err codemadness.org 70 i 5031 $ <span class="nv">pkg</span><span class="o">-</span><span class="nv">config</span> <span class="o">--</span><span class="nv">modversion</span> <span class="nv">gtk</span><span class="o">+-</span><span class="mi">3</span>.<span class="mi">0</span> Err codemadness.org 70 i 5032 <span class="mi">3</span>.<span class="mi">24</span>.<span class="mi">4</span> Err codemadness.org 70 i 5033 Err codemadness.org 70 i 5034 # <span class="nv">what</span> <span class="nv">compiler</span> <span class="nv">flags</span> <span class="nv">would</span> <span class="nv">a</span> <span class="nv">C</span> <span class="nv">compiler</span> <span class="nv">need</span> <span class="k">for</span> <span class="nv">GTK</span>? Err codemadness.org 70 i 5035 $ <span class="nv">pkg</span><span class="o">-</span><span class="nv">config</span> <span class="o">--</span><span class="nv">cflags</span> <span class="nv">gtk</span><span class="o">+-</span><span class="mi">3</span>.<span class="mi">0</span> Err codemadness.org 70 i 5036 <span class="o">-</span><span class="nv">pthread</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">gtk</span><span class="o">-</span><span class="mi">3</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">at</span><span class="o">-</span><span class="nv">spi2</span><span class="o">-</span><span class="nv">atk</span><span class="o">/</span><span class="mi">2</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">at</span><span class="o">-</span><span class="nv">spi</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">dbus</span><span class="o">-</span><span class="mi">1</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="nv">lib64</span><span class="o">/</span><span class="nv">dbus</span><span class="o">-</span><span class="mi">1</span>.<span class="mi">0</span><span class="o">/</span><span class="k">include</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">gtk</span><span class="o">-</span><span class="mi">3</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">gio</span><span class="o">-</span><span class="nv">unix</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span><span class="o">/</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">libxkbcommon</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">wayland</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">cairo</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">pango</span><span class="o">-</span><span class="mi">1</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">harfbuzz</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">pango</span><span class="o">-</span><span class="mi">1</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">fribidi</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">atk</span><span class="o">-</span><span class="mi">1</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">cairo</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">pixman</span><span class="o">-</span><span class="mi">1</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">freetype2</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">libdrm</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">libpng16</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">gdk</span><span class="o">-</span><span class="nv">pixbuf</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">libmount</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">blkid</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">uuid</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="k">include</span><span class="o">/</span><span class="nv">glib</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">I</span><span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="nv">lib64</span><span class="o">/</span><span class="nv">glib</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span><span class="o">/</span><span class="k">include</span> Err codemadness.org 70 i 5037 Err codemadness.org 70 i 5038 # <span class="nv">and</span> <span class="nv">which</span> <span class="nv">libraries</span>? Err codemadness.org 70 i 5039 $ <span class="nv">pkg</span><span class="o">-</span><span class="nv">config</span> <span class="o">--</span><span class="nv">libs</span> <span class="nv">gtk</span><span class="o">+-</span><span class="mi">3</span>.<span class="mi">0</span> Err codemadness.org 70 i 5040 <span class="o">-</span><span class="nv">lgtk</span><span class="o">-</span><span class="mi">3</span> <span class="o">-</span><span class="nv">lgdk</span><span class="o">-</span><span class="mi">3</span> <span class="o">-</span><span class="nv">lpangocairo</span><span class="o">-</span><span class="mi">1</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">lpango</span><span class="o">-</span><span class="mi">1</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">latk</span><span class="o">-</span><span class="mi">1</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">lcairo</span><span class="o">-</span><span class="nv">gobject</span> <span class="o">-</span><span class="nv">lcairo</span> <span class="o">-</span><span class="nv">lgdk_pixbuf</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">lgio</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">lgobject</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span> <span class="o">-</span><span class="nv">lglib</span><span class="o">-</span><span class="mi">2</span>.<span class="mi">0</span> Err codemadness.org 70 i 5041 </code></pre></div> Err codemadness.org 70 i 5042 Err codemadness.org 70 i 5043 <p>There is a <a href="https://docs.rs/pkg-config/0.3.14/pkg_config/"><code>pkg-config</code> crate</a> which <code>build.rs</code> can Err codemadness.org 70 i 5044 use to call this, and communicate that information to Cargo. The Err codemadness.org 70 i 5045 example in the crate's documentation is for asking pkg-config for the Err codemadness.org 70 i 5046 <code>foo</code> package, with version at least <code>1.2.3</code>:</p> Err codemadness.org 70 i 5047 <div class="highlight"><pre><span></span><code><span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">pkg_config</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5048 Err codemadness.org 70 i 5049 <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5050 <span class="w"> </span><span class="n">pkg_config</span>::<span class="n">Config</span>::<span class="n">new</span><span class="p">().</span><span class="n">atleast_version</span><span class="p">(</span><span class="s">&quot;1.2.3&quot;</span><span class="p">).</span><span class="n">probe</span><span class="p">(</span><span class="s">&quot;foo&quot;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 5051 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5052 </code></pre></div> Err codemadness.org 70 i 5053 Err codemadness.org 70 i 5054 <p>And the documentation says,</p> Err codemadness.org 70 i 5055 <blockquote> Err codemadness.org 70 i 5056 <p>After running pkg-config all appropriate Cargo metadata will be Err codemadness.org 70 i 5057 printed on stdout if the search was successful.</p> Err codemadness.org 70 i 5058 </blockquote> Err codemadness.org 70 i 5059 <p>Wait, what?</p> Err codemadness.org 70 i 5060 <p>Indeed, printing specially-formated stuff on stdout is how <code>build.rs</code> Err codemadness.org 70 i 5061 scripts communicate back to Cargo about their findings. To quote <a href="https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script">Cargo's docs Err codemadness.org 70 i 5062 on build scripts</a>; the following is talking Err codemadness.org 70 i 5063 about the stdout of <code>build.rs</code>:</p> Err codemadness.org 70 i 5064 <blockquote> Err codemadness.org 70 i 5065 <p>Any line that starts with cargo: is interpreted directly by Err codemadness.org 70 i 5066 Cargo. This line must be of the form cargo:key=value, like the Err codemadness.org 70 i 5067 examples below:</p> Err codemadness.org 70 i 5068 </blockquote> Err codemadness.org 70 i 5069 <div class="highlight"><pre><span></span><code># <span class="nv">specially</span> <span class="nv">recognized</span> <span class="nv">by</span> <span class="nv">Cargo</span> Err codemadness.org 70 i 5070 <span class="nv">cargo</span>:<span class="nv">rustc</span><span class="o">-</span><span class="nv">link</span><span class="o">-</span><span class="nv">lib</span><span class="o">=</span><span class="nv">static</span><span class="o">=</span><span class="nv">foo</span> Err codemadness.org 70 i 5071 <span class="nv">cargo</span>:<span class="nv">rustc</span><span class="o">-</span><span class="nv">link</span><span class="o">-</span><span class="nv">search</span><span class="o">=</span><span class="nv">native</span><span class="o">=/</span><span class="nv">path</span><span class="o">/</span><span class="nv">to</span><span class="o">/</span><span class="nv">foo</span> Err codemadness.org 70 i 5072 <span class="nv">cargo</span>:<span class="nv">rustc</span><span class="o">-</span><span class="nv">cfg</span><span class="o">=</span><span class="nv">foo</span> Err codemadness.org 70 i 5073 <span class="nv">cargo</span>:<span class="nv">rustc</span><span class="o">-</span><span class="nv">env</span><span class="o">=</span><span class="nv">FOO</span><span class="o">=</span><span class="nv">bar</span> Err codemadness.org 70 i 5074 # <span class="nv">arbitrary</span> <span class="nv">user</span><span class="o">-</span><span class="nv">defined</span> <span class="nv">metadata</span> Err codemadness.org 70 i 5075 <span class="nv">cargo</span>:<span class="nv">root</span><span class="o">=/</span><span class="nv">path</span><span class="o">/</span><span class="nv">to</span><span class="o">/</span><span class="nv">foo</span> Err codemadness.org 70 i 5076 <span class="nv">cargo</span>:<span class="nv">libdir</span><span class="o">=/</span><span class="nv">path</span><span class="o">/</span><span class="nv">to</span><span class="o">/</span><span class="nv">foo</span><span class="o">/</span><span class="nv">lib</span> Err codemadness.org 70 i 5077 <span class="nv">cargo</span>:<span class="k">include</span><span class="o">=/</span><span class="nv">path</span><span class="o">/</span><span class="nv">to</span><span class="o">/</span><span class="nv">foo</span><span class="o">/</span><span class="k">include</span> Err codemadness.org 70 i 5078 </code></pre></div> Err codemadness.org 70 i 5079 Err codemadness.org 70 i 5080 <p>One can use the stdout of a <code>build.rs</code> program to add additional Err codemadness.org 70 i 5081 command-line options for <code>rustc</code>, or set environment variables for it, Err codemadness.org 70 i 5082 or add library paths, or specific libraries.</p> Err codemadness.org 70 i 5083 <p><strong>Meson hates this scheme of things</strong>. I suppose it would prefer to Err codemadness.org 70 i 5084 do the pkg-config calls itself, and then pass that information down to Err codemadness.org 70 i 5085 Cargo, you guessed it, via command-line options or something Err codemadness.org 70 i 5086 well-defined like that. Again, the example <code>cargo_high_level</code> crate I Err codemadness.org 70 i 5087 proposed above could be used to communicate this information from Err codemadness.org 70 i 5088 Meson to Cargo scripts. Meson also doesn't like this because it would Err codemadness.org 70 i 5089 prefer to know about <code>pkg-config</code>-based libraries in a declarative Err codemadness.org 70 i 5090 fashion, without having to run a random script like <code>build.rs</code>.</p> Err codemadness.org 70 i 5091 <h2>Building C code from Rust</h2> Err codemadness.org 70 i 5092 <p>Finally, some Rust crates build a bit of C code and then link that Err codemadness.org 70 i 5093 into the compiled Rust code. I have no experience with that, but Err codemadness.org 70 i 5094 the respective build scripts generally use the <a href="https://docs.rs/cc/1.0.29/cc/"><code>cc</code> crate</a> to Err codemadness.org 70 i 5095 call a C compiler and pass options to it conveniently. I suppose Err codemadness.org 70 i 5096 Meson would prefer to do this instead, or at least to have a Err codemadness.org 70 i 5097 high-level way of passing down information to Cargo.</p> Err codemadness.org 70 i 5098 <p>In effect, Meson has to be in charge of picking the C compiler. Err codemadness.org 70 i 5099 Having the thing-to-be-built pick on its own has caused big problems Err codemadness.org 70 i 5100 in the past: GObject-Introspection made the same mistake years ago Err codemadness.org 70 i 5101 when it decided to use distutils to detect the C compiler; gtk-doc did Err codemadness.org 70 i 5102 as well. When those tools are used, we still deal with problems with Err codemadness.org 70 i 5103 cross-compilation and when the system has more than one C compiler in Err codemadness.org 70 i 5104 it.</p> Err codemadness.org 70 i 5105 <h2>Snarky comments about the Unix philosophy</h2> Err codemadness.org 70 i 5106 <p>If part of the Unix philosophy is that shit can be glued together with Err codemadness.org 70 i 5107 environment variables and stringly-typed stdout... it's a pretty bad Err codemadness.org 70 i 5108 philosophy. All the cases above boil down to having a well-defined, Err codemadness.org 70 i 5109 more or less strongly-typed way to pass information between programs Err codemadness.org 70 i 5110 instead of shaking proverbial tree of the filesystem and the Err codemadness.org 70 i 5111 environment and seeing if something usable falls down.</p> Err codemadness.org 70 i 5112 <h2>Would we really have to modify all <code>build.rs</code> scripts for this?</h2> Err codemadness.org 70 i 5113 <p>Probably. Why not? Meson already has a lot of very well-structured Err codemadness.org 70 i 5114 knowledge of how to deal with multi-platform compilation and Err codemadness.org 70 i 5115 installation. Re-creating this knowledge in ad-hoc ways in <code>build.rs</code> Err codemadness.org 70 i 5116 is not very pleasant or maintainable.</p> Err codemadness.org 70 i 5117 <h3>Related work</h3> Err codemadness.org 70 i 5118 <ul> Err codemadness.org 70 i 5119 <li> Err codemadness.org 70 i 5120 <p><a href="https://internals.rust-lang.org/t/external-dependencies-in-declarative-format/9372">Cargo internals Err codemadness.org 70 i 5121 thread on using a declarative format to specify external dependencies</a></p> Err codemadness.org 70 i 5122 </li> Err codemadness.org 70 i 5123 <li> Err codemadness.org 70 i 5124 <p><a href="https://github.com/joshtriplett/metadeps">Run pkg-config from declarative dependencies in Cargo.toml</a></p> Err codemadness.org 70 i 5125 </li> Err codemadness.org 70 i 5126 </ul>Who wrote librsvg?2019-02-15T13:12:19-06:002019-02-15T13:12:19-06:00Federico Mena Quinterotag:people.gnome.org,2019-02-15:/~federico/blog/who-wrote-librsvg.html<p>Authors by lines of code, each year:</p> Err codemadness.org 70 i 5127 <p><img alt="Librsvg authors by lines of code by year" src="https://people.gnome.org/~federico/blog/images/librsvg-authors-2019-02.png"></p> Err codemadness.org 70 i 5128 <p>Authors by percentage of lines of code, each year:</p> Err codemadness.org 70 i 5129 <p><img alt="Librsvg authors by percentage of lines of code by year" src="https://people.gnome.org/~federico/blog/images/librsvg-authors-normalized-2019-02.png"></p> Err codemadness.org 70 i 5130 <p>Which lines of code remain each year?</p> Err codemadness.org 70 i 5131 <p><img alt="Lines of code that remain each year" src="https://people.gnome.org/~federico/blog/images/librsvg-lines-of-code-2019-02.png"></p> Err codemadness.org 70 i 5132 <p>The shitty thing about a gradual rewrite is that a few people end up Err codemadness.org 70 i 5133 "owning" all the lines of source code. Hopefully this post is a little …</p><p>Authors by lines of code, each year:</p> Err codemadness.org 70 i 5134 <p><img alt="Librsvg authors by lines of code by year" src="https://people.gnome.org/~federico/blog/images/librsvg-authors-2019-02.png"></p> Err codemadness.org 70 i 5135 <p>Authors by percentage of lines of code, each year:</p> Err codemadness.org 70 i 5136 <p><img alt="Librsvg authors by percentage of lines of code by year" src="https://people.gnome.org/~federico/blog/images/librsvg-authors-normalized-2019-02.png"></p> Err codemadness.org 70 i 5137 <p>Which lines of code remain each year?</p> Err codemadness.org 70 i 5138 <p><img alt="Lines of code that remain each year" src="https://people.gnome.org/~federico/blog/images/librsvg-lines-of-code-2019-02.png"></p> Err codemadness.org 70 i 5139 <p>The shitty thing about a gradual rewrite is that a few people end up Err codemadness.org 70 i 5140 "owning" all the lines of source code. Hopefully this post is a little Err codemadness.org 70 i 5141 acknowledgment of the people that made librsvg possible.</p> Err codemadness.org 70 i 5142 <p>The charts are made with the incredible tool Err codemadness.org 70 i 5143 <a href="https://github.com/erikbern/git-of-theseus">git-of-theseus</a> — thanks Err codemadness.org 70 i 5144 to <a href="https://mastodon.art/@norwin">@norwin@mastodon.art</a> for digging it Err codemadness.org 70 i 5145 up! Its README also points to a Err codemadness.org 70 i 5146 <a href="https://github.com/src-d/hercules">Hercules</a> plotter with awesome Err codemadness.org 70 i 5147 graphs. You know, for if you needed something to keep your computer Err codemadness.org 70 i 5148 busy during the weekend.</p>Librsvg's GObject boilerplate is in Rust now2019-01-23T18:12:44-06:002019-01-23T18:12:44-06:00Federico Mena Quinterotag:people.gnome.org,2019-01-23:/~federico/blog/librsvg-gobject-in-rust.html<p>The other day I wrote about how <a href="https://people.gnome.org/~federico/blog/librsvg-is-almost-rustified.html">most of librsvg's library code is in Err codemadness.org 70 i 5149 Rust now</a>. </p> Err codemadness.org 70 i 5150 <p>Today I finished porting the GObject boilerplate for the main Err codemadness.org 70 i 5151 <code>RsvgHandle</code> object into Rust. This means that the C code no longer Err codemadness.org 70 i 5152 calls things like <code>g_type_register_static()</code>, nor implements Err codemadness.org 70 i 5153 <code>rsvg_handle_class_init()</code> and such; all those are …</p><p>The other day I wrote about how <a href="https://people.gnome.org/~federico/blog/librsvg-is-almost-rustified.html">most of librsvg's library code is in Err codemadness.org 70 i 5154 Rust now</a>. </p> Err codemadness.org 70 i 5155 <p>Today I finished porting the GObject boilerplate for the main Err codemadness.org 70 i 5156 <code>RsvgHandle</code> object into Rust. This means that the C code no longer Err codemadness.org 70 i 5157 calls things like <code>g_type_register_static()</code>, nor implements Err codemadness.org 70 i 5158 <code>rsvg_handle_class_init()</code> and such; all those are in Rust now. How Err codemadness.org 70 i 5159 is this done?</p> Err codemadness.org 70 i 5160 <h2>The life-changing magic of glib::subclass</h2> Err codemadness.org 70 i 5161 <p><a href="https://coaxion.net/blog/">Sebastian Dröge</a> has been working for many months on refining Err codemadness.org 70 i 5162 utilities to make it possible to subclass GObjects in Rust, with Err codemadness.org 70 i 5163 little or no unsafe code. This <a href="https://github.com/gtk-rs/glib/tree/master/src/subclass">subclass</a> module is now part of Err codemadness.org 70 i 5164 <a href="https://github.com/gtk-rs/glib">glib-rs</a>, the Rust bindings to GLib. </p> Err codemadness.org 70 i 5165 <p>Librsvg now uses the subclassing functionality in glib-rs, which takes Err codemadness.org 70 i 5166 care of some things automatically:</p> Err codemadness.org 70 i 5167 <ul> Err codemadness.org 70 i 5168 <li>Registering your GObject types at runtime.</li> Err codemadness.org 70 i 5169 <li>Creating safe traits on which you can implement <code>class_init</code>, Err codemadness.org 70 i 5170 <code>instance_init</code>, <code>set_property</code>, <code>get_property</code>, and all the usual Err codemadness.org 70 i 5171 GObject paraphernalia.</li> Err codemadness.org 70 i 5172 </ul> Err codemadness.org 70 i 5173 <p>Check this out:</p> Err codemadness.org 70 i 5174 <div class="highlight"><pre><span></span><code><span class="k">use</span><span class="w"> </span><span class="n">glib</span>::<span class="n">subclass</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5175 Err codemadness.org 70 i 5176 <span class="k">impl</span><span class="w"> </span><span class="n">ObjectSubclass</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Handle</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5177 <span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">NAME</span>: <span class="kp">&amp;</span><span class="o">&#39;</span><span class="nb">static</span> <span class="kt">str</span> <span class="o">=</span><span class="w"> </span><span class="s">&quot;RsvgHandle&quot;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5178 Err codemadness.org 70 i 5179 <span class="w"> </span><span class="k">type</span> <span class="nc">ParentType</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">glib</span>::<span class="n">Object</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5180 Err codemadness.org 70 i 5181 <span class="w"> </span><span class="k">type</span> <span class="nc">Instance</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RsvgHandle</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5182 <span class="w"> </span><span class="k">type</span> <span class="nc">Class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RsvgHandleClass</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5183 Err codemadness.org 70 i 5184 <span class="w"> </span><span class="n">glib_object_subclass</span><span class="o">!</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 5185 Err codemadness.org 70 i 5186 <span class="w"> </span><span class="k">fn</span> <span class="nf">class_init</span><span class="p">(</span><span class="n">klass</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">RsvgHandleClass</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5187 <span class="w"> </span><span class="n">klass</span><span class="p">.</span><span class="n">install_properties</span><span class="p">(</span><span class="o">&amp;</span><span class="n">PROPERTIES</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 5188 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5189 Err codemadness.org 70 i 5190 <span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5191 <span class="w"> </span><span class="n">Handle</span>::<span class="n">new</span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 5192 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5193 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5194 </code></pre></div> Err codemadness.org 70 i 5195 Err codemadness.org 70 i 5196 <p>In the <code>impl</code> line, <code>Handle</code> is librsvg's internals object — what used Err codemadness.org 70 i 5197 to be <code>RsvgHandlePrivate</code> in the C code.</p> Err codemadness.org 70 i 5198 <p>The following lines say this:</p> Err codemadness.org 70 i 5199 <ul> Err codemadness.org 70 i 5200 <li> Err codemadness.org 70 i 5201 <p><code>const NAME: &amp;'static str = "RsvgHandle";</code> - the name of the type, Err codemadness.org 70 i 5202 for GType's perusal.</p> Err codemadness.org 70 i 5203 </li> Err codemadness.org 70 i 5204 <li> Err codemadness.org 70 i 5205 <p><code>type ParentType = glib::Object;</code> - Parent class.</p> Err codemadness.org 70 i 5206 </li> Err codemadness.org 70 i 5207 <li> Err codemadness.org 70 i 5208 <p><code>type Instance</code>, <code>type Class</code> - Structs with <code>#[repr(C)]</code>, Err codemadness.org 70 i 5209 equivalent to GObject's class and instance structs.</p> Err codemadness.org 70 i 5210 </li> Err codemadness.org 70 i 5211 <li> Err codemadness.org 70 i 5212 <p><code>glib_object_subclass!();</code> - All the boilerplate happens here Err codemadness.org 70 i 5213 automatically.</p> Err codemadness.org 70 i 5214 </li> Err codemadness.org 70 i 5215 <li> Err codemadness.org 70 i 5216 <p><code>fn class_init</code> - Should be familiar to anyone who implements Err codemadness.org 70 i 5217 GObjects!</p> Err codemadness.org 70 i 5218 </li> Err codemadness.org 70 i 5219 </ul> Err codemadness.org 70 i 5220 <p>And then, a couple of the property declarations:</p> Err codemadness.org 70 i 5221 <div class="highlight"><pre><span></span><code><span class="k">static</span><span class="w"> </span><span class="n">PROPERTIES</span>: <span class="p">[</span><span class="n">subclass</span>::<span class="n">Property</span><span class="p">;</span><span class="w"> </span><span class="mi">11</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="w"></span> Err codemadness.org 70 i 5222 <span class="w"> </span><span class="n">subclass</span>::<span class="n">Property</span><span class="p">(</span><span class="s">&quot;flags&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">|</span><span class="n">name</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5223 <span class="w"> </span><span class="n">ParamSpec</span>::<span class="n">flags</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 5224 <span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5225 <span class="w"> </span><span class="s">&quot;Flags&quot;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5226 <span class="w"> </span><span class="s">&quot;Loading flags&quot;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5227 <span class="w"> </span><span class="n">HandleFlags</span>::<span class="n">static_type</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 5228 <span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5229 <span class="w"> </span><span class="n">ParamFlags</span>::<span class="n">READWRITE</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ParamFlags</span>::<span class="n">CONSTRUCT_ONLY</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5230 <span class="w"> </span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 5231 <span class="w"> </span><span class="p">}),</span><span class="w"></span> Err codemadness.org 70 i 5232 <span class="w"> </span><span class="n">subclass</span>::<span class="n">Property</span><span class="p">(</span><span class="s">&quot;dpi-x&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">|</span><span class="n">name</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5233 <span class="w"> </span><span class="n">ParamSpec</span>::<span class="n">double</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 5234 <span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5235 <span class="w"> </span><span class="s">&quot;Horizontal DPI&quot;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5236 <span class="w"> </span><span class="s">&quot;Horizontal resolution in dots per inch&quot;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5237 <span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5238 <span class="w"> </span><span class="kt">f64</span>::<span class="n">MAX</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5239 <span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5240 <span class="w"> </span><span class="n">ParamFlags</span>::<span class="n">READWRITE</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ParamFlags</span>::<span class="n">CONSTRUCT</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5241 <span class="w"> </span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 5242 <span class="w"> </span><span class="p">}),</span><span class="w"></span> Err codemadness.org 70 i 5243 <span class="w"> </span><span class="c1">// ... etcetera</span> Err codemadness.org 70 i 5244 <span class="p">];</span><span class="w"></span> Err codemadness.org 70 i 5245 </code></pre></div> Err codemadness.org 70 i 5246 Err codemadness.org 70 i 5247 <p>This is quite similar to the way C code usually registers properties Err codemadness.org 70 i 5248 for new GObject subclasses.</p> Err codemadness.org 70 i 5249 <p>The moment at which a new GObject subclass gets registered against the Err codemadness.org 70 i 5250 GType system is in the <code>foo_get_type()</code> call. This is the C code in Err codemadness.org 70 i 5251 librsvg for that:</p> Err codemadness.org 70 i 5252 <div class="highlight"><pre><span></span><code><span class="k">extern</span> <span class="n">GType</span> <span class="nf">rsvg_handle_rust_get_type</span> <span class="p">(</span><span class="kt">void</span><span class="p">);</span> Err codemadness.org 70 i 5253 Err codemadness.org 70 i 5254 <span class="n">GType</span> Err codemadness.org 70 i 5255 <span class="nf">rsvg_handle_get_type</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span> Err codemadness.org 70 i 5256 <span class="p">{</span> Err codemadness.org 70 i 5257 <span class="k">return</span> <span class="n">rsvg_handle_rust_get_type</span> <span class="p">();</span> Err codemadness.org 70 i 5258 <span class="p">}</span> Err codemadness.org 70 i 5259 </code></pre></div> Err codemadness.org 70 i 5260 Err codemadness.org 70 i 5261 <p>And the Rust function that actually implements this:</p> Err codemadness.org 70 i 5262 <div class="highlight"><pre><span></span><code><span class="cp">#[no_mangle]</span><span class="w"></span> Err codemadness.org 70 i 5263 <span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_handle_rust_get_type</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">glib_sys</span>::<span class="n">GType</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5264 <span class="w"> </span><span class="n">Handle</span>::<span class="n">get_type</span><span class="p">().</span><span class="n">to_glib</span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 5265 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5266 </code></pre></div> Err codemadness.org 70 i 5267 Err codemadness.org 70 i 5268 <p>Here, <code>Handle::get_type()</code> gets implemented automatically by Err codemadness.org 70 i 5269 Sebastian's <a href="https://github.com/gtk-rs/glib/tree/master/src/subclass">subclass</a> traits. It gets things like the type name and Err codemadness.org 70 i 5270 the parent class from the <code>impl ObjectSubclass for Handle</code> we saw Err codemadness.org 70 i 5271 above, and calls <code>g_type_register_static()</code> internally.</p> Err codemadness.org 70 i 5272 <p>I can confirm now that implementing GObjects in Rust in this way, and Err codemadness.org 70 i 5273 exposing them to C, really works and is actually quite pleasant to Err codemadness.org 70 i 5274 do. <a href="https://gitlab.gnome.org/federico/librsvg/blob/subclass/rsvg_internals/src/c_api.rs">You can look at librsvg's Rust code for GObject here</a>.</p> Err codemadness.org 70 i 5275 <h2>Further work</h2> Err codemadness.org 70 i 5276 <p>There is some auto-generated C code to register librsvg's error enum Err codemadness.org 70 i 5277 and a flags type against GType; I'll move those to Rust over the next Err codemadness.org 70 i 5278 few days.</p> Err codemadness.org 70 i 5279 <p>Then, I think I'll try to actually remove all of the library's entry Err codemadness.org 70 i 5280 points from the C code and implement them in Rust. Right now each C Err codemadness.org 70 i 5281 function is really just a single call to a Rust function, so this Err codemadness.org 70 i 5282 should be trivial-ish to do.</p> Err codemadness.org 70 i 5283 <p>I'm waiting for a glib-rs release, the first one that will have the Err codemadness.org 70 i 5284 <code>glib::subclass</code> code in it, before merging all of the above into Err codemadness.org 70 i 5285 librsvg's master branch.</p> Err codemadness.org 70 i 5286 <h2>A new Rust API for librsvg?</h2> Err codemadness.org 70 i 5287 <p>Finally, this got me thinking about what to do about the Rust bindings Err codemadness.org 70 i 5288 to librsvg itself. The <a href="https://github.com/selaux/rsvg-rs">rsvg crate</a> uses the gtk-rs Err codemadness.org 70 i 5289 machinery to generate the binding: it reads the <a href="https://people.gnome.org/~federico/blog/magic-of-gobject-introspection.html">GObject Err codemadness.org 70 i 5290 Introspection</a> data from <code>Rsvg.gir</code> and generates a Rust binding Err codemadness.org 70 i 5291 for it.</p> Err codemadness.org 70 i 5292 <p>However, the resulting API is mostly identical to the C API. There is Err codemadness.org 70 i 5293 an <code>rsvg::Handle</code> with the same methods as the ones from C's Err codemadness.org 70 i 5294 <code>RsvgHandle</code>... and that API is not particularly Rusty.</p> Err codemadness.org 70 i 5295 <p>At some point I had an unfinished branch to <a href="https://gitlab.gnome.org/GNOME/librsvg/commits/import-rsvg-rs">merge rsvg-rs into Err codemadness.org 70 i 5296 librsvg</a>. The intention was that librsvg's build procedure Err codemadness.org 70 i 5297 would first build <code>librsvg.so</code> itself, then generate <code>Rsvg.gir</code> as Err codemadness.org 70 i 5298 usual, and <strong>then</strong> generate rsvg-rs from that. But I got tired of Err codemadness.org 70 i 5299 fucking with Autotools, and didn't finish integrating the projects.</p> Err codemadness.org 70 i 5300 <p>Rsvg-rs is an <em>okay</em> Rust API for using librsvg. It still works Err codemadness.org 70 i 5301 perfectly well from the <a href="https://github.com/selaux/rsvg-rs">standalone crate</a>. However, now Err codemadness.org 70 i 5302 that all the functionality of librsvg is in Rust, I would like to take Err codemadness.org 70 i 5303 this opportunity to experiment with a better API for loading and Err codemadness.org 70 i 5304 rendering SVGs from Rust. This may make it more clear how to refactor Err codemadness.org 70 i 5305 the toplevel of the library. Maybe the <code>librsvg</code> project can provide Err codemadness.org 70 i 5306 its own Rust crate for public consumption, in addition to the usual Err codemadness.org 70 i 5307 <code>librsvg.so</code> and <code>Rsvg.gir</code> which need to remain with a stable API and Err codemadness.org 70 i 5308 ABI.</p>Librsvg is almost rustified now2019-01-10T12:28:11-06:002019-01-10T12:28:11-06:00Federico Mena Quinterotag:people.gnome.org,2019-01-10:/~federico/blog/librsvg-is-almost-rustified.html<p>Since a few days ago, librsvg's library implementation is almost 100% Err codemadness.org 70 i 5309 Rust code. Paolo Borelli's and Carlos Martín Nieto's latest commits Err codemadness.org 70 i 5310 made it possible.</p> Err codemadness.org 70 i 5311 <p>What does "almost 100% Rust code" mean here?</p> Err codemadness.org 70 i 5312 <ul> Err codemadness.org 70 i 5313 <li> Err codemadness.org 70 i 5314 <p>The C code no longer has struct fields that refer to the library's Err codemadness.org 70 i 5315 real work. The only field …</p></li></ul><p>Since a few days ago, librsvg's library implementation is almost 100% Err codemadness.org 70 i 5316 Rust code. Paolo Borelli's and Carlos Martín Nieto's latest commits Err codemadness.org 70 i 5317 made it possible.</p> Err codemadness.org 70 i 5318 <p>What does "almost 100% Rust code" mean here?</p> Err codemadness.org 70 i 5319 <ul> Err codemadness.org 70 i 5320 <li> Err codemadness.org 70 i 5321 <p>The C code no longer has struct fields that refer to the library's Err codemadness.org 70 i 5322 real work. The only field in <code>RsvgHandlePrivate</code> is an opaque Err codemadness.org 70 i 5323 pointer to a Rust-side structure. All the rest of the library's Err codemadness.org 70 i 5324 data lives in Rust structs.</p> Err codemadness.org 70 i 5325 </li> Err codemadness.org 70 i 5326 <li> Err codemadness.org 70 i 5327 <p>The public API is implemented in C, but it is just stubs that Err codemadness.org 70 i 5328 immediately call into Rust functions. For example:</p> Err codemadness.org 70 i 5329 </li> Err codemadness.org 70 i 5330 </ul> Err codemadness.org 70 i 5331 <div class="highlight"><pre><span></span><code><span class="n">gboolean</span> Err codemadness.org 70 i 5332 <span class="nf">rsvg_handle_render_cairo_sub</span> <span class="p">(</span><span class="n">RsvgHandle</span> <span class="o">*</span> <span class="n">handle</span><span class="p">,</span> <span class="n">cairo_t</span> <span class="o">*</span> <span class="n">cr</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">id</span><span class="p">)</span> Err codemadness.org 70 i 5333 <span class="p">{</span> Err codemadness.org 70 i 5334 <span class="n">g_return_val_if_fail</span> <span class="p">(</span><span class="n">RSVG_IS_HANDLE</span> <span class="p">(</span><span class="n">handle</span><span class="p">),</span> <span class="n">FALSE</span><span class="p">);</span> Err codemadness.org 70 i 5335 <span class="n">g_return_val_if_fail</span> <span class="p">(</span><span class="n">cr</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">);</span> Err codemadness.org 70 i 5336 Err codemadness.org 70 i 5337 <span class="k">return</span> <span class="n">rsvg_handle_rust_render_cairo_sub</span> <span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">cr</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span> Err codemadness.org 70 i 5338 <span class="p">}</span> Err codemadness.org 70 i 5339 </code></pre></div> Err codemadness.org 70 i 5340 Err codemadness.org 70 i 5341 <ul> Err codemadness.org 70 i 5342 <li> Err codemadness.org 70 i 5343 <p>The GObject boilerplate and supporting code is still in C: Err codemadness.org 70 i 5344 <code>rsvg_handle_class_init</code> and <code>set_property</code> and friends.</p> Err codemadness.org 70 i 5345 </li> Err codemadness.org 70 i 5346 <li> Err codemadness.org 70 i 5347 <p>All the high-level tests are still done in C.</p> Err codemadness.org 70 i 5348 </li> Err codemadness.org 70 i 5349 <li> Err codemadness.org 70 i 5350 <p>The gdk-pixbuf loader for SVG files is done in C.</p> Err codemadness.org 70 i 5351 </li> Err codemadness.org 70 i 5352 </ul> Err codemadness.org 70 i 5353 <p>Someone posted a <a href="https://www.reddit.com/r/rust/comments/ae5xwd/librsvg_oxidation/">chart on Reddit about the rustification of librsvg</a>, Err codemadness.org 70 i 5354 comparing lines of code in each language vs. time.</p> Err codemadness.org 70 i 5355 <h2>Rustifying the remaining C code</h2> Err codemadness.org 70 i 5356 <p>There is only a handful of very small functions from the public API Err codemadness.org 70 i 5357 still implemented in C, and I am converting them one by one to Rust. Err codemadness.org 70 i 5358 These are just helper functions built on top of other public API that Err codemadness.org 70 i 5359 does the real work.</p> Err codemadness.org 70 i 5360 <p>Converting the gdk-pixbuf loader to Rust seems like writing a little Err codemadness.org 70 i 5361 glue code for the loadable module; the actual loading is just a couple Err codemadness.org 70 i 5362 of calls to librsvg's API.</p> Err codemadness.org 70 i 5363 <h3>Rsvg-rs in rsvg?</h3> Err codemadness.org 70 i 5364 <p>Converting the tests to Rust... ideally this would use the <a href="https://github.com/selaux/rsvg-rs">rsvg-rs</a> Err codemadness.org 70 i 5365 bindings; for example, it is what I already use for <a href="https://gitlab.gnome.org/federico/rsvg-bench">rsvg-bench</a>, a Err codemadness.org 70 i 5366 benchmarking program for librsvg.</p> Err codemadness.org 70 i 5367 <p>I have an <a href="https://gitlab.gnome.org/federico/librsvg/commits/import-rsvg-rs">unfinished branch to merge the rsvg-rs repository</a> Err codemadness.org 70 i 5368 into librsvg's own repository. This is because...</p> Err codemadness.org 70 i 5369 <ol> Err codemadness.org 70 i 5370 <li>Librsvg builds its library, <code>librsvg.so</code></li> Err codemadness.org 70 i 5371 <li>Gobject-introspection runs on <code>librsvg.so</code> and the source code, and Err codemadness.org 70 i 5372 produces <code>librsvg.gir</code></li> Err codemadness.org 70 i 5373 <li>Rsvg-rs's build system calls <a href="https://github.com/gtk-rs/gir/">gir</a> on <code>librsvg.gir</code> to generate the Err codemadness.org 70 i 5374 Rust binding's code.</li> Err codemadness.org 70 i 5375 </ol> Err codemadness.org 70 i 5376 <p>As you can imagine, doing all of this with Autotools is... rather Err codemadness.org 70 i 5377 convoluted. It gives me a lot of anxiety to think that there is also Err codemadness.org 70 i 5378 an <a href="https://gitlab.gnome.org/GNOME/librsvg/commits/wip/meson">unfinished branch to port the build system to Meson</a>, where Err codemadness.org 70 i 5379 <em>probably</em> doing the .so→.gir→rs chain would be easier, but who Err codemadness.org 70 i 5380 knows. Help in this area is <strong>much</strong> appreciated!</p> Err codemadness.org 70 i 5381 <h3>An alternative?</h3> Err codemadness.org 70 i 5382 <p>Rustified tests could, of course, call the C API of librsvg by hand, Err codemadness.org 70 i 5383 in <code>unsafe</code> code. This may not be idiomatic, but sounds like it could Err codemadness.org 70 i 5384 be done relatively quickly.</p> Err codemadness.org 70 i 5385 <h2>Future work</h2> Err codemadness.org 70 i 5386 <p>There are two options to get rid of all the C code in the library, and Err codemadness.org 70 i 5387 just leave C header files for public consumption:</p> Err codemadness.org 70 i 5388 <ol> Err codemadness.org 70 i 5389 <li> Err codemadness.org 70 i 5390 <p>Do the GObject implementation in Rust, using Sebastian Dröge's work Err codemadness.org 70 i 5391 from GStreamer to do this easily.</p> Err codemadness.org 70 i 5392 </li> Err codemadness.org 70 i 5393 <li> Err codemadness.org 70 i 5394 <p>Work on making <a href="https://gitlab.gnome.org/federico/gnome-class">gnome-class</a> powerful enough to implement the librsvg Err codemadness.org 70 i 5395 API directly, and in an ABI-compatible fashion to what there is Err codemadness.org 70 i 5396 right now.</p> Err codemadness.org 70 i 5397 </li> Err codemadness.org 70 i 5398 </ol> Err codemadness.org 70 i 5399 <p>The second case will probably build upon the first one, since one of Err codemadness.org 70 i 5400 my plans for gnome-class is to make it generate code that uses Err codemadness.org 70 i 5401 Sebastian's, instead of generating all the GObject boilerplate by Err codemadness.org 70 i 5402 hand.</p>In support of Coraline Ada Ehmke2018-12-07T14:00:06-06:002018-12-07T14:00:06-06:00Federico Mena Quinterotag:people.gnome.org,2018-12-07:/~federico/blog/in-support-of-coraline.html<p>Last night, the linux.org DNS was hijacked and redirected to a page Err codemadness.org 70 i 5403 that doxed her. Coraline is doing extremely valuable work with the Err codemadness.org 70 i 5404 <a href="https://www.contributor-covenant.org/">Contributor Covenant</a> code of conduct, which many free software Err codemadness.org 70 i 5405 projects have <a href="https://www.contributor-covenant.org/adopters">adopted</a> already.</p> Err codemadness.org 70 i 5406 <p>Coraline has been working for years in making free software, and Err codemadness.org 70 i 5407 computer technology …</p><p>Last night, the linux.org DNS was hijacked and redirected to a page Err codemadness.org 70 i 5408 that doxed her. Coraline is doing extremely valuable work with the Err codemadness.org 70 i 5409 <a href="https://www.contributor-covenant.org/">Contributor Covenant</a> code of conduct, which many free software Err codemadness.org 70 i 5410 projects have <a href="https://www.contributor-covenant.org/adopters">adopted</a> already.</p> Err codemadness.org 70 i 5411 <p>Coraline has been working for years in making free software, and Err codemadness.org 70 i 5412 computer technology circles in general, a welcome place for Err codemadness.org 70 i 5413 underrepresented groups.</p> Err codemadness.org 70 i 5414 <p>I hope Coraline stays safe and strong. You can <a href="https://www.patreon.com/coraline">support her directly Err codemadness.org 70 i 5415 on Patreon</a>.</p>My GUADEC 2018 presentation2018-12-04T18:57:00-06:002018-12-06T15:09:43-06:00Federico Mena Quinterotag:people.gnome.org,2018-12-04:/~federico/blog/guadec-2018-presentation.html<p>I just realized that I forgot to publish my presentation from this Err codemadness.org 70 i 5416 year's GUADEC. Sorry, here it is!</p> Err codemadness.org 70 i 5417 <p><a href="https://people.gnome.org/~federico/blog/docs/fmq-refactoring-c-to-rust.pdf"><img alt="Patterns of refactoring C to Rust - link to PDF" src="https://people.gnome.org/~federico/blog/images/fmq-refactoring-c-to-rust.png"></a></p> Err codemadness.org 70 i 5418 <p>You can also get the <a href="https://people.gnome.org/~federico/blog/docs/fmq-refactoring-c-to-rust.odp">ODP file</a> for the presentation. This is Err codemadness.org 70 i 5419 released under a <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA license</a>.</p> Err codemadness.org 70 i 5420 <p>This is the <a href="http://videos.guadec.org/2018/GUADEC%202018%20-%20Federico%20Mena%20Quintero%20-%20Patterns%20of%20refactoring%20C%20to%20Rust-5mVMycYmoWE.mp4">video of the presentation</a>.</p> Err codemadness.org 70 i 5421 <p><strong><em>Update Dec/06:</em></strong> Keen readers spotted an <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/391">incorrect …</a></p><p>I just realized that I forgot to publish my presentation from this Err codemadness.org 70 i 5422 year's GUADEC. Sorry, here it is!</p> Err codemadness.org 70 i 5423 <p><a href="https://people.gnome.org/~federico/blog/docs/fmq-refactoring-c-to-rust.pdf"><img alt="Patterns of refactoring C to Rust - link to PDF" src="https://people.gnome.org/~federico/blog/images/fmq-refactoring-c-to-rust.png"></a></p> Err codemadness.org 70 i 5424 <p>You can also get the <a href="https://people.gnome.org/~federico/blog/docs/fmq-refactoring-c-to-rust.odp">ODP file</a> for the presentation. This is Err codemadness.org 70 i 5425 released under a <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA license</a>.</p> Err codemadness.org 70 i 5426 <p>This is the <a href="http://videos.guadec.org/2018/GUADEC%202018%20-%20Federico%20Mena%20Quintero%20-%20Patterns%20of%20refactoring%20C%20to%20Rust-5mVMycYmoWE.mp4">video of the presentation</a>.</p> Err codemadness.org 70 i 5427 <p><strong><em>Update Dec/06:</em></strong> Keen readers spotted an <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/391">incorrect use of opaque Err codemadness.org 70 i 5428 pointers</a>; I've updated the example code in the presentation to Err codemadness.org 70 i 5429 match <a href="https://gitlab.gnome.org/GNOME/librsvg/merge_requests/161">Jordan's fix with the recommended usage</a>. That merge Err codemadness.org 70 i 5430 request has an interesting conversation on FFI esoterica, too.</p>Refactoring allowed URLs in librsvg2018-11-29T11:31:37-06:002018-11-29T11:31:37-06:00Federico Mena Quinterotag:people.gnome.org,2018-11-29:/~federico/blog/refactoring-allowed-urls-in-librsvg.html<p>While in the middle of converting librsvg's code that processes XML from C Err codemadness.org 70 i 5431 to Rust, I went into a digression that has to do with the way librsvg Err codemadness.org 70 i 5432 decides which files are allowed to be referenced from within an SVG.</p> Err codemadness.org 70 i 5433 <h1>Resource references in SVG</h1> Err codemadness.org 70 i 5434 <p>SVG files can reference other files …</p><p>While in the middle of converting librsvg's code that processes XML from C Err codemadness.org 70 i 5435 to Rust, I went into a digression that has to do with the way librsvg Err codemadness.org 70 i 5436 decides which files are allowed to be referenced from within an SVG.</p> Err codemadness.org 70 i 5437 <h1>Resource references in SVG</h1> Err codemadness.org 70 i 5438 <p>SVG files can reference other files, i.e. they are not Err codemadness.org 70 i 5439 self-contained. For example, there can be an element like <code>&lt;image Err codemadness.org 70 i 5440 xlink:href="foo.png"&gt;</code>, or one can request that a sub-element of Err codemadness.org 70 i 5441 another SVG be included with <code>&lt;use xlink:href="secondary.svg#foo"&gt;</code>. Err codemadness.org 70 i 5442 Finally, there is the <code>xi:include</code> mechanism to include chunks of text Err codemadness.org 70 i 5443 or XML into another XML file.</p> Err codemadness.org 70 i 5444 <p>Since librsvg is sometimes used to render untrusted files that come from Err codemadness.org 70 i 5445 the internet, it needs to be careful not to allow those files to Err codemadness.org 70 i 5446 reference any random resource on the filesystem. We don't want Err codemadness.org 70 i 5447 something like Err codemadness.org 70 i 5448 <code>&lt;text&gt;&lt;xi:include href="/etc/passwd" parse="text"/&gt;&lt;/text&gt;</code> Err codemadness.org 70 i 5449 or something equally nefarious that would exfiltrate a random file Err codemadness.org 70 i 5450 into the rendered output.</p> Err codemadness.org 70 i 5451 <p>Also, want to catch malicious SVGs that want to "phone home" by Err codemadness.org 70 i 5452 referencing a network resource like Err codemadness.org 70 i 5453 <code>&lt;image xlink:href="http://evil.com/pingback.jpg"&gt;</code>.</p> Err codemadness.org 70 i 5454 <p>So, librsvg is careful to have a single place where it can load Err codemadness.org 70 i 5455 secondary resources, and first it validates the resource's URL to see Err codemadness.org 70 i 5456 if it is allowed.</p> Err codemadness.org 70 i 5457 <p>The actual validation rules are not very important for this Err codemadness.org 70 i 5458 discussion; they are something like "no absolute URLs allowed" (so you Err codemadness.org 70 i 5459 can't request <code>/etc/passwd</code>, "only siblings or (grand)children of Err codemadness.org 70 i 5460 siblings allowed" (so <code>foo.svg</code> can request <code>bar.svg</code> and Err codemadness.org 70 i 5461 <code>subdir/bar.svg</code>, but not <code>../../bar.svg</code>).</p> Err codemadness.org 70 i 5462 <h1>The code</h1> Err codemadness.org 70 i 5463 <p>There was a central function <code>rsvg_io_acquire_stream()</code> which took a Err codemadness.org 70 i 5464 URL as a string. The code assumed that that URL had been first Err codemadness.org 70 i 5465 validated with a function called <code>allow_load(url)</code>. While the code's Err codemadness.org 70 i 5466 structure guaranteed that all the places that may acquire a stream Err codemadness.org 70 i 5467 would actually go through <code>allow_load()</code> first, the structure of the Err codemadness.org 70 i 5468 code in Rust made it possible to actually make it impossible to Err codemadness.org 70 i 5469 acquire a disallowed URL.</p> Err codemadness.org 70 i 5470 <p>Before:</p> Err codemadness.org 70 i 5471 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">allow_load</span><span class="p">(</span><span class="n">url</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5472 Err codemadness.org 70 i 5473 <span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">acquire_stream</span><span class="p">(</span><span class="n">url</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="p">.)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">gio</span>::<span class="n">InputStream</span><span class="p">,</span><span class="w"> </span><span class="n">glib</span>::<span class="n">Error</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5474 Err codemadness.org 70 i 5475 <span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_acquire_stream</span><span class="p">(</span><span class="n">url</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="p">.)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">gio</span>::<span class="n">InputStream</span><span class="p">,</span><span class="w"> </span><span class="n">LoadingError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5476 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">allow_load</span><span class="p">(</span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5477 <span class="w"> </span><span class="n">acquire_stream</span><span class="p">(</span><span class="n">url</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="p">.)</span><span class="o">?</span><span class="w"></span> Err codemadness.org 70 i 5478 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5479 <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">LoadingError</span>::<span class="n">NotAllowed</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 5480 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5481 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5482 </code></pre></div> Err codemadness.org 70 i 5483 Err codemadness.org 70 i 5484 <p>The refactored code now has an <code>AllowedUrl</code> type that encapsulates a Err codemadness.org 70 i 5485 URL, plus the promise that it <strong>has</strong> gone through these steps:</p> Err codemadness.org 70 i 5486 <ul> Err codemadness.org 70 i 5487 <li>The URL has been run through a URL well-formedness parser.</li> Err codemadness.org 70 i 5488 <li>The resource is allowed to be loaded following librsvg's rules.</li> Err codemadness.org 70 i 5489 </ul> Err codemadness.org 70 i 5490 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">AllowedUrl</span><span class="p">(</span><span class="n">Url</span><span class="p">);</span><span class="w"> </span><span class="c1">// from the Url parsing crate</span> Err codemadness.org 70 i 5491 Err codemadness.org 70 i 5492 <span class="k">impl</span><span class="w"> </span><span class="n">AllowedUrl</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5493 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_href</span><span class="p">(</span><span class="n">href</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">AllowedUrl</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5494 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">parsed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Url</span>::<span class="n">parse</span><span class="p">(</span><span class="n">href</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"> </span><span class="c1">// may return LoadingError::InvalidUrl</span> Err codemadness.org 70 i 5495 Err codemadness.org 70 i 5496 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">allow_load</span><span class="p">(</span><span class="n">parsed</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5497 <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">AllowedUrl</span><span class="p">(</span><span class="n">parsed</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 5498 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5499 <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">LoadingError</span>::<span class="n">NotAllowed</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 5500 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5501 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5502 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5503 Err codemadness.org 70 i 5504 <span class="c1">// new prototype</span> Err codemadness.org 70 i 5505 <span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">acquire_stream</span><span class="p">(</span><span class="n">url</span>: <span class="kp">&amp;</span><span class="nc">AllowedUrl</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="p">.)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">gio</span>::<span class="n">InputStream</span><span class="p">,</span><span class="w"> </span><span class="n">glib</span>::<span class="n">Error</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5506 </code></pre></div> Err codemadness.org 70 i 5507 Err codemadness.org 70 i 5508 <p>This forces callers to validate the URLs as soon as possible, right Err codemadness.org 70 i 5509 after they get them from the SVG file. Now it is not possible to Err codemadness.org 70 i 5510 request a stream unless the URL has been validated first.</p> Err codemadness.org 70 i 5511 <h1>Plain URIs vs. fragment identifiers</h1> Err codemadness.org 70 i 5512 <p>Some of the elements in SVG that reference other data require full Err codemadness.org 70 i 5513 files:</p> Err codemadness.org 70 i 5514 <div class="highlight"><pre><span></span><code><span class="o">&lt;</span><span class="n">image</span> <span class="n">xlink</span><span class="o">:</span><span class="n">href</span><span class="o">=</span><span class="s">&quot;foo.png&quot;</span> <span class="p">...</span><span class="o">&gt;</span> <span class="o">&lt;!--</span> <span class="n">no</span> <span class="n">fragments</span> <span class="n">allowed</span> <span class="o">--&gt;</span> Err codemadness.org 70 i 5515 </code></pre></div> Err codemadness.org 70 i 5516 Err codemadness.org 70 i 5517 <p>And some others, that reference particular elements in secondary SVGs, Err codemadness.org 70 i 5518 require a fragment ID:</p> Err codemadness.org 70 i 5519 <div class="highlight"><pre><span></span><code><span class="o">&lt;</span><span class="n">use</span> <span class="n">xlink</span><span class="o">:</span><span class="n">href</span><span class="o">=</span><span class="s">&quot;icons.svg#app_name&quot;</span> <span class="p">...</span><span class="o">&gt;</span> <span class="o">&lt;!--</span> <span class="n">fragment</span> <span class="n">id</span> <span class="n">required</span> <span class="o">--&gt;</span> Err codemadness.org 70 i 5520 </code></pre></div> Err codemadness.org 70 i 5521 Err codemadness.org 70 i 5522 <p>And finally, the <code>feImage</code> element, used to paste an image as part of Err codemadness.org 70 i 5523 a filter effects pipeline, allows either:</p> Err codemadness.org 70 i 5524 <div class="highlight"><pre><span></span><code><span class="o">&lt;!--</span> <span class="n">will</span> <span class="n">use</span> <span class="n">that</span> <span class="n">image</span> <span class="o">--&gt;</span> Err codemadness.org 70 i 5525 <span class="o">&lt;</span><span class="n">feImage</span> <span class="n">xlink</span><span class="o">:</span><span class="n">href</span><span class="o">=</span><span class="s">&quot;foo.png&quot;</span> <span class="p">...</span><span class="o">&gt;</span> Err codemadness.org 70 i 5526 Err codemadness.org 70 i 5527 <span class="o">&lt;!--</span> <span class="n">will</span> <span class="n">render</span> <span class="n">just</span> <span class="n">this</span> <span class="n">element</span> <span class="n">from</span> <span class="n">an</span> <span class="n">SVG</span> <span class="kr">and</span> <span class="n">use</span> <span class="n">it</span> <span class="kr">as</span> <span class="n">an</span> <span class="n">image</span> <span class="o">--&gt;</span> Err codemadness.org 70 i 5528 <span class="o">&lt;</span><span class="n">feImage</span> <span class="n">xlink</span><span class="o">:</span><span class="n">href</span><span class="o">=</span><span class="s">&quot;foo.svg#element&quot;</span><span class="o">&gt;</span> Err codemadness.org 70 i 5529 </code></pre></div> Err codemadness.org 70 i 5530 Err codemadness.org 70 i 5531 <p>So, I introduced a general <code>Href</code> parser :</p> Err codemadness.org 70 i 5532 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">Href</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5533 <span class="w"> </span><span class="n">PlainUri</span><span class="p">(</span><span class="nb">String</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 5534 <span class="w"> </span><span class="n">WithFragment</span><span class="p">(</span><span class="n">Fragment</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 5535 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5536 Err codemadness.org 70 i 5537 <span class="sd">/// Optional URI, mandatory fragment id</span> Err codemadness.org 70 i 5538 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Fragment</span><span class="p">(</span><span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 5539 </code></pre></div> Err codemadness.org 70 i 5540 Err codemadness.org 70 i 5541 <p>The parts of the code that absolutely require a fragment id now take a Err codemadness.org 70 i 5542 <code>Fragment</code>. Parts which require a <code>PlainUri</code> can unwrap that case.</p> Err codemadness.org 70 i 5543 <p>The next step is making those structs contain an <code>AllowedUrl</code> Err codemadness.org 70 i 5544 directly, instead of just strings, so that for callers, obtaining a Err codemadness.org 70 i 5545 fully validated name is a one-step operation.</p> Err codemadness.org 70 i 5546 <p>In general, the code is moving towards a scheme where all file I/O is Err codemadness.org 70 i 5547 done at loading time. Right now, some of those external references Err codemadness.org 70 i 5548 get resolved at rendering time, which is somewhat awkward (for Err codemadness.org 70 i 5549 example, at rendering time the caller has no chance to use a Err codemadness.org 70 i 5550 <code>GCancellable</code> to cancel loading). This refactoring to do early Err codemadness.org 70 i 5551 validation is leaving the code in a very nice state.</p>Thessaloniki GNOME+Rust Hackfest 20182018-11-27T17:37:31-06:002018-11-27T17:37:31-06:00Federico Mena Quinterotag:people.gnome.org,2018-11-27:/~federico/blog/thessaloniki-gnome-rust-2018.html<p>A couple of weeks ago we had the <a href="https://wiki.gnome.org/Hackfests/Rust2018-2">fourth GNOME+Rust hackfest</a>, this time Err codemadness.org 70 i 5552 in Thessaloniki, Greece. This is the beautiful city that will host Err codemadness.org 70 i 5553 next year's GUADEC, but fortunately GUADEC will be in summertime!</p> Err codemadness.org 70 i 5554 <p>We held the hackfest at the <a href="http://coho.gr/">CoHo</a> coworking space, a small, cozy Err codemadness.org 70 i 5555 office between the …</p><p>A couple of weeks ago we had the <a href="https://wiki.gnome.org/Hackfests/Rust2018-2">fourth GNOME+Rust hackfest</a>, this time Err codemadness.org 70 i 5556 in Thessaloniki, Greece. This is the beautiful city that will host Err codemadness.org 70 i 5557 next year's GUADEC, but fortunately GUADEC will be in summertime!</p> Err codemadness.org 70 i 5558 <p>We held the hackfest at the <a href="http://coho.gr/">CoHo</a> coworking space, a small, cozy Err codemadness.org 70 i 5559 office between the University and the sea.</p> Err codemadness.org 70 i 5560 <p>Every such hackfest I am overwhelmed by the kind hackers who work on Err codemadness.org 70 i 5561 [gnome-class], the code generator for GObject implementations in Err codemadness.org 70 i 5562 Rust.</p> Err codemadness.org 70 i 5563 <p>Mredlek has been working on generalizing the code generators in Err codemadness.org 70 i 5564 gnome-class, so that we can have the following from the same run:</p> Err codemadness.org 70 i 5565 <ul> Err codemadness.org 70 i 5566 <li> Err codemadness.org 70 i 5567 <p>Rust code generation, for the GObject implementations themselves. Err codemadness.org 70 i 5568 Thanks to mredlek, this is much cleaner than it was before; now both Err codemadness.org 70 i 5569 classes and interfaces share the same code for most of the Err codemadness.org 70 i 5570 boilerplate.</p> Err codemadness.org 70 i 5571 </li> Err codemadness.org 70 i 5572 <li> Err codemadness.org 70 i 5573 <p>GObject Introspection (<code>.gir</code>) generation, so that language bindings Err codemadness.org 70 i 5574 can be generated automatically.</p> Err codemadness.org 70 i 5575 </li> Err codemadness.org 70 i 5576 <li> Err codemadness.org 70 i 5577 <p>C header files (<code>.h</code>), so the generated GObjects can be called from Err codemadness.org 70 i 5578 C code as usual.</p> Err codemadness.org 70 i 5579 </li> Err codemadness.org 70 i 5580 </ul> Err codemadness.org 70 i 5581 <p>So far, Rust and GIR work; C header files are not generated yet.</p> Err codemadness.org 70 i 5582 <p>Mredlek is a new contributor to gnome-class, but unfortunately was not Err codemadness.org 70 i 5583 able to attend the hackfest. Not only did he rewrite the gnome-class Err codemadness.org 70 i 5584 parser using the new version of <a href="https://docs.rs/syn/0.15.22/syn/">syn</a>; he also added support for Err codemadness.org 70 i 5585 passing owned types to GObject methods, such as <code>String</code> and Err codemadness.org 70 i 5586 <code>Variant</code>. But the biggest thing is probably that mredlek made it a Err codemadness.org 70 i 5587 lot easier to debug the generated Rust source; see <a href="https://federico.pages.gitlab.gnome.org/gnome-class/doc/gobject_gen/#debugging-aids-and-examining-generated-code">the documentation Err codemadness.org 70 i 5588 on debugging</a> for details.</p> Err codemadness.org 70 i 5589 <p>Speaking of which, thanks to Jordan Petridis for making the Err codemadness.org 70 i 5590 documentation be published automatically from Gitlab's Continuous Err codemadness.org 70 i 5591 Integration pipelines.</p> Err codemadness.org 70 i 5592 <p>Alex Crichton kindly refactored our error propagation code, and <a href="https://federico.pages.gitlab.gnome.org/gnome-class/book/errors.html">even Err codemadness.org 70 i 5593 wrote docs on it</a>! Along with Jordan, they updated the Err codemadness.org 70 i 5594 code for the Rust 2018 edition, and generally wrangled the build Err codemadness.org 70 i 5595 process to conform with the lastest Rust nightlies. Alex also made Err codemadness.org 70 i 5596 code generation a lot faster, by offloading auto-indentation to an Err codemadness.org 70 i 5597 external <code>rustfmt</code> process, instead of using it as a crate: using the Err codemadness.org 70 i 5598 <code>rustfmt</code> crate meant that the compiler had a lot more work to do. Err codemadness.org 70 i 5599 During the whole hackfest, Alex was very helpful with Rust questions Err codemadness.org 70 i 5600 in general. While my strategy to see what the compiler does is to Err codemadness.org 70 i 5601 examine the disassembly in gdb, his strategy seems to be to look at Err codemadness.org 70 i 5602 the LLVM intermediate representation instead... OMG.</p> Err codemadness.org 70 i 5603 <h1>And we can derive very simple GtkWidgets now!</h1> Err codemadness.org 70 i 5604 <p>Saving the best for last... Antoni Boucher, the author of <a href="http://relm.ml/">relm</a>, has Err codemadness.org 70 i 5605 been working on making it possible to derive from <code>gtk::Widget</code>. Once Err codemadness.org 70 i 5606 <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/40">this merge request</a> is done, we'll have an example of Err codemadness.org 70 i 5607 deriving from <code>gtk::DrawingArea</code> from Rust with very little code.</p> Err codemadness.org 70 i 5608 <p>Normally, the <a href="https://gtk-rs.org/">gtk-rs</a> bindings work as a statically-generated binding Err codemadness.org 70 i 5609 for GObject, which really is a type hierarchy defined at runtime. The Err codemadness.org 70 i 5610 static binding really wants to know what is a subclass of what: it Err codemadness.org 70 i 5611 needs to know in advance that <code>Button</code>'s hierarchy is <code>Button → Bin → Err codemadness.org 70 i 5612 Container → Widget → Object</code>, plus all the <code>GTypeInterface</code>s supported Err codemadness.org 70 i 5613 by any of those classes. Antoni has been working on making Err codemadness.org 70 i 5614 gnome-class extract that information automatically from GIR files, so Err codemadness.org 70 i 5615 that the gtk-rs macros that define new types will get all the Err codemadness.org 70 i 5616 necessary information.</p> Err codemadness.org 70 i 5617 <h1>Future work</h1> Err codemadness.org 70 i 5618 <p>There are still <a href="https://gitlab.gnome.org/GNOME/gtk/merge_requests/415">bugs</a> in the GIR pipeline that prevent us Err codemadness.org 70 i 5619 from deriving, say, from <code>gtk::Container</code>, but hopefully these will be Err codemadness.org 70 i 5620 resolved soon.</p> Err codemadness.org 70 i 5621 <p>Sebastian Dröge has been refactoring his Rust tools to create GObject Err codemadness.org 70 i 5622 subclasses with very idiomatic and refined Rust code. This is now at Err codemadness.org 70 i 5623 a state where gnome-class itself could generate that sort of code, Err codemadness.org 70 i 5624 instead of generating all the boilerplate from scratch. So, we'll Err codemadness.org 70 i 5625 start doing that, and integrating the necessary bits into gtk-rs as Err codemadness.org 70 i 5626 well.</p> Err codemadness.org 70 i 5627 <p>Finally, during the last day I took a little break from gnome-class to Err codemadness.org 70 i 5628 work on librsvg. Julian Sparber has been updating the code to use new Err codemadness.org 70 i 5629 bindings in cairo-rs, and is also adding a <a href="https://gitlab.gnome.org/GNOME/librsvg/merge_requests/152">new API</a> to Err codemadness.org 70 i 5630 fetch an SVG element's geometry precisely.</p> Err codemadness.org 70 i 5631 <h1>Thessaloniki</h1> Err codemadness.org 70 i 5632 <p>Oh, boy, I wish the weather had been warmer. The city looks Err codemadness.org 70 i 5633 delightful to walk around, especially in the narrow streets on the Err codemadness.org 70 i 5634 hills. Can't wait to see it in summer during GUADEC.</p> Err codemadness.org 70 i 5635 <h1>Thanks</h1> Err codemadness.org 70 i 5636 <p>Finally, thanks to <a href="http://coho.gr/">CoHo</a> for hosting the hackfest, and to the GNOME Err codemadness.org 70 i 5637 Foundation for sponsoring my travel and accomodation. And to Err codemadness.org 70 i 5638 <a href="https://www.centricular.com/">Centricular</a> for taking us all to dinner!</p> Err codemadness.org 70 i 5639 <p>Special thanks to Jordan Petridis for being on top of everything Err codemadness.org 70 i 5640 build-wise all the time.</p> Err codemadness.org 70 i 5641 <p><img alt="Sponsored by the GNOME Foundation" src="https://people.gnome.org/~federico/blog/images/sponsored-by-foundation.png"></p>Propagating Errors2018-11-21T13:58:12-06:002018-11-21T13:58:12-06:00Federico Mena Quinterotag:people.gnome.org,2018-11-21:/~federico/blog/propagating-errors.html<p>Lately, I have been converting the code in librsvg that handles XML Err codemadness.org 70 i 5642 from C to Rust. For many technical reasons, the library still uses Err codemadness.org 70 i 5643 libxml2, GNOME's historic XML parsing library, but some of the Err codemadness.org 70 i 5644 callbacks to handle XML events like <code>start_element</code>, <code>end_element</code>, Err codemadness.org 70 i 5645 <code>characters</code>, are now implemented in Rust. This has …</p><p>Lately, I have been converting the code in librsvg that handles XML Err codemadness.org 70 i 5646 from C to Rust. For many technical reasons, the library still uses Err codemadness.org 70 i 5647 libxml2, GNOME's historic XML parsing library, but some of the Err codemadness.org 70 i 5648 callbacks to handle XML events like <code>start_element</code>, <code>end_element</code>, Err codemadness.org 70 i 5649 <code>characters</code>, are now implemented in Rust. This has meant that I'm Err codemadness.org 70 i 5650 running into all the cases where the original C code in librsvg failed Err codemadness.org 70 i 5651 to handle errors properly; Rust really makes it obvious when that Err codemadness.org 70 i 5652 happens.</p> Err codemadness.org 70 i 5653 <p>In this post I want to talk a bit about propagating errors. You call Err codemadness.org 70 i 5654 a function, it returns an error, and then what?</p> Err codemadness.org 70 i 5655 <h2>What can fail?</h2> Err codemadness.org 70 i 5656 <p>It turns out that this question is highly context-dependent. Let's Err codemadness.org 70 i 5657 say a program is starting up and tries to read a configuration file. Err codemadness.org 70 i 5658 What could go wrong?</p> Err codemadness.org 70 i 5659 <ul> Err codemadness.org 70 i 5660 <li> Err codemadness.org 70 i 5661 <p>The file doesn't exist. Maybe it is the very first time the program Err codemadness.org 70 i 5662 is run, and so there <em>isn't</em> a configuration file at all? Can the Err codemadness.org 70 i 5663 program provide a default configuration in this case? Or does it Err codemadness.org 70 i 5664 absolutely need a pre-written configuration file to be somewhere?</p> Err codemadness.org 70 i 5665 </li> Err codemadness.org 70 i 5666 <li> Err codemadness.org 70 i 5667 <p>The file can't be parsed. Should the program warn the user and Err codemadness.org 70 i 5668 exit, or should it revert to a default configuration (should it Err codemadness.org 70 i 5669 overwrite the file with valid, default values)? <em>Can</em> Err codemadness.org 70 i 5670 the program warn the user, or is it a user-less program that at best Err codemadness.org 70 i 5671 can just shout into the void of a server-side log file?</p> Err codemadness.org 70 i 5672 </li> Err codemadness.org 70 i 5673 <li> Err codemadness.org 70 i 5674 <p>The file can be parsed, but the values are invalid. Same questions Err codemadness.org 70 i 5675 as the case above.</p> Err codemadness.org 70 i 5676 </li> Err codemadness.org 70 i 5677 <li> Err codemadness.org 70 i 5678 <p>Etcetera.</p> Err codemadness.org 70 i 5679 </li> Err codemadness.org 70 i 5680 </ul> Err codemadness.org 70 i 5681 <p>At each stage, the code will probably see very low-level errors ("file Err codemadness.org 70 i 5682 not found", "I/O error", "parsing failed", "value is out of range"). Err codemadness.org 70 i 5683 What the code decides to do, or what it is able to do at any Err codemadness.org 70 i 5684 particular stage, depends both on the semantics you want from the Err codemadness.org 70 i 5685 program, and from the code structure itself.</p> Err codemadness.org 70 i 5686 <h2>Structuring the problem</h2> Err codemadness.org 70 i 5687 <p>This is an easy, but very coarse way of handling things:</p> Err codemadness.org 70 i 5688 <div class="highlight"><pre><span></span><code><span class="n">gboolean</span> Err codemadness.org 70 i 5689 <span class="nf">read_configuration</span> <span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">config_file_name</span><span class="p">)</span> Err codemadness.org 70 i 5690 <span class="p">{</span> Err codemadness.org 70 i 5691 <span class="cm">/* open the file */</span> Err codemadness.org 70 i 5692 Err codemadness.org 70 i 5693 <span class="cm">/* parse it */</span> Err codemadness.org 70 i 5694 Err codemadness.org 70 i 5695 <span class="cm">/* set global variables to the configuration values */</span> Err codemadness.org 70 i 5696 Err codemadness.org 70 i 5697 <span class="cm">/* return true if success, or false if failure */</span> Err codemadness.org 70 i 5698 <span class="p">}</span> Err codemadness.org 70 i 5699 </code></pre></div> Err codemadness.org 70 i 5700 Err codemadness.org 70 i 5701 <p>What is bad about this? Let's see:</p> Err codemadness.org 70 i 5702 <ul> Err codemadness.org 70 i 5703 <li> Err codemadness.org 70 i 5704 <p>The calling code just gets a success/failure condition. In the case Err codemadness.org 70 i 5705 of failure, it doesn't get to know why things failed.</p> Err codemadness.org 70 i 5706 </li> Err codemadness.org 70 i 5707 <li> Err codemadness.org 70 i 5708 <p>If the function sets global variables with configuration values as Err codemadness.org 70 i 5709 they get read... and something goes wrong and the function returns Err codemadness.org 70 i 5710 an error... the caller ends up possibly in an inconsistent state, Err codemadness.org 70 i 5711 with a set of configuration variables that are only halfway-set.</p> Err codemadness.org 70 i 5712 </li> Err codemadness.org 70 i 5713 <li> Err codemadness.org 70 i 5714 <p>If the function finds parse errors, well, do you really want to call Err codemadness.org 70 i 5715 UI code from inside it? The caller might be a better place to make Err codemadness.org 70 i 5716 that decision.</p> Err codemadness.org 70 i 5717 </li> Err codemadness.org 70 i 5718 </ul> Err codemadness.org 70 i 5719 <h2>A slightly better structure</h2> Err codemadness.org 70 i 5720 <p>Let's add an enumeration to indicate the possible errors, and a Err codemadness.org 70 i 5721 structure of configuration values.</p> Err codemadness.org 70 i 5722 <div class="highlight"><pre><span></span><code><span class="k">enum</span> <span class="nc">ConfigError</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5723 <span class="w"> </span><span class="n">ConfigFileDoesntExist</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5724 <span class="w"> </span><span class="n">ParseError</span><span class="p">,</span><span class="w"> </span><span class="c1">// config file has bad syntax or something</span> Err codemadness.org 70 i 5725 <span class="w"> </span><span class="n">ValueError</span><span class="p">,</span><span class="w"> </span><span class="c1">// config file has an invalid value</span> Err codemadness.org 70 i 5726 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5727 Err codemadness.org 70 i 5728 <span class="k">struct</span> <span class="nc">ConfigValues</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5729 <span class="w"> </span><span class="c1">// a bunch of fields here with the program&#39;s configuration</span> Err codemadness.org 70 i 5730 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5731 Err codemadness.org 70 i 5732 <span class="k">fn</span> <span class="nf">read_configuration</span><span class="p">(</span><span class="n">filename</span>: <span class="kp">&amp;</span><span class="nc">Path</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">ConfigValues</span><span class="p">,</span><span class="w"> </span><span class="n">ConfigError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5733 <span class="w"> </span><span class="c1">// open the file, or return Err(ConfigError::ConfigFileDoesntExist)</span> Err codemadness.org 70 i 5734 Err codemadness.org 70 i 5735 <span class="w"> </span><span class="c1">// parse the file; or return Err(ConfigError::ParseError)</span> Err codemadness.org 70 i 5736 Err codemadness.org 70 i 5737 <span class="w"> </span><span class="c1">// validate the values, or return Err(ConfigError::ValueError)</span> Err codemadness.org 70 i 5738 Err codemadness.org 70 i 5739 <span class="w"> </span><span class="c1">// if everything succeeds, return Ok(ConfigValues)</span> Err codemadness.org 70 i 5740 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5741 </code></pre></div> Err codemadness.org 70 i 5742 Err codemadness.org 70 i 5743 <p>This is better, in that the caller decides what to do with the Err codemadness.org 70 i 5744 validated <code>ConfigValues</code>: maybe it can just copy them to the Err codemadness.org 70 i 5745 program's global variables for configuration.</p> Err codemadness.org 70 i 5746 <p>However, this scheme doesn't give the caller all the information it Err codemadness.org 70 i 5747 would like to present a really good error message. For example, the Err codemadness.org 70 i 5748 caller will get to know if there is a parse error, but it doesn't know Err codemadness.org 70 i 5749 specifically what failed during parsing. Similarly, it will just get Err codemadness.org 70 i 5750 to know if there was an invalid value, but not which one.</p> Err codemadness.org 70 i 5751 <h2>Ah, so the problem is fractal</h2> Err codemadness.org 70 i 5752 <p>We could have new structs to represent the little errors, and then Err codemadness.org 70 i 5753 make them part of the original error enum:</p> Err codemadness.org 70 i 5754 <div class="highlight"><pre><span></span><code><span class="k">struct</span> <span class="nc">ParseError</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5755 <span class="w"> </span><span class="n">line</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5756 <span class="w"> </span><span class="n">column</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5757 <span class="w"> </span><span class="n">error_reason</span>: <span class="nb">String</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5758 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5759 Err codemadness.org 70 i 5760 <span class="k">struct</span> <span class="nc">ValueError</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5761 <span class="w"> </span><span class="n">config_key</span>: <span class="nb">String</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5762 <span class="w"> </span><span class="n">error_reason</span>: <span class="nb">String</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5763 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5764 Err codemadness.org 70 i 5765 <span class="k">enum</span> <span class="nc">ConfigError</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5766 <span class="w"> </span><span class="n">ConfigFileDoesntExist</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5767 <span class="w"> </span><span class="n">ParseError</span><span class="p">(</span><span class="n">ParseError</span><span class="p">),</span><span class="w"> </span><span class="c1">// we put those structs in here</span> Err codemadness.org 70 i 5768 <span class="w"> </span><span class="n">ValueError</span><span class="p">(</span><span class="n">ValueError</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 5769 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5770 </code></pre></div> Err codemadness.org 70 i 5771 Err codemadness.org 70 i 5772 <p>Is that enough? It depends.</p> Err codemadness.org 70 i 5773 <p>The <code>ParseError</code> and <code>ValueError</code> structs have individual Err codemadness.org 70 i 5774 <code>error_reason</code> fields, which are strings. Presumably, one could have Err codemadness.org 70 i 5775 a <code>ParseError</code> with <code>error_reason = "unexpected token"</code>, or a Err codemadness.org 70 i 5776 <code>ValueError</code> with <code>error_reason = "cannot be a negative number"</code>.</p> Err codemadness.org 70 i 5777 <p>One problem with this is that if the low-level errors come with error Err codemadness.org 70 i 5778 messages in English, then the caller has to know how to localize them Err codemadness.org 70 i 5779 to the user's language. Also, if they don't have a machine-readable Err codemadness.org 70 i 5780 error code, then the calling code may not have enough information to Err codemadness.org 70 i 5781 decide what do do with the error.</p> Err codemadness.org 70 i 5782 <p>Let's say we had a <code>ParseErrorKind</code> enum with variants like Err codemadness.org 70 i 5783 <code>UnexpectedToken</code>, <code>EndOfFile</code>, etc. This is fine; it lets the Err codemadness.org 70 i 5784 calling code know the <em>reason</em> for the error. Also, there can be a Err codemadness.org 70 i 5785 <code>gimme_localized_error_message()</code> method for that particular type of Err codemadness.org 70 i 5786 error.</p> Err codemadness.org 70 i 5787 <div class="highlight"><pre><span></span><code><span class="k">enum</span> <span class="nc">ParseErrorKind</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5788 <span class="w"> </span><span class="n">UnexpectedToken</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5789 <span class="w"> </span><span class="n">EndOfFile</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5790 <span class="w"> </span><span class="n">MissingComma</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5791 <span class="w"> </span><span class="c1">// ... etc.</span> Err codemadness.org 70 i 5792 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5793 Err codemadness.org 70 i 5794 <span class="k">struct</span> <span class="nc">ParseError</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5795 <span class="w"> </span><span class="n">line</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5796 <span class="w"> </span><span class="n">column</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5797 <span class="w"> </span><span class="n">kind</span>: <span class="nc">ParseErrorKind</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 5798 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5799 </code></pre></div> Err codemadness.org 70 i 5800 Err codemadness.org 70 i 5801 <p>How can we expand this? Maybe the <code>ParseErrorKind::UnexpectedToken</code> Err codemadness.org 70 i 5802 variant wants to contain data that indicates <em>which</em> token it got that Err codemadness.org 70 i 5803 was wrong, so it would be <code>UnexpectedToken(String)</code> or something Err codemadness.org 70 i 5804 similar.</p> Err codemadness.org 70 i 5805 <p>But is <em>that</em> useful to the calling code? For our example program, Err codemadness.org 70 i 5806 which is reading a configuration file... it probably only needs to Err codemadness.org 70 i 5807 know if it could parse the file, but maybe it doesn't really need any Err codemadness.org 70 i 5808 additional details on the reason for the parse error, other than Err codemadness.org 70 i 5809 having something useful to present to the user. Whether it is Err codemadness.org 70 i 5810 appropriate to burden the user with the actual details... does the app Err codemadness.org 70 i 5811 expect to make it the user's job to fix broken configuration files? Err codemadness.org 70 i 5812 Yes for a web server, where the user is a sysadmin; probably not for a Err codemadness.org 70 i 5813 random end-user graphical app, where people shouldn't need to write Err codemadness.org 70 i 5814 configuration files by hand in the first place (should <em>those</em> have a Err codemadness.org 70 i 5815 "Details" section in the error message window? I don't know!).</p> Err codemadness.org 70 i 5816 <p>Maybe the low-level parsing/validation code <em>can</em> emit those detailed Err codemadness.org 70 i 5817 errors. But how can we propagate them to something more useful to the Err codemadness.org 70 i 5818 upper layers of the code?</p> Err codemadness.org 70 i 5819 <h2>Translation and propagation</h2> Err codemadness.org 70 i 5820 <p>Maybe our original <code>read_configuration()</code> function can translate the Err codemadness.org 70 i 5821 low-level errors into high-level ones:</p> Err codemadness.org 70 i 5822 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">read_configuration</span><span class="p">(</span><span class="n">filename</span>: <span class="kp">&amp;</span><span class="nc">Path</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">ConfigValues</span><span class="p">,</span><span class="w"> </span><span class="n">ConfigError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5823 <span class="w"> </span><span class="c1">// open file</span> Err codemadness.org 70 i 5824 Err codemadness.org 70 i 5825 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">cannot_open_file</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 5826 <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">ConfigError</span>::<span class="n">ConfigFileDoesntExist</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 5827 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5828 Err codemadness.org 70 i 5829 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">read_the_file</span><span class="p">().</span><span class="n">map_err</span><span class="p">(</span><span class="o">|</span><span class="n">e</span><span class="o">|</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="n">oops</span><span class="p">,</span><span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="n">need</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">IoError</span><span class="w"> </span><span class="n">case</span><span class="p">,</span><span class="w"> </span><span class="n">too</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5830 Err codemadness.org 70 i 5831 <span class="w"> </span><span class="c1">// parse file</span> Err codemadness.org 70 i 5832 Err codemadness.org 70 i 5833 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">parsed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parse</span><span class="p">(</span><span class="n">contents</span><span class="p">).</span><span class="n">map_err</span><span class="p">(</span><span class="o">|</span><span class="n">e</span><span class="o">|</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="n">translate</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">higher</span><span class="o">-</span><span class="n">level</span><span class="w"> </span><span class="n">error</span><span class="p">)</span><span class="o">?</span><span class="w"></span> Err codemadness.org 70 i 5834 Err codemadness.org 70 i 5835 <span class="w"> </span><span class="c1">// validate</span> Err codemadness.org 70 i 5836 Err codemadness.org 70 i 5837 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">validated</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">validate</span><span class="p">(</span><span class="n">parsed</span><span class="p">).</span><span class="n">map_err</span><span class="p">(</span><span class="o">|</span><span class="n">e</span><span class="o">|</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="n">translate</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">higher</span><span class="o">-</span><span class="n">level</span><span class="w"> </span><span class="n">error</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 5838 Err codemadness.org 70 i 5839 <span class="w"> </span><span class="c1">// yay!</span> Err codemadness.org 70 i 5840 <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">ConfigValues</span>::<span class="n">from</span><span class="p">(</span><span class="n">validated</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 5841 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 5842 </code></pre></div> Err codemadness.org 70 i 5843 Err codemadness.org 70 i 5844 <p>Etcetera. It is up to each part of the code to decide what do do with Err codemadness.org 70 i 5845 lower-level errors. Can it recover from them? Should it fail the Err codemadness.org 70 i 5846 whole operation and return a higher-level error? Should it warn the Err codemadness.org 70 i 5847 user right there?</p> Err codemadness.org 70 i 5848 <h2>Language facilities</h2> Err codemadness.org 70 i 5849 <p>C makes it really easy to ignore errors, and pretty hard to present Err codemadness.org 70 i 5850 detailed errors like the above. One could mimic what Rust is actually Err codemadness.org 70 i 5851 doing with a collection of <code>union</code> and <code>struct</code> and <code>enum</code>, but this Err codemadness.org 70 i 5852 gets very awkward very fast.</p> Err codemadness.org 70 i 5853 <p>Rust provides these facilities at the language level, and the idioms Err codemadness.org 70 i 5854 around <code>Result</code> and error handling are very nice to use. There are Err codemadness.org 70 i 5855 even crates like <a href="https://boats.gitlab.io/failure/intro.html"><code>failure</code></a> that go a long way towards Err codemadness.org 70 i 5856 automating error translation, propagation, and conversion to strings Err codemadness.org 70 i 5857 for presenting to users.</p> Err codemadness.org 70 i 5858 <h2>Infinite details</h2> Err codemadness.org 70 i 5859 <p>I've been recommending <a href="http://joeduffyblog.com/2016/02/07/the-error-model/">The Error Model</a> to anyone who Err codemadness.org 70 i 5860 comes into a discussion of error handling in programming languages. Err codemadness.org 70 i 5861 It's a long, detailed, but very enlightening read on recoverable Err codemadness.org 70 i 5862 vs. unrecoverable errors, simple error codes vs. exceptions Err codemadness.org 70 i 5863 vs. monadic results, the performance/reliability/ease of use of each Err codemadness.org 70 i 5864 model... Definitely worth a read.</p>My gdk-pixbuf braindump2018-09-05T21:35:41-05:002018-09-06T07:49:38-05:00Federico Mena Quinterotag:people.gnome.org,2018-09-05:/~federico/blog/my-gdk-pixbuf-braindump.html<p>I want to write a braindump on the stuff that I remember from Err codemadness.org 70 i 5865 gdk-pixbuf's history. There is some talk about replacing it with Err codemadness.org 70 i 5866 something newer; hopefully this history will show some things that Err codemadness.org 70 i 5867 worked, some that didn't, and why.</p> Err codemadness.org 70 i 5868 <h2>The beginnings</h2> Err codemadness.org 70 i 5869 <p>Gdk-pixbuf started as a replacement for Imlib, the image …</p><p>I want to write a braindump on the stuff that I remember from Err codemadness.org 70 i 5870 gdk-pixbuf's history. There is some talk about replacing it with Err codemadness.org 70 i 5871 something newer; hopefully this history will show some things that Err codemadness.org 70 i 5872 worked, some that didn't, and why.</p> Err codemadness.org 70 i 5873 <h2>The beginnings</h2> Err codemadness.org 70 i 5874 <p>Gdk-pixbuf started as a replacement for Imlib, the image loading and Err codemadness.org 70 i 5875 rendering library that GNOME used in its earliest versions. Imlib Err codemadness.org 70 i 5876 came from the Enlightenment project; it provided an easy API around Err codemadness.org 70 i 5877 the idiosyncratic libungif, libjpeg, libpng, etc., and it maintained Err codemadness.org 70 i 5878 decoded images in memory with a uniform representation. Imlib also Err codemadness.org 70 i 5879 worked as an image cache for the Enlightenment window manager, which Err codemadness.org 70 i 5880 made memory management very inconvenient for GNOME.</p> Err codemadness.org 70 i 5881 <p>Imlib worked well as a "just load me an image" library. It showed Err codemadness.org 70 i 5882 that a small, uniform API to load various image formats into a common Err codemadness.org 70 i 5883 representation was desirable. And in those days, hiding all the Err codemadness.org 70 i 5884 complexities of displaying images in X was very important indeed.</p> Err codemadness.org 70 i 5885 <h2>The initial API</h2> Err codemadness.org 70 i 5886 <p>Gdk-pixbuf replaced Imlib, and added two important features: Err codemadness.org 70 i 5887 reference counting for image data, and support for an alpha channel.</p> Err codemadness.org 70 i 5888 <p>Gdk-pixbuf appeared with support for RGB(A) images. And although in Err codemadness.org 70 i 5889 theory it was possible to grow the API to support other Err codemadness.org 70 i 5890 representations, <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf-core.h#L132-144"><code>GdkColorspace</code></a> never acquired anything other than Err codemadness.org 70 i 5891 <code>GDK_COLORSPACE_RGB</code>, and the <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf-core.h#L269-271"><code>bits_per_sample</code></a> argument to some Err codemadness.org 70 i 5892 functions <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf.c#L478">only ever supported being <code>8</code></a>. The presence or absence of an alpha Err codemadness.org 70 i 5893 channel was done with a <code>gboolean</code> argument in conjunction with that Err codemadness.org 70 i 5894 single <code>GDK_COLORSPACE_RGB</code> value; we didn't have something like Err codemadness.org 70 i 5895 <a href="https://gitlab.freedesktop.org/cairo/cairo/blob/201791a5/src/cairo.h#L385-424"><code>cairo_format_t</code></a> which actually specifies the pixel format in single Err codemadness.org 70 i 5896 enum values.</p> Err codemadness.org 70 i 5897 <p>While all the code in gdk-pixbuf carefully checks that those Err codemadness.org 70 i 5898 conditions are met — RGBA at 8 bits per channel —, some applications Err codemadness.org 70 i 5899 inadvertently assume that <em>that</em> is the only possible case, and would get Err codemadness.org 70 i 5900 into trouble really fast if gdk-pixbuf ever started returning pixbufs Err codemadness.org 70 i 5901 with different color spaces or depths.</p> Err codemadness.org 70 i 5902 <p>One can still see the battle between bilevel-alpha Err codemadness.org 70 i 5903 vs. continuous-alpha in <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf-core.h#L108-130">this enum</a>:</p> Err codemadness.org 70 i 5904 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">enum</span> Err codemadness.org 70 i 5905 <span class="p">{</span> Err codemadness.org 70 i 5906 <span class="n">GDK_PIXBUF_ALPHA_BILEVEL</span><span class="p">,</span> Err codemadness.org 70 i 5907 <span class="n">GDK_PIXBUF_ALPHA_FULL</span> Err codemadness.org 70 i 5908 <span class="p">}</span> <span class="n">GdkPixbufAlphaMode</span><span class="p">;</span> Err codemadness.org 70 i 5909 </code></pre></div> Err codemadness.org 70 i 5910 Err codemadness.org 70 i 5911 <p>Fortunately, only the "<a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/contrib/gdk-pixbuf-xlib/gdk-pixbuf-xlib.h#L62-70">render this pixbuf with alpha to an Xlib Err codemadness.org 70 i 5912 drawable</a>" functions take values of this type: before the Xrender Err codemadness.org 70 i 5913 days, it was a Big Deal to draw an image with alpha to an X window, Err codemadness.org 70 i 5914 and applications often opted to use a bitmask instead, even if they Err codemadness.org 70 i 5915 had jagged edges as a result.</p> Err codemadness.org 70 i 5916 <h2>Pixel formats</h2> Err codemadness.org 70 i 5917 <p>The only pixel format that ever got implemented was unpremultiplied Err codemadness.org 70 i 5918 RGBA on all platforms. Back then I didn't understand <a href="https://keithp.com/~keithp/porterduff/p253-porter.pdf">premultiplied Err codemadness.org 70 i 5919 alpha</a>! Also, the GIMP followed that scheme, and copying Err codemadness.org 70 i 5920 it seemed like the easiest thing.</p> Err codemadness.org 70 i 5921 <p>After gdk-pixbuf, libart also copied that pixel format, I think.</p> Err codemadness.org 70 i 5922 <p>But later we got Cairo, Pixman, and all the Xrender stack. These Err codemadness.org 70 i 5923 prefer premultiplied ARGB. Moreover, Cairo prefers it if each pixel Err codemadness.org 70 i 5924 is actually a 32-bit value, with the ARGB values inside it in Err codemadness.org 70 i 5925 platform-endian order. So if you look at a memory dump, a Cairo pixel Err codemadness.org 70 i 5926 looks like BGRA on a little-endian box, while it looks like ARGB on a Err codemadness.org 70 i 5927 big-endian box.</p> Err codemadness.org 70 i 5928 <p>Every time we paint a <code>GdkPixbuf</code> to a <code>cairo_t</code>, there is a Err codemadness.org 70 i 5929 conversion from unpremultiplied RGBA to premultiplied, platform-endian Err codemadness.org 70 i 5930 ARGB. I talked a bit about this in <a href="https://people.gnome.org/~federico/blog/reducing-image-copies.html">Reducing the number of image Err codemadness.org 70 i 5931 copies in GNOME</a>.</p> Err codemadness.org 70 i 5932 <h2>The loading API</h2> Err codemadness.org 70 i 5933 <p>The public loading API in gdk-pixbuf, and its relationship to loader Err codemadness.org 70 i 5934 plug-ins, evolved in interesting ways.</p> Err codemadness.org 70 i 5935 <p>At first the public API and loaders only implemented <code>load_from_file</code>: Err codemadness.org 70 i 5936 you gave the library a <code>FILE *</code> and it gave you back a <code>GdkPixbuf</code>. Err codemadness.org 70 i 5937 Back then we didn't have a robust MIME sniffing framework in the form Err codemadness.org 70 i 5938 of a library, so gdk-pixbuf got its own. This lives in the Err codemadness.org 70 i 5939 mostly-obsolete <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf-io.h#L329-359"><code>GdkPixbufFormat</code></a> machinery; it Err codemadness.org 70 i 5940 even has its own <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf-io.c#L125-175">little language</a> for sniffing file headers! Err codemadness.org 70 i 5941 Nowadays we do most MIME sniffing with GIO.</p> Err codemadness.org 70 i 5942 <p>After the intial <code>load_from_file</code> API... I think we got progressive Err codemadness.org 70 i 5943 loading first, and animation support aftewards.</p> Err codemadness.org 70 i 5944 <h2>Progressive loading</h2> Err codemadness.org 70 i 5945 <p>This where the calling program feeds chunks of bytes to the library, Err codemadness.org 70 i 5946 and at the end a fully-formed <code>GdkPixbuf</code> comes out, instead of having Err codemadness.org 70 i 5947 a single "read a whole file" operation.</p> Err codemadness.org 70 i 5948 <p>We conflated this with a way to get <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf-loader.h#L72-77">updates on how the image area gets Err codemadness.org 70 i 5949 modified</a> as the data gets parsed. I think we wanted to support the Err codemadness.org 70 i 5950 case of a web browser, which downloads images slowly over the network, Err codemadness.org 70 i 5951 and gradually displays them as they are downloaded. In 1998, images Err codemadness.org 70 i 5952 downloading slowly over the network was a real concern!</p> Err codemadness.org 70 i 5953 <p>It took a lot of very careful work to convert the image loaders, which Err codemadness.org 70 i 5954 parsed a whole file at a time, into loaders that could maintain some Err codemadness.org 70 i 5955 state between each time that they got handed an extra bit of buffer.</p> Err codemadness.org 70 i 5956 <p>It also sounded easy to implement the progressive updating API by Err codemadness.org 70 i 5957 simply emitting a signal that said, "this rectangular area got updated Err codemadness.org 70 i 5958 from the last read". It could handle the case of reading whole Err codemadness.org 70 i 5959 scanlines, or a few pixels, or even area-based updates for progressive Err codemadness.org 70 i 5960 JPEGs and PNGs.</p> Err codemadness.org 70 i 5961 <p>The internal API for the image format loaders still keeps a Err codemadness.org 70 i 5962 distinction between the "load a whole file" API and the "load an image Err codemadness.org 70 i 5963 in chunks". Not all loaders got redone to simply just use the second Err codemadness.org 70 i 5964 one: <code>io-jpeg.c</code> <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/io-jpeg.c#L554-722">still implements loading whole files</a> by calling the Err codemadness.org 70 i 5965 corresponding libjpeg functions. I think it could remove that code Err codemadness.org 70 i 5966 and use the progressive loading functions instead.</p> Err codemadness.org 70 i 5967 <h2>Animations</h2> Err codemadness.org 70 i 5968 <p>Animations: we followed the GIF model for animations, in which each Err codemadness.org 70 i 5969 frame overlays the previous one, and there's a delay set between each Err codemadness.org 70 i 5970 frame. This is not a video file; it's a hacky flipbook.</p> Err codemadness.org 70 i 5971 <p>However, animations presented the problem that the whole gdk-pixbuf Err codemadness.org 70 i 5972 API was meant for static images, and now we needed to support Err codemadness.org 70 i 5973 multi-frame images as well.</p> Err codemadness.org 70 i 5974 <p>We defined the "correct" way to use the gdk-pixbuf library as to Err codemadness.org 70 i 5975 actually try to load an animation, and then see if it is a Err codemadness.org 70 i 5976 single-frame image, in which case you can just get a <code>GdkPixbuf</code> for Err codemadness.org 70 i 5977 the only frame and use it.</p> Err codemadness.org 70 i 5978 <p>Or, if you got an animation, that would be a <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf-animation.h"><code>GdkPixbufAnimation</code></a> Err codemadness.org 70 i 5979 object, from which you could ask for an iterator to get each frame as Err codemadness.org 70 i 5980 a separate <code>GdkPixbuf</code>.</p> Err codemadness.org 70 i 5981 <p>However, the progressive updating API never got extended to really Err codemadness.org 70 i 5982 support animations. So, we have awkward functions like Err codemadness.org 70 i 5983 <code>gdk_pixbuf_animation_iter_on_currently_loading_frame()</code> instead.</p> Err codemadness.org 70 i 5984 <h2>Necessary accretion</h2> Err codemadness.org 70 i 5985 <p>Gdk-pixbuf got support for saving just a few formats: JPEG, PNG, Err codemadness.org 70 i 5986 TIFF, ICO, and some of the formats that are implemented with the Err codemadness.org 70 i 5987 Windows-native loaders.</p> Err codemadness.org 70 i 5988 <p>Over time gdk-pixbuf got support for preserving some metadata-ish Err codemadness.org 70 i 5989 chunks from formats that provide it: DPI, color profiles, image Err codemadness.org 70 i 5990 comments, hotspots for cursors/icons...</p> Err codemadness.org 70 i 5991 <p>While an image is being loaded with the progressive loaders, there is Err codemadness.org 70 i 5992 a clunky way to specify that one doesn't want the actual size of the Err codemadness.org 70 i 5993 image, but another size instead. The loader can handle that situation Err codemadness.org 70 i 5994 itself, hopefully if an image format actually embeds different sizes Err codemadness.org 70 i 5995 in it. Or if not, the main loading code will rescale the full loaded Err codemadness.org 70 i 5996 image into the size specified by the application.</p> Err codemadness.org 70 i 5997 <h2>Historical cruft</h2> Err codemadness.org 70 i 5998 <p><a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixdata.h"><code>GdkPixdata</code></a> - a way to embed binary image data in executables, with a Err codemadness.org 70 i 5999 funky encoding. Nowadays it's just easier to directly store a PNG or Err codemadness.org 70 i 6000 JPEG or whatever in a <code>GResource</code>.</p> Err codemadness.org 70 i 6001 <p><a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/contrib/gdk-pixbuf-xlib/"><code>contrib/gdk-pixbuf-xlib</code></a> - to deal with old-style X drawables. Err codemadness.org 70 i 6002 Hopefully mostly unused now, but there's a good number of mostly old, Err codemadness.org 70 i 6003 third-party software that still uses gdk-pixbuf as an image loader and Err codemadness.org 70 i 6004 renderer to X drawables.</p> Err codemadness.org 70 i 6005 <p><a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf-transform.h"><code>gdk-pixbuf-transform.h</code></a> - Gdk-pixbuf had some very high-quality Err codemadness.org 70 i 6006 scaling functions, which the original versions of EOG used for the Err codemadness.org 70 i 6007 core of the image viewer. Nowadays Cairo is the preferred way of Err codemadness.org 70 i 6008 doing this, since it not only does scaling, but general affine Err codemadness.org 70 i 6009 transformations as well. Did you know that Err codemadness.org 70 i 6010 <code>gdk_pixbuf_composite_color</code> takes 17 arguments, and it can composite Err codemadness.org 70 i 6011 an image with alpha on top of a checkerboard? Yes, that used to be Err codemadness.org 70 i 6012 the core of EOG.</p> Err codemadness.org 70 i 6013 <h2>Debatable historical cruft</h2> Err codemadness.org 70 i 6014 <p><code>gdk_pixbuf_get_pixels()</code>. This lets the program look into the actual Err codemadness.org 70 i 6015 pixels of a loaded pixbuf, and modify them. Gdk-pixbuf just did not Err codemadness.org 70 i 6016 have a concept of immutability.</p> Err codemadness.org 70 i 6017 <p>Back in GNOME 1.x / 2.x, when it was fashionable to put icons beside Err codemadness.org 70 i 6018 menu items, or in toolbar buttons, applications would load their icon Err codemadness.org 70 i 6019 images, and modify them in various ways before setting them onto the Err codemadness.org 70 i 6020 corresponding widgets. Some things they did: load a colorful icon, Err codemadness.org 70 i 6021 desaturate it for "insensitive" command buttons or menu items, or Err codemadness.org 70 i 6022 simulate desaturation by compositing a 1x1-pixel checkerboard on the Err codemadness.org 70 i 6023 icon image. Or lighten the icon and set it as the "prelight" one onto Err codemadness.org 70 i 6024 widgets.</p> Err codemadness.org 70 i 6025 <p>The concept of "decode an image and just give me the pixels" is of Err codemadness.org 70 i 6026 course useful. Image viewers, image processing programs, and all Err codemadness.org 70 i 6027 those, of course need this functionality.</p> Err codemadness.org 70 i 6028 <p>However, these days GTK would prefer to have a way to decode an image, Err codemadness.org 70 i 6029 and ship it as fast as possible ot the GPU, without intermediaries. Err codemadness.org 70 i 6030 There is all sorts of awkward machinery in the GTK widgets that Err codemadness.org 70 i 6031 can consume either an icon from an icon theme, or a user-supplied Err codemadness.org 70 i 6032 image, or one of the various schemes for providing icons that GTK has Err codemadness.org 70 i 6033 acquired over the years.</p> Err codemadness.org 70 i 6034 <p>It is interesting to note that <code>gdk_pixbuf_get_pixels()</code> was available Err codemadness.org 70 i 6035 pretty much since the beginning, but it was only until much later that Err codemadness.org 70 i 6036 we got <code>gdk_pixbuf_get_pixels_with_length()</code>, the "give me the <code>guchar Err codemadness.org 70 i 6037 *</code> buffer and also its length" function, so that calling code has a Err codemadness.org 70 i 6038 chance of actually checking for buffer overruns. (... and it is one Err codemadness.org 70 i 6039 of the broken "give me a length" functions that returns a <code>guint</code> Err codemadness.org 70 i 6040 rather than a <code>gsize</code>. There is a better Err codemadness.org 70 i 6041 <code>gdk_pixbuf_get_byte_length()</code> which actually returns a <code>gsize</code>, Err codemadness.org 70 i 6042 though.)</p> Err codemadness.org 70 i 6043 <h2>Problems with mutable pixbufs</h2> Err codemadness.org 70 i 6044 <p>The main problem is that as things are right now, we have no Err codemadness.org 70 i 6045 flexibility in changing the internal representation of image data to Err codemadness.org 70 i 6046 make it better for current idioms: GPU-specific pixel formats may not Err codemadness.org 70 i 6047 be unpremultiplied RGBA data.</p> Err codemadness.org 70 i 6048 <p>We have no API to say, "this pixbuf has been modified", akin to Err codemadness.org 70 i 6049 <code>cairo_surface_mark_dirty()</code>: once an application calls Err codemadness.org 70 i 6050 <code>gdk_pixbuf_get_pixels()</code>, gdk-pixbuf or GTK have to assume that the Err codemadness.org 70 i 6051 data <em>will</em> be changed and they have to re-run the pipeline to send Err codemadness.org 70 i 6052 the image to the GPU (format conversions? caching? creating a Err codemadness.org 70 i 6053 texture?).</p> Err codemadness.org 70 i 6054 <p>Also, ever since the beginnings of the gdk-pixbuf API, we had a way to Err codemadness.org 70 i 6055 create pixbufs from arbitrary user-supplied RGBA buffers: the Err codemadness.org 70 i 6056 <code>gdk_pixbuf_new_from_data</code> functions. One problem with this scheme is Err codemadness.org 70 i 6057 that memory management of the buffer is up to the calling application, Err codemadness.org 70 i 6058 so the resulting pixbuf isn't free to handle those resources as it Err codemadness.org 70 i 6059 pleases.</p> Err codemadness.org 70 i 6060 <p>A relatively recent addition is <code>gdk_pixbuf_new_from_bytes()</code>, which Err codemadness.org 70 i 6061 takes a <code>GBytes</code> buffer instead of a random <code>guchar *</code>. When a pixbuf Err codemadness.org 70 i 6062 is created that way, it is <em>assumed</em> to be immutable, since a <code>GBytes</code> Err codemadness.org 70 i 6063 is basically a shared reference into a byte buffer, and it's just Err codemadness.org 70 i 6064 easier to think of it as immutable. (Nothing in C actually enforces Err codemadness.org 70 i 6065 immutability, but the API indicates that convention.)</p> Err codemadness.org 70 i 6066 <p>Internally, <code>GdkPixbuf</code> actually prefers to be created from a Err codemadness.org 70 i 6067 <code>GBytes</code>. It will <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/blob/36939ecb/gdk-pixbuf/gdk-pixbuf.c#L719-743">downgrade itself</a> to a <code>guchar *</code> buffer if Err codemadness.org 70 i 6068 something calls the old <code>gdk_pixbuf_get_pixels()</code>; in the best case, Err codemadness.org 70 i 6069 that will just take ownership of the internal buffer from the Err codemadness.org 70 i 6070 <code>GBytes</code> (if the <code>GBytes</code> has a single reference count); in the worst Err codemadness.org 70 i 6071 case, it will copy the buffer from the <code>GBytes</code> and retain ownership Err codemadness.org 70 i 6072 of that copy. In either case, when the pixbuf downgrades itself to Err codemadness.org 70 i 6073 pixels, it is assumed that the calling application will modify the Err codemadness.org 70 i 6074 pixel data.</p> Err codemadness.org 70 i 6075 <h2>What would immutable pixbufs look like?</h2> Err codemadness.org 70 i 6076 <p>I mentioned this a bit in "<a href="https://people.gnome.org/~federico/blog/reducing-image-copies.html">Reducing Copies</a>". The Err codemadness.org 70 i 6077 loaders in gdk-pixbuf would create immutable pixbufs, with an internal Err codemadness.org 70 i 6078 representation that is friendly to GPUs. In the proposed scheme, that Err codemadness.org 70 i 6079 internal representation would be a Cairo image surface; it can be Err codemadness.org 70 i 6080 something else if GTK/GDK eventually prefer a different way of Err codemadness.org 70 i 6081 shipping image data into the toolkit.</p> Err codemadness.org 70 i 6082 <p>Those pixbufs would be immutable. In true C fashion we can call it Err codemadness.org 70 i 6083 undefined behavior to change the pixel data (say, an app could request Err codemadness.org 70 i 6084 <code>gimme_the_cairo_surface</code> and tweak it, but that would not be Err codemadness.org 70 i 6085 supported).</p> Err codemadness.org 70 i 6086 <p>I think we could also have a "just give me the pixels" API, and a Err codemadness.org 70 i 6087 "create a pixbuf from these pixels" one, but those would be one-time Err codemadness.org 70 i 6088 conversions at the edge of the API. Internally, the pixel data that Err codemadness.org 70 i 6089 actually lives inside a <code>GdkPixbuf</code> would remain immutable, in some Err codemadness.org 70 i 6090 preferred representation, which is not necessarily what the Err codemadness.org 70 i 6091 application sees.</p> Err codemadness.org 70 i 6092 <h2>What worked well</h2> Err codemadness.org 70 i 6093 <p>A small API to load multiple image formats, and paint the images Err codemadness.org 70 i 6094 easily to the screen, while handling most of the X awkwardness Err codemadness.org 70 i 6095 semi-automatically, was very useful!</p> Err codemadness.org 70 i 6096 <p>A way to get and modify pixel data: applications clearly like doing Err codemadness.org 70 i 6097 this. We can formalize it as an application-side thing only, and keep Err codemadness.org 70 i 6098 the internal representation immutable and in a format that can evolve Err codemadness.org 70 i 6099 according to the needs of the internal API.</p> Err codemadness.org 70 i 6100 <p>Pluggable loaders, up to a point. Gdk-pixbuf doesn't support all the Err codemadness.org 70 i 6101 image formats in the world out of the box, but it is relatively easy Err codemadness.org 70 i 6102 for third-parties to provide loaders that, once installed, are Err codemadness.org 70 i 6103 automatically usable for all applications.</p> Err codemadness.org 70 i 6104 <h2>What didn't work well</h2> Err codemadness.org 70 i 6105 <p>Having effectively two pixel formats supported, and nothing else: Err codemadness.org 70 i 6106 gdk-pixbuf does packed RGB and unpremultiplied RGBA, and that's it. Err codemadness.org 70 i 6107 This isn't completely terrible: applications which really want to Err codemadness.org 70 i 6108 know about indexed or grayscale images, or high bit-depth ones, are Err codemadness.org 70 i 6109 <em>probably</em> specialized enough that they can afford to have their own Err codemadness.org 70 i 6110 custom loaders with all the functionality they need.</p> Err codemadness.org 70 i 6111 <p>Pluggable loaders, up to a point. While it is relatively easy to Err codemadness.org 70 i 6112 create third-party loaders, installation is awkward from a system's Err codemadness.org 70 i 6113 perspective: one has to run the script to regenerate the loader cache, Err codemadness.org 70 i 6114 there are more shared libraries running around, and the loaders are Err codemadness.org 70 i 6115 not sandboxed by default.</p> Err codemadness.org 70 i 6116 <p>I'm not sure if it's worthwhile to let any application read "any" Err codemadness.org 70 i 6117 image format if gdk-pixbuf supports it. If your word processor lets Err codemadness.org 70 i 6118 you paste an image into the document... do you want it to use Err codemadness.org 70 i 6119 gdk-pixbuf's limited view of things and include a high bit-depth image Err codemadness.org 70 i 6120 with its probably inadequate conversions? Or would you rather do some Err codemadness.org 70 i 6121 processing by hand to ensure that the image looks as good as it can, Err codemadness.org 70 i 6122 in the format that your word processor actually supports? I don't Err codemadness.org 70 i 6123 know.</p> Err codemadness.org 70 i 6124 <p>The API for animations is very awkward. We don't even support Err codemadness.org 70 i 6125 APNG... but honestly I don't recall actually seeing one of those in Err codemadness.org 70 i 6126 the wild.</p> Err codemadness.org 70 i 6127 <p>The progressive loading API is awkward. The "feed some bytes into the Err codemadness.org 70 i 6128 loader" part is mostly okay; the "notify me about changes to the pixel Err codemadness.org 70 i 6129 data" is questionable nowadays. Web browsers don't use it; they Err codemadness.org 70 i 6130 implement their own loaders. Even EOG doesn't use it.</p> Err codemadness.org 70 i 6131 <p>I think most code that actually connects to <code>GdkPixbufLoader</code>'s Err codemadness.org 70 i 6132 signals only uses the <code>size-prepared</code> signal — the one that gets Err codemadness.org 70 i 6133 emitted soon after reading the image headers, when the loader gets to Err codemadness.org 70 i 6134 know the dimensions of the image. Apps sometimes use this to say, Err codemadness.org 70 i 6135 "this image is W*H pixels in size", but don't actually decode the Err codemadness.org 70 i 6136 rest of the image.</p> Err codemadness.org 70 i 6137 <p>The gdk-pixbuf model of static images, or GIF animations, doesn't work Err codemadness.org 70 i 6138 well for multi-page TIFFs. I'm not sure if this is actualy a problem. Err codemadness.org 70 i 6139 Again, applications with actual needs for multi-page TIFFs are Err codemadness.org 70 i 6140 probably specialized enough that they will want a full-featured TIFF Err codemadness.org 70 i 6141 loader of their own.</p> Err codemadness.org 70 i 6142 <h2>Awkward architectures</h2> Err codemadness.org 70 i 6143 <h3>Thumbnailers</h3> Err codemadness.org 70 i 6144 <p>The thumbnailing system has slowly been moving towards a model where Err codemadness.org 70 i 6145 we actually have thumbnailers specific to each file format, instead of Err codemadness.org 70 i 6146 just assuming that we can dump any image into a gdk-pixbuf loader.</p> Err codemadness.org 70 i 6147 <p>If we take this all the way, we would be able to remove some weird Err codemadness.org 70 i 6148 code in, for example, the JPEG pixbuf loader. Right now it supports Err codemadness.org 70 i 6149 loading images at a size that the calling code requests, not only at Err codemadness.org 70 i 6150 the "natural" size of the JPEG. The thumbnailer can say, "I want to Err codemadness.org 70 i 6151 load this JPEG at 128x128 pixels" or whatever, and <em>in theory</em> the Err codemadness.org 70 i 6152 JPEG loader will do the minimal amount of work required to do that. Err codemadness.org 70 i 6153 It's not 100% clear to me if this is actually working as intended, or Err codemadness.org 70 i 6154 if we downscale the whole image anyway.</p> Err codemadness.org 70 i 6155 <p>We had a distinction between in-process and out-of-process Err codemadness.org 70 i 6156 thumbnailers, and it had to do with the way pixbuf loaders are used; Err codemadness.org 70 i 6157 I'm not sure if they are all out-of-process and sandboxed now.</p> Err codemadness.org 70 i 6158 <h3>Non-raster data</h3> Err codemadness.org 70 i 6159 <p>There is a gdk-pixbuf loader for SVG images which uses librsvg Err codemadness.org 70 i 6160 internally, but only in a very basic way: it simply loads the SVG at Err codemadness.org 70 i 6161 its preferred size. Librsvg jumps through some hoops to compute a Err codemadness.org 70 i 6162 "preferred size" for SVGs, as not all of them actually indicate one. Err codemadness.org 70 i 6163 The SVG model would rather have the renderer say that the SVG is to be Err codemadness.org 70 i 6164 inserted into a rectangle of certain width/height, and Err codemadness.org 70 i 6165 scaled/positioned inside the rectangle according to some other Err codemadness.org 70 i 6166 parameters (i.e. like one would put it inside an HTML document, with a Err codemadness.org 70 i 6167 <code>preserveAspectRatio</code> attribute and all that). GNOME applications Err codemadness.org 70 i 6168 historically operated with a different model, one of "load me an Err codemadness.org 70 i 6169 image, I'll scale it to whatever size, and paint it".</p> Err codemadness.org 70 i 6170 <p>This gdk-pixbuf loader for SVG files gets used for the SVG Err codemadness.org 70 i 6171 thumbnailer, or more accurately, the "throw random images into a Err codemadness.org 70 i 6172 gdk-pixbuf loader" thumbnailer. It may be better/cleaner to have a Err codemadness.org 70 i 6173 specific thumbnailer for SVGs instead.</p> Err codemadness.org 70 i 6174 <p>Even EOG, our by-default image viewer, doesn't use the gdk-pixbuf Err codemadness.org 70 i 6175 loader for SVGs: it actually special-cases them and uses librsvg Err codemadness.org 70 i 6176 directly, to be able to load an SVG once and re-render it at different Err codemadness.org 70 i 6177 sizes if one changes the zoom factor, for example.</p> Err codemadness.org 70 i 6178 <p>GTK reads its SVG icons... without using librsvg... by assuming that Err codemadness.org 70 i 6179 librsvg installed its gdk-pixbuf loader, so it loads them as any Err codemadness.org 70 i 6180 normal raster image. This kind of dirty, but I can't quite pinpoint Err codemadness.org 70 i 6181 why. I'm sure it would be convenient for icon themes to ship a single Err codemadness.org 70 i 6182 SVG with tons of icons, and some metadata on their <code>id</code>s, so that GTK Err codemadness.org 70 i 6183 could pick them out of the SVG file with <code>rsvg_render_cairo_sub()</code> or Err codemadness.org 70 i 6184 something. Right now icon theme authors are responsible for splitting Err codemadness.org 70 i 6185 out those huge SVGs into many little ones, one for each icon, and I Err codemadness.org 70 i 6186 don't think that's their favorite thing in the world to do :)</p> Err codemadness.org 70 i 6187 <h3>Exotic raster data</h3> Err codemadness.org 70 i 6188 <p>High bit-depth images... would you expect EOG to be able to load them? Err codemadness.org 70 i 6189 Certainly; maybe not with all the fancy conversions from a real RAW Err codemadness.org 70 i 6190 photo editor. But maybe this can be done as EOG-specific plugins, Err codemadness.org 70 i 6191 rather than as low in the platform as the gdk-pixbuf loaders?</p> Err codemadness.org 70 i 6192 <p>(Same thing for thumbnailing high bit-depth images: the loading code Err codemadness.org 70 i 6193 should just provide its own thumbnailer program for those.)</p> Err codemadness.org 70 i 6194 <h3>Non-image metadata</h3> Err codemadness.org 70 i 6195 <p>The <code>gdk_pixbuf_set_option</code> / <code>gdk_pixbuf_get_option</code> family of Err codemadness.org 70 i 6196 functions is so that pixbuf loaders can set key/value pairs of strings Err codemadness.org 70 i 6197 onto a pixbuf. Loaders use this for <code>comment</code> blocks, or ICC profiles Err codemadness.org 70 i 6198 for color calibration, or DPI information for images that have it, or Err codemadness.org 70 i 6199 EXIF data from photos. It is up to applications to actually use this Err codemadness.org 70 i 6200 information.</p> Err codemadness.org 70 i 6201 <p>It's a bit uncomfortable that gdk-pixbuf makes no promises about the Err codemadness.org 70 i 6202 kind of raster data it gives to the caller: right now it is raw Err codemadness.org 70 i 6203 RGB(A) data that is not gamma-corrected nor in any particular color Err codemadness.org 70 i 6204 space. It is up to the caller to see if the pixbuf has an ICC profile Err codemadness.org 70 i 6205 attached to it as an <code>option</code>. Effectively, this means that Err codemadness.org 70 i 6206 applications don't know if they are getting SRGB, or linear RGB, or Err codemadness.org 70 i 6207 what... unless they specifically care to look.</p> Err codemadness.org 70 i 6208 <p>The gdk-pixbuf API could probably make promises: if you call <em>this Err codemadness.org 70 i 6209 function</em> you will get SRGB data; if you call <em>this other function</em>, Err codemadness.org 70 i 6210 you'll get the raw RGBA data and we'll tell you its Err codemadness.org 70 i 6211 colorspace/gamma/etc.</p> Err codemadness.org 70 i 6212 <p>The various <code>set_option</code> / <code>get_option</code> pairs are also usable by the Err codemadness.org 70 i 6213 gdk-pixbuf <em>saving</em> code (up to now we have just talked about Err codemadness.org 70 i 6214 loaders). I don't know enough about how applications use the saving Err codemadness.org 70 i 6215 code in gdk-pixbuf... the thumbnailers use it to save PNGs or JPEGs, Err codemadness.org 70 i 6216 but other apps? No idea.</p> Err codemadness.org 70 i 6217 <h2>What I would like to see</h2> Err codemadness.org 70 i 6218 <p><strong>Immutable pixbufs in a useful format.</strong> I've started <a href="https://gitlab.gnome.org/GNOME/gdk-pixbuf/merge_requests/6">work on Err codemadness.org 70 i 6219 this</a> in a merge request; the internal code is now ready Err codemadness.org 70 i 6220 to take in different internal representations of pixel data. My goal Err codemadness.org 70 i 6221 is to make Cairo image surfaces the preferred, immutable, internal Err codemadness.org 70 i 6222 representation. This would give us a Err codemadness.org 70 i 6223 <code>gdk_pixbuf_get_cairo_surface()</code>, which pretty much everything that Err codemadness.org 70 i 6224 needs one reimplements by hand.</p> Err codemadness.org 70 i 6225 <p><strong>Find places that assume mutable pixbufs.</strong> To gradually deprecate Err codemadness.org 70 i 6226 mutable pixbufs, I think we would need to audit applications and Err codemadness.org 70 i 6227 libraries to find places that cause <code>GdkPixbuf</code> structures to degrade Err codemadness.org 70 i 6228 into mutable ones: basically, find callers of Err codemadness.org 70 i 6229 <code>gdk_pixbuf_get_pixels()</code> and related functions, see what they do, and Err codemadness.org 70 i 6230 reimplement them differently. Maybe they don't need to tint icons by Err codemadness.org 70 i 6231 hand anymore? Maybe they <em>don't need icons</em> anymore, given our Err codemadness.org 70 i 6232 changing UI paradigms? Maybe they are using gdk-pixbuf as an image Err codemadness.org 70 i 6233 loader only?</p> Err codemadness.org 70 i 6234 <p><strong>Reconsider the loading-updates API.</strong> Do we need the Err codemadness.org 70 i 6235 <code>GdkPixbufLoader::area-updated</code> signal at all? Does anything break Err codemadness.org 70 i 6236 if we just... not emit it, or just emit it once at the end of the Err codemadness.org 70 i 6237 loading process? (Caveat: keeping it unchanged more or less means Err codemadness.org 70 i 6238 that "immutable pixbufs" as loaded by gdk-pixbuf actually mutate while Err codemadness.org 70 i 6239 being loaded, and this mutation is exposed to applications.)</p> Err codemadness.org 70 i 6240 <p><strong>Sandboxed loaders.</strong> While these days gdk-pixbuf loaders prefer the Err codemadness.org 70 i 6241 progressive feed-it-bytes API, sandboxed loaders would maybe prefer a Err codemadness.org 70 i 6242 read-a-whole-file approach. I don't know enough about memfd or how Err codemadness.org 70 i 6243 sandboxes pass data around to know how either would work.</p> Err codemadness.org 70 i 6244 <p><strong>Move loaders to Rust.</strong> Yes, really. Loaders are Err codemadness.org 70 i 6245 security-sensitive, and while we <em>do</em> need to sandbox them, it would Err codemadness.org 70 i 6246 certainly be better to do them in a memory-safe language. There are Err codemadness.org 70 i 6247 already pure Rust-based image loaders: <a href="https://crates.io/crates/jpeg-decoder">JPEG</a>, Err codemadness.org 70 i 6248 <a href="https://crates.io/crates/png">PNG</a>, <a href="https://github.com/PistonDevelopers/image-tiff">TIFF</a>, <a href="https://github.com/PistonDevelopers/image-gif">GIF</a>, <a href="https://github.com/PistonDevelopers/image/tree/master/src/ico">ICO</a>. Err codemadness.org 70 i 6249 I have no idea how featureful they are. We can certainly try them Err codemadness.org 70 i 6250 with gdk-pixbuf's own suite of test images. We can modify them to add Err codemadness.org 70 i 6251 hooks for things like a <code>size-prepared</code> notification, if they don't Err codemadness.org 70 i 6252 already have a way to read "just the image headers".</p> Err codemadness.org 70 i 6253 <p>Rust makes it very easy to plug in <a href="https://crates.io/crates/criterion">micro-benchmarks</a>, Err codemadness.org 70 i 6254 <a href="https://crates.io/crates/afl">fuzz testing</a>, and other modern amenities. These would be Err codemadness.org 70 i 6255 perfect for improving the loaders.</p> Err codemadness.org 70 i 6256 <p>I started <a href="https://gitlab.gnome.org/federico/gdk-pixbuf/tree/rust-loader/gdk-pixbuf/rust">sketching a Rust backend for gdk-pixbuf Err codemadness.org 70 i 6257 loaders</a> some months ago, but there's nothing useful Err codemadness.org 70 i 6258 yet. One mismatch between gdk-pixbuf's model for loaders, and the Err codemadness.org 70 i 6259 existing Rust codecs, is that Rust codecs generally take something Err codemadness.org 70 i 6260 that implements the <code>Read</code> trait: a blocking API to read bytes from Err codemadness.org 70 i 6261 abstract sources; it's a pull API. The gdk-pixbuf model is a push Err codemadness.org 70 i 6262 API: the calling code creates a loader object, and then pushes bytes Err codemadness.org 70 i 6263 into it. The gdk-pixbuf convenience functions that take a Err codemadness.org 70 i 6264 <code>GInputStream</code> basically do this:</p> Err codemadness.org 70 i 6265 <div class="highlight"><pre><span></span><code><span class="n">loader</span> <span class="o">=</span> <span class="n">gdk_pixbuf_loader_new</span> <span class="p">(...);</span> Err codemadness.org 70 i 6266 Err codemadness.org 70 i 6267 <span class="k">while</span> <span class="p">(</span><span class="n">more_bytes</span><span class="p">)</span> <span class="p">{</span> Err codemadness.org 70 i 6268 <span class="n">n_read</span> <span class="o">=</span> <span class="n">g_input_stream_read</span> <span class="p">(</span><span class="n">stream</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="p">...);</span> Err codemadness.org 70 i 6269 <span class="n">gdk_pixbuf_loader_write</span><span class="p">(</span><span class="n">loader</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">n_read</span><span class="p">,</span> <span class="p">...);</span> Err codemadness.org 70 i 6270 <span class="p">}</span> Err codemadness.org 70 i 6271 Err codemadness.org 70 i 6272 <span class="n">gdk_pixbuf_loader_close</span> <span class="p">(</span><span class="n">loader</span><span class="p">);</span> Err codemadness.org 70 i 6273 </code></pre></div> Err codemadness.org 70 i 6274 Err codemadness.org 70 i 6275 <p>However, this cannot be flipped around easily. We could probably use Err codemadness.org 70 i 6276 a second thread (easy, safe to do in Rust) to make the reader/decoder Err codemadness.org 70 i 6277 thread block while the main thread pushes bytes into it.</p> Err codemadness.org 70 i 6278 <p>Also, I don't know how the Rust bindings for GIO present things like Err codemadness.org 70 i 6279 <code>GInputStream</code> and friends, with our nice async cancellables and all Err codemadness.org 70 i 6280 that.</p> Err codemadness.org 70 i 6281 <p><strong>Deprecate animations?</strong> Move that code to EOG, just so one can look Err codemadness.org 70 i 6282 at memes in it? Do any "real apps" actually use GIF animations for Err codemadness.org 70 i 6283 their UI?</p> Err codemadness.org 70 i 6284 <p><strong>Formalize promises around returned color profiles, gamma, etc.</strong> As Err codemadness.org 70 i 6285 mentioned above: have an "easy API" that returns SRGB, and a "raw API" Err codemadness.org 70 i 6286 that returns the ARGB data from the image, plus info on its ICC Err codemadness.org 70 i 6287 profile, gamma, or any other info needed to turn this into a Err codemadness.org 70 i 6288 "good enough to be universal" representation. (I <em>think</em> all the Err codemadness.org 70 i 6289 Apple APIs that pass colors around do so with an ICC profile attached, Err codemadness.org 70 i 6290 which seems... pretty much necessary for correctness.)</p> Err codemadness.org 70 i 6291 <p><strong>Remove the internal MIME-sniffing machinery.</strong> And just use GIO's.</p> Err codemadness.org 70 i 6292 <p><strong>Deprecate the crufty/old APIs in gdk-pixbuf.</strong> Err codemadness.org 70 i 6293 Scaling/transformation, compositing, <code>GdkPixdata</code>, Err codemadness.org 70 i 6294 <code>gdk-pixbuf-csource</code>, all those. Pixel crunching can be done by Err codemadness.org 70 i 6295 Cairo; the others are better done with <code>GResource</code> these days.</p> Err codemadness.org 70 i 6296 <p><strong>Figure out if we want blessed codecs; fix thumbnailers.</strong> Link those Err codemadness.org 70 i 6297 loaders statically, unconditionally. Exotic formats can go in their Err codemadness.org 70 i 6298 own custom thumbnailers. Figure out if we want sandboxed loaders for Err codemadness.org 70 i 6299 everything, or just for user-side images (not ones read from the Err codemadness.org 70 i 6300 trusted system installation).</p> Err codemadness.org 70 i 6301 <p><strong>Have GTK4 communicate clearly about its drawing model.</strong> I think we Err codemadness.org 70 i 6302 are having a disconnect between the GUI chrome, which is CSS/GPU Err codemadness.org 70 i 6303 friendly, and graphical content generated by applications, which by Err codemadness.org 70 i 6304 default right now is done via Cairo. And having Cairo as a to-screen Err codemadness.org 70 i 6305 and to-printer API is certainly very convenient! You Wouldn't Print a Err codemadness.org 70 i 6306 GUI, but certainly you would print a displayed document.</p> Err codemadness.org 70 i 6307 <p>It would also be useful for GTK4 to actually define what its preferred Err codemadness.org 70 i 6308 image format is if it wants to ship it to the GPU with as little work Err codemadness.org 70 i 6309 as possible. Maybe it's a Cairo image surface? Maybe something else?</p> Err codemadness.org 70 i 6310 <h2>Conclusion</h2> Err codemadness.org 70 i 6311 <p>We seem to change imaging models every ten years or so. Xlib, then Err codemadness.org 70 i 6312 Xrender with Cairo, then GPUs and CSS-based drawing for widgets. Err codemadness.org 70 i 6313 We've gone from trusted data on your local machine, to potentially malicious data that Err codemadness.org 70 i 6314 rains from the Internet. Gdk-pixbuf has spanned all of these periods Err codemadness.org 70 i 6315 so far, and it is due for a big change.</p>Debugging an Rc<T> reference leak in Rust2018-08-29T16:47:13-05:002018-08-29T16:47:13-05:00Federico Mena Quinterotag:people.gnome.org,2018-08-29:/~federico/blog/debugging-reference-leak-in-rust.html<p>The <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/325">bug</a> that caused two brown-paper-bag released in librsvg — Err codemadness.org 70 i 6316 because it was leaking all the SVG nodes — has been interesting.</p> Err codemadness.org 70 i 6317 <p><em>Memory leaks in Rust? Isn't it supposed to prevent that?</em></p> Err codemadness.org 70 i 6318 <p>Well, yeah, but the leaks were caused by the C side of things, and by Err codemadness.org 70 i 6319 <code>unsafe</code> code in Rust, which …</p><p>The <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/325">bug</a> that caused two brown-paper-bag released in librsvg — Err codemadness.org 70 i 6320 because it was leaking all the SVG nodes — has been interesting.</p> Err codemadness.org 70 i 6321 <p><em>Memory leaks in Rust? Isn't it supposed to prevent that?</em></p> Err codemadness.org 70 i 6322 <p>Well, yeah, but the leaks were caused by the C side of things, and by Err codemadness.org 70 i 6323 <code>unsafe</code> code in Rust, which does not prevent leaks.</p> Err codemadness.org 70 i 6324 <p><a href="https://gitlab.gnome.org/federico/librsvg/commit/29af8b19ea103f754c318cfbf8b03c31265f0394">The first part of the bug</a> was easy: C code started calling a Err codemadness.org 70 i 6325 function implemented in Rust, which returns a newly-acquired reference Err codemadness.org 70 i 6326 to an SVG node. The old code simply got a pointer to the node, Err codemadness.org 70 i 6327 without acquiring a reference. The new code was forgetting to Err codemadness.org 70 i 6328 <code>rsvg_node_unref()</code>. No biggie.</p> Err codemadness.org 70 i 6329 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/commit/2d3ddca130d7d023daedf77a6ab58fefec510292">The second part of the bug</a> was trickier to find. The C code Err codemadness.org 70 i 6330 was apparently calling all the functions to unref nodes as Err codemadness.org 70 i 6331 appropriate, and even calling the <code>rsvg_tree_free()</code> function in the Err codemadness.org 70 i 6332 end; this is the "free the whole SVG tree" function.</p> Err codemadness.org 70 i 6333 <p>There are these types:</p> Err codemadness.org 70 i 6334 <div class="highlight"><pre><span></span><code><span class="c1">// We take a pointer to this and expose it as an opaque pointer to C</span> Err codemadness.org 70 i 6335 <span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">RsvgTree</span><span class="w"> </span><span class="p">{}</span><span class="w"></span> Err codemadness.org 70 i 6336 Err codemadness.org 70 i 6337 <span class="c1">// This is the real structure we care about</span> Err codemadness.org 70 i 6338 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Tree</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6339 <span class="w"> </span><span class="c1">// This is the Rc that was getting leaked</span> Err codemadness.org 70 i 6340 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">root</span>: <span class="nc">Rc</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6341 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 6342 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6343 </code></pre></div> Err codemadness.org 70 i 6344 Err codemadness.org 70 i 6345 <p><code>Tree</code> is the real struct that holds the root of the SVG tree and some Err codemadness.org 70 i 6346 other data. Each node is an <code>Rc&lt;Node&gt;</code>; the root node was getting Err codemadness.org 70 i 6347 leaked (... and all the children, recursively) because its reference Err codemadness.org 70 i 6348 count never went down from 1.</p> Err codemadness.org 70 i 6349 <p><code>RsvgTree</code> is just an empty type. The code does an unsafe cast of Err codemadness.org 70 i 6350 <code>*const Tree</code> as <code>*const RsvgTree</code> in order to expose a raw pointer to Err codemadness.org 70 i 6351 the C code.</p> Err codemadness.org 70 i 6352 <p>The <code>rsvg_tree_free()</code> function, callable from C, looked like this:</p> Err codemadness.org 70 i 6353 <div class="highlight"><pre><span></span><code><span class="cp">#[no_mangle]</span><span class="w"></span> Err codemadness.org 70 i 6354 <span class="k">pub</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">rsvg_tree_free</span><span class="p">(</span><span class="n">tree</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">RsvgTree</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6355 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">!</span><span class="n">tree</span><span class="p">.</span><span class="n">is_null</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6356 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nb">Box</span>::<span class="n">from_raw</span><span class="p">(</span><span class="n">tree</span><span class="p">)</span><span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 6357 <span class="w"> </span><span class="c1">// ^ this returns a Box&lt;RsvgTree&gt; which is an empty type!</span> Err codemadness.org 70 i 6358 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6359 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6360 </code></pre></div> Err codemadness.org 70 i 6361 Err codemadness.org 70 i 6362 <p>When we call <code>Box::from_raw()</code> on a <code>*mut RsvgTree</code>, it gives us back Err codemadness.org 70 i 6363 a <code>Box&lt;RsvgTree&gt;</code>... which is a box of a zero-sized type. So, the program Err codemadness.org 70 i 6364 frees zero memory when the box gets dropped.</p> Err codemadness.org 70 i 6365 <p>The code was missing this cast:</p> Err codemadness.org 70 i 6366 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">tree</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">tree</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">Tree</span><span class="p">)</span><span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 6367 <span class="w"> </span><span class="c1">// ^ this cast to the actual type inside the Box</span> Err codemadness.org 70 i 6368 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nb">Box</span>::<span class="n">from_raw</span><span class="p">(</span><span class="n">tree</span><span class="p">)</span><span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 6369 </code></pre></div> Err codemadness.org 70 i 6370 Err codemadness.org 70 i 6371 <p>So, <code>tree as *mut Tree</code> gives us a value which will cause Err codemadness.org 70 i 6372 <code>Box::from_raw()</code> to return a <code>Box&lt;Tree&gt;</code>, which is what we intended. Err codemadness.org 70 i 6373 Dropping the box will drop the <code>Tree</code>, reduce the last reference count Err codemadness.org 70 i 6374 on the root node, and free all the nodes recursively.</p> Err codemadness.org 70 i 6375 <h2>Monitoring an <code>Rc&lt;T&gt;</code>'s reference count in gdb</h2> Err codemadness.org 70 i 6376 <p>So, how does one set a gdb watchpoint on the reference count?</p> Err codemadness.org 70 i 6377 <p>First I set a breakpoint on a function which I knew would get passed Err codemadness.org 70 i 6378 the <code>Rc&lt;Node&gt;</code> I care about:</p> Err codemadness.org 70 i 6379 <div class="highlight"><pre><span></span><code>(gdb) b &lt;rsvg_internals::structure::NodeSvg as rsvg_internals::node::NodeTrait&gt;::set_atts Err codemadness.org 70 i 6380 Breakpoint 3 at 0x7ffff71f3aaa: file rsvg_internals/src/structure.rs, line 131. Err codemadness.org 70 i 6381 Err codemadness.org 70 i 6382 (gdb) c Err codemadness.org 70 i 6383 Continuing. Err codemadness.org 70 i 6384 Err codemadness.org 70 i 6385 Thread 1 &quot;rsvg-convert&quot; hit Breakpoint 3, &lt;rsvg_internals::structure::NodeSvg as rsvg_internals::node::NodeTrait&gt;::set_atts (self=0x646c60, node=0x64c890, pbag=0x64c820) at rsvg_internals/src/structure.rs:131 Err codemadness.org 70 i 6386 Err codemadness.org 70 i 6387 (gdb) p node Err codemadness.org 70 i 6388 $5 = (alloc::rc::Rc&lt;rsvg_internals::node::Node&gt; *) 0x64c890 Err codemadness.org 70 i 6389 </code></pre></div> Err codemadness.org 70 i 6390 Err codemadness.org 70 i 6391 <p>Okay, <code>node</code> is a reference to an <code>Rc&lt;Node&gt;</code>. What's inside?</p> Err codemadness.org 70 i 6392 <div class="highlight"><pre><span></span><code>(gdb) p *node Err codemadness.org 70 i 6393 $6 = {ptr = {pointer = {__0 = 0x625800}}, phantom = {&lt;No data fields&gt;}} Err codemadness.org 70 i 6394 </code></pre></div> Err codemadness.org 70 i 6395 Err codemadness.org 70 i 6396 <p>Why, a <code>pointer</code> to the actual contents of the <code>Rc</code>. Look inside Err codemadness.org 70 i 6397 again:</p> Err codemadness.org 70 i 6398 <div class="highlight"><pre><span></span><code>(gdb) p *node.ptr.pointer.__0 Err codemadness.org 70 i 6399 $9 = {strong = {value = {value = 3}}, weak = {value = {value = 1}}, ... and lots of extra crap ... Err codemadness.org 70 i 6400 </code></pre></div> Err codemadness.org 70 i 6401 Err codemadness.org 70 i 6402 <p>Aha! There are the <code>strong</code> and <code>weak</code> reference counts. So, set a Err codemadness.org 70 i 6403 watchpoint on the strong reference count:</p> Err codemadness.org 70 i 6404 <div class="highlight"><pre><span></span><code>(gdb) set $ptr = &amp;node.ptr.pointer.__0.strong.value.value Err codemadness.org 70 i 6405 (gdb) watch *$ptr Err codemadness.org 70 i 6406 Hardware watchpoint 4: *$ptr Err codemadness.org 70 i 6407 </code></pre></div> Err codemadness.org 70 i 6408 Err codemadness.org 70 i 6409 <p>Continue running the program until the reference count changes:</p> Err codemadness.org 70 i 6410 <div class="highlight"><pre><span></span><code><span class="ss">(</span><span class="nv">gdb</span><span class="ss">)</span> <span class="k">continue</span> Err codemadness.org 70 i 6411 <span class="nv">Thread</span> <span class="mi">1</span> <span class="s2">&quot;</span><span class="s">rsvg-convert</span><span class="s2">&quot;</span> <span class="nv">hit</span> <span class="nv">Hardware</span> <span class="nv">watchpoint</span> <span class="mi">4</span>: <span class="o">*</span>$<span class="nv">ptr</span> Err codemadness.org 70 i 6412 Err codemadness.org 70 i 6413 <span class="nv">Old</span> <span class="nv">value</span> <span class="o">=</span> <span class="mi">3</span> Err codemadness.org 70 i 6414 <span class="nv">New</span> <span class="nv">value</span> <span class="o">=</span> <span class="mi">2</span> Err codemadness.org 70 i 6415 </code></pre></div> Err codemadness.org 70 i 6416 Err codemadness.org 70 i 6417 <p>At this point I can print a stack trace and see if it makes sense, Err codemadness.org 70 i 6418 check that the refs/unrefs are matched, etc.</p> Err codemadness.org 70 i 6419 <p>TL;DR: dig into the <code>Rc&lt;T&gt;</code> until you find the reference count, and Err codemadness.org 70 i 6420 watch it. It's wrapped in several layers of Rust-y types; <code>NonNull</code> Err codemadness.org 70 i 6421 pointers, an <code>RcBox</code> for the actual container of the refcount plus the Err codemadness.org 70 i 6422 object it's wrapping, and <code>Cell</code>s for the refcount values. Just dig Err codemadness.org 70 i 6423 until you reach the refcount values and they are there.</p> Err codemadness.org 70 i 6424 <h2>So, how did I find the missing cast?</h2> Err codemadness.org 70 i 6425 <p>Using that gdb recipe, I watched the reference count of the toplevel Err codemadness.org 70 i 6426 SVG node change until the program exited. When the program Err codemadness.org 70 i 6427 terminated, the reference count was 1 — it should have dropped to 0 if Err codemadness.org 70 i 6428 there was no memory leak.</p> Err codemadness.org 70 i 6429 <p>The last place where the toplevel node loses a reference is in Err codemadness.org 70 i 6430 <code>rsvg_tree_free()</code>. I ran the program again and checked if that Err codemadness.org 70 i 6431 function was being called; it <em>was</em> being called correctly. So I knew Err codemadness.org 70 i 6432 that the problem must lie in that function. After a little Err codemadness.org 70 i 6433 head-scratching, I found the missing cast. Other functions of the Err codemadness.org 70 i 6434 form <code>rsvg_tree_whatever()</code> had that cast, but <code>rsvg_tree_free()</code> was Err codemadness.org 70 i 6435 missing it.</p> Err codemadness.org 70 i 6436 <p>I think Rust now has better facilities to tag structs that are exposed Err codemadness.org 70 i 6437 as raw pointers to <code>extern</code> code, to avoid this kind of perilous Err codemadness.org 70 i 6438 casting. We'll see.</p> Err codemadness.org 70 i 6439 <p>In the meantime, apologies for the buggy releases!</p>Logging from Rust in librsvg2018-08-03T19:29:43-05:002018-08-03T19:29:43-05:00Federico Mena Quinterotag:people.gnome.org,2018-08-03:/~federico/blog/logging-in-librsvg.html<p>Over in <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/281">this issue</a> we are discussing how to add debug logging Err codemadness.org 70 i 6440 for librsvg.</p> Err codemadness.org 70 i 6441 <p>A popular way to add logging to Rust code is to use the <a href="https://crates.io/crates/log">log</a> crate. Err codemadness.org 70 i 6442 This lets you sprinkle simple messages in your code:</p> Err codemadness.org 70 i 6443 <div class="highlight"><pre><span></span><code><span class="n">error</span><span class="o">!</span><span class="p">(</span><span class="s">&quot;something bad happened: {}&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">foo</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 6444 <span class="n">debug</span><span class="o">!</span><span class="p">(</span><span class="s">&quot;a debug message&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 6445 </code></pre></div> Err codemadness.org 70 i 6446 Err codemadness.org 70 i 6447 <p>However, the <a href="https://crates.io/crates/log">log …</a></p><p>Over in <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/281">this issue</a> we are discussing how to add debug logging Err codemadness.org 70 i 6448 for librsvg.</p> Err codemadness.org 70 i 6449 <p>A popular way to add logging to Rust code is to use the <a href="https://crates.io/crates/log">log</a> crate. Err codemadness.org 70 i 6450 This lets you sprinkle simple messages in your code:</p> Err codemadness.org 70 i 6451 <div class="highlight"><pre><span></span><code><span class="n">error</span><span class="o">!</span><span class="p">(</span><span class="s">&quot;something bad happened: {}&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">foo</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 6452 <span class="n">debug</span><span class="o">!</span><span class="p">(</span><span class="s">&quot;a debug message&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 6453 </code></pre></div> Err codemadness.org 70 i 6454 Err codemadness.org 70 i 6455 <p>However, the <a href="https://crates.io/crates/log">log</a> create is just a facade, and by default the Err codemadness.org 70 i 6456 messages do not get emitted anywhere. The calling code has to set up Err codemadness.org 70 i 6457 a logger. Crates like <a href="https://crates.io/crates/env_logger">env_logger</a> let one set up a logger, during Err codemadness.org 70 i 6458 program initialization, that gets configured through an environment Err codemadness.org 70 i 6459 variable.</p> Err codemadness.org 70 i 6460 <p>And this is a problem for librsvg: we are <em>not</em> the program's Err codemadness.org 70 i 6461 initialization! Librsvg is a library; it doesn't have a <code>main()</code> Err codemadness.org 70 i 6462 function. And since most of the calling code is not Rust, we can't Err codemadness.org 70 i 6463 assume that they can call code that can initialize the logging Err codemadness.org 70 i 6464 framework.</p> Err codemadness.org 70 i 6465 <h2>Why not use glib's logging stuff?</h2> Err codemadness.org 70 i 6466 <p>Currently this is a bit clunky to use from Rust, since glib's Err codemadness.org 70 i 6467 structured logging functions are not bound yet in <a href="http://gtk-rs.org/docs/glib/">glib-rs</a>. Maybe it Err codemadness.org 70 i 6468 would be good to bind them and get this over with.</p> Err codemadness.org 70 i 6469 <h2>What user experience do we want?</h2> Err codemadness.org 70 i 6470 <p>In the past, what has worked well for me to do logging from libraries Err codemadness.org 70 i 6471 is to allow the user to set an environment variable to control the Err codemadness.org 70 i 6472 logging, or to drop a log configuration file in their $HOME. The Err codemadness.org 70 i 6473 former works well when the user is in control of running the program Err codemadness.org 70 i 6474 that will print the logs; the latter is useful when the user is not Err codemadness.org 70 i 6475 directly in control, like for gnome-shell, which gets launched through Err codemadness.org 70 i 6476 a lot of magic during session startup.</p> Err codemadness.org 70 i 6477 <p>For librsvg, it's probably enough to just use an environment Err codemadness.org 70 i 6478 variable. Set <code>RSVG_LOG=parse_errors</code>, run your program, and get Err codemadness.org 70 i 6479 useful output. <a href="https://makezine.com/2016/06/10/push-button-receive-bacon/">Push button, receive bacon</a>.</p> Err codemadness.org 70 i 6480 <h2>Other options in Rust?</h2> Err codemadness.org 70 i 6481 <p>There is a <a href="https://crates.io/crates/slog">slog</a> crate which looks promising. Instead of using Err codemadness.org 70 i 6482 context-less macros which depend on a single global logger, it Err codemadness.org 70 i 6483 provides logging macros to which you pass a logger object.</p> Err codemadness.org 70 i 6484 <p>For librsvg, this means that the basic <code>RsvgHandle</code> could create its Err codemadness.org 70 i 6485 own logger, based on an environment variable or whatever, and pass it Err codemadness.org 70 i 6486 around to all its child functions for when they need to log something.</p> Err codemadness.org 70 i 6487 <p>Slog supports structured logging, and seems to have some fancy output Err codemadness.org 70 i 6488 modes. We'll see.</p>Three big things happening in librsvg2018-05-21T19:21:08-05:002018-05-21T19:21:08-05:00Federico Mena Quinterotag:people.gnome.org,2018-05-21:/~federico/blog/three-big-things-happening-in-librsvg.html<p>I am incredibly happy because of three <strong>big</strong> things that are going Err codemadness.org 70 i 6489 on in librsvg right now:</p> Err codemadness.org 70 i 6490 <ol> Err codemadness.org 70 i 6491 <li> Err codemadness.org 70 i 6492 <p>Paolo Borelli finished porting all the CSS properties to Rust. Err codemadness.org 70 i 6493 What was once a gigantic <code>RsvgState</code> struct in C is totally gone, Err codemadness.org 70 i 6494 along with all the janky C code to parse individual properties …</p></li></ol><p>I am incredibly happy because of three <strong>big</strong> things that are going Err codemadness.org 70 i 6495 on in librsvg right now:</p> Err codemadness.org 70 i 6496 <ol> Err codemadness.org 70 i 6497 <li> Err codemadness.org 70 i 6498 <p>Paolo Borelli finished porting all the CSS properties to Rust. Err codemadness.org 70 i 6499 What was once a gigantic <code>RsvgState</code> struct in C is totally gone, Err codemadness.org 70 i 6500 along with all the janky C code to parse individual properties. Err codemadness.org 70 i 6501 The process of porting <code>RsvgState</code> to Rust has been going on <a href="https://gitlab.gnome.org/GNOME/librsvg/merge_requests/39">since Err codemadness.org 70 i 6502 about two months ago</a>, and has involved many multi-commit Err codemadness.org 70 i 6503 merge requests and refactorings. This is a tremendous amount of Err codemadness.org 70 i 6504 really good work! The result is all in Rust now in a <code>State</code> Err codemadness.org 70 i 6505 struct, which is opaque from C's viewpoint. The only places in C Err codemadness.org 70 i 6506 that still require accessors to the <code>State</code> are in the filter Err codemadness.org 70 i 6507 effects code. Which brings me to...</p> Err codemadness.org 70 i 6508 </li> Err codemadness.org 70 i 6509 <li> Err codemadness.org 70 i 6510 <p>Ivan Molodetskikh, my Summer of Code student, submitted his <a href="https://gitlab.gnome.org/GNOME/librsvg/merge_requests/64">first Err codemadness.org 70 i 6511 merge request</a> and it's merged to master now. This ports Err codemadness.org 70 i 6512 the bookkeeping infrastructure for SVG filters to Rust, and also Err codemadness.org 70 i 6513 the <code>feOffset</code> filter is ported now. Right now the code doesn't do Err codemadness.org 70 i 6514 anything fancy to iterate over the pixels of Cairo image surfaces; Err codemadness.org 70 i 6515 that will come later. I am very happy that filters, which were a Err codemadness.org 70 i 6516 huge barrier, are now starting to get chipped away into nicer code.</p> Err codemadness.org 70 i 6517 </li> Err codemadness.org 70 i 6518 <li> Err codemadness.org 70 i 6519 <p>I have started to move librsvg's old representation of CSS Err codemadness.org 70 i 6520 properties into something that can really represent properties that Err codemadness.org 70 i 6521 are not specified, or explicitly set to <code>inherit</code> from an SVG Err codemadness.org 70 i 6522 element's parent, or set to a normal value. Librsvg never had a Err codemadness.org 70 i 6523 representation of property values that actually matched the SVG/CSS Err codemadness.org 70 i 6524 specs; it just knew whether a property was specified or not for an Err codemadness.org 70 i 6525 element. This worked fine for properties which the spec mandates Err codemadness.org 70 i 6526 that they should inherit automatically, but those that <em>don't</em>, Err codemadness.org 70 i 6527 were handled through special hacks. The new code makes this a lot Err codemadness.org 70 i 6528 cleaner. It should also make it easier to copy Servo's idioms for Err codemadness.org 70 i 6529 property inheritance.</p> Err codemadness.org 70 i 6530 </li> Err codemadness.org 70 i 6531 </ol>Reducing the number of image copies in GNOME2018-05-14T07:45:11-05:002018-05-14T07:45:11-05:00Federico Mena Quinterotag:people.gnome.org,2018-05-14:/~federico/blog/reducing-image-copies.html<p><a href="https://magcius.github.io/xplain/article/">Our graphics stack</a> that deals with images has evolved a lot Err codemadness.org 70 i 6532 over the years.</p> Err codemadness.org 70 i 6533 <h1>In ye olden days</h1> Err codemadness.org 70 i 6534 <p>In the context of GIMP/GNOME, the only thing that knew how to draw RGB Err codemadness.org 70 i 6535 images to X11 windows (doing palette mapping for 256-color graphics Err codemadness.org 70 i 6536 cards and dithering if necessary) was the …</p><p><a href="https://magcius.github.io/xplain/article/">Our graphics stack</a> that deals with images has evolved a lot Err codemadness.org 70 i 6537 over the years.</p> Err codemadness.org 70 i 6538 <h1>In ye olden days</h1> Err codemadness.org 70 i 6539 <p>In the context of GIMP/GNOME, the only thing that knew how to draw RGB Err codemadness.org 70 i 6540 images to X11 windows (doing palette mapping for 256-color graphics Err codemadness.org 70 i 6541 cards and dithering if necessary) was the GIMP. Later, when GTK+ was Err codemadness.org 70 i 6542 written, it exported a <code>GtkPreview</code> widget, which could take an RGB Err codemadness.org 70 i 6543 image buffer supplied by the application and render it to an X window Err codemadness.org 70 i 6544 — this was what GIMP plug-ins could use in their user interface to Err codemadness.org 70 i 6545 show, well, previews of what they were about to do with the user's Err codemadness.org 70 i 6546 images. Later we got some obscure magic in a <code>GdkColorContext</code> Err codemadness.org 70 i 6547 object, which helped allocate X11 colors for the X drawing primitives. Err codemadness.org 70 i 6548 In turn, <code>GdkColorContext</code> came from the port that Miguel and I did of Err codemadness.org 70 i 6549 XmHTML's color context object (and for those that remember, XmHTML Err codemadness.org 70 i 6550 became the first version of GtkHtml; later it was rewritten as a port Err codemadness.org 70 i 6551 of KDE's HTML widget). Thankfully all that stuff is gone now; we can Err codemadness.org 70 i 6552 now assume that video cards are 24-bit RGB or better everywhere, and Err codemadness.org 70 i 6553 there is no need to worry about limited color palettes and color Err codemadness.org 70 i 6554 allocation.</p> Err codemadness.org 70 i 6555 <p>Later, we started using the Imlib library, from the Enlightenment Err codemadness.org 70 i 6556 project, as an easy API to load images — the APIs from libungif, Err codemadness.org 70 i 6557 libjpeg, libpng, etc. were not something one really wanted to use Err codemadness.org 70 i 6558 directly — and also to keep images in memory with a uniform Err codemadness.org 70 i 6559 representation. Unfortunately, Imlib's memory management was Err codemadness.org 70 i 6560 peculiar, as it was tied to Enlightenment's model for caching and Err codemadness.org 70 i 6561 rendering loaded/scaled images.</p> Err codemadness.org 70 i 6562 <p>A bunch of people worked to write GdkPixbuf: it kept Imlib's concepts Err codemadness.org 70 i 6563 of a unified representation for image data, and an easy API to load Err codemadness.org 70 i 6564 various image formats. It added support for an alpha channel (we only Err codemadness.org 70 i 6565 had 1-bit masks before), and it put memory management in the hands of Err codemadness.org 70 i 6566 the calling application, in the form of reference counting. GdkPixbuf Err codemadness.org 70 i 6567 obtained some high-quality scaling functions, mainly for use by Eye Of Err codemadness.org 70 i 6568 Gnome (our image viewer) and by applications that just needed scaling Err codemadness.org 70 i 6569 instead of arbitrary transformations.</p> Err codemadness.org 70 i 6570 <p>Later, we got libart, the first library in GNOME to do antialiased Err codemadness.org 70 i 6571 vector rendering and affine transformations. Libart was more or less Err codemadness.org 70 i 6572 compatible with GdkPixbuf: they both had the same internal Err codemadness.org 70 i 6573 representation for pixel data, but one had to pass the Err codemadness.org 70 i 6574 pixels/width/height/rowstride around by hand.</p> Err codemadness.org 70 i 6575 <h1>Mea culpa</h1> Err codemadness.org 70 i 6576 <p>Back then I didn't understand <a href="https://keithp.com/~keithp/porterduff/p253-porter.pdf">premultiplied alpha</a>, Err codemadness.org 70 i 6577 which is now ubiquitous. The GIMP made the decision to use Err codemadness.org 70 i 6578 non-premultiplied alpha when it introduced layers with transparency, Err codemadness.org 70 i 6579 probably to "avoid losing data" from transparent pixels. GdkPixbuf Err codemadness.org 70 i 6580 follows the same scheme.</p> Err codemadness.org 70 i 6581 <p>(Now that the GIMP uses GEGL for its internal representation of Err codemadness.org 70 i 6582 images... I have no idea what it does with respect to alpha.)</p> Err codemadness.org 70 i 6583 <h1>Cairo and afterwards</h1> Err codemadness.org 70 i 6584 <p>Some time after the libart days, we got Cairo and pixman. Cairo had a Err codemadness.org 70 i 6585 different representation of images than GdkPixbuf's, and it supported Err codemadness.org 70 i 6586 more pixel formats and color models.</p> Err codemadness.org 70 i 6587 <p>GTK2 got patched to use Cairo in the toplevel API. We still had a Err codemadness.org 70 i 6588 dichotomy between Cairo's image surfaces, which are ARGB premultiplied Err codemadness.org 70 i 6589 data in memory, and GdkPixbufs, which are RGBA non-premultiplied. Err codemadness.org 70 i 6590 There are utilities in GTK+ to do these translations, but they are Err codemadness.org 70 i 6591 inconvenient: every time a program loads an image with GdkPixbuf's Err codemadness.org 70 i 6592 easy API, a translation has to happen from non-premul RGBA to premul Err codemadness.org 70 i 6593 ARGB.</p> Err codemadness.org 70 i 6594 <p>Having two formats means that we inevitably do translations back and Err codemadness.org 70 i 6595 forth of practically the same data. For example, when one embeds a Err codemadness.org 70 i 6596 JPEG inside an SVG, librsvg will read that JPEG using GdkPixbuf, Err codemadness.org 70 i 6597 translate it to Cairo's representation, composite it with Cairo onto Err codemadness.org 70 i 6598 the final result, and finally translate the whole thing back to a Err codemadness.org 70 i 6599 GdkPixbuf... if someone uses librsvg's legacy APIs to output pixbufs Err codemadness.org 70 i 6600 instead of rendering directly to a Cairo surface.</p> Err codemadness.org 70 i 6601 <p>Who uses that legacy API? GTK+, of course! GTK+ loads scalable SVG Err codemadness.org 70 i 6602 icons with GdkPixbuf's loader API, which dynamically links librsvg at Err codemadness.org 70 i 6603 runtime: in effect, GTK+ doesn't use librsvg directly. And the SVG Err codemadness.org 70 i 6604 pixbuf loader uses the "gimme a pixbuf" API in librsvg.</p> Err codemadness.org 70 i 6605 <h1>GPUs</h1> Err codemadness.org 70 i 6606 <p>Then, we got GPUs everywhere. Each GPU has its own preferred pixel Err codemadness.org 70 i 6607 format. Image data has to be copied to the GPU at some point. Err codemadness.org 70 i 6608 Cairo's ARGB needs to be translated to the GPU's preferred format and Err codemadness.org 70 i 6609 alignment.</p> Err codemadness.org 70 i 6610 <h1>Summary so far</h1> Err codemadness.org 70 i 6611 <ul> Err codemadness.org 70 i 6612 <li> Err codemadness.org 70 i 6613 <p>Libraries that load images from standard formats have different Err codemadness.org 70 i 6614 output formats. Generally they can be coaxed into spitting ARGB or Err codemadness.org 70 i 6615 RGBA, but we don't expect them to support any random representation Err codemadness.org 70 i 6616 that a GPU may want.</p> Err codemadness.org 70 i 6617 </li> Err codemadness.org 70 i 6618 <li> Err codemadness.org 70 i 6619 <p>GdkPixbuf uses non-premultiplied RGBA data, always in that order.</p> Err codemadness.org 70 i 6620 </li> Err codemadness.org 70 i 6621 <li> Err codemadness.org 70 i 6622 <p>Cairo uses premultiplied ARGB in platform-endian 32-bit chunks: if Err codemadness.org 70 i 6623 each pixel is 0xaarrggbb, then the bytes are shuffled around Err codemadness.org 70 i 6624 depending on whether the platform is little-endian or big-endian.</p> Err codemadness.org 70 i 6625 </li> Err codemadness.org 70 i 6626 <li> Err codemadness.org 70 i 6627 <p>Cairo internally uses a subset of the formats supported by pixman.</p> Err codemadness.org 70 i 6628 </li> Err codemadness.org 70 i 6629 <li> Err codemadness.org 70 i 6630 <p>GPUs use whatever they damn well please.</p> Err codemadness.org 70 i 6631 </li> Err codemadness.org 70 i 6632 <li> Err codemadness.org 70 i 6633 <p>Hilarity ensues.</p> Err codemadness.org 70 i 6634 </li> Err codemadness.org 70 i 6635 </ul> Err codemadness.org 70 i 6636 <h1>What would we like to do?</h1> Err codemadness.org 70 i 6637 <p>We would like to reduce the number of translations between image Err codemadness.org 70 i 6638 formats along the loading-processing-display pipeline. Here is a Err codemadness.org 70 i 6639 plan:</p> Err codemadness.org 70 i 6640 <ul> Err codemadness.org 70 i 6641 <li> Err codemadness.org 70 i 6642 <p>Make sure Cairo/pixman support the image formats that GPUs generally Err codemadness.org 70 i 6643 prefer. Have them do the necessary conversions if the rest of the Err codemadness.org 70 i 6644 program passes an unsupported format. Ensure that a Cairo image Err codemadness.org 70 i 6645 surface can be created with the GPU's preferred format.</p> Err codemadness.org 70 i 6646 </li> Err codemadness.org 70 i 6647 <li> Err codemadness.org 70 i 6648 <p>Make GdkPixbuf just be a wrapper around a Cairo image surface. Err codemadness.org 70 i 6649 <code>GdkPixbuf</code> is already an opaque structure, and it already knows how Err codemadness.org 70 i 6650 to copy pixel data in case the calling code requests it, or wants to Err codemadness.org 70 i 6651 turn a pixbuf from immutable to mutable.</p> Err codemadness.org 70 i 6652 </li> Err codemadness.org 70 i 6653 <li> Err codemadness.org 70 i 6654 <p>Provide GdkPixbuf APIs that deal with Cairo image surfaces. For Err codemadness.org 70 i 6655 example, deprecate <code>gdk_pixbuf_new()</code> and Err codemadness.org 70 i 6656 <code>gdk_pixbuf_new_from_data()</code>, in favor of a new Err codemadness.org 70 i 6657 <code>gdk_pixbuf_new_from_cairo_image_surface()</code>. Instead of Err codemadness.org 70 i 6658 <code>gdk_pixbuf_get_pixels()</code> and related functions, have Err codemadness.org 70 i 6659 <code>gdk_pixbuf_get_cairo_image_surface()</code>. Mark the "give me the pixel Err codemadness.org 70 i 6660 data" functions as highly discouraged, and only for use really by Err codemadness.org 70 i 6661 applications that want to use GdkPixbuf as an image loader and Err codemadness.org 70 i 6662 little else.</p> Err codemadness.org 70 i 6663 </li> Err codemadness.org 70 i 6664 <li> Err codemadness.org 70 i 6665 <p>Remove calls in GTK+ that cause image conversions; make them use Err codemadness.org 70 i 6666 Cairo image surfaces directly, from GdkTexture up.</p> Err codemadness.org 70 i 6667 </li> Err codemadness.org 70 i 6668 <li> Err codemadness.org 70 i 6669 <p>Audit applications to remove calls that cause image conversions. Err codemadness.org 70 i 6670 Generally, look for where they use GdkPixbuf's deprecated APIs and Err codemadness.org 70 i 6671 update them.</p> Err codemadness.org 70 i 6672 </li> Err codemadness.org 70 i 6673 </ul> Err codemadness.org 70 i 6674 <h1>Is this really a performance problem?</h1> Err codemadness.org 70 i 6675 <p>This is in the "<a href="https://people.gnome.org/~federico/docs/2005-GNOME-Summit/html/img13.html">excess work</a>" category of performance Err codemadness.org 70 i 6676 issues. All those conversions are not really slow (they don't make up Err codemadness.org 70 i 6677 for the biggest part of profiles), but they are nevertheless things Err codemadness.org 70 i 6678 that we could avoid doing. We may get some speedups, but it's Err codemadness.org 70 i 6679 probably more interesting to look at things like power consumption.</p> Err codemadness.org 70 i 6680 <p>Right now I'm seeing this as a cool, minor optimization, but more as Err codemadness.org 70 i 6681 <strong>a way to gradually modernize our image API</strong>.</p> Err codemadness.org 70 i 6682 <p>We seem to change imaging models every N years (X11 -&gt; libart Err codemadness.org 70 i 6683 -&gt; Cairo -&gt; render trees in GPUs -&gt; ???). It is very hard to change Err codemadness.org 70 i 6684 applications to use different APIs. In the meantime, we can provide a Err codemadness.org 70 i 6685 more linear path for image data, instead of doing unnecessary Err codemadness.org 70 i 6686 conversions everywhere.</p> Err codemadness.org 70 i 6687 <h1>Code</h1> Err codemadness.org 70 i 6688 <p>I have a <a href="https://gitlab.gnome.org/federico/gdk-pixbuf/tree/use-cairo-surface-internally"><code>use-cairo-surface-internally</code> branch in Err codemadness.org 70 i 6689 gdk-pixbuf</a>, Err codemadness.org 70 i 6690 which I'll be working on this week. Meanwhile, you may be interested Err codemadness.org 70 i 6691 in the ongoing <a href="https://wiki.gnome.org/Hackfests/Performance2018">Performance Hackfest in Cambridge</a>!</p>Madrid GNOME+Rust Hackfest, part 3 (conclusion)2018-04-23T15:04:32-05:002018-04-23T15:04:32-05:00Federico Mena Quinterotag:people.gnome.org,2018-04-23:/~federico/blog/madrid-gnome-rust-3.html<p>The last code I wrote during the hackfest was the start of code Err codemadness.org 70 i 6692 generation for GObject interfaces. This is so that you can do</p> Err codemadness.org 70 i 6693 <div class="highlight"><pre><span></span><code><span class="n">gobject_gen</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6694 <span class="w"> </span><span class="n">interface</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6695 <span class="w"> </span><span class="kr">virtual</span><span class="w"> </span><span class="k">fn</span> <span class="nf">frob</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 6696 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6697 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6698 </code></pre></div> Err codemadness.org 70 i 6699 Err codemadness.org 70 i 6700 <p>and it will generate the appropriate <code>FooIface</code> like one would expect Err codemadness.org 70 i 6701 with the C versions of interfaces.</p> Err codemadness.org 70 i 6702 <p>It turns …</p><p>The last code I wrote during the hackfest was the start of code Err codemadness.org 70 i 6703 generation for GObject interfaces. This is so that you can do</p> Err codemadness.org 70 i 6704 <div class="highlight"><pre><span></span><code><span class="n">gobject_gen</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6705 <span class="w"> </span><span class="n">interface</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6706 <span class="w"> </span><span class="kr">virtual</span><span class="w"> </span><span class="k">fn</span> <span class="nf">frob</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 6707 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6708 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6709 </code></pre></div> Err codemadness.org 70 i 6710 Err codemadness.org 70 i 6711 <p>and it will generate the appropriate <code>FooIface</code> like one would expect Err codemadness.org 70 i 6712 with the C versions of interfaces.</p> Err codemadness.org 70 i 6713 <p>It turns out that this can share a lot of code from the existing code Err codemadness.org 70 i 6714 generator for classes: both classes and interfaces are "just virtual Err codemadness.org 70 i 6715 method tables", plus signals and properties, and classes can actually Err codemadness.org 70 i 6716 have per-instance fields and such. I started refactoring the code Err codemadness.org 70 i 6717 generator to allow this.</p> Err codemadness.org 70 i 6718 <p>I also took a second look at how to present good error messages when Err codemadness.org 70 i 6719 the <code>syn</code> crate encounters a parse error. I need to sit down at home Err codemadness.org 70 i 6720 and experiment with this carefully.</p> Err codemadness.org 70 i 6721 <h2>Back home</h2> Err codemadness.org 70 i 6722 <p>I'm back home now, jetlagged but very happy that gnome-class is in a much Err codemadness.org 70 i 6723 more advanced a state than it was before the hackfest. I'm <strong>very Err codemadness.org 70 i 6724 thankful</strong> that practically everyone worked on it!</p> Err codemadness.org 70 i 6725 <p>Also, <strong>thanks</strong> to Alberto and Natalia for hosting me at their Err codemadness.org 70 i 6726 apartment and showing me around Madrid, all while wrangling their Err codemadness.org 70 i 6727 adorable baby Mario. We had a lovely time on Saturday, and ate Err codemadness.org 70 i 6728 excellent food downtown.</p> Err codemadness.org 70 i 6729 <p><img alt="Sponsored by the GNOME Foundation" src="https://people.gnome.org/~federico/blog/images/sponsored-badge-shadow.png"></p> Err codemadness.org 70 i 6730 <p><img alt="Hosted by OpenShine" src="https://www.openshine.com/wp-content/uploads/2016/03/openshine.png"></p>Madrid GNOME+Rust Hackfest, part 22018-04-20T08:59:23+02:002018-04-20T08:59:23+02:00Federico Mena Quinterotag:people.gnome.org,2018-04-20:/~federico/blog/madrid-gnome-rust-2.html<p>Hacking on <a href="https://gitlab.gnome.org/federico/gnome-class">gnome-class</a> continues apace!</p> Err codemadness.org 70 i 6731 <p>Philippe <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/8">updated our dependencies</a>.</p> Err codemadness.org 70 i 6732 <p>Alberto made the syntax for per-instance <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/10">private structs</a> Err codemadness.org 70 i 6733 more ergonomic, and then made that code <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/12">nice and compact</a>.</p> Err codemadness.org 70 i 6734 <p>Martin improved our <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/11">conversion</a> from <code>CamelCase</code> to Err codemadness.org 70 i 6735 <code>snake_case</code> for code generation.</p> Err codemadness.org 70 i 6736 <p>Daniel added initial support for <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/9">GObject properties</a>. Err codemadness.org 70 i 6737 This is not finished yet …</p><p>Hacking on <a href="https://gitlab.gnome.org/federico/gnome-class">gnome-class</a> continues apace!</p> Err codemadness.org 70 i 6738 <p>Philippe <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/8">updated our dependencies</a>.</p> Err codemadness.org 70 i 6739 <p>Alberto made the syntax for per-instance <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/10">private structs</a> Err codemadness.org 70 i 6740 more ergonomic, and then made that code <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/12">nice and compact</a>.</p> Err codemadness.org 70 i 6741 <p>Martin improved our <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/11">conversion</a> from <code>CamelCase</code> to Err codemadness.org 70 i 6742 <code>snake_case</code> for code generation.</p> Err codemadness.org 70 i 6743 <p>Daniel added initial support for <a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/9">GObject properties</a>. Err codemadness.org 70 i 6744 This is not finished yet, but the initial parser and code generation Err codemadness.org 70 i 6745 is done.</p> Err codemadness.org 70 i 6746 <p>Guillaume turned <a href="https://github.com/gtk-rs/gir">gir</a>, the binding generator in Err codemadness.org 70 i 6747 <a href="http://gtk-rs.org/">gtk-rs</a>, from a binary into a library crate. This will let Err codemadness.org 70 i 6748 us have all the GObject Introspection information for parent classes Err codemadness.org 70 i 6749 at compilation time.</p> Err codemadness.org 70 i 6750 <p>Antoni has been working on a tricky problem. <a href="https://gitlab.gnome.org/GNOME/gtk/blob/ecc612b1a2a0ec9791913cd22b4f94066f0448ae/gtk/gtkcontainer.h#L110">GTK+ structs that have Err codemadness.org 70 i 6751 bitfields</a> do not get reconstructed correctly from the Err codemadness.org 70 i 6752 GObject Introspection information — <a href="https://github.com/rust-lang/rfcs/issues/314">Rust does not handle C Err codemadness.org 70 i 6753 bitfields yet</a>. This has two implications. Err codemadness.org 70 i 6754 First, we lose some of the original struct fields in the generated Err codemadness.org 70 i 6755 bindings. Second, the sizes of the generated structs are not the Err codemadness.org 70 i 6756 same as the original C structs, so <code>g_type_register_static()</code> Err codemadness.org 70 i 6757 complains that one is trying to register an invalid class.</p> Err codemadness.org 70 i 6758 <p>Yesterday we got as far as reading the <a href="http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf">amd64</a> and [ARM][arm] Err codemadness.org 70 i 6759 ABI manuals to see what the hell C compilers are supposed to do for Err codemadness.org 70 i 6760 laying out structs with bitfields. Most likely, we will have a Err codemadness.org 70 i 6761 temporary fix in <a href="https://github.com/gtk-rs/gir">gir</a>'s code generator so that it generates Err codemadness.org 70 i 6762 structs with the same layout as the C ones, with padding in place of Err codemadness.org 70 i 6763 the space for bitfields. Later we can remove this when rustc gets Err codemadness.org 70 i 6764 support for C bitfields.</p> Err codemadness.org 70 i 6765 <p>I've been working on support for GObject interfaces. The basic Err codemadness.org 70 i 6766 parsing is done; I'm about to refactor the code generation so I can Err codemadness.org 70 i 6767 reuse the parts that fill vtables from classes.</p> Err codemadness.org 70 i 6768 <p>Yesterday we went to the Madrid Rust Meetup, a regular meeting of Err codemadness.org 70 i 6769 rustaceans here. Martin talked about WebRender; I talked about Err codemadness.org 70 i 6770 refactoring C to port it to Rust, and then Alex talked about Rust's Err codemadness.org 70 i 6771 plans for 2018. Fun times.</p> Err codemadness.org 70 i 6772 <p><img alt="Sponsored by the GNOME Foundation" src="https://people.gnome.org/~federico/blog/images/sponsored-badge-shadow.png"></p> Err codemadness.org 70 i 6773 <p><img alt="Hosted by OpenShine" src="https://www.openshine.com/wp-content/uploads/2016/03/openshine.png"></p> Err codemadness.org 70 i 6774 <p>[arm]: </p>Madrid GNOME+Rust Hackfest, part 12018-04-18T02:55:12-05:002018-04-18T02:55:12-05:00Federico Mena Quinterotag:people.gnome.org,2018-04-18:/~federico/blog/madrid-gnome-rust-1.html<p>I'm in Madrid since Monday, at the <a href="https://wiki.gnome.org/Hackfests/Rust2018">third GNOME+Rust hackfest</a>! Err codemadness.org 70 i 6775 The <a href="https://www.openshine.com/">OpenShine</a> folks are kindly letting us use their offices, on the Err codemadness.org 70 i 6776 seventh floor of a building by the <a href="https://www.openstreetmap.org/node/5303271991">Cuatro Caminos Err codemadness.org 70 i 6777 roundabout</a>.</p> Err codemadness.org 70 i 6778 <p>I am very, very thankful that this time everyone seems to be working Err codemadness.org 70 i 6779 on developing <a href="https://gitlab.gnome.org/federico/gnome-class">gnome-class</a>. It's …</p><p>I'm in Madrid since Monday, at the <a href="https://wiki.gnome.org/Hackfests/Rust2018">third GNOME+Rust hackfest</a>! Err codemadness.org 70 i 6780 The <a href="https://www.openshine.com/">OpenShine</a> folks are kindly letting us use their offices, on the Err codemadness.org 70 i 6781 seventh floor of a building by the <a href="https://www.openstreetmap.org/node/5303271991">Cuatro Caminos Err codemadness.org 70 i 6782 roundabout</a>.</p> Err codemadness.org 70 i 6783 <p>I am very, very thankful that this time everyone seems to be working Err codemadness.org 70 i 6784 on developing <a href="https://gitlab.gnome.org/federico/gnome-class">gnome-class</a>. It's a difficult project for me, and Err codemadness.org 70 i 6785 more brainpower is definitely welcome — all the indirection, type Err codemadness.org 70 i 6786 conversion, GObject obscurity, and procedural macro shenanigans Err codemadness.org 70 i 6787 definitely take a toll on oneself.</p> Err codemadness.org 70 i 6788 <h1>Gnome-class internals</h1> Err codemadness.org 70 i 6789 <p><img alt="Gnome-class internals on the whiteboard" src="https://people.gnome.org/~federico/blog/images/madrid-whiteboard.jpg"></p> Err codemadness.org 70 i 6790 <p>I explained how gnome-class works to the rest of the hackfest Err codemadness.org 70 i 6791 attendees. I've been writing a document on <a href="https://federico.pages.gitlab.gnome.org/gnome-class/">gnome-class's Err codemadness.org 70 i 6792 internals</a>, so the whiteboard was a whirlwind tour through Err codemadness.org 70 i 6793 it.</p> Err codemadness.org 70 i 6794 <h1>Error messages from the compiler</h1> Err codemadness.org 70 i 6795 <p>Antoni Boucher, the author of <a href="http://relm.ml/">relm</a> (a Rust crate to write GTK+ Err codemadness.org 70 i 6796 asynchronous widgets with an Elm-like model), explained to me how relm Err codemadness.org 70 i 6797 manages to present good error messages from the Rust compiler, when Err codemadness.org 70 i 6798 the user's code has mistakes. Right now this is in a very bad state Err codemadness.org 70 i 6799 in gnome-class: user errors within the invocation of the procedural Err codemadness.org 70 i 6800 macro get shown by the compiler as errors <em>at</em> the macro call, so you Err codemadness.org 70 i 6801 don't get line number information that is meaningful.</p> Err codemadness.org 70 i 6802 <p>For a large part of the day we tried to refactor bits of gnome-class Err codemadness.org 70 i 6803 to do something similar. It is very slightly better now, but this Err codemadness.org 70 i 6804 really requires me to sit down calmly, at home, and to fully Err codemadness.org 70 i 6805 understand how relm does it and what changes are needed in the <a href="https://github.com/dtolnay/syn">syn</a> Err codemadness.org 70 i 6806 parser crate to make it easy to present good errors.</p> Err codemadness.org 70 i 6807 <p>I think I'll continue this work at home, as there is a lot of source Err codemadness.org 70 i 6808 code to understand: the combinator parsers in <a href="https://github.com/dtolnay/syn">syn</a>, the error Err codemadness.org 70 i 6809 handling scheme in <a href="http://relm.ml/">relm</a>, and the peculiarities of gnome-class.</p> Err codemadness.org 70 i 6810 <h1>Further work during the hackfest</h1> Err codemadness.org 70 i 6811 <p>Other people working on gnome-class are adding support for GObject Err codemadness.org 70 i 6812 properties, inheritance from non-Rust classes, and improving the Err codemadness.org 70 i 6813 ergonomics of class-private structures.</p> Err codemadness.org 70 i 6814 <p>I think I'll stop working on error messages for now, and focus instead Err codemadness.org 70 i 6815 on either supporting GTypeInterfaces, or completing support for type Err codemadness.org 70 i 6816 conversions for methods and signals.</p> Err codemadness.org 70 i 6817 <h1>Other happenings in Rust</h1> Err codemadness.org 70 i 6818 <p>Paolo Borelli has been <a href="https://gitlab.gnome.org/GNOME/librsvg/merge_requests/50">porting RsvgState to Rust</a> in librsvg. Err codemadness.org 70 i 6819 This is the big structure that holds all the CSS state for SVG Err codemadness.org 70 i 6820 elements. This is very meticulous work, and I'm thankful that Paolo Err codemadness.org 70 i 6821 is paying good attention to it. Soon we will have all the style Err codemadness.org 70 i 6822 machinery for librsvg in Rust, which will make it easier to use the Err codemadness.org 70 i 6823 <a href="https://crates.io/crates/selectors">selectors crate from Servo</a> instead of libcroco, as the Err codemadness.org 70 i 6824 latter is unmaintained.</p> Err codemadness.org 70 i 6825 <h1>Food</h1> Err codemadness.org 70 i 6826 <p><img alt="Food in Madrid" src="https://people.gnome.org/~federico/blog/images/madrid-food.jpg"></p> Err codemadness.org 70 i 6827 <p>Ah, Spanish food. We have been enjoying cheese, jamón, tortilla, Err codemadness.org 70 i 6828 pimientos, oxtail stews, natillas, café con leche...</p> Err codemadness.org 70 i 6829 <h1>Thanks</h1> Err codemadness.org 70 i 6830 <p>Thanks to <a href="https://www.openshine.com/">OpenShine</a> for hosting the hackfest, and to the GNOME Err codemadness.org 70 i 6831 Foundation for sponsoring my travel. And thanks for Alberto Ruiz for Err codemadness.org 70 i 6832 putting me up in his house!</p> Err codemadness.org 70 i 6833 <p><img alt="Sponsored by the GNOME Foundation" src="https://people.gnome.org/~federico/blog/images/sponsored-badge-shadow.png"></p>Refactoring some repetitive code to a Rust macro2018-03-23T11:01:30-06:002018-03-23T11:01:30-06:00Federico Mena Quinterotag:people.gnome.org,2018-03-23:/~federico/blog/refactoring-some-repetitive-code-to-a-macro.html<p>I have started porting the code in librsvg that parses SVG's CSS Err codemadness.org 70 i 6834 properties from C to Rust. Many properties have symbolic values:</p> Err codemadness.org 70 i 6835 <div class="highlight"><pre><span></span><code>stroke-linejoin: miter | round | bevel | inherit Err codemadness.org 70 i 6836 Err codemadness.org 70 i 6837 stroke-linecap: butt | round | square | inherit Err codemadness.org 70 i 6838 Err codemadness.org 70 i 6839 fill-rule: nonzero | evenodd | inherit Err codemadness.org 70 i 6840 </code></pre></div> Err codemadness.org 70 i 6841 Err codemadness.org 70 i 6842 <p><code>StrokeLinejoin</code> is the first property that I ported. First I had to Err codemadness.org 70 i 6843 write a …</p><p>I have started porting the code in librsvg that parses SVG's CSS Err codemadness.org 70 i 6844 properties from C to Rust. Many properties have symbolic values:</p> Err codemadness.org 70 i 6845 <div class="highlight"><pre><span></span><code>stroke-linejoin: miter | round | bevel | inherit Err codemadness.org 70 i 6846 Err codemadness.org 70 i 6847 stroke-linecap: butt | round | square | inherit Err codemadness.org 70 i 6848 Err codemadness.org 70 i 6849 fill-rule: nonzero | evenodd | inherit Err codemadness.org 70 i 6850 </code></pre></div> Err codemadness.org 70 i 6851 Err codemadness.org 70 i 6852 <p><code>StrokeLinejoin</code> is the first property that I ported. First I had to Err codemadness.org 70 i 6853 write a little bunch of machinery to allow CSS properties to be kept Err codemadness.org 70 i 6854 in Rust-space instead of the main C structure that holds them Err codemadness.org 70 i 6855 (upcoming blog post about that). But for now, I just want to show how Err codemadness.org 70 i 6856 this boiled down to a macro after refactoring.</p> Err codemadness.org 70 i 6857 <h1>First cut at the code</h1> Err codemadness.org 70 i 6858 <p>The <code>stroke-linejoin</code> property can have the values <code>miter</code>, <code>round</code>, Err codemadness.org 70 i 6859 <code>bevel</code>, or <code>inherit</code>. Here is an enum definition for those values, Err codemadness.org 70 i 6860 and the conventional machinery which librsvg uses to parse property values:</p> Err codemadness.org 70 i 6861 <div class="highlight"><pre><span></span><code><span class="cp">#[derive(Debug, Copy, Clone)]</span><span class="w"></span> Err codemadness.org 70 i 6862 <span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">StrokeLinejoin</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6863 <span class="w"> </span><span class="n">Miter</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6864 <span class="w"> </span><span class="n">Round</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6865 <span class="w"> </span><span class="n">Bevel</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6866 <span class="w"> </span><span class="n">Inherit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6867 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6868 Err codemadness.org 70 i 6869 <span class="k">impl</span><span class="w"> </span><span class="n">Parse</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">StrokeLinejoin</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6870 <span class="w"> </span><span class="k">type</span> <span class="nc">Data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 6871 <span class="w"> </span><span class="k">type</span> <span class="nb">Err</span> <span class="o">=</span><span class="w"> </span><span class="n">AttributeError</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 6872 Err codemadness.org 70 i 6873 <span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">_</span>: <span class="nc">Self</span>::<span class="n">Data</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">StrokeLinejoin</span><span class="p">,</span><span class="w"> </span><span class="n">AttributeError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6874 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="n">trim</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6875 <span class="w"> </span><span class="s">&quot;miter&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StrokeLinejoin</span>::<span class="n">Miter</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 6876 <span class="w"> </span><span class="s">&quot;round&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StrokeLinejoin</span>::<span class="n">Round</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 6877 <span class="w"> </span><span class="s">&quot;bevel&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StrokeLinejoin</span>::<span class="n">Bevel</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 6878 <span class="w"> </span><span class="s">&quot;inherit&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StrokeLinejoin</span>::<span class="n">Inherit</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 6879 <span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">AttributeError</span>::<span class="n">from</span><span class="p">(</span><span class="n">ParseError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&quot;invalid value&quot;</span><span class="p">))),</span><span class="w"></span> Err codemadness.org 70 i 6880 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6881 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6882 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6883 </code></pre></div> Err codemadness.org 70 i 6884 Err codemadness.org 70 i 6885 <p>We <code>match</code> the allowed string values and map them to enum values. No Err codemadness.org 70 i 6886 big deal, right?</p> Err codemadness.org 70 i 6887 <p>Properties also have a default value. For example, the SVG spec says Err codemadness.org 70 i 6888 that if a shape doesn't have a <code>stroke-linejoin</code> property specified, Err codemadness.org 70 i 6889 it will use <code>miter</code> by default. Let's implement that:</p> Err codemadness.org 70 i 6890 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="nb">Default</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">StrokeLinejoin</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6891 <span class="w"> </span><span class="k">fn</span> <span class="nf">default</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">StrokeLinejoin</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6892 <span class="w"> </span><span class="n">StrokeLinejoin</span>::<span class="n">Miter</span><span class="w"></span> Err codemadness.org 70 i 6893 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6894 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6895 </code></pre></div> Err codemadness.org 70 i 6896 Err codemadness.org 70 i 6897 <p>So far, we have three things:</p> Err codemadness.org 70 i 6898 <ul> Err codemadness.org 70 i 6899 <li>An enum definition for the property's possible values.</li> Err codemadness.org 70 i 6900 <li><code>impl Parse</code> so we can parse the property from a string.</li> Err codemadness.org 70 i 6901 <li><code>impl Default</code> so the property knows its default value.</li> Err codemadness.org 70 i 6902 </ul> Err codemadness.org 70 i 6903 <h1>Where things got repetitive</h1> Err codemadness.org 70 i 6904 <p>The next property I ported was <code>stroke-linecap</code>, which can take the Err codemadness.org 70 i 6905 following values:</p> Err codemadness.org 70 i 6906 <div class="highlight"><pre><span></span><code><span class="cp">#[derive(Debug, Copy, Clone)]</span><span class="w"></span> Err codemadness.org 70 i 6907 <span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">StrokeLinecap</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6908 <span class="w"> </span><span class="n">Butt</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6909 <span class="w"> </span><span class="n">Round</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6910 <span class="w"> </span><span class="n">Square</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6911 <span class="w"> </span><span class="n">Inherit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6912 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6913 </code></pre></div> Err codemadness.org 70 i 6914 Err codemadness.org 70 i 6915 <p>This is similar in shape to the <code>StrokeLinejoin</code> enum above; Err codemadness.org 70 i 6916 it's just different names.</p> Err codemadness.org 70 i 6917 <p>The parsing has exactly the same shape, and just different values:</p> Err codemadness.org 70 i 6918 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">Parse</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">StrokeLinecap</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6919 <span class="w"> </span><span class="k">type</span> <span class="nc">Data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 6920 <span class="w"> </span><span class="k">type</span> <span class="nb">Err</span> <span class="o">=</span><span class="w"> </span><span class="n">AttributeError</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 6921 Err codemadness.org 70 i 6922 <span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">_</span>: <span class="nc">Self</span>::<span class="n">Data</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">StrokeLinecap</span><span class="p">,</span><span class="w"> </span><span class="n">AttributeError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6923 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="n">trim</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6924 <span class="w"> </span><span class="s">&quot;butt&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StrokeLinecap</span>::<span class="n">Butt</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 6925 <span class="w"> </span><span class="s">&quot;round&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StrokeLinecap</span>::<span class="n">Round</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 6926 <span class="w"> </span><span class="s">&quot;square&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StrokeLinecap</span>::<span class="n">Square</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 6927 <span class="w"> </span><span class="s">&quot;inherit&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StrokeLinecap</span>::<span class="n">Inherit</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 6928 Err codemadness.org 70 i 6929 <span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">AttributeError</span>::<span class="n">from</span><span class="p">(</span><span class="n">ParseError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&quot;invalid value&quot;</span><span class="p">))),</span><span class="w"></span> Err codemadness.org 70 i 6930 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6931 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6932 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6933 </code></pre></div> Err codemadness.org 70 i 6934 Err codemadness.org 70 i 6935 <p>Same thing with the default:</p> Err codemadness.org 70 i 6936 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="nb">Default</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">StrokeLinecap</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6937 <span class="w"> </span><span class="k">fn</span> <span class="nf">default</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">StrokeLinecap</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6938 <span class="w"> </span><span class="n">StrokeLinecap</span>::<span class="n">Butt</span><span class="w"></span> Err codemadness.org 70 i 6939 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6940 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6941 </code></pre></div> Err codemadness.org 70 i 6942 Err codemadness.org 70 i 6943 <p>Yes, the SVG spec has</p> Err codemadness.org 70 i 6944 <div class="highlight"><pre><span></span><code><span class="k">default</span><span class="o">:</span> <span class="n">butt</span> Err codemadness.org 70 i 6945 </code></pre></div> Err codemadness.org 70 i 6946 Err codemadness.org 70 i 6947 <p>somewhere in it, much to the delight of the 12-year old in me.</p> Err codemadness.org 70 i 6948 <h1>Refactoring to a macro</h1> Err codemadness.org 70 i 6949 <p>Here I wanted to define a <code>make_ident_property!()</code> macro that would Err codemadness.org 70 i 6950 get invoked like this:</p> Err codemadness.org 70 i 6951 <div class="highlight"><pre><span></span><code><span class="n">make_ident_property</span><span class="o">!</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 6952 <span class="w"> </span><span class="n">StrokeLinejoin</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6953 <span class="w"> </span><span class="n">default</span>: <span class="nc">Miter</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6954 Err codemadness.org 70 i 6955 <span class="w"> </span><span class="s">&quot;miter&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Miter</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6956 <span class="w"> </span><span class="s">&quot;round&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Round</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6957 <span class="w"> </span><span class="s">&quot;bevel&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Bevel</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6958 <span class="w"> </span><span class="s">&quot;inherit&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Inherit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6959 <span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 6960 </code></pre></div> Err codemadness.org 70 i 6961 Err codemadness.org 70 i 6962 <p>It's called <code>make_ident_property</code> because it makes a property Err codemadness.org 70 i 6963 definition from simple string identifiers. It has the name of the Err codemadness.org 70 i 6964 property (<code>StrokeLinejoin</code>), a <code>default</code> value, and a few repeating Err codemadness.org 70 i 6965 elements, one for each possible value.</p> Err codemadness.org 70 i 6966 <p>In Rust-speak, the macro's basic pattern is like this:</p> Err codemadness.org 70 i 6967 <div class="highlight"><pre><span></span><code><span class="fm">macro_rules!</span><span class="w"> </span><span class="n">make_ident_property</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6968 <span class="w"> </span><span class="p">(</span><span class="cp">$name</span>: <span class="nc">ident</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6969 <span class="w"> </span><span class="n">default</span>: <span class="cp">$default</span>: <span class="nc">ident</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6970 <span class="w"> </span><span class="cp">$($str_prop</span>: <span class="nc">expr</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="cp">$variant</span>: <span class="nc">ident</span><span class="p">,)</span><span class="o">+</span><span class="w"></span> Err codemadness.org 70 i 6971 <span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6972 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="kr">macro</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">here</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 6973 <span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 6974 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6975 </code></pre></div> Err codemadness.org 70 i 6976 Err codemadness.org 70 i 6977 <p>Let's dissect that pattern:</p> Err codemadness.org 70 i 6978 <div class="highlight"><pre><span></span><code><span class="fm">macro_rules!</span><span class="w"> </span><span class="n">make_ident_property</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6979 <span class="w"> </span><span class="p">(</span><span class="cp">$name</span>: <span class="nc">ident</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6980 <span class="c1">// ^^^^^^^^^^^^ will match an identifier and put it in $name</span> Err codemadness.org 70 i 6981 Err codemadness.org 70 i 6982 <span class="w"> </span><span class="n">default</span>: <span class="cp">$default</span>: <span class="nc">ident</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 6983 <span class="c1">// ^^^^^^^^^^^^^^^ will match an identifier and put it in $default</span> Err codemadness.org 70 i 6984 <span class="c1">// ^^^^^^^^ arbitrary text</span> Err codemadness.org 70 i 6985 Err codemadness.org 70 i 6986 <span class="w"> </span><span class="cp">$($str_prop</span>: <span class="nc">expr</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="cp">$variant</span>: <span class="nc">ident</span><span class="p">,)</span><span class="o">+</span><span class="w"></span> Err codemadness.org 70 i 6987 <span class="w"> </span><span class="o">^^</span><span class="w"> </span><span class="n">arbitrary</span><span class="w"> </span><span class="n">text</span><span class="w"></span> Err codemadness.org 70 i 6988 <span class="c1">// ^^ start of repetition ^^ end of repetition, repeats one or more times</span> Err codemadness.org 70 i 6989 Err codemadness.org 70 i 6990 <span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 6991 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 6992 <span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 6993 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 6994 </code></pre></div> Err codemadness.org 70 i 6995 Err codemadness.org 70 i 6996 <p>For example, saying "<code>$foo: ident</code>" in a macro's pattern means that the Err codemadness.org 70 i 6997 compiler will expect an identifier, and bind it to <code>$foo</code> within the Err codemadness.org 70 i 6998 macro's definition.</p> Err codemadness.org 70 i 6999 <p>Similarly, an <code>expr</code> means that the compiler will Err codemadness.org 70 i 7000 look for an expression — in this case, we want one of the string Err codemadness.org 70 i 7001 values.</p> Err codemadness.org 70 i 7002 <p>In a macro pattern, anything that is not a binding is just arbitrary Err codemadness.org 70 i 7003 text which must appear in the macro's invocation. This is how we can Err codemadness.org 70 i 7004 create a little syntax of our own within the macro: the "<code>default:</code>" Err codemadness.org 70 i 7005 part, and the "<code>=&gt;</code>" inside each string/symbol pair.</p> Err codemadness.org 70 i 7006 <p>Finally, macro patterns allow repetition. Anything within <code>$(...)</code> Err codemadness.org 70 i 7007 indicates repetition. Here, <code>$(...)+</code> indicates that the Err codemadness.org 70 i 7008 compiler must match one or more of the repeating elements.</p> Err codemadness.org 70 i 7009 <p>I pasted the duplicated code, and substituted the actual symbol names Err codemadness.org 70 i 7010 for the macro's bindings:</p> Err codemadness.org 70 i 7011 <div class="highlight"><pre><span></span><code><span class="fm">macro_rules!</span><span class="w"> </span><span class="n">make_ident_property</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7012 <span class="w"> </span><span class="p">(</span><span class="cp">$name</span>: <span class="nc">ident</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7013 <span class="w"> </span><span class="n">default</span>: <span class="cp">$default</span>: <span class="nc">ident</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7014 <span class="w"> </span><span class="cp">$($str_prop</span>: <span class="nc">expr</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="cp">$variant</span>: <span class="nc">ident</span><span class="p">,)</span><span class="o">+</span><span class="w"></span> Err codemadness.org 70 i 7015 <span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7016 <span class="w"> </span><span class="cp">#[derive(Debug, Copy, Clone)]</span><span class="w"></span> Err codemadness.org 70 i 7017 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="cp">$name</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7018 <span class="w"> </span><span class="cp">$($variant</span><span class="p">),</span><span class="o">+</span><span class="w"></span> Err codemadness.org 70 i 7019 <span class="c1">// ^^^^^^^^^^^^^ this is how we invoke a repeated element</span> Err codemadness.org 70 i 7020 Err codemadness.org 70 i 7021 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7022 Err codemadness.org 70 i 7023 <span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="nb">Default</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="cp">$name</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7024 <span class="w"> </span><span class="k">fn</span> <span class="nf">default</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="cp">$name</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7025 <span class="w"> </span><span class="cp">$name</span>::<span class="cp">$default</span><span class="w"></span> Err codemadness.org 70 i 7026 <span class="c1">// ^^^^^^^^^^^^^^^ construct an enum::variant</span> Err codemadness.org 70 i 7027 Err codemadness.org 70 i 7028 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7029 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7030 Err codemadness.org 70 i 7031 <span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">Parse</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="cp">$name</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7032 <span class="w"> </span><span class="k">type</span> <span class="nc">Data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 7033 <span class="w"> </span><span class="k">type</span> <span class="nb">Err</span> <span class="o">=</span><span class="w"> </span><span class="n">AttributeError</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7034 Err codemadness.org 70 i 7035 <span class="w"> </span><span class="k">fn</span> <span class="nf">parse</span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">_</span>: <span class="nc">Self</span>::<span class="n">Data</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="cp">$name</span><span class="p">,</span><span class="w"> </span><span class="n">AttributeError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7036 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="n">trim</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7037 <span class="w"> </span><span class="cp">$($str_prop</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="cp">$name</span>::<span class="cp">$variant</span><span class="p">),)</span><span class="o">+</span><span class="w"></span> Err codemadness.org 70 i 7038 <span class="c1">// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expand repeated elements</span> Err codemadness.org 70 i 7039 Err codemadness.org 70 i 7040 <span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">AttributeError</span>::<span class="n">from</span><span class="p">(</span><span class="n">ParseError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&quot;invalid value&quot;</span><span class="p">))),</span><span class="w"></span> Err codemadness.org 70 i 7041 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7042 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7043 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7044 <span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 7045 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7046 </code></pre></div> Err codemadness.org 70 i 7047 Err codemadness.org 70 i 7048 <h1>Getting rid of duplicated code</h1> Err codemadness.org 70 i 7049 <p>Now we have a macro that we can call to define new properties. Err codemadness.org 70 i 7050 Librsvg now has this, which is much more readable than all the code Err codemadness.org 70 i 7051 written by hand:</p> Err codemadness.org 70 i 7052 <div class="highlight"><pre><span></span><code><span class="n">make_ident_property</span><span class="o">!</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 7053 <span class="w"> </span><span class="n">StrokeLinejoin</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7054 <span class="w"> </span><span class="n">default</span>: <span class="nc">Miter</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7055 Err codemadness.org 70 i 7056 <span class="w"> </span><span class="s">&quot;miter&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Miter</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7057 <span class="w"> </span><span class="s">&quot;round&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Round</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7058 <span class="w"> </span><span class="s">&quot;bevel&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Bevel</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7059 <span class="w"> </span><span class="s">&quot;inherit&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Inherit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7060 <span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7061 Err codemadness.org 70 i 7062 <span class="n">make_ident_property</span><span class="o">!</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 7063 <span class="w"> </span><span class="n">StrokeLinecap</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7064 <span class="w"> </span><span class="n">default</span>: <span class="nc">Butt</span><span class="p">,</span><span class="w"> </span><span class="c1">// :)</span> Err codemadness.org 70 i 7065 Err codemadness.org 70 i 7066 <span class="w"> </span><span class="s">&quot;butt&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Butt</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7067 <span class="w"> </span><span class="s">&quot;round&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Round</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7068 <span class="w"> </span><span class="s">&quot;square&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Square</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7069 <span class="w"> </span><span class="s">&quot;inherit&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Inherit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7070 <span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7071 Err codemadness.org 70 i 7072 <span class="n">make_ident_property</span><span class="o">!</span><span class="p">(</span><span class="w"></span> Err codemadness.org 70 i 7073 <span class="w"> </span><span class="n">FillRule</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7074 <span class="w"> </span><span class="n">default</span>: <span class="nc">NonZero</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7075 Err codemadness.org 70 i 7076 <span class="w"> </span><span class="s">&quot;nonzero&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">NonZero</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7077 <span class="w"> </span><span class="s">&quot;evenodd&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">EvenOdd</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7078 <span class="w"> </span><span class="s">&quot;inherit&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Inherit</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7079 <span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7080 </code></pre></div> Err codemadness.org 70 i 7081 Err codemadness.org 70 i 7082 <p>Etcetera. It's now easy to port similar symbol-based properties from Err codemadness.org 70 i 7083 C to Rust.</p> Err codemadness.org 70 i 7084 <p>Eventually I'll need to refactor all the crap that deals with Err codemadness.org 70 i 7085 inheritable properties, but that's for another time.</p> Err codemadness.org 70 i 7086 <h1>Conclusion and references</h1> Err codemadness.org 70 i 7087 <p>Rust macros are very powerful to refactor repetitive code like this.</p> Err codemadness.org 70 i 7088 <p><a href="https://doc.rust-lang.org/book/second-edition/appendix-04-macros.html">The Rust book</a> Err codemadness.org 70 i 7089 has an introductory appendix to macros, and <a href="https://danielkeep.github.io/tlborm/book/index.html">The Little Book of Rust Err codemadness.org 70 i 7090 Macros</a> is a Err codemadness.org 70 i 7091 fantastic resource that really dives into what you can do.</p>Making sure the repository doesn't break, automatically2018-03-20T19:37:02-06:002018-03-20T20:33:46-06:00Federico Mena Quinterotag:people.gnome.org,2018-03-20:/~federico/blog/making-sure-the-repository-doesnt-break.html<p>Gitlab has a fairly conventional Continuous Integration system: you Err codemadness.org 70 i 7092 push some commits, the CI pipelines build the code and presumably run Err codemadness.org 70 i 7093 the test suite, and later you can know if this succeeded of failed.</p> Err codemadness.org 70 i 7094 <p>But by the time something fails, the broken code is already in the Err codemadness.org 70 i 7095 public repository.</p> Err codemadness.org 70 i 7096 <p>The …</p><p>Gitlab has a fairly conventional Continuous Integration system: you Err codemadness.org 70 i 7097 push some commits, the CI pipelines build the code and presumably run Err codemadness.org 70 i 7098 the test suite, and later you can know if this succeeded of failed.</p> Err codemadness.org 70 i 7099 <p>But by the time something fails, the broken code is already in the Err codemadness.org 70 i 7100 public repository.</p> Err codemadness.org 70 i 7101 <p>The Rust community uses Bors, a bot that prevents this from happening:</p> Err codemadness.org 70 i 7102 <ul> Err codemadness.org 70 i 7103 <li> Err codemadness.org 70 i 7104 <p>You push some commits and submit a merge request.</p> Err codemadness.org 70 i 7105 </li> Err codemadness.org 70 i 7106 <li> Err codemadness.org 70 i 7107 <p>A human looks at your merge request; they may tell you to make Err codemadness.org 70 i 7108 changes, or they may tell Bors that your request is approved for Err codemadness.org 70 i 7109 merging.</p> Err codemadness.org 70 i 7110 </li> Err codemadness.org 70 i 7111 <li> Err codemadness.org 70 i 7112 <p>Bors looks for approved merge requests. It merges each into a Err codemadness.org 70 i 7113 <em>temporary branch</em> and waits for the CI pipeline to run there. If Err codemadness.org 70 i 7114 CI passes, Bors automatically merges to master. If CI fails, Bors Err codemadness.org 70 i 7115 annotates the merge request with the failure, <strong>and the main Err codemadness.org 70 i 7116 repository stays working</strong>.</p> Err codemadness.org 70 i 7117 </li> Err codemadness.org 70 i 7118 </ul> Err codemadness.org 70 i 7119 <p>Bors also tells you if the mainline has moved forward and there's a Err codemadness.org 70 i 7120 merge conflict. In that case you need to do a rebase yourself; the Err codemadness.org 70 i 7121 repository stays working in the meantime.</p> Err codemadness.org 70 i 7122 <p>This leads to a very fair, very transparent process for contributors Err codemadness.org 70 i 7123 and for maintainers. For all the details, watch <a href="https://www.youtube.com/watch?v=dIageYT0Vgg">Emily Dunham's Err codemadness.org 70 i 7124 presentation on Rust's community Err codemadness.org 70 i 7125 automation</a> Err codemadness.org 70 i 7126 (<a href="http://edunham.net/2016/09/27/rust_s_community_automation.html">transcript</a>).</p> Err codemadness.org 70 i 7127 <p>For a description of where Bors came from, read <a href="https://graydon.livejournal.com/186550.html">Graydon Hoare's Err codemadness.org 70 i 7128 blog</a>.</p> Err codemadness.org 70 i 7129 <p><a href="https://github.com/graydon/bors">Bors</a> evolved into Err codemadness.org 70 i 7130 <a href="https://github.com/servo/homu">Homu</a> and it is what Rust and Servo Err codemadness.org 70 i 7131 use currently. However, Homu depends on Github.</p> Err codemadness.org 70 i 7132 <p>I just found out that there is a <a href="https://github.com/coldnight/homu-gitlab">port of Homu for Err codemadness.org 70 i 7133 Gitlab</a>. Would anyone care Err codemadness.org 70 i 7134 to set it up?</p> Err codemadness.org 70 i 7135 <p><strong>Update:</strong> <a href="https://a.weirder.earth/@bb010g/99719506883461036">Two</a> Err codemadness.org 70 i 7136 <a href="https://octodon.social/@graydon/99719514193737493">people</a> have Err codemadness.org 70 i 7137 suggested porting <a href="https://bors.tech/">Bors-ng</a> to Gitlab instead, Err codemadness.org 70 i 7138 <a href="https://a.weirder.earth/@bb010g/99719537971696863">for scalability Err codemadness.org 70 i 7139 reasons</a>.</p>Librsvg and Gnome-class accepting interns2018-03-12T19:00:08-06:002018-03-13T10:04:33-06:00Federico Mena Quinterotag:people.gnome.org,2018-03-12:/~federico/blog/interns-summer-2018.html<p>I would like to mentor people for <a href="https://gitlab.gnome.org/GNOME/librsvg">librsvg</a> and <a href="https://gitlab.gnome.org/federico/gnome-class">gnome-class</a> this Err codemadness.org 70 i 7140 Summer, both for <a href="https://www.outreachy.org/">Outreachy</a> and <a href="https://wiki.gnome.org/Outreach/SummerOfCode">Summer of Code</a>.</p> Err codemadness.org 70 i 7141 <h1>Librsvg projects</h1> Err codemadness.org 70 i 7142 <p><strong><em>Project:</em></strong> <a href="https://www.outreachy.org/2018-may-august/communities/gnome/">port filter effects from C to Rust</a></p> Err codemadness.org 70 i 7143 <p>Currently librsvg implements SVG filter effects in C. These are basic Err codemadness.org 70 i 7144 image processing filters like Gaussian blur, matrix convolution, Err codemadness.org 70 i 7145 Porter-Duff alpha …</p><p>I would like to mentor people for <a href="https://gitlab.gnome.org/GNOME/librsvg">librsvg</a> and <a href="https://gitlab.gnome.org/federico/gnome-class">gnome-class</a> this Err codemadness.org 70 i 7146 Summer, both for <a href="https://www.outreachy.org/">Outreachy</a> and <a href="https://wiki.gnome.org/Outreach/SummerOfCode">Summer of Code</a>.</p> Err codemadness.org 70 i 7147 <h1>Librsvg projects</h1> Err codemadness.org 70 i 7148 <p><strong><em>Project:</em></strong> <a href="https://www.outreachy.org/2018-may-august/communities/gnome/">port filter effects from C to Rust</a></p> Err codemadness.org 70 i 7149 <p>Currently librsvg implements SVG filter effects in C. These are basic Err codemadness.org 70 i 7150 image processing filters like Gaussian blur, matrix convolution, Err codemadness.org 70 i 7151 Porter-Duff alpha compositing, etc.</p> Err codemadness.org 70 i 7152 <p>There are <a href="https://gitlab.gnome.org/GNOME/librsvg/milestones/4">some things</a> that need to be done:</p> Err codemadness.org 70 i 7153 <ul> Err codemadness.org 70 i 7154 <li> Err codemadness.org 70 i 7155 <p>Split the single <code>rsvg-filter.c</code> into multiple source files, so it's Err codemadness.org 70 i 7156 easier to port each one individually.</p> Err codemadness.org 70 i 7157 </li> Err codemadness.org 70 i 7158 <li> Err codemadness.org 70 i 7159 <p>Figure out the common infrasctructure: <code>RsvgFilter</code>, Err codemadness.org 70 i 7160 <code>RsvgFilterPrimitive</code>. All the filter use these to store Err codemadness.org 70 i 7161 intermediate results when processing SVG elements.</p> Err codemadness.org 70 i 7162 </li> Err codemadness.org 70 i 7163 <li> Err codemadness.org 70 i 7164 <p>Experiment with the correct Rust abstractions to process images Err codemadness.org 70 i 7165 pixel-by-pixel. We would like to omit per-pixel bounds checks on Err codemadness.org 70 i 7166 array accesses. The <a href="https://crates.io/crates/image">image crate</a> has some nice iterator Err codemadness.org 70 i 7167 traits for pixels. WebKit's implementation of SVG filters also has Err codemadness.org 70 i 7168 interesting abstractions for things like the need for a sliding Err codemadness.org 70 i 7169 window with edge handling for Gaussian blurs.</p> Err codemadness.org 70 i 7170 </li> Err codemadness.org 70 i 7171 <li> Err codemadness.org 70 i 7172 <p>Ensure that our current filters code is actually working. Not all Err codemadness.org 70 i 7173 of the official SVG test suite's tests are in place right now for Err codemadness.org 70 i 7174 the filter effects; it is likely that some of our implementation is Err codemadness.org 70 i 7175 broken.</p> Err codemadness.org 70 i 7176 </li> Err codemadness.org 70 i 7177 </ul> Err codemadness.org 70 i 7178 <p>For this project, it will be especially helpful to have a little Err codemadness.org 70 i 7179 background in image processing. You don't need to be an expert; just Err codemadness.org 70 i 7180 to have done some pixel crunching at some point. You need to be able Err codemadness.org 70 i 7181 to read C and write Rust.</p> Err codemadness.org 70 i 7182 <p><strong><em>Project:</em></strong> <a href="https://gitlab.gnome.org/GNOME/librsvg/milestones/6">CSS styling with rust-selectors</a></p> Err codemadness.org 70 i 7183 <p>Librsvg uses an very simplistic algorithm for CSS cascading. It uses Err codemadness.org 70 i 7184 libcroco to parse CSS style data; libcroco is unmaintained and rather Err codemadness.org 70 i 7185 prone to exploits. I want to use Servo's selectors crate to do the Err codemadness.org 70 i 7186 cascading; we already use the rust-cssparser crate as a tokenizer for Err codemadness.org 70 i 7187 basic CSS properties.</p> Err codemadness.org 70 i 7188 <ul> Err codemadness.org 70 i 7189 <li> Err codemadness.org 70 i 7190 <p>For each node in its DOM tree, librsvg's <code>Node</code> structure keeps a Err codemadness.org 70 i 7191 <code>Vec&lt;&gt;</code> of children. <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/220">We need to move this to store the next Err codemadness.org 70 i 7192 sibling and the first/last children instead</a>. This is the Err codemadness.org 70 i 7193 data structure that rust-selectors prefers. The Kuchiki crate has Err codemadness.org 70 i 7194 an example implementation; borrowing some patterns from there could Err codemadness.org 70 i 7195 also help us simplify our reference counting for nodes.</p> Err codemadness.org 70 i 7196 </li> Err codemadness.org 70 i 7197 <li> Err codemadness.org 70 i 7198 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/issues/223">Our styling machinery needs porting to Rust</a>. We have a Err codemadness.org 70 i 7199 big <code>RsvgState</code> struct which holds the CSS state for each node. It Err codemadness.org 70 i 7200 is easy to port this to Rust; it's more interesting to gradually Err codemadness.org 70 i 7201 move it to a scheme like Servo's, with a distinction between Err codemadness.org 70 i 7202 specified/computed/used values for each CSS property.</p> Err codemadness.org 70 i 7203 </li> Err codemadness.org 70 i 7204 </ul> Err codemadness.org 70 i 7205 <p>For this project, it will be helpful to know a bit of how CSS works. Err codemadness.org 70 i 7206 Definitely be comfortable with Rust concepts like ownership and Err codemadness.org 70 i 7207 borrowing. You don't need to be an expert, but if you are going Err codemadness.org 70 i 7208 through the "fighting the borrow checker" stage, you'll have a harder Err codemadness.org 70 i 7209 time with this. Or it may be what lets you grow out of it! You need Err codemadness.org 70 i 7210 to be able to read C and write Rust.</p> Err codemadness.org 70 i 7211 <p><strong><em>Bugs for newcomers:</em></strong> We have a number of <a href="https://gitlab.gnome.org/GNOME/librsvg/issues?scope=all&amp;utf8=%E2%9C%93&amp;state=opened&amp;label_name[]=4.%20Newcomers">easy bugs for newcomers Err codemadness.org 70 i 7212 to librsvg</a>. Some of these are in the Rust part, some Err codemadness.org 70 i 7213 in the C part, some in both &mdash; take your pick!</p> Err codemadness.org 70 i 7214 <h1>Projects for gnome-class</h1> Err codemadness.org 70 i 7215 <p><a href="https://gitlab.gnome.org/federico/gnome-class">Gnome-class</a> is the code generator that lets you write Err codemadness.org 70 i 7216 GObject implementations in Rust. Or at least that's the intention Err codemadness.org 70 i 7217 &mdash; the project is in early development. The code is so new that Err codemadness.org 70 i 7218 <a href="https://gitlab.gnome.org/federico/gnome-class/issues">practically all of our bugs</a> are of an exploratory Err codemadness.org 70 i 7219 nature.</p> Err codemadness.org 70 i 7220 <p>Gnome-class works like a little compiler. This is from one of the Err codemadness.org 70 i 7221 examples; note the call to <code>gobject_gen!</code> in there:</p> Err codemadness.org 70 i 7222 <div class="highlight"><pre><span></span><code><span class="k">struct</span> <span class="nc">SignalerPrivate</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7223 <span class="w"> </span><span class="n">val</span>: <span class="nc">Cell</span><span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="w"></span> Err codemadness.org 70 i 7224 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7225 Err codemadness.org 70 i 7226 <span class="k">impl</span><span class="w"> </span><span class="nb">Default</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">SignalerPrivate</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7227 <span class="w"> </span><span class="k">fn</span> <span class="nf">default</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7228 <span class="w"> </span><span class="n">SignalerPrivate</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7229 <span class="w"> </span><span class="n">val</span>: <span class="nc">Cell</span>::<span class="n">new</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 7230 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7231 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7232 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7233 Err codemadness.org 70 i 7234 <span class="n">gobject_gen</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7235 <span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="n">Signaler</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7236 <span class="w"> </span><span class="k">type</span> <span class="nc">InstancePrivate</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">SignalerPrivate</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7237 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7238 Err codemadness.org 70 i 7239 <span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">Signaler</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7240 <span class="w"> </span><span class="n">signal</span><span class="w"> </span><span class="k">fn</span> <span class="nf">value_changed</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7241 Err codemadness.org 70 i 7242 <span class="w"> </span><span class="k">fn</span> <span class="nf">set_value</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">v</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7243 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">private</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">get_priv</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 7244 <span class="w"> </span><span class="n">private</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">set</span><span class="p">(</span><span class="n">v</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7245 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">emit_value_changed</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 7246 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7247 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7248 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7249 </code></pre></div> Err codemadness.org 70 i 7250 Err codemadness.org 70 i 7251 <p>Gnome-class implements this <code>gobject_gen!</code> macro as follows:</p> Err codemadness.org 70 i 7252 <ol> Err codemadness.org 70 i 7253 <li> Err codemadness.org 70 i 7254 <p>First we parse the code inside the macro using the <code>syn</code> crate. Err codemadness.org 70 i 7255 This is a crate that lets you parse Rust source code from the Err codemadness.org 70 i 7256 <code>TokenStream</code> that the compiler hands to implementations of procedural Err codemadness.org 70 i 7257 macros. You give a <code>TokenStream</code> to <code>syn</code>, and it gives you back Err codemadness.org 70 i 7258 structs that represent function definitions, <code>impl</code> blocks, Err codemadness.org 70 i 7259 expressions, etc. From this parsing stage we build an Abstract Syntax Err codemadness.org 70 i 7260 Tree (AST) that closely matches the structure of the code that the Err codemadness.org 70 i 7261 user wrote.</p> Err codemadness.org 70 i 7262 </li> Err codemadness.org 70 i 7263 <li> Err codemadness.org 70 i 7264 <p>Second, we take the AST and convert it to higher-level concepts, Err codemadness.org 70 i 7265 while verifying that the code is semantically valid. For example, we Err codemadness.org 70 i 7266 build up a <code>Class</code> structure for each defined GObject class, and Err codemadness.org 70 i 7267 annotate it with the methods and signals that the user defined for it. Err codemadness.org 70 i 7268 This stage is the High-level Internal Representation (HIR).</p> Err codemadness.org 70 i 7269 </li> Err codemadness.org 70 i 7270 <li> Err codemadness.org 70 i 7271 <p>Third, we generate Rust code from the validated HIR. For each Err codemadness.org 70 i 7272 class, we write out the boilerplate needed to register it against the Err codemadness.org 70 i 7273 GObject type system. For each virtual method we write a trampoline to Err codemadness.org 70 i 7274 let the C code call into the Rust implementation, and then write out Err codemadness.org 70 i 7275 the actual Rust impl that the user wrote. For each signal, we Err codemadness.org 70 i 7276 register it against the GObjectClass, and write the appropriate Err codemadness.org 70 i 7277 trampolines both to invoke the signal's default handler and any Rust Err codemadness.org 70 i 7278 callbacks for signal handlers.</p> Err codemadness.org 70 i 7279 </li> Err codemadness.org 70 i 7280 </ol> Err codemadness.org 70 i 7281 <p>For this project, you definitely need to have written GObject code in Err codemadness.org 70 i 7282 C in the past. You don't need to know the GObject internals; just Err codemadness.org 70 i 7283 know that there are things like type registration, signal creation, Err codemadness.org 70 i 7284 argument marshalling, etc.</p> Err codemadness.org 70 i 7285 <p>You don't need to know about compiler internals.</p> Err codemadness.org 70 i 7286 <p>You don't need to have written Rust procedural macros; you can learn Err codemadness.org 70 i 7287 as you go. The code has enough infrastructure right now that you can Err codemadness.org 70 i 7288 cut&amp;paste useful bits to get started with new features. You should Err codemadness.org 70 i 7289 definitely be comfortable with the Rust borrow checker and simple Err codemadness.org 70 i 7290 lifetimes &mdash; again, you can cut&amp;paste useful code already, and Err codemadness.org 70 i 7291 I'm happy to help with those.</p> Err codemadness.org 70 i 7292 <p>This project demands a little patience. Working on the implementation Err codemadness.org 70 i 7293 of procedural macros is not the smoothest experience right now (one Err codemadness.org 70 i 7294 needs to examine generated code carefully, and play some tricks with Err codemadness.org 70 i 7295 the compiler to debug things), but it's getting better very fast.</p> Err codemadness.org 70 i 7296 <h1>How to apply as an intern</h1> Err codemadness.org 70 i 7297 <p><a href="https://www.outreachy.org/apply/">Details for Outreachy</a></p> Err codemadness.org 70 i 7298 <p><a href="https://wiki.gnome.org/Outreach/SummerOfCode/Students">Details for Summer of Code</a></p>Helping Cairo2018-03-06T18:22:52-06:002018-03-06T18:22:52-06:00Federico Mena Quinterotag:people.gnome.org,2018-03-06:/~federico/blog/helping-cairo.html<p><a href="https://www.cairographics.org/">Cairo</a> needs help. It is the main 2D rendering library we use Err codemadness.org 70 i 7299 in GNOME, and in particular, it's what librsvg uses to render all Err codemadness.org 70 i 7300 SVGs.</p> Err codemadness.org 70 i 7301 <p>My immediate problem with Cairo is that it explodes when called with Err codemadness.org 70 i 7302 floating-point coordinates that fall outside the range that its Err codemadness.org 70 i 7303 internal fixed-point numbers can …</p><p><a href="https://www.cairographics.org/">Cairo</a> needs help. It is the main 2D rendering library we use Err codemadness.org 70 i 7304 in GNOME, and in particular, it's what librsvg uses to render all Err codemadness.org 70 i 7305 SVGs.</p> Err codemadness.org 70 i 7306 <p>My immediate problem with Cairo is that it explodes when called with Err codemadness.org 70 i 7307 floating-point coordinates that fall outside the range that its Err codemadness.org 70 i 7308 internal fixed-point numbers can represent. There is no validation of Err codemadness.org 70 i 7309 incoming data, so the polygon intersector ends up with data that makes Err codemadness.org 70 i 7310 no sense, and it crashes.</p> Err codemadness.org 70 i 7311 <p>I've been studying how Cairo converts from floating-point to its Err codemadness.org 70 i 7312 fixed-point representation, and it's a nifty little algorithm. So I Err codemadness.org 70 i 7313 thought, no problem, I'll add validation, see how to represent the Err codemadness.org 70 i 7314 error state internally in Cairo, and see if clients are happy with Err codemadness.org 70 i 7315 getting back a <code>cairo_t</code> in an error state.</p> Err codemadness.org 70 i 7316 <p>Cairo has a very thorough test suite... <strong><em>that doesn't pass</em></strong>. It Err codemadness.org 70 i 7317 is documented to be very hard to pass fully for all rendering Err codemadness.org 70 i 7318 backends. This is understandable, as there may be bugs in X servers Err codemadness.org 70 i 7319 or OpenGL implementations and such. But for the basic, software-only, Err codemadness.org 70 i 7320 in-memory image backend, Cairo should 100% pass its test suite all the Err codemadness.org 70 i 7321 time. This is not the case right now; in my tree, for all the tests Err codemadness.org 70 i 7322 of the image backend I get</p> Err codemadness.org 70 i 7323 <div class="highlight"><pre><span></span><code><span class="mf">497</span> <span class="n">Passed</span><span class="p">,</span> <span class="mf">54</span> <span class="n">Failed</span> <span class="err">[</span><span class="mf">0</span> <span class="n">crashed</span><span class="p">,</span> <span class="mf">14</span> <span class="nb">exp</span><span class="n">ected</span><span class="err">]</span><span class="p">,</span> <span class="mf">27</span> <span class="n">Skipped</span> Err codemadness.org 70 i 7324 </code></pre></div> Err codemadness.org 70 i 7325 Err codemadness.org 70 i 7326 <p>I have been looking at test failures to see what needs fixing. Some Err codemadness.org 70 i 7327 reference images just need to be regenerated: there have been minor Err codemadness.org 70 i 7328 changes in font rendering that broke the reference tests. Some Err codemadness.org 70 i 7329 others have small differences in rendering gradients - not noticeable Err codemadness.org 70 i 7330 by eye, just by diff tools.</p> Err codemadness.org 70 i 7331 <p>But some tests, I have no idea what changed that made them break.</p> Err codemadness.org 70 i 7332 <p>Cairo's git repository is accessible through [cgit.freedesktop.org]. Err codemadness.org 70 i 7333 As far as I know there is no continuous integration infrastructure to Err codemadness.org 70 i 7334 ensure that tests keep passing.</p> Err codemadness.org 70 i 7335 <h1>Adding minimal continuous testing</h1> Err codemadness.org 70 i 7336 <p>I've set up a <a href="https://gitlab.com/federicomenaquintero/cairo/tree/105084-ft-font-face-init">Cairo repository at gitlab.com</a>. That branch Err codemadness.org 70 i 7337 already has a <a href="https://bugs.freedesktop.org/show_bug.cgi?id=105084">fix for an uninitialized-memory bug which leads to an Err codemadness.org 70 i 7338 invalid <code>free()</code></a>, and some regenerated test files.</p> Err codemadness.org 70 i 7339 <p>The repository <a href="https://gitlab.com/federicomenaquintero/cairo/blob/105084-ft-font-face-init/.gitlab-ci.yml">is configured to run a continuous integration Err codemadness.org 70 i 7340 pipeline</a> on every commit. The test artifacts can then be Err codemadness.org 70 i 7341 downloaded when the test suite fails. Right now it is only testing Err codemadness.org 70 i 7342 the image backend, for in-memory software rendering.</p> Err codemadness.org 70 i 7343 <h1>Initial bugs</h1> Err codemadness.org 70 i 7344 <p>I've started reporting <a href="https://gitlab.com/federicomenaquintero/cairo/issues">a few bugs</a> against that repository for Err codemadness.org 70 i 7345 tests that fail. These should really be in Cairo's Bugzilla, but for Err codemadness.org 70 i 7346 now Gitlab makes it much easier to include test images directly in the Err codemadness.org 70 i 7347 bug descriptions, so that they are easier to browse. Read on.</p> Err codemadness.org 70 i 7348 <h1>Would you like to help?</h1> Err codemadness.org 70 i 7349 <p>A lot of projects use Cairo. We owe it to ourselves to have a library Err codemadness.org 70 i 7350 with a test suite that doesn't break. Getting to that point requires Err codemadness.org 70 i 7351 several things:</p> Err codemadness.org 70 i 7352 <ul> Err codemadness.org 70 i 7353 <li>Fixing current failures in the image backend.</li> Err codemadness.org 70 i 7354 <li>Setting up the CI infrastructure to be able to test other backends.</li> Err codemadness.org 70 i 7355 <li>Fixing failures in the other backends.</li> Err codemadness.org 70 i 7356 </ul> Err codemadness.org 70 i 7357 <p>If you have experience with Cairo, please take a look at the <a href="https://gitlab.com/federicomenaquintero/cairo/issues">bugs</a>. Err codemadness.org 70 i 7358 You can see the <a href="https://gitlab.com/federicomenaquintero/cairo/blob/105084-ft-font-face-init/.gitlab-ci.yml">CI</a> configuration to see how to run the test Err codemadness.org 70 i 7359 suite in the same fashion on your machine.</p> Err codemadness.org 70 i 7360 <p>I think we can make use of modern infrastructure like gitlab and Err codemadness.org 70 i 7361 continuous integration to improve Cairo quickly. Currently it suffers Err codemadness.org 70 i 7362 from lack of attention and hostile tools. Help us out if you can!</p>Quick and dirty checklist to update syn 0.11.x to syn 0.122018-02-26T19:20:42-06:002018-02-26T19:20:42-06:00Federico Mena Quinterotag:people.gnome.org,2018-02-26:/~federico/blog/syn-012.html<p>Today I ported <a href="https://github.com/federicomenaquintero/gnome-class">gnome-class</a> from version 0.11 of the <a href="https://github.com/dtolnay/syn/">syn</a> crate to Err codemadness.org 70 i 7363 version 0.12. <code>syn</code> is a somewhat esoteric crate that you use to Err codemadness.org 70 i 7364 parse Rust code... from a stream of tokens... from within the Err codemadness.org 70 i 7365 implementation of a procedural macro. Gnome-class implements a Err codemadness.org 70 i 7366 mini-language inside your own Rust …</p><p>Today I ported <a href="https://github.com/federicomenaquintero/gnome-class">gnome-class</a> from version 0.11 of the <a href="https://github.com/dtolnay/syn/">syn</a> crate to Err codemadness.org 70 i 7367 version 0.12. <code>syn</code> is a somewhat esoteric crate that you use to Err codemadness.org 70 i 7368 parse Rust code... from a stream of tokens... from within the Err codemadness.org 70 i 7369 implementation of a procedural macro. Gnome-class implements a Err codemadness.org 70 i 7370 mini-language inside your own Rust code, and so it needs to parse Err codemadness.org 70 i 7371 Rust!</p> Err codemadness.org 70 i 7372 <p>The API of <code>syn</code> has changed <em>a lot</em>, which is kind of a pain in the Err codemadness.org 70 i 7373 ass — but the new API seems on the road to stabilization, and is nicer Err codemadness.org 70 i 7374 indeed.</p> Err codemadness.org 70 i 7375 <p>Here is a quick list of things I had to change in gnome-class to Err codemadness.org 70 i 7376 upgrade its version of <code>syn</code>.</p> Err codemadness.org 70 i 7377 <p>There is no <code>extern crate synom</code> anymore. You can use <code>syn::synom</code> now.</p> Err codemadness.org 70 i 7378 <div class="highlight"><pre><span></span><code><span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">synom</span><span class="p">;</span><span class="w"> </span>-&gt; <span class="nc">use</span><span class="w"> </span><span class="n">syn</span>::<span class="n">synom</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7379 </code></pre></div> Err codemadness.org 70 i 7380 Err codemadness.org 70 i 7381 <p><code>SynomBuffer</code> is now <code>TokenBuffer</code>:</p> Err codemadness.org 70 i 7382 <div class="highlight"><pre><span></span><code><span class="n">synom</span>::<span class="n">SynomBuffer</span><span class="w"> </span>-&gt; <span class="nc">syn</span>::<span class="n">buffer</span>:<span class="nc">TokenBuffer</span><span class="w"></span> Err codemadness.org 70 i 7383 </code></pre></div> Err codemadness.org 70 i 7384 Err codemadness.org 70 i 7385 <p><code>PResult</code>, the result of <code>Synom::parse()</code>, now has the tuple's Err codemadness.org 70 i 7386 arguments reversed:</p> Err codemadness.org 70 i 7387 <div class="highlight"><pre><span></span><code><span class="o">-</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">type</span> <span class="nc">PResult</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">O</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">(</span><span class="n">Cursor</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="n">O</span><span class="p">),</span><span class="w"> </span><span class="n">ParseError</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7388 <span class="o">+</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">type</span> <span class="nc">PResult</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">O</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">(</span><span class="n">O</span><span class="p">,</span><span class="w"> </span><span class="n">Cursor</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="o">&gt;</span><span class="p">),</span><span class="w"> </span><span class="n">ParseError</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7389 Err codemadness.org 70 i 7390 <span class="c1">// therefore:</span> Err codemadness.org 70 i 7391 Err codemadness.org 70 i 7392 <span class="k">impl</span><span class="w"> </span><span class="n">Synom</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">MyThing</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7393 Err codemadness.org 70 i 7394 <span class="kd">let</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MyThing</span>::<span class="n">parse</span><span class="p">(</span><span class="o">..</span><span class="p">.).</span><span class="n">unwrap</span><span class="p">().</span><span class="mi">1</span><span class="p">;</span><span class="w"> </span>-&gt; <span class="nc">let</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MyThing</span>::<span class="n">parse</span><span class="p">(</span><span class="o">..</span><span class="p">.).</span><span class="n">unwrap</span><span class="p">().</span><span class="mi">0</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7395 </code></pre></div> Err codemadness.org 70 i 7396 Err codemadness.org 70 i 7397 <p>The language tokens like <code>synom::tokens::Amp</code>, and keywords like Err codemadness.org 70 i 7398 <code>synom::tokens::Type</code>, are easier to use now. There is a <code>Token!</code> Err codemadness.org 70 i 7399 macro which you can use in type definitions, instead of having to Err codemadness.org 70 i 7400 remember the particular name of each token type:</p> Err codemadness.org 70 i 7401 <div class="highlight"><pre><span></span><code><span class="n">synom</span>::<span class="n">tokens</span>::<span class="n">Amp</span><span class="w"> </span>-&gt; <span class="nc">Token</span><span class="o">!</span><span class="p">(</span><span class="o">&amp;</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 7402 Err codemadness.org 70 i 7403 <span class="n">synom</span>::<span class="n">tokens</span>::<span class="n">For</span><span class="w"> </span>-&gt; <span class="nc">Token</span><span class="o">!</span><span class="p">(</span><span class="k">for</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 7404 </code></pre></div> Err codemadness.org 70 i 7405 Err codemadness.org 70 i 7406 <p>And for the corresponding values when matching:</p> Err codemadness.org 70 i 7407 <div class="highlight"><pre><span></span><code><span class="n">syn</span><span class="o">!</span><span class="p">(</span><span class="n">tokens</span>::<span class="n">Colon</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">punct</span><span class="o">!</span><span class="p">(</span>:<span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 7408 Err codemadness.org 70 i 7409 <span class="n">syn</span><span class="o">!</span><span class="p">(</span><span class="n">tokens</span>::<span class="n">Type</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">keyword</span><span class="o">!</span><span class="p">(</span><span class="k">type</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 7410 </code></pre></div> Err codemadness.org 70 i 7411 Err codemadness.org 70 i 7412 <p>And to instantiate them for quoting/spanning:</p> Err codemadness.org 70 i 7413 <div class="highlight"><pre><span></span><code><span class="o">-</span><span class="w"> </span><span class="n">tokens</span>::<span class="n">Comma</span>::<span class="n">default</span><span class="p">().</span><span class="n">to_tokens</span><span class="p">(</span><span class="n">tokens</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7414 <span class="o">+</span><span class="w"> </span><span class="n">Token</span><span class="o">!</span><span class="p">(,)([</span><span class="n">Span</span>::<span class="n">def_site</span><span class="p">()]).</span><span class="n">to_tokens</span><span class="p">(</span><span class="n">tokens</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7415 </code></pre></div> Err codemadness.org 70 i 7416 Err codemadness.org 70 i 7417 <p>(OK, that one wasn't nicer after all.)</p> Err codemadness.org 70 i 7418 <p>To the get string for an <code>Ident</code>:</p> Err codemadness.org 70 i 7419 <div class="highlight"><pre><span></span><code><span class="n">ident</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">as_str</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">ident</span><span class="p">.</span><span class="n">as_ref</span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 7420 </code></pre></div> Err codemadness.org 70 i 7421 Err codemadness.org 70 i 7422 <p>There is no <code>Delimited</code> anymore; instead there is a <code>Punctuated</code> Err codemadness.org 70 i 7423 struct. My diff has this:</p> Err codemadness.org 70 i 7424 <div class="highlight"><pre><span></span><code>- inputs: parens!(call!(Delimited::&lt;MyThing, tokens::Comma&gt;::parse_terminated)) &gt;&gt; Err codemadness.org 70 i 7425 + inputs: parens!(syn!(Punctuated&lt;MyThing, Token!(,)&gt;)) &gt;&gt; Err codemadness.org 70 i 7426 </code></pre></div> Err codemadness.org 70 i 7427 Err codemadness.org 70 i 7428 <p>There is no <code>syn::Mutability</code> anymore; now it's an <code>Option&lt;token&gt;</code>, so Err codemadness.org 70 i 7429 basically</p> Err codemadness.org 70 i 7430 <div class="highlight"><pre><span></span><code><span class="n">syn</span>::<span class="n">Mutability</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="n">Token</span><span class="o">!</span><span class="p">[</span><span class="k">mut</span><span class="p">]</span><span class="o">&gt;</span><span class="w"></span> Err codemadness.org 70 i 7431 </code></pre></div> Err codemadness.org 70 i 7432 Err codemadness.org 70 i 7433 <p>which I guess lets you refer to the span of the original <code>mut</code> token Err codemadness.org 70 i 7434 if you need.</p> Err codemadness.org 70 i 7435 <p>Some things changed names:</p> Err codemadness.org 70 i 7436 <div class="highlight"><pre><span></span><code><span class="n">TypeTup</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tys</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">}</span><span class="w"> </span>-&gt; <span class="nc">TypeTuple</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">elems</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7437 Err codemadness.org 70 i 7438 <span class="n">PatIdent</span><span class="w"> </span><span class="p">{</span><span class="w"> </span>-&gt; <span class="nc">PatIdent</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7439 <span class="w"> </span><span class="n">mode</span>: <span class="nc">BindingMode</span><span class="p">(</span><span class="n">Mutability</span><span class="p">)</span><span class="w"> </span><span class="n">by_ref</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="n">Token</span><span class="o">!</span><span class="p">(</span><span class="k">ref</span><span class="p">)</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7440 <span class="w"> </span><span class="n">mutability</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="n">Token</span><span class="o">!</span><span class="p">[</span><span class="k">mut</span><span class="p">]</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7441 <span class="w"> </span><span class="n">ident</span>: <span class="nc">Ident</span><span class="p">,</span><span class="w"> </span><span class="n">ident</span>: <span class="nc">Ident</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7442 <span class="w"> </span><span class="n">subpat</span>: <span class="o">..</span><span class="p">.,</span><span class="w"> </span><span class="n">subpat</span>: <span class="nb">Option</span><span class="o">&lt;</span><span class="p">(</span><span class="n">Token</span><span class="o">!</span><span class="p">[</span><span class="o">@</span><span class="p">],</span><span class="w"> </span><span class="nb">Box</span><span class="o">&lt;</span><span class="n">Pat</span><span class="o">&gt;</span><span class="p">)</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7443 <span class="w"> </span><span class="n">at_token</span>: <span class="o">..</span><span class="p">.,</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7444 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7445 Err codemadness.org 70 i 7446 <span class="n">TypeParen</span><span class="p">.</span><span class="n">ty</span><span class="w"> </span>-&gt; <span class="nc">TypeParen</span><span class="p">.</span><span class="n">elem</span><span class="w"> </span><span class="p">(</span><span class="n">and</span><span class="w"> </span><span class="n">others</span><span class="w"> </span><span class="n">like</span><span class="w"> </span><span class="n">this</span><span class="p">,</span><span class="w"> </span><span class="n">too</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 7447 </code></pre></div> Err codemadness.org 70 i 7448 Err codemadness.org 70 i 7449 <p>(I don't know everything that changed names; gnome-class doesn't use Err codemadness.org 70 i 7450 all the syn types yet; these are just the ones I've run into.)</p> Err codemadness.org 70 i 7451 <p>This new <code>syn</code> is much better at acknowledging the fine points of Err codemadness.org 70 i 7452 macro hygiene. The <a href="https://github.com/dtolnay/syn/tree/master/examples">examples directory</a> is particularly instructive; Err codemadness.org 70 i 7453 it shows how to properly span generated code vs. original code, so Err codemadness.org 70 i 7454 compiler error messages are nice. I <a href="https://github.com/rust-lang-nursery/rustc-guide/issues/15">need to write something about Err codemadness.org 70 i 7455 macro hygiene</a> at some point.</p>Librsvg's continuous integration pipeline2018-02-23T16:25:13-06:002018-02-23T16:25:13-06:00Federico Mena Quinterotag:people.gnome.org,2018-02-23:/~federico/blog/librsvg-ci-pipeline.html<p><a href="https://gitlab.gnome.org/alatiera">Jordan Petridis</a> has been kicking ass by overhauling Err codemadness.org 70 i 7456 librsvg's continous integration (CI) pipeline. Take a look at this Err codemadness.org 70 i 7457 beauty:</p> Err codemadness.org 70 i 7458 <p><img alt="Continuous integration pipeline" src="https://people.gnome.org/~federico/blog/images/librsvg-pipeline.png"></p> Err codemadness.org 70 i 7459 <p>On every push, we run the <strong>Test</strong> stage. This is a quick compilation Err codemadness.org 70 i 7460 on a Fedora container that runs "<code>make check</code>" and ensures that the Err codemadness.org 70 i 7461 test suite passes.</p> Err codemadness.org 70 i 7462 <p>We have a …</p><p><a href="https://gitlab.gnome.org/alatiera">Jordan Petridis</a> has been kicking ass by overhauling Err codemadness.org 70 i 7463 librsvg's continous integration (CI) pipeline. Take a look at this Err codemadness.org 70 i 7464 beauty:</p> Err codemadness.org 70 i 7465 <p><img alt="Continuous integration pipeline" src="https://people.gnome.org/~federico/blog/images/librsvg-pipeline.png"></p> Err codemadness.org 70 i 7466 <p>On every push, we run the <strong>Test</strong> stage. This is a quick compilation Err codemadness.org 70 i 7467 on a Fedora container that runs "<code>make check</code>" and ensures that the Err codemadness.org 70 i 7468 test suite passes.</p> Err codemadness.org 70 i 7469 <p>We have a <strong>Lint</strong> stage which can be run manually. This runs <code>cargo Err codemadness.org 70 i 7470 clippy</code> to get Rust lints (check the style of Rust idioms), and <code>cargo Err codemadness.org 70 i 7471 fmt</code> to check indentation and code style and such.</p> Err codemadness.org 70 i 7472 <p>We have a <strong>Distro_test</strong> stage which I think will be scheduled Err codemadness.org 70 i 7473 weekly, using Gitlab's <em>Schedules</em> feature, to check that the tests Err codemadness.org 70 i 7474 pass on three major Linux distros. Recently we had trouble with Err codemadness.org 70 i 7475 different rendering due to differences in Freetype versions, which Err codemadness.org 70 i 7476 broke the tests (<em>ahem, likely because </em><em>I</em><em> hadn't updated my Err codemadness.org 70 i 7477 Freetype in a while and distros were already using a newer one</em>); these Err codemadness.org 70 i 7478 distro tests are intended to catch that.</p> Err codemadness.org 70 i 7479 <p>Finally, we have a <strong>Rustc_test</strong> stage. The various crates that Err codemadness.org 70 i 7480 librsvg depends on have different minimum versions for the Rust Err codemadness.org 70 i 7481 compiler. These tests are intended to show when updating a dependency Err codemadness.org 70 i 7482 changes the minimum Rust version on which librsvg would compile. We Err codemadness.org 70 i 7483 don't have a policy yet for "how far from $newest" we should always Err codemadness.org 70 i 7484 work on, and it would be good to get input from distros on this. I Err codemadness.org 70 i 7485 think these Rust tests will be scheduled weekly as well.</p> Err codemadness.org 70 i 7486 <p>Jordan has been experimenting with the pipeline's stages and the Err codemadness.org 70 i 7487 distro-specific idiosyncrasies for each build. This pipeline depends Err codemadness.org 70 i 7488 on some <a href="https://gitlab.com/alatiera/librsvg-oci-images">custom-built container images</a> that already have Err codemadness.org 70 i 7489 librsvg's dependencies installed. These images are built weekly in Err codemadness.org 70 i 7490 <code>gitlab.com</code>, so every week <code>gitlab.gnome.org</code> gets fresh images for Err codemadness.org 70 i 7491 librsvg's CI pipelines. Once image registries are enabled in Err codemadness.org 70 i 7492 <code>gitlab.gnome.org</code>, we should be able to regenerate the container Err codemadness.org 70 i 7493 images locally without depending on an external service.</p> Err codemadness.org 70 i 7494 <p>With the pre-built images, and caching of Rust artifacts, Jordan was Err codemadness.org 70 i 7495 able to <strong>reduce the time for the "test on every commit" builds</strong> from Err codemadness.org 70 i 7496 around 20 minutes, to little under 4 minutes in the current Err codemadness.org 70 i 7497 iteration. This will get even faster if the builds start using ccache Err codemadness.org 70 i 7498 and parallel builds from GNU make.</p> Err codemadness.org 70 i 7499 <p>Currently we have a problem in that <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/178">tests are failing on 32-bit Err codemadness.org 70 i 7500 builds</a>, and haven't had a chance to investigate the root Err codemadness.org 70 i 7501 cause. Hopefully we can add 32-bit jobs to the CI pipeline to catch Err codemadness.org 70 i 7502 this breakage as soon as possible.</p> Err codemadness.org 70 i 7503 <p>Having all these container images built for the CI infrastructure also Err codemadness.org 70 i 7504 means that it will be easy for people to <strong>set up a development Err codemadness.org 70 i 7505 environment</strong> for librsvg, even though we have <a href="https://gitlab.gnome.org/GNOME/librsvg/blob/b75c20fb3137af9610ff48d0d31ab45e008893ff/COMPILING.md#installing-dependencies-for-building">better instructions Err codemadness.org 70 i 7506 now</a> thanks to Jordan. I haven't investigated setting up a Err codemadness.org 70 i 7507 Flatpak-based environment; this would be nice to have as well.</p>RFC: Integrating rsvg-rs into librsvg2018-02-22T09:57:52-06:002018-02-22T09:57:52-06:00Federico Mena Quinterotag:people.gnome.org,2018-02-22:/~federico/blog/rfc-integrating-rsvg-rs-into-librsvg.html<p>I have started an <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/207">RFC to integrate rsvg-rs into librsvg</a>. Err codemadness.org 70 i 7508 <code>rsvg-rs</code> is the Rust binding to librsvg. Like the <a href="http://gtk-rs.org/">gtk-rs</a> bindings, Err codemadness.org 70 i 7509 it gets generated from a pre-built <a href="https://people.gnome.org/~federico/blog/magic-of-gobject-introspection.html">GIR</a> file.</p> Err codemadness.org 70 i 7510 <p>It would be nice for librsvg to provide the Rust binding by itself, so Err codemadness.org 70 i 7511 that librsvg's own internal tools can be …</p><p>I have started an <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/207">RFC to integrate rsvg-rs into librsvg</a>. Err codemadness.org 70 i 7512 <code>rsvg-rs</code> is the Rust binding to librsvg. Like the <a href="http://gtk-rs.org/">gtk-rs</a> bindings, Err codemadness.org 70 i 7513 it gets generated from a pre-built <a href="https://people.gnome.org/~federico/blog/magic-of-gobject-introspection.html">GIR</a> file.</p> Err codemadness.org 70 i 7514 <p>It would be nice for librsvg to provide the Rust binding by itself, so Err codemadness.org 70 i 7515 that librsvg's own internal tools can be implemented in Rust — Err codemadness.org 70 i 7516 currently all the tests are done in C, as are the <code>rsvg-convert(1)</code> and Err codemadness.org 70 i 7517 <code>rsvg-view-3(1)</code> programs.</p> Err codemadness.org 70 i 7518 <p>There are some implications for how <code>rsvg-rs</code> would get built then. Err codemadness.org 70 i 7519 For librsvg's internal consumption, the binding can be built from the Err codemadness.org 70 i 7520 <code>Rsvg-2.0.gir</code> file that gets built out of the main <code>librsvg.so</code>. But Err codemadness.org 70 i 7521 for public consumption of <code>rsvg-rs</code>, when it is being used as a normal Err codemadness.org 70 i 7522 crate and built by Cargo, that <code>Rsvg-2.0.gir</code> needs to be already Err codemadness.org 70 i 7523 built and available: it wouldn't be appropriate for Cargo to build Err codemadness.org 70 i 7524 librsvg and the <code>.gir</code> file itself.</p> Err codemadness.org 70 i 7525 <p>If this sort of thing interests you, <a href="https://gitlab.gnome.org/GNOME/librsvg/issues/207">take a look at the RFC</a>!</p>Rust things I miss in C2018-02-18T21:26:04-06:002018-02-23T18:28:23-06:00Federico Mena Quinterotag:people.gnome.org,2018-02-18:/~federico/blog/rust-things-i-miss-in-c.html<p>Librsvg feels like it is reaching a tipping point, where suddenly it Err codemadness.org 70 i 7526 seems like it would be easier to just port some major parts from C to Err codemadness.org 70 i 7527 Rust than to just add accessors for them. Also, more and more of the Err codemadness.org 70 i 7528 meat of the library is in Rust now.</p> Err codemadness.org 70 i 7529 <p>I'm …</p><p>Librsvg feels like it is reaching a tipping point, where suddenly it Err codemadness.org 70 i 7530 seems like it would be easier to just port some major parts from C to Err codemadness.org 70 i 7531 Rust than to just add accessors for them. Also, more and more of the Err codemadness.org 70 i 7532 meat of the library is in Rust now.</p> Err codemadness.org 70 i 7533 <p>I'm switching back and forth a lot between C and Rust these days, and Err codemadness.org 70 i 7534 C feels very, very primitive these days.</p> Err codemadness.org 70 i 7535 <h1>A sort of elegy to C</h1> Err codemadness.org 70 i 7536 <p>I fell in love with the C language about 24 years ago. I learned the Err codemadness.org 70 i 7537 basics of it by reading a Spanish translation of <a href="https://en.wikipedia.org/wiki/The_C_Programming_Language">The C Programming Err codemadness.org 70 i 7538 Language by K&amp;R</a> second edition. I had been using Turbo Pascal Err codemadness.org 70 i 7539 before in a reasonably low-level fashion, with pointers and manual Err codemadness.org 70 i 7540 memory allocation, and C felt refreshing and empowering.</p> Err codemadness.org 70 i 7541 <p>K&amp;R is a great book for its <em>style of writing</em> and its conciseness of Err codemadness.org 70 i 7542 programming. This little book even taught you how to implement a Err codemadness.org 70 i 7543 simple <code>malloc()</code>/<code>free()</code>, which was completely enlightening. Even Err codemadness.org 70 i 7544 low-level constructs that seemed part of the language could be Err codemadness.org 70 i 7545 implemented in the language itself!</p> Err codemadness.org 70 i 7546 <p>I got good at C over the following years. It is a small language, Err codemadness.org 70 i 7547 with a small standard library. It was probably the perfect language Err codemadness.org 70 i 7548 to implement Unix kernels in 20,000 lines of code or so.</p> Err codemadness.org 70 i 7549 <p>The GIMP and GTK+ taught me how to do fancy object orientation in C. Err codemadness.org 70 i 7550 GNOME taught me how to maintain large-scale software in C. 20,000 Err codemadness.org 70 i 7551 lines of C code started to seem like a project one could more or less Err codemadness.org 70 i 7552 fully understand in a few weeks.</p> Err codemadness.org 70 i 7553 <p>But our code bases are not that small anymore. Our software now has Err codemadness.org 70 i 7554 <em>huge</em> expectations on the features that are available in the Err codemadness.org 70 i 7555 language's standard library.</p> Err codemadness.org 70 i 7556 <h2>Some good experiences with C</h2> Err codemadness.org 70 i 7557 <p>Reading the POV-Ray code source code for the first time and learning Err codemadness.org 70 i 7558 how to do object orientation and inheritance in C.</p> Err codemadness.org 70 i 7559 <p>Reading the GTK+ source code and learning a C style that was legible, Err codemadness.org 70 i 7560 maintainable, and clean.</p> Err codemadness.org 70 i 7561 <p>Reading SIOD's source code, then the early Guile sources, and seeing Err codemadness.org 70 i 7562 how a Scheme interpreter can be written in C.</p> Err codemadness.org 70 i 7563 <p>Writing the initial versions of Eye of Gnome and fine-tuning the Err codemadness.org 70 i 7564 microtile rendering.</p> Err codemadness.org 70 i 7565 <h2>Some bad experiences with C</h2> Err codemadness.org 70 i 7566 <p>In the Evolution team, when everything was crashing. We had to buy a Err codemadness.org 70 i 7567 Solaris machine just to be able to buy Purify; there was no Valgrind Err codemadness.org 70 i 7568 back then.</p> Err codemadness.org 70 i 7569 <p>Debugging gnome-vfs threading deadlocks.</p> Err codemadness.org 70 i 7570 <p>Debugging Mesa and getting nowhere.</p> Err codemadness.org 70 i 7571 <p>Taking over the intial versions of Nautilus-share and seeing that it Err codemadness.org 70 i 7572 never <code>free()</code>d anything.</p> Err codemadness.org 70 i 7573 <p>Trying to refactor code where I had no idea about the memory Err codemadness.org 70 i 7574 management strategy.</p> Err codemadness.org 70 i 7575 <p>Trying to turn code into a library when it is full of global variables Err codemadness.org 70 i 7576 and no functions are <code>static</code>.</p> Err codemadness.org 70 i 7577 <p>But anyway — let's get on with things in Rust I miss in C.</p> Err codemadness.org 70 i 7578 <h1>Automatic resource management</h1> Err codemadness.org 70 i 7579 <p>One of the first blog posts I read about Rust was "<a href="http://blog.skylight.io/rust-means-never-having-to-close-a-socket/">Rust means never Err codemadness.org 70 i 7580 having to close a socket</a>". Rust borrows C++'s ideas about Err codemadness.org 70 i 7581 <a href="http://wiki.c2.com/?ResourceAcquisitionIsInitialization">Resource Acquisition Is Initialization (RAII)</a>, Smart Pointers, Err codemadness.org 70 i 7582 adds in the single-ownership principle for values, and gives you Err codemadness.org 70 i 7583 automatic, deterministic resource management in a very neat package.</p> Err codemadness.org 70 i 7584 <ul> Err codemadness.org 70 i 7585 <li> Err codemadness.org 70 i 7586 <p>Automatic: you don't <code>free()</code> by hand. Memory gets deallocated, Err codemadness.org 70 i 7587 files get closed, mutexes get unlocked when they go out of scope. Err codemadness.org 70 i 7588 If you are wrapping an external resource, you just implement the Err codemadness.org 70 i 7589 <a href="https://doc.rust-lang.org/book/second-edition/ch15-03-drop.html">Drop</a> trait and that's basically it. The wrapped resource feels Err codemadness.org 70 i 7590 like part of the language since you don't have to babysit its Err codemadness.org 70 i 7591 lifetime by hand.</p> Err codemadness.org 70 i 7592 </li> Err codemadness.org 70 i 7593 <li> Err codemadness.org 70 i 7594 <p>Deterministic: resources get created (memory allocated, initialized, Err codemadness.org 70 i 7595 files opened, etc.), and they get destroyed when they go out of Err codemadness.org 70 i 7596 scope. There is no garbage collection: things really get terminated Err codemadness.org 70 i 7597 when you close a brace. You start to see your program's data Err codemadness.org 70 i 7598 lifetimes as a tree of function calls.</p> Err codemadness.org 70 i 7599 </li> Err codemadness.org 70 i 7600 </ul> Err codemadness.org 70 i 7601 <p>After forgetting to free/close/destroy C objects all the time, or Err codemadness.org 70 i 7602 worse, figuring out where code that I didn't write forgot to do those Err codemadness.org 70 i 7603 things (or did them <em>twice</em>, incorrectly)... I don't want to do it Err codemadness.org 70 i 7604 again.</p> Err codemadness.org 70 i 7605 <h1>Generics</h1> Err codemadness.org 70 i 7606 <p><code>Vec&lt;T&gt;</code> really is a vector of whose elements are the size of <code>T</code>. Err codemadness.org 70 i 7607 It's not an array of pointers to individually allocated objects. It Err codemadness.org 70 i 7608 gets compiled <em>specifically</em> to code that can only handle objects of Err codemadness.org 70 i 7609 type <code>T</code>.</p> Err codemadness.org 70 i 7610 <p>After writing many janky macros in C to do similar things... I don't Err codemadness.org 70 i 7611 want to do it again.</p> Err codemadness.org 70 i 7612 <h1>Traits are not just interfaces</h1> Err codemadness.org 70 i 7613 <p><a href="https://doc.rust-lang.org/book/second-edition/ch17-00-oop.html">Rust is not a Java-like object-oriented language</a>. Instead it Err codemadness.org 70 i 7614 has traits, which at first seem like Java interfaces — an easy way to Err codemadness.org 70 i 7615 do dynamic dispatch, so that if an object implements <code>Drawable</code> then Err codemadness.org 70 i 7616 you can assume it has a <code>draw()</code> method.</p> Err codemadness.org 70 i 7617 <p>However, traits are more powerful than that.</p> Err codemadness.org 70 i 7618 <h2>Associated types</h2> Err codemadness.org 70 i 7619 <p><a href="https://doc.rust-lang.org/book/second-edition/ch19-03-advanced-traits.html">Traits can have associated types</a>. As an example, Rust Err codemadness.org 70 i 7620 provies the <code>Iterator</code> trait which you can implement:</p> Err codemadness.org 70 i 7621 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="nb">Iterator</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7622 <span class="w"> </span><span class="k">type</span> <span class="nc">Item</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7623 <span class="w"> </span><span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="bp">Self</span>::<span class="n">Item</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7624 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7625 </code></pre></div> Err codemadness.org 70 i 7626 Err codemadness.org 70 i 7627 <p>This means that whenever you implement <code>Iterator</code> for some iterable Err codemadness.org 70 i 7628 object, you also have to specify an <code>Item</code> type for the things that Err codemadness.org 70 i 7629 will be produced. If you call <code>next()</code> and there are more elements, Err codemadness.org 70 i 7630 you'll get back a <code>Some(YourElementType)</code>. When your iterator runs Err codemadness.org 70 i 7631 out of items, it will return <code>None</code>.</p> Err codemadness.org 70 i 7632 <p>Associated types can refer to <em>other</em> traits.</p> Err codemadness.org 70 i 7633 <p>For example, in Rust, you can use <code>for</code> loops on anything that Err codemadness.org 70 i 7634 implements the <code>IntoIterator</code> trait:</p> Err codemadness.org 70 i 7635 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="nb">IntoIterator</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7636 <span class="w"> </span><span class="sd">/// The type of the elements being iterated over.</span> Err codemadness.org 70 i 7637 <span class="w"> </span><span class="k">type</span> <span class="nc">Item</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7638 Err codemadness.org 70 i 7639 <span class="w"> </span><span class="sd">/// Which kind of iterator are we turning this into?</span> Err codemadness.org 70 i 7640 <span class="w"> </span><span class="k">type</span> <span class="nc">IntoIter</span>: <span class="nb">Iterator</span><span class="o">&lt;</span><span class="n">Item</span><span class="o">=</span><span class="bp">Self</span>::<span class="n">Item</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7641 Err codemadness.org 70 i 7642 <span class="w"> </span><span class="k">fn</span> <span class="nf">into_iter</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span>::<span class="n">IntoIter</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7643 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7644 </code></pre></div> Err codemadness.org 70 i 7645 Err codemadness.org 70 i 7646 <p>When implementing this trait, you must provide both the type of the Err codemadness.org 70 i 7647 <code>Item</code> which your iterator will produce, and <code>IntoIter</code>, the actual Err codemadness.org 70 i 7648 type that implements <code>Iterator</code> and that holds your iterator's state.</p> Err codemadness.org 70 i 7649 <p>This way you can build webs of types that refer to each other. You Err codemadness.org 70 i 7650 can have a trait that says, "I can do foo and bar, but only if you Err codemadness.org 70 i 7651 give me a type that can do this and that".</p> Err codemadness.org 70 i 7652 <h1>Slices</h1> Err codemadness.org 70 i 7653 <p>I already posted about <a href="https://people.gnome.org/~federico/blog/rant-on-string-slices.html">the lack of string slices in C</a> and Err codemadness.org 70 i 7654 how this is a pain in the ass once you get used to having them.</p> Err codemadness.org 70 i 7655 <h1>Modern tooling for dependency management</h1> Err codemadness.org 70 i 7656 <p>Instead of </p> Err codemadness.org 70 i 7657 <ul> Err codemadness.org 70 i 7658 <li>Having to invoke <code>pkg-config</code> by hand or with Autotools macros</li> Err codemadness.org 70 i 7659 <li>Wrangling include paths for header files...</li> Err codemadness.org 70 i 7660 <li>... and library files.</li> Err codemadness.org 70 i 7661 <li>And basically depending on the user to ensure that the correct Err codemadness.org 70 i 7662 versions of libraries are installed,</li> Err codemadness.org 70 i 7663 </ul> Err codemadness.org 70 i 7664 <p>You write a <code>Cargo.toml</code> file which lists the names and versions of Err codemadness.org 70 i 7665 your dependencies. These get downloaded from a well-known location, Err codemadness.org 70 i 7666 or from elsewhere if you specify.</p> Err codemadness.org 70 i 7667 <p>You don't have to fight dependencies. It just works when you <code>cargo build</code>.</p> Err codemadness.org 70 i 7668 <h1>Tests</h1> Err codemadness.org 70 i 7669 <p>C makes it very hard to have unit tests for several reasons:</p> Err codemadness.org 70 i 7670 <ul> Err codemadness.org 70 i 7671 <li> Err codemadness.org 70 i 7672 <p>Internal functions are often <code>static</code>. This means they can't be Err codemadness.org 70 i 7673 called outside of the source file that defined them. A test program Err codemadness.org 70 i 7674 either has to <code>#include</code> the source file where the static functions Err codemadness.org 70 i 7675 live, or use <code>#ifdef</code>s to remove the <code>static</code>s only during testing.</p> Err codemadness.org 70 i 7676 </li> Err codemadness.org 70 i 7677 <li> Err codemadness.org 70 i 7678 <p>You have to write Makefile-related hackery to link the test program Err codemadness.org 70 i 7679 to only part of your code's dependencies, or to only part of the Err codemadness.org 70 i 7680 rest of your code.</p> Err codemadness.org 70 i 7681 </li> Err codemadness.org 70 i 7682 <li> Err codemadness.org 70 i 7683 <p>You have to pick a testing framework. You have to register tests Err codemadness.org 70 i 7684 against the testing framework. You have to <em>learn</em> the testing Err codemadness.org 70 i 7685 framework.</p> Err codemadness.org 70 i 7686 </li> Err codemadness.org 70 i 7687 </ul> Err codemadness.org 70 i 7688 <p>In Rust you write</p> Err codemadness.org 70 i 7689 <div class="highlight"><pre><span></span><code><span class="cp">#[test]</span><span class="w"></span> Err codemadness.org 70 i 7690 <span class="k">fn</span> <span class="nf">test_that_foo_works</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7691 <span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="n">foo</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">expected_result</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7692 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7693 </code></pre></div> Err codemadness.org 70 i 7694 Err codemadness.org 70 i 7695 <p>anywhere in your program or library, and when you type <code>cargo test</code>, Err codemadness.org 70 i 7696 IT JUST FUCKING WORKS. That code only gets linked into the Err codemadness.org 70 i 7697 test binary. You don't have to compile anything twice by hand, or Err codemadness.org 70 i 7698 write Makefile hackery, or figure out how to extract internal Err codemadness.org 70 i 7699 functions for testing.</p> Err codemadness.org 70 i 7700 <p>This is a very killer feature for me.</p> Err codemadness.org 70 i 7701 <h1>Documentation, with tests</h1> Err codemadness.org 70 i 7702 <p>Rust generates documentation from comments in Markdown syntax. Code Err codemadness.org 70 i 7703 in the docs <em>gets run as tests</em>. You can illustrate how a function is Err codemadness.org 70 i 7704 used <em>and</em> test it at the same time:</p> Err codemadness.org 70 i 7705 <div class="highlight"><pre><span></span><code><span class="sd">/// Multiples the specified number by two</span> Err codemadness.org 70 i 7706 <span class="sd">///</span> Err codemadness.org 70 i 7707 <span class="sd">/// ```</span> Err codemadness.org 70 i 7708 <span class="sd">/// assert_eq!(multiply_by_two(5), 10);</span> Err codemadness.org 70 i 7709 <span class="sd">/// ```</span> Err codemadness.org 70 i 7710 <span class="k">fn</span> <span class="nf">multiply_by_two</span><span class="p">(</span><span class="n">x</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7711 <span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">2</span><span class="w"></span> Err codemadness.org 70 i 7712 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7713 </code></pre></div> Err codemadness.org 70 i 7714 Err codemadness.org 70 i 7715 <p>Your example code <em>gets run as tests</em> to ensure that your Err codemadness.org 70 i 7716 documentation stays up to date with the actual code.</p> Err codemadness.org 70 i 7717 <p><strong>Update 2018/Feb/23:</strong> QuietMisdreavus has posted <a href="https://quietmisdreavus.net/code/2018/02/23/how-the-doctests-get-made/">how rustdoc turns Err codemadness.org 70 i 7718 doctests into runnable code Err codemadness.org 70 i 7719 internally</a>. Err codemadness.org 70 i 7720 This is high-grade magic and thoroughly interesting.</p> Err codemadness.org 70 i 7721 <h1>Hygienic macros</h1> Err codemadness.org 70 i 7722 <p>Rust has hygienic macros that avoid all of C's problems with things in Err codemadness.org 70 i 7723 macros that inadvertently shadow identifiers in the code. You don't Err codemadness.org 70 i 7724 need to write macros where every symbol has to be in parentheses for Err codemadness.org 70 i 7725 <code>max(5 + 3, 4)</code> to work correctly.</p> Err codemadness.org 70 i 7726 <h1>No automatic coercions</h1> Err codemadness.org 70 i 7727 <p>All the bugs in C that result from inadvertently converting an <code>int</code> Err codemadness.org 70 i 7728 to a <code>short</code> or <code>char</code> or whatever — Rust doesn't do them. You have Err codemadness.org 70 i 7729 to explicitly convert.</p> Err codemadness.org 70 i 7730 <h1>No integer overflow</h1> Err codemadness.org 70 i 7731 <p>Enough said.</p> Err codemadness.org 70 i 7732 <h1>Generally, no undefined behavior in safe Rust</h1> Err codemadness.org 70 i 7733 <p>In Rust, it is considered a bug in the language if something written Err codemadness.org 70 i 7734 in "safe Rust" (what you would be allowed to write outside <code>unsafe {}</code> Err codemadness.org 70 i 7735 blocks) results in undefined behavior. You can shift-right a negative Err codemadness.org 70 i 7736 integer and it will do exactly what you expect.</p> Err codemadness.org 70 i 7737 <h1>Pattern matching</h1> Err codemadness.org 70 i 7738 <p>You know how <code>gcc</code> warns you if you <code>switch()</code> on an enum but don't Err codemadness.org 70 i 7739 handle all values? That's like a little baby.</p> Err codemadness.org 70 i 7740 <p>Rust has <a href="https://doc.rust-lang.org/book/second-edition/ch18-03-pattern-syntax.html">pattern matching</a> in various places. It can do that Err codemadness.org 70 i 7741 trick for enums inside a <code>match()</code> expression. It can do Err codemadness.org 70 i 7742 destructuring so you can return multiple values from a function:</p> Err codemadness.org 70 i 7743 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="kt">f64</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7744 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">sin_cos</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="p">(</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="kt">f64</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7745 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7746 Err codemadness.org 70 i 7747 <span class="kd">let</span><span class="w"> </span><span class="n">angle</span>: <span class="kt">f64</span> <span class="o">=</span><span class="w"> </span><span class="mf">42.0</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7748 <span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="n">sin_angle</span><span class="p">,</span><span class="w"> </span><span class="n">cos_angle</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">angle</span><span class="p">.</span><span class="n">sin_cos</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 7749 </code></pre></div> Err codemadness.org 70 i 7750 Err codemadness.org 70 i 7751 <p>You can <code>match()</code> on strings. YOU CAN MATCH ON FUCKING STRINGS.</p> Err codemadness.org 70 i 7752 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;green&quot;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7753 Err codemadness.org 70 i 7754 <span class="k">match</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7755 <span class="w"> </span><span class="s">&quot;red&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&quot;it&#39;s red&quot;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 7756 <span class="w"> </span><span class="s">&quot;green&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&quot;it&#39;s green&quot;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 7757 <span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&quot;it&#39;s something else&quot;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 7758 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7759 </code></pre></div> Err codemadness.org 70 i 7760 Err codemadness.org 70 i 7761 <p>You know how this is illegible?</p> Err codemadness.org 70 i 7762 <div class="highlight"><pre><span></span><code>my_func(true, false, false) Err codemadness.org 70 i 7763 </code></pre></div> Err codemadness.org 70 i 7764 Err codemadness.org 70 i 7765 <p>How about this instead, with pattern matching on function arguments:</p> Err codemadness.org 70 i 7766 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Fubarize</span><span class="p">(</span><span class="k">pub</span><span class="w"> </span><span class="kt">bool</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7767 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Frobnify</span><span class="p">(</span><span class="k">pub</span><span class="w"> </span><span class="kt">bool</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7768 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Bazificate</span><span class="p">(</span><span class="k">pub</span><span class="w"> </span><span class="kt">bool</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7769 Err codemadness.org 70 i 7770 <span class="k">fn</span> <span class="nf">my_func</span><span class="p">(</span><span class="n">Fubarize</span><span class="p">(</span><span class="n">fub</span><span class="p">)</span>: <span class="nc">Fubarize</span><span class="p">,</span><span class="w"> </span> Err codemadness.org 70 i 7771 <span class="w"> </span><span class="n">Frobnify</span><span class="p">(</span><span class="n">frob</span><span class="p">)</span>: <span class="nc">Frobnify</span><span class="p">,</span><span class="w"> </span> Err codemadness.org 70 i 7772 <span class="w"> </span><span class="n">Bazificate</span><span class="p">(</span><span class="n">baz</span><span class="p">)</span>: <span class="nc">Bazificate</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7773 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">fub</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7774 <span class="w"> </span><span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 7775 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7776 Err codemadness.org 70 i 7777 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">frob</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7778 <span class="w"> </span><span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 7779 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7780 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7781 Err codemadness.org 70 i 7782 <span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 7783 Err codemadness.org 70 i 7784 <span class="n">my_func</span><span class="p">(</span><span class="n">Fubarize</span><span class="p">(</span><span class="kc">true</span><span class="p">),</span><span class="w"> </span><span class="n">Frobnify</span><span class="p">(</span><span class="kc">false</span><span class="p">),</span><span class="w"> </span><span class="n">Bazificate</span><span class="p">(</span><span class="kc">true</span><span class="p">));</span><span class="w"></span> Err codemadness.org 70 i 7785 </code></pre></div> Err codemadness.org 70 i 7786 Err codemadness.org 70 i 7787 <h1>Standard, useful error handling</h1> Err codemadness.org 70 i 7788 <p>I've talked at length about this. No more returning a boolean with no Err codemadness.org 70 i 7789 extra explanation for an error, no ignoring errors inadvertently, no Err codemadness.org 70 i 7790 exception handling with nonlocal jumps.</p> Err codemadness.org 70 i 7791 <h1>#[derive(Debug)]</h1> Err codemadness.org 70 i 7792 <p>If you write a new type (say, a struct with a ton of fields), you can Err codemadness.org 70 i 7793 <code>#[derive(Debug)]</code> and Rust will know how to automatically print that Err codemadness.org 70 i 7794 type's contents for debug output. You no longer have to write a Err codemadness.org 70 i 7795 special function that you must call in gdb by hand just to examine a Err codemadness.org 70 i 7796 custom type.</p> Err codemadness.org 70 i 7797 <h1>Closures</h1> Err codemadness.org 70 i 7798 <p>No more passing function pointers and a <code>user_data</code> by hand.</p> Err codemadness.org 70 i 7799 <h1>Conclusion</h1> Err codemadness.org 70 i 7800 <p>I haven't done the "<a href="https://doc.rust-lang.org/book/second-edition/ch16-00-concurrency.html">fearless concurrency</a>" bit yet, where the Err codemadness.org 70 i 7801 compiler is able to prevent data races in threaded code. I imagine it Err codemadness.org 70 i 7802 being a game-changer for people who write concurrent code on an Err codemadness.org 70 i 7803 everyday basis.</p> Err codemadness.org 70 i 7804 <p>C is an old language with primitive constructs and primitive tooling. Err codemadness.org 70 i 7805 It was a good language for small uniprocessor Unix kernels that ran in Err codemadness.org 70 i 7806 trusted, academic environments. It's no longer a good language for Err codemadness.org 70 i 7807 the software of today.</p> Err codemadness.org 70 i 7808 <p>Rust is not easy to learn, but I think it is completely worth it. Err codemadness.org 70 i 7809 It's hard because it demands a lot from your understanding of the code Err codemadness.org 70 i 7810 you want to write. I think it's one of those languages that make you Err codemadness.org 70 i 7811 a better programmer and that let you tackle more ambitious problems.</p>Writing a command-line program in Rust2018-02-03T11:41:20-06:002018-02-03T11:41:20-06:00Federico Mena Quinterotag:people.gnome.org,2018-02-03:/~federico/blog/writing-a-command-line-program-in-rust.html<p>As a library writer, it feels a bit strange, but refreshing, to write Err codemadness.org 70 i 7812 a program that actually has a <code>main()</code> function.</p> Err codemadness.org 70 i 7813 <p>My experience with Rust so far has been threefold:</p> Err codemadness.org 70 i 7814 <ul> Err codemadness.org 70 i 7815 <li> Err codemadness.org 70 i 7816 <p>Porting chunks of C to Rust for librsvg - this is all work on Err codemadness.org 70 i 7817 librsvg's internals and no users are exposed …</p></li></ul><p>As a library writer, it feels a bit strange, but refreshing, to write Err codemadness.org 70 i 7818 a program that actually has a <code>main()</code> function.</p> Err codemadness.org 70 i 7819 <p>My experience with Rust so far has been threefold:</p> Err codemadness.org 70 i 7820 <ul> Err codemadness.org 70 i 7821 <li> Err codemadness.org 70 i 7822 <p>Porting chunks of C to Rust for librsvg - this is all work on Err codemadness.org 70 i 7823 librsvg's internals and no users are exposed to it directly.</p> Err codemadness.org 70 i 7824 </li> Err codemadness.org 70 i 7825 <li> Err codemadness.org 70 i 7826 <p>Working on <a href="https://github.com/nikomatsakis/gnome-class/">gnome-class</a>, the procedural macro ("a little compiler") Err codemadness.org 70 i 7827 to generate GObject boilerplate from Rust. This feels like working Err codemadness.org 70 i 7828 on the edge of the exotic; it is something that runs <em>in</em> the Rust Err codemadness.org 70 i 7829 compiler and spits code on behalf of the programmer.</p> Err codemadness.org 70 i 7830 </li> Err codemadness.org 70 i 7831 <li> Err codemadness.org 70 i 7832 <p>A few patches to the <a href="http://gtk-rs.org">gtk-rs</a> ecosystem. Again, work on the Err codemadness.org 70 i 7833 internals, or something that feels library-like.</p> Err codemadness.org 70 i 7834 </li> Err codemadness.org 70 i 7835 </ul> Err codemadness.org 70 i 7836 <p>But other than toy programs to test things, I haven't written a Err codemadness.org 70 i 7837 stand-alone tool until <a href="https://people.gnome.org/~federico/blog/rsvg-bench.html">rsvg-bench</a>. It's quite a thrill to be able Err codemadness.org 70 i 7838 to just <em>run the thing</em> instead of waiting for other people to write Err codemadness.org 70 i 7839 code to use it!</p> Err codemadness.org 70 i 7840 <h1>Parsing command-line arguments</h1> Err codemadness.org 70 i 7841 <p>There are quite a few Rust crates ("libraries") to parse command-line Err codemadness.org 70 i 7842 arguments. I read about <a href="https://docs.rs/structopt-derive/0.1.5/structopt_derive/">structopt</a> via <a href="http://robert.ocallahan.org/2017/11/in-praise-of-rusts-structopt-for.html">Robert O'Callahan's Err codemadness.org 70 i 7843 blog</a>; structopt lets you define a <code>struct</code> to hold the values of Err codemadness.org 70 i 7844 your command-line options, and then you annotate the fields in that Err codemadness.org 70 i 7845 <code>struct</code> to indicate how they should be parsed from the command line. Err codemadness.org 70 i 7846 It works via Rust's procedural macros. Internally it generates stuff Err codemadness.org 70 i 7847 for the <a href="https://docs.rs/clap/2.29.2/clap/">clap</a> crate, a well-established mechanism for dealing with Err codemadness.org 70 i 7848 command-line options.</p> Err codemadness.org 70 i 7849 <p>And it is quite pleasant! This is basically all I needed to do:</p> Err codemadness.org 70 i 7850 <div class="highlight"><pre><span></span><code><span class="cp">#[derive(StructOpt, Debug)]</span><span class="w"></span> Err codemadness.org 70 i 7851 <span class="cp">#[structopt(name = </span><span class="s">&quot;rsvg-bench&quot;</span><span class="cp">, about = </span><span class="s">&quot;Benchmarking utility for librsvg.&quot;</span><span class="cp">)]</span><span class="w"></span> Err codemadness.org 70 i 7852 <span class="k">struct</span> <span class="nc">Opt</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7853 <span class="w"> </span><span class="cp">#[structopt(short = </span><span class="s">&quot;s&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7854 <span class="cp"> long = </span><span class="s">&quot;sleep&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7855 <span class="cp"> help = </span><span class="s">&quot;Number of seconds to sleep before starting to process SVGs&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7856 <span class="cp"> default_value = </span><span class="s">&quot;0&quot;</span><span class="cp">)]</span><span class="w"></span> Err codemadness.org 70 i 7857 <span class="w"> </span><span class="n">sleep_secs</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7858 Err codemadness.org 70 i 7859 <span class="w"> </span><span class="cp">#[structopt(short = </span><span class="s">&quot;p&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7860 <span class="cp"> long = </span><span class="s">&quot;num-parse&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7861 <span class="cp"> help = </span><span class="s">&quot;Number of times to parse each file&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7862 <span class="cp"> default_value = </span><span class="s">&quot;100&quot;</span><span class="cp">)]</span><span class="w"></span> Err codemadness.org 70 i 7863 <span class="w"> </span><span class="n">num_parse</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7864 Err codemadness.org 70 i 7865 <span class="w"> </span><span class="cp">#[structopt(short = </span><span class="s">&quot;r&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7866 <span class="cp"> long = </span><span class="s">&quot;num-render&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7867 <span class="cp"> help = </span><span class="s">&quot;Number of times to render each file&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7868 <span class="cp"> default_value = </span><span class="s">&quot;100&quot;</span><span class="cp">)]</span><span class="w"></span> Err codemadness.org 70 i 7869 <span class="w"> </span><span class="n">num_render</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7870 Err codemadness.org 70 i 7871 <span class="w"> </span><span class="cp">#[structopt(long = </span><span class="s">&quot;pixbuf&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7872 <span class="cp"> help = </span><span class="s">&quot;Render to a GdkPixbuf instead of a Cairo image surface&quot;</span><span class="cp">)]</span><span class="w"></span> Err codemadness.org 70 i 7873 <span class="w"> </span><span class="n">render_to_pixbuf</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7874 Err codemadness.org 70 i 7875 <span class="w"> </span><span class="cp">#[structopt(help = </span><span class="s">&quot;Input files or directories&quot;</span><span class="cp">,</span> Err codemadness.org 70 i 7876 <span class="cp"> parse(from_os_str))]</span><span class="w"></span> Err codemadness.org 70 i 7877 <span class="w"> </span><span class="n">inputs</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PathBuf</span><span class="o">&gt;</span><span class="w"></span> Err codemadness.org 70 i 7878 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7879 Err codemadness.org 70 i 7880 <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7881 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">opt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Opt</span>::<span class="n">from_args</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 7882 Err codemadness.org 70 i 7883 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">opt</span><span class="p">.</span><span class="n">inputs</span><span class="p">.</span><span class="n">len</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7884 <span class="w"> </span><span class="fm">eprintln!</span><span class="p">(</span><span class="s">&quot;No input files or directories specified</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7885 <span class="w"> </span><span class="n">process</span><span class="p">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7886 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7887 Err codemadness.org 70 i 7888 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 7889 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7890 </code></pre></div> Err codemadness.org 70 i 7891 Err codemadness.org 70 i 7892 <p>Each field in the <code>Opt</code> struct above corresponds to one command-line Err codemadness.org 70 i 7893 argument; each field has annotations for <code>structopt</code> to generate the Err codemadness.org 70 i 7894 appropriate code to parse each option. For example, the Err codemadness.org 70 i 7895 <code>render_to_pixbuf</code> field has a long option name called <code>"pixbuf"</code>; Err codemadness.org 70 i 7896 that field will be set to <code>true</code> if the <code>--pixbuf</code> option gets passed Err codemadness.org 70 i 7897 to rsvg-bench.</p> Err codemadness.org 70 i 7898 <h1>Handling errors</h1> Err codemadness.org 70 i 7899 <p>Command-line programs generally have the luxury of being able to just Err codemadness.org 70 i 7900 exit as soon as they encounter an error.</p> Err codemadness.org 70 i 7901 <p>In C this is a bit cumbersome since you need to deal with <em>every</em> Err codemadness.org 70 i 7902 place that may return an error, find out what to print, and call Err codemadness.org 70 i 7903 <code>exit(1)</code> by hand or something. If you miss a single place where an Err codemadness.org 70 i 7904 error is returned, your program will keep running with an inconsistent Err codemadness.org 70 i 7905 state.</p> Err codemadness.org 70 i 7906 <p>In languages with exception handling, it's a bit easier - a small Err codemadness.org 70 i 7907 script can just let exceptions be thrown wherever, and if it catches Err codemadness.org 70 i 7908 them at the toplevel, it can just print the exception and abort Err codemadness.org 70 i 7909 gracefully. However, these nonlocal jumps make me uncomfortable; I Err codemadness.org 70 i 7910 think <a href="http://joeduffyblog.com/2016/02/07/the-error-model/">exceptions are hard to reason about</a>.</p> Err codemadness.org 70 i 7911 <p>Rust makes this easy: it forces you to handle every call that may Err codemadness.org 70 i 7912 return an error, but it lets you bubble errors up easily, or handle Err codemadness.org 70 i 7913 them in-place, or translate them to a higher-level error.</p> Err codemadness.org 70 i 7914 <p>In the Rust world the [<code>failure</code>] crate is getting a lot of traction Err codemadness.org 70 i 7915 as a convenient, modern way to handle errors.</p> Err codemadness.org 70 i 7916 <p>In rsvg-bench, errors can come from several places:</p> Err codemadness.org 70 i 7917 <ul> Err codemadness.org 70 i 7918 <li> Err codemadness.org 70 i 7919 <p>I/O errors when reading files and directories.</p> Err codemadness.org 70 i 7920 </li> Err codemadness.org 70 i 7921 <li> Err codemadness.org 70 i 7922 <p>Errors from librsvg's parsing stage; you get a <a href="https://developer.gnome.org/glib/stable/glib-Error-Reporting.html">GError</a>.</p> Err codemadness.org 70 i 7923 </li> Err codemadness.org 70 i 7924 <li> Err codemadness.org 70 i 7925 <p>Errors from the rendering stage. This can be a Cairo error (a Err codemadness.org 70 i 7926 <a href="https://www.cairographics.org/manual/cairo-Error-handling.html">cairo_status_t</a>), or a simple "something bad happened; can't Err codemadness.org 70 i 7927 render" from librsvg's old convenience api in C. Don't you hate it Err codemadness.org 70 i 7928 when C code just gives up and returns NULL or a boolean false, Err codemadness.org 70 i 7929 without any further details on <em>what</em> went wrong?</p> Err codemadness.org 70 i 7930 </li> Err codemadness.org 70 i 7931 </ul> Err codemadness.org 70 i 7932 <p>For rsvg-bench, I just needed to be able to represent Cairo errors and Err codemadness.org 70 i 7933 generic rendering errors. Everything else, like an <code>io::Error</code>, is Err codemadness.org 70 i 7934 automatically wrapped by the <code>failure</code> crate's mechanism. I just Err codemadness.org 70 i 7935 needed to do this:</p> Err codemadness.org 70 i 7936 <div class="highlight"><pre><span></span><code><span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">failure</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7937 <span class="cp">#[macro_use]</span><span class="w"></span> Err codemadness.org 70 i 7938 <span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">failure_derive</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7939 Err codemadness.org 70 i 7940 <span class="cp">#[derive(Debug, Fail)]</span><span class="w"></span> Err codemadness.org 70 i 7941 <span class="k">enum</span> <span class="nc">ProcessingError</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7942 <span class="w"> </span><span class="cp">#[fail(display = </span><span class="s">&quot;Cairo error: {:?}&quot;</span><span class="cp">, status)]</span><span class="w"></span> Err codemadness.org 70 i 7943 <span class="w"> </span><span class="n">CairoError</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7944 <span class="w"> </span><span class="n">status</span>: <span class="nc">cairo</span>::<span class="n">Status</span><span class="w"></span> Err codemadness.org 70 i 7945 <span class="w"> </span><span class="p">},</span><span class="w"></span> Err codemadness.org 70 i 7946 Err codemadness.org 70 i 7947 <span class="w"> </span><span class="cp">#[fail(display = </span><span class="s">&quot;Rendering error&quot;</span><span class="cp">)]</span><span class="w"></span> Err codemadness.org 70 i 7948 <span class="w"> </span><span class="n">RenderingError</span><span class="w"></span> Err codemadness.org 70 i 7949 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7950 </code></pre></div> Err codemadness.org 70 i 7951 Err codemadness.org 70 i 7952 <p>Whenever the code gets a Cairo error, I can translate it to a Err codemadness.org 70 i 7953 <code>ProcessingError::CairoError</code> and bubble it up:</p> Err codemadness.org 70 i 7954 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">render_to_cairo</span><span class="p">(</span><span class="n">handle</span>: <span class="kp">&amp;</span><span class="nc">rsvg</span>::<span class="n">Handle</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">Error</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7955 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">dim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">handle</span><span class="p">.</span><span class="n">get_dimensions</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 7956 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">surface</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cairo</span>::<span class="n">ImageSurface</span>::<span class="n">create</span><span class="p">(</span><span class="n">cairo</span>::<span class="n">Format</span>::<span class="n">ARgb32</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7957 <span class="w"> </span><span class="n">dim</span><span class="p">.</span><span class="n">width</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 7958 <span class="w"> </span><span class="n">dim</span><span class="p">.</span><span class="n">height</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 7959 <span class="w"> </span><span class="p">.</span><span class="n">map_err</span><span class="p">(</span><span class="o">|</span><span class="n">e</span><span class="o">|</span><span class="w"> </span><span class="n">ProcessingError</span>::<span class="n">CairoError</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">status</span>: <span class="nc">e</span><span class="w"> </span><span class="p">})</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7960 Err codemadness.org 70 i 7961 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 7962 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7963 </code></pre></div> Err codemadness.org 70 i 7964 Err codemadness.org 70 i 7965 <p>And when librsvg returns a "couldn't render" error, I translate that Err codemadness.org 70 i 7966 to a <code>ProcessingError::RenderingError</code>:</p> Err codemadness.org 70 i 7967 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">render_to_cairo</span><span class="p">(</span><span class="n">handle</span>: <span class="kp">&amp;</span><span class="nc">rsvg</span>::<span class="n">Handle</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">Error</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7968 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 7969 Err codemadness.org 70 i 7970 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">cr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cairo</span>::<span class="n">Context</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">surface</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 7971 Err codemadness.org 70 i 7972 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">handle</span><span class="p">.</span><span class="n">render_cairo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cr</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7973 <span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"></span> Err codemadness.org 70 i 7974 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7975 <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">Error</span>::<span class="n">from</span><span class="p">(</span><span class="n">ProcessingError</span>::<span class="n">RenderingError</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 7976 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7977 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7978 </code></pre></div> Err codemadness.org 70 i 7979 Err codemadness.org 70 i 7980 <p>Here, the <code>Ok()</code> case of the <code>Result</code> does not contain any value — Err codemadness.org 70 i 7981 it's just <code>()</code>, as the generated images are not stored anywhere: they Err codemadness.org 70 i 7982 are just rendered to get some timings, not to be saved or anything.</p> Err codemadness.org 70 i 7983 <h1>Up to where do errors bubble?</h1> Err codemadness.org 70 i 7984 <p>This is the "do everything" function:</p> Err codemadness.org 70 i 7985 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">run</span><span class="p">(</span><span class="n">opt</span>: <span class="kp">&amp;</span><span class="nc">Opt</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">Error</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7986 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 7987 Err codemadness.org 70 i 7988 <span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="o">&amp;</span><span class="n">opt</span><span class="p">.</span><span class="n">inputs</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 7989 <span class="w"> </span><span class="n">process_path</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">path</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 7990 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7991 Err codemadness.org 70 i 7992 <span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"></span> Err codemadness.org 70 i 7993 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 7994 </code></pre></div> Err codemadness.org 70 i 7995 Err codemadness.org 70 i 7996 <p>For each path passed in the command line, process it. The program Err codemadness.org 70 i 7997 sees if the path corresponds to a directory, and it will scan it Err codemadness.org 70 i 7998 recursively. Or if the path is an SVG file, the program will load the Err codemadness.org 70 i 7999 file and render it.</p> Err codemadness.org 70 i 8000 <p>Finally, <code>main()</code> just has this:</p> Err codemadness.org 70 i 8001 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8002 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">opt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Opt</span>::<span class="n">from_args</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 8003 Err codemadness.org 70 i 8004 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 8005 Err codemadness.org 70 i 8006 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">run</span><span class="p">(</span><span class="o">&amp;</span><span class="n">opt</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8007 <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 8008 <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8009 <span class="w"> </span><span class="fm">eprintln!</span><span class="p">(</span><span class="s">&quot;{}&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">e</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8010 <span class="w"> </span><span class="n">process</span>::<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8011 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8012 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8013 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8014 </code></pre></div> Err codemadness.org 70 i 8015 Err codemadness.org 70 i 8016 <p>I.e. process command line arguments, run the whole thing, and print an Err codemadness.org 70 i 8017 error if there was one.</p> Err codemadness.org 70 i 8018 <p>I really appreciate that most places that can return an error an just Err codemadness.org 70 i 8019 put a <code>?</code> for the error to bubble up. This is much more legible than Err codemadness.org 70 i 8020 in C, where every call must have an <code>if (something_bad_happened) { Err codemadness.org 70 i 8021 deal_with_it; }</code> after it... and Rust won't let me get away with Err codemadness.org 70 i 8022 ignoring an error, but it makes it easy to actually deal with it properly.</p> Err codemadness.org 70 i 8023 <h1>Reading an SVG file quickly</h1> Err codemadness.org 70 i 8024 <p>Why, just <code>mmap()</code> it and feed it to librsvg, to avoid buffer copies. Err codemadness.org 70 i 8025 This is easy in Rust:</p> Err codemadness.org 70 i 8026 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">process_file</span><span class="o">&lt;</span><span class="n">P</span>: <span class="nb">AsRef</span><span class="o">&lt;</span><span class="n">Path</span><span class="o">&gt;&gt;</span><span class="p">(</span><span class="n">opt</span>: <span class="kp">&amp;</span><span class="nc">Opt</span><span class="p">,</span><span class="w"> </span><span class="n">path</span>: <span class="nc">P</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">Error</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8027 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">File</span>::<span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8028 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">mmap</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">MmapOptions</span>::<span class="n">new</span><span class="p">().</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="n">file</span><span class="p">)</span><span class="o">?</span><span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 8029 Err codemadness.org 70 i 8030 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">bytes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="n">mmap</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8031 Err codemadness.org 70 i 8032 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">handle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rsvg</span>::<span class="n">Handle</span>::<span class="n">new_from_data</span><span class="p">(</span><span class="n">bytes</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8033 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 8034 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8035 </code></pre></div> Err codemadness.org 70 i 8036 Err codemadness.org 70 i 8037 <p>Many things can go wrong here:</p> Err codemadness.org 70 i 8038 <ul> Err codemadness.org 70 i 8039 <li><code>File::open()</code> can return an io::Error.</li> Err codemadness.org 70 i 8040 <li><code>MmapOptions::map()</code> can return an io::Error from the <code>mmap(2)</code> Err codemadness.org 70 i 8041 system call, or from the <code>fstat(2)</code> to read the file's size to map Err codemadness.org 70 i 8042 it.</li> Err codemadness.org 70 i 8043 <li><code>rsvg::Handle::new_from_data()</code> can return a GError from parsing the Err codemadness.org 70 i 8044 file.</li> Err codemadness.org 70 i 8045 </ul> Err codemadness.org 70 i 8046 <p>The little <code>?</code> characters after each call that can return an error Err codemadness.org 70 i 8047 mean, just give me back the result, or convert the error to a Err codemadness.org 70 i 8048 <code>failure::Error</code> that can be examined later. This is beautifully Err codemadness.org 70 i 8049 legible to me.</p> Err codemadness.org 70 i 8050 <h1>Summary</h1> Err codemadness.org 70 i 8051 <p>Writing command-line programs in Rust is fun! It's nice to have Err codemadness.org 70 i 8052 neurotically-safe scripts that one can trust in the future.</p> Err codemadness.org 70 i 8053 <p><a href="https://gitlab.gnome.org/federico/rsvg-bench">Rsvg-bench is available here</a>.</p>rsvg-bench - a benchmark for librsvg2018-02-02T16:10:34-06:002018-02-02T16:10:34-06:00Federico Mena Quinterotag:people.gnome.org,2018-02-02:/~federico/blog/rsvg-bench.html<p>Librsvg 2.42.0 came out with a rather major performance regression Err codemadness.org 70 i 8054 compared to 2.40.20: SVGs with many <a href="https://www.w3.org/TR/SVG/coords.html#TransformAttribute"><code>transform</code></a> Err codemadness.org 70 i 8055 attributes would slow it down. It was fixed in 2.42.1. We changed Err codemadness.org 70 i 8056 from using a <a href="https://github.com/lalrpop/lalrpop/issues/269">parser that would recompile regexes</a> each time it was Err codemadness.org 70 i 8057 called, to <a href="https://github.com/servo/rust-cssparser">one …</a></p><p>Librsvg 2.42.0 came out with a rather major performance regression Err codemadness.org 70 i 8058 compared to 2.40.20: SVGs with many <a href="https://www.w3.org/TR/SVG/coords.html#TransformAttribute"><code>transform</code></a> Err codemadness.org 70 i 8059 attributes would slow it down. It was fixed in 2.42.1. We changed Err codemadness.org 70 i 8060 from using a <a href="https://github.com/lalrpop/lalrpop/issues/269">parser that would recompile regexes</a> each time it was Err codemadness.org 70 i 8061 called, to <a href="https://github.com/servo/rust-cssparser">one that does simple string-based matching</a> and Err codemadness.org 70 i 8062 parsing.</p> Err codemadness.org 70 i 8063 <p>When I rewrote librsvg's parser for the <code>transform</code> attribute from C Err codemadness.org 70 i 8064 to Rust, I was just <a href="https://people.gnome.org/~federico/news-2017-02.html#24">learning about writing parsers in Rust</a>. Err codemadness.org 70 i 8065 I chose <a href="https://github.com/lalrpop/lalrpop">lalrpop</a>, an excellent, Yacc-like parser generator for Rust. Err codemadness.org 70 i 8066 It generates big, fast parsers, like what you would need for a Err codemadness.org 70 i 8067 compiler — but it compiles the tokenizer's regexes each time you call Err codemadness.org 70 i 8068 the parser. This is not a problem for a compiler, where you basically Err codemadness.org 70 i 8069 call the parser only once, but in librsvg, we may call it thousands of Err codemadness.org 70 i 8070 times for an SVG file with thousands of objects with <code>transform</code> Err codemadness.org 70 i 8071 attributes.</p> Err codemadness.org 70 i 8072 <p>So, for 2.42.1 I rewrote that parser using Err codemadness.org 70 i 8073 <a href="https://github.com/servo/rust-cssparser">rust-cssparser</a>. This is what <a href="https://servo.org/">Servo</a> uses to Err codemadness.org 70 i 8074 parse CSS data; it's a simple tokenizer with an API that knows about Err codemadness.org 70 i 8075 CSS's particular constructs. This is exactly the kind of data that Err codemadness.org 70 i 8076 librsvg cares about. Today all of librsvg's internal parsers work Err codemadness.org 70 i 8077 using rust-cssparser, or they are so simple that they can be done with Err codemadness.org 70 i 8078 Rust's normal functions to split strings and such.</p> Err codemadness.org 70 i 8079 <h1>Getting good timings</h1> Err codemadness.org 70 i 8080 <p>Librsvg ships with <code>rsvg-convert</code>, a command-line utility that can Err codemadness.org 70 i 8081 render an SVG file and write the output to a PNG. While it would be Err codemadness.org 70 i 8082 possible to get timings for SVG rendering by timing how long Err codemadness.org 70 i 8083 <code>rsvg-convert</code> takes to run, it's a bit clunky for that. The process Err codemadness.org 70 i 8084 startup adds noise to the timings, and it only handles one file at a Err codemadness.org 70 i 8085 time.</p> Err codemadness.org 70 i 8086 <p>So, I've written <a href="https://gitlab.gnome.org/federico/rsvg-bench">rsvg-bench</a>, a small utility to get timings out of Err codemadness.org 70 i 8087 librsvg. I wanted a tool that:</p> Err codemadness.org 70 i 8088 <ul> Err codemadness.org 70 i 8089 <li> Err codemadness.org 70 i 8090 <p>Is able to process many SVG images with a single command. For Err codemadness.org 70 i 8091 example, this lets us answer a question like, "how long does version Err codemadness.org 70 i 8092 N of librsvg take to render a directory full of SVG icons?" — which Err codemadness.org 70 i 8093 is important for the performance of an application chooser.</p> Err codemadness.org 70 i 8094 </li> Err codemadness.org 70 i 8095 <li> Err codemadness.org 70 i 8096 <p>Is able to <em>repeatedly</em> process SVG files, for example, "render this Err codemadness.org 70 i 8097 SVG 1000 times in a row". This is useful to get accurate timings, Err codemadness.org 70 i 8098 as a single render may only take a few microseconds and may be hard Err codemadness.org 70 i 8099 to measure. It also helps with running profilers, as they will be Err codemadness.org 70 i 8100 able to get more useful samples if the SVG rendering process runs Err codemadness.org 70 i 8101 repeatedly for a long time.</p> Err codemadness.org 70 i 8102 </li> Err codemadness.org 70 i 8103 <li> Err codemadness.org 70 i 8104 <p>Exercises librsvg's major code paths for parsing and rendering Err codemadness.org 70 i 8105 separately. For example, librsvg uses different parts of the XML Err codemadness.org 70 i 8106 parser depending on whether it is being pushed data, vs. being asked Err codemadness.org 70 i 8107 to pull data from a stream. Also, we may only want to benchmark the Err codemadness.org 70 i 8108 parser but not the renderer; or we may want to parse SVGs only once Err codemadness.org 70 i 8109 but render them many times after that.</p> Err codemadness.org 70 i 8110 </li> Err codemadness.org 70 i 8111 <li> Err codemadness.org 70 i 8112 <p>Is aware of librsvg's peculiarities, such as the extra pass to Err codemadness.org 70 i 8113 convert a Cairo image surface to a GdkPixbuf when one uses the Err codemadness.org 70 i 8114 convenience function <code>rsvg_handle_get_pixbuf()</code>.</p> Err codemadness.org 70 i 8115 </li> Err codemadness.org 70 i 8116 </ul> Err codemadness.org 70 i 8117 <p>Currently rsvg-bench supports all of that.</p> Err codemadness.org 70 i 8118 <h1>An initial benchmark</h1> Err codemadness.org 70 i 8119 <p>I ran this</p> Err codemadness.org 70 i 8120 <p><code>/usr/bin/time rsvg-bench -p 1 -r 1 /usr/share/icons</code></p> Err codemadness.org 70 i 8121 <p>to cause every SVG icon in <code>/usr/share/icons</code> to be parsed once, and Err codemadness.org 70 i 8122 rendered once (i.e. just render every file sequentially). I did this Err codemadness.org 70 i 8123 for librsvg 2.40.20 (C only), and 2.42.{0, 1, 2} (C and Rust). There Err codemadness.org 70 i 8124 are 5522 SVG files in there. The timings look like this:</p> Err codemadness.org 70 i 8125 <table> Err codemadness.org 70 i 8126 <thead> Err codemadness.org 70 i 8127 <tr> Err codemadness.org 70 i 8128 <th>version</th> Err codemadness.org 70 i 8129 <th>time (sec)</th> Err codemadness.org 70 i 8130 </tr> Err codemadness.org 70 i 8131 </thead> Err codemadness.org 70 i 8132 <tbody> Err codemadness.org 70 i 8133 <tr> Err codemadness.org 70 i 8134 <td>2.40.20</td> Err codemadness.org 70 i 8135 <td>95.54</td> Err codemadness.org 70 i 8136 </tr> Err codemadness.org 70 i 8137 <tr> Err codemadness.org 70 i 8138 <td>2.42.0</td> Err codemadness.org 70 i 8139 <td>209.50</td> Err codemadness.org 70 i 8140 </tr> Err codemadness.org 70 i 8141 <tr> Err codemadness.org 70 i 8142 <td>2.42.1</td> Err codemadness.org 70 i 8143 <td>97.18</td> Err codemadness.org 70 i 8144 </tr> Err codemadness.org 70 i 8145 <tr> Err codemadness.org 70 i 8146 <td>2.42.2</td> Err codemadness.org 70 i 8147 <td>95.89</td> Err codemadness.org 70 i 8148 </tr> Err codemadness.org 70 i 8149 </tbody> Err codemadness.org 70 i 8150 </table> Err codemadness.org 70 i 8151 <p><img alt="Bar chart of timings" src="https://people.gnome.org/~federico/blog/images/rsvg-bench-timings.png"></p> Err codemadness.org 70 i 8152 <p>So, 2.42.0 was over twice as slow as the C-only version, due to the Err codemadness.org 70 i 8153 parsing problems. But now, 2.42.2 is practically just as fast as the Err codemadness.org 70 i 8154 C only version. What made this possible?</p> Err codemadness.org 70 i 8155 <ul> Err codemadness.org 70 i 8156 <li>2.40.20 - the old C-only version</li> Err codemadness.org 70 i 8157 <li>2.42.0 - C + Rust, with a lalrpop parser for the <code>transform</code> attribute</li> Err codemadness.org 70 i 8158 <li>2.42.1 - Servo's cssparser for the <code>transform</code> attribute</li> Err codemadness.org 70 i 8159 <li>2.42.2 - removed most C-to-Rust string copies during parsing</li> Err codemadness.org 70 i 8160 </ul> Err codemadness.org 70 i 8161 <p>I have started taking profiles of rsvg-bench runs with sysprof, and Err codemadness.org 70 i 8162 there are some improvements worth making. Expect news soon!</p> Err codemadness.org 70 i 8163 <p><a href="https://gitlab.gnome.org/federico/rsvg-bench">Rsvg-bench is available in Gnome's gitlab instance</a>.</p>Help needed for librsvg 2.42.12018-01-16T11:01:05-06:002018-01-16T11:01:05-06:00Federico Mena Quinterotag:people.gnome.org,2018-01-16:/~federico/blog/help-needed-for-librsvg-2421.html<p>Would you like to help fix a couple of bugs in <a href="https://gitlab.gnome.org/GNOME/librsvg">librsvg</a>, in Err codemadness.org 70 i 8164 preparation for the 2.42.1 release?</p> Err codemadness.org 70 i 8165 <p>I have prepared a list of bugs which I'd like to be fixed in the Err codemadness.org 70 i 8166 <a href="https://gitlab.gnome.org/GNOME/librsvg/issues?milestone_title=2.42.1">2.42.1 milestone</a>. Two of them are assigned to myself, as Err codemadness.org 70 i 8167 I'm already working …</p><p>Would you like to help fix a couple of bugs in <a href="https://gitlab.gnome.org/GNOME/librsvg">librsvg</a>, in Err codemadness.org 70 i 8168 preparation for the 2.42.1 release?</p> Err codemadness.org 70 i 8169 <p>I have prepared a list of bugs which I'd like to be fixed in the Err codemadness.org 70 i 8170 <a href="https://gitlab.gnome.org/GNOME/librsvg/issues?milestone_title=2.42.1">2.42.1 milestone</a>. Two of them are assigned to myself, as Err codemadness.org 70 i 8171 I'm already working on them.</p> Err codemadness.org 70 i 8172 <p>There are two other bugs which I'd love someone to look at. Neither Err codemadness.org 70 i 8173 of these requires deep knowledge of librsvg, just some debugging and Err codemadness.org 70 i 8174 code-writing:</p> Err codemadness.org 70 i 8175 <ul> Err codemadness.org 70 i 8176 <li> Err codemadness.org 70 i 8177 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/issues/141">Bug 141</a> - GNOME's thumbnailing machinery creates an icon which has Err codemadness.org 70 i 8178 the wrong fill: it's an image of a builder's trowel, and the inside Err codemadness.org 70 i 8179 is filled black instead of with a nice gradient. This is the only Err codemadness.org 70 i 8180 place in librsvg where a <code>cairo_surface_t</code> is converted to a Err codemadness.org 70 i 8181 <code>GdkPixbuf</code>; this involves unpremultiplying the alpha channel. Err codemadness.org 70 i 8182 Maybe the relevant function is buggy?</p> Err codemadness.org 70 i 8183 </li> Err codemadness.org 70 i 8184 <li> Err codemadness.org 70 i 8185 <p><a href="https://gitlab.gnome.org/GNOME/librsvg/issues/136">Bug 136</a>: The <code>stroke-dasharray</code> attribute in SVG elements is Err codemadness.org 70 i 8186 parsed incorrectly. It is a list of CSS length values, separated by Err codemadness.org 70 i 8187 commas or spaces. Currently librsvg uses a shitty parser based on Err codemadness.org 70 i 8188 <code>g_strsplit()</code> only for commas; it doesn't allow just a Err codemadness.org 70 i 8189 space-separated list. Then, it uses <code>g_ascii_strtod()</code> to parse Err codemadness.org 70 i 8190 plain numbers; it doesn't support CSS lengths generically. This Err codemadness.org 70 i 8191 parser needs to be rewritten in Rust; we already have machinery Err codemadness.org 70 i 8192 there to parse CSS length values properly.</p> Err codemadness.org 70 i 8193 </li> Err codemadness.org 70 i 8194 </ul> Err codemadness.org 70 i 8195 <p>Feel free to <a href="federico@gnome.org">contact me</a> by mail, or write something in the Err codemadness.org 70 i 8196 bugs themselves, if you would like to work on them. I'll happily Err codemadness.org 70 i 8197 guide you through the code :)</p>Librsvg gets Continuous Integration2018-01-12T14:04:20-06:002018-01-12T14:04:20-06:00Federico Mena Quinterotag:people.gnome.org,2018-01-12:/~federico/blog/librsvg-gets-continuous-integration.html<p>One nice thing about <code>gitlab.gnome.org</code> is that we can now have Err codemadness.org 70 i 8198 Continuous Integration (CI) enabled for projects there. After every Err codemadness.org 70 i 8199 commit, the CI machinery can build the project, run the tests, and Err codemadness.org 70 i 8200 tell you if something goes wrong.</p> Err codemadness.org 70 i 8201 <p><a href="https://mail.gnome.org/archives/desktop-devel-list/2018-January/msg00008.html">Carlos Soriano posted</a> a "tips of the Err codemadness.org 70 i 8202 week" mail to …</p><p>One nice thing about <code>gitlab.gnome.org</code> is that we can now have Err codemadness.org 70 i 8203 Continuous Integration (CI) enabled for projects there. After every Err codemadness.org 70 i 8204 commit, the CI machinery can build the project, run the tests, and Err codemadness.org 70 i 8205 tell you if something goes wrong.</p> Err codemadness.org 70 i 8206 <p><a href="https://mail.gnome.org/archives/desktop-devel-list/2018-January/msg00008.html">Carlos Soriano posted</a> a "tips of the Err codemadness.org 70 i 8207 week" mail to desktop-devel-list, and a link to how Nautilus Err codemadness.org 70 i 8208 implements CI in Gitlab. It turns out that it's reasonably easy to Err codemadness.org 70 i 8209 set up: you just create a <a href="https://docs.gitlab.com/ce/ci/yaml/README.html"><code>.gitlab-ci.yml</code></a> file in the Err codemadness.org 70 i 8210 toplevel of your project, and that has the configuration for what to Err codemadness.org 70 i 8211 run on every commit.</p> Err codemadness.org 70 i 8212 <p>Of course instead of reading the manual, I copied-and-pasted the file Err codemadness.org 70 i 8213 from Nautilus and just changed some things in it. <a href="https://gitlab.gnome.org/ci/lint">There is a .yml Err codemadness.org 70 i 8214 linter</a> so you can at least check the syntax before pushing a Err codemadness.org 70 i 8215 full job.</p> Err codemadness.org 70 i 8216 <p>Then I read <a href="https://mail.gnome.org/archives/desktop-devel-list/2018-January/msg00010.html">Robert Ancell's reply</a> about how simple-scan Err codemadness.org 70 i 8217 builds its CI jobs on both Fedora and Ubuntu... and then the Err codemadness.org 70 i 8218 realization hit me:</p> Err codemadness.org 70 i 8219 <p><em>This lets me CI librsvg on multiple distros at once.</em> I've had Err codemadness.org 70 i 8220 trouble with slight differences in fontconfig/freetype in the past, Err codemadness.org 70 i 8221 and this would let me catch them early.</p> Err codemadness.org 70 i 8222 <p>However, people on IRC advised against this, as <strong>we need more Err codemadness.org 70 i 8223 hardware</strong> to run CI on a large scale.</p> Err codemadness.org 70 i 8224 <p>Linux distros have a vested interest in getting code out of gnome.org Err codemadness.org 70 i 8225 that works well. Surely they can give us some hardware?</p>Loving Gitlab.gnome.org, and getting notifications2018-01-08T11:45:01-06:002018-01-08T11:45:01-06:00Federico Mena Quinterotag:people.gnome.org,2018-01-08:/~federico/blog/loving-gitlab.html<p>I'm loving <a href="https://gitlab.gnome.org"><code>gitlab.gnome.org</code></a>. It has been only a couple of Err codemadness.org 70 i 8226 weeks since <a href="https://people.gnome.org/~federico/blog/librsvg-moves-to-gitlab.html">librsvg moved to gitlab</a>, and I've Err codemadness.org 70 i 8227 already received and merged <a href="https://gitlab.gnome.org/GNOME/librsvg/merge_requests?scope=all&amp;utf8=%E2%9C%93&amp;state=merged">two merge requests</a>. (Isn't it a bit Err codemadness.org 70 i 8228 weird that Github uses "pull request" and Everyone(tm) knows the PR Err codemadness.org 70 i 8229 acronym, but Gitlab uses "merge request"?)</p> Err codemadness.org 70 i 8230 <h1>Notifications …</h1><p>I'm loving <a href="https://gitlab.gnome.org"><code>gitlab.gnome.org</code></a>. It has been only a couple of Err codemadness.org 70 i 8231 weeks since <a href="https://people.gnome.org/~federico/blog/librsvg-moves-to-gitlab.html">librsvg moved to gitlab</a>, and I've Err codemadness.org 70 i 8232 already received and merged <a href="https://gitlab.gnome.org/GNOME/librsvg/merge_requests?scope=all&amp;utf8=%E2%9C%93&amp;state=merged">two merge requests</a>. (Isn't it a bit Err codemadness.org 70 i 8233 weird that Github uses "pull request" and Everyone(tm) knows the PR Err codemadness.org 70 i 8234 acronym, but Gitlab uses "merge request"?)</p> Err codemadness.org 70 i 8235 <h1>Notifications about merge requests</h1> Err codemadness.org 70 i 8236 <p>One thing to note if your GNOME project has moved to Gitlab: <strong>if you Err codemadness.org 70 i 8237 want to get notified of incoming merge requests</strong>, you need Err codemadness.org 70 i 8238 to tell Gitlab that you want to "<strong>Watch</strong>" that project, instead of Err codemadness.org 70 i 8239 using one of the default notification settings. <a href="https://gitlab.gnome.org/GNOMEInfrastructure/GitLab/issues/95">Thanks to Carlos Err codemadness.org 70 i 8240 Soriano</a> for making me aware of this.</p> Err codemadness.org 70 i 8241 <h1>Notifications from Github's mirror</h1> Err codemadness.org 70 i 8242 <p>The <a href="https://github.com/GNOME/">github</a> mirror of git.gnome.org is configured so that pull Err codemadness.org 70 i 8243 requests are <a href="https://wiki.gnome.org/Sysadmin/GitHub">automatically closed</a>, since currently there Err codemadness.org 70 i 8244 is no way to notify the upstream maintainers when someone creates a Err codemadness.org 70 i 8245 pull request in the mirror (this is super-unfriendly by default, but Err codemadness.org 70 i 8246 at least submitters get notified that their PR would not be looked at Err codemadness.org 70 i 8247 by anyone, by default).</p> Err codemadness.org 70 i 8248 <p>If you have a Github account, you can Watch the project in question to Err codemadness.org 70 i 8249 get notified — the bot will close the pull request, but you will get Err codemadness.org 70 i 8250 notified, and then you can check it by hand, review it as appropriate, Err codemadness.org 70 i 8251 or redirect the submitter to gitlab.gnome.org instead.</p>Librsvg 2.40.20 is released2017-12-15T18:31:50-06:002017-12-15T18:31:50-06:00Federico Mena Quinterotag:people.gnome.org,2017-12-15:/~federico/blog/librsvg-24020-is-released.html<p>Today I released <a href="https://ftp.gnome.org/pub/GNOME/sources/librsvg/2.40/">librsvg 2.40.20</a>. This will be the Err codemadness.org 70 i 8252 <strong>last release</strong> in the 2.40.x series, which is deprecated effectively Err codemadness.org 70 i 8253 immediately.</p> Err codemadness.org 70 i 8254 <p>People and distros are <strong>strongly encouraged</strong> to switch to Err codemadness.org 70 i 8255 <a href="https://ftp.gnome.org/pub/GNOME/sources/librsvg/2.41/">librsvg 2.41.x</a> as soon as possible. This is the version that is Err codemadness.org 70 i 8256 implemented in a …</p><p>Today I released <a href="https://ftp.gnome.org/pub/GNOME/sources/librsvg/2.40/">librsvg 2.40.20</a>. This will be the Err codemadness.org 70 i 8257 <strong>last release</strong> in the 2.40.x series, which is deprecated effectively Err codemadness.org 70 i 8258 immediately.</p> Err codemadness.org 70 i 8259 <p>People and distros are <strong>strongly encouraged</strong> to switch to Err codemadness.org 70 i 8260 <a href="https://ftp.gnome.org/pub/GNOME/sources/librsvg/2.41/">librsvg 2.41.x</a> as soon as possible. This is the version that is Err codemadness.org 70 i 8261 implemented in a mixture of C and Rust. It is 100% API and ABI Err codemadness.org 70 i 8262 compatible with 2.40.x, so it is a drop-in replacement for it. If you Err codemadness.org 70 i 8263 or your distro can compile Firefox 57, you can probably build Err codemadness.org 70 i 8264 librsvg-2.41.x without problems.</p> Err codemadness.org 70 i 8265 <h1>Some statistics</h1> Err codemadness.org 70 i 8266 <p>Here are a few runs of <a href="https://github.com/cgag/loc">loc</a> — a tool to count lines of code — Err codemadness.org 70 i 8267 when run on librsvg. The output is trimmed by hand to only include C Err codemadness.org 70 i 8268 and Rust files.</p> Err codemadness.org 70 i 8269 <div class="highlight"><pre><span></span><code><span class="c">This is 2</span><span class="nt">.</span><span class="c">40</span><span class="nt">.</span><span class="c">20:</span> Err codemadness.org 70 i 8270 <span class="nb">-------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 8271 <span class="c"> Language Files Lines Blank Comment Code</span> Err codemadness.org 70 i 8272 <span class="nb">-------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 8273 <span class="c"> C 41 20972 3438 2100 15434</span> Err codemadness.org 70 i 8274 <span class="c"> C/C</span><span class="nb">++</span><span class="c"> Header 27 2377 452 625 1300</span> Err codemadness.org 70 i 8275 </code></pre></div> Err codemadness.org 70 i 8276 Err codemadness.org 70 i 8277 <div class="highlight"><pre><span></span><code><span class="c">This is 2</span><span class="nt">.</span><span class="c">41</span><span class="nt">.</span><span class="c">latest (the master branch):</span> Err codemadness.org 70 i 8278 <span class="nb">-------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 8279 <span class="c"> Language Files Lines Blank Comment Code</span> Err codemadness.org 70 i 8280 <span class="nb">-------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 8281 <span class="c"> C 34 17253 3024 1892 12337</span> Err codemadness.org 70 i 8282 <span class="c"> C/C</span><span class="nb">++</span><span class="c"> Header 23 2327 501 624 1202</span> Err codemadness.org 70 i 8283 <span class="c"> Rust 38 11254 1873 675 8706</span> Err codemadness.org 70 i 8284 </code></pre></div> Err codemadness.org 70 i 8285 Err codemadness.org 70 i 8286 <div class="highlight"><pre><span></span><code><span class="c">And this is 2</span><span class="nt">.</span><span class="c">41</span><span class="nt">.</span><span class="c">latest *without unit tests*</span><span class="nt">,</span><span class="c"> </span> Err codemadness.org 70 i 8287 <span class="c">just &quot;real source code&quot;:</span> Err codemadness.org 70 i 8288 <span class="nb">-------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 8289 <span class="c"> Language Files Lines Blank Comment Code</span> Err codemadness.org 70 i 8290 <span class="nb">-------------------------------------------------------</span><span class="c"></span> Err codemadness.org 70 i 8291 <span class="c"> C 34 17253 3024 1892 12337</span> Err codemadness.org 70 i 8292 <span class="c"> C/C</span><span class="nb">++</span><span class="c"> Header 23 2327 501 624 1202</span> Err codemadness.org 70 i 8293 <span class="c"> Rust 38 9340 1513 610 7217</span> Err codemadness.org 70 i 8294 </code></pre></div> Err codemadness.org 70 i 8295 Err codemadness.org 70 i 8296 <h2>Summary</h2> Err codemadness.org 70 i 8297 <p>Not counting blank lines nor comments:</p> Err codemadness.org 70 i 8298 <ul> Err codemadness.org 70 i 8299 <li> Err codemadness.org 70 i 8300 <p>The C-only version has 16734 lines of C code.</p> Err codemadness.org 70 i 8301 </li> Err codemadness.org 70 i 8302 <li> Err codemadness.org 70 i 8303 <p>The C-only version has <strong>no unit tests</strong>, just some integration tests.</p> Err codemadness.org 70 i 8304 </li> Err codemadness.org 70 i 8305 <li> Err codemadness.org 70 i 8306 <p>The Rust-and-C version has 13539 lines of C code, 7217 lines of Rust Err codemadness.org 70 i 8307 code, and 1489 lines of unit tests in Rust.</p> Err codemadness.org 70 i 8308 </li> Err codemadness.org 70 i 8309 </ul> Err codemadness.org 70 i 8310 <p>As for the integration tests:</p> Err codemadness.org 70 i 8311 <ul> Err codemadness.org 70 i 8312 <li> Err codemadness.org 70 i 8313 <p>The C-only version has 64 integration tests.</p> Err codemadness.org 70 i 8314 </li> Err codemadness.org 70 i 8315 <li> Err codemadness.org 70 i 8316 <p>The Rust-and-C version has 130 integration tests.</p> Err codemadness.org 70 i 8317 </li> Err codemadness.org 70 i 8318 </ul> Err codemadness.org 70 i 8319 <p>The Rust-and-C version supports a few more SVG features, and it is A Err codemadness.org 70 i 8320 LOT more robust and spec-compliant with the SVG features that were Err codemadness.org 70 i 8321 supported in the C-only version.</p> Err codemadness.org 70 i 8322 <p>The C sources in librsvg are shrinking steadily. It would be Err codemadness.org 70 i 8323 incredibly awesome if someone could run some <code>git filter-branch</code> magic Err codemadness.org 70 i 8324 with the <a href="https://github.com/cgag/loc"><code>loc</code></a> tool and generate some pretty graphs of source Err codemadness.org 70 i 8325 lines vs. commits over time.</p>Librsvg moves to Gitlab2017-12-13T14:09:32-06:002017-12-13T14:09:32-06:00Federico Mena Quinterotag:people.gnome.org,2017-12-13:/~federico/blog/librsvg-moves-to-gitlab.html<p>Librsvg now lives in GNOME's <a href="https://gitlab.gnome.org/">Gitlab</a> instance. You can Err codemadness.org 70 i 8326 access it <a href="https://gitlab.gnome.org/GNOME/librsvg">here</a>.</p> Err codemadness.org 70 i 8327 <p>Gitlab allows workflows similar to Github: you can create an account Err codemadness.org 70 i 8328 there, fork the librsvg repository, file bug reports, create merge Err codemadness.org 70 i 8329 requests... Hopefully this will make it nicer for contributors.</p> Err codemadness.org 70 i 8330 <p>In the meantime, feel free to <a href="https://gitlab.gnome.org/GNOME/librsvg">take a …</a></p><p>Librsvg now lives in GNOME's <a href="https://gitlab.gnome.org/">Gitlab</a> instance. You can Err codemadness.org 70 i 8331 access it <a href="https://gitlab.gnome.org/GNOME/librsvg">here</a>.</p> Err codemadness.org 70 i 8332 <p>Gitlab allows workflows similar to Github: you can create an account Err codemadness.org 70 i 8333 there, fork the librsvg repository, file bug reports, create merge Err codemadness.org 70 i 8334 requests... Hopefully this will make it nicer for contributors.</p> Err codemadness.org 70 i 8335 <p>In the meantime, feel free to <a href="https://gitlab.gnome.org/GNOME/librsvg">take a look</a>!</p> Err codemadness.org 70 i 8336 <p>This is a huge improvement for GNOME's development infrastructure. Err codemadness.org 70 i 8337 Thanks to Carlos Soriano, Andrea Veri, Philip Chimento, Alberto Ruiz, Err codemadness.org 70 i 8338 and all the people that made the move to Gitlab possible.</p>A mini-rant on the lack of string slices in C2017-12-07T09:54:14-06:002017-12-07T09:54:14-06:00Federico Mena Quinterotag:people.gnome.org,2017-12-07:/~federico/blog/rant-on-string-slices.html<p>Porting of librsvg to Rust goes on. Yesterday I started porting the Err codemadness.org 70 i 8339 C code that implements SVG's <code>&lt;text&gt;</code> family of elements. I have also Err codemadness.org 70 i 8340 been replacing the little parsers in librsvg with Rust code.</p> Err codemadness.org 70 i 8341 <p>And these days, the lack of string slices in C is bothering me <em>a Err codemadness.org 70 i 8342 lot</em>.</p> Err codemadness.org 70 i 8343 <h1>What …</h1><p>Porting of librsvg to Rust goes on. Yesterday I started porting the Err codemadness.org 70 i 8344 C code that implements SVG's <code>&lt;text&gt;</code> family of elements. I have also Err codemadness.org 70 i 8345 been replacing the little parsers in librsvg with Rust code.</p> Err codemadness.org 70 i 8346 <p>And these days, the lack of string slices in C is bothering me <em>a Err codemadness.org 70 i 8347 lot</em>.</p> Err codemadness.org 70 i 8348 <h1>What if...</h1> Err codemadness.org 70 i 8349 <p>It feels like it should be easy to just write something like</p> Err codemadness.org 70 i 8350 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 8351 <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">ptr</span><span class="p">;</span> Err codemadness.org 70 i 8352 <span class="kt">size_t</span> <span class="n">len</span><span class="p">;</span> Err codemadness.org 70 i 8353 <span class="p">}</span> <span class="n">StringSlice</span><span class="p">;</span> Err codemadness.org 70 i 8354 </code></pre></div> Err codemadness.org 70 i 8355 Err codemadness.org 70 i 8356 <p>And then a whole family of functions. The starting point, where you Err codemadness.org 70 i 8357 slice a whole string:</p> Err codemadness.org 70 i 8358 <div class="highlight"><pre><span></span><code><span class="n">StringSlice</span> Err codemadness.org 70 i 8359 <span class="nf">make_slice_from_string</span> <span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">)</span> Err codemadness.org 70 i 8360 <span class="p">{</span> Err codemadness.org 70 i 8361 <span class="n">StringSlice</span> <span class="n">slice</span><span class="p">;</span> Err codemadness.org 70 i 8362 Err codemadness.org 70 i 8363 <span class="n">assert</span> <span class="p">(</span><span class="n">s</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> Err codemadness.org 70 i 8364 Err codemadness.org 70 i 8365 <span class="n">slice</span><span class="p">.</span><span class="n">ptr</span> <span class="o">=</span> <span class="n">s</span><span class="p">;</span> Err codemadness.org 70 i 8366 <span class="n">slice</span><span class="p">.</span><span class="n">len</span> <span class="o">=</span> <span class="n">strlen</span> <span class="p">(</span><span class="n">s</span><span class="p">);</span> Err codemadness.org 70 i 8367 <span class="k">return</span> <span class="n">slice</span><span class="p">;</span> Err codemadness.org 70 i 8368 <span class="p">}</span> Err codemadness.org 70 i 8369 </code></pre></div> Err codemadness.org 70 i 8370 Err codemadness.org 70 i 8371 <p>But that wouldn't keep track of the lifetime of the original string. Err codemadness.org 70 i 8372 Okay, this is C, so you are used to keeping track of that yourself.</p> Err codemadness.org 70 i 8373 <p>Onwards. Substrings?</p> Err codemadness.org 70 i 8374 <div class="highlight"><pre><span></span><code><span class="n">StringSlice</span> Err codemadness.org 70 i 8375 <span class="nf">make_sub_slice</span><span class="p">(</span><span class="n">StringSlice</span> <span class="n">slice</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">start</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> Err codemadness.org 70 i 8376 <span class="p">{</span> Err codemadness.org 70 i 8377 <span class="n">StringSlice</span> <span class="n">sub</span><span class="p">;</span> Err codemadness.org 70 i 8378 Err codemadness.org 70 i 8379 <span class="n">assert</span> <span class="p">(</span><span class="n">len</span> <span class="o">&lt;=</span> <span class="n">slice</span><span class="p">.</span><span class="n">len</span><span class="p">);</span> Err codemadness.org 70 i 8380 <span class="n">assert</span> <span class="p">(</span><span class="n">start</span> <span class="o">&lt;=</span> <span class="n">slice</span><span class="p">.</span><span class="n">len</span> <span class="o">-</span> <span class="n">len</span><span class="p">);</span> <span class="cm">/* Not &quot;start + len &lt;= slice.len&quot; or it can overflow. */</span> Err codemadness.org 70 i 8381 <span class="cm">/* The subtraction can&#39;t underflow because of the previous assert */</span> Err codemadness.org 70 i 8382 <span class="n">sub</span><span class="p">.</span><span class="n">ptr</span> <span class="o">=</span> <span class="n">slice</span><span class="p">.</span><span class="n">ptr</span> <span class="o">+</span> <span class="n">start</span><span class="p">;</span> Err codemadness.org 70 i 8383 <span class="n">sub</span><span class="p">.</span><span class="n">len</span> <span class="o">=</span> <span class="n">len</span><span class="p">;</span> Err codemadness.org 70 i 8384 <span class="k">return</span> <span class="n">sub</span><span class="p">;</span> Err codemadness.org 70 i 8385 <span class="p">}</span> Err codemadness.org 70 i 8386 </code></pre></div> Err codemadness.org 70 i 8387 Err codemadness.org 70 i 8388 <p>Then you could write a million wrappers for <code>g_strsplit()</code> and Err codemadness.org 70 i 8389 friends, or equivalents to them, to give you slices instead of C Err codemadness.org 70 i 8390 strings. But then:</p> Err codemadness.org 70 i 8391 <ul> Err codemadness.org 70 i 8392 <li> Err codemadness.org 70 i 8393 <p>You have to keep track of lifetimes yourself.</p> Err codemadness.org 70 i 8394 </li> Err codemadness.org 70 i 8395 <li> Err codemadness.org 70 i 8396 <p>You have to wrap every function that returns a plain "<code>char *</code>"...</p> Err codemadness.org 70 i 8397 </li> Err codemadness.org 70 i 8398 <li> Err codemadness.org 70 i 8399 <p>... and every function that takes a plain "<code>char *</code>" as an argument, Err codemadness.org 70 i 8400 without a length parameter, because...</p> Err codemadness.org 70 i 8401 </li> Err codemadness.org 70 i 8402 <li> Err codemadness.org 70 i 8403 <p>You <strong>CANNOT</strong> take <code>slice.ptr</code> and pass it to a function that just Err codemadness.org 70 i 8404 expects a plain "<code>char *</code>", because your slice does not include a Err codemadness.org 70 i 8405 nul terminator (the <code>'\0</code> byte at the end of a C string). This is Err codemadness.org 70 i 8406 what kills the whole plan.</p> Err codemadness.org 70 i 8407 </li> Err codemadness.org 70 i 8408 </ul> Err codemadness.org 70 i 8409 <p>Even if you had a helper library that implements C string slices Err codemadness.org 70 i 8410 like that, you would have a mismatch every time you needed to call a C Err codemadness.org 70 i 8411 function that expects a conventional C string in the form of a Err codemadness.org 70 i 8412 "<code>char *</code>". <em>You need to put a nul terminator somewhere</em>, and if you Err codemadness.org 70 i 8413 only have a slice, you need to <em>allocate memory</em>, copy the slice into Err codemadness.org 70 i 8414 it, and slap a 0 byte at the end. <em>Then</em> you can pass that to a Err codemadness.org 70 i 8415 function that expects a normal C string.</p> Err codemadness.org 70 i 8416 <p>There is hacky C code that needs to pass a substring to another Err codemadness.org 70 i 8417 function, so it <em>overwrites the byte after the substring with a 0</em>, Err codemadness.org 70 i 8418 passes the substring, and <em>overwrites the byte back</em>. This is Err codemadness.org 70 i 8419 horrible, and doesn't work with strings that live in read-only Err codemadness.org 70 i 8420 memory. But that's the best that C lets you do.</p> Err codemadness.org 70 i 8421 <p>I'm very happy with string slices in Rust, which work exactly like the Err codemadness.org 70 i 8422 <code>StringSlice</code> above, but <code>&amp;str</code> is actually at the language level and Err codemadness.org 70 i 8423 everything knows how to handle it.</p> Err codemadness.org 70 i 8424 <p>The <code>glib-rs</code> crate has conversion traits to go from Rust strings or Err codemadness.org 70 i 8425 slices into C, and vice-versa. We alredy saw some of those in the Err codemadness.org 70 i 8426 blog post about <a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html#pointer-types">conversions in Glib-rs</a>.</p> Err codemadness.org 70 i 8427 <h1>Sizes of things</h1> Err codemadness.org 70 i 8428 <p>Rust uses <code>usize</code> to specify the size of things; it's an unsigned Err codemadness.org 70 i 8429 integer; 32 bits on 32-bit machines, and 64 bits on 64-bit machines; Err codemadness.org 70 i 8430 it's like C's <code>size_t</code>.</p> Err codemadness.org 70 i 8431 <p>In the Glib/C world, we have an assortment of types to represent the Err codemadness.org 70 i 8432 sizes of things:</p> Err codemadness.org 70 i 8433 <ul> Err codemadness.org 70 i 8434 <li> Err codemadness.org 70 i 8435 <p><code>gsize</code>, the same as <code>size_t</code>. This is an unsigned integer; it's okay.</p> Err codemadness.org 70 i 8436 </li> Err codemadness.org 70 i 8437 <li> Err codemadness.org 70 i 8438 <p><code>gssize</code>, a signed integer of the same size as <code>gsize</code>. This is Err codemadness.org 70 i 8439 okay if used to represent a negative offset, and <em>really funky</em> in Err codemadness.org 70 i 8440 the Glib functions like Err codemadness.org 70 i 8441 <code>g_string_new_len (const char *str, gssize len)</code>, where <code>len == -1</code> Err codemadness.org 70 i 8442 means "call <code>strlen(str)</code> for me because I'm too lazy to compute the Err codemadness.org 70 i 8443 length myself".</p> Err codemadness.org 70 i 8444 </li> Err codemadness.org 70 i 8445 <li> Err codemadness.org 70 i 8446 <p><code>int</code> - broken, as in libxml2, but we can't change the API. On Err codemadness.org 70 i 8447 64-bit machines, an <code>int</code> to specify a length means you can't pass Err codemadness.org 70 i 8448 objects bigger than 2 GB.</p> Err codemadness.org 70 i 8449 </li> Err codemadness.org 70 i 8450 <li> Err codemadness.org 70 i 8451 <p><code>long</code> - marginally better than <code>int</code>, since it has a better chance Err codemadness.org 70 i 8452 of actually being the same size as <code>size_t</code>, but still funky. Err codemadness.org 70 i 8453 Probably okay for negative offsets; problematic for sizes which Err codemadness.org 70 i 8454 should really be unsigned.</p> Err codemadness.org 70 i 8455 </li> Err codemadness.org 70 i 8456 <li> Err codemadness.org 70 i 8457 <p>etc.</p> Err codemadness.org 70 i 8458 </li> Err codemadness.org 70 i 8459 </ul> Err codemadness.org 70 i 8460 <p>I'm not sure how old <code>size_t</code> is in the C standard library, but it Err codemadness.org 70 i 8461 can't have been there since the beginning of time &mdash; otherwise Err codemadness.org 70 i 8462 people wouldn't have been using <code>int</code> to specify the sizes of things.</p>Code Hospitality2017-11-17T14:21:04-06:002017-11-17T14:21:06-06:00Federico Mena Quinterotag:people.gnome.org,2017-11-17:/~federico/blog/code-hospitality.html<p>Recently on the <a href="http://www.greaterthancode.com/">Greater than Code podcast</a> there was an episode Err codemadness.org 70 i 8463 called "<a href="http://www.greaterthancode.com/podcast/054-code-hospitality-with-nadia-odunayo/">Code Hospitality</a>", by Err codemadness.org 70 i 8464 <a href="http://www.nadiaodunayo.com/">Nadia Odunayo</a>. </p> Err codemadness.org 70 i 8465 <p>Nadia talks about thinking of how to make people comfortable in your Err codemadness.org 70 i 8466 code and in your team/organization/etc., and does it in terms of Err codemadness.org 70 i 8467 thinking about host/guest relationships. Have you ever …</p><p>Recently on the <a href="http://www.greaterthancode.com/">Greater than Code podcast</a> there was an episode Err codemadness.org 70 i 8468 called "<a href="http://www.greaterthancode.com/podcast/054-code-hospitality-with-nadia-odunayo/">Code Hospitality</a>", by Err codemadness.org 70 i 8469 <a href="http://www.nadiaodunayo.com/">Nadia Odunayo</a>. </p> Err codemadness.org 70 i 8470 <p>Nadia talks about thinking of how to make people comfortable in your Err codemadness.org 70 i 8471 code and in your team/organization/etc., and does it in terms of Err codemadness.org 70 i 8472 thinking about host/guest relationships. Have you ever stayed in an Err codemadness.org 70 i 8473 AirBnB where the host carefully prepares some "welcome instructions" Err codemadness.org 70 i 8474 for you, or puts little notes in their apartment to orient/guide you, Err codemadness.org 70 i 8475 or gives you basic guidance around their city's transportation system? Err codemadness.org 70 i 8476 We can think in similar ways of how to make people comfortable with Err codemadness.org 70 i 8477 code bases.</p> Err codemadness.org 70 i 8478 <p>This of course hit me on so many levels, because in the past I've Err codemadness.org 70 i 8479 written about analogies between software and urbanism/architecture. Err codemadness.org 70 i 8480 <a href="https://people.gnome.org/~federico/docs/software-with-qwan/index.html">Software that has the Quality Without A Name</a> Err codemadness.org 70 i 8481 talks about Christopher Alexander's architecture/urbanism patterns in Err codemadness.org 70 i 8482 the context of software, based on <a href="http://dreamsongs.com/">Richard Gabriel</a>'s ideas, Err codemadness.org 70 i 8483 and <a href="http://zeta.math.utsa.edu/~yxk833/">Nikos Salingaros</a>'s formalization of the design Err codemadness.org 70 i 8484 process. <a href="https://people.gnome.org/~federico/blog/legacy-systems-as-old-cities.html">Legacy Systems as Old Cities</a> talks about Err codemadness.org 70 i 8485 how GNOME evolved parts of its user-visible software, and makes an Err codemadness.org 70 i 8486 analogy with cities that evolve over time instead of being torn down Err codemadness.org 70 i 8487 and rebuilt, based on urbanism ideas by Jane Jacobs, and Err codemadness.org 70 i 8488 architecture/construction ideas by Stewart Brand.</p> Err codemadness.org 70 i 8489 <p>I definitely intend to do some thinking on Nadia's ideas for Code Err codemadness.org 70 i 8490 Hospitality and try to connect them with this.</p> Err codemadness.org 70 i 8491 <p>In the meantime, I've just rewritten the <a href="https://github.com/federicomenaquintero/gnome-class/blob/master/README.md">README in Err codemadness.org 70 i 8492 gnome-class</a> to make it suitable as an Err codemadness.org 70 i 8493 introduction to hacking there.</p>Rust+GNOME Hackfest in Berlin, 20172017-11-16T18:33:58-06:002017-11-16T18:34:01-06:00Federico Mena Quinterotag:people.gnome.org,2017-11-16:/~federico/blog/rust-gnome-hackfest-berlin.html<p>Last weekend I was in Berlin for the <a href="https://wiki.gnome.org/Hackfests/Rust2017-2">second Rust+GNOME Err codemadness.org 70 i 8494 Hackfest</a>, kindly hosted at the <a href="https://kinvolk.io/">Kinvolk</a> office. Err codemadness.org 70 i 8495 This is in a <em>great</em> location, half a block away from the <a href="https://www.openstreetmap.org/node/3900136339">Kottbusser Err codemadness.org 70 i 8496 Tor</a> station, right at the entrance of the trendy Kreuzberg Err codemadness.org 70 i 8497 neighborhood — full of interesting people, incredible graffitti, and Err codemadness.org 70 i 8498 good …</p><p>Last weekend I was in Berlin for the <a href="https://wiki.gnome.org/Hackfests/Rust2017-2">second Rust+GNOME Err codemadness.org 70 i 8499 Hackfest</a>, kindly hosted at the <a href="https://kinvolk.io/">Kinvolk</a> office. Err codemadness.org 70 i 8500 This is in a <em>great</em> location, half a block away from the <a href="https://www.openstreetmap.org/node/3900136339">Kottbusser Err codemadness.org 70 i 8501 Tor</a> station, right at the entrance of the trendy Kreuzberg Err codemadness.org 70 i 8502 neighborhood — full of interesting people, incredible graffitti, and Err codemadness.org 70 i 8503 good, diverse food.</p> Err codemadness.org 70 i 8504 <p><a href="https://people.gnome.org/~federico/blog/images/kottbusser.jpg"><img alt="Rug of Kottbusser Tor" src="https://people.gnome.org/~federico/blog/images/kottbusser-thumb.jpg"></a></p> Err codemadness.org 70 i 8505 <h1>My goals for the hackfest</h1> Err codemadness.org 70 i 8506 <p>Over the past weeks I had been converting <a href="https://github.com/federicomenaquintero/gnome-class">gnome-class</a> Err codemadness.org 70 i 8507 from the old <a href="https://github.com/nikomatsakis/lalrpop/">lalrpop</a>-based parser into the new Procedural Err codemadness.org 70 i 8508 Macros framework for Rust, or <code>proc-macro2</code> for short. To do this the Err codemadness.org 70 i 8509 parser for the gnome-class mini-language needs to be rewritten from Err codemadness.org 70 i 8510 being specified in a lalrpop grammar, to using Rust's <a href="https://github.com/dtolnay/syn/">syn</a> Err codemadness.org 70 i 8511 crate.</p> Err codemadness.org 70 i 8512 <p>Syn is a parser for Rust source code, written as a set of <a href="https://github.com/geal/nom">nom</a> Err codemadness.org 70 i 8513 combinator parser macros. For gnome-class we want to extend the Rust Err codemadness.org 70 i 8514 language with a few conveniences to be able to specify GObject Err codemadness.org 70 i 8515 classes/subclasses, methods, signals, properties, interfaces, and all Err codemadness.org 70 i 8516 the goodies that GObject Introspection would expect.</p> Err codemadness.org 70 i 8517 <p>During the hackfest, <a href="https://github.com/alexcrichton/">Alex Crichton</a>, from the Rust core team, Err codemadness.org 70 i 8518 kindly took over my baby steps in compiler writing and made everything Err codemadness.org 70 i 8519 much more functional. It was invaluable to have him there to reason Err codemadness.org 70 i 8520 about macro hygiene (we <em>are</em> generating an unhygienic macro!), bugs Err codemadness.org 70 i 8521 in the quoting system, and general Rust-iness of the whole thing.</p> Err codemadness.org 70 i 8522 <p>I was also able to talk to <a href="https://coaxion.net/blog/">Sebastian Dröge</a> about his work in Err codemadness.org 70 i 8523 writing GObjects in Rust by hand, for GStreamer, and what sort of Err codemadness.org 70 i 8524 things gnome-class could make easier. Sebastian knows GObject very Err codemadness.org 70 i 8525 well, and has been doing awesome work in making it easy to derive Err codemadness.org 70 i 8526 GObjects by hand in Rust, without lots of boilerplate — something with Err codemadness.org 70 i 8527 which gnome-class can certainly help.</p> Err codemadness.org 70 i 8528 <p>I was also looking forward to talking again with <a href="https://github.com/GuillaumeGomez">Guillaume Err codemadness.org 70 i 8529 Gomez</a>, one of the maintainers of <a href="http://gtk-rs.org/">gtk-rs</a>, and who Err codemadness.org 70 i 8530 does so much work in the Rust ecosystem that I can't believe he has Err codemadness.org 70 i 8531 time for it all.</p> Err codemadness.org 70 i 8532 <p><img alt="Graffitti heads" src="https://people.gnome.org/~federico/blog/images/graffitti-heads.jpg"></p> Err codemadness.org 70 i 8533 <h1>Extend the Rust language for GObject? Like Vala?</h1> Err codemadness.org 70 i 8534 <p>Yeah, pretty much.</p> Err codemadness.org 70 i 8535 <p>Except that instead of a wholly new language, we use Rust as-is, and Err codemadness.org 70 i 8536 we just add syntactic constructs that make it easy to write GObjects Err codemadness.org 70 i 8537 without boilerplate. For example, this works right now:</p> Err codemadness.org 70 i 8538 <div class="highlight"><pre><span></span><code><span class="cp">#![feature(proc_macro)]</span><span class="w"></span> Err codemadness.org 70 i 8539 Err codemadness.org 70 i 8540 <span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">gobject_gen</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8541 Err codemadness.org 70 i 8542 <span class="cp">#[macro_use]</span><span class="w"></span> Err codemadness.org 70 i 8543 <span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">glib</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8544 <span class="k">use</span><span class="w"> </span><span class="n">gobject_gen</span>::<span class="n">gobject_gen</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8545 Err codemadness.org 70 i 8546 <span class="n">gobject_gen</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8547 <span class="w"> </span><span class="c1">// Derives from GObject</span> Err codemadness.org 70 i 8548 <span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="n">One</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8549 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8550 Err codemadness.org 70 i 8551 <span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">One</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8552 <span class="w"> </span><span class="c1">// non-virtual method</span> Err codemadness.org 70 i 8553 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">one</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">u32</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8554 <span class="w"> </span><span class="mi">1</span><span class="w"></span> Err codemadness.org 70 i 8555 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8556 Err codemadness.org 70 i 8557 <span class="w"> </span><span class="kr">virtual</span><span class="w"> </span><span class="k">fn</span> <span class="nf">get</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">u32</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8558 <span class="w"> </span><span class="mi">1</span><span class="w"></span> Err codemadness.org 70 i 8559 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8560 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8561 Err codemadness.org 70 i 8562 <span class="w"> </span><span class="c1">// Inherits from our other class</span> Err codemadness.org 70 i 8563 <span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="n">Two</span>: <span class="nc">One</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8564 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8565 Err codemadness.org 70 i 8566 <span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">One</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Two</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8567 <span class="w"> </span><span class="c1">// overrides the virtual method</span> Err codemadness.org 70 i 8568 <span class="w"> </span><span class="c1">// maybe we should use &quot;override&quot; instead of &quot;virtual&quot; here?</span> Err codemadness.org 70 i 8569 <span class="w"> </span><span class="kr">virtual</span><span class="w"> </span><span class="k">fn</span> <span class="nf">get</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">u32</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8570 <span class="w"> </span><span class="mi">2</span><span class="w"></span> Err codemadness.org 70 i 8571 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8572 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8573 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8574 Err codemadness.org 70 i 8575 <span class="cp">#[test]</span><span class="w"></span> Err codemadness.org 70 i 8576 <span class="k">fn</span> <span class="nf">test</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8577 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">One</span>::<span class="n">new</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 8578 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">two</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Two</span>::<span class="n">new</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 8579 Err codemadness.org 70 i 8580 <span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="n">one</span><span class="p">.</span><span class="n">one</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8581 <span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="n">one</span><span class="p">.</span><span class="n">get</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8582 <span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="n">two</span><span class="p">.</span><span class="n">one</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8583 <span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="n">two</span><span class="p">.</span><span class="n">get</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">2</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8584 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8585 </code></pre></div> Err codemadness.org 70 i 8586 Err codemadness.org 70 i 8587 <p>This generates a little boatload of <a href="https://people.gnome.org/~federico/blog/docs/generated.rs">generated code</a>, Err codemadness.org 70 i 8588 including a good number of <code>unsafe</code> calls to GObject functions Err codemadness.org 70 i 8589 like <code>g_type_register_static_simple()</code>. It also creates all the Err codemadness.org 70 i 8590 traits and paraphernalia that Glib-rs would create for the Rust Err codemadness.org 70 i 8591 binding of a normal GObject written in C.</p> Err codemadness.org 70 i 8592 <p>The idea is that from the outside world, your generated GObject Err codemadness.org 70 i 8593 classes are indistinguishable from GObjects implemented in C.</p> Err codemadness.org 70 i 8594 <p><em>The idea is to write GObject libraries in a better language than C, Err codemadness.org 70 i 8595 which can then be consumed from language bindings.</em></p> Err codemadness.org 70 i 8596 <h2>Current status of gnome-class</h2> Err codemadness.org 70 i 8597 <p>Up to about two weeks before the hackfest, the syntax for this Err codemadness.org 70 i 8598 mini-language was totally ad-hoc and limited. After a very productive Err codemadness.org 70 i 8599 <a href="https://mail.gnome.org/archives/rust-list/2017-October/msg00000.html">discussion on the mailing list</a>, we came up with a better Err codemadness.org 70 i 8600 syntax that definitely looks more Rust-like. It is also easier to Err codemadness.org 70 i 8601 implement, since the Rust parser in syn can be mostly reused as-is, or Err codemadness.org 70 i 8602 pruned down for the parts where we only support GObject-like methods, Err codemadness.org 70 i 8603 and not all the Rust bells and whistles (generics, lifetimes, trait Err codemadness.org 70 i 8604 bounds).</p> Err codemadness.org 70 i 8605 <p>Gnome-class supports deriving classes directly from the basic GObject, Err codemadness.org 70 i 8606 or from other GObject subclasses in the style of <a href="https://github.com/gtk-rs/glib">glib-rs</a>.</p> Err codemadness.org 70 i 8607 <p>You can define virtual and non-virtual methods. You can override Err codemadness.org 70 i 8608 virtual methods from your superclasses.</p> Err codemadness.org 70 i 8609 <p>Not all argument types are supported. In the end we should support Err codemadness.org 70 i 8610 argument types which are convertible from Rust to C types. We need to Err codemadness.org 70 i 8611 finish figuring out the annotations for ownership transfer of Err codemadness.org 70 i 8612 references.</p> Err codemadness.org 70 i 8613 <p>We don't support GObject signals yet; I think that's my next task.</p> Err codemadness.org 70 i 8614 <p>We don't support GObject properties yet.</p> Err codemadness.org 70 i 8615 <p>We don't support defining new GType interfaces yet, but it is planned. Err codemadness.org 70 i 8616 It should be easy to support implementing existing interfaces, as it Err codemadness.org 70 i 8617 is pretty much the same as implementing a subclass.</p> Err codemadness.org 70 i 8618 <p>The best way to see what works right now is probably to <a href="https://github.com/federicomenaquintero/gnome-class/tree/master/tests">look at the Err codemadness.org 70 i 8619 examples</a>, which also work as tests.</p> Err codemadness.org 70 i 8620 <h1>Digression on macro hygiene</h1> Err codemadness.org 70 i 8621 <p>Rust macros are <em>hygienic</em>, unlike C macros which work just through Err codemadness.org 70 i 8622 textual substitution. That is, names declared inside Rust macros will Err codemadness.org 70 i 8623 not clash with names in the calling code.</p> Err codemadness.org 70 i 8624 <p>One peculiar thing about gnome-class is that the user gives us a few Err codemadness.org 70 i 8625 names, like a class name <code>Foo</code> and some things inside it, say, a Err codemadness.org 70 i 8626 method name <code>bar</code>, and a signal <code>baz</code> and a property <code>qux</code>. Err codemadness.org 70 i 8627 From there we want to generate a bunch of boilerplate for GObject Err codemadness.org 70 i 8628 registration and implementaiton. Some of the generated names in that Err codemadness.org 70 i 8629 boilerplate would be</p> Err codemadness.org 70 i 8630 <div class="highlight"><pre><span></span><code><span class="n">Foo</span> <span class="o">//</span> <span class="n">base</span> <span class="n">name</span> Err codemadness.org 70 i 8631 <span class="n">FooClass</span> <span class="o">//</span> <span class="n">generated</span> <span class="n">name</span> <span class="k">for</span> <span class="n">the</span> <span class="k">class</span> <span class="n">struct</span> Err codemadness.org 70 i 8632 <span class="n">Foo</span><span class="p">::</span><span class="n">bar</span><span class="p">()</span> <span class="o">//</span> <span class="n">A</span> <span class="n">method</span> Err codemadness.org 70 i 8633 <span class="n">Foo</span><span class="p">::</span><span class="n">emit_baz</span><span class="p">()</span> <span class="o">//</span> <span class="n">Generated</span> <span class="n">from</span> <span class="n">the</span> <span class="k">signal</span> <span class="n">name</span> Err codemadness.org 70 i 8634 <span class="n">Foo</span><span class="p">::</span><span class="n">set_qux</span><span class="p">()</span> <span class="o">//</span> <span class="n">Generated</span> <span class="n">property</span> <span class="n">setter</span> Err codemadness.org 70 i 8635 <span class="n">foo_bar</span><span class="p">()</span> <span class="o">//</span> <span class="n">Generated</span> <span class="n">C</span> <span class="n">function</span> <span class="k">for</span> <span class="n">a</span> <span class="n">method</span> <span class="n">call</span> Err codemadness.org 70 i 8636 <span class="n">foo_get_type</span><span class="p">()</span> <span class="o">//</span> <span class="n">Generated</span> <span class="n">C</span> <span class="n">function</span> <span class="n">that</span> <span class="n">all</span> <span class="n">GObjects</span> <span class="n">have</span> Err codemadness.org 70 i 8637 </code></pre></div> Err codemadness.org 70 i 8638 Err codemadness.org 70 i 8639 <p>However, if we want to actually generate those names inside our Err codemadness.org 70 i 8640 gnome-class macro <em>and make them visible to the caller</em>, we need to do Err codemadness.org 70 i 8641 so <em>unhygienically</em>. Alex started started a <a href="https://github.com/rust-lang/rust/issues/45934">very interesting discussion Err codemadness.org 70 i 8642 on macro hygiene</a>, so expect some news in the Rust world Err codemadness.org 70 i 8643 soon.</p> Err codemadness.org 70 i 8644 <p>TL;DR: there is a difference between a <em>code generator</em>, which Err codemadness.org 70 i 8645 gnome-class mostly intends to be, and a <em>macro system</em> which is just Err codemadness.org 70 i 8646 an aid in typing repetitive code.</p> Err codemadness.org 70 i 8647 <p><img alt="Fuck wars" src="https://people.gnome.org/~federico/blog/images/fuck-wars.jpg"></p> Err codemadness.org 70 i 8648 <h1>People for whom to to be thankful</h1> Err codemadness.org 70 i 8649 <p>During the hackfest, <a href="http://blog.nirbheek.in/">Nirbheek</a> has been porting librsvg Err codemadness.org 70 i 8650 from Autotools to the Meson build system, and dealing with Rust Err codemadness.org 70 i 8651 peculiarities along the way. This is exactly what I needed! Thanks, Err codemadness.org 70 i 8652 Nirbheek!</p> Err codemadness.org 70 i 8653 <p><a href="https://coaxion.net/blog/">Sebastian</a> answered many of my questions about GObject Err codemadness.org 70 i 8654 internals and how to use them from the Rust side.</p> Err codemadness.org 70 i 8655 <p><a href="http://zee-nix.blogspot.com/">Zeeshan</a> took us to a bunch of good restaurants. Korean, Err codemadness.org 70 i 8656 ramen, Greek, excellent pizza... My stomach is definitely thankful.</p> Err codemadness.org 70 i 8657 <h1>Berlin</h1> Err codemadness.org 70 i 8658 <p>I love Berlin. It is a cosmopolitan, progressive, LGBTQ-friendly Err codemadness.org 70 i 8659 city, with lots of things to do, vast distances to be traveled, with Err codemadness.org 70 i 8660 good public transport and bike lanes, diverse food to be eaten along Err codemadness.org 70 i 8661 the way...</p> Err codemadness.org 70 i 8662 <p>But damnit, it's also cold at this time of the year. I don't think Err codemadness.org 70 i 8663 the weather was ever above 10°C while we were there, and mostly in a Err codemadness.org 70 i 8664 constant state of not-quite-rain. This is much different from the Berlin Err codemadness.org 70 i 8665 in the summer that I knew!</p> Err codemadness.org 70 i 8666 <p><a href="https://people.gnome.org/~federico/blog/images/kimchi.jpg"><img alt="Hackers at Kimchi Princess" src="https://people.gnome.org/~federico/blog/images/kimchi-thumb.jpg"></a></p> Err codemadness.org 70 i 8667 <p>This is my third time visiting Berlin. The first one was during the Err codemadness.org 70 i 8668 Desktop Summit in 2011, and the second one was when my family and I Err codemadness.org 70 i 8669 visited the city two years ago. It is a city that I would definitely Err codemadness.org 70 i 8670 like to know better.</p> Err codemadness.org 70 i 8671 <h1>Thanks to the GNOME Foundation...</h1> Err codemadness.org 70 i 8672 <p>... for sponsoring my travel and accomodation during the hackfest.</p> Err codemadness.org 70 i 8673 <p><img alt="Sponsored by the GNOME Foundation" src="https://people.gnome.org/~federico/blog/images/sponsored-badge-shadow.png"></p>Compilation notifications in Emacs2017-11-07T10:47:52-06:002017-11-07T10:47:52-06:00Federico Mena Quinterotag:people.gnome.org,2017-11-07:/~federico/blog/compilation-notifications-in-emacs.html<p>Here is a little Emacs Lisp snippet that I've started using. It makes Err codemadness.org 70 i 8674 Emacs pop up a desktop-wide notification when a compilation finishes, Err codemadness.org 70 i 8675 i.e. after "<code>M-x compile</code>" is done. Let's see if that keeps me from Err codemadness.org 70 i 8676 wasting time in the web when I launch a compilation.</p> Err codemadness.org 70 i 8677 <div class="highlight"><pre><span></span><code><span class="p">(</span><span class="k">setq</span> <span class="nv">compilation-finish-functions</span> Err codemadness.org 70 i 8678 <span class="p">(</span><span class="nb">append …</span></code></pre></div><p>Here is a little Emacs Lisp snippet that I've started using. It makes Err codemadness.org 70 i 8679 Emacs pop up a desktop-wide notification when a compilation finishes, Err codemadness.org 70 i 8680 i.e. after "<code>M-x compile</code>" is done. Let's see if that keeps me from Err codemadness.org 70 i 8681 wasting time in the web when I launch a compilation.</p> Err codemadness.org 70 i 8682 <div class="highlight"><pre><span></span><code><span class="p">(</span><span class="k">setq</span> <span class="nv">compilation-finish-functions</span> Err codemadness.org 70 i 8683 <span class="p">(</span><span class="nb">append</span> <span class="nv">compilation-finish-functions</span> Err codemadness.org 70 i 8684 <span class="o">&#39;</span><span class="p">(</span><span class="nv">fmq-compilation-finish</span><span class="p">)))</span> Err codemadness.org 70 i 8685 Err codemadness.org 70 i 8686 <span class="p">(</span><span class="nb">defun</span> <span class="nv">fmq-compilation-finish</span> <span class="p">(</span><span class="nv">buffer</span> <span class="nv">status</span><span class="p">)</span> Err codemadness.org 70 i 8687 <span class="p">(</span><span class="nv">call-process</span> <span class="s">&quot;notify-send&quot;</span> <span class="no">nil</span> <span class="no">nil</span> <span class="no">nil</span> Err codemadness.org 70 i 8688 <span class="s">&quot;-t&quot;</span> <span class="s">&quot;0&quot;</span> Err codemadness.org 70 i 8689 <span class="s">&quot;-i&quot;</span> <span class="s">&quot;emacs&quot;</span> Err codemadness.org 70 i 8690 <span class="s">&quot;Compilation finished in Emacs&quot;</span> Err codemadness.org 70 i 8691 <span class="nv">status</span><span class="p">))</span> Err codemadness.org 70 i 8692 </code></pre></div>How glib-rs works, part 3: Boxed types2017-09-08T19:28:11-05:002017-09-08T19:28:11-05:00Federico Mena Quinterotag:people.gnome.org,2017-09-08:/~federico/blog/how-glib-rs-works-part-3.html<p>(<a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">First part</a> of the series, with index to all the articles)</p> Err codemadness.org 70 i 8693 <p>Now let's get on and see how glib-rs handles boxed types.</p> Err codemadness.org 70 i 8694 <h1>Boxed types?</h1> Err codemadness.org 70 i 8695 <p>Let's say you are given a sealed cardboard box with <em>something</em>, but you Err codemadness.org 70 i 8696 can't know what's inside. You can just pass it on to someone else …</p><p>(<a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">First part</a> of the series, with index to all the articles)</p> Err codemadness.org 70 i 8697 <p>Now let's get on and see how glib-rs handles boxed types.</p> Err codemadness.org 70 i 8698 <h1>Boxed types?</h1> Err codemadness.org 70 i 8699 <p>Let's say you are given a sealed cardboard box with <em>something</em>, but you Err codemadness.org 70 i 8700 can't know what's inside. You can just pass it on to someone else, or Err codemadness.org 70 i 8701 burn it. And since computers are magic duplication machines, you may Err codemadness.org 70 i 8702 want to copy the box and its contents... and maybe some day you will Err codemadness.org 70 i 8703 get around to opening it.</p> Err codemadness.org 70 i 8704 <p>That's a boxed type. You get a pointer to <em>something</em>, who knows Err codemadness.org 70 i 8705 what's inside. You can just pass it on to someone else, burn it — I Err codemadness.org 70 i 8706 mean, free it — or since computers are magic, copy the pointer and Err codemadness.org 70 i 8707 whatever it points to.</p> Err codemadness.org 70 i 8708 <p>That's exactly the API for boxed types.</p> Err codemadness.org 70 i 8709 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="n">gpointer</span> <span class="p">(</span><span class="o">*</span><span class="n">GBoxedCopyFunc</span><span class="p">)</span> <span class="p">(</span><span class="n">gpointer</span> <span class="n">boxed</span><span class="p">);</span> Err codemadness.org 70 i 8710 <span class="k">typedef</span> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">GBoxedFreeFunc</span><span class="p">)</span> <span class="p">(</span><span class="n">gpointer</span> <span class="n">boxed</span><span class="p">);</span> Err codemadness.org 70 i 8711 Err codemadness.org 70 i 8712 <span class="n">GType</span> <span class="nf">g_boxed_type_register_static</span> <span class="p">(</span><span class="k">const</span> <span class="n">gchar</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> Err codemadness.org 70 i 8713 <span class="n">GBoxedCopyFunc</span> <span class="n">boxed_copy</span><span class="p">,</span> Err codemadness.org 70 i 8714 <span class="n">GBoxedFreeFunc</span> <span class="n">boxed_free</span><span class="p">);</span> Err codemadness.org 70 i 8715 </code></pre></div> Err codemadness.org 70 i 8716 Err codemadness.org 70 i 8717 <h2>Simple copying, simple freeing</h2> Err codemadness.org 70 i 8718 <p>Imagine you have a color...</p> Err codemadness.org 70 i 8719 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 8720 <span class="n">guchar</span> <span class="n">r</span><span class="p">;</span> Err codemadness.org 70 i 8721 <span class="n">guchar</span> <span class="n">g</span><span class="p">;</span> Err codemadness.org 70 i 8722 <span class="n">guchar</span> <span class="n">b</span><span class="p">;</span> Err codemadness.org 70 i 8723 <span class="p">}</span> <span class="n">Color</span><span class="p">;</span> Err codemadness.org 70 i 8724 </code></pre></div> Err codemadness.org 70 i 8725 Err codemadness.org 70 i 8726 <p>If you had a pointer to a Color, how would you copy it? Easy:</p> Err codemadness.org 70 i 8727 <div class="highlight"><pre><span></span><code><span class="n">Color</span> <span class="o">*</span><span class="nf">copy_color</span> <span class="p">(</span><span class="n">Color</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span> Err codemadness.org 70 i 8728 <span class="p">{</span> Err codemadness.org 70 i 8729 <span class="n">Color</span> <span class="o">*</span><span class="n">b</span> <span class="o">=</span> <span class="n">g_new</span> <span class="p">(</span><span class="n">Color</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> Err codemadness.org 70 i 8730 <span class="o">*</span><span class="n">b</span> <span class="o">=</span> <span class="o">*</span><span class="n">a</span><span class="p">;</span> Err codemadness.org 70 i 8731 <span class="k">return</span> <span class="n">b</span><span class="p">;</span> Err codemadness.org 70 i 8732 <span class="p">}</span> Err codemadness.org 70 i 8733 </code></pre></div> Err codemadness.org 70 i 8734 Err codemadness.org 70 i 8735 <p>That is, allocate a new <code>Color</code>, and essentially <code>memcpy()</code> the Err codemadness.org 70 i 8736 contents.</p> Err codemadness.org 70 i 8737 <p>And to free it? A simple <code>g_free()</code> works — there are no internal Err codemadness.org 70 i 8738 things that need to be freed individually.</p> Err codemadness.org 70 i 8739 <h2>Complex copying, complex freeing</h2> Err codemadness.org 70 i 8740 <p>And if we had a color with a name?</p> Err codemadness.org 70 i 8741 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 8742 <span class="n">guchar</span> <span class="n">r</span><span class="p">;</span> Err codemadness.org 70 i 8743 <span class="n">guchar</span> <span class="n">g</span><span class="p">;</span> Err codemadness.org 70 i 8744 <span class="n">guchar</span> <span class="n">b</span><span class="p">;</span> Err codemadness.org 70 i 8745 <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">;</span> Err codemadness.org 70 i 8746 <span class="p">}</span> <span class="n">ColorWithName</span><span class="p">;</span> Err codemadness.org 70 i 8747 </code></pre></div> Err codemadness.org 70 i 8748 Err codemadness.org 70 i 8749 <p>We can't just <code>*a = *b</code> here, as we actually need to copy the string Err codemadness.org 70 i 8750 <code>name</code>. Okay:</p> Err codemadness.org 70 i 8751 <div class="highlight"><pre><span></span><code><span class="n">ColorWithName</span> <span class="o">*</span><span class="nf">copy_color_with_name</span> <span class="p">(</span><span class="n">ColorWithName</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span> Err codemadness.org 70 i 8752 <span class="p">{</span> Err codemadness.org 70 i 8753 <span class="n">ColorWithName</span> <span class="o">*</span><span class="n">b</span> <span class="o">=</span> <span class="n">g_new</span> <span class="p">(</span><span class="n">ColorWithName</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> Err codemadness.org 70 i 8754 <span class="n">b</span><span class="o">-&gt;</span><span class="n">r</span> <span class="o">=</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">r</span><span class="p">;</span> Err codemadness.org 70 i 8755 <span class="n">b</span><span class="o">-&gt;</span><span class="n">g</span> <span class="o">=</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">g</span><span class="p">;</span> Err codemadness.org 70 i 8756 <span class="n">b</span><span class="o">-&gt;</span><span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">b</span><span class="p">;</span> Err codemadness.org 70 i 8757 <span class="n">b</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">=</span> <span class="n">g_strdup</span> <span class="p">(</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> Err codemadness.org 70 i 8758 <span class="k">return</span> <span class="n">b</span><span class="p">;</span> Err codemadness.org 70 i 8759 <span class="p">}</span> Err codemadness.org 70 i 8760 </code></pre></div> Err codemadness.org 70 i 8761 Err codemadness.org 70 i 8762 <p>The corresponding <code>free_color_with_name()</code> would <code>g_free(b-&gt;name)</code> and then Err codemadness.org 70 i 8763 <code>g_free(b)</code>, of course.</p> Err codemadness.org 70 i 8764 <h1>Glib-rs and boxed types</h1> Err codemadness.org 70 i 8765 <p>Let's look at this by parts. First, a <a href="https://github.com/gtk-rs/glib/blob/f0a2aae96162fd628fb0e3eedd3d3e042c25e2c4/src/boxed.rs#L265"><code>BoxedMemoryManager</code> Err codemadness.org 70 i 8766 trait</a> to define the basic API to manage the Err codemadness.org 70 i 8767 memory of boxed types. This is what defines the <code>copy</code> and <code>free</code> Err codemadness.org 70 i 8768 functions, like above.</p> Err codemadness.org 70 i 8769 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">BoxedMemoryManager</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span>: <span class="o">&#39;</span><span class="nb">static</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8770 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">copy</span><span class="p">(</span><span class="n">ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">T</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">T</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8771 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">free</span><span class="p">(</span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">T</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8772 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8773 </code></pre></div> Err codemadness.org 70 i 8774 Err codemadness.org 70 i 8775 <p>Second, the <a href="https://github.com/gtk-rs/glib/blob/f0a2aae96162fd628fb0e3eedd3d3e042c25e2c4/src/boxed.rs#L273">actual representation</a> of a <code>Boxed</code> type:</p> Err codemadness.org 70 i 8776 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Boxed</span><span class="o">&lt;</span><span class="n">T</span>: <span class="o">&#39;</span><span class="nb">static</span><span class="p">,</span><span class="w"> </span><span class="n">MM</span>: <span class="nc">BoxedMemoryManager</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8777 <span class="w"> </span><span class="n">inner</span>: <span class="nc">AnyBox</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 8778 <span class="w"> </span><span class="n">_dummy</span>: <span class="nc">PhantomData</span><span class="o">&lt;</span><span class="n">MM</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 8779 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8780 </code></pre></div> Err codemadness.org 70 i 8781 Err codemadness.org 70 i 8782 <p>This struct is generic over <code>T</code>, the actual type that we will be Err codemadness.org 70 i 8783 wrapping, and <code>MM</code>, something which must implement the Err codemadness.org 70 i 8784 <code>BoxedMemoryManager</code> trait.</p> Err codemadness.org 70 i 8785 <p>Inside, it stores <code>inner</code>, an <code>AnyBox</code>, which we will see shortly. Err codemadness.org 70 i 8786 The <code>_dummy: PhantomData&lt;MM&gt;</code> is a <a href="https://doc.rust-lang.org/std/marker/struct.PhantomData.html">Rust-ism</a> to indicate that although this Err codemadness.org 70 i 8787 struct doesn't actually store a memory manager, it acts as if it does Err codemadness.org 70 i 8788 — it does not concern us here.</p> Err codemadness.org 70 i 8789 <h2>The <em>actual</em> representation of boxed data</h2> Err codemadness.org 70 i 8790 <p>Let's look at that <a href="https://github.com/gtk-rs/glib/blob/f0a2aae96162fd628fb0e3eedd3d3e042c25e2c4/src/boxed.rs#L247"><code>AnyBox</code></a> that is stored inside a <code>Boxed</code>:</p> Err codemadness.org 70 i 8791 <div class="highlight"><pre><span></span><code><span class="k">enum</span> <span class="nc">AnyBox</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8792 <span class="w"> </span><span class="n">Native</span><span class="p">(</span><span class="nb">Box</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 8793 <span class="w"> </span><span class="n">ForeignOwned</span><span class="p">(</span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">T</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 8794 <span class="w"> </span><span class="n">ForeignBorrowed</span><span class="p">(</span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">T</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 8795 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8796 </code></pre></div> Err codemadness.org 70 i 8797 Err codemadness.org 70 i 8798 <p>We have three cases:</p> Err codemadness.org 70 i 8799 <ul> Err codemadness.org 70 i 8800 <li> Err codemadness.org 70 i 8801 <p><code>Native(Box&lt;T&gt;)</code> - this boxed value <code>T</code> comes from Rust itself, so we Err codemadness.org 70 i 8802 know everything about it!</p> Err codemadness.org 70 i 8803 </li> Err codemadness.org 70 i 8804 <li> Err codemadness.org 70 i 8805 <p><code>ForeignOwned(*mut T)</code> - this boxed value <code>T</code> came from the outside, but Err codemadness.org 70 i 8806 we own it now. We will have to free it when we are done with it.</p> Err codemadness.org 70 i 8807 </li> Err codemadness.org 70 i 8808 <li> Err codemadness.org 70 i 8809 <p><code>ForeignBorrowed(*mut T)</code> - this boxed value <code>T</code> came from the Err codemadness.org 70 i 8810 outside, but we are just borrowing it temporarily: we <strong>don't</strong> want to Err codemadness.org 70 i 8811 free it when we are done with it.</p> Err codemadness.org 70 i 8812 </li> Err codemadness.org 70 i 8813 </ul> Err codemadness.org 70 i 8814 <p>For example, if we look at the <a href="https://github.com/gtk-rs/glib/blob/f0a2aae96162fd628fb0e3eedd3d3e042c25e2c4/src/boxed.rs#L367">implementation of the <code>Drop</code> Err codemadness.org 70 i 8815 trait</a> for the <code>Boxed</code> struct, we will indeed see that it Err codemadness.org 70 i 8816 calls the <code>BoxedMemoryManager::free()</code> <strong>only</strong> if we have a Err codemadness.org 70 i 8817 <code>ForeignOwned</code> value:</p> Err codemadness.org 70 i 8818 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span>: <span class="o">&#39;</span><span class="nb">static</span><span class="p">,</span><span class="w"> </span><span class="n">MM</span>: <span class="nc">BoxedMemoryManager</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nb">Drop</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Boxed</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">MM</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8819 <span class="w"> </span><span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8820 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8821 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">AnyBox</span>::<span class="n">ForeignOwned</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">inner</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8822 <span class="w"> </span><span class="n">MM</span>::<span class="n">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8823 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8824 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8825 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8826 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8827 </code></pre></div> Err codemadness.org 70 i 8828 Err codemadness.org 70 i 8829 <p>If we had a <code>Native(Box&lt;T&gt;)</code> value, it means it came from Rust itself, Err codemadness.org 70 i 8830 and Rust knows how to <code>Drop</code> its own <code>Box&lt;T&gt;</code> (i.e. a chunk of memory Err codemadness.org 70 i 8831 allocated in the heap).</p> Err codemadness.org 70 i 8832 <p>But for external resources, we must tell Rust how to manage them. Err codemadness.org 70 i 8833 Again: in the case where the Rust side owns the reference to the Err codemadness.org 70 i 8834 external boxed data, we have a <code>ForeignOwned</code> and <code>Drop</code> it by Err codemadness.org 70 i 8835 <code>free()</code>ing it; in the case where the Rust side is just borrowing the Err codemadness.org 70 i 8836 data temporarily, we have a <code>ForeignBorrowed</code> and don't touch it when Err codemadness.org 70 i 8837 we are done.</p> Err codemadness.org 70 i 8838 <h2>Copying</h2> Err codemadness.org 70 i 8839 <p>When do we have to copy a boxed value? For example, when we transfer Err codemadness.org 70 i 8840 from Rust to Glib with full transfer of ownership, i.e. the Err codemadness.org 70 i 8841 <a href="https://github.com/gtk-rs/glib/blob/f0a2aae96162fd628fb0e3eedd3d3e042c25e2c4/src/boxed.rs#L312"><code>to_glib_full()</code></a> pattern <a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html#ptr-transfer-full">that we saw Err codemadness.org 70 i 8842 before</a>. This is how that trait method is Err codemadness.org 70 i 8843 implemented for <code>Boxed</code>:</p> Err codemadness.org 70 i 8844 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">T</span>: <span class="o">&#39;</span><span class="nb">static</span><span class="p">,</span><span class="w"> </span><span class="n">MM</span>: <span class="nc">BoxedMemoryManager</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">ToGlibPtr</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Boxed</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">MM</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8845 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_glib_full</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">T</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8846 <span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="bp">self</span>::<span class="n">AnyBox</span>::<span class="o">*</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8847 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">inner</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8848 <span class="w"> </span><span class="n">Native</span><span class="p">(</span><span class="k">ref</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;**</span><span class="n">b</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">T</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 8849 <span class="w"> </span><span class="n">ForeignOwned</span><span class="p">(</span><span class="n">p</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ForeignBorrowed</span><span class="p">(</span><span class="n">p</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">T</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 8850 <span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 8851 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">MM</span>::<span class="n">copy</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8852 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8853 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8854 </code></pre></div> Err codemadness.org 70 i 8855 Err codemadness.org 70 i 8856 <p>See the <code>MM:copy(ptr)</code> in the last line? That's where the copy Err codemadness.org 70 i 8857 happens. The lines above just get the appropriate pointer to the data Err codemadness.org 70 i 8858 data from the <code>AnyBox</code> and cast it.</p> Err codemadness.org 70 i 8859 <p>There is extra boilerplate in <code>boxed.rs</code> which you can look at; it's Err codemadness.org 70 i 8860 mostly a bunch of trait implementations to copy the boxed data at the Err codemadness.org 70 i 8861 appropriate times (e.g. the <code>FromGlibPtrNone</code> trait), also an Err codemadness.org 70 i 8862 implementation of the <code>Deref</code> trait to get to the contents of a <code>Boxed Err codemadness.org 70 i 8863 / AnyBox</code> easily, etc. The trait implementations are there just to Err codemadness.org 70 i 8864 make it as convenient as possible to handle <code>Boxed</code> types.</p> Err codemadness.org 70 i 8865 <h2>Who implements BoxedMemoryManager?</h2> Err codemadness.org 70 i 8866 <p>Up to now, we have seen things like the implementation of <code>Drop</code> for Err codemadness.org 70 i 8867 <code>Boxed</code>, which uses <code>BoxedMemoryManager::free()</code>, and the Err codemadness.org 70 i 8868 implementation of <code>ToGlibPtr</code> which uses <code>::copy()</code>.</p> Err codemadness.org 70 i 8869 <p>But those are just the trait's "abstract" methods, so to speak. What Err codemadness.org 70 i 8870 actually implements them?</p> Err codemadness.org 70 i 8871 <p>Glib-rs has a general-purpose macro to wrap Glib types. It can wrap Err codemadness.org 70 i 8872 boxed types, shared pointer types, and GObjects. For now we will just Err codemadness.org 70 i 8873 look at boxed types.</p> Err codemadness.org 70 i 8874 <p>Glib-rs comes with a macro, <a href="http://gtk-rs.org/docs/glib/macro.glib_wrapper.html#boxed"><code>glib_wrapper!()</code></a>, that can be used in Err codemadness.org 70 i 8875 different ways. You can use it to automatically write the boilerplate Err codemadness.org 70 i 8876 for a boxed type like this:</p> Err codemadness.org 70 i 8877 <div class="highlight"><pre><span></span><code><span class="n">glib_wrapper</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8878 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Color</span><span class="p">(</span><span class="n">Boxed</span><span class="o">&lt;</span><span class="n">ffi</span>::<span class="n">Color</span><span class="o">&gt;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8879 Err codemadness.org 70 i 8880 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="k">fn</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8881 <span class="w"> </span><span class="n">copy</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">|</span><span class="n">ptr</span><span class="o">|</span><span class="w"> </span><span class="n">ffi</span>::<span class="n">color_copy</span><span class="p">(</span><span class="n">mut_override</span><span class="p">(</span><span class="n">ptr</span><span class="p">)),</span><span class="w"></span> Err codemadness.org 70 i 8882 <span class="w"> </span><span class="n">free</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">|</span><span class="n">ptr</span><span class="o">|</span><span class="w"> </span><span class="n">ffi</span>::<span class="n">color_free</span><span class="p">(</span><span class="n">ptr</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 8883 <span class="w"> </span><span class="n">get_type</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">ffi</span>::<span class="n">color_get_type</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 8884 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8885 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8886 </code></pre></div> Err codemadness.org 70 i 8887 Err codemadness.org 70 i 8888 <p>This expands to an internal Err codemadness.org 70 i 8889 <a href="https://github.com/gtk-rs/glib/blob/f0a2aae96162fd628fb0e3eedd3d3e042c25e2c4/src/boxed.rs#L15"><code>glib_boxed_wrapper!()</code></a> macro that does a few Err codemadness.org 70 i 8890 things. We will only look at particularly interesting bits.</p> Err codemadness.org 70 i 8891 <p>First, the macro creates a newtype around a tuple with 1) the actual Err codemadness.org 70 i 8892 data type you want to box, and 2) a memory manager. In the example Err codemadness.org 70 i 8893 above, the newtype would be called <code>Color</code>, and it would wrap an Err codemadness.org 70 i 8894 <code>ffi:Color</code> (say, a C struct).</p> Err codemadness.org 70 i 8895 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="cp">$name</span><span class="p">(</span><span class="n">Boxed</span><span class="o">&lt;</span><span class="cp">$ffi_name</span><span class="p">,</span><span class="w"> </span><span class="n">MemoryManager</span><span class="o">&gt;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8896 </code></pre></div> Err codemadness.org 70 i 8897 Err codemadness.org 70 i 8898 <p>Aha! And that <code>MemoryManager</code>? The macro defines it as a zero-sized Err codemadness.org 70 i 8899 type:</p> Err codemadness.org 70 i 8900 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MemoryManager</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8901 </code></pre></div> Err codemadness.org 70 i 8902 Err codemadness.org 70 i 8903 <p>Then it <a href="https://github.com/gtk-rs/glib/blob/f0a2aae96162fd628fb0e3eedd3d3e042c25e2c4/src/boxed.rs#L59">implements</a> the <code>BoxedMemoryManager</code> trait for that Err codemadness.org 70 i 8904 <code>MemoryManager</code> struct:</p> Err codemadness.org 70 i 8905 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">BoxedMemoryManager</span><span class="o">&lt;</span><span class="cp">$ffi_name</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">MemoryManager</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8906 <span class="w"> </span><span class="cp">#[inline]</span><span class="w"></span> Err codemadness.org 70 i 8907 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">copy</span><span class="p">(</span><span class="cp">$copy_arg</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="cp">$ffi_name</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="cp">$ffi_name</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8908 <span class="w"> </span><span class="cp">$copy_expr</span><span class="w"></span> Err codemadness.org 70 i 8909 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8910 Err codemadness.org 70 i 8911 <span class="w"> </span><span class="cp">#[inline]</span><span class="w"></span> Err codemadness.org 70 i 8912 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">free</span><span class="p">(</span><span class="cp">$free_arg</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="cp">$ffi_name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8913 <span class="w"> </span><span class="cp">$free_expr</span><span class="w"></span> Err codemadness.org 70 i 8914 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8915 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8916 </code></pre></div> Err codemadness.org 70 i 8917 Err codemadness.org 70 i 8918 <p>There! <em>This</em> is where the <code>copy/free</code> methods are implemented, based Err codemadness.org 70 i 8919 on the bits of code with which you invoked the macro. In the call to Err codemadness.org 70 i 8920 <code>glib_wrapper!()</code> we had this:</p> Err codemadness.org 70 i 8921 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="n">copy</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">|</span><span class="n">ptr</span><span class="o">|</span><span class="w"> </span><span class="n">ffi</span>::<span class="n">color_copy</span><span class="p">(</span><span class="n">mut_override</span><span class="p">(</span><span class="n">ptr</span><span class="p">)),</span><span class="w"></span> Err codemadness.org 70 i 8922 <span class="w"> </span><span class="n">free</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">|</span><span class="n">ptr</span><span class="o">|</span><span class="w"> </span><span class="n">ffi</span>::<span class="n">color_free</span><span class="p">(</span><span class="n">ptr</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 8923 </code></pre></div> Err codemadness.org 70 i 8924 Err codemadness.org 70 i 8925 <p>In the impl aboe, the <code>$copy_expr</code> will expand to Err codemadness.org 70 i 8926 <code>ffi::color_copy(mut_override(ptr))</code> and <code>$free_expr</code> will expand to Err codemadness.org 70 i 8927 <code>ffi::color_free(ptr)</code>, which defines our implementation of a memory Err codemadness.org 70 i 8928 manager for our <code>Color</code> boxed type.</p> Err codemadness.org 70 i 8929 <h2>Zero-sized what?</h2> Err codemadness.org 70 i 8930 <p>Within the macro's definition, let's look again at the definitions of Err codemadness.org 70 i 8931 our boxed type and the memory manager object that actually implements Err codemadness.org 70 i 8932 the <code>BoxedMemoryManager</code> trait. Here is what the macro would expand Err codemadness.org 70 i 8933 to with our <code>Color</code> example:</p> Err codemadness.org 70 i 8934 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Color</span><span class="p">(</span><span class="n">Boxed</span><span class="o">&lt;</span><span class="n">ffi</span>::<span class="n">Color</span><span class="p">,</span><span class="w"> </span><span class="n">MemoryManager</span><span class="o">&gt;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 8935 Err codemadness.org 70 i 8936 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MemoryManager</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 8937 Err codemadness.org 70 i 8938 <span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">BoxedMemoryManager</span><span class="o">&lt;</span><span class="n">ffi</span>::<span class="n">Color</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">MemoryManager</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 8939 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">copy</span><span class="p">(</span><span class="o">..</span><span class="p">.)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">ffi</span>::<span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8940 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">free</span><span class="p">(</span><span class="o">..</span><span class="p">.)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8941 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 8942 </code></pre></div> Err codemadness.org 70 i 8943 Err codemadness.org 70 i 8944 <p>Here, <code>MemoryManager</code> is a zero-sized type. This means <strong>it doesn't Err codemadness.org 70 i 8945 take up any space</strong> in the <code>Color</code> tuple! When a <code>Color</code> is allocated Err codemadness.org 70 i 8946 in the heap, it is really as if it contained an <code>ffi::Color</code> (the Err codemadness.org 70 i 8947 C struct we are wrapping) <em>and nothing else</em>.</p> Err codemadness.org 70 i 8948 <p>All the knowledge about how to copy/free <code>ffi::Color</code> <strong>lives only in Err codemadness.org 70 i 8949 the compiler</strong> thanks to the trait implementation. When the compiler Err codemadness.org 70 i 8950 expands all the macros and monomorphizes all the generic functions, Err codemadness.org 70 i 8951 the calls to <code>ffi::color_copy()</code> and <code>ffi::color_free()</code> <strong>will be Err codemadness.org 70 i 8952 inlined at the appropriate spots</strong>. There is no need to have Err codemadness.org 70 i 8953 auxiliary structures taking up space in the heap, just to store Err codemadness.org 70 i 8954 function pointers to the copy/free functions, or anything like that.</p> Err codemadness.org 70 i 8955 <h1>Next up</h1> Err codemadness.org 70 i 8956 <p>You may have seen that our example call to <code>glib_wrapper!()</code> also Err codemadness.org 70 i 8957 passed in a <code>ffi::color_get_type()</code> function. We haven't talked about Err codemadness.org 70 i 8958 how glib-rs wraps Glib's <code>GType</code>, <code>GValue</code>, and all of that. We are Err codemadness.org 70 i 8959 getting closer and closer to being able to wrap <code>GObject</code>.</p> Err codemadness.org 70 i 8960 <p>Stay tuned!</p>Initial posts about librsvg's C to Rust conversion2017-09-07T15:50:46-05:002017-09-07T15:50:46-05:00Federico Mena Quinterotag:people.gnome.org,2017-09-07:/~federico/blog/librsvg-posts.html<p>The initial articles about librsvg's conversion to Rust are in my <a href="https://people.gnome.org/~federico/news.html">old Err codemadness.org 70 i 8961 blog</a>, so they may be a bit hard to find from this new blog. Err codemadness.org 70 i 8962 Here is a list of those posts, just so they are easier to find:</p> Err codemadness.org 70 i 8963 <ul> Err codemadness.org 70 i 8964 <li><a href="https://people.gnome.org/~federico/news-2016-10.html#25">Librsvg gets Rusty</a></li> Err codemadness.org 70 i 8965 <li><a href="https://people.gnome.org/~federico/news-2016-10.html#28">Porting a few C functions to Rust …</a></li></ul><p>The initial articles about librsvg's conversion to Rust are in my <a href="https://people.gnome.org/~federico/news.html">old Err codemadness.org 70 i 8966 blog</a>, so they may be a bit hard to find from this new blog. Err codemadness.org 70 i 8967 Here is a list of those posts, just so they are easier to find:</p> Err codemadness.org 70 i 8968 <ul> Err codemadness.org 70 i 8969 <li><a href="https://people.gnome.org/~federico/news-2016-10.html#25">Librsvg gets Rusty</a></li> Err codemadness.org 70 i 8970 <li><a href="https://people.gnome.org/~federico/news-2016-10.html#28">Porting a few C functions to Rust</a></li> Err codemadness.org 70 i 8971 <li><a href="https://people.gnome.org/~federico/news-2016-11.html#01">Bézier curves, markers, and SVG's concept of directionality</a></li> Err codemadness.org 70 i 8972 <li><a href="https://people.gnome.org/~federico/news-2016-11.html#03">Refactoring C to make Rustification easier</a></li> Err codemadness.org 70 i 8973 <li><a href="https://people.gnome.org/~federico/news-2016-11.html#14">Exposing Rust objects to C code</a></li> Err codemadness.org 70 i 8974 <li><a href="https://people.gnome.org/~federico/news-2016-11.html#16">Debugging Rust code inside a C library</a></li> Err codemadness.org 70 i 8975 <li><a href="https://people.gnome.org/~federico/news-2017-01.html#11">Reproducible font rendering for librsvg's tests</a></li> Err codemadness.org 70 i 8976 <li><a href="https://people.gnome.org/~federico/news-2017-02.html#03">Algebraic data types in Rust, and basic parsing</a></li> Err codemadness.org 70 i 8977 <li><a href="https://people.gnome.org/~federico/news-2017-02.html#17">How librsvg exports reference-counted objects from Rust to C</a></li> Err codemadness.org 70 i 8978 <li><a href="https://people.gnome.org/~federico/news-2017-02.html#24">Griping about parsers and shitty specifications</a></li> Err codemadness.org 70 i 8979 <li><a href="https://people.gnome.org/~federico/news-2017-02.html#28">Porting librsvg's tree of nodes to Rust</a></li> Err codemadness.org 70 i 8980 <li><a href="https://people.gnome.org/~federico/news-2017-04.html#28">gboolean is not Rust bool</a></li> Err codemadness.org 70 i 8981 </ul> Err codemadness.org 70 i 8982 <p>Within this new blog, you can look for articles with the <a href="https://people.gnome.org/~federico/blog/tag/librsvg.html">librsvg tag</a>.</p>The Magic of GObject Introspection2017-09-07T11:46:03-05:002017-09-07T11:46:03-05:00Federico Mena Quinterotag:people.gnome.org,2017-09-07:/~federico/blog/magic-of-gobject-introspection.html<p>Before continuing with the <a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">glib-rs architecture</a>, let's take Err codemadness.org 70 i 8983 a detour and look at <a href="https://wiki.gnome.org/Projects/GObjectIntrospection">GObject Introspection</a>. Although it can Err codemadness.org 70 i 8984 seem like an obscure part of the GNOME platform, it is an absolutely Err codemadness.org 70 i 8985 vital part of it: it is what lets people write GNOME applications in Err codemadness.org 70 i 8986 any language.</p> Err codemadness.org 70 i 8987 <p>Let's start with a …</p><p>Before continuing with the <a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">glib-rs architecture</a>, let's take Err codemadness.org 70 i 8988 a detour and look at <a href="https://wiki.gnome.org/Projects/GObjectIntrospection">GObject Introspection</a>. Although it can Err codemadness.org 70 i 8989 seem like an obscure part of the GNOME platform, it is an absolutely Err codemadness.org 70 i 8990 vital part of it: it is what lets people write GNOME applications in Err codemadness.org 70 i 8991 any language.</p> Err codemadness.org 70 i 8992 <p>Let's start with a bit of history.</p> Err codemadness.org 70 i 8993 <h1>Brief history of language bindings in GNOME</h1> Err codemadness.org 70 i 8994 <p>When we started GNOME in 1997, we didn't want to write <em>all</em> of it in Err codemadness.org 70 i 8995 C. We had some inspiration from elsewhere.</p> Err codemadness.org 70 i 8996 <h2>Prehistory: GIMP and the Procedural Database</h2> Err codemadness.org 70 i 8997 <p>There was already good precedent for software written in a combination of Err codemadness.org 70 i 8998 programming languages. Emacs, the flagship text editor of the Err codemadness.org 70 i 8999 GNU project, was written with a relatively small core in C, and the Err codemadness.org 70 i 9000 majority of the program in Emacs Lisp.</p> Err codemadness.org 70 i 9001 <p>In similar fashion, we were very influenced by the design of the GIMP, Err codemadness.org 70 i 9002 which was very innovative at that time. The GIMP has a large core Err codemadness.org 70 i 9003 written in C. However, it supports plug-ins or <em>scripts</em> written in a Err codemadness.org 70 i 9004 variety of languages. Initially the only scripting language available Err codemadness.org 70 i 9005 for the GIMP was Scheme.</p> Err codemadness.org 70 i 9006 <p>The GIMP's plug-ins and scripts run as separate processes, so Err codemadness.org 70 i 9007 they don't have immediate access to the data of the image being Err codemadness.org 70 i 9008 edited, or to the core functions of the program like "paint with a Err codemadness.org 70 i 9009 brush at this location". To let plug-ins and scripts access these Err codemadness.org 70 i 9010 data and these functions, the GIMP has what it calls a Err codemadness.org 70 i 9011 Procedural Database (PDB). This is a Err codemadness.org 70 i 9012 list of functions that the core program or plug-ins wish to export. Err codemadness.org 70 i 9013 For example, there are functions like <code>gimp-scale-image</code> and Err codemadness.org 70 i 9014 <code>gimp-move-layer</code>. Once these functions are registered in the Err codemadness.org 70 i 9015 PDB, any part of the program or plug-ins can call them. Scripts are Err codemadness.org 70 i 9016 often written to automate common tasks — for example, when one wants Err codemadness.org 70 i 9017 to adjust the contrast of photos and scale them in bulk. Scripts can Err codemadness.org 70 i 9018 call functions in the PDB easily, irrespective of the programming Err codemadness.org 70 i 9019 language they are written in.</p> Err codemadness.org 70 i 9020 <p>We wanted to write GNOME's core libraries in C, and write a similar Err codemadness.org 70 i 9021 Procedural Database to allow those libraries to be called from any Err codemadness.org 70 i 9022 programming language. Eventually it turned out that a PDB was not Err codemadness.org 70 i 9023 necessary, and there were better ways to go about enabling different Err codemadness.org 70 i 9024 programming languages.</p> Err codemadness.org 70 i 9025 <h2>Enabling sane memory management</h2> Err codemadness.org 70 i 9026 <p>GTK+ started out with a very simple scheme for memory management: a Err codemadness.org 70 i 9027 container owned its child widgets, and so on recursively. When you Err codemadness.org 70 i 9028 freed a container, it would be responsible for freeing its children.</p> Err codemadness.org 70 i 9029 <p>However, consider what happens when a widget needs to hold a reference Err codemadness.org 70 i 9030 to another widget that is not one of its children. For example, a Err codemadness.org 70 i 9031 GtkLabel with an underlined mnemonic ("_N_ame:") needs to have a Err codemadness.org 70 i 9032 reference to the GtkEntry that should be focused when you press Err codemadness.org 70 i 9033 Alt-N. In the very earliest versions of GTK+, how to do this was Err codemadness.org 70 i 9034 undefined: C programmers were already used to having shared pointers Err codemadness.org 70 i 9035 everywhere, and they were used to being responsible for managing their Err codemadness.org 70 i 9036 memory.</p> Err codemadness.org 70 i 9037 <p>Of course, this was prone to bugs. If you have something like</p> Err codemadness.org 70 i 9038 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 9039 <span class="n">GtkWidget</span> <span class="n">parent</span><span class="p">;</span> Err codemadness.org 70 i 9040 Err codemadness.org 70 i 9041 <span class="kt">char</span> <span class="o">*</span><span class="n">label_string</span><span class="p">;</span> Err codemadness.org 70 i 9042 <span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget_to_focus</span><span class="p">;</span> Err codemadness.org 70 i 9043 <span class="p">}</span> <span class="n">GtkLabel</span><span class="p">;</span> Err codemadness.org 70 i 9044 </code></pre></div> Err codemadness.org 70 i 9045 Err codemadness.org 70 i 9046 <p>then if you are writing the destructor, you may simply want to</p> Err codemadness.org 70 i 9047 <div class="highlight"><pre><span></span><code><span class="k">static</span> <span class="kt">void</span> Err codemadness.org 70 i 9048 <span class="nf">gtk_label_free</span> <span class="p">(</span><span class="n">GtkLabel</span> <span class="o">*</span><span class="n">label</span><span class="p">)</span> Err codemadness.org 70 i 9049 <span class="p">{</span> Err codemadness.org 70 i 9050 <span class="n">g_free</span> <span class="p">(</span><span class="n">label_string</span><span class="p">);</span> Err codemadness.org 70 i 9051 <span class="n">gtk_widget_free</span> <span class="p">(</span><span class="n">widget_to_focus</span><span class="p">);</span> <span class="cm">/* oops, we don&#39;t own this */</span> Err codemadness.org 70 i 9052 Err codemadness.org 70 i 9053 <span class="n">free_parent_instance</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">label</span><span class="o">-&gt;</span><span class="n">parent</span><span class="p">);</span> Err codemadness.org 70 i 9054 <span class="p">}</span> Err codemadness.org 70 i 9055 </code></pre></div> Err codemadness.org 70 i 9056 Err codemadness.org 70 i 9057 <p>Say you have a GtkBox with the label and its associated GtkEntry. Err codemadness.org 70 i 9058 Then, freeing the GtkBox would recursively free the label with that Err codemadness.org 70 i 9059 <code>gtk_label_free()</code>, and then the entry with its own function. But by Err codemadness.org 70 i 9060 the time the entry gets freed, the line Err codemadness.org 70 i 9061 <code>gtk_widget_free (widget_to_focus)</code> has already freed the entry, and Err codemadness.org 70 i 9062 we get a double-free bug!</p> Err codemadness.org 70 i 9063 <p>Madness!</p> Err codemadness.org 70 i 9064 <p>That is, we had no idea what we were doing. Or rather, our Err codemadness.org 70 i 9065 understanding of widgets had not evolved to the point of acknowledging Err codemadness.org 70 i 9066 that a widget tree is not a simply tree, but rather a Err codemadness.org 70 i 9067 directed graph of container-child relationships, plus Err codemadness.org 70 i 9068 random-widget-to-random-widget relationships. And of course, other Err codemadness.org 70 i 9069 parts of the program <em>which are not even widget implementations</em> may Err codemadness.org 70 i 9070 need to keep references to widgets and free them or not as Err codemadness.org 70 i 9071 appropriate.</p> Err codemadness.org 70 i 9072 <p>I think Marius Vollmer was the first person to start formalizing Err codemadness.org 70 i 9073 this. He came from the world of GNU Guile, a Scheme interpreter, and Err codemadness.org 70 i 9074 so he already knew how garbage collection and seas of shared Err codemadness.org 70 i 9075 references ought to work.</p> Err codemadness.org 70 i 9076 <p>Marius implemented reference-counting for GTK+ — that's where Err codemadness.org 70 i 9077 <code>gtk_object_ref()</code> and <code>gtk_object_unref()</code> come from; they eventually Err codemadness.org 70 i 9078 got moved to the base <code>GObject</code> class, so we now have <code>g_object_ref()</code> Err codemadness.org 70 i 9079 and <code>g_object_unref()</code> and a host of functions to have weak Err codemadness.org 70 i 9080 references, notification of destruction, and all the things required Err codemadness.org 70 i 9081 to keep garbage collectors happy.</p> Err codemadness.org 70 i 9082 <h2>The first language bindings</h2> Err codemadness.org 70 i 9083 <p>The very first language bindings were written by hand. The GTK+ API Err codemadness.org 70 i 9084 was small, and it seemed feasible to take</p> Err codemadness.org 70 i 9085 <div class="highlight"><pre><span></span><code><span class="kt">void</span> <span class="nf">gtk_widget_show</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">);</span> Err codemadness.org 70 i 9086 <span class="kt">void</span> <span class="nf">gtk_widget_hide</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">);</span> Err codemadness.org 70 i 9087 Err codemadness.org 70 i 9088 <span class="kt">void</span> <span class="nf">gtk_container_add</span> <span class="p">(</span><span class="n">GtkContainer</span> <span class="o">*</span><span class="n">container</span><span class="p">,</span> <span class="n">GtkWidget</span> <span class="o">*</span><span class="n">child</span><span class="p">);</span> Err codemadness.org 70 i 9089 <span class="kt">void</span> <span class="nf">gtk_container_remove</span> <span class="p">(</span><span class="n">GtkContainer</span> <span class="o">*</span><span class="n">container</span><span class="p">,</span> <span class="n">GtkWidget</span> <span class="o">*</span><span class="n">child</span><span class="p">);</span> Err codemadness.org 70 i 9090 </code></pre></div> Err codemadness.org 70 i 9091 Err codemadness.org 70 i 9092 <p>and just wrap those functions in various languages, by hand, on an Err codemadness.org 70 i 9093 as-needed basis.</p> Err codemadness.org 70 i 9094 <p>Of course, there is a lot of duplication when doing things that way. Err codemadness.org 70 i 9095 As the C API grows, one needs to do more and more manual work to Err codemadness.org 70 i 9096 keep up with it.</p> Err codemadness.org 70 i 9097 <p>Also, C structs with public fields are problematic. If we had</p> Err codemadness.org 70 i 9098 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 9099 <span class="n">guchar</span> <span class="n">r</span><span class="p">;</span> Err codemadness.org 70 i 9100 <span class="n">guchar</span> <span class="n">g</span><span class="p">;</span> Err codemadness.org 70 i 9101 <span class="n">guchar</span> <span class="n">b</span><span class="p">;</span> Err codemadness.org 70 i 9102 <span class="p">}</span> <span class="n">GdkColor</span><span class="p">;</span> Err codemadness.org 70 i 9103 </code></pre></div> Err codemadness.org 70 i 9104 Err codemadness.org 70 i 9105 <p>and we <em>expect</em> program code to fill in a <code>GdkColor</code> by hand and Err codemadness.org 70 i 9106 pass it to a drawing function like</p> Err codemadness.org 70 i 9107 <div class="highlight"><pre><span></span><code><span class="kt">void</span> <span class="nf">gdk_set_foreground_color</span> <span class="p">(</span><span class="n">GdkDrawingContext</span> <span class="o">*</span><span class="n">gc</span><span class="p">,</span> <span class="n">GdkColor</span> <span class="o">*</span><span class="n">color</span><span class="p">);</span> Err codemadness.org 70 i 9108 </code></pre></div> Err codemadness.org 70 i 9109 Err codemadness.org 70 i 9110 <p>then it is no problem to do that in C:</p> Err codemadness.org 70 i 9111 <div class="highlight"><pre><span></span><code><span class="n">GdkColor</span> <span class="n">magenta</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span> <span class="p">};</span> Err codemadness.org 70 i 9112 Err codemadness.org 70 i 9113 <span class="n">gdk_set_foreground_color</span> <span class="p">(</span><span class="n">gc</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">magenta</span><span class="p">);</span> Err codemadness.org 70 i 9114 </code></pre></div> Err codemadness.org 70 i 9115 Err codemadness.org 70 i 9116 <p>But to do that in a high level language? You don't have access to C Err codemadness.org 70 i 9117 struct fields! And back then, libffi wasn't generally available.</p> Err codemadness.org 70 i 9118 <p>Authors of language bindings had to write some glue code, in C, by Err codemadness.org 70 i 9119 hand, to let people access a C struct and then pass it on to GTK+. Err codemadness.org 70 i 9120 For example, for Python, they would need to write something like</p> Err codemadness.org 70 i 9121 <div class="highlight"><pre><span></span><code><span class="n">PyObject</span> <span class="o">*</span> Err codemadness.org 70 i 9122 <span class="nf">make_wrapped_gdk_color</span> <span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">kwargs</span><span class="p">)</span> Err codemadness.org 70 i 9123 <span class="p">{</span> Err codemadness.org 70 i 9124 <span class="n">GdkColor</span> <span class="o">*</span><span class="n">g_color</span><span class="p">;</span> Err codemadness.org 70 i 9125 <span class="n">PyObject</span> <span class="o">*</span><span class="n">py_color</span><span class="p">;</span> Err codemadness.org 70 i 9126 Err codemadness.org 70 i 9127 <span class="n">g_color</span> <span class="o">=</span> <span class="n">g_new</span> <span class="p">(</span><span class="n">GdkColor</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> Err codemadness.org 70 i 9128 <span class="cm">/* ... fill in g_color-&gt;r, g, b from the Python args */</span> Err codemadness.org 70 i 9129 Err codemadness.org 70 i 9130 <span class="n">py_color</span> <span class="o">=</span> <span class="n">wrap_g_color</span> <span class="p">(</span><span class="n">g_color</span><span class="p">);</span> Err codemadness.org 70 i 9131 <span class="k">return</span> <span class="n">py_color</span><span class="p">;</span> Err codemadness.org 70 i 9132 <span class="p">}</span> Err codemadness.org 70 i 9133 </code></pre></div> Err codemadness.org 70 i 9134 Err codemadness.org 70 i 9135 <p>Writing that by hand is an incredible amount of drudgery.</p> Err codemadness.org 70 i 9136 <p>What language bindings needed was a <em>description</em> of the API in a Err codemadness.org 70 i 9137 machine-readable format, so that the glue code could be written by a Err codemadness.org 70 i 9138 code generator.</p> Err codemadness.org 70 i 9139 <h2>The first API descriptions</h2> Err codemadness.org 70 i 9140 <p>I don't remember if it was the GNU Guile people, or the PyGTK people, Err codemadness.org 70 i 9141 who started to write descriptions of the GNOME API by hand. For ease Err codemadness.org 70 i 9142 of parsing, it was done in a Scheme-like dialect. A description may Err codemadness.org 70 i 9143 look like</p> Err codemadness.org 70 i 9144 <div class="highlight"><pre><span></span><code><span class="p">(</span><span class="nf">class</span> <span class="nv">GtkWidget</span> Err codemadness.org 70 i 9145 <span class="c1">;;; void gtk_widget_show (GtkWidget *widget);</span> Err codemadness.org 70 i 9146 <span class="p">(</span><span class="nf">method</span> <span class="nv">show</span> Err codemadness.org 70 i 9147 <span class="p">(</span><span class="nf">args</span> <span class="nv">nil</span><span class="p">)</span> Err codemadness.org 70 i 9148 <span class="p">(</span><span class="nf">retval</span> <span class="nv">nil</span><span class="p">))</span> Err codemadness.org 70 i 9149 Err codemadness.org 70 i 9150 <span class="c1">;;; void gtk_widget_hide (GtkWidget *widget);</span> Err codemadness.org 70 i 9151 <span class="p">(</span><span class="nf">method</span> <span class="nv">hide</span> Err codemadness.org 70 i 9152 <span class="p">(</span><span class="nf">args</span> <span class="nv">nil</span><span class="p">)</span> Err codemadness.org 70 i 9153 <span class="p">(</span><span class="nf">retval</span> <span class="nv">nil</span><span class="p">)))</span> Err codemadness.org 70 i 9154 Err codemadness.org 70 i 9155 <span class="p">(</span><span class="nf">class</span> <span class="nv">GtkContainer</span> Err codemadness.org 70 i 9156 <span class="c1">;;; void gtk_container_add (GtkContainer *container, GtkWidget *child);</span> Err codemadness.org 70 i 9157 <span class="p">(</span><span class="nf">method</span> <span class="nv">add</span> Err codemadness.org 70 i 9158 <span class="p">(</span><span class="nf">args</span> <span class="nv">GtkWidget</span><span class="p">)</span> Err codemadness.org 70 i 9159 <span class="p">(</span><span class="nf">retval</span> <span class="nv">nil</span><span class="p">)))</span> Err codemadness.org 70 i 9160 Err codemadness.org 70 i 9161 <span class="p">(</span><span class="nf">struct</span> <span class="nv">GdkColor</span> Err codemadness.org 70 i 9162 <span class="p">(</span><span class="nf">field</span> <span class="nv">r</span> <span class="p">(</span><span class="nf">type</span> <span class="ss">&#39;guchar</span><span class="p">))</span> Err codemadness.org 70 i 9163 <span class="p">(</span><span class="nf">field</span> <span class="nv">g</span> <span class="p">(</span><span class="nf">type</span> <span class="ss">&#39;guchar</span><span class="p">))</span> Err codemadness.org 70 i 9164 <span class="p">(</span><span class="nf">field</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">type</span> <span class="ss">&#39;guchar</span><span class="p">)))</span> Err codemadness.org 70 i 9165 </code></pre></div> Err codemadness.org 70 i 9166 Err codemadness.org 70 i 9167 <p>Again, writing those descriptions by hand (and keeping up with the C Err codemadness.org 70 i 9168 API) was a lot of work, but the glue code to implement the binding Err codemadness.org 70 i 9169 could be done mostly automatically. The generated code may need Err codemadness.org 70 i 9170 subsequent tweaks by hand to deal with details that the Scheme-like Err codemadness.org 70 i 9171 descriptions didn't contemplate, but it was better than writing Err codemadness.org 70 i 9172 <em>everything</em> by hand.</p> Err codemadness.org 70 i 9173 <h2 id="type-system">Glib gets a real type system</h2> Err codemadness.org 70 i 9174 <p>Tim Janik took over the parts of Glib that implement Err codemadness.org 70 i 9175 objects/signals/types, and added a lot of things to create a good type Err codemadness.org 70 i 9176 system for C. This is where things like <code>GType</code>, <code>GValue</code>, <code>GParamSpec</code>, and Err codemadness.org 70 i 9177 fundamental types come from.</p> Err codemadness.org 70 i 9178 <p>For example, a <code>GType</code> is an identifier for a type, and a <code>GValue</code> is a Err codemadness.org 70 i 9179 type plus, well, a value of that type. You can ask a <code>GValue</code>, "are Err codemadness.org 70 i 9180 you an int? are you a GObject?".</p> Err codemadness.org 70 i 9181 <p>You can register new types: for example, there would be code in Gdk Err codemadness.org 70 i 9182 that registers a new <code>GType</code> for <code>GdkColor</code>, so you can ask a value, Err codemadness.org 70 i 9183 "are you a color?".</p> Err codemadness.org 70 i 9184 <p>Registering a type involves telling the GObject system things like how Err codemadness.org 70 i 9185 to copy values of that type, and how to free them. For <code>GdkColor</code> Err codemadness.org 70 i 9186 this may be just <code>g_new() / g_free()</code>; for reference-counted objects Err codemadness.org 70 i 9187 it may be <code>g_object_ref() / g_object_unref()</code>.</p> Err codemadness.org 70 i 9188 <h3>Objects can be queried about some of their properties</h3> Err codemadness.org 70 i 9189 <p>A widget can tell you when you press a mouse button mouse on it: it Err codemadness.org 70 i 9190 will emit the <code>button-press-event</code> signal. When <code>GtkWidget</code>'s Err codemadness.org 70 i 9191 implementation registers this signal, it calls something like</p> Err codemadness.org 70 i 9192 <div class="highlight"><pre><span></span><code> <span class="n">g_signal_new</span> <span class="p">(</span><span class="s">&quot;button-press-event&quot;</span><span class="p">,</span> Err codemadness.org 70 i 9193 <span class="n">gtk_widget_get_type</span><span class="p">(),</span> <span class="cm">/* type of object for which this signal is being created */</span> Err codemadness.org 70 i 9194 <span class="p">...</span> Err codemadness.org 70 i 9195 <span class="n">G_TYPE_BOOLEAN</span><span class="p">,</span> <span class="cm">/* type of return value */</span> Err codemadness.org 70 i 9196 <span class="mi">1</span><span class="p">,</span> <span class="cm">/* number of arguments */</span> Err codemadness.org 70 i 9197 <span class="n">GDK_TYPE_EVENT</span><span class="p">);</span> <span class="cm">/* type of first and only argument */</span> Err codemadness.org 70 i 9198 </code></pre></div> Err codemadness.org 70 i 9199 Err codemadness.org 70 i 9200 <p>This tells GObject that <code>GtkWidget</code> will have a signal called Err codemadness.org 70 i 9201 <code>button-press-event</code>, with a return type of <code>G_TYPE_BOOLEAN</code>, and with Err codemadness.org 70 i 9202 a single argument of type <code>GDK_TYPE_EVENT</code>. This lets GObject do the Err codemadness.org 70 i 9203 appropriate marshalling of arguments when the signal is emitted.</p> Err codemadness.org 70 i 9204 <p>But also! <em>You can query the signal for its argument types!</em> You can Err codemadness.org 70 i 9205 run <code>g_signal_query()</code>, which will then tell you all the details of Err codemadness.org 70 i 9206 the signal: its name, return type, argument types, etc. A language Err codemadness.org 70 i 9207 binding could run <code>g_signal_query()</code> <em>and generate a description of the Err codemadness.org 70 i 9208 signal automatically</em> to the Scheme-like description language. And Err codemadness.org 70 i 9209 then generate the binding from <em>that</em>.</p> Err codemadness.org 70 i 9210 <h2>Not all of an object's properties can be queried</h2> Err codemadness.org 70 i 9211 <p>Unfortunately, although GObject signals and properties <em>can</em> be Err codemadness.org 70 i 9212 queried, methods can't be. C doesn't have classes with methods, and GObject does Err codemadness.org 70 i 9213 not really have any provisions to implement them. </p> Err codemadness.org 70 i 9214 <p>Conventionally, for a static method one would just do</p> Err codemadness.org 70 i 9215 <div class="highlight"><pre><span></span><code><span class="kt">void</span> Err codemadness.org 70 i 9216 <span class="nf">gtk_widget_set_flags</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">,</span> <span class="n">GtkWidgetFlags</span> <span class="n">flags</span><span class="p">)</span> Err codemadness.org 70 i 9217 <span class="p">{</span> Err codemadness.org 70 i 9218 <span class="cm">/* modify a struct field within &quot;widget&quot; or whatever */</span> Err codemadness.org 70 i 9219 <span class="cm">/* repaint or something */</span> Err codemadness.org 70 i 9220 <span class="p">}</span> Err codemadness.org 70 i 9221 </code></pre></div> Err codemadness.org 70 i 9222 Err codemadness.org 70 i 9223 <p>And for a virtual method one would put a function pointer in the class Err codemadness.org 70 i 9224 structure, and provide a convenient way to call it:</p> Err codemadness.org 70 i 9225 <div class="highlight"><pre><span></span><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 9226 <span class="n">GtkObjectClass</span> <span class="n">parent_class</span><span class="p">;</span> Err codemadness.org 70 i 9227 Err codemadness.org 70 i 9228 <span class="kt">void</span> <span class="p">(</span><span class="o">*</span> <span class="n">draw</span><span class="p">)</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">,</span> <span class="n">cairo_t</span> <span class="o">*</span><span class="n">cr</span><span class="p">);</span> Err codemadness.org 70 i 9229 <span class="p">}</span> <span class="n">GtkWidgetClass</span><span class="p">;</span> Err codemadness.org 70 i 9230 Err codemadness.org 70 i 9231 <span class="kt">void</span> Err codemadness.org 70 i 9232 <span class="nf">gtk_widget_draw</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">,</span> <span class="n">cairo_t</span> <span class="o">*</span><span class="n">cr</span><span class="p">)</span> Err codemadness.org 70 i 9233 <span class="p">{</span> Err codemadness.org 70 i 9234 <span class="n">GtkWidgetClass</span> <span class="o">*</span><span class="n">klass</span> <span class="o">=</span> <span class="n">find_widget_class</span> <span class="p">(</span><span class="n">widget</span><span class="p">);</span> Err codemadness.org 70 i 9235 Err codemadness.org 70 i 9236 <span class="p">(</span><span class="o">*</span> <span class="n">klass</span><span class="o">-&gt;</span><span class="n">draw</span><span class="p">)</span> <span class="p">(</span><span class="n">widget</span><span class="p">,</span> <span class="n">cr</span><span class="p">);</span> Err codemadness.org 70 i 9237 <span class="p">}</span> Err codemadness.org 70 i 9238 </code></pre></div> Err codemadness.org 70 i 9239 Err codemadness.org 70 i 9240 <p>And GObject has no idea about this method — there is no way to query Err codemadness.org 70 i 9241 it; it just exists in C-space.</p> Err codemadness.org 70 i 9242 <p>Now, historically, GTK+'s header files have been written in a <em>very</em> Err codemadness.org 70 i 9243 consistent style. It is quite possible to write a tool that will take Err codemadness.org 70 i 9244 a header file like</p> Err codemadness.org 70 i 9245 <div class="highlight"><pre><span></span><code><span class="cm">/* gtkwidget.h */</span> Err codemadness.org 70 i 9246 <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> Err codemadness.org 70 i 9247 <span class="n">GtkObject</span> <span class="n">parent_class</span><span class="p">;</span> Err codemadness.org 70 i 9248 Err codemadness.org 70 i 9249 <span class="kt">void</span> <span class="p">(</span><span class="o">*</span> <span class="n">draw</span><span class="p">)</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">,</span> <span class="n">cairo_t</span> <span class="o">*</span><span class="n">cr</span><span class="p">);</span> Err codemadness.org 70 i 9250 <span class="p">}</span> <span class="n">GtkWidgetClass</span><span class="p">;</span> Err codemadness.org 70 i 9251 Err codemadness.org 70 i 9252 <span class="kt">void</span> <span class="nf">gtk_widget_set_flags</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">,</span> <span class="n">GtkWidgetFlags</span> <span class="n">flags</span><span class="p">);</span> Err codemadness.org 70 i 9253 <span class="kt">void</span> <span class="nf">gtk_widget_draw</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">,</span> <span class="n">cairo_t</span> <span class="o">*</span><span class="n">cr</span><span class="p">);</span> Err codemadness.org 70 i 9254 </code></pre></div> Err codemadness.org 70 i 9255 Err codemadness.org 70 i 9256 <p>and parse it, even if it is with a simple parser that does not Err codemadness.org 70 i 9257 completely understand the C language, and have heuristics like</p> Err codemadness.org 70 i 9258 <ul> Err codemadness.org 70 i 9259 <li> Err codemadness.org 70 i 9260 <p>Is there a <code>class_name_foo()</code> function prototype with no Err codemadness.org 70 i 9261 corresponding <code>foo</code> field in the <code>Class</code> structure? It's probably a Err codemadness.org 70 i 9262 static method.</p> Err codemadness.org 70 i 9263 </li> Err codemadness.org 70 i 9264 <li> Err codemadness.org 70 i 9265 <p>Is there a <code>class_name_bar()</code> function with a <code>bar</code> field in the Err codemadness.org 70 i 9266 <code>Class</code> structure? It's probably a virtual method.</p> Err codemadness.org 70 i 9267 </li> Err codemadness.org 70 i 9268 <li> Err codemadness.org 70 i 9269 <p>Etc.</p> Err codemadness.org 70 i 9270 </li> Err codemadness.org 70 i 9271 </ul> Err codemadness.org 70 i 9272 <p>And in fact, that's what we had. C header files would get parsed Err codemadness.org 70 i 9273 with those heuristics, and the Scheme-like description files would get Err codemadness.org 70 i 9274 generated.</p> Err codemadness.org 70 i 9275 <h2>Scheme-like descriptions get reused, kind of</h2> Err codemadness.org 70 i 9276 <p>Language binding authors started reusing the Scheme-like Err codemadness.org 70 i 9277 descriptions. Sometimes they would cannibalize the descriptions from Err codemadness.org 70 i 9278 PyGTK, or Guile (again, I don't remember where the canonical version Err codemadness.org 70 i 9279 was maintained) and use them as they were.</p> Err codemadness.org 70 i 9280 <p>Other times they would copy the files, modify them by hand some more, Err codemadness.org 70 i 9281 and <em>then</em> use them to generate their language binding.</p> Err codemadness.org 70 i 9282 <h2>C being hostile</h2> Err codemadness.org 70 i 9283 <p>From just reading/parsing a C function prototype, you cannot know Err codemadness.org 70 i 9284 certain things. If one function argument is of type <code>Foo *</code>, does it mean:</p> Err codemadness.org 70 i 9285 <ul> Err codemadness.org 70 i 9286 <li> Err codemadness.org 70 i 9287 <p>the function gets a pointer to something which it should not modify Err codemadness.org 70 i 9288 ("in" parameter)</p> Err codemadness.org 70 i 9289 </li> Err codemadness.org 70 i 9290 <li> Err codemadness.org 70 i 9291 <p>the function gets a pointer to uninitialized data which it will set Err codemadness.org 70 i 9292 ("out" parameter)</p> Err codemadness.org 70 i 9293 </li> Err codemadness.org 70 i 9294 <li> Err codemadness.org 70 i 9295 <p>the function gets a pointer to initialized data which it will use Err codemadness.org 70 i 9296 and modify ("inout" parameter)</p> Err codemadness.org 70 i 9297 </li> Err codemadness.org 70 i 9298 <li> Err codemadness.org 70 i 9299 <p>the function will copy that pointer and hold a reference to the Err codemadness.org 70 i 9300 pointed data, and not free it when it's done</p> Err codemadness.org 70 i 9301 </li> Err codemadness.org 70 i 9302 <li> Err codemadness.org 70 i 9303 <p>the function will take over the ownership of the pointed data, and Err codemadness.org 70 i 9304 free it when it's done</p> Err codemadness.org 70 i 9305 </li> Err codemadness.org 70 i 9306 <li> Err codemadness.org 70 i 9307 <p>etc.</p> Err codemadness.org 70 i 9308 </li> Err codemadness.org 70 i 9309 </ul> Err codemadness.org 70 i 9310 <p>Sometimes people would include these annotations in the Scheme-like Err codemadness.org 70 i 9311 description language. But wouldn't it be better if those annotations Err codemadness.org 70 i 9312 came <em>from the C code itself</em>?</p> Err codemadness.org 70 i 9313 <h1>GObject Introspection appears</h1> Err codemadness.org 70 i 9314 <p>For GNOME 3, we wanted a unified solution for language bindings:</p> Err codemadness.org 70 i 9315 <ul> Err codemadness.org 70 i 9316 <li> Err codemadness.org 70 i 9317 <p>Have a single way to extract the machine-readable descriptions of Err codemadness.org 70 i 9318 the C API.</p> Err codemadness.org 70 i 9319 </li> Err codemadness.org 70 i 9320 <li> Err codemadness.org 70 i 9321 <p>Have every language binding be automatically generated from those Err codemadness.org 70 i 9322 descriptions.</p> Err codemadness.org 70 i 9323 </li> Err codemadness.org 70 i 9324 <li> Err codemadness.org 70 i 9325 <p>In the descriptions, have <em>all</em> the information necessary to Err codemadness.org 70 i 9326 generate a correct language binding...</p> Err codemadness.org 70 i 9327 </li> Err codemadness.org 70 i 9328 <li> Err codemadness.org 70 i 9329 <p>... including documentation.</p> Err codemadness.org 70 i 9330 </li> Err codemadness.org 70 i 9331 </ul> Err codemadness.org 70 i 9332 <p>We had to do a lot of work to accomplish this. For example:</p> Err codemadness.org 70 i 9333 <ul> Err codemadness.org 70 i 9334 <li> Err codemadness.org 70 i 9335 <p>Remove C-isms from the public API. Varargs functions, those that Err codemadness.org 70 i 9336 have <code>foo (int x, ...)</code>, can't be easily described and called from Err codemadness.org 70 i 9337 other languages. Instead, have something like Err codemadness.org 70 i 9338 <code>foov (int x, int num_args, GValue *args_array)</code> that can be easily Err codemadness.org 70 i 9339 consumed by other languages.</p> Err codemadness.org 70 i 9340 </li> Err codemadness.org 70 i 9341 <li> Err codemadness.org 70 i 9342 <p>Add <em>annotations</em> throughout the code so that the ad-hoc C parser Err codemadness.org 70 i 9343 can know about in/out/inout arguments, and whether pointer arguments Err codemadness.org 70 i 9344 are borrowed references or a full transfership of ownership.</p> Err codemadness.org 70 i 9345 </li> Err codemadness.org 70 i 9346 <li> Err codemadness.org 70 i 9347 <p>Take the in-line documentation comments and store them as part of Err codemadness.org 70 i 9348 the machine-readable description of the API.</p> Err codemadness.org 70 i 9349 </li> Err codemadness.org 70 i 9350 <li> Err codemadness.org 70 i 9351 <p>When compiling a library, automatically do all the things like Err codemadness.org 70 i 9352 <code>g_signal_query()</code> and spit out machine-readable descriptions of Err codemadness.org 70 i 9353 those parts of the API.</p> Err codemadness.org 70 i 9354 </li> Err codemadness.org 70 i 9355 </ul> Err codemadness.org 70 i 9356 <p>So, GObject Introspection is all of those things.</p> Err codemadness.org 70 i 9357 <h2>Annotations</h2> Err codemadness.org 70 i 9358 <p>If you have looked at the C code for a GNOME library, you may have Err codemadness.org 70 i 9359 seen something like this:</p> Err codemadness.org 70 i 9360 <div class="highlight"><pre><span></span><code><span class="cm">/**</span> Err codemadness.org 70 i 9361 <span class="cm"> * gtk_widget_get_parent:</span> Err codemadness.org 70 i 9362 <span class="cm"> * @widget: a #GtkWidget</span> Err codemadness.org 70 i 9363 <span class="cm"> *</span> Err codemadness.org 70 i 9364 <span class="cm"> * Returns the parent container of @widget.</span> Err codemadness.org 70 i 9365 <span class="cm"> *</span> Err codemadness.org 70 i 9366 <span class="cm"> * Returns: (transfer none) (nullable): the parent container of @widget, or %NULL</span> Err codemadness.org 70 i 9367 <span class="cm"> **/</span> Err codemadness.org 70 i 9368 <span class="n">GtkWidget</span> <span class="o">*</span> Err codemadness.org 70 i 9369 <span class="nf">gtk_widget_get_parent</span> <span class="p">(</span><span class="n">GtkWidget</span> <span class="o">*</span><span class="n">widget</span><span class="p">)</span> Err codemadness.org 70 i 9370 <span class="p">{</span> Err codemadness.org 70 i 9371 <span class="p">...</span> Err codemadness.org 70 i 9372 <span class="p">}</span> Err codemadness.org 70 i 9373 </code></pre></div> Err codemadness.org 70 i 9374 Err codemadness.org 70 i 9375 <p>See that "<code>(transfer none) (nullable)</code>" in the documentation comments? Err codemadness.org 70 i 9376 The <code>(transfer none)</code> means that the return value is a pointer whose Err codemadness.org 70 i 9377 ownership does <em>not</em> get transferred to the caller, i.e. the widget Err codemadness.org 70 i 9378 retains ownership. Finally, the <code>(nullable)</code> indicates that the Err codemadness.org 70 i 9379 function can return <code>NULL</code>, when the widget has no parent.</p> Err codemadness.org 70 i 9380 <p>A language binding will then use this information as follows:</p> Err codemadness.org 70 i 9381 <ul> Err codemadness.org 70 i 9382 <li> Err codemadness.org 70 i 9383 <p>It will not <code>unref()</code> the parent widget when it is done with it.</p> Err codemadness.org 70 i 9384 </li> Err codemadness.org 70 i 9385 <li> Err codemadness.org 70 i 9386 <p>It will deal with a <code>NULL</code> pointer in a special way, instead of Err codemadness.org 70 i 9387 assuming that references are not null.</p> Err codemadness.org 70 i 9388 </li> Err codemadness.org 70 i 9389 </ul> Err codemadness.org 70 i 9390 <p>Every now and then someone discovers a public function which is Err codemadness.org 70 i 9391 lacking an annotation of that sort — for GNOME's purposes this is a Err codemadness.org 70 i 9392 bug; fortunately, it is easy to add that annotation to the C sources Err codemadness.org 70 i 9393 and regenerate the machine-readable descriptions.</p> Err codemadness.org 70 i 9394 <h2>Machine-readable descriptions, or repository files</h2> Err codemadness.org 70 i 9395 <p>So, what do those machine-readable descriptions actually look like? Err codemadness.org 70 i 9396 They moved away from a Scheme-like language and got turned into XML, Err codemadness.org 70 i 9397 because early XXIst century.</p> Err codemadness.org 70 i 9398 <p>The machine-readable descriptions are called <em>GObject Introspection Err codemadness.org 70 i 9399 Repository files</em>, or GIR for short.</p> Err codemadness.org 70 i 9400 <p>Let's look at some parts of <code>Gtk-3.0.gir</code>, which your distro may put in Err codemadness.org 70 i 9401 <code>/usr/share/gir-1.0/Gtk-3.0.gir</code>.</p> Err codemadness.org 70 i 9402 <div class="highlight"><pre><span></span><code><span class="nt">&lt;repository</span> <span class="na">version=</span><span class="s">&quot;1.2&quot;</span> <span class="err">...</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9403 Err codemadness.org 70 i 9404 <span class="nt">&lt;namespace</span> <span class="na">name=</span><span class="s">&quot;Gtk&quot;</span> Err codemadness.org 70 i 9405 <span class="na">version=</span><span class="s">&quot;3.0&quot;</span> Err codemadness.org 70 i 9406 <span class="na">shared-library=</span><span class="s">&quot;libgtk-3.so.0,libgdk-3.so.0&quot;</span> Err codemadness.org 70 i 9407 <span class="na">c:identifier-prefixes=</span><span class="s">&quot;Gtk&quot;</span> Err codemadness.org 70 i 9408 <span class="na">c:symbol-prefixes=</span><span class="s">&quot;gtk&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9409 </code></pre></div> Err codemadness.org 70 i 9410 Err codemadness.org 70 i 9411 <p>For the toplevel "<code>Gtk</code>" namespace, this is what the <code>.so</code> library is Err codemadness.org 70 i 9412 called. All identifiers have "<code>Gtk</code>" or "<code>gtk</code>" prefixes.</p> Err codemadness.org 70 i 9413 <h3>A class with methods and a signal</h3> Err codemadness.org 70 i 9414 <p>Let's look at the description for <code>GtkEntry</code>...</p> Err codemadness.org 70 i 9415 <div class="highlight"><pre><span></span><code> <span class="nt">&lt;class</span> <span class="na">name=</span><span class="s">&quot;Entry&quot;</span> Err codemadness.org 70 i 9416 <span class="na">c:symbol-prefix=</span><span class="s">&quot;entry&quot;</span> Err codemadness.org 70 i 9417 <span class="na">c:type=</span><span class="s">&quot;GtkEntry&quot;</span> Err codemadness.org 70 i 9418 <span class="na">parent=</span><span class="s">&quot;Widget&quot;</span> Err codemadness.org 70 i 9419 <span class="na">glib:type-name=</span><span class="s">&quot;GtkEntry&quot;</span> Err codemadness.org 70 i 9420 <span class="na">glib:get-type=</span><span class="s">&quot;gtk_entry_get_type&quot;</span> Err codemadness.org 70 i 9421 <span class="na">glib:type-struct=</span><span class="s">&quot;EntryClass&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9422 Err codemadness.org 70 i 9423 <span class="nt">&lt;doc</span> <span class="na">xml:space=</span><span class="s">&quot;preserve&quot;</span><span class="nt">&gt;</span>The #GtkEntry widget is a single line text entry Err codemadness.org 70 i 9424 widget. A fairly large set of key bindings are supported Err codemadness.org 70 i 9425 by default. If the entered text is longer than the allocation Err codemadness.org 70 i 9426 ... Err codemadness.org 70 i 9427 <span class="nt">&lt;/doc&gt;</span> Err codemadness.org 70 i 9428 </code></pre></div> Err codemadness.org 70 i 9429 Err codemadness.org 70 i 9430 <p>This is the start of the description for <code>GtkEntry</code>. We already know Err codemadness.org 70 i 9431 that everything is prefixed with "<code>Gtk</code>", so the name is just given as Err codemadness.org 70 i 9432 "<code>Entry</code>". Its parent class is <code>Widget</code> and the function which Err codemadness.org 70 i 9433 registers it against the GObject type system is <code>gtk_entry_get_type</code>.</p> Err codemadness.org 70 i 9434 <p>Also, there are the toplevel documentation comments for the <code>Entry</code> Err codemadness.org 70 i 9435 class.</p> Err codemadness.org 70 i 9436 <p>Onwards!</p> Err codemadness.org 70 i 9437 <div class="highlight"><pre><span></span><code> <span class="nt">&lt;implements</span> <span class="na">name=</span><span class="s">&quot;Atk.ImplementorIface&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9438 <span class="nt">&lt;implements</span> <span class="na">name=</span><span class="s">&quot;Buildable&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9439 <span class="nt">&lt;implements</span> <span class="na">name=</span><span class="s">&quot;CellEditable&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9440 <span class="nt">&lt;implements</span> <span class="na">name=</span><span class="s">&quot;Editable&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9441 </code></pre></div> Err codemadness.org 70 i 9442 Err codemadness.org 70 i 9443 <p>GObject classes can implement various interfaces; this is the list Err codemadness.org 70 i 9444 that <code>GtkEntry</code> supports.</p> Err codemadness.org 70 i 9445 <p>Next, let's look at a single method:</p> Err codemadness.org 70 i 9446 <div class="highlight"><pre><span></span><code> <span class="nt">&lt;method</span> <span class="na">name=</span><span class="s">&quot;get_text&quot;</span> <span class="na">c:identifier=</span><span class="s">&quot;gtk_entry_get_text&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9447 <span class="nt">&lt;doc</span> <span class="na">xml:space=</span><span class="s">&quot;preserve&quot;</span><span class="nt">&gt;</span>Retrieves the contents of the entry widget. ... <span class="nt">&lt;/doc&gt;</span> Err codemadness.org 70 i 9448 Err codemadness.org 70 i 9449 <span class="nt">&lt;return-value</span> <span class="na">transfer-ownership=</span><span class="s">&quot;none&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9450 <span class="nt">&lt;type</span> <span class="na">name=</span><span class="s">&quot;utf8&quot;</span> <span class="na">c:type=</span><span class="s">&quot;const gchar*&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9451 <span class="nt">&lt;/return-value&gt;</span> Err codemadness.org 70 i 9452 Err codemadness.org 70 i 9453 <span class="nt">&lt;parameters&gt;</span> Err codemadness.org 70 i 9454 <span class="nt">&lt;instance-parameter</span> <span class="na">name=</span><span class="s">&quot;entry&quot;</span> <span class="na">transfer-ownership=</span><span class="s">&quot;none&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9455 <span class="nt">&lt;type</span> <span class="na">name=</span><span class="s">&quot;Entry&quot;</span> <span class="na">c:type=</span><span class="s">&quot;GtkEntry*&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9456 <span class="nt">&lt;/instance-parameter&gt;</span> Err codemadness.org 70 i 9457 <span class="nt">&lt;/parameters&gt;</span> Err codemadness.org 70 i 9458 <span class="nt">&lt;/method&gt;</span> Err codemadness.org 70 i 9459 </code></pre></div> Err codemadness.org 70 i 9460 Err codemadness.org 70 i 9461 <p>The method <code>get_text</code> and its corresponding C symbol. Its return Err codemadness.org 70 i 9462 value is an UTF-8 encoded string, and ownership of the memory for that Err codemadness.org 70 i 9463 string is not transferred to the caller.</p> Err codemadness.org 70 i 9464 <p>The method takes a single parameter which is the <code>entry</code> instance itself.</p> Err codemadness.org 70 i 9465 <p>Now, let's look at a signal:</p> Err codemadness.org 70 i 9466 <div class="highlight"><pre><span></span><code> <span class="nt">&lt;glib:signal</span> <span class="na">name=</span><span class="s">&quot;activate&quot;</span> <span class="na">when=</span><span class="s">&quot;last&quot;</span> <span class="na">action=</span><span class="s">&quot;1&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9467 <span class="nt">&lt;doc</span> <span class="na">xml:space=</span><span class="s">&quot;preserve&quot;</span><span class="nt">&gt;</span>The ::activate signal is emitted when the user hits Err codemadness.org 70 i 9468 the Enter key. ...<span class="nt">&lt;/doc&gt;</span> Err codemadness.org 70 i 9469 Err codemadness.org 70 i 9470 <span class="nt">&lt;return-value</span> <span class="na">transfer-ownership=</span><span class="s">&quot;none&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9471 <span class="nt">&lt;type</span> <span class="na">name=</span><span class="s">&quot;none&quot;</span> <span class="na">c:type=</span><span class="s">&quot;void&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9472 <span class="nt">&lt;/return-value&gt;</span> Err codemadness.org 70 i 9473 <span class="nt">&lt;/glib:signal&gt;</span> Err codemadness.org 70 i 9474 Err codemadness.org 70 i 9475 <span class="nt">&lt;/class&gt;</span> Err codemadness.org 70 i 9476 </code></pre></div> Err codemadness.org 70 i 9477 Err codemadness.org 70 i 9478 <p>The "<code>activate</code>" signal takes no arguments, and has a return value of Err codemadness.org 70 i 9479 type <code>void</code>, i.e. no return value.</p> Err codemadness.org 70 i 9480 <h3>A struct with public fields</h3> Err codemadness.org 70 i 9481 <p>The following comes from <code>Gdk-3.0.gir</code>; it's the description for Err codemadness.org 70 i 9482 <code>GdkRectangle</code>.</p> Err codemadness.org 70 i 9483 <div class="highlight"><pre><span></span><code> <span class="nt">&lt;record</span> <span class="na">name=</span><span class="s">&quot;Rectangle&quot;</span> Err codemadness.org 70 i 9484 <span class="na">c:type=</span><span class="s">&quot;GdkRectangle&quot;</span> Err codemadness.org 70 i 9485 <span class="na">glib:type-name=</span><span class="s">&quot;GdkRectangle&quot;</span> Err codemadness.org 70 i 9486 <span class="na">glib:get-type=</span><span class="s">&quot;gdk_rectangle_get_type&quot;</span> Err codemadness.org 70 i 9487 <span class="na">c:symbol-prefix=</span><span class="s">&quot;rectangle&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9488 Err codemadness.org 70 i 9489 <span class="nt">&lt;field</span> <span class="na">name=</span><span class="s">&quot;x&quot;</span> <span class="na">writable=</span><span class="s">&quot;1&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9490 <span class="nt">&lt;type</span> <span class="na">name=</span><span class="s">&quot;gint&quot;</span> <span class="na">c:type=</span><span class="s">&quot;int&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9491 <span class="nt">&lt;/field&gt;</span> Err codemadness.org 70 i 9492 <span class="nt">&lt;field</span> <span class="na">name=</span><span class="s">&quot;y&quot;</span> <span class="na">writable=</span><span class="s">&quot;1&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9493 <span class="nt">&lt;type</span> <span class="na">name=</span><span class="s">&quot;gint&quot;</span> <span class="na">c:type=</span><span class="s">&quot;int&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9494 <span class="nt">&lt;/field&gt;</span> Err codemadness.org 70 i 9495 <span class="nt">&lt;field</span> <span class="na">name=</span><span class="s">&quot;width&quot;</span> <span class="na">writable=</span><span class="s">&quot;1&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9496 <span class="nt">&lt;type</span> <span class="na">name=</span><span class="s">&quot;gint&quot;</span> <span class="na">c:type=</span><span class="s">&quot;int&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9497 <span class="nt">&lt;/field&gt;</span> Err codemadness.org 70 i 9498 <span class="nt">&lt;field</span> <span class="na">name=</span><span class="s">&quot;height&quot;</span> <span class="na">writable=</span><span class="s">&quot;1&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9499 <span class="nt">&lt;type</span> <span class="na">name=</span><span class="s">&quot;gint&quot;</span> <span class="na">c:type=</span><span class="s">&quot;int&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9500 <span class="nt">&lt;/field&gt;</span> Err codemadness.org 70 i 9501 Err codemadness.org 70 i 9502 <span class="nt">&lt;/record&gt;</span> Err codemadness.org 70 i 9503 </code></pre></div> Err codemadness.org 70 i 9504 Err codemadness.org 70 i 9505 <p>So that's the <code>x/y/width/height</code> fields in the struct, in the same Err codemadness.org 70 i 9506 order as they are defined in the C code.</p> Err codemadness.org 70 i 9507 <p>And so on. The idea is for the whole API exported by a GObject Err codemadness.org 70 i 9508 library to be describable by that format. If something can't be Err codemadness.org 70 i 9509 described, it's a bug in the library, or a bug in the format.</p> Err codemadness.org 70 i 9510 <h1>Making language bindings start up quickly: typelib files</h1> Err codemadness.org 70 i 9511 <p>As we saw, the GIR files are the XML descriptions of GObject APIs. Err codemadness.org 70 i 9512 Dynamic languages like Python would prefer to generate the language Err codemadness.org 70 i 9513 binding on the fly, as needed, instead of pre-generating a huge Err codemadness.org 70 i 9514 binding.</p> Err codemadness.org 70 i 9515 <p>However, GTK+ is a big API: <code>Gtk-3.0.gir</code> is 7 MB of XML. Parsing Err codemadness.org 70 i 9516 all of that just to be able to generate <code>gtk_widget_show()</code> on the fly Err codemadness.org 70 i 9517 would be too slow. Also, there are GTK+'s dependencies: Atk, Gdk, Err codemadness.org 70 i 9518 Cairo, etc. You don't want to parse <em>everything</em> just to start up!</p> Err codemadness.org 70 i 9519 <p>So, we have an extra step that compiles the GIR files down to binary Err codemadness.org 70 i 9520 <code>.typelib</code> files. For example, Err codemadness.org 70 i 9521 <code>/usr/lib64/girepository-1.0/Gtk-3.0.typelib</code> is about 600 KB on my Err codemadness.org 70 i 9522 machine. Those files get <code>mmap()</code>ed for fast access, and can be Err codemadness.org 70 i 9523 shared between processes.</p> Err codemadness.org 70 i 9524 <h2>How dynamic language bindings use typelib files</h2> Err codemadness.org 70 i 9525 <p>GObject Introspection comes with a library that language binding Err codemadness.org 70 i 9526 implementors can use to consume those <code>.typelib</code> files. The Err codemadness.org 70 i 9527 <code>libgirepository</code> library has functions like "list all the classes Err codemadness.org 70 i 9528 available in this namespace", or "call this function with these Err codemadness.org 70 i 9529 values for arguments, and give me back the return value here".</p> Err codemadness.org 70 i 9530 <p>Internally, <code>libgirepository</code> uses <code>libffi</code> to actually call the C Err codemadness.org 70 i 9531 functions in the dynamically-linked libraries.</p> Err codemadness.org 70 i 9532 <p>So, when you write <code>foo.py</code> and do</p> Err codemadness.org 70 i 9533 <div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">gi</span> Err codemadness.org 70 i 9534 <span class="n">gi</span><span class="o">.</span><span class="n">require_version</span><span class="p">(</span><span class="s1">&#39;Gtk&#39;</span><span class="p">,</span> <span class="s1">&#39;3.0&#39;</span><span class="p">)</span> Err codemadness.org 70 i 9535 <span class="kn">from</span> <span class="nn">gi.repository</span> <span class="kn">import</span> <span class="n">Gtk</span> Err codemadness.org 70 i 9536 <span class="n">win</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Window</span><span class="p">()</span> Err codemadness.org 70 i 9537 </code></pre></div> Err codemadness.org 70 i 9538 Err codemadness.org 70 i 9539 <p>what happens is that <code>pygobject</code> calls <code>libgirepository</code> to <code>mmap()</code> Err codemadness.org 70 i 9540 the <code>.typelib</code>, and sees that the constructor for <code>Gtk.Window</code> is a C Err codemadness.org 70 i 9541 function called <code>gtk_window_new()</code>. After seeing how that function Err codemadness.org 70 i 9542 wants to be called, it calls the function using <code>libffi</code>, wraps the Err codemadness.org 70 i 9543 result with a <code>PyObject</code>, and that's what you get on the Python side.</p> Err codemadness.org 70 i 9544 <h1>Static languages</h1> Err codemadness.org 70 i 9545 <p>A static language like Rust prefers to have the whole language binding Err codemadness.org 70 i 9546 pre-generated. This is what the various crates in <a href="https://github.com/gtk-rs/">gtk-rs</a> Err codemadness.org 70 i 9547 do.</p> Err codemadness.org 70 i 9548 <p><a href="https://github.com/gtk-rs/gir/tree/master/src">The gir crate</a> takes a <code>.gir</code> file (i.e. the XML descriptions) Err codemadness.org 70 i 9549 and does two things:</p> Err codemadness.org 70 i 9550 <ul> Err codemadness.org 70 i 9551 <li> Err codemadness.org 70 i 9552 <p>Reconstructs the C function prototypes and C struct declarations, Err codemadness.org 70 i 9553 but in a way Rust can understand them. This gets output to the <a href="https://github.com/gtk-rs/sys">sys Err codemadness.org 70 i 9554 crate</a>.</p> Err codemadness.org 70 i 9555 </li> Err codemadness.org 70 i 9556 <li> Err codemadness.org 70 i 9557 <p>Creates idiomatic Rust code for the language binding. This gets Err codemadness.org 70 i 9558 output to the various crates; for example, <a href="https://github.com/gtk-rs/gtk">the gtk one</a>.</p> Err codemadness.org 70 i 9559 </li> Err codemadness.org 70 i 9560 </ul> Err codemadness.org 70 i 9561 <p>When reconstructing the C structs and prototypes, we get stuff like</p> Err codemadness.org 70 i 9562 <div class="highlight"><pre><span></span><code><span class="cp">#[repr(C)]</span><span class="w"></span> Err codemadness.org 70 i 9563 <span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">GtkWidget</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 9564 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">parent_instance</span>: <span class="nc">gobject</span>::<span class="n">GInitiallyUnowned</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 9565 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">priv_</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">GtkWidgetPrivate</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 9566 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 9567 Err codemadness.org 70 i 9568 <span class="k">extern</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 9569 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">gtk_entry_new</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">GtkWidget</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 9570 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 9571 </code></pre></div> Err codemadness.org 70 i 9572 Err codemadness.org 70 i 9573 <p>And the idiomatic bindings? Stay tuned!</p>Librsvg's build infrastructure: Autotools and Rust2017-09-01T18:15:29-05:002017-11-11T09:37:08-06:00Federico Mena Quinterotag:people.gnome.org,2017-09-01:/~federico/blog/librsvg-build-infrastructure.html<p>Today I released <a href="https://mail.gnome.org/archives/desktop-devel-list/2017-September/msg00008.html">librsvg 2.41.1</a>, and it's a big release! Err codemadness.org 70 i 9574 Apart from all the Rust goodness, and the large number of bug fixes, I Err codemadness.org 70 i 9575 am very happy with the way the build system works these days. I've Err codemadness.org 70 i 9576 found it invaluable to have good examples of Autotools incantations to …</p><p>Today I released <a href="https://mail.gnome.org/archives/desktop-devel-list/2017-September/msg00008.html">librsvg 2.41.1</a>, and it's a big release! Err codemadness.org 70 i 9577 Apart from all the Rust goodness, and the large number of bug fixes, I Err codemadness.org 70 i 9578 am very happy with the way the build system works these days. I've Err codemadness.org 70 i 9579 found it invaluable to have good examples of Autotools incantations to Err codemadness.org 70 i 9580 copy&amp;paste, so hopefully this will be useful to someone else.</p> Err codemadness.org 70 i 9581 <p>There are some subtleties that a "good" autotools setup demands, and Err codemadness.org 70 i 9582 so far I think librsvg is doing well:</p> Err codemadness.org 70 i 9583 <ul> Err codemadness.org 70 i 9584 <li> Err codemadness.org 70 i 9585 <p>The <code>configure</code> script checks for <code>cargo</code> and <code>rustc</code>.</p> Err codemadness.org 70 i 9586 </li> Err codemadness.org 70 i 9587 <li> Err codemadness.org 70 i 9588 <p>"<code>make distcheck</code>" works. This means that the build can be Err codemadness.org 70 i 9589 performed with <code>builddir != srcdir</code>, and also that <code>make check</code> runs Err codemadness.org 70 i 9590 the available tests and they all pass.</p> Err codemadness.org 70 i 9591 </li> Err codemadness.org 70 i 9592 <li> Err codemadness.org 70 i 9593 <p>The <code>rsvg_internals</code> library is built with Rust, and our Err codemadness.org 70 i 9594 <code>Makefile.am</code> calls <code>cargo build</code> with the correct options. It is Err codemadness.org 70 i 9595 able to handle debug and release builds.</p> Err codemadness.org 70 i 9596 </li> Err codemadness.org 70 i 9597 <li> Err codemadness.org 70 i 9598 <p>"<code>make clean</code>" cleans up the Rust build directories as well.</p> Err codemadness.org 70 i 9599 </li> Err codemadness.org 70 i 9600 <li> Err codemadness.org 70 i 9601 <p>If you change a <code>.rs</code> file and type <code>make</code>, only the necessary stuff Err codemadness.org 70 i 9602 gets rebuilt.</p> Err codemadness.org 70 i 9603 </li> Err codemadness.org 70 i 9604 <li> Err codemadness.org 70 i 9605 <p>Etcetera. I think librsvg feels like a normal autotool'ed library. Err codemadness.org 70 i 9606 Let's see how this is done.</p> Err codemadness.org 70 i 9607 </li> Err codemadness.org 70 i 9608 </ul> Err codemadness.org 70 i 9609 <h1>Librsvg's basic autotools setup</h1> Err codemadness.org 70 i 9610 <p>Librsvg started out with a fairly traditional autotools setup with a Err codemadness.org 70 i 9611 <a href="https://git.gnome.org/browse/librsvg/tree/configure.ac?h=2.41.1"><code>configure.ac</code></a> and <a href="https://git.gnome.org/browse/librsvg/tree/Makefile.am?h=2.41.1"><code>Makefile.am</code></a>. For Err codemadness.org 70 i 9612 historical reasons the <code>.[ch]</code> source files live in the toplevel Err codemadness.org 70 i 9613 <code>librsvg/</code> directory, not in a <code>src</code> subdirectory or something like Err codemadness.org 70 i 9614 that.</p> Err codemadness.org 70 i 9615 <div class="highlight"><pre><span></span><code>librsvg Err codemadness.org 70 i 9616 ├ configure.ac Err codemadness.org 70 i 9617 ├ Makefile.am Err codemadness.org 70 i 9618 ├ *.[ch] Err codemadness.org 70 i 9619 ├ src/ Err codemadness.org 70 i 9620 ├ doc/ Err codemadness.org 70 i 9621 ├ tests/ Err codemadness.org 70 i 9622 └ win32/ Err codemadness.org 70 i 9623 </code></pre></div> Err codemadness.org 70 i 9624 Err codemadness.org 70 i 9625 <h1>Adding Rust to the build</h1> Err codemadness.org 70 i 9626 <p>The Rust source code lives in <a href="https://git.gnome.org/browse/librsvg/tree/rust?h=2.41.1"><code>librsvg/rust</code></a>; that's Err codemadness.org 70 i 9627 where <a href="https://git.gnome.org/browse/librsvg/tree/rust/Cargo.toml?h=2.41.1"><code>Cargo.toml</code></a> lives, and of course there is the conventional Err codemadness.org 70 i 9628 <code>src</code> subdirectory with the <code>*.rs</code> files.</p> Err codemadness.org 70 i 9629 <div class="highlight"><pre><span></span><code>librsvg Err codemadness.org 70 i 9630 ├ configure.ac Err codemadness.org 70 i 9631 ├ Makefile.am Err codemadness.org 70 i 9632 ├ *.[ch] Err codemadness.org 70 i 9633 ├ src/ Err codemadness.org 70 i 9634 ├ rust/ &lt;--- this is new! Err codemadness.org 70 i 9635 │ ├ Cargo.toml Err codemadness.org 70 i 9636 │ └ src/ Err codemadness.org 70 i 9637 ├ doc/ Err codemadness.org 70 i 9638 ├ tests/ Err codemadness.org 70 i 9639 └ win32/ Err codemadness.org 70 i 9640 </code></pre></div> Err codemadness.org 70 i 9641 Err codemadness.org 70 i 9642 <h2>Detecting the presence of <code>cargo</code> and <code>rustc</code> in <code>configure.ac</code></h2> Err codemadness.org 70 i 9643 <p>This goes in <code>configure.ac</code>:</p> Err codemadness.org 70 i 9644 <div class="highlight"><pre><span></span><code>AC_CHECK_PROG<span class="o">(</span>CARGO, <span class="o">[</span>cargo<span class="o">]</span>, <span class="o">[</span>yes<span class="o">]</span>, <span class="o">[</span>no<span class="o">])</span> Err codemadness.org 70 i 9645 AS_IF<span class="o">(</span><span class="nb">test</span> x<span class="nv">$CARGO</span> <span class="o">=</span> xno, Err codemadness.org 70 i 9646 AC_MSG_ERROR<span class="o">([</span>cargo is required. Please install the Rust toolchain from https://www.rust-lang.org/<span class="o">])</span> Err codemadness.org 70 i 9647 <span class="o">)</span> Err codemadness.org 70 i 9648 AC_CHECK_PROG<span class="o">(</span>RUSTC, <span class="o">[</span>rustc<span class="o">]</span>, <span class="o">[</span>yes<span class="o">]</span>, <span class="o">[</span>no<span class="o">])</span> Err codemadness.org 70 i 9649 AS_IF<span class="o">(</span><span class="nb">test</span> x<span class="nv">$RUSTC</span> <span class="o">=</span> xno, Err codemadness.org 70 i 9650 AC_MSG_ERROR<span class="o">([</span>rustc is required. Please install the Rust toolchain from https://www.rust-lang.org/<span class="o">])</span> Err codemadness.org 70 i 9651 <span class="o">)</span> Err codemadness.org 70 i 9652 </code></pre></div> Err codemadness.org 70 i 9653 Err codemadness.org 70 i 9654 <p>These two try to execute <code>cargo</code> and <code>rustc</code>, respectively, and abort Err codemadness.org 70 i 9655 with an error message if they are not present.</p> Err codemadness.org 70 i 9656 <h2>Supporting debug or release mode for the Rust build</h2> Err codemadness.org 70 i 9657 <p>One can call cargo like "<code>cargo build --release</code>" to turn on expensive Err codemadness.org 70 i 9658 optimizations, or normally like just "<code>cargo build</code>" to build with Err codemadness.org 70 i 9659 debug information. That is, the latter is the default: if you don't Err codemadness.org 70 i 9660 pass any options, cargo does a debug build.</p> Err codemadness.org 70 i 9661 <p>Autotools and C compilers normally work a bit differently; one must Err codemadness.org 70 i 9662 call the configure script like "<code>CFLAGS='-g -O0' ./configure</code>" for a Err codemadness.org 70 i 9663 debug build, or "<code>CFLAGS='-O2 -fomit-frame-pointer' ./configure</code>" for Err codemadness.org 70 i 9664 a release build.</p> Err codemadness.org 70 i 9665 <p>Linux distros already have all the infrastructure to pass the Err codemadness.org 70 i 9666 appropriate <code>CFLAGS</code> to <code>configure</code>. We need to be able to pass the Err codemadness.org 70 i 9667 appropriate flag to Cargo. My main requirement for this was:</p> Err codemadness.org 70 i 9668 <ul> Err codemadness.org 70 i 9669 <li>Distros shouldn't have to substantially change their RPM specfiles Err codemadness.org 70 i 9670 (or whatever) to accomodate the Rust build.</li> Err codemadness.org 70 i 9671 <li>I assume that distros will want to make release builds by default.</li> Err codemadness.org 70 i 9672 <li>I as a developer am comfortable with passing extra options to make Err codemadness.org 70 i 9673 debug builds on my machine.</li> Err codemadness.org 70 i 9674 </ul> Err codemadness.org 70 i 9675 <p>The scheme in librsvg lets you run "<code>configure --enable-debug</code>" to Err codemadness.org 70 i 9676 make it call a plain <code>cargo build</code>, or a plain "<code>configure</code>" to make Err codemadness.org 70 i 9677 it use <code>cargo build --release</code> instead. The <code>CFLAGS</code> are passed as Err codemadness.org 70 i 9678 usual through an environment variable. This way, distros don't have Err codemadness.org 70 i 9679 to change their packaging to keep on making release builds as usual.</p> Err codemadness.org 70 i 9680 <p>This goes in <code>configure.ac</code>:</p> Err codemadness.org 70 i 9681 <div class="highlight"><pre><span></span><code>dnl Specify --enable-debug to make a development release. By default, Err codemadness.org 70 i 9682 dnl we build <span class="k">in</span> public release mode. Err codemadness.org 70 i 9683 Err codemadness.org 70 i 9684 AC_ARG_ENABLE<span class="o">(</span>debug, Err codemadness.org 70 i 9685 AC_HELP_STRING<span class="o">([</span>--enable-debug<span class="o">]</span>, Err codemadness.org 70 i 9686 <span class="o">[</span>Build Rust code with debugging information <span class="o">[</span><span class="nv">default</span><span class="o">=</span>no<span class="o">]])</span>, Err codemadness.org 70 i 9687 <span class="o">[</span><span class="nv">debug_release</span><span class="o">=</span><span class="nv">$enableval</span><span class="o">]</span>, Err codemadness.org 70 i 9688 <span class="o">[</span><span class="nv">debug_release</span><span class="o">=</span>no<span class="o">])</span> Err codemadness.org 70 i 9689 Err codemadness.org 70 i 9690 AC_MSG_CHECKING<span class="o">(</span>whether to build Rust code with debugging information<span class="o">)</span> Err codemadness.org 70 i 9691 <span class="k">if</span> <span class="nb">test</span> <span class="s2">&quot;x</span><span class="nv">$debug_release</span><span class="s2">&quot;</span> <span class="o">=</span> <span class="s2">&quot;xyes&quot;</span> <span class="p">;</span> <span class="k">then</span> Err codemadness.org 70 i 9692 AC_MSG_RESULT<span class="o">(</span>yes<span class="o">)</span> Err codemadness.org 70 i 9693 <span class="nv">RUST_TARGET_SUBDIR</span><span class="o">=</span>debug Err codemadness.org 70 i 9694 <span class="k">else</span> Err codemadness.org 70 i 9695 AC_MSG_RESULT<span class="o">(</span>no<span class="o">)</span> Err codemadness.org 70 i 9696 <span class="nv">RUST_TARGET_SUBDIR</span><span class="o">=</span>release Err codemadness.org 70 i 9697 <span class="k">fi</span> Err codemadness.org 70 i 9698 AM_CONDITIONAL<span class="o">([</span>DEBUG_RELEASE<span class="o">]</span>, <span class="o">[</span><span class="nb">test</span> <span class="s2">&quot;x</span><span class="nv">$debug_release</span><span class="s2">&quot;</span> <span class="o">=</span> <span class="s2">&quot;xyes&quot;</span><span class="o">])</span> Err codemadness.org 70 i 9699 Err codemadness.org 70 i 9700 AC_SUBST<span class="o">([</span>RUST_TARGET_SUBDIR<span class="o">])</span> Err codemadness.org 70 i 9701 </code></pre></div> Err codemadness.org 70 i 9702 Err codemadness.org 70 i 9703 <p>This defines an Automake conditional called <code>DEBUG_RELEASE</code>, which we Err codemadness.org 70 i 9704 will use in <code>Makefile.am</code> later.</p> Err codemadness.org 70 i 9705 <p>It also causes <code>@RUST_TARGET_SUBDIR@</code> to be substituted in Makefile.am Err codemadness.org 70 i 9706 with either <code>debug</code> or <code>release</code>; we will see what these are about.</p> Err codemadness.org 70 i 9707 <h2>Adding Rust source files</h2> Err codemadness.org 70 i 9708 <p>The <code>librsvg/rust/src</code> directory has all the <code>*.rs</code> files, and cargo Err codemadness.org 70 i 9709 tracks their dependencies and whether they need to be rebuilt if one changes. Err codemadness.org 70 i 9710 However, since that directory is not tracked by <code>make</code>, it won't Err codemadness.org 70 i 9711 rebuild things if a Rust source file changes! So, we need to tell our Err codemadness.org 70 i 9712 <code>Makefile.am</code> about those files:</p> Err codemadness.org 70 i 9713 <div class="highlight"><pre><span></span><code><span class="nv">RUST_SOURCES</span> <span class="o">=</span> <span class="se">\</span> Err codemadness.org 70 i 9714 rust/build.rs <span class="se">\</span> Err codemadness.org 70 i 9715 rust/Cargo.toml <span class="se">\</span> Err codemadness.org 70 i 9716 rust/src/aspect_ratio.rs <span class="se">\</span> Err codemadness.org 70 i 9717 rust/src/bbox.rs <span class="se">\</span> Err codemadness.org 70 i 9718 rust/src/cnode.rs <span class="se">\</span> Err codemadness.org 70 i 9719 rust/src/color.rs <span class="se">\</span> Err codemadness.org 70 i 9720 ... Err codemadness.org 70 i 9721 Err codemadness.org 70 i 9722 <span class="nv">RUST_EXTRA</span> <span class="o">=</span> <span class="se">\</span> Err codemadness.org 70 i 9723 rust/Cargo.lock Err codemadness.org 70 i 9724 Err codemadness.org 70 i 9725 <span class="nv">EXTRA_DIST</span> <span class="o">+=</span> <span class="k">$(</span>RUST_SOURCES<span class="k">)</span> <span class="k">$(</span>RUST_EXTRA<span class="k">)</span> Err codemadness.org 70 i 9726 </code></pre></div> Err codemadness.org 70 i 9727 Err codemadness.org 70 i 9728 <p>It's a bit unfortunate that the change tracking is duplicated in the Err codemadness.org 70 i 9729 <code>Makefile</code>, but we are already used to listing all the C source files Err codemadness.org 70 i 9730 in there, anyway.</p> Err codemadness.org 70 i 9731 <p>Most notably, the <code>rust</code> subdirectory is <em>not</em> listed in the <code>SUBDIRS</code> Err codemadness.org 70 i 9732 in <code>Makefile.am</code>, since there is no <code>rust/Makefile</code> at all!</p> Err codemadness.org 70 i 9733 <h2>Cargo release or debug build?</h2> Err codemadness.org 70 i 9734 <div class="highlight"><pre><span></span><code><span class="cp">if DEBUG_RELEASE</span> Err codemadness.org 70 i 9735 <span class="nv">CARGO_RELEASE_ARGS</span><span class="o">=</span> Err codemadness.org 70 i 9736 <span class="cp">else</span> Err codemadness.org 70 i 9737 <span class="nv">CARGO_RELEASE_ARGS</span><span class="o">=</span>--release Err codemadness.org 70 i 9738 <span class="cp">endif</span> Err codemadness.org 70 i 9739 </code></pre></div> Err codemadness.org 70 i 9740 Err codemadness.org 70 i 9741 <p>We will call <code>cargo build</code> with that argument later.</p> Err codemadness.org 70 i 9742 <h2>Verbose or quiet build?</h2> Err codemadness.org 70 i 9743 <p>Librsvg uses <code>AM_SILENT_RULES([yes])</code> in <code>configure.ac</code>. This lets Err codemadness.org 70 i 9744 you just run "<code>make</code>" for a quiet build, or "<code>make V=1</code>" to get the Err codemadness.org 70 i 9745 full command lines passed to the compiler. Cargo supports something Err codemadness.org 70 i 9746 similar, so let's add it to <code>Makefile.am</code>:</p> Err codemadness.org 70 i 9747 <div class="highlight"><pre><span></span><code><span class="nv">CARGO_VERBOSE</span> <span class="o">=</span> <span class="k">$(</span>cargo_verbose_<span class="k">$(</span>V<span class="k">))</span> Err codemadness.org 70 i 9748 <span class="nv">cargo_verbose_</span> <span class="o">=</span> <span class="k">$(</span>cargo_verbose_<span class="k">$(</span>AM_DEFAULT_VERBOSITY<span class="k">))</span> Err codemadness.org 70 i 9749 <span class="nv">cargo_verbose_0</span> <span class="o">=</span> Err codemadness.org 70 i 9750 <span class="nv">cargo_verbose_1</span> <span class="o">=</span> --verbose Err codemadness.org 70 i 9751 </code></pre></div> Err codemadness.org 70 i 9752 Err codemadness.org 70 i 9753 <p>This expands the <code>V</code> variable to empty, <code>0</code>, or <code>1</code>. The result of Err codemadness.org 70 i 9754 expanding <em>that</em> gives us the final command-line argument in the Err codemadness.org 70 i 9755 <code>CARGO_VERBOSE</code> variable.</p> Err codemadness.org 70 i 9756 <h2>What's the filename of the library we are building?</h2> Err codemadness.org 70 i 9757 <div class="highlight"><pre><span></span><code><span class="nv">RUST_LIB</span><span class="o">=</span>@abs_top_builddir@/rust/target/@RUST_TARGET_SUBDIR@/librsvg_internals.a Err codemadness.org 70 i 9758 </code></pre></div> Err codemadness.org 70 i 9759 Err codemadness.org 70 i 9760 <p>Remember our <code>@RUST_TARGET_SUBDIR@</code> from <code>configure.ac</code>? If you call Err codemadness.org 70 i 9761 plain "<code>cargo build</code>", it will put the binaries in Err codemadness.org 70 i 9762 <code>rust/target/debug</code>. But if you call "<code>cargo build --release</code>", it Err codemadness.org 70 i 9763 will put the binaries in <code>rust/target/release</code>.</p> Err codemadness.org 70 i 9764 <p>With the bit above, the <code>RUST_LIB</code> variable now has the correct path Err codemadness.org 70 i 9765 for the built library. The <code>@abs_top_builddir@</code> makes it work when Err codemadness.org 70 i 9766 the build directory is not the same as the source directory.</p> Err codemadness.org 70 i 9767 <h2>Okay, so how do we call <code>cargo</code>?</h2> Err codemadness.org 70 i 9768 <div class="highlight"><pre><span></span><code><span class="nf">@abs_top_builddir@/rust/target/@RUST_TARGET_SUBDIR@/librsvg_internals.a</span><span class="o">:</span> <span class="k">$(</span><span class="nv">RUST_SOURCES</span><span class="k">)</span> Err codemadness.org 70 i 9769 <span class="nb">cd</span> <span class="k">$(</span>top_srcdir<span class="k">)</span>/rust <span class="o">&amp;&amp;</span> <span class="se">\</span> Err codemadness.org 70 i 9770 <span class="nv">CARGO_TARGET_DIR</span><span class="o">=</span>@abs_top_builddir@/rust/target cargo build <span class="k">$(</span>CARGO_VERBOSE<span class="k">)</span> <span class="k">$(</span>CARGO_RELEASE_ARGS<span class="k">)</span> Err codemadness.org 70 i 9771 </code></pre></div> Err codemadness.org 70 i 9772 Err codemadness.org 70 i 9773 <p>We make the funky library filename depend on <code>$(RUST_SOURCES)</code>. Err codemadness.org 70 i 9774 That's what will cause <code>make</code> to rebuild the Rust library if one of Err codemadness.org 70 i 9775 the Rust source files changes.</p> Err codemadness.org 70 i 9776 <p>We override the <code>CARGO_TARGET_DIR</code> with Automake's preference, and Err codemadness.org 70 i 9777 call <code>cargo build</code> with the correct arguments.</p> Err codemadness.org 70 i 9778 <h2>Linking into the main C library</h2> Err codemadness.org 70 i 9779 <div class="highlight"><pre><span></span><code><span class="err">librsvg_@RSVG_API_MAJOR_VERSION@</span><span class="nv">_la_LIBADD</span> <span class="o">=</span> <span class="se">\</span> Err codemadness.org 70 i 9780 <span class="k">$(</span>LIBRSVG_LIBS<span class="k">)</span> <span class="se">\</span> Err codemadness.org 70 i 9781 <span class="k">$(</span>LIBM<span class="k">)</span> <span class="se">\</span> Err codemadness.org 70 i 9782 <span class="k">$(</span>RUST_LIB<span class="k">)</span> Err codemadness.org 70 i 9783 </code></pre></div> Err codemadness.org 70 i 9784 Err codemadness.org 70 i 9785 <p>This expands our <code>$(RUST_LIB)</code> from above into our linker line, along Err codemadness.org 70 i 9786 with librsvg's other dependencies.</p> Err codemadness.org 70 i 9787 <h2><code>make check</code></h2> Err codemadness.org 70 i 9788 <p>This is our hook so that <code>make check</code> will cause <code>cargo test</code> to run:</p> Err codemadness.org 70 i 9789 <div class="highlight"><pre><span></span><code><span class="nf">check-local</span><span class="o">:</span> Err codemadness.org 70 i 9790 <span class="nb">cd</span> <span class="k">$(</span>srcdir<span class="k">)</span>/rust <span class="o">&amp;&amp;</span> <span class="se">\</span> Err codemadness.org 70 i 9791 <span class="nv">CARGO_TARGET_DIR</span><span class="o">=</span>@abs_top_builddir@/rust/target cargo <span class="nb">test</span> Err codemadness.org 70 i 9792 </code></pre></div> Err codemadness.org 70 i 9793 Err codemadness.org 70 i 9794 <h2><code>make clean</code></h2> Err codemadness.org 70 i 9795 <p>Same thing for <code>make clean</code> and <code>cargo clean</code>:</p> Err codemadness.org 70 i 9796 <div class="highlight"><pre><span></span><code><span class="nf">clean-local</span><span class="o">:</span> Err codemadness.org 70 i 9797 <span class="nb">cd</span> <span class="k">$(</span>top_srcdir<span class="k">)</span>/rust <span class="o">&amp;&amp;</span> <span class="se">\</span> Err codemadness.org 70 i 9798 <span class="nv">CARGO_TARGET_DIR</span><span class="o">=</span>@abs_top_builddir@/rust/target cargo clean Err codemadness.org 70 i 9799 </code></pre></div> Err codemadness.org 70 i 9800 Err codemadness.org 70 i 9801 <h1>Vendoring dependencies</h1> Err codemadness.org 70 i 9802 <p>Linux distros probably want Rust packages to come bundled with their Err codemadness.org 70 i 9803 dependencies, so that they can replace them later with newer/patched Err codemadness.org 70 i 9804 versions.</p> Err codemadness.org 70 i 9805 <p>Here is a hook so that <code>make dist</code> will cause <code>cargo vendor</code> to be Err codemadness.org 70 i 9806 run before making the tarball. That command will creates a Err codemadness.org 70 i 9807 <code>rust/vendor</code> directory with a copy of all the Rust crates that Err codemadness.org 70 i 9808 librsvg depends on.</p> Err codemadness.org 70 i 9809 <div class="highlight"><pre><span></span><code><span class="nv">RUST_EXTRA</span> <span class="o">+=</span> rust/cargo-vendor-config Err codemadness.org 70 i 9810 Err codemadness.org 70 i 9811 <span class="nf">dist-hook</span><span class="o">:</span> Err codemadness.org 70 i 9812 <span class="o">(</span><span class="nb">cd</span> <span class="k">$(</span>distdir<span class="k">)</span>/rust <span class="o">&amp;&amp;</span> <span class="se">\</span> Err codemadness.org 70 i 9813 cargo vendor -q <span class="o">&amp;&amp;</span> <span class="se">\</span> Err codemadness.org 70 i 9814 mkdir .cargo <span class="o">&amp;&amp;</span> <span class="se">\</span> Err codemadness.org 70 i 9815 cp cargo-vendor-config .cargo/config<span class="o">)</span> Err codemadness.org 70 i 9816 </code></pre></div> Err codemadness.org 70 i 9817 Err codemadness.org 70 i 9818 <p>The tarball needs to have a <code>rust/.cargo/config</code> to know where to find Err codemadness.org 70 i 9819 the vendored sources (i.e. the embedded dependencies), but we don't Err codemadness.org 70 i 9820 want <em>that</em> in our development source tree. Instead, we generate it Err codemadness.org 70 i 9821 from a <a href="https://git.gnome.org/browse/librsvg/tree/rust/cargo-vendor-config?h=2.41.1"><code>rust/cargo-vendor-config</code></a> file in our Err codemadness.org 70 i 9822 source tree:</p> Err codemadness.org 70 i 9823 <div class="highlight"><pre><span></span><code><span class="c1"># This is used after `cargo vendor` is run from `make dist`.</span> Err codemadness.org 70 i 9824 <span class="c1">#</span> Err codemadness.org 70 i 9825 <span class="c1"># In the distributed tarball, this file should end up in</span> Err codemadness.org 70 i 9826 <span class="c1"># rust/.cargo/config</span> Err codemadness.org 70 i 9827 Err codemadness.org 70 i 9828 <span class="k">[source.crates-io]</span> Err codemadness.org 70 i 9829 <span class="n">registry</span> <span class="o">=</span> <span class="s">&#39;https://github.com/rust-lang/crates.io-index&#39;</span> Err codemadness.org 70 i 9830 <span class="n">replace-with</span> <span class="o">=</span> <span class="s">&#39;vendored-sources&#39;</span> Err codemadness.org 70 i 9831 Err codemadness.org 70 i 9832 <span class="k">[source.vendored-sources]</span> Err codemadness.org 70 i 9833 <span class="n">directory</span> <span class="o">=</span> <span class="s">&#39;./vendor&#39;</span> Err codemadness.org 70 i 9834 </code></pre></div> Err codemadness.org 70 i 9835 Err codemadness.org 70 i 9836 <h1>One last thing</h1> Err codemadness.org 70 i 9837 <p>If you put this in your <code>Cargo.toml</code>, release binaries will be a lot Err codemadness.org 70 i 9838 smaller. This turns on link-time optimizations (LTO), which removes Err codemadness.org 70 i 9839 unused functions from the binary.</p> Err codemadness.org 70 i 9840 <div class="highlight"><pre><span></span><code><span class="k">[profile.release]</span> Err codemadness.org 70 i 9841 <span class="n">lto</span> <span class="o">=</span> <span class="kc">true</span> Err codemadness.org 70 i 9842 </code></pre></div> Err codemadness.org 70 i 9843 Err codemadness.org 70 i 9844 <h1>Summary and thanks</h1> Err codemadness.org 70 i 9845 <p>I think the above is some good boilerplate that you can put in your Err codemadness.org 70 i 9846 <code>configure.ac</code> / <code>Makefile.am</code> to integrate a Rust sub-library into Err codemadness.org 70 i 9847 your C code. It handles <code>make</code>-y things like <code>make clean</code> and <code>make Err codemadness.org 70 i 9848 check</code>; debug and release builds; verbose and quiet builds; Err codemadness.org 70 i 9849 <code>builddir != srcdir</code>; all the goodies.</p> Err codemadness.org 70 i 9850 <p>I think the only thing I'm missing is to check for the <code>cargo-vendor</code> Err codemadness.org 70 i 9851 binary. I'm not sure how to only check for that if I'm the one making Err codemadness.org 70 i 9852 tarballs... maybe an <code>--enable-maintainer-mode</code> flag?</p> Err codemadness.org 70 i 9853 <p>This would definitely not have been possible without prior work. Err codemadness.org 70 i 9854 Thanks to everyone who figured out Autotools before me, so I could Err codemadness.org 70 i 9855 cut&amp;paste your goodies:</p> Err codemadness.org 70 i 9856 <p><em>Update 2017/Nov/11:</em> Fixed the initialization of <code>RUST_EXTRA</code>; thanks Err codemadness.org 70 i 9857 to Tobias Mueller for catching this.</p> Err codemadness.org 70 i 9858 <ul> Err codemadness.org 70 i 9859 <li><a href="https://www.figuiere.net/hub/blog/?2016/10/07/862-rust-and-automake">Hubert Figuière's "Rust and Automake"</a></li> Err codemadness.org 70 i 9860 <li><a href="http://lukenukem.co.nz/gsoc/2017/05/17/gso_2.html">Luke Nukem's "Autotools and Rust"</a></li> Err codemadness.org 70 i 9861 <li><a href="https://github.com/endlessm/ostree/commit/9169268c31df31cc09495e2a04c30cd251f22b5d">OStree's incantation for <code>cargo vendor</code></a></li> Err codemadness.org 70 i 9862 <li><a href="https://blog.ometer.com/2017/01/10/dear-package-managers-dependency-resolution-results-should-be-in-version-control/">Havoc's "Cargo.lock should be in version control"</a></li> Err codemadness.org 70 i 9863 </ul>How Glib-rs works, part 2: Transferring lists and arrays2017-08-28T20:26:47-05:002017-08-28T20:26:47-05:00Federico Mena Quinterotag:people.gnome.org,2017-08-28:/~federico/blog/how-glib-rs-works-part-2.html<p>(<a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">First part</a> of the series, with index to all the articles)</p> Err codemadness.org 70 i 9864 <p>In the <a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">first part</a>, we saw how <a href="http://gtk-rs.org/docs/glib/">glib-rs</a> provides Err codemadness.org 70 i 9865 the <a href="http://gtk-rs.org/docs/glib/translate/trait.FromGlib.html"><code>FromGlib</code></a> and <a href="http://gtk-rs.org/docs/glib/translate/trait.ToGlib.html"><code>ToGlib</code></a> traits to let Rust Err codemadness.org 70 i 9866 code convert from/to Glib's simple types, like to convert from a Glib Err codemadness.org 70 i 9867 <code>gboolean</code> to a Rust <code>bool</code> and vice-versa. We also …</p><p>(<a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">First part</a> of the series, with index to all the articles)</p> Err codemadness.org 70 i 9868 <p>In the <a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">first part</a>, we saw how <a href="http://gtk-rs.org/docs/glib/">glib-rs</a> provides Err codemadness.org 70 i 9869 the <a href="http://gtk-rs.org/docs/glib/translate/trait.FromGlib.html"><code>FromGlib</code></a> and <a href="http://gtk-rs.org/docs/glib/translate/trait.ToGlib.html"><code>ToGlib</code></a> traits to let Rust Err codemadness.org 70 i 9870 code convert from/to Glib's simple types, like to convert from a Glib Err codemadness.org 70 i 9871 <code>gboolean</code> to a Rust <code>bool</code> and vice-versa. We also saw the special Err codemadness.org 70 i 9872 needs of strings; since they are passed by reference and are not Err codemadness.org 70 i 9873 copied as simple values, we can use Err codemadness.org 70 i 9874 <a href="http://gtk-rs.org/docs/glib/translate/trait.FromGlibPtrNone.html"><code>FromGlibPtrNone</code></a> and Err codemadness.org 70 i 9875 <a href="http://gtk-rs.org/docs/glib/translate/trait.FromGlibPtrFull.html"><code>FromGlibPtrFull</code></a> depending on what kind of Err codemadness.org 70 i 9876 <em>ownership transfer</em> we want, none for "just make it look like we are Err codemadness.org 70 i 9877 using a borrowed reference", or full for "I'll take over the data and Err codemadness.org 70 i 9878 free it when I'm done". Going the other way around, we can use Err codemadness.org 70 i 9879 <a href="http://gtk-rs.org/docs/glib/translate/trait.ToGlibPtr.html"><code>ToGlibPtr</code></a> and its methods to pass things from Rust <em>to</em> Err codemadness.org 70 i 9880 Glib.</p> Err codemadness.org 70 i 9881 <p>In this part, we'll see the tools that glib-rs provides to do Err codemadness.org 70 i 9882 conversions of more complex data types. We'll look at two cases:</p> Err codemadness.org 70 i 9883 <ul> Err codemadness.org 70 i 9884 <li> Err codemadness.org 70 i 9885 <p><a href="#null-term-string-array">Passing null-terminated arrays of strings</a> Err codemadness.org 70 i 9886 from Glib to Rust</p> Err codemadness.org 70 i 9887 </li> Err codemadness.org 70 i 9888 <li> Err codemadness.org 70 i 9889 <p><a href="#passing-glists">Passing <code>GList</code>s to Rust</a></p> Err codemadness.org 70 i 9890 </li> Err codemadness.org 70 i 9891 </ul> Err codemadness.org 70 i 9892 <p>And one final case just in passing:</p> Err codemadness.org 70 i 9893 <ul> Err codemadness.org 70 i 9894 <li><a href="#passing-containers-to-glib">Passing containers from Rust to Glib</a></li> Err codemadness.org 70 i 9895 </ul> Err codemadness.org 70 i 9896 <h1>Passing arrays from Glib to Rust</h1> Err codemadness.org 70 i 9897 <p>We'll look at the case for transferring null-terminated arrays of Err codemadness.org 70 i 9898 strings, since it's an interesting one. There are other traits to Err codemadness.org 70 i 9899 convert from Glib arrays whose length is known, not implied with a Err codemadness.org 70 i 9900 NULL element, but for now we'll only look at arrays of strings.</p> Err codemadness.org 70 i 9901 <h2 id="null-term-string-array">Null-terminated arrays of strings</h2> Err codemadness.org 70 i 9902 <p>Look at this function for <code>GtkAboutDialog</code>:</p> Err codemadness.org 70 i 9903 <div class="highlight"><pre><span></span><code><span class="cm">/**</span> Err codemadness.org 70 i 9904 <span class="cm"> * gtk_about_dialog_add_credit_section:</span> Err codemadness.org 70 i 9905 <span class="cm"> * @about: A #GtkAboutDialog</span> Err codemadness.org 70 i 9906 <span class="cm"> * @section_name: The name of the section</span> Err codemadness.org 70 i 9907 <span class="cm"> * @people: (array zero-terminated=1): The people who belong to that section</span> Err codemadness.org 70 i 9908 <span class="cm"> * ...</span> Err codemadness.org 70 i 9909 <span class="cm"> */</span> Err codemadness.org 70 i 9910 <span class="kt">void</span> Err codemadness.org 70 i 9911 <span class="n">gtk_about_dialog_add_credit_section</span> <span class="p">(</span><span class="n">GtkAboutDialog</span> <span class="o">*</span><span class="n">about</span><span class="p">,</span> Err codemadness.org 70 i 9912 <span class="k">const</span> <span class="n">gchar</span> <span class="o">*</span><span class="n">section_name</span><span class="p">,</span> Err codemadness.org 70 i 9913 <span class="k">const</span> <span class="n">gchar</span> <span class="o">**</span><span class="n">people</span><span class="p">)</span> Err codemadness.org 70 i 9914 </code></pre></div> Err codemadness.org 70 i 9915 Err codemadness.org 70 i 9916 <p>You would use this like</p> Err codemadness.org 70 i 9917 <div class="highlight"><pre><span></span><code><span class="k">const</span> <span class="n">gchar</span> <span class="o">*</span><span class="n">translators</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> Err codemadness.org 70 i 9918 <span class="s">&quot;Alice &lt;alice@example.com&gt;&quot;</span><span class="p">,</span> Err codemadness.org 70 i 9919 <span class="s">&quot;Bob &lt;bob@example.com&gt;&quot;</span><span class="p">,</span> Err codemadness.org 70 i 9920 <span class="s">&quot;Clara &lt;clara@example.com&gt;&quot;</span><span class="p">,</span> Err codemadness.org 70 i 9921 <span class="nb">NULL</span> Err codemadness.org 70 i 9922 <span class="p">};</span> Err codemadness.org 70 i 9923 Err codemadness.org 70 i 9924 <span class="n">gtk_about_dialog_add_credit_section</span> <span class="p">(</span><span class="n">my_about_dialog</span><span class="p">,</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Translators&quot;</span><span class="p">),</span> <span class="n">translators</span><span class="p">);</span> Err codemadness.org 70 i 9925 </code></pre></div> Err codemadness.org 70 i 9926 Err codemadness.org 70 i 9927 <p>The function expects an array of <code>gchar *</code>, where the last element is Err codemadness.org 70 i 9928 a NULL. Instead of passing an explicit length for the array, it's Err codemadness.org 70 i 9929 done implicitly by requiring a NULL pointer after the last element. Err codemadness.org 70 i 9930 The gtk-doc annotation says <code>(array zero-terminated=1)</code>. When we Err codemadness.org 70 i 9931 generate information for the GObject-Introspection Repository (GIR), Err codemadness.org 70 i 9932 this is what comes out:</p> Err codemadness.org 70 i 9933 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span> Err codemadness.org 70 i 9934 <span class="normal"> 2</span> Err codemadness.org 70 i 9935 <span class="normal"> 3</span> Err codemadness.org 70 i 9936 <span class="normal"> 4</span> Err codemadness.org 70 i 9937 <span class="normal"> 5</span> Err codemadness.org 70 i 9938 <span class="normal"> 6</span> Err codemadness.org 70 i 9939 <span class="normal"> 7</span> Err codemadness.org 70 i 9940 <span class="normal"> 8</span> Err codemadness.org 70 i 9941 <span class="normal"> 9</span> Err codemadness.org 70 i 9942 <span class="normal">10</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="nt">&lt;method</span> <span class="na">name=</span><span class="s">&quot;add_credit_section&quot;</span> Err codemadness.org 70 i 9943 <span class="na">c:identifier=</span><span class="s">&quot;gtk_about_dialog_add_credit_section&quot;</span> Err codemadness.org 70 i 9944 <span class="na">version=</span><span class="s">&quot;3.4&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9945 .. Err codemadness.org 70 i 9946 <span class="nt">&lt;parameter</span> <span class="na">name=</span><span class="s">&quot;people&quot;</span> <span class="na">transfer-ownership=</span><span class="s">&quot;none&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9947 <span class="nt">&lt;doc</span> <span class="na">xml:space=</span><span class="s">&quot;preserve&quot;</span><span class="nt">&gt;</span>The people who belong to that section<span class="nt">&lt;/doc&gt;</span> Err codemadness.org 70 i 9948 <span class="nt">&lt;array</span> <span class="na">c:type=</span><span class="s">&quot;gchar**&quot;</span><span class="nt">&gt;</span> Err codemadness.org 70 i 9949 <span class="nt">&lt;type</span> <span class="na">name=</span><span class="s">&quot;utf8&quot;</span> <span class="na">c:type=</span><span class="s">&quot;gchar*&quot;</span><span class="nt">/&gt;</span> Err codemadness.org 70 i 9950 <span class="nt">&lt;/array&gt;</span> Err codemadness.org 70 i 9951 <span class="nt">&lt;/parameter&gt;</span> Err codemadness.org 70 i 9952 </code></pre></div> Err codemadness.org 70 i 9953 </td></tr></table> Err codemadness.org 70 i 9954 <p>You can see the <code>transfer-ownership="none"</code> in line 5. This means Err codemadness.org 70 i 9955 that the function will not take ownership of the passed array; it will Err codemadness.org 70 i 9956 make its own copy instead. By convention, GIR assumes that arrays of Err codemadness.org 70 i 9957 strings are NULL-terminated, so there is no special annotation for Err codemadness.org 70 i 9958 that here. If we were implementing this function in Rust, how would we Err codemadness.org 70 i 9959 read that C array of UTF-8 strings and turn it into a Rust Err codemadness.org 70 i 9960 <code>Vec&lt;String&gt;</code> or something? Easy:</p> Err codemadness.org 70 i 9961 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">c_char_array</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">c_char</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">..</span><span class="p">.;</span><span class="w"> </span><span class="c1">// comes from Glib</span> Err codemadness.org 70 i 9962 <span class="kd">let</span><span class="w"> </span><span class="n">rust_translators</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FromGlibPtrContainer</span>::<span class="n">from_glib_none</span><span class="p">(</span><span class="n">c_char_array</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 9963 <span class="c1">// rust_translators is a Vec&lt;String&gt;</span> Err codemadness.org 70 i 9964 </code></pre></div> Err codemadness.org 70 i 9965 Err codemadness.org 70 i 9966 <p>Let's look at how this bad boy is implemented.</p> Err codemadness.org 70 i 9967 <h3>First stage: <code>impl FromGlibPtrContainer for Vec&lt;T&gt;</code></h3> Err codemadness.org 70 i 9968 <p>We want to go from a "<code>*mut *mut c_char</code>" (in C parlance, a "<code>gchar **</code>") Err codemadness.org 70 i 9969 to a <code>Vec&lt;String&gt;</code>. Indeed, there is an implementation of the Err codemadness.org 70 i 9970 <code>FromGlibPtrContainer</code> trait for <code>Vec</code>s Err codemadness.org 70 i 9971 <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L1136">here</a>. These are the first few lines:</p> Err codemadness.org 70 i 9972 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="o">&lt;</span><span class="n">P</span>: <span class="nc">Ptr</span><span class="p">,</span><span class="w"> </span><span class="n">PP</span>: <span class="nc">Ptr</span><span class="p">,</span><span class="w"> </span><span class="n">T</span>: <span class="nc">FromGlibPtrArrayContainerAsVec</span><span class="o">&lt;</span><span class="n">P</span><span class="p">,</span><span class="w"> </span><span class="n">PP</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">FromGlibPtrContainer</span><span class="o">&lt;</span><span class="n">P</span><span class="p">,</span><span class="w"> </span><span class="n">PP</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 9973 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none</span><span class="p">(</span><span class="n">ptr</span>: <span class="nc">PP</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 9974 <span class="w"> </span><span class="n">FromGlibPtrArrayContainerAsVec</span>::<span class="n">from_glib_none_as_vec</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 9975 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 9976 </code></pre></div> Err codemadness.org 70 i 9977 Err codemadness.org 70 i 9978 <p>So... that <code>from_glib_none()</code> will return a <code>Vec&lt;T&gt;</code>, which is what we Err codemadness.org 70 i 9979 want. Let's look at the first few lines of <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L1087"><code>FromGlibPtrArrayContainerAsVec</code></a>:</p> Err codemadness.org 70 i 9980 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span> Err codemadness.org 70 i 9981 <span class="normal">2</span> Err codemadness.org 70 i 9982 <span class="normal">3</span> Err codemadness.org 70 i 9983 <span class="normal">4</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">FromGlibPtrArrayContainerAsVec</span><span class="o">&lt;</span><span class="cp">$ffi_name</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="cp">$ffi_name</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="cp">$name</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 9984 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none_as_vec</span><span class="p">(</span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="cp">$ffi_name</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 9985 <span class="w"> </span><span class="n">FromGlibContainerAsVec</span>::<span class="n">from_glib_none_num_as_vec</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span><span class="w"> </span><span class="n">c_ptr_array_len</span><span class="p">(</span><span class="n">ptr</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 9986 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 9987 </code></pre></div> Err codemadness.org 70 i 9988 </td></tr></table> Err codemadness.org 70 i 9989 <p>Aha! This is inside a <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L1117">macro</a>, thus the <code>$ffi_name</code> garbage. Err codemadness.org 70 i 9990 It's done like that so the same trait can be implemented for <code>const</code> and Err codemadness.org 70 i 9991 <code>mut</code> pointers to <code>c_char</code>.</p> Err codemadness.org 70 i 9992 <p>See the call to <code>c_ptr_array_len()</code> in line 3? That's what figures Err codemadness.org 70 i 9993 out where the NULL pointer is at the end of the array: it figures out Err codemadness.org 70 i 9994 the array's length. </p> Err codemadness.org 70 i 9995 <h3>Second stage: <code>impl FromGlibContainerAsVec::from_glib_none_num_as_vec()</code></h3> Err codemadness.org 70 i 9996 <p>Now that the length of the array is known, the implementation calls Err codemadness.org 70 i 9997 <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L1038"><code>FromGlibContainerAsVec::from_glib_none_num_as_vec()</code></a></p> Err codemadness.org 70 i 9998 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span> Err codemadness.org 70 i 9999 <span class="normal"> 2</span> Err codemadness.org 70 i 10000 <span class="normal"> 3</span> Err codemadness.org 70 i 10001 <span class="normal"> 4</span> Err codemadness.org 70 i 10002 <span class="normal"> 5</span> Err codemadness.org 70 i 10003 <span class="normal"> 6</span> Err codemadness.org 70 i 10004 <span class="normal"> 7</span> Err codemadness.org 70 i 10005 <span class="normal"> 8</span> Err codemadness.org 70 i 10006 <span class="normal"> 9</span> Err codemadness.org 70 i 10007 <span class="normal">10</span> Err codemadness.org 70 i 10008 <span class="normal">11</span> Err codemadness.org 70 i 10009 <span class="normal">12</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">FromGlibContainerAsVec</span><span class="o">&lt;</span><span class="cp">$ffi_name</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="cp">$ffi_name</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="cp">$name</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10010 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none_num_as_vec</span><span class="p">(</span><span class="n">ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="cp">$ffi_name</span><span class="p">,</span><span class="w"> </span><span class="n">num</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10011 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10012 <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 10013 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10014 Err codemadness.org 70 i 10015 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="n">num</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10016 <span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">num</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10017 <span class="w"> </span><span class="n">res</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">from_glib_none</span><span class="p">(</span><span class="n">ptr</span>::<span class="n">read</span><span class="p">(</span><span class="n">ptr</span><span class="p">.</span><span class="n">offset</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">isize</span><span class="p">))</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="cp">$ffi_name</span><span class="p">));</span><span class="w"></span> Err codemadness.org 70 i 10018 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10019 <span class="w"> </span><span class="n">res</span><span class="w"></span> Err codemadness.org 70 i 10020 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10021 </code></pre></div> Err codemadness.org 70 i 10022 </td></tr></table> Err codemadness.org 70 i 10023 <p>Lines 3/4: If the number of elements is zero, or the array is NULL, Err codemadness.org 70 i 10024 return an empty <code>Vec</code>.</p> Err codemadness.org 70 i 10025 <p>Line 7: Allocate a <code>Vec</code> of suitable size.</p> Err codemadness.org 70 i 10026 <p>Lines 8/9: For each of the pointers in the C array, call Err codemadness.org 70 i 10027 <code>from_glib_none()</code> to convert it from a <code>*const c_char</code> to a <code>String</code>, Err codemadness.org 70 i 10028 like we saw in <a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">the first part</a>.</p> Err codemadness.org 70 i 10029 <p>Done! We started with a <code>*mut *mut c_char</code> or a <code>*const *const Err codemadness.org 70 i 10030 c_char</code> and ended up with a <code>Vec&lt;String&gt;</code>, which is what we wanted.</p> Err codemadness.org 70 i 10031 <h1 id="passing-glists">Passing <code>GList</code>s to Rust</h1> Err codemadness.org 70 i 10032 <p>Some functions don't give you an array; they give you a <code>GList</code> or Err codemadness.org 70 i 10033 <code>GSList</code>. There is an implementation of Err codemadness.org 70 i 10034 <code>FromGlibPtrArrayContainerAsVec</code> <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L1254">that understands Err codemadness.org 70 i 10035 <code>GList</code></a>:</p> Err codemadness.org 70 i 10036 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">FromGlibPtrArrayContainerAsVec</span><span class="o">&lt;&lt;</span><span class="n">T</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">GlibPtrDefault</span><span class="o">&gt;</span>::<span class="n">GlibType</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">GList</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">T</span><span class="w"></span> Err codemadness.org 70 i 10037 <span class="k">where</span><span class="w"> </span><span class="n">T</span>: <span class="nc">GlibPtrDefault</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">FromGlibPtrNone</span><span class="o">&lt;&lt;</span><span class="n">T</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">GlibPtrDefault</span><span class="o">&gt;</span>::<span class="n">GlibType</span><span class="o">&gt;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">FromGlibPtrFull</span><span class="o">&lt;&lt;</span><span class="n">T</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">GlibPtrDefault</span><span class="o">&gt;</span>::<span class="n">GlibType</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10038 Err codemadness.org 70 i 10039 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none_as_vec</span><span class="p">(</span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">GList</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10040 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">g_list_length</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10041 <span class="w"> </span><span class="n">FromGlibContainer</span>::<span class="n">from_glib_none_num</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 10042 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10043 </code></pre></div> Err codemadness.org 70 i 10044 Err codemadness.org 70 i 10045 <p>The <code>impl</code> declaration is pretty horrible, so just look at the Err codemadness.org 70 i 10046 method: <code>from_glib_none_as_vec()</code> takes in a <code>GList</code>, then calls Err codemadness.org 70 i 10047 <code>g_list_length()</code> on it, and finally calls Err codemadness.org 70 i 10048 <code>FromGlibContainer::from_glib_none_num()</code> with the length it computed.</p> Err codemadness.org 70 i 10049 <h3>I have a Glib container and its length</h3> Err codemadness.org 70 i 10050 <p>In turn, that <code>from_glib_none_num()</code> goes <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L1122">here</a>:</p> Err codemadness.org 70 i 10051 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="o">&lt;</span><span class="n">P</span><span class="p">,</span><span class="w"> </span><span class="n">PP</span>: <span class="nc">Ptr</span><span class="p">,</span><span class="w"> </span><span class="n">T</span>: <span class="nc">FromGlibContainerAsVec</span><span class="o">&lt;</span><span class="n">P</span><span class="p">,</span><span class="w"> </span><span class="n">PP</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">FromGlibContainer</span><span class="o">&lt;</span><span class="n">P</span><span class="p">,</span><span class="w"> </span><span class="n">PP</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10052 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none_num</span><span class="p">(</span><span class="n">ptr</span>: <span class="nc">PP</span><span class="p">,</span><span class="w"> </span><span class="n">num</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10053 <span class="w"> </span><span class="n">FromGlibContainerAsVec</span>::<span class="n">from_glib_none_num_as_vec</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 10054 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10055 </code></pre></div> Err codemadness.org 70 i 10056 Err codemadness.org 70 i 10057 <p>Okay, getting closer to the actual implementation.</p> Err codemadness.org 70 i 10058 <h3>Give me a vector already</h3> Err codemadness.org 70 i 10059 <p>Finally, we get to the function that <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L1211">walks the <code>GList</code></a>:</p> Err codemadness.org 70 i 10060 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span> Err codemadness.org 70 i 10061 <span class="normal"> 2</span> Err codemadness.org 70 i 10062 <span class="normal"> 3</span> Err codemadness.org 70 i 10063 <span class="normal"> 4</span> Err codemadness.org 70 i 10064 <span class="normal"> 5</span> Err codemadness.org 70 i 10065 <span class="normal"> 6</span> Err codemadness.org 70 i 10066 <span class="normal"> 7</span> Err codemadness.org 70 i 10067 <span class="normal"> 8</span> Err codemadness.org 70 i 10068 <span class="normal"> 9</span> Err codemadness.org 70 i 10069 <span class="normal">10</span> Err codemadness.org 70 i 10070 <span class="normal">11</span> Err codemadness.org 70 i 10071 <span class="normal">12</span> Err codemadness.org 70 i 10072 <span class="normal">13</span> Err codemadness.org 70 i 10073 <span class="normal">14</span> Err codemadness.org 70 i 10074 <span class="normal">15</span> Err codemadness.org 70 i 10075 <span class="normal">16</span> Err codemadness.org 70 i 10076 <span class="normal">17</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">FromGlibContainerAsVec</span><span class="o">&lt;&lt;</span><span class="n">T</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">GlibPtrDefault</span><span class="o">&gt;</span>::<span class="n">GlibType</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">GList</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">T</span><span class="w"></span> Err codemadness.org 70 i 10077 <span class="k">where</span><span class="w"> </span><span class="n">T</span>: <span class="nc">GlibPtrDefault</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">FromGlibPtrNone</span><span class="o">&lt;&lt;</span><span class="n">T</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">GlibPtrDefault</span><span class="o">&gt;</span>::<span class="n">GlibType</span><span class="o">&gt;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">FromGlibPtrFull</span><span class="o">&lt;&lt;</span><span class="n">T</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">GlibPtrDefault</span><span class="o">&gt;</span>::<span class="n">GlibType</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10078 Err codemadness.org 70 i 10079 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none_num_as_vec</span><span class="p">(</span><span class="k">mut</span><span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">GList</span><span class="p">,</span><span class="w"> </span><span class="n">num</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10080 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10081 <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 10082 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10083 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="n">num</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10084 <span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">num</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10085 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">item_ptr</span>: <span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">GlibPtrDefault</span><span class="o">&gt;</span>::<span class="n">GlibType</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Ptr</span>::<span class="n">from</span><span class="p">((</span><span class="o">*</span><span class="n">ptr</span><span class="p">).</span><span class="n">data</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10086 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">!</span><span class="n">item_ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10087 <span class="w"> </span><span class="n">res</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">from_glib_none</span><span class="p">(</span><span class="n">item_ptr</span><span class="p">));</span><span class="w"></span> Err codemadness.org 70 i 10088 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10089 <span class="w"> </span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">ptr</span><span class="p">).</span><span class="n">next</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10090 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10091 <span class="w"> </span><span class="n">res</span><span class="w"></span> Err codemadness.org 70 i 10092 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10093 </code></pre></div> Err codemadness.org 70 i 10094 </td></tr></table> Err codemadness.org 70 i 10095 <p>Again, ignore the horrible <code>impl</code> declaration and just look at Err codemadness.org 70 i 10096 <code>from_glib_none_num_as_vec()</code>.</p> Err codemadness.org 70 i 10097 <p>Line 4: that function takes in a <code>ptr</code> to a <code>GList</code>, and a <code>num</code> with Err codemadness.org 70 i 10098 the list's length, which we already computed above.</p> Err codemadness.org 70 i 10099 <p>Line 5: Return an empty vector if we have an empty list.</p> Err codemadness.org 70 i 10100 <p>Line 8: Allocate a vector of suitable capacity.</p> Err codemadness.org 70 i 10101 <p>Line 9: For each element, convert it with <code>from_glib_none()</code> and push Err codemadness.org 70 i 10102 it to the array.</p> Err codemadness.org 70 i 10103 <p>Line 14: Walk to the next element in the list.</p> Err codemadness.org 70 i 10104 <h1 id="passing-containers-to-glib">Passing containers from Rust to Glib</h1> Err codemadness.org 70 i 10105 <p>This post is getting a bit long, so I'll just mention this briefly. Err codemadness.org 70 i 10106 There is a trait <code>ToGlibContainerFromSlice</code> that takes a Rust slice, Err codemadness.org 70 i 10107 and can convert it to various Glib types.</p> Err codemadness.org 70 i 10108 <ul> Err codemadness.org 70 i 10109 <li> Err codemadness.org 70 i 10110 <p>To <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L616"><code>GSlist</code></a> and <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L566"><code>GList</code></a>. These have Err codemadness.org 70 i 10111 methods like <code>to_glib_none_from_slice()</code> and Err codemadness.org 70 i 10112 <code>to_glib_full_from_slice()</code></p> Err codemadness.org 70 i 10113 </li> Err codemadness.org 70 i 10114 <li> Err codemadness.org 70 i 10115 <p>To an <a href="https://github.com/gtk-rs/glib/blob/e46aa7f27cc74f3cdcb54d94feb4a8861df21c7f/src/translate.rs#L471">array of fundamental types</a>. Here, you can choose Err codemadness.org 70 i 10116 between <code>to_glib_none_from_slice()</code>, which gives you a <code>Stash</code> like Err codemadness.org 70 i 10117 we saw <a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">the last time</a>. Or, you can use Err codemadness.org 70 i 10118 <code>to_glib_full_from_slice()</code>, which gives you back a <code>g_malloc()</code>ed Err codemadness.org 70 i 10119 array with copied items. Finally, <code>to_glib_container_from_slice()</code> Err codemadness.org 70 i 10120 gives you back a <code>g_malloc()</code>ed array of <em>pointers</em> to values rather Err codemadness.org 70 i 10121 than plain values themselves. Which function you choose depends on Err codemadness.org 70 i 10122 which C API you want to call.</p> Err codemadness.org 70 i 10123 </li> Err codemadness.org 70 i 10124 </ul> Err codemadness.org 70 i 10125 <p>I hope this post gives you enough practice to be able to "follow the Err codemadness.org 70 i 10126 traits" for each of those if you want to look at the implementations.</p> Err codemadness.org 70 i 10127 <h1>Next up</h1> Err codemadness.org 70 i 10128 <p>Passing boxed types, like public structs.</p> Err codemadness.org 70 i 10129 <p>Passing reference-counted types.</p> Err codemadness.org 70 i 10130 <p>How glib-rs wraps GObjects.</p>How Glib-rs works, part 1: Type conversions2017-08-25T16:07:47-05:002017-08-25T16:07:47-05:00Federico Mena Quinterotag:people.gnome.org,2017-08-25:/~federico/blog/how-glib-rs-works-part-1.html<ul> Err codemadness.org 70 i 10131 <li><a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">How Glib-rs works, part 1: Type conversions</a></li> Err codemadness.org 70 i 10132 <li><a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-2.html">How Glib-rs works, part 2: Transferring lists and arrays</a></li> Err codemadness.org 70 i 10133 <li><a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-3.html">How Glib-rs works, part 3: Boxed types</a></li> Err codemadness.org 70 i 10134 </ul> Err codemadness.org 70 i 10135 <p>During the <a href="https://wiki.gnome.org/Hackfests/Rust2017">GNOME+Rust hackfest in Mexico City</a>, <a href="http://smallcultfollowing.com/babysteps/">Niko Matsakis</a> Err codemadness.org 70 i 10136 started the implementation of <a href="http://smallcultfollowing.com/babysteps/blog/2017/05/02/gnome-class-integrating-rust-and-the-gnome-object-system/">gnome-class</a>, a procedural macro Err codemadness.org 70 i 10137 that will let people implement new GObject classes in …</p><ul> Err codemadness.org 70 i 10138 <li><a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-1.html">How Glib-rs works, part 1: Type conversions</a></li> Err codemadness.org 70 i 10139 <li><a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-2.html">How Glib-rs works, part 2: Transferring lists and arrays</a></li> Err codemadness.org 70 i 10140 <li><a href="https://people.gnome.org/~federico/blog/how-glib-rs-works-part-3.html">How Glib-rs works, part 3: Boxed types</a></li> Err codemadness.org 70 i 10141 </ul> Err codemadness.org 70 i 10142 <p>During the <a href="https://wiki.gnome.org/Hackfests/Rust2017">GNOME+Rust hackfest in Mexico City</a>, <a href="http://smallcultfollowing.com/babysteps/">Niko Matsakis</a> Err codemadness.org 70 i 10143 started the implementation of <a href="http://smallcultfollowing.com/babysteps/blog/2017/05/02/gnome-class-integrating-rust-and-the-gnome-object-system/">gnome-class</a>, a procedural macro Err codemadness.org 70 i 10144 that will let people implement new GObject classes in Rust and export Err codemadness.org 70 i 10145 them to the world. Currently, if you want to write a new GObject Err codemadness.org 70 i 10146 (e.g. a new widget) and put it in a library so that it can be used Err codemadness.org 70 i 10147 from language bindings via GObject-Introspection, you have to do it in Err codemadness.org 70 i 10148 C. It would be nice to be able to do this in a safe language like Err codemadness.org 70 i 10149 Rust.</p> Err codemadness.org 70 i 10150 <h1>How would it be done by hand?</h1> Err codemadness.org 70 i 10151 <p>In a C implementation of a new GObject subclass, one calls things like Err codemadness.org 70 i 10152 <code>g_type_register_static()</code> and <code>g_signal_new()</code> by hand, while being Err codemadness.org 70 i 10153 careful to specify the correct <code>GType</code> for each value, and being Err codemadness.org 70 i 10154 super-careful about everything, as C demands.</p> Err codemadness.org 70 i 10155 <p>In Rust, one <em>can</em> in fact do exactly the same thing. You can call the Err codemadness.org 70 i 10156 same, low-level GObject and GType functions. You can use Err codemadness.org 70 i 10157 <code>#[repr(C)]</code>] for the instance and class structs that GObject will Err codemadness.org 70 i 10158 allocate for you, and which you then fill in.</p> Err codemadness.org 70 i 10159 <p>You can see an example of this in gst-plugins-rs. This is where it implements a <code>Sink</code> Err codemadness.org 70 i 10160 GObject, in Rust, by calling Glib functions by Err codemadness.org 70 i 10161 hand: <a href="https://github.com/sdroege/gst-plugin-rs/blob/782fe5dcc93dbd1c5501821659c8ae1e4331e494/gst-plugin/src/sink.rs#L297">struct declarations</a>, <a href="https://github.com/sdroege/gst-plugin-rs/blob/782fe5dcc93dbd1c5501821659c8ae1e4331e494/gst-plugin/src/sink.rs#L356"><code>class_init()</code> function</a>, Err codemadness.org 70 i 10162 <a href="https://github.com/sdroege/gst-plugin-rs/blob/782fe5dcc93dbd1c5501821659c8ae1e4331e494/gst-plugin/src/sink.rs#L479">registration of type and interfaces</a>.</p> Err codemadness.org 70 i 10163 <h1>How would it be done by a machine?</h1> Err codemadness.org 70 i 10164 <p>That's what Niko's gnome-class is about. During the hackfest it Err codemadness.org 70 i 10165 got to the point of being able to generate the code to create a new Err codemadness.org 70 i 10166 GObject subclass, register it, and export functions for methods. The Err codemadness.org 70 i 10167 syntax is not finalized yet, but it looks something like this:</p> Err codemadness.org 70 i 10168 <div class="highlight"><pre><span></span><code><span class="n">gobject_gen</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10169 <span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="n">Counter</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10170 <span class="w"> </span><span class="k">struct</span> <span class="nc">CounterPrivate</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10171 <span class="w"> </span><span class="n">val</span>: <span class="nc">Cell</span><span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="w"></span> Err codemadness.org 70 i 10172 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10173 Err codemadness.org 70 i 10174 <span class="w"> </span><span class="n">signal</span><span class="w"> </span><span class="n">value_changed</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10175 Err codemadness.org 70 i 10176 <span class="w"> </span><span class="k">fn</span> <span class="nf">set_value</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">v</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10177 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">private</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">private</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 10178 <span class="w"> </span><span class="n">private</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">set</span><span class="p">(</span><span class="n">v</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10179 <span class="w"> </span><span class="c1">// private.emit_value_changed();</span> Err codemadness.org 70 i 10180 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10181 Err codemadness.org 70 i 10182 <span class="w"> </span><span class="k">fn</span> <span class="nf">get_value</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">u32</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10183 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">private</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">private</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 10184 <span class="w"> </span><span class="n">private</span><span class="p">.</span><span class="n">val</span><span class="p">.</span><span class="n">get</span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 10185 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10186 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10187 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10188 </code></pre></div> Err codemadness.org 70 i 10189 Err codemadness.org 70 i 10190 <p>I started adding support for declaring GObject signals — mainly being Err codemadness.org 70 i 10191 able to parse them from what goes inside <code>gobject_gen!()</code> — and then Err codemadness.org 70 i 10192 being able to call <code>g_signal_newv()</code> at the appropriate time during Err codemadness.org 70 i 10193 the <code>class_init()</code> implementation.</p> Err codemadness.org 70 i 10194 <h1>Types in signals</h1> Err codemadness.org 70 i 10195 <p>Creating a signal for a GObject class is basically like specifying a Err codemadness.org 70 i 10196 function prototype: the object will invoke a callback function with Err codemadness.org 70 i 10197 certain arguments and return value when the signal is emitted. For Err codemadness.org 70 i 10198 example, this is how <code>GtkButton</code> registers its <a href="https://git.gnome.org/browse/gtk+/tree/gtk/gtkwidget.c?id=e26b60d48cc29a319b3ccc6c92157d9da1f9ceba#n2000"><code>button-press-event</code> Err codemadness.org 70 i 10199 signal</a>:</p> Err codemadness.org 70 i 10200 <div class="highlight"><pre><span></span><code> <span class="n">button_press_event_id</span> <span class="o">=</span> Err codemadness.org 70 i 10201 <span class="n">g_signal_new</span> <span class="p">(</span><span class="n">I_</span><span class="p">(</span><span class="s">&quot;button-press-event&quot;</span><span class="p">),</span> Err codemadness.org 70 i 10202 <span class="p">...</span> Err codemadness.org 70 i 10203 <span class="n">G_TYPE_BOOLEAN</span><span class="p">,</span> <span class="cm">/* type of return value */</span> Err codemadness.org 70 i 10204 <span class="mi">1</span><span class="p">,</span> <span class="cm">/* how many arguments? */</span> Err codemadness.org 70 i 10205 <span class="n">GDK_TYPE_EVENT</span><span class="p">);</span> <span class="cm">/* type of first and only argument */</span> Err codemadness.org 70 i 10206 </code></pre></div> Err codemadness.org 70 i 10207 Err codemadness.org 70 i 10208 <p><code>g_signal_new()</code> creates the signal and returns a <em>signal id</em>, an Err codemadness.org 70 i 10209 integer. Later, when the object wants to emit the signal, it uses Err codemadness.org 70 i 10210 that signal id like this:</p> Err codemadness.org 70 i 10211 <div class="highlight"><pre><span></span><code><span class="n">GtkEventButton</span> <span class="n">event</span> <span class="o">=</span> <span class="p">...;</span> Err codemadness.org 70 i 10212 <span class="n">gboolean</span> <span class="n">return_val</span><span class="p">;</span> Err codemadness.org 70 i 10213 Err codemadness.org 70 i 10214 <span class="n">g_signal_emit</span> <span class="p">(</span><span class="n">widget</span><span class="p">,</span> <span class="n">button_press_event_id</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">return_val</span><span class="p">);</span> Err codemadness.org 70 i 10215 </code></pre></div> Err codemadness.org 70 i 10216 Err codemadness.org 70 i 10217 <p>In the nice <code>gobject_gen!()</code> macro, if I am going to have a signal Err codemadness.org 70 i 10218 declaration like</p> Err codemadness.org 70 i 10219 <div class="highlight"><pre><span></span><code><span class="n">signal</span><span class="w"> </span><span class="n">button_press_event</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">event</span>: <span class="kp">&amp;</span><span class="nc">ButtonPressEvent</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10220 </code></pre></div> Err codemadness.org 70 i 10221 Err codemadness.org 70 i 10222 <p>then I will need to be able to translate the type names for Err codemadness.org 70 i 10223 <code>ButtonPressEvent</code> and <code>bool</code> into something that <code>g_signal_newv()</code> will Err codemadness.org 70 i 10224 understand: I need the <a href="https://developer.gnome.org/gobject/stable/gobject-Type-Information.html#GType">GType</a> values for those. Fundamental Err codemadness.org 70 i 10225 types like <code>gboolean</code> get constants like <a href="https://developer.gnome.org/gobject/stable/gobject-Type-Information.html#G-TYPE-BOOLEAN:CAPS">G_TYPE_BOOLEAN</a>. Types Err codemadness.org 70 i 10226 that are defined at runtime, like <code>GDK_TYPE_EVENT</code>, get GType values Err codemadness.org 70 i 10227 generated at runtime, too, when one registers the type with Err codemadness.org 70 i 10228 <code>g_type_register_*()</code>.</p> Err codemadness.org 70 i 10229 <table> Err codemadness.org 70 i 10230 <thead> Err codemadness.org 70 i 10231 <tr> Err codemadness.org 70 i 10232 <th align="left">Rust type</th> Err codemadness.org 70 i 10233 <th align="left">GType</th> Err codemadness.org 70 i 10234 </tr> Err codemadness.org 70 i 10235 </thead> Err codemadness.org 70 i 10236 <tbody> Err codemadness.org 70 i 10237 <tr> Err codemadness.org 70 i 10238 <td align="left">i32</td> Err codemadness.org 70 i 10239 <td align="left">G_TYPE_INT</td> Err codemadness.org 70 i 10240 </tr> Err codemadness.org 70 i 10241 <tr> Err codemadness.org 70 i 10242 <td align="left">u32</td> Err codemadness.org 70 i 10243 <td align="left">G_TYPE_UINT</td> Err codemadness.org 70 i 10244 </tr> Err codemadness.org 70 i 10245 <tr> Err codemadness.org 70 i 10246 <td align="left">bool</td> Err codemadness.org 70 i 10247 <td align="left">G_TYPE_BOOLEAN</td> Err codemadness.org 70 i 10248 </tr> Err codemadness.org 70 i 10249 <tr> Err codemadness.org 70 i 10250 <td align="left">etc.</td> Err codemadness.org 70 i 10251 <td align="left">etc.</td> Err codemadness.org 70 i 10252 </tr> Err codemadness.org 70 i 10253 </tbody> Err codemadness.org 70 i 10254 </table> Err codemadness.org 70 i 10255 <h1>Glib types in Rust</h1> Err codemadness.org 70 i 10256 <p>How does <a href="http://gtk-rs.org/docs/glib/">glib-rs</a>, the Rust binding to Glib and GObject, handle Err codemadness.org 70 i 10257 types?</p> Err codemadness.org 70 i 10258 <h2>Going from Glib to Rust</h2> Err codemadness.org 70 i 10259 <p>First we need a way to convert Glib's types to Rust, and vice-versa. Err codemadness.org 70 i 10260 There is a trait to convert simple Glib types into Rust types:</p> Err codemadness.org 70 i 10261 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">FromGlib</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span>: <span class="nb">Sized</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10262 <span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib</span><span class="p">(</span><span class="n">val</span>: <span class="nc">T</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10263 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10264 </code></pre></div> Err codemadness.org 70 i 10265 Err codemadness.org 70 i 10266 <p>This means, if I have a <code>T</code> which is a Glib type, this trait will give Err codemadness.org 70 i 10267 you a <code>from_glib()</code> function which will convert it to a Rust type Err codemadness.org 70 i 10268 which is <code>Sized</code>, i.e. a type whose size is known at compilation time.</p> Err codemadness.org 70 i 10269 <p>For example, this is how it is implemented for booleans:</p> Err codemadness.org 70 i 10270 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">FromGlib</span><span class="o">&lt;</span><span class="n">glib_ffi</span>::<span class="n">gboolean</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10271 <span class="w"> </span><span class="cp">#[inline]</span><span class="w"></span> Err codemadness.org 70 i 10272 <span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib</span><span class="p">(</span><span class="n">val</span>: <span class="nc">glib_ffi</span>::<span class="n">gboolean</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10273 <span class="w"> </span><span class="o">!</span><span class="p">(</span><span class="n">val</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">GFALSE</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 10274 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10275 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10276 </code></pre></div> Err codemadness.org 70 i 10277 Err codemadness.org 70 i 10278 <p>and you use it like this:</p> Err codemadness.org 70 i 10279 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">my_gboolean</span>: <span class="nc">glib_ffi</span>::<span class="n">gboolean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_some_function_that_returns_gboolean</span><span class="w"> </span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 10280 Err codemadness.org 70 i 10281 <span class="kd">let</span><span class="w"> </span><span class="n">my_rust_bool</span>: <span class="kt">bool</span> <span class="o">=</span><span class="w"> </span><span class="n">from_glib</span><span class="w"> </span><span class="p">(</span><span class="n">my_gboolean</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10282 </code></pre></div> Err codemadness.org 70 i 10283 Err codemadness.org 70 i 10284 <p>Booleans in glib and Rust have <a href="https://people.gnome.org/~federico/news-2017-04.html#gboolean-is-not-rust-bool">different sizes</a>, and also Err codemadness.org 70 i 10285 different values. Glib's booleans use the C convention: 0 is false Err codemadness.org 70 i 10286 and anything else is true, while in Rust booleans are strictly <code>false</code> Err codemadness.org 70 i 10287 or <code>true</code>, and the size is undefined (with the current Rust ABI, it's Err codemadness.org 70 i 10288 one byte).</p> Err codemadness.org 70 i 10289 <h2>Going from Rust to Glib</h2> Err codemadness.org 70 i 10290 <p>And to go the other way around, from a Rust <code>bool</code> to a <code>gboolean</code>? Err codemadness.org 70 i 10291 There is this trait:</p> Err codemadness.org 70 i 10292 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">ToGlib</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10293 <span class="w"> </span><span class="k">type</span> <span class="nc">GlibType</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10294 Err codemadness.org 70 i 10295 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_glib</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span>::<span class="n">GlibType</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10296 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10297 </code></pre></div> Err codemadness.org 70 i 10298 Err codemadness.org 70 i 10299 <p>This means, if you have a Rust type that maps to a corresponding Err codemadness.org 70 i 10300 <code>GlibType</code>, this will give you a <code>to_glib()</code> function to do the Err codemadness.org 70 i 10301 conversion.</p> Err codemadness.org 70 i 10302 <p>This is the implementation for booleans:</p> Err codemadness.org 70 i 10303 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">ToGlib</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10304 <span class="w"> </span><span class="k">type</span> <span class="nc">GlibType</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">gboolean</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10305 Err codemadness.org 70 i 10306 <span class="w"> </span><span class="cp">#[inline]</span><span class="w"></span> Err codemadness.org 70 i 10307 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_glib</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">glib_ffi</span>::<span class="n">gboolean</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10308 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">*</span><span class="bp">self</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">GTRUE</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">GFALSE</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10309 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10310 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10311 </code></pre></div> Err codemadness.org 70 i 10312 Err codemadness.org 70 i 10313 <p>And it is used like this:</p> Err codemadness.org 70 i 10314 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">my_rust_bool</span>: <span class="kt">bool</span> <span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10315 Err codemadness.org 70 i 10316 <span class="n">g_some_function_that_takes_gboolean</span><span class="w"> </span><span class="p">(</span><span class="n">my_rust_bool</span><span class="p">.</span><span class="n">to_glib</span><span class="w"> </span><span class="p">());</span><span class="w"></span> Err codemadness.org 70 i 10317 </code></pre></div> Err codemadness.org 70 i 10318 Err codemadness.org 70 i 10319 <p>(If you are thinking "a function call to marshal a boolean" — note how Err codemadness.org 70 i 10320 the functions are inlined, and the optimizer basically compiles them Err codemadness.org 70 i 10321 down to nothing.)</p> Err codemadness.org 70 i 10322 <h2 id="pointer-types">Pointer types - from Glib to Rust</h2> Err codemadness.org 70 i 10323 <p>That's all very nice for simple types like booleans and ints. Err codemadness.org 70 i 10324 Pointers to other objects are slightly more complicated.</p> Err codemadness.org 70 i 10325 <p>GObject-Introspection allows one to specify how pointer arguments to Err codemadness.org 70 i 10326 functions are handled by using a <a href="https://developer.gnome.org/gi/stable/gi-GIArgInfo.html#GITransfer"><em>transfer</em></a> specifier. </p> Err codemadness.org 70 i 10327 <h3><code>(transfer none)</code></h3> Err codemadness.org 70 i 10328 <p>For example, if you call <code>gtk_window_set_title(window, "Hello")</code>, you Err codemadness.org 70 i 10329 would expect the function to make its own copy of the <code>"Hello"</code> Err codemadness.org 70 i 10330 string. In Rust terms, you would be passing it a simple <em>borrowed Err codemadness.org 70 i 10331 reference</em>. GObject-Introspection (we'll abbreviate it as GI) calls Err codemadness.org 70 i 10332 this <code>GI_TRANSFER_NOTHING</code>, and it's specified by using Err codemadness.org 70 i 10333 <code>(transfer none)</code> in the documentation strings for function arguments Err codemadness.org 70 i 10334 or return values.</p> Err codemadness.org 70 i 10335 <p>The corresponding trait to bring in pointers from Glib to Rust, Err codemadness.org 70 i 10336 without taking ownership, is this. It's <code>unsafe</code> because it will be Err codemadness.org 70 i 10337 used to de-reference pointers that come from the wild west:</p> Err codemadness.org 70 i 10338 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">FromGlibPtrNone</span><span class="o">&lt;</span><span class="n">P</span>: <span class="nc">Ptr</span><span class="o">&gt;</span>: <span class="nb">Sized</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10339 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none</span><span class="p">(</span><span class="n">ptr</span>: <span class="nc">P</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10340 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10341 </code></pre></div> Err codemadness.org 70 i 10342 Err codemadness.org 70 i 10343 <p>And you use it via this generic function:</p> Err codemadness.org 70 i 10344 <div class="highlight"><pre><span></span><code><span class="cp">#[inline]</span><span class="w"></span> Err codemadness.org 70 i 10345 <span class="k">pub</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none</span><span class="o">&lt;</span><span class="n">P</span>: <span class="nc">Ptr</span><span class="p">,</span><span class="w"> </span><span class="n">T</span>: <span class="nc">FromGlibPtrNone</span><span class="o">&lt;</span><span class="n">P</span><span class="o">&gt;&gt;</span><span class="p">(</span><span class="n">ptr</span>: <span class="nc">P</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">T</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10346 <span class="w"> </span><span class="n">FromGlibPtrNone</span>::<span class="n">from_glib_none</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 10347 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10348 </code></pre></div> Err codemadness.org 70 i 10349 Err codemadness.org 70 i 10350 <p>Let's look at how this works. Here is the <code>FromGlibPtrNone</code> trait Err codemadness.org 70 i 10351 implemented for strings.</p> Err codemadness.org 70 i 10352 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span> Err codemadness.org 70 i 10353 <span class="normal">2</span> Err codemadness.org 70 i 10354 <span class="normal">3</span> Err codemadness.org 70 i 10355 <span class="normal">4</span> Err codemadness.org 70 i 10356 <span class="normal">5</span> Err codemadness.org 70 i 10357 <span class="normal">6</span> Err codemadness.org 70 i 10358 <span class="normal">7</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">FromGlibPtrNone</span><span class="o">&lt;*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nb">String</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10359 <span class="w"> </span><span class="cp">#[inline]</span><span class="w"></span> Err codemadness.org 70 i 10360 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_none</span><span class="p">(</span><span class="n">ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10361 <span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span> Err codemadness.org 70 i 10362 <span class="w"> </span><span class="nb">String</span>::<span class="n">from_utf8_lossy</span><span class="p">(</span><span class="n">CStr</span>::<span class="n">from_ptr</span><span class="p">(</span><span class="n">ptr</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">()).</span><span class="n">into_owned</span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 10363 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10364 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10365 </code></pre></div> Err codemadness.org 70 i 10366 </td></tr></table> Err codemadness.org 70 i 10367 <p>Line 1: given a pointer to a <code>c_char</code>, the conversion to <code>String</code>...</p> Err codemadness.org 70 i 10368 <p>Line 4: check for NULL pointers</p> Err codemadness.org 70 i 10369 <p>Line 5: Use the CStr to wrap the C Err codemadness.org 70 i 10370 <code>ptr</code>, <a href="https://people.gnome.org/~federico/blog/correctness-in-rust-reading-strings.html">like we looked at last time</a>, validate it as UTF-8 and Err codemadness.org 70 i 10371 copy the string for us.</p> Err codemadness.org 70 i 10372 <p>Unfortunately, there's a copy involved in the last step. It may be Err codemadness.org 70 i 10373 possible to use <a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html"><code>Cow&lt;&amp;str&gt;</code></a> there instead to avoid a copy if Err codemadness.org 70 i 10374 the <code>char*</code> from Glib is indeed valid UTF-8.</p> Err codemadness.org 70 i 10375 <h3><code>(transfer full)</code></h3> Err codemadness.org 70 i 10376 <p>And how about transferring ownership of the pointed-to value? There Err codemadness.org 70 i 10377 is this trait:</p> Err codemadness.org 70 i 10378 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">FromGlibPtrFull</span><span class="o">&lt;</span><span class="n">P</span>: <span class="nc">Ptr</span><span class="o">&gt;</span>: <span class="nb">Sized</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10379 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_full</span><span class="p">(</span><span class="n">ptr</span>: <span class="nc">P</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10380 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10381 </code></pre></div> Err codemadness.org 70 i 10382 Err codemadness.org 70 i 10383 <p>And the implementation for strings is as follows. In Glib's scheme of Err codemadness.org 70 i 10384 things, "transferring ownership of a string" means that the recipient Err codemadness.org 70 i 10385 of the string must eventually <code>g_free()</code> it.</p> Err codemadness.org 70 i 10386 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span> Err codemadness.org 70 i 10387 <span class="normal">2</span> Err codemadness.org 70 i 10388 <span class="normal">3</span> Err codemadness.org 70 i 10389 <span class="normal">4</span> Err codemadness.org 70 i 10390 <span class="normal">5</span> Err codemadness.org 70 i 10391 <span class="normal">6</span> Err codemadness.org 70 i 10392 <span class="normal">7</span> Err codemadness.org 70 i 10393 <span class="normal">8</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">FromGlibPtrFull</span><span class="o">&lt;*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nb">String</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10394 <span class="w"> </span><span class="cp">#[inline]</span><span class="w"></span> Err codemadness.org 70 i 10395 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_glib_full</span><span class="p">(</span><span class="n">ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10396 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">from_glib_none</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10397 <span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">g_free</span><span class="p">(</span><span class="n">ptr</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">_</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10398 <span class="w"> </span><span class="n">res</span><span class="w"></span> Err codemadness.org 70 i 10399 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10400 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10401 </code></pre></div> Err codemadness.org 70 i 10402 </td></tr></table> Err codemadness.org 70 i 10403 <p>Line 1: given a pointer to a <code>c_char</code>, the conversion to <code>String</code>...</p> Err codemadness.org 70 i 10404 <p>Line 4: Do the conversion with <code>from_glib_none()</code> with the trait we Err codemadness.org 70 i 10405 saw before, put it in <code>res</code>.</p> Err codemadness.org 70 i 10406 <p>Line 5: Call <code>g_free()</code> on the original C string.</p> Err codemadness.org 70 i 10407 <p>Line 6: Return the <code>res</code>, a Rust string which we own.</p> Err codemadness.org 70 i 10408 <h2>Pointer types - from Rust to Glib</h2> Err codemadness.org 70 i 10409 <p>Consider the case where you want to pass a <code>String</code> from Rust to a Glib function Err codemadness.org 70 i 10410 that takes a <code>*const c_char</code> — in C parlance, a <code>char *</code>, without the Err codemadness.org 70 i 10411 Glib function acquiring ownership of the string. For example, assume Err codemadness.org 70 i 10412 that the C version of <code>gtk_window_set_title()</code> is in the <code>gtk_ffi</code> Err codemadness.org 70 i 10413 module. You may want to call it like this:</p> Err codemadness.org 70 i 10414 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">rust_binding_to_window_set_title</span><span class="p">(</span><span class="n">window</span>: <span class="kp">&amp;</span><span class="nc">Gtk</span>::<span class="n">Window</span><span class="p">,</span><span class="w"> </span><span class="n">title</span>: <span class="kp">&amp;</span><span class="nb">String</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10415 <span class="w"> </span><span class="n">gtk_ffi</span>::<span class="n">gtk_window_set_title</span><span class="p">(</span><span class="o">..</span><span class="p">.,</span><span class="w"> </span><span class="n">make_c_string_from_rust_string</span><span class="p">(</span><span class="n">title</span><span class="p">));</span><span class="w"></span> Err codemadness.org 70 i 10416 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10417 </code></pre></div> Err codemadness.org 70 i 10418 Err codemadness.org 70 i 10419 <p>Now, what would that <code>make_c_string_from_rust_string()</code> look like?</p> Err codemadness.org 70 i 10420 <ul> Err codemadness.org 70 i 10421 <li> Err codemadness.org 70 i 10422 <p><strong>We have:</strong> a Rust <code>String</code> — UTF-8, known length, no nul terminator</p> Err codemadness.org 70 i 10423 </li> Err codemadness.org 70 i 10424 <li> Err codemadness.org 70 i 10425 <p><strong>We want:</strong> a <code>*const char</code> — nul-terminated UTF-8</p> Err codemadness.org 70 i 10426 </li> Err codemadness.org 70 i 10427 </ul> Err codemadness.org 70 i 10428 <p>So, let's write this:</p> Err codemadness.org 70 i 10429 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span> Err codemadness.org 70 i 10430 <span class="normal">2</span> Err codemadness.org 70 i 10431 <span class="normal">3</span> Err codemadness.org 70 i 10432 <span class="normal">4</span> Err codemadness.org 70 i 10433 <span class="normal">5</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">make_c_string_from_rust_string</span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="nb">String</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10434 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">cstr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CString</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">[</span><span class="o">..</span><span class="p">]).</span><span class="n">unwrap</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 10435 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cstr</span><span class="p">.</span><span class="n">into_raw</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10436 <span class="w"> </span><span class="n">ptr</span><span class="w"></span> Err codemadness.org 70 i 10437 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10438 </code></pre></div> Err codemadness.org 70 i 10439 </td></tr></table> Err codemadness.org 70 i 10440 <p>Line 1: Take in a <code>&amp;String</code>; return a <code>*const c_char</code>.</p> Err codemadness.org 70 i 10441 <p>Line 2: Build a <a href="https://people.gnome.org/~federico/blog/correctness-in-rust-reading-strings.html"><code>CString</code></a> like we way a few days ago: this Err codemadness.org 70 i 10442 allocates a byte buffer with space for a nul terminator, and copies Err codemadness.org 70 i 10443 the string's bytes. We <code>unwrap()</code> for this simple example, because Err codemadness.org 70 i 10444 <code>CString::new()</code> will return an error if the <code>String</code> contained nul Err codemadness.org 70 i 10445 characters in the middle of the string, which C doesn't understand.</p> Err codemadness.org 70 i 10446 <p>Line 3: Call <code>into_raw()</code> to get a pointer to the byte buffer, and Err codemadness.org 70 i 10447 cast it to a <code>*const c_char</code>. <em>We'll need to free this value later.</em></p> Err codemadness.org 70 i 10448 <p>But this kind of sucks, because we the have to use this function, pass Err codemadness.org 70 i 10449 the pointer to a C function, and then reconstitute the <code>CString</code> so it Err codemadness.org 70 i 10450 can free the byte buffer:</p> Err codemadness.org 70 i 10451 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">make_c_string_from_rust_string</span><span class="p">(</span><span class="n">my_string</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10452 <span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">c_function_that_takes_a_string</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10453 <span class="kd">let</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CString</span>::<span class="n">from_raw</span><span class="p">(</span><span class="n">buf</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">c_char</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10454 </code></pre></div> Err codemadness.org 70 i 10455 Err codemadness.org 70 i 10456 <p>The solution that Glib-rs provides for this is very Rusty, and rather Err codemadness.org 70 i 10457 elegant.</p> Err codemadness.org 70 i 10458 <h3>Stashes</h3> Err codemadness.org 70 i 10459 <p>We want:</p> Err codemadness.org 70 i 10460 <ul> Err codemadness.org 70 i 10461 <li>A temporary place to put a piece of data</li> Err codemadness.org 70 i 10462 <li>A pointer to that buffer</li> Err codemadness.org 70 i 10463 <li>Automatic memory management for both of those</li> Err codemadness.org 70 i 10464 </ul> Err codemadness.org 70 i 10465 <p>Glib-rs defines a <code>Stash</code> for this:</p> Err codemadness.org 70 i 10466 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span> Err codemadness.org 70 i 10467 <span class="normal">2</span> Err codemadness.org 70 i 10468 <span class="normal">3</span> Err codemadness.org 70 i 10469 <span class="normal">4</span> Err codemadness.org 70 i 10470 <span class="normal">5</span> Err codemadness.org 70 i 10471 <span class="normal">6</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Stash</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="c1">// we have a lifetime</span> Err codemadness.org 70 i 10472 <span class="w"> </span><span class="n">P</span>: <span class="nb">Copy</span><span class="p">,</span><span class="w"> </span><span class="c1">// the pointer must be copy-able</span> Err codemadness.org 70 i 10473 <span class="w"> </span><span class="n">T</span>: <span class="o">?</span><span class="nb">Sized</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">ToGlibPtr</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">P</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="c1">// Type for the temporary place</span> Err codemadness.org 70 i 10474 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">P</span><span class="p">,</span><span class="w"> </span><span class="c1">// We store a pointer...</span> Err codemadness.org 70 i 10475 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">ToGlibPtr</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">P</span><span class="o">&gt;&gt;</span>::<span class="n">Storage</span><span class="w"> </span><span class="c1">// ... to a piece of data with that lifetime ...</span> Err codemadness.org 70 i 10476 <span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10477 </code></pre></div> Err codemadness.org 70 i 10478 </td></tr></table> Err codemadness.org 70 i 10479 <p>... and the piece of data must be of of the <em>associated type</em> Err codemadness.org 70 i 10480 <code>ToGlibPtr::Storage</code>, which we will see shortly.</p> Err codemadness.org 70 i 10481 <p>This struct <code>Stash</code> goes along with the <code>ToGlibPtr</code> trait:</p> Err codemadness.org 70 i 10482 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">ToGlibPtr</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">P</span>: <span class="nb">Copy</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10483 <span class="w"> </span><span class="k">type</span> <span class="nc">Storage</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10484 Err codemadness.org 70 i 10485 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_glib_none</span><span class="p">(</span><span class="o">&amp;&#39;</span><span class="na">a</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Stash</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">P</span><span class="p">,</span><span class="w"> </span><span class="bp">Self</span><span class="o">&gt;</span><span class="p">;</span><span class="w"> </span><span class="c1">// returns a Stash whose temporary storage</span> Err codemadness.org 70 i 10486 <span class="w"> </span><span class="c1">// has the lifetime of our original data</span> Err codemadness.org 70 i 10487 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10488 </code></pre></div> Err codemadness.org 70 i 10489 Err codemadness.org 70 i 10490 <p>Let's unpack this by looking at the implementation of the "transfer a Err codemadness.org 70 i 10491 String to a C function while keeping ownership":</p> Err codemadness.org 70 i 10492 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span> Err codemadness.org 70 i 10493 <span class="normal">2</span> Err codemadness.org 70 i 10494 <span class="normal">3</span> Err codemadness.org 70 i 10495 <span class="normal">4</span> Err codemadness.org 70 i 10496 <span class="normal">5</span> Err codemadness.org 70 i 10497 <span class="normal">6</span> Err codemadness.org 70 i 10498 <span class="normal">7</span> Err codemadness.org 70 i 10499 <span class="normal">8</span> Err codemadness.org 70 i 10500 <span class="normal">9</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="o">&gt;</span><span class="w"> </span><span class="n">ToGlibPtr</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nb">String</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10501 <span class="w"> </span><span class="k">type</span> <span class="nc">Storage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CString</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10502 Err codemadness.org 70 i 10503 <span class="w"> </span><span class="cp">#[inline]</span><span class="w"></span> Err codemadness.org 70 i 10504 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_glib_none</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Stash</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10505 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">tmp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CString</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">[</span><span class="o">..</span><span class="p">]).</span><span class="n">unwrap</span><span class="p">();</span><span class="w"></span> Err codemadness.org 70 i 10506 <span class="w"> </span><span class="n">Stash</span><span class="p">(</span><span class="n">tmp</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">(),</span><span class="w"> </span><span class="n">tmp</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 10507 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10508 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10509 </code></pre></div> Err codemadness.org 70 i 10510 </td></tr></table> Err codemadness.org 70 i 10511 <p>Line 1: We implement <code>ToGlibPtr&lt;'a *const c_char&gt;</code> for <code>String</code>, Err codemadness.org 70 i 10512 declaring the lifetime <code>'a</code> for the <code>Stash</code>.</p> Err codemadness.org 70 i 10513 <p>Line 2: Our temporary storage is a <code>CString</code>.</p> Err codemadness.org 70 i 10514 <p>Line 6: Make a CString like before.</p> Err codemadness.org 70 i 10515 <p>Line 7: Create the <code>Stash</code> with a pointer to the CString's contents, Err codemadness.org 70 i 10516 and the CString itself.</p> Err codemadness.org 70 i 10517 <h3><code>(transfer none)</code></h3> Err codemadness.org 70 i 10518 <p>Now, we can use "<code>.0</code>" to extract the first field from our <code>Stash</code>, Err codemadness.org 70 i 10519 which is precisely the pointer we want to a byte buffer:</p> Err codemadness.org 70 i 10520 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">my_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">..</span><span class="p">.;</span><span class="w"></span> Err codemadness.org 70 i 10521 <span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">c_function_which_takes_a_string</span><span class="p">(</span><span class="n">my_string</span><span class="p">.</span><span class="n">to_glib_none</span><span class="p">().</span><span class="mi">0</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10522 </code></pre></div> Err codemadness.org 70 i 10523 Err codemadness.org 70 i 10524 <p>Now Rust knows that the temporary buffer inside the <code>Stash</code> has the lifetime of Err codemadness.org 70 i 10525 <code>my_string</code>, and it will free it automatically when the string goes Err codemadness.org 70 i 10526 out of scope. If we can accept the <code>.to_glib_none().0</code> incantation Err codemadness.org 70 i 10527 for "lending" pointers to C, this works perfectly.</p> Err codemadness.org 70 i 10528 <h3 id="ptr-transfer-full"><code>(transfer full)</code></h3> Err codemadness.org 70 i 10529 <p>And for transferring ownership to the C function? The <code>ToGlibPtr</code> Err codemadness.org 70 i 10530 trait has another method:</p> Err codemadness.org 70 i 10531 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">ToGlibPtr</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="n">P</span>: <span class="nb">Copy</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10532 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 10533 Err codemadness.org 70 i 10534 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_glib_full</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">P</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10535 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10536 </code></pre></div> Err codemadness.org 70 i 10537 Err codemadness.org 70 i 10538 <p>And here is the implementation for strings:</p> Err codemadness.org 70 i 10539 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="o">&gt;</span><span class="w"> </span><span class="n">ToGlibPtr</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nb">String</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10540 <span class="w"> </span><span class="k">fn</span> <span class="nf">to_glib_full</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10541 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10542 <span class="w"> </span><span class="n">glib_ffi</span>::<span class="n">g_strndup</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="p">,</span><span class="w"> </span> Err codemadness.org 70 i 10543 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">len</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">size_t</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 10544 <span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="w"></span> Err codemadness.org 70 i 10545 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10546 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10547 </code></pre></div> Err codemadness.org 70 i 10548 Err codemadness.org 70 i 10549 <p>We basically <code>g_strndup()</code> the Rust string's contents from its byte Err codemadness.org 70 i 10550 buffer <em>and</em> its <code>len()</code>, and we can then pass this on to C. <em>That</em> Err codemadness.org 70 i 10551 code will be responsible for <code>g_free()</code>ing the C-side string.</p> Err codemadness.org 70 i 10552 <h1>Next up</h1> Err codemadness.org 70 i 10553 <p>Transferring lists and arrays. Stay tuned!</p>Correctness in Rust: building strings2017-08-16T20:26:39-05:002017-08-16T20:26:39-05:00Federico Mena Quinterotag:people.gnome.org,2017-08-16:/~federico/blog/correctness-in-rust-reading-strings.html<p>Rust tries to follow the "make illegal states unrepresentable" mantra Err codemadness.org 70 i 10554 in several ways. In this post I'll show several things related to the Err codemadness.org 70 i 10555 process of building strings, from bytes in memory, or from a file, or Err codemadness.org 70 i 10556 from <code>char *</code> things passed from C.</p> Err codemadness.org 70 i 10557 <h1>Strings in Rust</h1> Err codemadness.org 70 i 10558 <p>The easiest way to build …</p><p>Rust tries to follow the "make illegal states unrepresentable" mantra Err codemadness.org 70 i 10559 in several ways. In this post I'll show several things related to the Err codemadness.org 70 i 10560 process of building strings, from bytes in memory, or from a file, or Err codemadness.org 70 i 10561 from <code>char *</code> things passed from C.</p> Err codemadness.org 70 i 10562 <h1>Strings in Rust</h1> Err codemadness.org 70 i 10563 <p>The easiest way to build a string is to do it directly at compile Err codemadness.org 70 i 10564 time:</p> Err codemadness.org 70 i 10565 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">my_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;Hello, world!&quot;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10566 </code></pre></div> Err codemadness.org 70 i 10567 Err codemadness.org 70 i 10568 <p>In Rust, strings are UTF-8. Here, the compiler checks our string Err codemadness.org 70 i 10569 literal is valid UTF-8. If we try to be sneaky and insert an Err codemadness.org 70 i 10570 invalid character...</p> Err codemadness.org 70 i 10571 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">my_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;Hello \xf0&quot;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10572 </code></pre></div> Err codemadness.org 70 i 10573 Err codemadness.org 70 i 10574 <p>We get a compiler error:</p> Err codemadness.org 70 i 10575 <div class="highlight"><pre><span></span><code>error: this form of character escape may only be used with characters in the range [\x00-\x7f] Err codemadness.org 70 i 10576 --&gt; foo.rs:2:30 Err codemadness.org 70 i 10577 | Err codemadness.org 70 i 10578 2 | let my_string = &quot;Hello \xf0&quot;; Err codemadness.org 70 i 10579 | ^^ Err codemadness.org 70 i 10580 </code></pre></div> Err codemadness.org 70 i 10581 Err codemadness.org 70 i 10582 <p>Rust strings know their length, unlike C strings. They <em>can</em> contain Err codemadness.org 70 i 10583 a nul character in the middle, because they don't need a nul Err codemadness.org 70 i 10584 terminator at the end.</p> Err codemadness.org 70 i 10585 <div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">my_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;Hello </span><span class="se">\x00</span><span class="s"> zero&quot;</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10586 <span class="fm">println!</span><span class="p">(</span><span class="s">&quot;{}&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">my_string</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10587 </code></pre></div> Err codemadness.org 70 i 10588 Err codemadness.org 70 i 10589 <p>The output is what you expect:</p> Err codemadness.org 70 i 10590 <div class="highlight"><pre><span></span><code>$ ./foo | hexdump -C Err codemadness.org 70 i 10591 00000000 48 65 6c 6c 6f 20 00 20 7a 65 72 6f 0a |Hello . zero.| Err codemadness.org 70 i 10592 0000000d ^ note the nul char here Err codemadness.org 70 i 10593 $ Err codemadness.org 70 i 10594 </code></pre></div> Err codemadness.org 70 i 10595 Err codemadness.org 70 i 10596 <p>So, to summarize, in Rust:</p> Err codemadness.org 70 i 10597 <ul> Err codemadness.org 70 i 10598 <li>Strings are encoded in UTF-8</li> Err codemadness.org 70 i 10599 <li>Strings know their length</li> Err codemadness.org 70 i 10600 <li>Strings can have nul chars in the middle</li> Err codemadness.org 70 i 10601 </ul> Err codemadness.org 70 i 10602 <p>This is a bit different from C:</p> Err codemadness.org 70 i 10603 <ul> Err codemadness.org 70 i 10604 <li>Strings don't exist!</li> Err codemadness.org 70 i 10605 </ul> Err codemadness.org 70 i 10606 <p>Okay, just kidding. In C:</p> Err codemadness.org 70 i 10607 <ul> Err codemadness.org 70 i 10608 <li>A lot of software has standardized on UTF-8.</li> Err codemadness.org 70 i 10609 <li>Strings don't know their length - a <code>char *</code> is a raw pointer to the Err codemadness.org 70 i 10610 beginning of the string.</li> Err codemadness.org 70 i 10611 <li>Strings conventionally have a nul terminator, that is, a zero byte Err codemadness.org 70 i 10612 that marks the end of the string. Therefore, you can't have nul Err codemadness.org 70 i 10613 characters in the middle of strings.</li> Err codemadness.org 70 i 10614 </ul> Err codemadness.org 70 i 10615 <h1>Building a string from bytes</h1> Err codemadness.org 70 i 10616 <p>Let's say you have an array of bytes and want to make a string from Err codemadness.org 70 i 10617 them. Rust won't let you just cast the array, like C would. First Err codemadness.org 70 i 10618 you need to do UTF-8 validation. For example:</p> Err codemadness.org 70 i 10619 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span> Err codemadness.org 70 i 10620 <span class="normal"> 2</span> Err codemadness.org 70 i 10621 <span class="normal"> 3</span> Err codemadness.org 70 i 10622 <span class="normal"> 4</span> Err codemadness.org 70 i 10623 <span class="normal"> 5</span> Err codemadness.org 70 i 10624 <span class="normal"> 6</span> Err codemadness.org 70 i 10625 <span class="normal"> 7</span> Err codemadness.org 70 i 10626 <span class="normal"> 8</span> Err codemadness.org 70 i 10627 <span class="normal"> 9</span> Err codemadness.org 70 i 10628 <span class="normal">10</span> Err codemadness.org 70 i 10629 <span class="normal">11</span> Err codemadness.org 70 i 10630 <span class="normal">12</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">convert_and_print</span><span class="p">(</span><span class="n">bytes</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u8</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10631 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span>::<span class="n">from_utf8</span><span class="p">(</span><span class="n">bytes</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10632 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10633 <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&quot;{}&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">string</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10634 <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&quot;{:?}&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 10635 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10636 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10637 Err codemadness.org 70 i 10638 <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10639 <span class="w"> </span><span class="n">convert_and_print</span><span class="p">(</span><span class="fm">vec!</span><span class="p">[</span><span class="mh">0x48</span><span class="p">,</span><span class="w"> </span><span class="mh">0x65</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6f</span><span class="p">]);</span><span class="w"></span> Err codemadness.org 70 i 10640 <span class="w"> </span><span class="n">convert_and_print</span><span class="p">(</span><span class="fm">vec!</span><span class="p">[</span><span class="mh">0x48</span><span class="p">,</span><span class="w"> </span><span class="mh">0x65</span><span class="p">,</span><span class="w"> </span><span class="mh">0xf0</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6f</span><span class="p">]);</span><span class="w"></span> Err codemadness.org 70 i 10641 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10642 </code></pre></div> Err codemadness.org 70 i 10643 </td></tr></table> Err codemadness.org 70 i 10644 <p>In lines 10 and 11, we call <code>convert_and_print()</code> with different Err codemadness.org 70 i 10645 arrays of bytes; the first one is valid UTF-8, and the second one Err codemadness.org 70 i 10646 isn't.</p> Err codemadness.org 70 i 10647 <p>Line 2 calls <a href="https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8"><code>String::from_utf8()</code></a>, which returns a <code>Result</code>, Err codemadness.org 70 i 10648 i.e. something with a success value or an error. In lines 3-5 we Err codemadness.org 70 i 10649 unpack this <code>Result</code>. If it's <code>Ok</code>, we print the converted string, Err codemadness.org 70 i 10650 which has been validated for UTF-8. Otherwise, we print the debug Err codemadness.org 70 i 10651 representation of the error.</p> Err codemadness.org 70 i 10652 <p>The program prints the following:</p> Err codemadness.org 70 i 10653 <div class="highlight"><pre><span></span><code>$ ~/foo Err codemadness.org 70 i 10654 Hello Err codemadness.org 70 i 10655 FromUtf8Error { bytes: [72, 101, 240, 108, 108, 111], error: Utf8Error { valid_up_to: 2, error_len: Some(1) } } Err codemadness.org 70 i 10656 </code></pre></div> Err codemadness.org 70 i 10657 Err codemadness.org 70 i 10658 <p>Here, in the error case, the <a href="https://doc.rust-lang.org/std/str/struct.Utf8Error.html"><code>Utf8Error</code></a> tells us that the bytes Err codemadness.org 70 i 10659 are UTF-8 and are <code>valid_up_to</code> index 2; that is the first problematic Err codemadness.org 70 i 10660 index. We also get some extra information which lets the program know Err codemadness.org 70 i 10661 if the problematic sequence was incomplete and truncated at the end of Err codemadness.org 70 i 10662 the byte array, or if it's complete and in the middle.</p> Err codemadness.org 70 i 10663 <p>And for a "just make this printable, pls" API? We can Err codemadness.org 70 i 10664 use <a href="https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8_lossy"><code>String::from_utf8_lossy()</code></a>, which replaces invalid UTF-8 Err codemadness.org 70 i 10665 sequences with <code>U+FFFD REPLACEMENT CHARACTER</code>:</p> Err codemadness.org 70 i 10666 <div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">convert_and_print</span><span class="p">(</span><span class="n">bytes</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u8</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10667 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span>::<span class="n">from_utf8_lossy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">bytes</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10668 <span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&quot;{}&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">string</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10669 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10670 Err codemadness.org 70 i 10671 <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10672 <span class="w"> </span><span class="n">convert_and_print</span><span class="p">(</span><span class="fm">vec!</span><span class="p">[</span><span class="mh">0x48</span><span class="p">,</span><span class="w"> </span><span class="mh">0x65</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6f</span><span class="p">]);</span><span class="w"></span> Err codemadness.org 70 i 10673 <span class="w"> </span><span class="n">convert_and_print</span><span class="p">(</span><span class="fm">vec!</span><span class="p">[</span><span class="mh">0x48</span><span class="p">,</span><span class="w"> </span><span class="mh">0x65</span><span class="p">,</span><span class="w"> </span><span class="mh">0xf0</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x6f</span><span class="p">]);</span><span class="w"></span> Err codemadness.org 70 i 10674 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10675 </code></pre></div> Err codemadness.org 70 i 10676 Err codemadness.org 70 i 10677 <p>This prints the following:</p> Err codemadness.org 70 i 10678 <div class="highlight"><pre><span></span><code>$ ~/foo Err codemadness.org 70 i 10679 Hello Err codemadness.org 70 i 10680 He�llo Err codemadness.org 70 i 10681 </code></pre></div> Err codemadness.org 70 i 10682 Err codemadness.org 70 i 10683 <h1>Reading from files into strings</h1> Err codemadness.org 70 i 10684 <p>Now, let's assume you want to read chunks of a file and put them into Err codemadness.org 70 i 10685 strings. Let's go from the low-level parts up to the high level "read Err codemadness.org 70 i 10686 a line" API.</p> Err codemadness.org 70 i 10687 <h2>Single bytes and single UTF-8 characters</h2> Err codemadness.org 70 i 10688 <p>When you open a <a href="https://doc.rust-lang.org/std/fs/struct.File.html"><code>File</code></a>, you get an object that implements the Err codemadness.org 70 i 10689 <a href="https://doc.rust-lang.org/std/io/trait.Read.html"><code>Read</code></a> trait. In addition to the usual "read me some bytes" method, Err codemadness.org 70 i 10690 it can also give you back an iterator over <em>bytes</em>, or an iterator Err codemadness.org 70 i 10691 over UTF-8 <em>characters</em>.</p> Err codemadness.org 70 i 10692 <p>The <a href="https://doc.rust-lang.org/std/io/trait.Read.html#method.bytes"><code>Read.bytes()</code></a> method gives you back a <a href="https://doc.rust-lang.org/std/io/struct.Bytes.html"><code>Bytes</code></a> iterator, Err codemadness.org 70 i 10693 whose <code>next()</code> method returns <code>Result&lt;u8, io::Error&gt;</code>. When you ask Err codemadness.org 70 i 10694 the iterator for its next item, that <code>Result</code> means you'll get a byte Err codemadness.org 70 i 10695 out of it successfully, or an I/O error.</p> Err codemadness.org 70 i 10696 <p>In contrast, the <a href="https://doc.rust-lang.org/std/io/trait.Read.html#method.chars"><code>Read.chars()</code></a> method gives you back Err codemadness.org 70 i 10697 a <a href="https://doc.rust-lang.org/std/io/struct.Chars.html"><code>Chars</code></a> iterator, and its <code>next()</code> method returns Err codemadness.org 70 i 10698 <code>Result&lt;char, CharsError&gt;</code>, not <code>io::Error</code>. This Err codemadness.org 70 i 10699 extended <a href="https://doc.rust-lang.org/std/io/enum.CharsError.html"><code>CharsError</code></a> has a <code>NotUtf8</code> case, which you get back Err codemadness.org 70 i 10700 when <code>next()</code> tries to read the next UTF-8 sequence from the file and Err codemadness.org 70 i 10701 the file has invalid data. <code>CharsError</code> also has a case for normal Err codemadness.org 70 i 10702 I/O errors.</p> Err codemadness.org 70 i 10703 <h2>Reading lines</h2> Err codemadness.org 70 i 10704 <p>While you could build a UTF-8 string one character at a time, there Err codemadness.org 70 i 10705 are more efficient ways to do it.</p> Err codemadness.org 70 i 10706 <p>You can create a <a href="https://doc.rust-lang.org/std/io/struct.BufReader.html"><code>BufReader</code></a>, a buffered reader, out of anything Err codemadness.org 70 i 10707 that implements the <a href="https://doc.rust-lang.org/std/io/trait.Read.html"><code>Read</code></a> trait. <code>BufReader</code> has a Err codemadness.org 70 i 10708 convenient <a href="https://doc.rust-lang.org/std/io/trait.BufRead.html#method.read_line"><code>read_line()</code></a> method, to which you pass a mutable Err codemadness.org 70 i 10709 String and it returns a <code>Result&lt;usize, io::Error&gt;</code> with either the Err codemadness.org 70 i 10710 number of bytes read, or an error.</p> Err codemadness.org 70 i 10711 <p>That method is declared in the <a href="https://doc.rust-lang.org/std/io/trait.BufRead.html"><code>BufRead</code></a> trait, which <code>BufReader</code> Err codemadness.org 70 i 10712 implements. Why the separation? Because other concrete structs also Err codemadness.org 70 i 10713 implement <code>BufRead</code>, such as <a href="https://doc.rust-lang.org/std/io/struct.Cursor.html"><code>Cursor</code></a> — a nice wrapper that lets Err codemadness.org 70 i 10714 you use a vector of bytes like an I/O <code>Read</code> or <code>Write</code> Err codemadness.org 70 i 10715 implementation, similar to <a href="https://developer.gnome.org/gio/stable/GMemoryInputStream.html"><code>GMemoryInputStream</code></a>.</p> Err codemadness.org 70 i 10716 <p>If you prefer an iterator rather than the <code>read_line()</code> function, Err codemadness.org 70 i 10717 <code>BufRead</code> also gives you a <a href="https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines"><code>lines()</code></a> method, which gives you back Err codemadness.org 70 i 10718 a <a href="https://doc.rust-lang.org/std/io/struct.Lines.html"><code>Lines</code></a> iterator.</p> Err codemadness.org 70 i 10719 <p>In both cases — the <code>read_line()</code> method or the <code>Lines</code> iterator, the Err codemadness.org 70 i 10720 error that you can get back can be of <a href="https://doc.rust-lang.org/std/io/enum.ErrorKind.html"><code>ErrorKind</code></a><code>::InvalidData</code>, Err codemadness.org 70 i 10721 which indicates that there was an invalid UTF-8 sequence in the line Err codemadness.org 70 i 10722 to be read. It can also be a normal I/O error, of course.</p> Err codemadness.org 70 i 10723 <h1>Summary so far</h1> Err codemadness.org 70 i 10724 <p>There is no way to build a <code>String</code>, or a <code>&amp;str</code> slice, from invalid Err codemadness.org 70 i 10725 UTF-8 data. All the methods that let you turn bytes into string-like Err codemadness.org 70 i 10726 things perform validation, and return a <code>Result</code> to let you know if Err codemadness.org 70 i 10727 your bytes validated correctly.</p> Err codemadness.org 70 i 10728 <p>The exceptions are in the <code>unsafe</code> methods, Err codemadness.org 70 i 10729 like <a href="https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8_unchecked"><code>String::from_utf8_unchecked()</code></a>. You should really only use Err codemadness.org 70 i 10730 them if you are <em>absolutely sure</em> that your bytes were validated as Err codemadness.org 70 i 10731 UTF-8 beforehand.</p> Err codemadness.org 70 i 10732 <p>There is no way to bring in data from a file (or anything file-like, Err codemadness.org 70 i 10733 that implements the <a href="https://doc.rust-lang.org/std/io/trait.Read.html"><code>Read</code></a> trait) and turn it into a <code>String</code> Err codemadness.org 70 i 10734 without going through functions that do UTF-8 validation. There is Err codemadness.org 70 i 10735 not an unsafe "read a line" API without validation — you would have to Err codemadness.org 70 i 10736 build one yourself, but the I/O hit is probably going to be slower than Err codemadness.org 70 i 10737 validating data in memory, anyway, so you may as well validate.</p> Err codemadness.org 70 i 10738 <h1>C strings and Rust</h1> Err codemadness.org 70 i 10739 <p>For unfortunate historical reasons, C flings around <code>char *</code> to mean Err codemadness.org 70 i 10740 different things. In the context of Glib, it can mean</p> Err codemadness.org 70 i 10741 <ul> Err codemadness.org 70 i 10742 <li>A valid, nul-terminated UTF-8 sequence of bytes (a "normal string")</li> Err codemadness.org 70 i 10743 <li>A nul-terminated file path, which has no meaningful encoding</li> Err codemadness.org 70 i 10744 <li>A nul-terminated sequence of bytes, not validated as UTF-8.</li> Err codemadness.org 70 i 10745 </ul> Err codemadness.org 70 i 10746 <p>What a particular <code>char *</code> means depends on which API you got it from.</p> Err codemadness.org 70 i 10747 <h2>Bringing a string from C to Rust</h2> Err codemadness.org 70 i 10748 <p>From Rust's viewpoint, getting a raw <code>char *</code> from C (a "<code>*const Err codemadness.org 70 i 10749 c_char</code>" in Rust parlance) means that it gets a pointer to a buffer of Err codemadness.org 70 i 10750 unknown length.</p> Err codemadness.org 70 i 10751 <p>Now, that may not be entirely accurate:</p> Err codemadness.org 70 i 10752 <ul> Err codemadness.org 70 i 10753 <li>You may indeed only have a pointer to a buffer of unknown length</li> Err codemadness.org 70 i 10754 <li>You may have a pointer to a buffer, and also know its length Err codemadness.org 70 i 10755 (i.e. the offset at which the nul terminator is)</li> Err codemadness.org 70 i 10756 </ul> Err codemadness.org 70 i 10757 <p>The Rust standard library provides a <a href="https://doc.rust-lang.org/std/ffi/struct.CStr.html"><code>CStr</code></a> object, which means, Err codemadness.org 70 i 10758 "I have a pointer to an array of bytes, and I know its length, and I Err codemadness.org 70 i 10759 know the last byte is a nul".</p> Err codemadness.org 70 i 10760 <p><code>CStr</code> provides an <a href="https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.from_ptr"><code>unsafe from_ptr()</code></a> constructor which takes a Err codemadness.org 70 i 10761 raw pointer, and walks the memory to which it points until it finds a Err codemadness.org 70 i 10762 nul byte. You <em>must</em> give it a valid pointer, and you had better Err codemadness.org 70 i 10763 guarantee that there is a nul terminator, or <code>CStr</code> will walk until Err codemadness.org 70 i 10764 the end of your process' address space looking for one.</p> Err codemadness.org 70 i 10765 <p>Alternatively, if you know the length of your byte array, and you know Err codemadness.org 70 i 10766 that it has a nul byte at the end, you can Err codemadness.org 70 i 10767 call <a href="https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.from_bytes_with_nul"><code>CStr::from_bytes_with_nul()</code></a>. You pass it a <code>&amp;[u8]</code> slice; Err codemadness.org 70 i 10768 the function will check that a) the last byte in that slice is indeed Err codemadness.org 70 i 10769 a nul, and b) there are no nul bytes in the middle.</p> Err codemadness.org 70 i 10770 <p>The unsafe version of this last function Err codemadness.org 70 i 10771 is <a href="https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.from_bytes_with_nul_unchecked"><code>unsafe CStr::from_bytes_with_nul_unchecked()</code></a>: it also takes Err codemadness.org 70 i 10772 an <code>&amp;[u8]</code> slice, but <em>you</em> must guarantee that the last byte is a nul Err codemadness.org 70 i 10773 and that there are no nul bytes in the middle.</p> Err codemadness.org 70 i 10774 <p><em>I really like that the Rust documentation tells you when functions Err codemadness.org 70 i 10775 are not "instantaneous" and must instead walks arrays, like to do Err codemadness.org 70 i 10776 validation or to look for the nul terminator above.</em></p> Err codemadness.org 70 i 10777 <h2>Turning a CStr into a string-like</h2> Err codemadness.org 70 i 10778 <p>Now, the above indicates that a <code>CStr</code> is a nul-terminated array of Err codemadness.org 70 i 10779 bytes. We have no idea what the bytes inside look like; we just know Err codemadness.org 70 i 10780 that they don't contain any other nul bytes.</p> Err codemadness.org 70 i 10781 <p>There is a <a href="https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.to_str"><code>CStr::to_str()</code></a> method, which returns a Err codemadness.org 70 i 10782 <code>Result&lt;&amp;str, Utf8Error&gt;</code>. It performs UTF-8 validation on the array Err codemadness.org 70 i 10783 of bytes. If the array is valid, the function just returns a slice of Err codemadness.org 70 i 10784 the validated bytes minus the nul terminator (i.e. just what you Err codemadness.org 70 i 10785 expect for a Rust string slice). Otherwise, it returns an <code>Utf8Error</code> Err codemadness.org 70 i 10786 with the details like we discussed before.</p> Err codemadness.org 70 i 10787 <p>There is also <a href="https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.to_string_lossy"><code>CStr::to_string_lossy()</code></a> which does the Err codemadness.org 70 i 10788 replacement of invalid UTF-8 sequences like we discussed before.</p> Err codemadness.org 70 i 10789 <h1>Conclusion</h1> Err codemadness.org 70 i 10790 <p>Strings in Rust are UTF-8 encoded, they know their length, and they Err codemadness.org 70 i 10791 can have nul bytes in the middle.</p> Err codemadness.org 70 i 10792 <p>To build a string from raw bytes, you must go through functions that Err codemadness.org 70 i 10793 do UTF-8 validation and tell you if it failed. There are unsafe Err codemadness.org 70 i 10794 functions that let you skip validation, but then of course you are on Err codemadness.org 70 i 10795 your own.</p> Err codemadness.org 70 i 10796 <p>The low-level functions which read data from files operate on bytes. Err codemadness.org 70 i 10797 On top of those, there are convenience functions to read validated Err codemadness.org 70 i 10798 UTF-8 characters, lines, etc. All of these tell you when there was Err codemadness.org 70 i 10799 invalid UTF-8 or an I/O error.</p> Err codemadness.org 70 i 10800 <p>Rust lets you wrap a raw <code>char *</code> that you got from C into something Err codemadness.org 70 i 10801 that can later be validated and turned into a string. Anything that Err codemadness.org 70 i 10802 manipulates a raw pointer is <code>unsafe</code>; this includes the "wrap me this Err codemadness.org 70 i 10803 pointer into a C string abstraction" API, and the "build me an array Err codemadness.org 70 i 10804 of bytes from this raw pointer" API. Later, you can validate <em>those</em> Err codemadness.org 70 i 10805 as UTF-8 and build real Rust strings — or know if the validation Err codemadness.org 70 i 10806 failed.</p> Err codemadness.org 70 i 10807 <p>Rust builds these little "corridors" through the API so that illegal Err codemadness.org 70 i 10808 states are unrepresentable.</p>GUADEC 2017 presentation2017-08-09T20:20:49-05:002017-08-09T20:20:52-05:00Federico Mena Quinterotag:people.gnome.org,2017-08-09:/~federico/blog/guadec-2017.html<p>During GUADEC this year I gave a presentation Err codemadness.org 70 i 10809 called Err codemadness.org 70 i 10810 <a href="https://people.gnome.org/~federico/blog/docs/fmq-porting-c-to-rust.pdf">Replacing C library code with Rust: what I learned with librsvg</a>. Err codemadness.org 70 i 10811 This is the PDF file; be sure to scroll past the full-page Err codemadness.org 70 i 10812 presentation pages until you reach the speaker's notes, especially for Err codemadness.org 70 i 10813 the code sections!</p> Err codemadness.org 70 i 10814 <p><a href="https://people.gnome.org/~federico/blog/docs/fmq-porting-c-to-rust.pdf"><img alt="Replacing C library code with Rust - link to PDF" src="https://people.gnome.org/~federico/blog/images/fmq-porting-c-to-rust.png"></a></p> Err codemadness.org 70 i 10815 <p>You can also get the …</p><p>During GUADEC this year I gave a presentation Err codemadness.org 70 i 10816 called Err codemadness.org 70 i 10817 <a href="https://people.gnome.org/~federico/blog/docs/fmq-porting-c-to-rust.pdf">Replacing C library code with Rust: what I learned with librsvg</a>. Err codemadness.org 70 i 10818 This is the PDF file; be sure to scroll past the full-page Err codemadness.org 70 i 10819 presentation pages until you reach the speaker's notes, especially for Err codemadness.org 70 i 10820 the code sections!</p> Err codemadness.org 70 i 10821 <p><a href="https://people.gnome.org/~federico/blog/docs/fmq-porting-c-to-rust.pdf"><img alt="Replacing C library code with Rust - link to PDF" src="https://people.gnome.org/~federico/blog/images/fmq-porting-c-to-rust.png"></a></p> Err codemadness.org 70 i 10822 <p>You can also get the <a href="https://people.gnome.org/~federico/blog/docs/fmq-porting-c-to-rust.odp">ODP file</a> for the presentation. This is Err codemadness.org 70 i 10823 released under a <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA license</a>.</p> Err codemadness.org 70 i 10824 <p>For the presentation, my daughter Luciana made some drawings of Err codemadness.org 70 i 10825 Ferris, the Rust mascot, also released under the same license:</p> Err codemadness.org 70 i 10826 <p><a href="https://people.gnome.org/~federico/blog/docs/ferris-1.png"><img alt="Ferris says hi" src="https://people.gnome.org/~federico/blog/docs/ferris-1-thumb.png"></a> Err codemadness.org 70 i 10827 <a href="https://people.gnome.org/~federico/blog/docs/ferris-2.png"><img alt="Ferris busy at work" src="https://people.gnome.org/~federico/blog/docs/ferris-2-thumb.png"></a> Err codemadness.org 70 i 10828 <a href="https://people.gnome.org/~federico/blog/docs/ferris-3.png"><img alt="Ferris makes a mess" src="https://people.gnome.org/~federico/blog/docs/ferris-3-thumb.png"></a> Err codemadness.org 70 i 10829 <a href="https://people.gnome.org/~federico/blog/docs/ferris-4.png"><img alt="Ferris presents her work" src="https://people.gnome.org/~federico/blog/docs/ferris-4-thumb.png"></a></p>Surviving a rust-cssparser API break2017-08-01T06:02:30-05:002017-08-01T06:02:37-05:00Federico Mena Quinterotag:people.gnome.org,2017-08-01:/~federico/blog/surviving-rust-cssparser-api-break.html<p>Yesterday I looked into updating librsvg's Rust dependencies. There have Err codemadness.org 70 i 10830 been some API breaks (!!!) in the unstable libraries that it uses Err codemadness.org 70 i 10831 since the last time I locked them. This post is about an interesting Err codemadness.org 70 i 10832 case of API breakage.</p> Err codemadness.org 70 i 10833 <p><a href="https://github.com/servo/rust-cssparser">rust-cssparser</a> is the crate that <a href="https://servo.org/">Servo</a> uses for parsing Err codemadness.org 70 i 10834 CSS. Well, more …</p><p>Yesterday I looked into updating librsvg's Rust dependencies. There have Err codemadness.org 70 i 10835 been some API breaks (!!!) in the unstable libraries that it uses Err codemadness.org 70 i 10836 since the last time I locked them. This post is about an interesting Err codemadness.org 70 i 10837 case of API breakage.</p> Err codemadness.org 70 i 10838 <p><a href="https://github.com/servo/rust-cssparser">rust-cssparser</a> is the crate that <a href="https://servo.org/">Servo</a> uses for parsing Err codemadness.org 70 i 10839 CSS. Well, more like <em>tokenizing</em> CSS: you give it a string, it Err codemadness.org 70 i 10840 gives you back tokens, and you are supposed to compose CSS selector Err codemadness.org 70 i 10841 information or other CSS values from the tokens.</p> Err codemadness.org 70 i 10842 <p>Librsvg uses rust-cssparser now for most of the micro-languages in Err codemadness.org 70 i 10843 SVG's attribute values, instead of its old, fragile C parsers. I hope Err codemadness.org 70 i 10844 to be able to use it in conjunction with Servo's <a href="https://github.com/servo/servo/tree/master/components/selectors">rust-selectors</a> Err codemadness.org 70 i 10845 crate to fully parse CSS data and replace <a href="https://git.gnome.org/browse/libcroco">libcroco</a>.</p> Err codemadness.org 70 i 10846 <p>A few months ago, rust-cssparser's API looked more or less like the Err codemadness.org 70 i 10847 following. This is the old representation of a <code>Token</code>:</p> Err codemadness.org 70 i 10848 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">Token</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10849 <span class="w"> </span><span class="c1">// an identifier</span> Err codemadness.org 70 i 10850 <span class="w"> </span><span class="n">Ident</span><span class="p">(</span><span class="n">Cow</span><span class="o">&lt;&#39;</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="kt">str</span><span class="o">&gt;</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10851 Err codemadness.org 70 i 10852 <span class="w"> </span><span class="c1">// a plain number</span> Err codemadness.org 70 i 10853 <span class="w"> </span><span class="n">Number</span><span class="p">(</span><span class="n">NumericValue</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10854 Err codemadness.org 70 i 10855 <span class="w"> </span><span class="c1">// a percentage value normalized to [0.0, 1.0]</span> Err codemadness.org 70 i 10856 <span class="w"> </span><span class="n">Percentage</span><span class="p">(</span><span class="n">PercentageValue</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10857 Err codemadness.org 70 i 10858 <span class="w"> </span><span class="n">WhiteSpace</span><span class="p">(</span><span class="o">&amp;&#39;</span><span class="na">a</span><span class="w"> </span><span class="kt">str</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10859 <span class="w"> </span><span class="n">Comma</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 10860 Err codemadness.org 70 i 10861 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 10862 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10863 </code></pre></div> Err codemadness.org 70 i 10864 Err codemadness.org 70 i 10865 <p>That is, a <code>Token</code> can be an <code>Ident</code>ifier with a string name, or a Err codemadness.org 70 i 10866 <code>Number</code>, a <code>Percentage</code>, whitespace, a comma, and many others.</p> Err codemadness.org 70 i 10867 <p>On top of that is the old API for a <code>Parser</code>, which you construct with Err codemadness.org 70 i 10868 a string and then it gives you back tokens:</p> Err codemadness.org 70 i 10869 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Parser</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10870 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">input</span>: <span class="kp">&amp;</span><span class="o">&#39;</span><span class="na">i</span> <span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Parser</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="p">,</span><span class="w"> </span><span class="o">&#39;</span><span class="na">i</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10871 Err codemadness.org 70 i 10872 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">Token</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10873 Err codemadness.org 70 i 10874 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 10875 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10876 </code></pre></div> Err codemadness.org 70 i 10877 Err codemadness.org 70 i 10878 <p>This means the following. You create the parser out of a string slice Err codemadness.org 70 i 10879 with <code>new()</code>. You can then extract a <code>Result</code> with a <code>Token</code> Err codemadness.org 70 i 10880 sucessfully, or with an empty error value. The parser uses a lifetime Err codemadness.org 70 i 10881 <code>'i</code> on the string from which it is constructed: the <code>Token</code>s that Err codemadness.org 70 i 10882 return identifiers, for example, could return sub-string slices that Err codemadness.org 70 i 10883 come from the original string, and the parser has to be marked with a Err codemadness.org 70 i 10884 lifetime so that it does not outlive its underlying string.</p> Err codemadness.org 70 i 10885 <p>A few commits later, rust-cssparser got changed to return detailed Err codemadness.org 70 i 10886 error values, so that instead of <code>()</code> you get a a <code>BasicParseError</code> Err codemadness.org 70 i 10887 with sub-cases like <code>UnexpectedToken</code> or <code>EndOfInput</code>.</p> Err codemadness.org 70 i 10888 <p>After the changes to the error values for results, I didn't pay much Err codemadness.org 70 i 10889 attention to rust-cssparser for while. Yesterday I wanted to update Err codemadness.org 70 i 10890 librsvg to use the newest rust-cssparser, and had some interesting Err codemadness.org 70 i 10891 problems.</p> Err codemadness.org 70 i 10892 <p>First, <code>Parser::new()</code> was changed from taking just a <code>&amp;str</code> slice to Err codemadness.org 70 i 10893 taking a <code>ParserInput</code> struct. This is an implementation detail which Err codemadness.org 70 i 10894 lets the parser cache the last token it saw. Not a big deal:</p> Err codemadness.org 70 i 10895 <div class="highlight"><pre><span></span><code><span class="c1">// instead of constructing a parser like</span> Err codemadness.org 70 i 10896 <span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Parser</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="n">my_string</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10897 Err codemadness.org 70 i 10898 <span class="c1">// you now construct it like</span> Err codemadness.org 70 i 10899 <span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ParserInput</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="n">my_string</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10900 <span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Parser</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">input</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10901 </code></pre></div> Err codemadness.org 70 i 10902 Err codemadness.org 70 i 10903 <p>I am not completely sure why this is exposed to the public API, since Err codemadness.org 70 i 10904 Rust won't allow you to have two mutable references to a Err codemadness.org 70 i 10905 <code>ParserInput</code>, and the only consumer of a (mutable) <code>ParserInput</code> is Err codemadness.org 70 i 10906 the <code>Parser</code>, anyway.</p> Err codemadness.org 70 i 10907 <p>However, the <code>parser.next()</code> function changed:</p> Err codemadness.org 70 i 10908 <div class="highlight"><pre><span></span><code><span class="c1">// old version</span> Err codemadness.org 70 i 10909 <span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">Token</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10910 Err codemadness.org 70 i 10911 <span class="c1">// new version</span> Err codemadness.org 70 i 10912 <span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;&amp;</span><span class="n">Token</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="n">BasicParseError</span><span class="o">&lt;&#39;</span><span class="na">i</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="p">{</span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10913 <span class="c1">// note this bad boy here -------^</span> Err codemadness.org 70 i 10914 </code></pre></div> Err codemadness.org 70 i 10915 Err codemadness.org 70 i 10916 <p>The successful <code>Result</code> from <code>next()</code> is now a <em>reference</em> to a Err codemadness.org 70 i 10917 <code>Token</code>, not a plain <code>Token</code> value which you now own. The parser is Err codemadness.org 70 i 10918 giving you a borrowed reference to its internally-cached token.</p> Err codemadness.org 70 i 10919 <p>My parsing functions for the old API looked similar to the Err codemadness.org 70 i 10920 following. This is a function that parses a string into an angle; it Err codemadness.org 70 i 10921 can look like <code>"45deg"</code> or <code>"1.5rad"</code>, for example.</p> Err codemadness.org 70 i 10922 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span> Err codemadness.org 70 i 10923 <span class="normal"> 2</span> Err codemadness.org 70 i 10924 <span class="normal"> 3</span> Err codemadness.org 70 i 10925 <span class="normal"> 4</span> Err codemadness.org 70 i 10926 <span class="normal"> 5</span> Err codemadness.org 70 i 10927 <span class="normal"> 6</span> Err codemadness.org 70 i 10928 <span class="normal"> 7</span> Err codemadness.org 70 i 10929 <span class="normal"> 8</span> Err codemadness.org 70 i 10930 <span class="normal"> 9</span> Err codemadness.org 70 i 10931 <span class="normal">10</span> Err codemadness.org 70 i 10932 <span class="normal">11</span> Err codemadness.org 70 i 10933 <span class="normal">12</span> Err codemadness.org 70 i 10934 <span class="normal">13</span> Err codemadness.org 70 i 10935 <span class="normal">14</span> Err codemadness.org 70 i 10936 <span class="normal">15</span> Err codemadness.org 70 i 10937 <span class="normal">16</span> Err codemadness.org 70 i 10938 <span class="normal">17</span> Err codemadness.org 70 i 10939 <span class="normal">18</span> Err codemadness.org 70 i 10940 <span class="normal">19</span> Err codemadness.org 70 i 10941 <span class="normal">20</span> Err codemadness.org 70 i 10942 <span class="normal">21</span> Err codemadness.org 70 i 10943 <span class="normal">22</span> Err codemadness.org 70 i 10944 <span class="normal">23</span> Err codemadness.org 70 i 10945 <span class="normal">24</span> Err codemadness.org 70 i 10946 <span class="normal">25</span> Err codemadness.org 70 i 10947 <span class="normal">26</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">parse_angle_degrees</span><span class="w"> </span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span> <span class="o">&lt;</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="n">ParseError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10948 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Parser</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 10949 Err codemadness.org 70 i 10950 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parser</span><span class="p">.</span><span class="n">next</span><span class="w"> </span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 10951 <span class="w"> </span><span class="p">.</span><span class="n">map_err</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10952 Err codemadness.org 70 i 10953 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10954 <span class="w"> </span><span class="n">Token</span>::<span class="n">Number</span><span class="w"> </span><span class="p">(</span><span class="n">NumericValue</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">value</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">f64</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10955 Err codemadness.org 70 i 10956 <span class="w"> </span><span class="n">Token</span>::<span class="n">Dimension</span><span class="w"> </span><span class="p">(</span><span class="n">NumericValue</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">value</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="n">unit</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10957 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">f64</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10958 Err codemadness.org 70 i 10959 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">unit</span><span class="p">.</span><span class="n">as_ref</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10960 <span class="w"> </span><span class="s">&quot;deg&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10961 <span class="w"> </span><span class="s">&quot;grad&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">360.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">400.0</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10962 <span class="w"> </span><span class="s">&quot;rad&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">180.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">PI</span><span class="p">),</span><span class="w"></span> Err codemadness.org 70 i 10963 <span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Err</span><span class="w"> </span><span class="p">(</span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 10964 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10965 <span class="w"> </span><span class="p">},</span><span class="w"></span> Err codemadness.org 70 i 10966 Err codemadness.org 70 i 10967 <span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Err</span><span class="w"> </span><span class="p">(</span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 10968 <span class="w"> </span><span class="p">}.</span><span class="n">and_then</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">r</span><span class="o">|</span><span class="w"></span> Err codemadness.org 70 i 10969 <span class="w"> </span><span class="n">parser</span><span class="p">.</span><span class="n">expect_exhausted</span><span class="w"> </span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 10970 <span class="w"> </span><span class="p">.</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 10971 <span class="w"> </span><span class="p">.</span><span class="n">map_err</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">)))</span><span class="w"></span> Err codemadness.org 70 i 10972 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 10973 </code></pre></div> Err codemadness.org 70 i 10974 </td></tr></table> Err codemadness.org 70 i 10975 <p>This is a bit ugly, but it was the first version that passed the Err codemadness.org 70 i 10976 tests. Lines 4 and 5 mean, "get the first token or return an error". Err codemadness.org 70 i 10977 Line 17 means, "anything except <code>deg</code>, <code>grad</code>, or <code>rad</code> for the units Err codemadness.org 70 i 10978 causes the <code>match</code> expression to generate an error". Finally, I was Err codemadness.org 70 i 10979 feeling very proud of using <code>and_then()</code> in line 22, with Err codemadness.org 70 i 10980 <code>parser.expect_exhausted()</code>, to ensure that the parser would not find Err codemadness.org 70 i 10981 any more tokens after the angle/units.</p> Err codemadness.org 70 i 10982 <p>However, in the new version of rust-cssparser, Parser.next() gives Err codemadness.org 70 i 10983 back a <code>Result</code> with a <code>&amp;Token</code> success value — a <em>reference</em> to a Err codemadness.org 70 i 10984 token —, while the old version returned a plain <code>Token</code>. No problem, Err codemadness.org 70 i 10985 I thought, I'm just going to de-reference the value in the <code>match</code> and Err codemadness.org 70 i 10986 be done with it:</p> Err codemadness.org 70 i 10987 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parser</span><span class="p">.</span><span class="n">next</span><span class="w"> </span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 10988 <span class="w"> </span><span class="p">.</span><span class="n">map_err</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 10989 Err codemadness.org 70 i 10990 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="o">*</span><span class="n">token</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10991 <span class="w"> </span><span class="c1">// ^ dereference here...</span> Err codemadness.org 70 i 10992 <span class="w"> </span><span class="n">Token</span>::<span class="n">Number</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">value</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 10993 Err codemadness.org 70 i 10994 <span class="w"> </span><span class="n">Token</span>::<span class="n">Dimension</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">value</span><span class="p">,</span><span class="w"> </span><span class="k">ref</span><span class="w"> </span><span class="n">unit</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 10995 <span class="w"> </span><span class="c1">// ^ avoid moving the unit value</span> Err codemadness.org 70 i 10996 </code></pre></div> Err codemadness.org 70 i 10997 Err codemadness.org 70 i 10998 <p>The compiler complained elsewhere. The whole function now looked like Err codemadness.org 70 i 10999 this:</p> Err codemadness.org 70 i 11000 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span> Err codemadness.org 70 i 11001 <span class="normal"> 2</span> Err codemadness.org 70 i 11002 <span class="normal"> 3</span> Err codemadness.org 70 i 11003 <span class="normal"> 4</span> Err codemadness.org 70 i 11004 <span class="normal"> 5</span> Err codemadness.org 70 i 11005 <span class="normal"> 6</span> Err codemadness.org 70 i 11006 <span class="normal"> 7</span> Err codemadness.org 70 i 11007 <span class="normal"> 8</span> Err codemadness.org 70 i 11008 <span class="normal"> 9</span> Err codemadness.org 70 i 11009 <span class="normal">10</span> Err codemadness.org 70 i 11010 <span class="normal">11</span> Err codemadness.org 70 i 11011 <span class="normal">12</span> Err codemadness.org 70 i 11012 <span class="normal">13</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">parse_angle_degrees</span><span class="w"> </span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span> <span class="o">&lt;</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="n">ParseError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11013 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Parser</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 11014 Err codemadness.org 70 i 11015 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parser</span><span class="p">.</span><span class="n">next</span><span class="w"> </span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 11016 <span class="w"> </span><span class="p">.</span><span class="n">map_err</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11017 Err codemadness.org 70 i 11018 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11019 <span class="w"> </span><span class="c1">// ...</span> Err codemadness.org 70 i 11020 <span class="w"> </span><span class="p">}.</span><span class="n">and_then</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">r</span><span class="o">|</span><span class="w"></span> Err codemadness.org 70 i 11021 <span class="w"> </span><span class="n">parser</span><span class="p">.</span><span class="n">expect_exhausted</span><span class="w"> </span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 11022 <span class="w"> </span><span class="p">.</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 11023 <span class="w"> </span><span class="p">.</span><span class="n">map_err</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">)))</span><span class="w"></span> Err codemadness.org 70 i 11024 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11025 </code></pre></div> Err codemadness.org 70 i 11026 </td></tr></table> Err codemadness.org 70 i 11027 <p>But in line 4, <code>token</code> is now a reference to something that lives Err codemadness.org 70 i 11028 inside <code>parser</code>, and <code>parser</code> is therefore borrowed mutably. The Err codemadness.org 70 i 11029 compiler didn't like that line 10 (the call to Err codemadness.org 70 i 11030 <code>parser.expect_exhausted()</code>) was trying to borrow <code>parser</code> mutably Err codemadness.org 70 i 11031 again.</p> Err codemadness.org 70 i 11032 <p>I played a bit with creating a temporary scope around the assignment Err codemadness.org 70 i 11033 to <code>token</code> so that it would only borrow <code>parser</code> mutably inside that Err codemadness.org 70 i 11034 scope. Things ended up like this, without the call to <code>and_then()</code> Err codemadness.org 70 i 11035 after the <code>match</code>:</p> Err codemadness.org 70 i 11036 <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span> Err codemadness.org 70 i 11037 <span class="normal"> 2</span> Err codemadness.org 70 i 11038 <span class="normal"> 3</span> Err codemadness.org 70 i 11039 <span class="normal"> 4</span> Err codemadness.org 70 i 11040 <span class="normal"> 5</span> Err codemadness.org 70 i 11041 <span class="normal"> 6</span> Err codemadness.org 70 i 11042 <span class="normal"> 7</span> Err codemadness.org 70 i 11043 <span class="normal"> 8</span> Err codemadness.org 70 i 11044 <span class="normal"> 9</span> Err codemadness.org 70 i 11045 <span class="normal">10</span> Err codemadness.org 70 i 11046 <span class="normal">11</span> Err codemadness.org 70 i 11047 <span class="normal">12</span> Err codemadness.org 70 i 11048 <span class="normal">13</span> Err codemadness.org 70 i 11049 <span class="normal">14</span> Err codemadness.org 70 i 11050 <span class="normal">15</span> Err codemadness.org 70 i 11051 <span class="normal">16</span> Err codemadness.org 70 i 11052 <span class="normal">17</span> Err codemadness.org 70 i 11053 <span class="normal">18</span> Err codemadness.org 70 i 11054 <span class="normal">19</span> Err codemadness.org 70 i 11055 <span class="normal">20</span> Err codemadness.org 70 i 11056 <span class="normal">21</span> Err codemadness.org 70 i 11057 <span class="normal">22</span> Err codemadness.org 70 i 11058 <span class="normal">23</span> Err codemadness.org 70 i 11059 <span class="normal">24</span> Err codemadness.org 70 i 11060 <span class="normal">25</span> Err codemadness.org 70 i 11061 <span class="normal">26</span> Err codemadness.org 70 i 11062 <span class="normal">27</span> Err codemadness.org 70 i 11063 <span class="normal">28</span> Err codemadness.org 70 i 11064 <span class="normal">29</span> Err codemadness.org 70 i 11065 <span class="normal">30</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">angle_degrees</span><span class="w"> </span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span> <span class="o">&lt;</span><span class="kt">f64</span><span class="p">,</span><span class="w"> </span><span class="n">ParseError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11066 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ParserInput</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 11067 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Parser</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">input</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 11068 Err codemadness.org 70 i 11069 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">angle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11070 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parser</span><span class="p">.</span><span class="n">next</span><span class="w"> </span><span class="p">()</span><span class="w"></span> Err codemadness.org 70 i 11071 <span class="w"> </span><span class="p">.</span><span class="n">map_err</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11072 Err codemadness.org 70 i 11073 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="o">*</span><span class="n">token</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11074 <span class="w"> </span><span class="n">Token</span>::<span class="n">Number</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">value</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">f64</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11075 Err codemadness.org 70 i 11076 <span class="w"> </span><span class="n">Token</span>::<span class="n">Dimension</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">value</span><span class="p">,</span><span class="w"> </span><span class="k">ref</span><span class="w"> </span><span class="n">unit</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11077 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">f64</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11078 Err codemadness.org 70 i 11079 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">unit</span><span class="p">.</span><span class="n">as_ref</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11080 <span class="w"> </span><span class="s">&quot;deg&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">value</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11081 <span class="w"> </span><span class="s">&quot;grad&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">360.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">400.0</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11082 <span class="w"> </span><span class="s">&quot;rad&quot;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">180.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">PI</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11083 <span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Err</span><span class="w"> </span><span class="p">(</span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected &#39;deg&#39; | &#39;grad&#39; | &#39;rad&#39;&quot;</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 11084 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11085 <span class="w"> </span><span class="p">},</span><span class="w"></span> Err codemadness.org 70 i 11086 Err codemadness.org 70 i 11087 <span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Err</span><span class="w"> </span><span class="p">(</span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 11088 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11089 <span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 11090 Err codemadness.org 70 i 11091 <span class="w"> </span><span class="n">parser</span><span class="p">.</span><span class="n">expect_exhausted</span><span class="w"> </span><span class="p">().</span><span class="n">map_err</span><span class="w"> </span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="n">ParseError</span>::<span class="n">new</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;expected angle&quot;</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11092 Err codemadness.org 70 i 11093 <span class="w"> </span><span class="nb">Ok</span><span class="w"> </span><span class="p">(</span><span class="n">angle</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 11094 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11095 </code></pre></div> Err codemadness.org 70 i 11096 </td></tr></table> Err codemadness.org 70 i 11097 <p>Lines 5 through 25 are basically</p> Err codemadness.org 70 i 11098 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">angle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11099 <span class="w"> </span><span class="c1">// parse out the angle; return if error</span> Err codemadness.org 70 i 11100 <span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 11101 </code></pre></div> Err codemadness.org 70 i 11102 Err codemadness.org 70 i 11103 <p>And after <em>that</em> is done, I test for <code>parser.expect_exhausted()</code>. Err codemadness.org 70 i 11104 There is no chaining of results with helper functions; instead it's Err codemadness.org 70 i 11105 just going through each token linearly.</p> Err codemadness.org 70 i 11106 <p>The API break was annoying to deal with, but fortunately the calling Err codemadness.org 70 i 11107 code ended up cleaner, and I didn't have to change anything in the Err codemadness.org 70 i 11108 tests. I hope rust-cssparser can stabilize its API for consumers that Err codemadness.org 70 i 11109 are not Servo.</p>Legacy Systems as Old Cities2017-06-28T21:26:00-05:002017-07-10T22:40:24-05:00Federico Mena Quinterotag:people.gnome.org,2017-06-28:/~federico/blog/legacy-systems-as-old-cities.html<p><em>I just realized that I only tweeted about this a couple of months ago, Err codemadness.org 70 i 11110 but never blogged about it. Shame on me!</em></p> Err codemadness.org 70 i 11111 <p>I wrote an article, <a href="https://recompilermag.com/issues/issue-4/legacy-systems-as-old-cities/">Legacy Systems as Old Cities</a>, for The Err codemadness.org 70 i 11112 Recompiler magazine. Is GNOME, now at 20 years old, legacy software? Err codemadness.org 70 i 11113 Is it different from mainframe software …</p><p><em>I just realized that I only tweeted about this a couple of months ago, Err codemadness.org 70 i 11114 but never blogged about it. Shame on me!</em></p> Err codemadness.org 70 i 11115 <p>I wrote an article, <a href="https://recompilermag.com/issues/issue-4/legacy-systems-as-old-cities/">Legacy Systems as Old Cities</a>, for The Err codemadness.org 70 i 11116 Recompiler magazine. Is GNOME, now at 20 years old, legacy software? Err codemadness.org 70 i 11117 Is it different from mainframe software because "everyone" can change Err codemadness.org 70 i 11118 it? Does long-lived software have the same patterns of change as Err codemadness.org 70 i 11119 cities and physical artifacts? Can we learn from the building trades Err codemadness.org 70 i 11120 and urbanism for maintaining software in the long term? <em>Could we Err codemadness.org 70 i 11121 turn legacy software into a good legacy?</em></p> Err codemadness.org 70 i 11122 <p>You can read the article <a href="https://recompilermag.com/issues/issue-4/legacy-systems-as-old-cities/">here</a>.</p> Err codemadness.org 70 i 11123 <p>Also, let me take this opportunity to recommend <a href="https://recompilermag.com">The Recompiler</a> Err codemadness.org 70 i 11124 magazine. It is the most enjoyable technical publication I read. Err codemadness.org 70 i 11125 Their <a href="https://recompilermag.com/podcast/">podcast</a> is also excellent!</p> Err codemadness.org 70 i 11126 <p><strong>Update 2017/06/10</strong> - Spanish version of the article, <a href="legacy-systems-as-old-cities-es.html">Los Sistemas Heredados como Ciudades Viejas</a></p>Setting Alt-Tab behavior in gnome-shell2017-06-22T10:25:02-05:002017-06-22T10:25:06-05:00Federico Mena Quinterotag:people.gnome.org,2017-06-22:/~federico/blog/alt-tab.html<p>After updating my distro a few months ago, I somehow lost my tweaks to Err codemadness.org 70 i 11127 the Alt-Tab behavior in gnome-shell.</p> Err codemadness.org 70 i 11128 <p>The default is to have <code>Alt-Tab</code> switch you between applications in the Err codemadness.org 70 i 11129 current workspace. One can use <code>Alt-backtick</code> (or whatever key you Err codemadness.org 70 i 11130 have above Tab) to switch between windows in the …</p><p>After updating my distro a few months ago, I somehow lost my tweaks to Err codemadness.org 70 i 11131 the Alt-Tab behavior in gnome-shell.</p> Err codemadness.org 70 i 11132 <p>The default is to have <code>Alt-Tab</code> switch you between applications in the Err codemadness.org 70 i 11133 current workspace. One can use <code>Alt-backtick</code> (or whatever key you Err codemadness.org 70 i 11134 have above Tab) to switch between windows in the current application.</p> Err codemadness.org 70 i 11135 <p>I prefer a Windows-like setup, where <code>Alt-Tab</code> switches between Err codemadness.org 70 i 11136 windows in the current workspace, regardless of the application to Err codemadness.org 70 i 11137 which they belong.</p> Err codemadness.org 70 i 11138 <p>Many moons ago there was a gnome-shell extension to change this Err codemadness.org 70 i 11139 behavior, but these days (GNOME 3.24) it can be done without Err codemadness.org 70 i 11140 extensions. It is a bit convoluted.</p> Err codemadness.org 70 i 11141 <h1>With the GUI</h1> Err codemadness.org 70 i 11142 <p>If you are using X instead of Wayland, this works:</p> Err codemadness.org 70 i 11143 <ol> Err codemadness.org 70 i 11144 <li> Err codemadness.org 70 i 11145 <p>Unset the <strong>Switch applications</strong> command. To do this, run Err codemadness.org 70 i 11146 <code>gnome-control-center</code>, go to <em>Keyboard</em>, and find the <em>Switch Err codemadness.org 70 i 11147 applications</em> command. Click on it, and hit <code>Backspace</code> in the Err codemadness.org 70 i 11148 dialog that prompts you for the keyboard shortcut. Click on the Err codemadness.org 70 i 11149 <em>Set</em> button.</p> Err codemadness.org 70 i 11150 </li> Err codemadness.org 70 i 11151 <li> Err codemadness.org 70 i 11152 <p>Set the <strong>Switch windows</strong> command. While still in the Err codemadness.org 70 i 11153 <em>Keyboard</em> settings, find the <em>Switch windows</em> command. Click on Err codemadness.org 70 i 11154 it, and hit <code>Alt-Tab</code>. Click <em>Set</em>.</p> Err codemadness.org 70 i 11155 </li> Err codemadness.org 70 i 11156 </ol> Err codemadness.org 70 i 11157 <p>That should be all you need, unless you are in Wayland. In that case, Err codemadness.org 70 i 11158 you need to do it on the command line.</p> Err codemadness.org 70 i 11159 <h1>With the command line, or in Wayland</h1> Err codemadness.org 70 i 11160 <p>The kind people on <a href="irc://irc.gnome.org/#gnome-hackers"><code>#gnome-hackers</code></a> tell me that as of GNOME Err codemadness.org 70 i 11161 3.24, changing <code>Alt-Tab</code> doesn't work on Wayland as in (2) above, Err codemadness.org 70 i 11162 because the compositor captures the <code>Alt-Tab</code> key when you type it Err codemadness.org 70 i 11163 inside the dialog that prompts you for a keyboard shortcut. In that Err codemadness.org 70 i 11164 case, you have to change the configuration keys directly instead of Err codemadness.org 70 i 11165 using the GUI:</p> Err codemadness.org 70 i 11166 <div class="highlight"><pre><span></span><code>gsettings <span class="nb">set</span> org.gnome.desktop.wm.keybindings switch-applications <span class="s2">&quot;[]&quot;</span> Err codemadness.org 70 i 11167 gsettings <span class="nb">set</span> org.gnome.desktop.wm.keybindings switch-applications-backward <span class="s2">&quot;[]&quot;</span> Err codemadness.org 70 i 11168 gsettings <span class="nb">set</span> org.gnome.desktop.wm.keybindings switch-windows <span class="s2">&quot;[&#39;&lt;Alt&gt;Tab&#39;, &#39;&lt;Super&gt;Tab&#39;]&quot;</span> Err codemadness.org 70 i 11169 gsettings <span class="nb">set</span> org.gnome.desktop.wm.keybindings switch-windows-backward <span class="s2">&quot;[&#39;&lt;Alt&gt;&lt;Shift&gt;Tab&#39;, &#39;&lt;Super&gt;&lt;Shift&gt;Tab&#39;]&quot;</span> Err codemadness.org 70 i 11170 </code></pre></div> Err codemadness.org 70 i 11171 Err codemadness.org 70 i 11172 <p>Of course the above also works in X, too.</p> Err codemadness.org 70 i 11173 <h1>Changing windows across all workspaces</h1> Err codemadness.org 70 i 11174 <p>If you'd like to switch between windows in all workspaces, rather than Err codemadness.org 70 i 11175 in the current workspace, find the <code>org.gnome.shell.window-switcher Err codemadness.org 70 i 11176 current-workspace-only</code> GSettings key and change it. You can do this Err codemadness.org 70 i 11177 in <code>dconf-editor</code>, or on the command line with</p> Err codemadness.org 70 i 11178 <div class="highlight"><pre><span></span><code>gsettings <span class="nb">set</span> org.gnome.shell.window-switcher current-workspace-only <span class="nb">true</span> Err codemadness.org 70 i 11179 </code></pre></div>Exploring Rust's standard library: system calls and errors2017-06-12T10:55:26-05:002017-06-12T17:11:52-05:00Federico Mena Quinterotag:people.gnome.org,2017-06-12:/~federico/blog/rust-libstd-syscalls-and-errors.html<p>In this post I'll show you the code path that Rust takes inside its Err codemadness.org 70 i 11180 standard library when you open a file. I wanted to learn how Rust Err codemadness.org 70 i 11181 handles system calls and <code>errno</code>, and all the little subtleties of the Err codemadness.org 70 i 11182 POSIX API. This is what I learned!</p> Err codemadness.org 70 i 11183 <h1>The C side of …</h1><p>In this post I'll show you the code path that Rust takes inside its Err codemadness.org 70 i 11184 standard library when you open a file. I wanted to learn how Rust Err codemadness.org 70 i 11185 handles system calls and <code>errno</code>, and all the little subtleties of the Err codemadness.org 70 i 11186 POSIX API. This is what I learned!</p> Err codemadness.org 70 i 11187 <h1>The C side of things</h1> Err codemadness.org 70 i 11188 <p>When you open a file, or create a socket, or do anything else that Err codemadness.org 70 i 11189 returns an object that can be accessed like a file, you get a <em>file Err codemadness.org 70 i 11190 descriptor</em> in the form of an <code>int</code>.</p> Err codemadness.org 70 i 11191 <div class="highlight"><pre><span></span><code><span class="cm">/* All of these return a int with a file descriptor, or</span> Err codemadness.org 70 i 11192 <span class="cm"> * -1 in case of error.</span> Err codemadness.org 70 i 11193 <span class="cm"> */</span> Err codemadness.org 70 i 11194 <span class="kt">int</span> <span class="nf">open</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pathname</span><span class="p">,</span> <span class="kt">int</span> <span class="n">flags</span><span class="p">,</span> <span class="p">...);</span> Err codemadness.org 70 i 11195 <span class="kt">int</span> <span class="nf">socket</span><span class="p">(</span><span class="kt">int</span> <span class="n">domain</span><span class="p">,</span> <span class="kt">int</span> <span class="n">type</span><span class="p">,</span> <span class="kt">int</span> <span class="n">protocol</span><span class="p">);</span> Err codemadness.org 70 i 11196 </code></pre></div> Err codemadness.org 70 i 11197 Err codemadness.org 70 i 11198 <p>You get a nonnegative integer in case of success, or -1 in case of an Err codemadness.org 70 i 11199 error. If there's an error, you look at <code>errno</code>, which gives you an Err codemadness.org 70 i 11200 integer error code. </p> Err codemadness.org 70 i 11201 <div class="highlight"><pre><span></span><code><span class="kt">int</span> <span class="n">fd</span><span class="p">;</span> Err codemadness.org 70 i 11202 Err codemadness.org 70 i 11203 <span class="nl">retry_open</span><span class="p">:</span> Err codemadness.org 70 i 11204 <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span> <span class="p">(</span><span class="s">&quot;/foo/bar/baz.txt&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> Err codemadness.org 70 i 11205 <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">==</span> <span class="mi">-1</span><span class="p">)</span> <span class="p">{</span> Err codemadness.org 70 i 11206 <span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">==</span> <span class="n">ENOENT</span><span class="p">)</span> <span class="p">{</span> Err codemadness.org 70 i 11207 <span class="cm">/* File doesn&#39;t exist */</span> Err codemadness.org 70 i 11208 <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">==</span> <span class="p">...)</span> <span class="p">[</span> Err codemadness.org 70 i 11209 <span class="p">...</span> Err codemadness.org 70 i 11210 <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">==</span> <span class="n">EINTR</span><span class="p">)</span> <span class="p">{</span> Err codemadness.org 70 i 11211 <span class="k">goto</span> <span class="n">retry_open</span><span class="p">;</span> <span class="cm">/* interrupted system call; let&#39;s retry */</span> Err codemadness.org 70 i 11212 <span class="p">}</span> Err codemadness.org 70 i 11213 <span class="p">}</span> Err codemadness.org 70 i 11214 </code></pre></div> Err codemadness.org 70 i 11215 Err codemadness.org 70 i 11216 <p>Many system calls can return <code>EINTR</code>, which means "interrupted system Err codemadness.org 70 i 11217 call", which means that <em>something</em> interrupted the kernel while it Err codemadness.org 70 i 11218 was doing your system call and it returned control to userspace, with Err codemadness.org 70 i 11219 the syscall unfinished. For example, your process may have received a Err codemadness.org 70 i 11220 Unix signal (e.g. you send it <code>SIGSTOP</code> by pressing Ctrl-Z on a Err codemadness.org 70 i 11221 terminal, or you resized the terminal and your process got a Err codemadness.org 70 i 11222 <code>SIGWINCH</code>). Most of the time <code>EINTR</code> means simply that you must Err codemadness.org 70 i 11223 retry the operation: if you Control-Z a program to suspend it, and Err codemadness.org 70 i 11224 then <code>fg</code> to continue it again; and if the program was in the middle Err codemadness.org 70 i 11225 of <code>open()</code>ing a file, you would expect it to continue at that exact Err codemadness.org 70 i 11226 point and to actually open the file. Software that doesn't check for Err codemadness.org 70 i 11227 <code>EINTR</code> can fail in very subtle ways!</p> Err codemadness.org 70 i 11228 <p>Once you have an open file descriptor, you can read from it:</p> Err codemadness.org 70 i 11229 <div class="highlight"><pre><span></span><code><span class="kt">ssize_t</span> Err codemadness.org 70 i 11230 <span class="nf">read_five_bytes</span> <span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">)</span> Err codemadness.org 70 i 11231 <span class="p">{</span> Err codemadness.org 70 i 11232 <span class="kt">ssize_t</span> <span class="n">result</span><span class="p">;</span> Err codemadness.org 70 i 11233 Err codemadness.org 70 i 11234 <span class="nl">retry</span><span class="p">:</span> Err codemadness.org 70 i 11235 <span class="n">result</span> <span class="o">=</span> <span class="n">read</span> <span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span> Err codemadness.org 70 i 11236 <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">==</span> <span class="mi">-1</span><span class="p">)</span> <span class="p">{</span> Err codemadness.org 70 i 11237 <span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">==</span> <span class="n">EINTR</span><span class="p">)</span> <span class="p">{</span> Err codemadness.org 70 i 11238 <span class="k">goto</span> <span class="n">retry</span><span class="p">;</span> Err codemadness.org 70 i 11239 <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> Err codemadness.org 70 i 11240 <span class="k">return</span> <span class="mi">-1</span><span class="p">;</span> <span class="cm">/* the caller should cherk errno */</span> Err codemadness.org 70 i 11241 <span class="p">}</span> Err codemadness.org 70 i 11242 <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> Err codemadness.org 70 i 11243 <span class="k">return</span> <span class="n">result</span><span class="p">;</span> <span class="cm">/* success */</span> Err codemadness.org 70 i 11244 <span class="p">}</span> Err codemadness.org 70 i 11245 <span class="p">}</span> Err codemadness.org 70 i 11246 </code></pre></div> Err codemadness.org 70 i 11247 Err codemadness.org 70 i 11248 <p>... and one has to remember that if <code>read()</code> returns 0, it means we Err codemadness.org 70 i 11249 were at the end-of-file; if it returns less than the number of bytes Err codemadness.org 70 i 11250 requested it means we were close to the end of file; if this is a Err codemadness.org 70 i 11251 nonblocking socket and it returns <code>EWOULDBLOCK</code> or <code>EAGAIN</code> then one Err codemadness.org 70 i 11252 must decide to retry the operation or actually wait and try again Err codemadness.org 70 i 11253 later.</p> Err codemadness.org 70 i 11254 <p>There is a lot of buggy software written in C that tries to use the Err codemadness.org 70 i 11255 POSIX API directly, and gets these subtleties wrong. Most programs Err codemadness.org 70 i 11256 written in high-level languages use the I/O facilities provided by Err codemadness.org 70 i 11257 their language, which hopefully make things easier.</p> Err codemadness.org 70 i 11258 <h1>I/O in Rust</h1> Err codemadness.org 70 i 11259 <p>Rust makes <a href="https://doc.rust-lang.org/book/first-edition/error-handling.html">error handling</a> convenient and safe. If you decide to Err codemadness.org 70 i 11260 ignore an error, the code <em>looks</em> like it is ignoring the error Err codemadness.org 70 i 11261 (e.g. you can grep for <code>unwrap()</code> and find lazy code). The Err codemadness.org 70 i 11262 code actually <em>looks better</em> if it doesn't ignore the error and Err codemadness.org 70 i 11263 properly propagates it upstream (e.g. you can use the <code>?</code> shortcut to Err codemadness.org 70 i 11264 propagate errors to the calling function).</p> Err codemadness.org 70 i 11265 <p>I keep recommending <a href="http://joeduffyblog.com/2016/02/07/the-error-model/">this article on error models</a> to people; it Err codemadness.org 70 i 11266 discusses POSIX-like error codes vs. exceptions vs. more modern Err codemadness.org 70 i 11267 approaches like Haskell's and Rust's - definitely worth studying over Err codemadness.org 70 i 11268 a few of days (also, see Miguel's valiant effort to <a href="https://github.com/migueldeicaza/NStack">move C# I/O away Err codemadness.org 70 i 11269 from exceptions for I/O errors</a>).</p> Err codemadness.org 70 i 11270 <p>So, what happens when one opens a file in Rust, from the toplevel API Err codemadness.org 70 i 11271 down to the system calls? Let's go down the rabbit hole.</p> Err codemadness.org 70 i 11272 <p>You can open a file like this:</p> Err codemadness.org 70 i 11273 <div class="highlight"><pre><span></span><code><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">fs</span>::<span class="n">File</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11274 Err codemadness.org 70 i 11275 <span class="k">fn</span> <span class="nf">main</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11276 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">File</span>::<span class="n">open</span><span class="w"> </span><span class="p">(</span><span class="s">&quot;foo.txt&quot;</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 11277 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 11278 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11279 </code></pre></div> Err codemadness.org 70 i 11280 Err codemadness.org 70 i 11281 <p>This does <em>not</em> give you a raw file descriptor; it gives you an Err codemadness.org 70 i 11282 <code>io::Result&lt;fs::File, io::Error&gt;</code>, which you must pick apart to see if Err codemadness.org 70 i 11283 you actually got back a File that you can operate on, or an error.</p> Err codemadness.org 70 i 11284 <p>Let's look at the <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/fs.rs#L235">implementation of <code>File::open()</code> and <code>File::create()</code></a>.</p> Err codemadness.org 70 i 11285 <div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">File</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11286 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">open</span><span class="o">&lt;</span><span class="n">P</span>: <span class="nb">AsRef</span><span class="o">&lt;</span><span class="n">Path</span><span class="o">&gt;&gt;</span><span class="p">(</span><span class="n">path</span>: <span class="nc">P</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11287 <span class="w"> </span><span class="n">OpenOptions</span>::<span class="n">new</span><span class="p">().</span><span class="n">read</span><span class="p">(</span><span class="kc">true</span><span class="p">).</span><span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">as_ref</span><span class="p">())</span><span class="w"></span> Err codemadness.org 70 i 11288 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11289 Err codemadness.org 70 i 11290 <span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">create</span><span class="o">&lt;</span><span class="n">P</span>: <span class="nb">AsRef</span><span class="o">&lt;</span><span class="n">Path</span><span class="o">&gt;&gt;</span><span class="p">(</span><span class="n">path</span>: <span class="nc">P</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11291 <span class="w"> </span><span class="n">OpenOptions</span>::<span class="n">new</span><span class="p">().</span><span class="n">write</span><span class="p">(</span><span class="kc">true</span><span class="p">).</span><span class="n">create</span><span class="p">(</span><span class="kc">true</span><span class="p">).</span><span class="n">truncate</span><span class="p">(</span><span class="kc">true</span><span class="p">).</span><span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">as_ref</span><span class="p">())</span><span class="w"></span> Err codemadness.org 70 i 11292 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11293 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 11294 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11295 </code></pre></div> Err codemadness.org 70 i 11296 Err codemadness.org 70 i 11297 <p>Here, <code>OpenOptions</code> is an auxiliary struct that implements a "builder" Err codemadness.org 70 i 11298 pattern. Instead of passing bitflags for the various Err codemadness.org 70 i 11299 <code>O_CREATE/O_APPEND/etc.</code> flags from the <code>open(2)</code> system call, one Err codemadness.org 70 i 11300 builds a struct with the desired options, and finally calls <code>.open()</code> Err codemadness.org 70 i 11301 on it.</p> Err codemadness.org 70 i 11302 <p>So, let's look at the <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/fs.rs#L670">implementation of <code>OpenOptions.open()</code></a>:</p> Err codemadness.org 70 i 11303 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">open</span><span class="o">&lt;</span><span class="n">P</span>: <span class="nb">AsRef</span><span class="o">&lt;</span><span class="n">Path</span><span class="o">&gt;&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">path</span>: <span class="nc">P</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11304 <span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">_open</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">as_ref</span><span class="p">())</span><span class="w"></span> Err codemadness.org 70 i 11305 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11306 Err codemadness.org 70 i 11307 <span class="w"> </span><span class="k">fn</span> <span class="nf">_open</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">path</span>: <span class="kp">&amp;</span><span class="nc">Path</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11308 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">inner</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fs_imp</span>::<span class="n">File</span>::<span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="mi">0</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11309 <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">File</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">inner</span>: <span class="nc">inner</span><span class="w"> </span><span class="p">})</span><span class="w"></span> Err codemadness.org 70 i 11310 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11311 </code></pre></div> Err codemadness.org 70 i 11312 Err codemadness.org 70 i 11313 <p>See that <code>fs_imp::File::open()</code>? That's what we want: it's the Err codemadness.org 70 i 11314 platform-specific wrapper for opening files. Let's look Err codemadness.org 70 i 11315 at <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/sys/unix/fs.rs#L422">its implementation for Unix</a>:</p> Err codemadness.org 70 i 11316 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span>: <span class="kp">&amp;</span><span class="nc">Path</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span>: <span class="kp">&amp;</span><span class="nc">OpenOptions</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11317 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cstr</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11318 <span class="w"> </span><span class="n">File</span>::<span class="n">open_c</span><span class="p">(</span><span class="o">&amp;</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 11319 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11320 </code></pre></div> Err codemadness.org 70 i 11321 Err codemadness.org 70 i 11322 <p>The first line, <code>let path = cstr(path)?</code> tries to convert a <code>Path</code> Err codemadness.org 70 i 11323 into a nul-terminated C string. The second line calls the following:</p> Err codemadness.org 70 i 11324 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">open_c</span><span class="p">(</span><span class="n">path</span>: <span class="kp">&amp;</span><span class="nc">CStr</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span>: <span class="kp">&amp;</span><span class="nc">OpenOptions</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11325 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">flags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">libc</span>::<span class="n">O_CLOEXEC</span><span class="w"> </span><span class="o">|</span><span class="w"></span> Err codemadness.org 70 i 11326 <span class="w"> </span><span class="n">opts</span><span class="p">.</span><span class="n">get_access_mode</span><span class="p">()</span><span class="o">?</span><span class="w"> </span><span class="o">|</span><span class="w"></span> Err codemadness.org 70 i 11327 <span class="w"> </span><span class="n">opts</span><span class="p">.</span><span class="n">get_creation_mode</span><span class="p">()</span><span class="o">?</span><span class="w"> </span><span class="o">|</span><span class="w"></span> Err codemadness.org 70 i 11328 <span class="w"> </span><span class="p">(</span><span class="n">opts</span><span class="p">.</span><span class="n">custom_flags</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">c_int</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="o">!</span><span class="n">libc</span>::<span class="n">O_ACCMODE</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 11329 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">fd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cvt_r</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11330 <span class="w"> </span><span class="n">open64</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">(),</span><span class="w"> </span><span class="n">flags</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span><span class="p">.</span><span class="n">mode</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">c_int</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 11331 <span class="w"> </span><span class="p">})</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11332 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">fd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FileDesc</span>::<span class="n">new</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span><span class="w"></span> Err codemadness.org 70 i 11333 Err codemadness.org 70 i 11334 <span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"></span> Err codemadness.org 70 i 11335 Err codemadness.org 70 i 11336 <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">File</span><span class="p">(</span><span class="n">fd</span><span class="p">))</span><span class="w"></span> Err codemadness.org 70 i 11337 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11338 </code></pre></div> Err codemadness.org 70 i 11339 Err codemadness.org 70 i 11340 <p>Here, <code>let flags = ...</code> converts the <code>OpenOptions</code> we had in the Err codemadness.org 70 i 11341 beginning to an int with bit flags.</p> Err codemadness.org 70 i 11342 <p>Then, it does <code>let fd = cvt_r (LAMBDA)</code>, and that lambda function Err codemadness.org 70 i 11343 calls the actual <code>open64()</code> from libc (a Rust wrapper for the system's Err codemadness.org 70 i 11344 libc): it returns a file descriptor, or -1 on error. Why is this Err codemadness.org 70 i 11345 done in a lambda? Let's look at <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/sys/unix/mod.rs#L155"><code>cvt_r()</code></a>:</p> Err codemadness.org 70 i 11346 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">cvt_r</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">F</span><span class="o">&gt;</span><span class="p">(</span><span class="k">mut</span><span class="w"> </span><span class="n">f</span>: <span class="nc">F</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"></span> Err codemadness.org 70 i 11347 <span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">T</span>: <span class="nc">IsMinusOne</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11348 <span class="w"> </span><span class="n">F</span>: <span class="nb">FnMut</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">T</span><span class="w"></span> Err codemadness.org 70 i 11349 <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11350 <span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11351 <span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">cvt</span><span class="p">(</span><span class="n">f</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11352 <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="k">ref</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">e</span><span class="p">.</span><span class="n">kind</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">ErrorKind</span>::<span class="n">Interrupted</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{}</span><span class="w"></span> Err codemadness.org 70 i 11353 <span class="w"> </span><span class="n">other</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">other</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11354 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11355 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11356 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11357 </code></pre></div> Err codemadness.org 70 i 11358 Err codemadness.org 70 i 11359 <p>Okay! Here <code>f</code> is the lambda that calls <code>open64()</code>; <code>cvt_r()</code> calls Err codemadness.org 70 i 11360 it in a loop and translates the POSIX-like result into something Err codemadness.org 70 i 11361 friendly to Rust. This loop is where it handles <code>EINTR</code>, which gets Err codemadness.org 70 i 11362 translated into <code>ErrorKind::Interrupted</code>. I suppose <code>cvt_r()</code> stands Err codemadness.org 70 i 11363 for <code>convert_retry()</code>? Let's look at Err codemadness.org 70 i 11364 the <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/sys/unix/mod.rs#L147">implementation of <code>cvt()</code></a>, which fetches the error code:</p> Err codemadness.org 70 i 11365 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">cvt</span><span class="o">&lt;</span><span class="n">T</span>: <span class="nc">IsMinusOne</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span>: <span class="nc">T</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11366 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">t</span><span class="p">.</span><span class="n">is_minus_one</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11367 <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">io</span>::<span class="n">Error</span>::<span class="n">last_os_error</span><span class="p">())</span><span class="w"></span> Err codemadness.org 70 i 11368 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11369 <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">t</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 11370 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11371 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11372 </code></pre></div> Err codemadness.org 70 i 11373 Err codemadness.org 70 i 11374 <p>(The <code>IsMinusOne</code> shenanigans are just a Rust-ism to help convert Err codemadness.org 70 i 11375 multiple integer types without a lot of <code>as</code> casts.)</p> Err codemadness.org 70 i 11376 <p>The above means, if the POSIX-like result was -1, return an <code>Err()</code> from Err codemadness.org 70 i 11377 the last error returned by the operating system. That should surely Err codemadness.org 70 i 11378 be <code>errno</code> internally, correct? Let's look at Err codemadness.org 70 i 11379 the <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/io/error.rs#L268">implementation for <code>io::Error::last_os_error()</code></a>:</p> Err codemadness.org 70 i 11380 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">last_os_error</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">Error</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11381 <span class="w"> </span><span class="n">Error</span>::<span class="n">from_raw_os_error</span><span class="p">(</span><span class="n">sys</span>::<span class="n">os</span>::<span class="n">errno</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">i32</span><span class="p">)</span><span class="w"></span> Err codemadness.org 70 i 11382 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11383 </code></pre></div> Err codemadness.org 70 i 11384 Err codemadness.org 70 i 11385 <p>We don't need to look at <code>Error::from_raw_os_error()</code>; it's just a Err codemadness.org 70 i 11386 conversion function from an <code>errno</code> value into a Rust enum value. Err codemadness.org 70 i 11387 However, let's look at <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/sys/unix/os.rs#L60"><code>sys::os::errno()</code></a>:</p> Err codemadness.org 70 i 11388 <div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">errno</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11389 <span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11390 <span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">errno_location</span><span class="p">())</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">i32</span><span class="w"></span> Err codemadness.org 70 i 11391 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11392 <span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11393 </code></pre></div> Err codemadness.org 70 i 11394 Err codemadness.org 70 i 11395 <p>Here, <code>errno_location()</code> is an <code>extern</code> function defined in GNU libc Err codemadness.org 70 i 11396 (or whatever C library your Unix uses). It returns a pointer to the Err codemadness.org 70 i 11397 actual int which is the <code>errno</code> thread-local variable. Since non-C Err codemadness.org 70 i 11398 code can't use libc's global variables directly, there needs to be a Err codemadness.org 70 i 11399 way to get their addresses via function calls - that's what Err codemadness.org 70 i 11400 <code>errno_location()</code> is for.</p> Err codemadness.org 70 i 11401 <h2>And on Windows?</h2> Err codemadness.org 70 i 11402 <p>Remember the internal <code>File.open()</code>? This is what it looks Err codemadness.org 70 i 11403 like <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/sys/windows/fs.rs#L257">on Windows</a>:</p> Err codemadness.org 70 i 11404 <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span>: <span class="kp">&amp;</span><span class="nc">Path</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span>: <span class="kp">&amp;</span><span class="nc">OpenOptions</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11405 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">to_u16s</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span> Err codemadness.org 70 i 11406 <span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">handle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11407 <span class="w"> </span><span class="n">c</span>::<span class="n">CreateFileW</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 11408 <span class="w"> </span><span class="n">opts</span><span class="p">.</span><span class="n">get_access_mode</span><span class="p">()</span><span class="o">?</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11409 <span class="w"> </span><span class="n">opts</span><span class="p">.</span><span class="n">share_mode</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11410 <span class="w"> </span><span class="n">opts</span><span class="p">.</span><span class="n">security_attributes</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">_</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11411 <span class="w"> </span><span class="n">opts</span><span class="p">.</span><span class="n">get_creation_mode</span><span class="p">()</span><span class="o">?</span><span class="p">,</span><span class="w"></span> Err codemadness.org 70 i 11412 <span class="w"> </span><span class="n">opts</span><span class="p">.</span><span class="n">get_flags_and_attributes</span><span class="p">(),</span><span class="w"></span> Err codemadness.org 70 i 11413 <span class="w"> </span><span class="n">ptr</span>::<span class="n">null_mut</span><span class="p">())</span><span class="w"></span> Err codemadness.org 70 i 11414 <span class="w"> </span><span class="p">};</span><span class="w"></span> Err codemadness.org 70 i 11415 <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">handle</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">c</span>::<span class="n">INVALID_HANDLE_VALUE</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11416 <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">Error</span>::<span class="n">last_os_error</span><span class="p">())</span><span class="w"></span> Err codemadness.org 70 i 11417 <span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> Err codemadness.org 70 i 11418 <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">File</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">handle</span>: <span class="nc">Handle</span>::<span class="n">new</span><span class="p">(</span><span class="n">handle</span><span class="p">)</span><span class="w"> </span><span class="p">})</span><span class="w"></span> Err codemadness.org 70 i 11419 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11420 <span class="w"> </span><span class="p">}</span><span class="w"></span> Err codemadness.org 70 i 11421 </code></pre></div> Err codemadness.org 70 i 11422 Err codemadness.org 70 i 11423 <p><code>CreateFileW()</code> is the Windows API function to open files. The Err codemadness.org 70 i 11424 conversion of error codes inside <code>Error::last_os_error()</code> happens Err codemadness.org 70 i 11425 analogously - it calls <code>GetLastError()</code> from the Windows API and Err codemadness.org 70 i 11426 converts it.</p> Err codemadness.org 70 i 11427 <h2>Can we not call C libraries?</h2> Err codemadness.org 70 i 11428 <p>The Rust/Unix code above depends on the system's libc for <code>open()</code> and Err codemadness.org 70 i 11429 <code>errno</code>, which are entirely C constructs. Libc is what actually does Err codemadness.org 70 i 11430 the system calls. There are efforts to make the Rust standard library Err codemadness.org 70 i 11431 <em>not</em> use libc and use syscalls directly.</p> Err codemadness.org 70 i 11432 <p>As an example, you can look at Err codemadness.org 70 i 11433 the <a href="https://github.com/rust-lang/rust/blob/3f8b93693da78c2cfe1d7f1dc6834c5ba61e0cc0/src/libstd/sys/redox/syscall/call.rs">Rust standard library for Redox</a>. Redox is a new operating Err codemadness.org 70 i 11434 system kernel entirely written in Rust. Fun times!</p> Err codemadness.org 70 i 11435 <p><strong>Update:</strong> If you want to see what a C-less libstd would look Err codemadness.org 70 i 11436 like, <a href="https://github.com/japaric/steed">take a look at steed</a>, an effort to reimplement Rust's libstd Err codemadness.org 70 i 11437 without C dependencies.</p> Err codemadness.org 70 i 11438 <h1>Conclusion</h1> Err codemadness.org 70 i 11439 <p>Rust is very meticulous about error handling, but it succeeds in Err codemadness.org 70 i 11440 making it pleasant to read. I/O functions give you back an Err codemadness.org 70 i 11441 <code>io::Result&lt;&gt;</code>, which you piece apart to see if it succeeded or got an Err codemadness.org 70 i 11442 error.</p> Err codemadness.org 70 i 11443 <p>Internally, and for each platform it supports, the Rust standard Err codemadness.org 70 i 11444 library translates <code>errno</code> from libc into an <code>io::ErrorKind</code> Rust Err codemadness.org 70 i 11445 enum. The standard library also automatically handles Unix-isms like Err codemadness.org 70 i 11446 retrying operations on <code>EINTR</code>.</p> Err codemadness.org 70 i 11447 <p>I've been enjoying reading the <a href="https://github.com/rust-lang/rust/tree/master/src/libstd">Rust standard library code</a>: it Err codemadness.org 70 i 11448 has taught me many Rust-isms, and it's nice to see how the Err codemadness.org 70 i 11449 hairy/historical libc constructs are translated into clean Rust Err codemadness.org 70 i 11450 idioms. I hope this little trip down the rabbit hole for the Err codemadness.org 70 i 11451 <code>open(2)</code> system call lets you look in other interesting places, too.</p>Moving to a new blog engine2017-06-09T09:08:15-05:002017-06-09T09:13:41-05:00Federico Mena Quinterotag:people.gnome.org,2017-06-09:/~federico/blog/new-blog.html<p>In 2003 I wrote Err codemadness.org 70 i 11452 an Err codemadness.org 70 i 11453 <a href="https://people.gnome.org/~federico/misc/activity-log.el">Emacs script to write my blog and produce an RSS feed</a>. Err codemadness.org 70 i 11454 Back then, I seemed to write multiple short blog entries in a day Err codemadness.org 70 i 11455 rather than longer articles (<em>doing Mastodon before it was cool?</em>). Err codemadness.org 70 i 11456 But my blogging patterns have changed. I've been wanting to add …</p><p>In 2003 I wrote Err codemadness.org 70 i 11457 an Err codemadness.org 70 i 11458 <a href="https://people.gnome.org/~federico/misc/activity-log.el">Emacs script to write my blog and produce an RSS feed</a>. Err codemadness.org 70 i 11459 Back then, I seemed to write multiple short blog entries in a day Err codemadness.org 70 i 11460 rather than longer articles (<em>doing Mastodon before it was cool?</em>). Err codemadness.org 70 i 11461 But my blogging patterns have changed. I've been wanting to add some Err codemadness.org 70 i 11462 more features to the script: moving to a page-per-post model, support Err codemadness.org 70 i 11463 for draft articles, tags, and syntax highlighting for code excerpts...</p> Err codemadness.org 70 i 11464 <p>This is a wheel that I do not find worth reinventing these days. Err codemadness.org 70 i 11465 After <a href="https://mastodon.social/@federicomena/8360985">asking on Mastodon</a> about static site Err codemadness.org 70 i 11466 generators (thanks to everyone who replied!), I've decided to give Err codemadness.org 70 i 11467 <a href="https://blog.getpelican.com/">Pelican</a> a try. I've reached the age where "obvious, beautiful Err codemadness.org 70 i 11468 documentation" is high on my list of things to look for when shopping Err codemadness.org 70 i 11469 for tools, and Pelican's docs are nice from the start.</p> Err codemadness.org 70 i 11470 <p>The old blog is still available <a href="https://people.gnome.org/~federico/news.html">in the old location</a>.</p> Err codemadness.org 70 i 11471 <p>If you find broken links, or stuff that doesn't work correctly here, Err codemadness.org 70 i 11472 Err codemadness.org 70 .