<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><title>Cang</title><link href="https://www.ogura.io/atom.xml" rel="self"/><link href="https://www.ogura.io/"/><updated>20260209T12:36:15.008Z</updated><id>https://www.ogura.io/</id><author><name>xiao_cang</name></author><generator uri="https://gohugo.io/">Hugo</generator><entry><title>Vibe Coding a Desktop App: The Experience</title><link href="https://www.ogura.io/posts/2026/02/vibe-coding-24-days/"/><id>https://www.ogura.io/posts/2026/02/vibe-coding-24-days/</id><published>20260209T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>I&rsquo;m a backend developer who primarily works with C, Lua, OpenResty, and Perl. C#, Swift, WinUI 3, Windows desktop development — all completely foreign territory to me.</p>
<p>I wanted to run an experiment: can you ship a publishable desktop app by relying purely on vibe coding in a tech stack you&rsquo;ve never touched?</p>
<p>The key phrase here is &ldquo;never touched.&rdquo; I don&rsquo;t know C#, I don&rsquo;t know Swift (the original codebase), I don&rsquo;t know XAML, and I&rsquo;ve never built a Windows desktop app. I wanted to strip away all my domain expertise and experience vibe coding — AI agent-driven development — in its purest form.</p>
<p>The result: <strong>24 days, from zero to Windows Store listing, 19 translation services, 715 test cases.</strong></p>
<h1 id="tools">Tools</h1>
<p>Only two AI tools were used throughout the entire project:</p>
<ul>
<li><strong>Claude Code</strong> — 100% of the coding work (writing code, research, debugging, refactoring, testing, documentation)</li>
<li><strong>GitHub Copilot Review</strong> — PR-level code review</li>
</ul>
<p>No Cursor, no Windsurf, no ChatGPT. The editor was VS Code, but I barely touched the code manually.</p>
<p>The split was roughly <strong>90% AI : 10% human</strong>. My 10% went to directional decisions (WinUI 3 vs. Tauri, feature prioritization), having Claude Code explain code and approaches then picking the reasonable one, final review, and providing hints when the AI got stuck.</p>
<p>Initially, I ran Claude Code on WSL. The experience was quite fragmented: the AI would modify code in WSL, then I&rsquo;d sync it to a Windows directory and compile in VS Code. Switching between the two environments constantly was inefficient. Later, I installed Claude Code directly on Windows, and the entire workflow became much smoother — edit, compile, and run in one place. This was an important factor in the productivity gains that followed.</p>
<h1 id="origin-and-the-decision">Origin and the Decision</h1>
<p><a href="https://github.com/tisfeng/Easydict">Easydict</a> is a popular translation dictionary app on macOS. I wanted to port it to Windows.</p>
<p>The initial idea was simple: keep the Swift backend code and only build a Windows UI frontend. I used vibe coding to evaluate several approaches and narrowed it down to two, developed in parallel: WinUI 3 (Microsoft&rsquo;s official Windows desktop UI framework) and Tauri (a cross-platform framework based on Rust + Web technologies).</p>
<p>After pushing both approaches forward, a common problem quickly emerged: the backend code also had to be fully ported. Keeping the Swift backend was impractical — either the dependency chain became extremely complex, or the results on Windows were poor.</p>
<p>Then the Tauri approach hit a wall first: the build environment was too complex, and I could never get a POC sample running. Meanwhile, WinUI 3 had already produced a working demo. <strong>So I dropped Tauri and went all-in on WinUI 3.</strong></p>
<p>For the backend, I chose .NET 8 (an LTS version), which felt very smooth on Windows. The whole C# + WinUI 3 + .NET 8 stack is truly a first-class citizen in the Windows ecosystem.</p>
<p>During the POC phase, I created a subdirectory directly inside the Easydict macOS source repository<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. Once the POC was running and the approach was validated, I created a completely independent project, detaching from the original source repository.</p>
<h1 id="prototyping-and-building-features">Prototyping and Building Features</h1>
<p>I gave the AI agent almost full control over the UI design. My only requirements were: reuse the original Easydict icon assets where possible, keep the interface clean and minimal, and support Dark/Light themes. The result was satisfactory — what you see today. As someone who knows absolutely nothing about XAML, the outcome exceeded my expectations.</p>
<p>After the prototype was running, I started stacking features one by one. The key strategy at this stage: <strong>commit each feature individually with Git.</strong> One commit per feature, preventing later changes from breaking earlier ones. This is basic instinct for a backend developer, but it&rsquo;s especially important in vibe coding — the AI sometimes accidentally breaks existing functionality when adding new features.</p>
<p>Once two or three features were working properly, I started adding test coverage. On 2026-01-20, I added the first unit test framework (xUnit + FluentAssertions).</p>
<h1 id="parallel-pr-development">Parallel PR Development</h1>
<p>After pushing the code to GitHub, I started using a PR workflow to develop features in parallel. That&rsquo;s when I discovered an unexpectedly good combination: GitHub Copilot Review + Claude Code work remarkably well together.</p>
<p>My development loop looked like this:</p>
<pre tabindex="0"><code>Claude Code writes code and submits PR
    → GitHub Copilot Review finds issues, posts review comments
        → Claude Code reads review comments and resolves them
            → Push
                → Another round of review
                    → Loop until approved
</code></pre><p>Code quality was generally well-maintained through this loop. And because multiple PRs were running in parallel, while waiting for Copilot review or Claude Code to resolve comments, I&rsquo;d switch to the next feature&rsquo;s PR. Very efficient.</p>
<p>Not much domain knowledge was needed throughout this process. I had zero knowledge of C# and the original Swift code. I&rsquo;d have Claude Code explain things to me and pick the approach that sounded reasonable. Occasionally I&rsquo;d apply general software engineering intuition — &ldquo;this design is too tightly coupled&rdquo; or &ldquo;this should have an interface abstraction.&rdquo;</p>
<h1 id="release-journey">Release Journey</h1>
<h2 id="v010--first-release-day-5">v0.1.0 — First Release (Day 5)</h2>
<p>5 days after the project started, the first usable version was released. On the same day, I set up CI/CD (GitHub Actions), merged the first PR (#1), and had a basic automated build and test pipeline running. This pace would be hard to imagine in traditional development — I still didn&rsquo;t know C#&rsquo;s basic syntax.</p>
<h2 id="v020--the-turning-point-day-12">v0.2.0 — The Turning Point (Day 12)</h2>
<p>12 days after the project started, I submitted release builds to two distribution channels simultaneously: Windows Store (MSIX format) and WinGet (Portable ZIP format).</p>
<p>This version was a major turning point. It meant the app was on a real trajectory — end users could install it through normal channels, and I could start collecting real user feedback instead of building a hardcore tool that required developers to compile locally.</p>
<p>But the release process was far from smooth. This version went through <strong>17 Release Candidates (RCs)</strong>, from rc1 to rc17, spanning 2 days. The main pain points were in MSIX manifest configuration: MinVersion requirements, architecture support configuration<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, icon asset specification requirements, and Store submission compliance checks. These issues surfaced one after another, and each small fix required repackaging, testing, and resubmitting — hence the 17 RCs.</p>
<h2 id="the-blurry-msix-icon-saga">The Blurry MSIX Icon Saga</h2>
<p>After packaging as MSIX, a stubborn issue appeared: the Start Menu icon was extremely blurry. Oddly, the .zip portable version had no such problem — only the MSIX-installed version was affected. It took multiple version iterations to resolve. The root cause was that MSIX packages have special size and format requirements for icon assets, different from how regular .exe icons are handled.</p>
<h1 id="testing-and-performance">Testing and Performance</h1>
<p>To improve stability, a multi-layer testing system was gradually established: unit tests (Day 4), integration tests (Day 12), UI automation screenshots (Day 14), and UI regression tests (Day 15). With these in place, my local verification workload dropped significantly — I could just look at automated screenshots in CI to determine whether a change caused visual regression, instead of manually running the app and checking each feature.</p>
<p>In the later stages, I started focusing on performance. I added performance regression tests to monitor the latency of hot paths like mouse-select-to-translate and application startup. However, the test framework can&rsquo;t yet intuitively prove that a given change hasn&rsquo;t impacted performance — benchmark stability in CI environments has too much variance. This needs more research.</p>
<h1 id="where-ai-exceeded-expectations">Where AI Exceeded Expectations</h1>
<p>Two aspects of the AI&rsquo;s performance clearly exceeded my expectations.</p>
<p>The first was <strong>architecture design</strong>. The overall project structure proposed by the AI was remarkably sound: the solution was split into <code>Easydict.WinUI</code> (UI layer), <code>Easydict.TranslationService</code> (business logic layer), and <code>Easydict.SidecarClient</code> (IPC layer); the streaming translation architecture used SSE + <code>IAsyncEnumerable&lt;string&gt;</code>, the idiomatic C# approach for streaming data<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>; the Translation Service inheritance hierarchy was cleanly designed: <code>ITranslationService</code> → <code>BaseTranslationService</code> → <code>BaseOpenAIService</code>, where adding a new OpenAI-compatible service only requires extending <code>BaseOpenAIService</code> and providing Endpoint/ApiKey/Model. Even if I knew C#, these architectural decisions would probably have taken me several refactoring cycles to reach this level of design quality.</p>
<p>The second was <strong>debugging ability</strong>. When bugs appeared, the AI could locate and fix issues on its own with a surprisingly high success rate. This was especially true for WinUI 3-specific pitfalls (thread dispatching, COM object lifecycles) — issues that would have cost me significant time searching through documentation and Stack Overflow.</p>
<h1 id="toolchain-pain-points">Toolchain Pain Points</h1>
<p>Beyond the code itself, the toolchain caused more pain than the code.</p>
<p>The entire MSIX packaging, signing, and Store submission pipeline was extremely complex. Every configuration item in the manifest could potentially cause a packaging failure or Store review rejection. Most of the effort behind v0.2.0&rsquo;s 17 RCs was spent here.</p>
<p>The GitHub Actions Windows runner environment also had many subtle differences from the local development environment: different Windows App SDK versions and installation states, WinUI tests requiring a graphical environment that mostly fails on headless CI, and MSIX packaging on CI requiring additional toolchain configuration. The AI could help resolve these issues, but it often took multiple attempts. The AI&rsquo;s solutions are based on documentation and general knowledge, while the actual CI environment is a black box — you can only debug through the push → check logs → fix → push again cycle.</p>
<h1 id="conclusions-and-advice">Conclusions and Advice</h1>
<p><strong>What is vibe coding best for?</strong> Exploring unfamiliar technology domains and rapidly prototyping. This is vibe coding&rsquo;s greatest value today. Going from zero to Store listing in 24 days — without AI, given my zero-knowledge baseline in C#/WinUI, this timeline would have been at least 5-10x longer.</p>
<p>But foundational engineering skills are still essential. Even though 90% of the code was written by AI, the remaining 10% of human intervention was still critical. That 10% wasn&rsquo;t about writing code — it was Git workflow (per-feature commits, parallel PRs, branch management), CI/CD mindset (establishing automated pipelines early), testing strategy (knowing when to add tests and at what level), release strategy (shipping to users early to gather feedback), and architectural judgment (choosing the more reasonable approach among the AI&rsquo;s proposals). These are all universal software engineering skills, independent of any specific language or framework. <strong>Vibe coding lowers the barrier of the tech stack, but it doesn&rsquo;t lower the barrier of engineering discipline.</strong></p>
<p>A few pieces of advice for developers wanting to try vibe coding:</p>
<ol>
<li>Pick a domain you don&rsquo;t know but want to learn — vibe coding&rsquo;s greatest value isn&rsquo;t writing code you already know how to write, but helping you do things you can&rsquo;t</li>
<li>Establish Git + CI + tests early — this is your only safety net. AI makes mistakes, and those mistakes are often hard to spot</li>
<li>Use PR review tools as quality gates — AI-written code needs another AI to review it; humans make the final call</li>
<li>Ship early — don&rsquo;t wait for &ldquo;perfect.&rdquo; Deliver a minimum viable version first</li>
<li>Maintain your sense of direction — AI can do 90% of the execution, but only you can make that 10% of directional decisions</li>
</ol>
<hr>
<h1 id="project-data">Project Data</h1>
<h3 id="key-metrics">Key Metrics</h3>
<table>
  <thead>
      <tr>
          <th>Metric</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Project Duration</td>
          <td><strong>24 days</strong> (2026-01-16 ~ 2026-02-08)</td>
      </tr>
      <tr>
          <td>Total Commits</td>
          <td><strong>422</strong> (master: 220)</td>
      </tr>
      <tr>
          <td>Pull Requests</td>
          <td><strong>~46</strong> (squash merge)</td>
      </tr>
      <tr>
          <td>Releases</td>
          <td><strong>12</strong> (v0.1.0 ~ v0.3.4)</td>
      </tr>
      <tr>
          <td>Release Tags (incl. RCs)</td>
          <td><strong>51</strong></td>
      </tr>
      <tr>
          <td>Source Code (C# + XAML)</td>
          <td><strong>19,554 lines</strong></td>
      </tr>
      <tr>
          <td>Test Code</td>
          <td><strong>17,474 lines</strong></td>
      </tr>
      <tr>
          <td>Source:Test Ratio</td>
          <td><strong>1.12 : 1</strong></td>
      </tr>
      <tr>
          <td>Translation Services</td>
          <td><strong>19</strong></td>
      </tr>
      <tr>
          <td>Test Methods</td>
          <td><strong>715</strong> ([Fact] 695 + [Theory] 20)</td>
      </tr>
      <tr>
          <td>Source Files</td>
          <td>73 .cs + 9 .xaml</td>
      </tr>
      <tr>
          <td>Test Files</td>
          <td>93 .cs</td>
      </tr>
      <tr>
          <td>CI Workflows</td>
          <td>9</td>
      </tr>
  </tbody>
</table>
<h3 id="code-by-module">Code by Module</h3>
<table>
  <thead>
      <tr>
          <th>Module</th>
          <th>C# Lines</th>
          <th>XAML Lines</th>
          <th>Total</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Easydict.WinUI (UI layer)</td>
          <td>11,191</td>
          <td>1,859</td>
          <td><strong>13,050</strong></td>
      </tr>
      <tr>
          <td>Easydict.TranslationService (business layer)</td>
          <td>5,930</td>
          <td>—</td>
          <td><strong>5,930</strong></td>
      </tr>
      <tr>
          <td>Easydict.SidecarClient (IPC layer)</td>
          <td>574</td>
          <td>—</td>
          <td><strong>574</strong></td>
      </tr>
      <tr>
          <td>TranslationService.Tests</td>
          <td>9,218</td>
          <td>—</td>
          <td><strong>9,218</strong></td>
      </tr>
      <tr>
          <td>WinUI.Tests</td>
          <td>3,324</td>
          <td>—</td>
          <td><strong>3,324</strong></td>
      </tr>
      <tr>
          <td>UIAutomation.Tests</td>
          <td>2,466</td>
          <td>—</td>
          <td><strong>2,466</strong></td>
      </tr>
  </tbody>
</table>
<h3 id="milestone-timeline">Milestone Timeline</h3>
<pre tabindex="0"><code>2026-01-16  Day 0   Project started, first commit
2026-01-20  Day 4   Unit test framework added (xUnit + FluentAssertions)
2026-01-21  Day 5   v0.1.0 released + CI/CD + first PR (#1)
2026-01-25  Day 9   MSIX packaging + WinGet release workflow
2026-01-26  Day 10  Windows Store submission begins (17 RC iterations)
2026-01-28  Day 12  v0.2.0 released — turning point (Store + WinGet dual-channel)
2026-01-30  Day 14  UI automation tests + screenshot verification
2026-02-01  Day 16  v0.2.2 + UI regression tests
2026-02-02  Day 17  v0.2.3 + mouse-select-to-translate + Copilot Review Loop
2026-02-03  Day 18  Performance benchmarks
2026-02-04  Day 19  v0.3.0 (Inno Setup EXE installer + GitHub Pages website)
2026-02-06  Day 21  v0.3.1 / v0.3.2
2026-02-08  Day 23  v0.3.3 / v0.3.4
</code></pre><h3 id="19-translation-services">19 Translation Services</h3>
<p>Google Translate, Google Web, Bing, DeepL, OpenAI, DeepSeek, Gemini, Groq, Zhipu AI, GitHub Models, Doubao, Volcano, Caiyun, NiuTrans, Linguee, Youdao, Ollama, Built-in AI, Custom OpenAI</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This made it easy to reference the original Swift code at any time, letting the AI compare and understand the business logic and translation service implementations.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Eventually dropped x86, keeping only x64 and arm64.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>As someone who doesn&rsquo;t know C#, I could never have proposed this approach.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>I&rsquo;m a backend developer who primarily works with C, Lua, OpenResty, and Perl. C#, Swift, WinUI 3, Windows desktop development — all completely foreign territory to me.</p>
<p>I wanted to run an experiment: can you ship a publishable desktop app by relying purely on vibe coding in a tech stack you&rsquo;ve never touched?</p>
<p>The key phrase here is &ldquo;never touched.&rdquo; I don&rsquo;t know C#, I don&rsquo;t know Swift (the original codebase), I don&rsquo;t know XAML, and I&rsquo;ve never built a Windows desktop app. I wanted to strip away all my domain expertise and experience vibe coding — AI agent-driven development — in its purest form.</p>]]></summary></entry><entry><title>Building Local Repositories for Various Linux Distributions</title><link href="https://www.ogura.io/posts/2021/02/build-local-repo-for-deb-rpm-and-zypper/"/><id>https://www.ogura.io/posts/2021/02/build-local-repo-for-deb-rpm-and-zypper/</id><published>20210201T22:36:08.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <h1 id="background">Background</h1>
<p>Containerized applications often spend a lot of time downloading packages during build (sometimes encountering network issues, requiring multiple builds for one version to succeed), yet most dependency packages rarely change. So I had this optimization idea for building images:</p>
<p>Use a minimal container to pre-download required packages locally, then build these packages into local repositories. In containers that need to be built, replace the software sources with local repositories to save container build time (by orders of magnitude).</p>
<p>Another reason I&rsquo;m writing this blog post: although everything used here is existing tools and software, besides their own manuals and <code>--help</code>, other helpful documentation is really scattered. And from my searching, these existing tools always have small &ldquo;pitfalls&rdquo; not mentioned in documentation, or even rarely encountered on sites like Stack Overflow - solving these &ldquo;pitfalls&rdquo; is what takes the most time.</p>
<h1 id="tldr">TL;DR</h1>
<p>I&rsquo;ll later open source part of this on GitHub, placed here (TODO)</p>
<p>Currently tested compatible distributions:</p>
<ul>
<li>centos 6 / 7 / 8</li>
<li>fedora 31 / 32 / 33</li>
<li>amazonlinux 1 / 2</li>
<li>ubuntu trusty (14.04) / xenial (16.04) / bionic (18.04) / focal (20.04)</li>
<li>debian jessie (8) / stretch (9) / buster (10)</li>
<li>opensuse leap 15</li>
</ul>
<h1 id="process">Process</h1>
<ol>
<li>Based on a minimal container for the distribution, add some software sources that will be needed.</li>
<li>For different distributions, use the corresponding package management tool to download all packages in the required package list plus their dependencies</li>
<li>Place these packages in corresponding directories by distribution, use repository creation commands in the container to build local repositories</li>
<li>Use a simple static web server, listening on a local port. This sets up a local http software repository</li>
<li>Add the local software source to the <code>Dockerfile</code> of containers that need frequent rebuilds. Note that local software repositories generally don&rsquo;t have signature verification or https, so you need to manually add trust.</li>
</ol>
<p>Here I&rsquo;ll only explain the more tedious steps</p>
<h2 id="0x02-package-download">0x02. Package Download</h2>
<h3 id="yum--dnf">yum / dnf</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cd /path/to/dir <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> yumdownloader --resolve pkg-1 pkg-2 ...
</span></span></code></pre></div><ul>
<li><code>yumdownloader</code> is preferred here. The previous approach tried <code>dnf install --downloadonly</code>, and found quite a few unknown pitfalls. One of them is that after downloading, packages already downloaded locally occasionally get deleted - feels like <code>dnf / yum</code> has some storage optimization strategies.</li>
<li>The <code>--resolve</code> option tells <code>yumdownloader</code> to download dependencies for the specified packages</li>
<li><code>--installroot</code> <strong>Not recommended</strong> to use this option to specify download path. After using this option, macros (variables) in software source config files won&rsquo;t be automatically resolved. For example, the common <code>$releasever</code> variable would need extra manual specification.</li>
<li><code>yumdownloader</code> downloads packages directly to the working directory, just use cd to switch the working directory first</li>
</ul>
<h3 id="apt-get">apt-get</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cd /path/to/dir <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> apt-get download <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">$(</span>apt-cache depends --recurse --no-recommends --no-suggests <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        --no-conflicts --no-breaks --no-replaces --no-enhances <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        pkg-1 pkg-2 ... | grep <span style="color:#e6db74">&#34;^\w&#34;</span><span style="color:#66d9ef">)</span>
</span></span></code></pre></div><ul>
<li>If using <code>apt-get install --donwload-only --reinstall</code> to download packages, dependency packages that already exist in the current container won&rsquo;t be downloaded again.</li>
</ul>
<p>For example, if the downloader-container already has <code>ca-certificates</code> and <code>openssl</code> packages installed, when executing the following command, the result is: due to the <code>--reinstall</code> option <code>ca-certificates</code> will be downloaded, but <code>openssl</code> as a dependency of <code>ca-certificates</code> will be ignored.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ apt-get download <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">$(</span>apt-cache depends --recurse --no-recommends --no-suggests <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        --no-conflicts --no-breaks --no-replaces --no-enhances <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        ca-certificates | grep <span style="color:#e6db74">&#34;^\w&#34;</span><span style="color:#66d9ef">)</span>
</span></span></code></pre></div><ul>
<li>Here we use <code>apt-get download</code> rather than <code>apt-get --install --donwload-only</code>, mainly because in the subcommand <code>apt-cache depends</code>, the queried dependencies will have preferred and alternative choices, and these two are often conflicting. Even if <code>apt-get install</code> uses <code>--donwload-only</code>, it will cause package download failure because conflicts can&rsquo;t be resolved.</li>
</ul>
<p>Below is an example of <code>apt-cache depends</code> output, where <code>pinentry-curses</code> is the more preferred choice over <code>&lt;pinentry:i386&gt;</code>.
Detailed explanation can be found at <a href="https://www.thecodeship.com/gnu-linux/understanding-apt-cache-depends-output/">https://www.thecodeship.com/gnu-linux/understanding-apt-cache-depends-output/</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ apt-cache depends --recurse --no-recommends <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-suggests --no-conflicts --no-breaks <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-replaces --no-enhances --no-pre-depends <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    gnupg2 | grep -E <span style="color:#e6db74">&#39;^gnupg-agent:i386&#39;</span> -A10
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>gnupg-agent:i386
</span></span><span style="display:flex;"><span> |Depends: pinentry-curses:i386
</span></span><span style="display:flex;"><span>  Depends: &lt;pinentry:i386&gt;
</span></span><span style="display:flex;"><span>    mew-beta-bin:i386
</span></span><span style="display:flex;"><span>    mew-bin:i386
</span></span><span style="display:flex;"><span>    pinentry-curses:i386
</span></span><span style="display:flex;"><span>    pinentry-gnome3:i386
</span></span><span style="display:flex;"><span>    pinentry-gtk2:i386
</span></span><span style="display:flex;"><span>    pinentry-qt:i386
</span></span><span style="display:flex;"><span>    pinentry-tty:i386
</span></span><span style="display:flex;"><span>  Depends: libassuan0:i386
</span></span></code></pre></div><ul>
<li><code>apt-get download</code> also downloads packages directly to the current directory, so just use the <code>cd</code> command to switch working directory first</li>
</ul>
<h3 id="zypper">zypper</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ zypper --no-gpg-checks --non-interactive <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --pkg-cache-dir /path/to/dir <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    install -y -f --download-only <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    pkg-1 pkg-2 ...
</span></span></code></pre></div><ul>
<li><code>--non-interactive</code> is mainly for scripts, preventing <code>zypper</code> from waiting for user input until timeout</li>
<li><code>--pkg-cache-dir</code> specifies the download directory</li>
<li><code>-f</code> forces downloading already installed packages. This actually encounters the same problem as <code>apt-get install --download-only</code>, where dependency packages won&rsquo;t be downloaded if already installed. Currently I write it this way, adding missing base packages manually.</li>
<li>For <code>zypper</code>, distinguish between global arguments and subcommand arguments. Specifically for this command, before install are global arguments, and after are subcommand arguments</li>
</ul>
<h2 id="0x03-directory-structure">0x03. Directory Structure</h2>
<h3 id="yum">yum</h3>
<p>The yum repository directory structure is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>base/
</span></span><span style="display:flex;"><span>├── amazonlinux-1
</span></span><span style="display:flex;"><span>│   └── x86_64
</span></span><span style="display:flex;"><span>|       ├── audit-libs-2.6.5-3.28.amzn2.i686.rpm
</span></span><span style="display:flex;"><span>|       ├── ...
</span></span><span style="display:flex;"><span>│       └── repodata
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div><p>Note: yum repository structure is relatively simple. Under distribution subdirectory -&gt; CPU architecture directory, store downloaded rpm packages, then create local repository index in the same directory.</p>
<p>Command to create yum repository index:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /path/to/dir <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> createrepo --update ./
</span></span></code></pre></div><p>There&rsquo;s also a C version <code>createrepo_c</code> which is faster with the same usage. Recommended for newer distributions, like centos 8 / fedora 31+ / amazonlinux</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /path/to/dir <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> createrepo_c --update ./
</span></span></code></pre></div><p>Newer distributions have some packages built with modularity<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. If you want to build local repositories for these packages, extra commands are needed:</p>
<blockquote>
<p>Documentation at: <a href="https://docs.fedoraproject.org/en-US/modularity/hosting-modules/">https://docs.fedoraproject.org/en-US/modularity/hosting-modules/</a></p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /path/to/dir <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> createrepo_c --update ./ <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> repo2module -s stable -n REPO_NAME -d ./ ./repodata/modules <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> modifyrepo_c --mdtype<span style="color:#f92672">=</span>modules ./repodata/modules.yaml ./repodata
</span></span></code></pre></div><p>Where <code>REPO_NAME</code> is the local repository name</p>
<p>A noteworthy command here is <code>repo2module</code> (from <a href="https://github.com/rpm-software-management/modulemd-tools">https://github.com/rpm-software-management/modulemd-tools</a>), because the above documentation doesn&rsquo;t mention how to generate the <code>modules.yaml</code> file.</p>
<p>fedora or centos 8 (needs additional epel repository) can install the <code>repo2module</code> command via <code>dnf install -y python3-gobject modulemd-tools</code></p>
<h3 id="apt">apt</h3>
<p>The apt repository directory structure is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ubuntu/
</span></span><span style="display:flex;"><span>├── dists
</span></span><span style="display:flex;"><span>│   ├── bionic
</span></span><span style="display:flex;"><span>│   │   └── base
</span></span><span style="display:flex;"><span>│   │       └── main
</span></span><span style="display:flex;"><span>│   │           └── binary-amd64
</span></span><span style="display:flex;"><span>|  ...
</span></span><span style="display:flex;"><span>└── pool
</span></span><span style="display:flex;"><span>    ├── bionic
</span></span><span style="display:flex;"><span>    │   └── base
</span></span><span style="display:flex;"><span>    │       └── main
</span></span><span style="display:flex;"><span>    │           └── binary-amd64
</span></span><span style="display:flex;"><span>   ...
</span></span></code></pre></div><p>Note: apt repository has two subdirectories <code>dists/</code> and <code>pool/</code>. <code>dists/</code> subdirectory stores indexes, <code>pool/</code> subdirectory stores packages.</p>
<p>Command to create apt repository index:</p>
<blockquote>
<p>Here the local repository won&rsquo;t use gpg signed Release, full command at: <a href="https://medium.com/sqooba/create-your-own-custom-and-authenticated-apt-repository-1e4a4cf0b864#35dd">https://medium.com/sqooba/create-your-own-custom-and-authenticated-apt-repository-1e4a4cf0b864#35dd</a></p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /path/to/dir
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>apt-ftparchive --arch amd64 packages <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    pool/bionic/base/main/binary-amd64 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    &gt; dists/base/main/binary-amd64/Packages
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>gzip -k -c <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -f dists/base/main/binary-amd64/Packages <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    &gt; dists/base/main/binary-amd64/Packages.gz
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>apt-ftparchive release dists/bionic/base &gt; dists/bionic/Release
</span></span></code></pre></div><p>Where <code>base</code> is a custom repository subdirectory, convenient for future expansion.
The <code>apt-ftparchive</code> command can be installed via <code>apt-get install -y dpkg-dev</code>.</p>
<h2 id="0x05-adding-local-repository">0x05. Adding Local Repository</h2>
<p>The <code>host.docker.internal</code> below is a hostname added via <code>docker build</code>&rsquo;s <code>--add-host</code>, 4891 is the port openresty listens on locally</p>
<h3 id="yum-1">yum</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>printf <span style="color:#e6db74">&#34;[local-base]\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">name=Local Base Repo\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">baseurl=http://host.docker.internal:4891/base/centos-7/x86_64/\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">skip_if_unavailable=True\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">gpgcheck=0\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">repo_gpgcheck=0\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">enabled=1\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">enabled_metadata=1&#34;</span> &gt; /etc/yum.repos.d/local-base.repo
</span></span></code></pre></div><h3 id="zypper-1">zypper</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>printf <span style="color:#e6db74">&#34;[local-base]\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">name=Local Base Repo\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">baseurl=http://host.docker.internal:4891/base/sles-12/x86_64/\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">skip_if_unavailable=True\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">gpgcheck=0\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">repo_gpgcheck=0\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">enabled=1\n\
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">enabled_metadata=1&#34;</span> &gt; /root/local-base.repo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> zypper -n ar --check --refresh -G file:///root/local-base.repo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> zypper -n mr --gpgcheck-allow-unsigned-repo local-base <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> zypper -n mr --gpgcheck-allow-unsigned-package local-base <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;&amp;</span> rm -f /root/local-base.repo
</span></span></code></pre></div><h3 id="apt-1">apt</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;deb [trusted=yes] http://host.docker.internal:4891/ubuntu bionic/base main&#34;</span> &gt; /etc/apt/sources.list
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://docs.pagure.org/modularity/">https://docs.pagure.org/modularity/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <h1 id="background">Background</h1>
<p>Containerized applications often spend a lot of time downloading packages during build (sometimes encountering network issues, requiring multiple builds for one version to succeed), yet most dependency packages rarely change. So I had this optimization idea for building images:</p>
<p>Use a minimal container to pre-download required packages locally, then build these packages into local repositories. In containers that need to be built, replace the software sources with local repositories to save container build time (by orders of magnitude).</p>]]></summary></entry><entry><title>Compatibility Issue Between Older Kernel Versions and AMD Family 17h CPUs</title><link href="https://www.ogura.io/posts/2020/11/amd-family-17h-compatibility-issue-with-older-kernel-version/"/><id>https://www.ogura.io/posts/2020/11/amd-family-17h-compatibility-issue-with-older-kernel-version/</id><published>20201113T17:20:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Recently, while testing the compatibility of our low-level software across different kernel versions, a colleague encountered a strange issue: when using KVM to boot <code>CentOS 7</code> with specific kernel versions, a fatal error occurred that prevented the system from starting.</p>
<p>The affected kernel versions were:</p>
<ul>
<li><code>3.10.0-123</code></li>
<li><code>3.10.0-229</code></li>
</ul>
<h1 id="tldr">TL;DR</h1>
<p>This is actually a compatibility bug between AMD Family-17h architecture CPUs and older kernel versions. The bug has been fixed in newer kernel versions:</p>
<p><a href="https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/commit/?id=e40ed1542dd779e5037a22c6b534e57127472365">https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/commit/?id=e40ed1542dd779e5037a22c6b534e57127472365</a></p>
<h1 id="troubleshooting-process">Troubleshooting Process</h1>
<h2 id="lvm">LVM?</h2>
<p>After the OS in KVM failed to boot, I connected to the machine via VNC and found this error:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>dracut-initqueue timeout and could not boot – warning /dev/centos/root-lv does not exist
</span></span></code></pre></div><p>I&rsquo;ve seen this error many times, and searching for keywords brings up numerous solutions. Most cases are caused by UUID mismatches, but since I was using LVM, I suspected the kernel wasn&rsquo;t recognizing the LVM partition during boot, preventing system startup.</p>
<p>I then tried using standard partitions instead of LVM and reinstalled the system. The error persisted - still couldn&rsquo;t boot.</p>
<h2 id="xfsext4ext2">XFS/EXT4/EXT2?</h2>
<p>At this point, I began to suspect the boot partition filesystem. The reinstalled system was using <code>XFS</code>, and I recalled that very old kernel versions only support ext2/ext3/ext4 filesystems. So I reinstalled the OS twice more, using <code>ext4</code> and <code>ext2</code> for the <code>/boot</code> partition respectively. Still no luck.</p>
<h2 id="grub2">GRUB2?</h2>
<p>Then I started suspecting GRUB 2 parameters. After switching to standard partitions, the boot disk specification became <code>linux16 ... root=UUID=xxxx</code>. Could this <code>root=UUID=xxx</code> format also be unsupported by older kernel versions? So I added <code>GRUB_DISABLE_LINUX_UUID=true</code> to <code>/etc/default/grub</code>, then ran:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Note: KVM wasn&#39;t using EFI boot</span>
</span></span><span style="display:flex;"><span>grub2-mkconfig -o /boot/grub2/grub.cfg
</span></span></code></pre></div><p>After updating, I tried rebooting again&hellip;</p>
<p>Still didn&rsquo;t work.</p>
<h2 id="virtio">VIRTIO?</h2>
<p>I started suspecting the kernel couldn&rsquo;t find the device at all, so I began investigating KVM&rsquo;s virtio. Virtio is the disk bus provided by KVM, which should require corresponding drivers. I ran the <code>lsinitrd</code> command to check for the drivers:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># sudo lsinitrd /boot/initramfs-3.10.0-229.7.2.el7.x86_64.img | grep virtio</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>-rw-r--r--   <span style="color:#ae81ff">1</span> root     root        <span style="color:#ae81ff">27885</span> Jun <span style="color:#ae81ff">24</span>  <span style="color:#ae81ff">2015</span> usr/lib/modules/3.10.0-229.7.2.el7.x86_64/kernel/drivers/block/virtio_blk.ko
</span></span><span style="display:flex;"><span>-rw-r--r--   <span style="color:#ae81ff">1</span> root     root        <span style="color:#ae81ff">52861</span> Jun <span style="color:#ae81ff">24</span>  <span style="color:#ae81ff">2015</span> usr/lib/modules/3.10.0-229.7.2.el7.x86_64/kernel/drivers/char/virtio_console.ko
</span></span><span style="display:flex;"><span>-rw-r--r--   <span style="color:#ae81ff">1</span> root     root        <span style="color:#ae81ff">50501</span> Jun <span style="color:#ae81ff">24</span>  <span style="color:#ae81ff">2015</span> usr/lib/modules/3.10.0-229.7.2.el7.x86_64/kernel/drivers/net/virtio_net.ko
</span></span><span style="display:flex;"><span>-rw-r--r--   <span style="color:#ae81ff">1</span> root     root        <span style="color:#ae81ff">29125</span> Jun <span style="color:#ae81ff">24</span>  <span style="color:#ae81ff">2015</span> usr/lib/modules/3.10.0-229.7.2.el7.x86_64/kernel/drivers/scsi/virtio_scsi.ko
</span></span><span style="display:flex;"><span>drwxr-xr-x   <span style="color:#ae81ff">2</span> root     root            <span style="color:#ae81ff">0</span> Nov <span style="color:#ae81ff">13</span> 16:19 usr/lib/modules/3.10.0-229.7.2.el7.x86_64/kernel/drivers/virtio
</span></span><span style="display:flex;"><span>-rw-r--r--   <span style="color:#ae81ff">1</span> root     root        <span style="color:#ae81ff">15797</span> Jun <span style="color:#ae81ff">24</span>  <span style="color:#ae81ff">2015</span> usr/lib/modules/3.10.0-229.7.2.el7.x86_64/kernel/drivers/virtio/virtio.ko
</span></span><span style="display:flex;"><span>-rw-r--r--   <span style="color:#ae81ff">1</span> root     root        <span style="color:#ae81ff">21253</span> Jun <span style="color:#ae81ff">24</span>  <span style="color:#ae81ff">2015</span> usr/lib/modules/3.10.0-229.7.2.el7.x86_64/kernel/drivers/virtio/virtio_pci.ko
</span></span><span style="display:flex;"><span>-rw-r--r--   <span style="color:#ae81ff">1</span> root     root        <span style="color:#ae81ff">25541</span> Jun <span style="color:#ae81ff">24</span>  <span style="color:#ae81ff">2015</span> usr/lib/modules/3.10.0-229.7.2.el7.x86_64/kernel/drivers/virtio/virtio_ring.ko
</span></span></code></pre></div><p>Result: drivers were present, no difference from newer kernels that boot successfully.</p>
<h2 id="amd-family-17h">AMD Family-17h!</h2>
<p>There was still one clue - a message that flashed by quickly during boot (I had to take screenshots after many reboots to finally read it): <code>core perfctr but no constraints; unknown hardware!</code></p>
<p>Googling this keyword, everything became clear: this is a compatibility issue between AMD Family-17h and older kernel versions. Here&rsquo;s the explanation:</p>
<blockquote>
<p>In family-17h, there is no PMC-event constraint. All events, irrespective of
the type, can be measured using any of the six generic performance counters.</p>
</blockquote>
<p>AMD Family-17h corresponds to the Zen/Zen+/Zen2 architecture CPUs, which matches the host machine running this VM. This also explains why the same kernel version worked fine on Intel platforms.</p>
<p>The solution is simply to upgrade the kernel - this fix has been applied by major operating systems years ago.</p>
]]></content><summary type="html"><![CDATA[ <p>Recently, while testing the compatibility of our low-level software across different kernel versions, a colleague encountered a strange issue: when using KVM to boot <code>CentOS 7</code> with specific kernel versions, a fatal error occurred that prevented the system from starting.</p>
<p>The affected kernel versions were:</p>
<ul>
<li><code>3.10.0-123</code></li>
<li><code>3.10.0-229</code></li>
</ul>
<h1 id="tldr">TL;DR</h1>
<p>This is actually a compatibility bug between AMD Family-17h architecture CPUs and older kernel versions. The bug has been fixed in newer kernel versions:</p>]]></summary></entry><entry><title>Yubikey polkit Configuration</title><link href="https://www.ogura.io/posts/2020/04/yubikey-with-polkit/"/><id>https://www.ogura.io/posts/2020/04/yubikey-with-polkit/</id><published>20200414T10:13:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Due to the need to store GPG keys at work, and not wanting to store keys in cloud services or Git services, I purchased a Yubikey5 Nano version in February last year.</p>
<p>Overall I&rsquo;m very satisfied with it. Although I did encounter some issues during use, I basically solved them with Google.</p>
<p>At the time I also planned to write a related troubleshooting tutorial, but unfortunately I kept putting it off. This article basically covers most issues you&rsquo;ll encounter: <a href="https://mechanus.io/ke-neng-shi-zui-hao-de-yubikey-gpg-ssh-zhi-neng-qia-jiao-cheng/">https://mechanus.io/ke-neng-shi-zui-hao-de-yubikey-gpg-ssh-zhi-neng-qia-jiao-cheng/</a></p>
<p>This article mainly discusses the pitfalls of getting Yubikey and polkit tools to work together:</p>
<h1 id="reproduction">Reproduction</h1>
<p>Environment: fedora 30 / 31 (encountered on two virtual machines)</p>
<p>After inserting the Yubikey, running <code>gpg2 --card-status</code> as a non-root user shows no device found, with the prompt:</p>
<pre tabindex="0"><code class="language-log" data-lang="log">gpg: selecting card failed: No such device
gpg: OpenPGP card not available: No such device
</code></pre><p>Running <code>journalctl -xe</code> shows logs similar to the following:</p>
<pre tabindex="0"><code class="language-log" data-lang="log">pcscd[13141]: 00000000 ifdhandler.c:150:CreateChannelByNameOrChannel() failed
pcscd[13141]: 00000071 readerfactory.c:1105:RFInitializeReader() Open Port 0x200000 Failed (usb:1050/0407:libudev:0:/dev/bus/usb/003/006)
pcscd[13141]: 00000004 readerfactory.c:376:RFAddReader() Yubico YubiKey OTP+FIDO+CCID init failed.
pcscd[13141]: 00004720 ifdhandler.c:150:CreateChannelByNameOrChannel() failed
pcscd[13141]: 00000023 readerfactory.c:1105:RFInitializeReader() Open Port 0x200000 Failed (usb:1050/0407:libudev:1:/dev/bus/usb/003/006)
pcscd[13141]: 00000004 readerfactory.c:376:RFAddReader() Yubico YubiKey OTP+FIDO+CCID init failed.
pcscd[13141]: 00143849 auth.c:135:IsClientAuthorized() Process 13120 (user: 1000) is NOT authorized for action: access_pcsc
pcscd[13141]: 00000140 winscard_svc.c:335:ContextThread() Rejected unauthorized PC/SC client
</code></pre><h1 id="analysis">Analysis</h1>
<p>Carefully analyzing this log, you can see this is an exception call stack output, and the crux of the matter is that <code>user: 1000</code> doesn&rsquo;t have <code>access_pcsc</code> permission.</p>
<p>By reviewing the <a href="https://wiki.archlinux.org/index.php/Polkit">polkit documentation</a>, we know that polkit is a permission management toolset designed to enable communication between low-priority processes (pcscd) and high-priority processes (drivers).</p>
<h1 id="solution">Solution</h1>
<p>To grant permissions, you need to add a configuration file <code>051-org.debian.pcsc-lite.rules</code> (filename can be customized, just needs to end with <code>.rules</code>) to the <code>/etc/polkit-1/rules.d/</code> directory.
050 is the default rule, after 050 are custom rules, before 050 are supplements to the default rules</p>
<p>Configuration file as follows:</p>
<pre tabindex="0"><code>polkit.addRule(function(action, subject) {
    // Here I granted access_pcsc permission to all users in the wheel group
    if (action.id == &#34;org.debian.pcsc-lite.access_pcsc&#34; &amp;&amp;
        subject.isInGroup(&#34;wheel&#34;)) {
        return polkit.Result.YES;
    }
});

polkit.addRule(function(action, subject) {
    // ditto ...
    if (action.id == &#34;org.debian.pcsc-lite.access_card&#34; &amp;&amp;
        subject.isInGroup(&#34;wheel&#34;)) {
        return polkit.Result.YES;
    }
});
</code></pre><h1 id="debugging-tips">Debugging Tips</h1>
<p>The polkit configuration file syntax is very similar to JS. You can use polkit.log for debug output. Other methods and explanations are clearly described in the polkit documentation<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>During debugging, pay close attention to logs. If you see keywords like <code>Error compiling script</code>, it means there&rsquo;s a compilation error and this change won&rsquo;t take effect.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://wiki.archlinux.org/index.php/Polkit">Polkit Documentation</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>Due to the need to store GPG keys at work, and not wanting to store keys in cloud services or Git services, I purchased a Yubikey5 Nano version in February last year.</p>
<p>Overall I&rsquo;m very satisfied with it. Although I did encounter some issues during use, I basically solved them with Google.</p>
<p>At the time I also planned to write a related troubleshooting tutorial, but unfortunately I kept putting it off. This article basically covers most issues you&rsquo;ll encounter: <a href="https://mechanus.io/ke-neng-shi-zui-hao-de-yubikey-gpg-ssh-zhi-neng-qia-jiao-cheng/">https://mechanus.io/ke-neng-shi-zui-hao-de-yubikey-gpg-ssh-zhi-neng-qia-jiao-cheng/</a></p>]]></summary></entry><entry><title>COUNTU Swimming Counter (Recommendation)</title><link href="https://www.ogura.io/posts/2019/08/smzdm-countu/"/><id>https://www.ogura.io/posts/2019/08/smzdm-countu/</id><published>20190812T22:32:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <h1 id="preface">Preface</h1>
<p>After learning <a href="/posts/2019/07/learn-to-crawl/">freestyle</a>, my Xiaomi band gloriously malfunctioned after only 8 uses.</p>
<p><img src="/images/2019/08/mi-band-crash.jpg" alt="mi-band-crash"></p>
<h1 id="options">Options</h1>
<p>So I started looking for reliable alternatives on JD and Taobao:</p>
<ol>
<li>Moov Now Swimming Smart Band: ¥399.00</li>
<li>Swimovate PoolMate2 Standard Edition: ¥790.00</li>
<li>Swimovate PoolMate Live Agile Edition + Swimovate PoolMate Live Agile Edition USB Data Clip: ¥945.00 + ¥335.00</li>
<li>GARMIN Forerunner735: ¥2280.00</li>
<li>COUNTU 2018: ¥288.17</li>
<li>SportCount: ¥238.00</li>
</ol>
<p>The final choice, as the title suggests, was COUNTU 2018</p>
<p><img src="/images/2019/08/countu-2018.png" alt="countu-2018"></p>
<h1 id="reasoning">Reasoning</h1>
<p>Moov Now was my first choice, but after reading several reviews, I found its measurement accuracy was still mediocre. Having learned from the Mi band experience, I passed on it.</p>
<p>The two Swimovate models are listed separately because of price and features. In theory, these two watches achieve professional-level measurement accuracy. The Live version has an additional data export feature, but requires buying a USB data clip. If only considering measurement accuracy, buying the Swimovate PoolMate2 Standard Edition is sufficient. Pending.</p>
<p>GARMIN Forerunner735 is a professional triathlon watch, but the price isn&rsquo;t pretty, so I passed.</p>
<p>Two remaining options are non-band form factors: COUNTU and SportCount. These are actually the only two finger-ring type counters for swimming that can be purchased online. The SportCount&rsquo;s operation and display are reportedly quite dumb, while COUNTU as a domestic brand has made many user-friendly optimizations.</p>
<p>In the end, I chose the more hardcore and cheaper option between Swimovate and COUNTU: COUNTU.</p>
]]></content><summary type="html"><![CDATA[ <h1 id="preface">Preface</h1>
<p>After learning <a href="/posts/2019/07/learn-to-crawl/">freestyle</a>, my Xiaomi band gloriously malfunctioned after only 8 uses.</p>
<p><img src="/images/2019/08/mi-band-crash.jpg" alt="mi-band-crash"></p>
<h1 id="options">Options</h1>
<p>So I started looking for reliable alternatives on JD and Taobao:</p>
<ol>
<li>Moov Now Swimming Smart Band: ¥399.00</li>
<li>Swimovate PoolMate2 Standard Edition: ¥790.00</li>
<li>Swimovate PoolMate Live Agile Edition + Swimovate PoolMate Live Agile Edition USB Data Clip: ¥945.00 + ¥335.00</li>
<li>GARMIN Forerunner735: ¥2280.00</li>
<li>COUNTU 2018: ¥288.17</li>
<li>SportCount: ¥238.00</li>
</ol>
<p>The final choice, as the title suggests, was COUNTU 2018</p>]]></summary></entry><entry><title>Learning Freestyle Swimming - A Review</title><link href="https://www.ogura.io/posts/2019/07/learn-to-crawl/"/><id>https://www.ogura.io/posts/2019/07/learn-to-crawl/</id><published>20190714T15:06:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <h1 id="introduction">Introduction</h1>
<p>Summer is here, and I plan to maintain my daily exercise routine through swimming outside of work.</p>
<p>Of course, this choice isn&rsquo;t completely out of the blue. When I was still working in Beijing, my leader at the time applied for swimming cards and took us to the swimming pool next to the company once a week. Every time I think about it, I can still remember how we&rsquo;d eat some jianbing guozi (Chinese crepe) behind the company after swimming and then go back to work.</p>
<h1 id="background">Background</h1>
<p>Here it&rsquo;s necessary to mention my swimming experience:</p>
<ul>
<li>After the college entrance exam, relatives bought me some local swimming tickets. I probably went about a dozen times that summer</li>
<li>Sophomore year swimming class, got a yearly card for the school&rsquo;s swimming pool (later mainly used for showering)</li>
<li>After working in Beijing, leader took us to the swimming pool for exercise</li>
</ul>
<p>As you can see, I started swimming quite late, and going to the pool was basically just messing around. I&rsquo;m still learning in the shallow end to this day.</p>
<p>My current company reimburses a certain amount of fitness expenses, so I signed up for a swimming class near my home. Next, I&rsquo;ll talk about my experience in the swimming class and my practice afterwards.</p>
<h1 id="learning">Learning</h1>
<p>I signed up for an adult breaststroke class at the swimming pool, with classes from Monday to Friday evenings from 7:30 to 8:30. My planned learning schedule was after work on Monday, Tuesday, and Thursday.</p>
<p>First week, went to class with a sense of unfamiliarity. Told the coach about my level and swam a short stretch of &ldquo;breaststroke&rdquo; in front of the coach. The coach shook his head: You should learn freestyle instead, your leg kicks look like butterfly stroke.</p>
<p>So I began learning freestyle:</p>
<ol>
<li>Holding the edge, kicking</li>
<li>Holding a kickboard, kicking</li>
<li>Holding kickboard, kicking + single-arm stroke</li>
<li>Holding kickboard, kicking + stroke + high elbow recovery</li>
<li>Holding kickboard, kicking + single-arm stroke + breathing</li>
<li>Holding kickboard, kicking + stroke + breathing</li>
<li>Kickboard + coordination</li>
<li>No board coordination practice
9-12. Coordination practice</li>
</ol>
<p>Besides learning in the swimming class, I also looked for video tutorials on video sites. The one I&rsquo;m currently watching is &ldquo;<a href="https://www.youtube.com/playlist?list=PLgbBfAM7XPKi3EBeNUjlq1N_80b1v3tW8">Mengjue Teaches Swimming &ndash; &lsquo;Freestyle Introduction Version 2&rsquo; (with subtitles)</a>&rdquo; which has been very helpful.</p>
<p>A few key knowledge points that were important for me:</p>
<ol>
<li>Side body kicking</li>
<li>Breathing</li>
<li>Training plan</li>
</ol>
<p>Let me explain the &ldquo;training plan&rdquo; knowledge point - it&rsquo;s very helpful for improving swimming level: before getting in the water each time, plan out the training for this session. For example, my current routine is 200 meters kicking practice + technique drills + 1.5km - 2km coordination practice.</p>
<p>This kind of planned training is much more efficient than simply practicing coordination swimming or sprint swimming each time. It&rsquo;s also less boring, allowing for visible improvement in skill level.</p>
<h1 id="results">Results</h1>
<p>Currently swimming twice a week, about 1500-2000 meters of coordination practice each time. Due to breathing still not being smooth enough (gets distracting), swimming more than 100 meters continuously is still difficult. But breathing issues like choking on water and fake breathing have improved, and swimming 50 meters continuously is no longer an obstacle :)</p>
]]></content><summary type="html"><![CDATA[ <h1 id="introduction">Introduction</h1>
<p>Summer is here, and I plan to maintain my daily exercise routine through swimming outside of work.</p>
<p>Of course, this choice isn&rsquo;t completely out of the blue. When I was still working in Beijing, my leader at the time applied for swimming cards and took us to the swimming pool next to the company once a week. Every time I think about it, I can still remember how we&rsquo;d eat some jianbing guozi (Chinese crepe) behind the company after swimming and then go back to work.</p>]]></summary></entry><entry><title>OpenResty Development Overview</title><link href="https://www.ogura.io/posts/2019/01/openresty_quickview/"/><id>https://www.ogura.io/posts/2019/01/openresty_quickview/</id><published>20190124T21:42:02.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <h1 id="introduction">Introduction</h1>
<p>Openresty is a high-performance web platform based on Nginx and Lua. Its main components are:</p>
<ol>
<li>Nginx<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li>Lua virtual machine</li>
<li>lua-nginx-module: A project that embeds the Lua virtual machine into Nginx and provides Nginx APIs for Lua to call. At the Lua level, you can achieve non-blocking effects by using these APIs, mainly thanks to cosocket and the nginx event model</li>
<li>stream-lua-nginx-module: Similar functionality to <code>lua-nginx-module</code>, the difference is that <code>lua-nginx-module</code> provides APIs for nginx&rsquo;s http module, while <code>stream-lua-nginx-module</code> provides APIs for nginx&rsquo;s stream module.</li>
<li>lua-resty-core: Uses FFI to provide a series of common APIs at the Lua level</li>
<li>lua-resty-*: Based on the above modules, a series of commonly used service modules are encapsulated. Such as: <code>lua-resty-redis</code>/<code>lua-resty-mysql</code>/<code>lua-resty-http</code></li>
</ol>
<h1 id="development">Development</h1>
<p>In Openresty-based development, you need to understand the working principles of both Nginx and Lua, then further understand the principles of lua-nginx-module. This way you can have a project with a good balance between performance and maintainability.</p>
<h2 id="1-nginx">1. Nginx</h2>
<p>In Openresty-based development, you first need to recognize Nginx&rsquo;s role in the entire project. Typical roles include:</p>
<ol>
<li>Reverse proxy server</li>
<li>Web Server itself</li>
<li>Forward proxy server</li>
</ol>
<p>Considering that projects developed with Openresty generally lean toward the server side, we&rsquo;ll only discuss the first two cases here</p>
<h3 id="11-reverse-proxy">1.1 Reverse Proxy</h3>
<p>This is Nginx&rsquo;s typical usage, i.e., acting as a gateway or load balancer frontend, directly receiving external requests, doing simple processing, then using the <code>upstream</code> feature to proxy traffic to the backend.</p>
<pre tabindex="0"><code>+---------+---------+    +------------------------+
|         |         |    |                        |
|         |         |    |    Web server          |
|         |         |    |                        |
|         |         |    +------------------------+
|         |         |
|         |         |    +------------------------+
|         |         |    |                        |
|   Lua   |Upstream |    |    Web server          |
|         |         |    |                        |
|         |         |    +------------------------+
|         |         |
|         |         |    +------------------------+
|         |         |    |                        |
|         |         |    |    Web server          |
|         |         |    |                        |
+---------+---------+    +------------------------+
</code></pre><p>Here&rsquo;s a typical architectural diagram. The Openresty part is abstracted into the gateway logic layer handled by <code>Lua</code>, and the <code>Upstream</code> functionality layer handled by <code>Nginx</code>.</p>
<p>Here we&rsquo;ll focus on Nginx&rsquo;s role in this architecture:</p>
<ol>
<li>Receiving requests
<ol>
<li>If nginx works in master-worker multi-process mode, multiple worker processes listening on the same port will face a &ldquo;thundering herd&rdquo; problem: multiple processes are awakened simultaneously by a connection event, but only one process actually handles the connection. Nginx uses the <code>ngx_accept_mutex</code> synchronization lock mechanism to solve this problem.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li>Load balancing between multiple processes: Uses load thresholds to represent process load conditions, thus dynamically balancing load between multiple processes</li>
</ol>
</li>
<li>Simple routing functionality: This mainly refers to multiple levels of keywords set in nginx configuration files, such as <code>http</code>/<code>server</code>/<code>location</code>, providing simple routing functionality (of course complex routing can be set up, but I tend to hand off complex routing to Lua code)</li>
<li>Providing hook points for lua-nginx-module to load lua virtual machine and code: These are Nginx&rsquo;s module mount points. These mount points subdivide the request lifecycle into multiple phases, each phase with clear purposes, facilitating modular program management.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> The above phase subdivision is provided to nginx module developers. Before developing specific functionality for a nginx module, there are roughly several things to do: initialize configuration directive data structures, module context, handle configuration directive conflicts, register the module, and only then can you start developing the module&rsquo;s specific functionality. After openresty provides multiple hook points and exposes appropriate APIs at corresponding hook points, development costs are significantly reduced</li>
<li>Upstream functionality</li>
</ol>
<h3 id="12-web-server">1.2 Web Server</h3>
<p>In this architecture, my general approach is:</p>
<p>Hand off the simple routing part to Nginx configuration files, then run Lua programs that respond to user requests through mount points provided by <code>lua-nginx-module</code></p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">server {
    listen 80;
    server_name example.com;

    location /backend/ {
        content_by_lua_block {
            local handler = require &#34;handler&#34;
            handler.go()
        }
    }
}
</code></pre><p>If you need to use an existing web framework here, you might see frameworks like <code>Vanilla</code>.</p>
<p>Personally, I prefer combining multiple lightweight modules:</p>
<ol>
<li>Use simple lua tables for routing</li>
<li>Use <a href="https://github.com/openresty/lemplate.git">openresty/lemplate</a> for rendering pages or results</li>
<li>Use lua-resty-* series modules for accessing databases or making http requests</li>
</ol>
<p>The advantages of web servers developed this way:</p>
<ol>
<li>Easy packaging, no need for <code>luarocks</code> tool (actually openresty officially provides the <code>opm</code> tool to solve <code>lua-resty-*</code> dependencies)</li>
<li>Easy deployment</li>
<li>Simple modularization</li>
</ol>
<h2 id="2-lua">2. Lua</h2>
<p>There are simple analyses of Lua source code here, so I won&rsquo;t elaborate further.</p>
<blockquote>
</blockquote>
<p>Lua source code series articles:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2017/12/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
<ol>
<li>Use <code>table.new</code> to allocate lua tables of known size, because <code>table.new</code> calls the <code>lua_createtable()</code> function which can be optimized in <code>LuaJIT</code>. Another advantage is pre-allocating table size, preventing resource consumption during table growth.</li>
<li>Use local variables to cache results returned by APIs provided by <code>lua-nginx-module</code> or <code>lua-resty-core</code>. This reduces consumption by reducing unnecessary stack operations.</li>
<li>Note the application of lua&rsquo;s <code>__gc</code> metamethod in some modules to prevent strange bugs caused by triggering <code>__gc</code><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></li>
<li>Note that in openresty, <code>lua</code> level code should avoid blocking IO: such as using lua&rsquo;s native <code>os</code> library to read/write local files or system calls, which will affect the entire nginx worker&rsquo;s operation</li>
</ol>
<h2 id="3-lua-nginx-module">3. lua-nginx-module</h2>
<p>This section mainly discusses some important configurations in lua-nginx-module.</p>
<ul>
<li><code>lua_code_cache: on|off</code>: Turning off code caching means each request runs a separate Lua VM instance, i.e., changes to lua code take effect immediately. This feature is recommended only during debugging. Some module functionality may depend on code caching</li>
<li><code>lua_package_path</code> / <code>lua_package_cpath</code> These two directives directly determine whether nginx can find the lua modules you want to reference, so they are very important</li>
</ul>
<p>Other directive documentation can also be clearly found in the official <a href="https://github.com/openresty/lua-nginx-module">openresty/lua-nginx-module</a> documentation.</p>
<h2 id="4-tools">4. Tools</h2>
<ol>
<li><a href="https://github.com/openresty/openresty-devel-utils">openresty/openresty-devel-utils</a>: This repository has many convenient small tools to use during openresty-related development, such as:
<ol>
<li><code>lua-releng</code>: A wrapper around the <code>luac</code> command line, bringing multiple global variables provided by <code>openresty</code> into the correct scope</li>
<li><code>reindex</code>: Mainly a syntax format check for test files based on the <code>Test::Nginx</code> module</li>
</ol>
</li>
<li><a href="https://github.com/spacewander/luacov-console">spacewander/luacov-console</a>: Combined with the <code>luacov</code> tool, generates colored code coverage in the terminal, and with slight processing can be used as a code coverage data source in the CI tool chain</li>
<li><code>Test::Nginx</code>: The data-driven testing framework officially used by openresty</li>
</ol>
<h1 id="see-also">See Also</h1>
<ol>
<li><a href="https://moonbingbing.gitbooks.io/openresty-best-practices/content/">OpenResty Best Practices</a></li>
<li><a href="https://github.com/openresty/lua-nginx-module">openresty/lua-nginx-module</a> / <a href="https://github.com/openresty/stream-lua-nginx-module">openresty/stream-lua-nginx-module</a> / <a href="https://github.com/openresty/lua-resty-core">openresty/lua-resty-core</a> official documentation</li>
</ol>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Historical versions used: 1.11.7 -&gt; 1.13.6. The important reason for upgrading: stream-lua-nginx-module added udp server support&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>mutex synchronization lock: (TODO)&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Mount points in http-related modules:
NGX_HTTP_POST_READ_PHASE: /* Read request content phase <em>/
NGX_HTTP_SERVER_REWRITE_PHASE:/</em> Server request address rewrite phase <em>/
NGX_HTTP_FIND_CONFIG_PHASE: /</em> Configuration lookup phase: <em>/
NGX_HTTP_REWRITE_PHASE: /</em> Location request address rewrite phase <em>/
NGX_HTTP_POST_REWRITE_PHASE: /</em> Request address rewrite submission phase <em>/
NGX_HTTP_PREACCESS_PHASE: /</em> Access permission check preparation phase <em>/
NGX_HTTP_ACCESS_PHASE: /</em> Access permission check phase <em>/
NGX_HTTP_POST_ACCESS_PHASE: /</em> Access permission check submission phase <em>/
NGX_HTTP_TRY_FILES_PHASE: /</em> Configuration item try_files processing phase <em>/
NGX_HTTP_CONTENT_PHASE: /</em> Content generation phase <em>/
NGX_HTTP_LOG_PHASE: /</em> Log module processing phase */&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>__gc metamethod related issue example <a href="https://github.com/openresty/lua-resty-lock/issues/20">https://github.com/openresty/lua-resty-lock/issues/20</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <h1 id="introduction">Introduction</h1>
<p>Openresty is a high-performance web platform based on Nginx and Lua. Its main components are:</p>
<ol>
<li>Nginx<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li>Lua virtual machine</li>
<li>lua-nginx-module: A project that embeds the Lua virtual machine into Nginx and provides Nginx APIs for Lua to call. At the Lua level, you can achieve non-blocking effects by using these APIs, mainly thanks to cosocket and the nginx event model</li>
<li>stream-lua-nginx-module: Similar functionality to <code>lua-nginx-module</code>, the difference is that <code>lua-nginx-module</code> provides APIs for nginx&rsquo;s http module, while <code>stream-lua-nginx-module</code> provides APIs for nginx&rsquo;s stream module.</li>
<li>lua-resty-core: Uses FFI to provide a series of common APIs at the Lua level</li>
<li>lua-resty-*: Based on the above modules, a series of commonly used service modules are encapsulated. Such as: <code>lua-resty-redis</code>/<code>lua-resty-mysql</code>/<code>lua-resty-http</code></li>
</ol>
<h1 id="development">Development</h1>
<p>In Openresty-based development, you need to understand the working principles of both Nginx and Lua, then further understand the principles of lua-nginx-module. This way you can have a project with a good balance between performance and maintainability.</p>]]></summary></entry><entry><title>Hugo Migration Notes</title><link href="https://www.ogura.io/posts/2019/01/move_to_hugo/"/><id>https://www.ogura.io/posts/2019/01/move_to_hugo/</id><published>20190118T15:40:05.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <h1 id="background">Background</h1>
<p>A few days ago, I wanted to implement a feature that adds a tag in the blog&rsquo;s <code>front-matter</code> to distinguish lifestyle articles from technical articles, with the homepage displaying different link colors.</p>
<h1 id="problem">Problem</h1>
<p>While searching for solutions on the Hexo website and Google, I found several issues:</p>
<ol>
<li>Hexo&rsquo;s default template <a href="https://github.com/paularmstrong/swig">paularmstrong/swig</a> is no longer maintained<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li>The documentation on the Hexo website was still quite sparse</li>
</ol>
<p>As I mentioned in <a href="/posts/2018/08/hexo-serve-static-hidden-file/">hexo + gitlab serving hidden static files</a>:</p>
<blockquote>
<p>Having previous experience with hexo, plus the exceptionally thriving node ecosystem for personal blogs (lots of third-party node-related plugins available), I decided to migrate over.</p>
</blockquote>
<p>This is actually one of the causes of problem one. Problem one has been discussed in hexo&rsquo;s GitHub issues: <a href="https://github.com/hexojs/hexo/issues/1593">Why not totally replace Swig with Nunjucks? #1593</a>, where <a href="https://github.com/mozilla/nunjucks">mozilla/nunjucks</a><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> as an upgrade to the <code>Swig</code> template, can serve as a replacement after <code>Swig</code> is no longer maintained. But the hexo project wasn&rsquo;t so quick to replace the default template.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>Problem 2 has been an issue for a long time, being quite unfriendly to theme developers. Here you can see one theme developer&rsquo;s feelings <a href="https://blessing.studio/get-hexo-posts-by-category-or-tag/">https://blessing.studio/get-hexo-posts-by-category-or-tag/</a>:</p>
<blockquote>
<p>Today while porting my blog theme to Hexo, I wanted to get all posts under a certain category or tag (more accurately, I wanted to get the total number of posts). When searching with Chinese keywords, I didn&rsquo;t get any useful information (perhaps my search technique was wrong). After switching to the English keyword &ldquo;hexo category all posts&rdquo;, I found the information I needed, so I decided to write a post to document this, hoping to help others later.~~~~</p>
</blockquote>
<blockquote>
<p>I have to complain here - Hexo&rsquo;s documentation is really terrible, just terrible. Writing a theme, sometimes wanting to implement a feature requires frantically reading Hexo source code, I&rsquo;m speechless.</p>
</blockquote>
<h1 id="solution">Solution</h1>
<p>My solution options were:</p>
<ul>
<li>Option 1: Keep the <code>swig</code> template unchanged, search for similar implementation methods.</li>
<li>Option 2: Use a <code>nunjucks</code> plugin to enable hexo to support that template, then implement the feature.</li>
<li>Option 3: Switch to other template engines supported by Hexo, such as <a href="https://github.com/hexojs/hexo-renderer-ejs">hexojs/hexo-renderer-ejs</a>, <a href="hexojs/hexo-renderer-haml">https://github.com/hexojs/hexo-renderer-haml</a>, <a href="https://github.com/hexojs/hexo-renderer-jade">hexojs/hexo-renderer-jade</a>, re-implement the theme, and implement the feature along the way</li>
<li>Option 4: Switch to another static blog generation platform</li>
</ul>
<p>My solution process happened to follow the order I listed</p>
<h2 id="option-1">Option 1</h2>
<p>Since hexo&rsquo;s custom front-matter parts require hexo import scripts, and I didn&rsquo;t want to spend too much time on the hexo framework itself. <strong>So I gave up.</strong>
During the process, I used another method: using a rarely-used <code>front-matter</code> field to mark link colors (I used the <code>layout</code> variable). The actual effect achieved a similar result, but the code looks confusing.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<h2 id="option-2">Option 2</h2>
<p><code>nunjucks</code> only has a few hexo-related plugins, <a href="https://www.npmjs.com/package/hexo-renderer-nunjucks">hexo-renderer-nunjucks</a> and <a href="https://www.npmjs.com/package/hexo-nunjucks">hexo-nunjucks</a>, and opening them shows these two projects&rsquo; last update time was locked to three years ago. <strong>So I gave up.</strong></p>
<h2 id="option-3">Option 3</h2>
<p>These template engines are directly supported by Hexo official, but when actually using them, I still felt the outdated <code>swig</code> was slightly better (mainly because it matched my impression of templates). <strong>So I gave up.</strong></p>
<h2 id="option-4">Option 4</h2>
<p>First, let me list the alternatives:<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<ul>
<li>JavaScript: Next.js &amp; Gatsby (for React), Nuxt &amp; VuePress (for Vue), Hexo, Eleventy, GitBook, Metalsmith, Harp, Spike.</li>
<li>Python: Pelican, MkDocs, Cactus.</li>
<li>Ruby: Jekyll, Middleman, Nanoc, Octopress.</li>
<li>Go: Hugo, InkPaper.</li>
<li>.NET: Wyam, pretzel.</li>
</ul>
<p>I finally chose Hugo from these, mainly because I was learning <code>Go</code> recently. And Hugo&rsquo;s <a href="https://golang.org/pkg/text/template/">&ldquo;text/template&rdquo;</a> is also a standard extension module for <code>Golang</code>, so it shouldn&rsquo;t have the jumping between multiple templates like Hexo^(problem 1)^. Hugo&rsquo;s official documentation is also visibly more extensive^(problem 2)^.</p>
<h1 id="implementation">Implementation</h1>
<p>Having determined the solution, let me organize what needs to be done after choosing this solution:</p>
<ol>
<li>Article migration</li>
<li>Template migration<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></li>
<li>Feature implementation</li>
<li>Deployment plan</li>
</ol>
<h2 id="1-article-migration">1. Article Migration</h2>
<p>Since both use articles written in <code>markdown</code>, migration is basically just a <code>cp</code> command. I won&rsquo;t elaborate here.</p>
<h2 id="2-template-migration">2. Template Migration</h2>
<p>For the template part, it was basically pixel-level COPY: implementing <code>swig</code> template functionality line by line using <code>text/template</code>. Of course, the syntax style follows Hugo&rsquo;s official documentation.
I still encountered a few small challenges during the process, and I&rsquo;ll post the solutions here:</p>
<ol>
<li>When implementing the <code>/tags/</code> page, I needed to first group articles by tag, then iterate through articles in each tag group. In <code>text/template</code>, variable scoping is very strange, code as follows:</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go-html-template" data-lang="go-html-template"><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">$v</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;init&#34;</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">if</span> <span style="color:#66d9ef">true</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$v</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;changed&#34;</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">end</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>v: <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$v</span> <span style="color:#75715e">}}</span> <span style="color:#75715e">{{/* =&gt; init */}}</span>
</span></span></code></pre></div><p>Intuitively judging this code, <code>$v</code> should output <code>&quot;changed&quot;</code>. However, the actual result is surprising. My understanding of this situation: in <code>text/template</code>&rsquo;s implementation, each code block has an independent scope, and this scope doesn&rsquo;t inherit when nesting occurs.
This is probably the cleanest and simplest code implementation. In such cases, the official recommendation is to use <code>.Scratch</code> to create a page-level scope readable-writable variable, but this is a bit heavy for theme templates.</p>
<p>With Google&rsquo;s help, I found this gist: <a href="https://gist.github.com/Xeoncross/203d8b1459463a153a3c734c98b342a9">https://gist.github.com/Xeoncross/203d8b1459463a153a3c734c98b342a9</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go-html-template" data-lang="go-html-template"><span style="display:flex;"><span>         &lt;<span style="color:#f92672">ul</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;tags&#34;</span>&gt;
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">$name</span><span style="color:#f92672">,</span> <span style="color:#a6e22e">$taxonomy</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">.Site.Taxonomies.tags</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>              &lt;<span style="color:#f92672">li</span>&gt;&lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-transform: capitalize&#34;</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;#</span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">$name</span> <span style="color:#f92672">|</span> <span style="color:#a6e22e">urlize</span><span style="color:#75715e">}}</span><span style="color:#e6db74">&#34;</span>&gt;<span style="color:#75715e">{{</span> <span style="color:#a6e22e">$name</span> <span style="color:#75715e">}}</span>&lt;/<span style="color:#f92672">a</span>&gt;
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">&lt;!-- &lt;span&gt;(</span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">len</span> <span style="color:#a6e22e">$taxonomy</span> <span style="color:#75715e">}}</span><span style="color:#75715e">)&lt;/span&gt; --&gt;</span>
</span></span><span style="display:flex;"><span>              &lt;/<span style="color:#f92672">li</span>&gt;
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#66d9ef">end</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>          &lt;/<span style="color:#f92672">ul</span>&gt;
</span></span></code></pre></div><ol start="2">
<li>In articles with a table of contents, I found the automatically rendered TOC would have empty <code>·</code> appearing.</li>
</ol>
<p>In some articles, my subheadings are marked with <code>&lt;h3&gt;</code>, but the auto-generated TOC template didn&rsquo;t automatically remove unused <code>&lt;h1&gt;</code> and <code>&lt;h2&gt;</code></p>
<pre tabindex="0"><code>·    ·    · Heading level 3 1
          · Heading level 3 2
</code></pre><p>This is also reflected in Hugo&rsquo;s GitHub Issues: <a href="https://github.com/gohugoio/hugo/issues/1778">Heading levels in Markdown table of contents #1778</a>, and this involves another issue - the markdown rendering template <a href="https://github.com/russross/blackfriday">russross/blackfriday</a> used by Hugo is still v1 version in Hugo, and v1 version outputs an HTML fragment after parsing markdown. Because of this, there&rsquo;s this ugly inefficient code when generating <code>.TableOfContents</code>: <a href="https://github.com/gohugoio/hugo/blob/master/helpers/content.go#L416">https://github.com/gohugoio/hugo/blob/master/helpers/content.go#L416</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">ExtractTOC</span>(<span style="color:#a6e22e">content</span> []<span style="color:#66d9ef">byte</span>) (<span style="color:#a6e22e">newcontent</span> []<span style="color:#66d9ef">byte</span>, <span style="color:#a6e22e">toc</span> []<span style="color:#66d9ef">byte</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">bytes</span>.<span style="color:#a6e22e">Contains</span>(<span style="color:#a6e22e">content</span>, []byte(<span style="color:#e6db74">&#34;&lt;nav&gt;&#34;</span>)) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">content</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">origContent</span> <span style="color:#f92672">:=</span> make([]<span style="color:#66d9ef">byte</span>, len(<span style="color:#a6e22e">content</span>))
</span></span><span style="display:flex;"><span>    copy(<span style="color:#a6e22e">origContent</span>, <span style="color:#a6e22e">content</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">first</span> <span style="color:#f92672">:=</span> []byte(<span style="color:#e6db74">`&lt;nav&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;ul&gt;`</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">last</span> <span style="color:#f92672">:=</span> []byte(<span style="color:#e6db74">`&lt;/ul&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;/nav&gt;`</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">replacement</span> <span style="color:#f92672">:=</span> []byte(<span style="color:#e6db74">`&lt;nav id=&#34;TableOfContents&#34;&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;ul&gt;`</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">startOfTOC</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bytes</span>.<span style="color:#a6e22e">Index</span>(<span style="color:#a6e22e">content</span>, <span style="color:#a6e22e">first</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">peekEnd</span> <span style="color:#f92672">:=</span> len(<span style="color:#a6e22e">content</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">peekEnd</span> &gt; <span style="color:#ae81ff">70</span><span style="color:#f92672">+</span><span style="color:#a6e22e">startOfTOC</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">peekEnd</span> = <span style="color:#ae81ff">70</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">startOfTOC</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">startOfTOC</span> &lt; <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">stripEmptyNav</span>(<span style="color:#a6e22e">content</span>), <span style="color:#a6e22e">toc</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Need to peek ahead to see if this nav element is actually the right one.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">correctNav</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bytes</span>.<span style="color:#a6e22e">Index</span>(<span style="color:#a6e22e">content</span>[<span style="color:#a6e22e">startOfTOC</span>:<span style="color:#a6e22e">peekEnd</span>], []byte(<span style="color:#e6db74">`&lt;li&gt;&lt;a href=&#34;#`</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">correctNav</span> &lt; <span style="color:#ae81ff">0</span> { <span style="color:#75715e">// no match found</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">content</span>, <span style="color:#a6e22e">toc</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">lengthOfTOC</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bytes</span>.<span style="color:#a6e22e">Index</span>(<span style="color:#a6e22e">content</span>[<span style="color:#a6e22e">startOfTOC</span>:], <span style="color:#a6e22e">last</span>) <span style="color:#f92672">+</span> len(<span style="color:#a6e22e">last</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">endOfTOC</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">startOfTOC</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">lengthOfTOC</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">newcontent</span> = append(<span style="color:#a6e22e">content</span>[:<span style="color:#a6e22e">startOfTOC</span>], <span style="color:#a6e22e">content</span>[<span style="color:#a6e22e">endOfTOC</span>:]<span style="color:#f92672">...</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">toc</span> = append(<span style="color:#a6e22e">replacement</span>, <span style="color:#a6e22e">origContent</span>[<span style="color:#a6e22e">startOfTOC</span><span style="color:#f92672">+</span>len(<span style="color:#a6e22e">first</span>):<span style="color:#a6e22e">endOfTOC</span>]<span style="color:#f92672">...</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Here, string processing methods are used to parse content in <code>.Content</code>, then piece together the content to be generated and add it to the original content. This problem is solved in <code>blackfriday.v2</code>, which outputs an <code>AST</code> for other programs to process, which also ensures compatibility with subsequent versions. But in Hugo, the author has repeatedly postponed this feature&rsquo;s milestone <a href="https://github.com/gohugoio/hugo/issues/3949">Upgrade to Blackfriday v2 #3949</a><sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>.</p>
<p>But in the Issue discussion, various experts proposed their own solutions - you can click into <a href="https://github.com/gohugoio/hugo/issues/1778">Heading levels in Markdown table of contents #1778</a> to see details. I adopted the template solution from there:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go-html-template" data-lang="go-html-template"><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$toc</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">.TableOfContents</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$toc</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">(</span><span style="color:#a6e22e">replace</span> <span style="color:#a6e22e">$toc</span> <span style="color:#e6db74">&#34;&lt;ul&gt;\n&lt;li&gt;\n&lt;ul&gt;&#34;</span> <span style="color:#e6db74">&#34;&lt;ul&gt;&#34;</span><span style="color:#f92672">)</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$toc</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">(</span><span style="color:#a6e22e">replace</span> <span style="color:#a6e22e">$toc</span> <span style="color:#e6db74">&#34;&lt;ul&gt;\n&lt;li&gt;\n&lt;ul&gt;&#34;</span> <span style="color:#e6db74">&#34;&lt;ul&gt;&#34;</span><span style="color:#f92672">)</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$toc</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">(</span><span style="color:#a6e22e">replace</span> <span style="color:#a6e22e">$toc</span> <span style="color:#e6db74">&#34;&lt;ul&gt;\n&lt;li&gt;\n&lt;ul&gt;&#34;</span> <span style="color:#e6db74">&#34;&lt;ul&gt;&#34;</span><span style="color:#f92672">)</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$toc</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">(</span><span style="color:#a6e22e">replace</span> <span style="color:#a6e22e">$toc</span> <span style="color:#e6db74">&#34;&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;&#34;</span> <span style="color:#e6db74">&#34;&lt;/ul&gt;&#34;</span><span style="color:#f92672">)</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$toc</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">(</span><span style="color:#a6e22e">replace</span> <span style="color:#a6e22e">$toc</span> <span style="color:#e6db74">&#34;&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;&#34;</span> <span style="color:#e6db74">&#34;&lt;/ul&gt;&#34;</span><span style="color:#f92672">)</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#a6e22e">$toc</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">(</span><span style="color:#a6e22e">replace</span> <span style="color:#a6e22e">$toc</span> <span style="color:#e6db74">&#34;&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;&#34;</span> <span style="color:#e6db74">&#34;&lt;/ul&gt;&#34;</span><span style="color:#f92672">)</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">&lt;!-- count the number of remaining li tags --&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">&lt;!-- and only display ToC if more than 1, otherwise why bother --&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#66d9ef">if</span> <span style="color:#66d9ef">gt</span> <span style="color:#f92672">(</span><span style="color:#66d9ef">len</span> <span style="color:#f92672">(</span><span style="color:#a6e22e">split</span> <span style="color:#a6e22e">$toc</span> <span style="color:#e6db74">&#34;&lt;li&gt;&#34;</span><span style="color:#f92672">))</span> <span style="color:#a6e22e">2</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>              <span style="color:#75715e">{{</span> <span style="color:#a6e22e">safeHTML</span> <span style="color:#a6e22e">$toc</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">{{</span> <span style="color:#66d9ef">end</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>          <span style="color:#75715e">{{</span> <span style="color:#66d9ef">end</span> <span style="color:#75715e">}}</span>
</span></span></code></pre></div><p>This solution can be easily removed from the template after future feature version merges. It&rsquo;s one of the simpler approaches.</p>
<h2 id="3-feature-implementation">3. Feature Implementation</h2>
<p>After template migration was complete, I started implementing the feature I originally wanted. In Hexo, <code>front-matter</code> can include user-defined fields (documentation at: <a href="https://gohugo.io/variables/page/#page-level-params">https://gohugo.io/variables/page/#page-level-params</a>).
I chose to use the <code>linkcolor</code> field here.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span>title: test
</span></span><span style="display:flex;"><span>linkcolor: #7076c7
</span></span><span style="display:flex;"><span>---
</span></span></code></pre></div><p>Then add the variable judgment where the homepage iterates through titles:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go-html-template" data-lang="go-html-template"><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">.Params.linkcolor</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">$color</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">.Params.linkcolor</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">.Permalink</span> <span style="color:#75715e">}}</span><span style="color:#e6db74">&#34;</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;post-list-item&#34;</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;color:</span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">$color</span> <span style="color:#75715e">}}</span><span style="color:#e6db74">;&#34;</span>&gt;
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">else</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>                    &lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">.Permalink</span> <span style="color:#75715e">}}</span><span style="color:#e6db74">&#34;</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;post-list-item&#34;</span>&gt;
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">end</span> <span style="color:#75715e">}}</span>
</span></span></code></pre></div><p>I felt that writing an abstract color <code>#7076c7</code> each time didn&rsquo;t look good, so I added the following content to the <code>data/color.toml</code> directory (documentation at: <a href="https://gohugo.io/templates/data-templates/">https://gohugo.io/templates/data-templates/</a>)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#a6e22e">link</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">blue</span> = <span style="color:#e6db74">&#34;#7076c7&#34;</span>
</span></span></code></pre></div><p>Modified the homepage template</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go-html-template" data-lang="go-html-template"><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">.Params.linkcolor</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">$color</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">index</span> <span style="color:#a6e22e">.Site.Data.color.link</span> <span style="color:#a6e22e">.Params.linkcolor</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">.Permalink</span> <span style="color:#75715e">}}</span><span style="color:#e6db74">&#34;</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;post-list-item&#34;</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;color:</span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">$color</span> <span style="color:#75715e">}}</span><span style="color:#e6db74">;&#34;</span>&gt;
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">else</span> <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span>                    &lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#75715e">{{</span> <span style="color:#a6e22e">.Permalink</span> <span style="color:#75715e">}}</span><span style="color:#e6db74">&#34;</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;post-list-item&#34;</span>&gt;
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> <span style="color:#66d9ef">end</span> <span style="color:#75715e">}}</span>
</span></span></code></pre></div><p>This way, in <code>front-matter</code> you only need to write <code>linkcolor: blue</code> to achieve the same effect. Future color-related extension features can also be conveniently implemented.</p>
<h2 id="4-deployment-plan">4. Deployment Plan</h2>
<p>The official documentation covers this in detail. I use the <code>Netlify</code> platform to publish my blog, documentation at <a href="https://gohugo.io/hosting-and-deployment/hosting-on-netlify/">https://gohugo.io/hosting-and-deployment/hosting-on-netlify/</a>
Other commonly used platforms are also covered in the documentation.</p>
<p>The main focus here is migrating commit history from the old blog. I put the Hexo blog files and directories into a separate directory <code>hexo_archive</code>, and put Hugo platform code in the project root directory. This way, previous commit history and files are preserved, while leaving a relatively clean directory for Hugo.</p>
<h1 id="see-also">See Also</h1>
<ul>
<li><a href="https://gist.github.com/Xeoncross/203d8b1459463a153a3c734c98b342a9">https://gist.github.com/Xeoncross/203d8b1459463a153a3c734c98b342a9</a>: <strong>Various situations you&rsquo;ll encounter with Hugo templates</strong></li>
<li><a href="https://epatr.com/blog/2017/hugo-and-netlify/">https://epatr.com/blog/2017/hugo-and-netlify/</a>: <strong>Using Hugo with Netlify</strong></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>GitHub page <a href="https://github.com/paularmstrong/swig">https://github.com/paularmstrong/swig</a> already shows: &ldquo;This repository has been archived by the owner. It is now read-only.&rdquo;&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Seeing the mozilla prefix, I feel the Nunjucks project should be stably maintained for a while&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>As of 2019-01-18, there is a related PR in the hexo project <a href="https://github.com/hexojs/hexo/pull/2903">Replace default swig engine with nunjucks #2903</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Code details at: <a href="https://github.com/wukra/izhengfan.github.io/commit/2e3d6782b1bf5c43d149fabe961b7fb09c84a2c5">https://github.com/wukra/izhengfan.github.io/commit/2e3d6782b1bf5c43d149fabe961b7fb09c84a2c5</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>From <a href="https://snipcart.com/blog/choose-best-static-site-generator">https://snipcart.com/blog/choose-best-static-site-generator</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>As mentioned in the <a href="[/posts/2018/09/izhengfan-next-mixed-hexo-theme/">NexT &amp; izhengfan Combined Theme</a> article, I migrated this theme from Jekyll to Hexo. <strong>I really like this current theme</strong>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>As of 2019-01-18, this feature was originally planned to be added in the <code>v0.31</code> version in October 2017, but was repeatedly postponed, and is now placed in the <code>v0.55</code> release plan&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <h1 id="background">Background</h1>
<p>A few days ago, I wanted to implement a feature that adds a tag in the blog&rsquo;s <code>front-matter</code> to distinguish lifestyle articles from technical articles, with the homepage displaying different link colors.</p>
<h1 id="problem">Problem</h1>
<p>While searching for solutions on the Hexo website and Google, I found several issues:</p>
<ol>
<li>Hexo&rsquo;s default template <a href="https://github.com/paularmstrong/swig">paularmstrong/swig</a> is no longer maintained<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li>The documentation on the Hexo website was still quite sparse</li>
</ol>
<p>As I mentioned in <a href="/posts/2018/08/hexo-serve-static-hidden-file/">hexo + gitlab serving hidden static files</a>:</p>]]></summary></entry><entry><title>TCP/IP Knowledge Overview</title><link href="https://www.ogura.io/posts/2018/12/tcp-ip-description/"/><id>https://www.ogura.io/posts/2018/12/tcp-ip-description/</id><published>20181217T09:44:08.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <blockquote>
<p>This mainly uses text narration to try to explain this process, used to organize knowledge points and identify gaps. If any descriptions are inaccurate, please forgive me.</p>
</blockquote>
<p>Q: From entering an address in the browser and pressing enter, to receiving the response - describe the details of this process.</p>
<p>A:</p>
<h2 id="premise">Premise</h2>
<p>Complex programs need to be layered.</p>
<h2 id="overview">Overview</h2>
<p>Explaining this process according to the OSI seven-layer protocol, focusing on the performance in OSI layer 2 - Data Link Layer (hereafter MAC layer), layer 3 - Network Layer (hereafter IP layer), layer 4 - Transport Layer, and the Application Layer. After reaching the Application Layer, I&rsquo;ll discuss CDN principles and common architectures. Then briefly cover data center architecture.</p>
<h2 id="osi-seven-layers-vs-tcpip-four-layers">OSI Seven Layers vs TCP/IP Four Layers</h2>
<p>The OSI seven-layer model is a reference model for open systems interconnection, while TCP/IP protocol suite is a set of communication protocols for implementing network interconnection.
The seven layers of OSI are: Physical Layer, Data Link Layer, Network Layer, Transport Layer, Session Layer, Presentation Layer, Application Layer.
Memory mnemonic: <em>A</em>ll <em>P</em>eople <em>S</em>eem <em>T</em>o <em>N</em>eed <em>D</em>ata <em>P</em>rocessing</p>
<p>The four layers of TCP/IP are: Interface Layer, Internet Layer, Transport Layer, Application Layer.
The Interface Layer in TCP/IP corresponds to Physical and Data Link Layers, while Application Layer corresponds to Session, Presentation, and Application Layers.</p>
<h2 id="data-link-layer">Data Link Layer</h2>
<p>Every networked device has a unique hardware address, commonly called a MAC address. MAC addresses are used to find corresponding devices within a broadcast domain. Although MAC addresses are unique, they&rsquo;re not locationable, so IP protocol is used for communication outside the broadcast domain.</p>
<p>Layer 2 devices can cache the correspondence between MAC addresses and port devices, caching devices within the same broadcast domain.
The correspondence between MAC addresses and IP addresses can be cached in local routing devices, called the routing table. When IP is known but MAC address is unknown, an ARP request can be sent to query the MAC address.</p>
<h2 id="internetnetwork-layer">Internet/Network Layer</h2>
<p>What&rsquo;s entered in the browser is usually a domain name, which is resolved to an IP through DNS.</p>
<p>First determine if the corresponding IP is in the same network segment through CIDR. If not, the request goes through the gateway using routing protocols to find the corresponding network and device. When passing through intermediate routing devices (layer 3), first compare if MAC address matches, then if IP address matches, to see if it&rsquo;s a packet for itself or needs forwarding.</p>
<h3 id="routing-protocols">Routing Protocols</h3>
<p>Routing is divided into dynamic and static routing. The routing protocols here refer to dynamic routing protocols.</p>
<h4 id="distance-vector-routing-protocol">Distance Vector Routing Protocol</h4>
<p>Based on Bellman-Ford algorithm, routers transmit part or all of their routing table to adjacent routers</p>
<p>RIP:
1. Uses hop count as metric
2. Maximum hop count is 15
3. RIP v1 periodically synchronizes the entire routing table</p>
<p>BGP: BGP can be seen as an advanced distance vector routing protocol. In BGP systems, networks can be divided into multiple autonomous systems. Within autonomous systems, iBGP is used to synchronize routing information, while eBGP broadcasts routes between autonomous systems.
Autonomous System: All IP networks and routers under the management of one (or more) entities. IANA assigns an ASN (Autonomous System Number) to autonomous systems, enabling BGP protocol to run between ISPs on the Internet.
ASN: A 16-bit number, now with 32-bit notation: <code>&lt;high 16 bits decimal&gt;.&lt;low 16 bits decimal&gt;</code></p>
<p>Autonomous System Classification:
1. Multihomed AS: Autonomous system with more than one connection. This type doesn&rsquo;t allow other autonomous systems to pass through it to access another autonomous system.
2. Stub AS: Autonomous system connected to only one other autonomous system.
3. Transit AS: An autonomous system that provides connectivity between separate networks. This is the essence of ISPs.</p>
<p>BGP Usage Conditions:
1. Need routers that support storing large routing tables
2. Need multiple connections
3. Have sufficient bandwidth to transmit required data (including routing tables)</p>
<h4 id="shortest-path-first-algorithm">Shortest Path First Algorithm</h4>
<p>Based on Dijkstra algorithm, routers transmit link state information to all routers in the same area</p>
<p>OSPF:
1. Uses multicast to send link state updates, uses triggered updates on link state changes, improving bandwidth utilization
2. No maximum hop count limit, uses delay and cost as metrics</p>
<h4 id="difference-and-connection-between-igp-and-ibgp">Difference and Connection Between IGP and IBGP</h4>
<p>IGP includes protocols like OSPF/RIP, used within autonomous systems, mainly for route discovery and calculation.
IBGP is also used within routing systems. The differences are:
1. IBGP delegates route discovery entirely to IGP, focusing on route control itself.
2. IGP has poor capability handling large routing tables, while IBGP can handle them hierarchically.
3. If BGP routing information is given directly to IGP, route attributes are lost, creating routing loop risks. IBGP can handle these route attributes (point 1)</p>
<h3 id="gateway">Gateway</h3>
<p>Type one: After leaving the gateway, the destination MAC address changes to the next hop device&rsquo;s MAC address, while source and destination IP remain unchanged until reaching the specified device. This method is suitable for scenarios without IP address conflicts. For overlapping IP addresses, NAT gateway is needed.</p>
<p>Type two: NAT gateway. After the request leaves the gateway, both source IP and MAC become the gateway&rsquo;s, while destination IP remains unchanged, reaching the next hop. When the response arrives, destination IP and MAC are mapped back.</p>
<h3 id="dns">DNS</h3>
<p>DNS is a distributed data query system for domain name to IP address translation. When a client initiates a DNS query, it first queries the local DNS server (the DNS server configured on the ISP or router). If no record exists, it queries root name servers, which return top-level domain server addresses, which in turn return authoritative name server addresses. The client&rsquo;s query to local server is recursive, while local DNS server&rsquo;s upward queries are iterative.</p>
<h2 id="transport-layer">Transport Layer</h2>
<p>The IP packet header identifies the transport layer protocol type, commonly UDP and TCP.</p>
<h3 id="udp">UDP</h3>
<p>UDP is a connectionless protocol. It inherits most of IP protocol&rsquo;s characteristics: packet-based, stateless, unordered, no congestion control. Simply put, it&rsquo;s a stateless transport protocol. In UDP headers, only source and destination port numbers identify the connection. Can be used in simple environments, intranets, and scenarios where packet loss is acceptable. Application layer can also implement state maintenance, making it a reliable connection.</p>
<h3 id="tcp">TCP</h3>
<p>TCP is a connection-oriented protocol. A connection here means a series of state transitions. After maintaining a complex state machine, connections become ordered, reliable, with congestion control, etc. But TCP&rsquo;s underlying IP protocol is connectionless and unordered, so TCP heavily uses retransmission and congestion control algorithms to implement these features.</p>
<p>Three-way handshake: Requester sends SYN packet to establish connection (SYN_SENT), receiver returns ACK + SYN packet (SYN_RCVD), requester receives ACK packet (ESTABLISHED), then responds to peer&rsquo;s SYN with ACK. When peer receives ACK, state becomes ESTABLISHED. At this point, both sides have completed one send-receive cycle, state is ESTABLISHED, connection established.</p>
<p>Four-way handshake (termination): Requester sends FIN packet to terminate connection (FIN_WAIT_1), receiver returns ACK after receiving FIN (CLOSE_WAIT), initiator enters (FIN_WAIT_2) after receiving ACK(seq=k+1). When receiver finishes upper layer logic, returns FIN + ACK packet (seq=k+1), initiating termination (LAST_ACK). Sender responds with ACK after receiving these packets, enters TIME_WAIT state, waits two MSL periods then closes connection. Receiver also closes after receiving ACK. TIME_WAIT state is to prevent receiver not receiving the last ACK, triggering FIN + ACK retry.</p>
<h2 id="application-layer">Application Layer</h2>
<h3 id="http">HTTP</h3>
<p>Viewing web pages generally uses HTTP protocol for data transmission. HTTP is a protocol built on TCP. In HTTP, both request and response messages are plaintext. Request messages consist of: request line, request headers, request body, where request line can be subdivided into request method and request URL. Response messages consist of: response status, response headers, and response body. Response status is divided into status code and reason.</p>
<p>Common request methods: <code>GET</code>/<code>POST</code>/<code>PUT</code>/<code>DELETE</code>/<code>OPTION</code>
Common response codes: 200 OK/201 Created/301/302/403 Forbidden/404 Not Found/405 Not Allowed/500 Internal Error/502 Bad Gateway/503 Service Unavailable/504 Gateway Timeout</p>
<h3 id="keepalive">Keepalive</h3>
<p>TCP protocol has a keepalive concept, and HTTP also has a keepalive concept which is enabled by default after HTTP/1.1. HTTP&rsquo;s keep-alive allows clients to send multiple requests over the same TCP connection, while TCP&rsquo;s keep-alive is a mechanism to keep TCP connections alive through heartbeats. The two are not directly related.</p>
<h3 id="https">HTTPS</h3>
<p>Since HTTP request and response messages are plaintext, it&rsquo;s not suitable for high-security scenarios. This introduces HTTPS.
HTTPS uses asymmetric encryption to exchange keys, then symmetric encryption after key exchange, based on HTTP protocol. The handshake process is:</p>
<ol>
<li>Client initiates client hello, mainly to negotiate encryption protocol version, compression algorithm, random number c1, and SNI info</li>
<li>Server responds with server hello, informing client of adopted encryption protocol version, compression algorithm, and random number s1 generated on server side. At this point, client-generated c1 is stored for later use</li>
<li>Server also responds with server certificate for client verification</li>
<li>Finally responds with server hello done to tell client hello info is complete</li>
<li>After client verifies certificate is legitimate (hash certificate info and compare with CA signature decrypted with CA public key), generates pre-master-key for symmetric encryption</li>
<li>Transmits random pre-master-key to server, the client key exchange</li>
<li>Client initiates change cipher spec, changing from asymmetric to symmetric encryption</li>
<li>Client initiates encrypted handshake message, transmitting info encrypted with c1 + s1 + pre-master-key to server</li>
<li>Server also initiates change cipher spec</li>
<li>Server similarly initiates encrypted handshake message
At this point, both sides&rsquo; SSL handshake is complete. Encrypted data transmission begins.</li>
</ol>
]]></content><summary type="html"><![CDATA[ <blockquote>
<p>This mainly uses text narration to try to explain this process, used to organize knowledge points and identify gaps. If any descriptions are inaccurate, please forgive me.</p>
</blockquote>
<p>Q: From entering an address in the browser and pressing enter, to receiving the response - describe the details of this process.</p>
<p>A:</p>
<h2 id="premise">Premise</h2>
<p>Complex programs need to be layered.</p>
<h2 id="overview">Overview</h2>
<p>Explaining this process according to the OSI seven-layer protocol, focusing on the performance in OSI layer 2 - Data Link Layer (hereafter MAC layer), layer 3 - Network Layer (hereafter IP layer), layer 4 - Transport Layer, and the Application Layer. After reaching the Application Layer, I&rsquo;ll discuss CDN principles and common architectures. Then briefly cover data center architecture.</p>]]></summary></entry><entry><title>Drifting</title><link href="https://www.ogura.io/posts/2018/11/poor/"/><id>https://www.ogura.io/posts/2018/11/poor/</id><published>20181109T08:34:57.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>From graduation until 2018, I&rsquo;ve moved 8 times - truly a drifter. In 2015, I switched from drifting in Beijing to drifting in Shenzhen. Here I&rsquo;ll document some details about my 8th move:</p>
<h1 id="finding-an-apartment">Finding an Apartment</h1>
<p>Almost as soon as I started my <a href="/posts/2018/10/vacation/">unemployment</a>, I began paying attention to apartment hunting. My general requirements were:</p>
<ul>
<li>Larger room, a big one-bedroom or small two-bedroom</li>
<li>Normal layout</li>
<li>Quiet community</li>
<li>Within two kilometers of subway or served by shuttle bus</li>
<li>Budget about 100 yuan more than last year&rsquo;s rent</li>
<li>Direct contract with landlord, and landlord should be easy to communicate with</li>
</ul>
<p>Following these requirements, I found three candidates in three days:</p>
<ul>
<li>Apartment A: Pros are quiet community and cheap rent. Cons are 2.6km from subway, and no commercial area nearby.</li>
<li>Apartment B: Pros are newer apartment. Cons are empty without appliances - adding everything would add about 1k monthly rent.</li>
<li>Apartment C: Pros are quiet community, large room, and landlord is easy to communicate with. Cons are older building, no elevator, and some furniture is seriously worn.</li>
</ul>
<p>After various trade-offs, I chose Apartment C.</p>
<h1 id="moving">Moving</h1>
<p>Looking back at the entire moving process, it took exactly one week.</p>
<p>Monday: Rented a car to move simple clothes, mattress, bedding, and cleaning supplies. Aired out the mattress and bedding, set aside.
Tuesday: Organized items at home, ready to move.
Wednesday: Moved pots, pans, bowls, and other fragile items.
Thursday: Moved some small items. Washed and dried sofa covers. Arranged broadband transfer.
Friday: Booked moving company, packed and organized all items. Cleared furniture. Cleaned and disinfected Apartment C.
Saturday: Morning followed moving company to move items to Apartment C. Afternoon rented a car-share vehicle, moved remaining items in three trips. Negotiated a price for the wardrobe with a shady furniture buyer, and between the second and third trips arranged for someone to mount the TV on the wall. Finished all three trips after 1 AM.
Sunday: Morning made another trip for kitchen items and refrigerator contents, sold the wardrobe. Replaced bathroom shower head, showered and rested. Had lobster for dinner, done.</p>
<h1 id="lessons-learned">Lessons Learned</h1>
<p>Apartment hunting: If your requirements are clear, quickly filter down to two or three suitable options, then make trade-offs among them.
Driving: I rarely drove after getting my license and was timid about it. But this move involved seven round trips by car, navigating through the harsh environment of the residential complex. The timidity about driving is gone - I can even listen to the radio to relax in congested traffic now. Wonderful.
Organization: Prepare plenty of boxes. For walk-up buildings, medium-sized boxes are better - a heavy box carried up several floors will make your back ache.</p>
<p>Moving:
Observing the moving crew, I suddenly appreciated the importance of work.</p>
<blockquote>
<p>I had moved things up and down the stairs several times before - compared to the efficiency of the moving crew, I was way behind.</p>
</blockquote>
<p>On one hand, it&rsquo;s the use of various tools, and on the other, the difference in physical fitness. Only the concept of &ldquo;work&rdquo; can gather such a group of professionals together, and through the process of repeated work, they&rsquo;ll summarize many techniques to improve efficiency.</p>
<blockquote>
<p>The driver in charge of the moving truck only drove the truck in and out and operated the cargo lift, but didn&rsquo;t help with the actual moving.</p>
</blockquote>
<p>Different division of labor allows people to spend more time deepening expertise in vertical domains, avoiding dispersed learning effort.</p>
]]></content><summary type="html"><![CDATA[ <p>From graduation until 2018, I&rsquo;ve moved 8 times - truly a drifter. In 2015, I switched from drifting in Beijing to drifting in Shenzhen. Here I&rsquo;ll document some details about my 8th move:</p>
<h1 id="finding-an-apartment">Finding an Apartment</h1>
<p>Almost as soon as I started my <a href="/posts/2018/10/vacation/">unemployment</a>, I began paying attention to apartment hunting. My general requirements were:</p>
<ul>
<li>Larger room, a big one-bedroom or small two-bedroom</li>
<li>Normal layout</li>
<li>Quiet community</li>
<li>Within two kilometers of subway or served by shuttle bus</li>
<li>Budget about 100 yuan more than last year&rsquo;s rent</li>
<li>Direct contract with landlord, and landlord should be easy to communicate with</li>
</ul>
<p>Following these requirements, I found three candidates in three days:</p>]]></summary></entry><entry><title>Vacation</title><link href="https://www.ogura.io/posts/2018/10/vacation/"/><id>https://www.ogura.io/posts/2018/10/vacation/</id><published>20181030T22:29:10.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>After quitting without having another job lined up, this long-overdue vacation made me feel on edge. Compared to being busy at work, my heart feels even heavier. Within my field of vision, there have been waves of resignations. But where will these people end up going? I&rsquo;ve read quite a few articles like <a href="https://new.qq.com/omn/20180712/20180712A1EZNN.html">Jumping ship too hastily is just jumping from one pit to another</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and one piece of advice stands out:</p>
<blockquote>
<p>I also want to sound a warning to friends who are job hopping - even if you hate your current job, don&rsquo;t just jump into another one casually. You must understand why you&rsquo;re jumping, and think about what you want.
If you really want to leave but truly haven&rsquo;t found something suitable, it&rsquo;s better to quit without a backup, be poor for a little while, than to enter a new pit. Because a new pit will make you feel even more desperate, and also damages your career stability.</p>
</blockquote>
<p>I hope I can maintain my original intentions in these tides and wait for the truly suitable [pit] for me.</p>
<p>Here&rsquo;s a list of what I&rsquo;m doing during this vacation:</p>
<ol>
<li>Summarize knowledge points from previous work, distill them into writing. And clarify the unclear parts through learning.</li>
<li>Look for a place to rent: The current apartment lease is expiring, hoping to find a comfortable place to live.</li>
<li>Rest :)</li>
</ol>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Please ignore the tarot card part in the article - that&rsquo;s the part I automatically skipped while reading too&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>After quitting without having another job lined up, this long-overdue vacation made me feel on edge. Compared to being busy at work, my heart feels even heavier. Within my field of vision, there have been waves of resignations. But where will these people end up going? I&rsquo;ve read quite a few articles like <a href="https://new.qq.com/omn/20180712/20180712A1EZNN.html">Jumping ship too hastily is just jumping from one pit to another</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and one piece of advice stands out:</p>]]></summary></entry><entry><title>Migrating Old Blog Posts</title><link href="https://www.ogura.io/posts/2018/10/migrate-from-old-blog/"/><id>https://www.ogura.io/posts/2018/10/migrate-from-old-blog/</id><published>20181010T19:14:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>From a comment <a href="http://disq.us/p/1wev2g5">http://disq.us/p/1wev2g5</a> on the article <a href="http://yihui.name/cn/2018/09/countryman/">http://yihui.name/cn/2018/09/countryman/</a>:</p>
<blockquote>
<p>Haha, the blog post from thirteen years ago was remembering things from three years before that. So even just for this coolness, you should persist in blogging.</p>
</blockquote>
<p>I&rsquo;ve moved between different blog platforms<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> multiple times, and also abandoned maintaining some posts due to domain and hosting issues. The earliest traceable posts are actually from 2013-2014, during the period when I built a blog with Jekyll on GitHub. It&rsquo;s been 5 years already.</p>
<p>I decided to migrate those posts over to add some historical weight to the blog - you can see <code>since 2013</code> in the footer now :)</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Wordpress -&gt; Jekyll -&gt; bitcron -&gt; hexo&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>From a comment <a href="http://disq.us/p/1wev2g5">http://disq.us/p/1wev2g5</a> on the article <a href="http://yihui.name/cn/2018/09/countryman/">http://yihui.name/cn/2018/09/countryman/</a>:</p>
<blockquote>
<p>Haha, the blog post from thirteen years ago was remembering things from three years before that. So even just for this coolness, you should persist in blogging.</p>
</blockquote>
<p>I&rsquo;ve moved between different blog platforms<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> multiple times, and also abandoned maintaining some posts due to domain and hosting issues. The earliest traceable posts are actually from 2013-2014, during the period when I built a blog with Jekyll on GitHub. It&rsquo;s been 5 years already.</p>]]></summary></entry><entry><title>Enabling True-Color Support in tmux and vim</title><link href="https://www.ogura.io/posts/2018/09/true-color-in-vim-under-tmux-on-ubuntu/"/><id>https://www.ogura.io/posts/2018/09/true-color-in-vim-under-tmux-on-ubuntu/</id><published>20180929T11:23:02.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Inspired by <a href="http://blog.acgtyrant.com/%E5%9C%A8-Linux-%E4%B8%8B%E5%85%A8%E9%9D%A2%E4%BD%BF%E7%94%A8%E7%9C%9F%E5%BD%A9.html">http://blog.acgtyrant.com/%E5%9C%A8-Linux-%E4%B8%8B%E5%85%A8%E9%9D%A2%E4%BD%BF%E7%94%A8%E7%9C%9F%E5%BD%A9.html</a>, I learned about the existence of True-Color beyond 256 colors in the terminal world<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.
For heavy Vim users who never leave the terminal, this multi-color support is irresistibly attractive.</p>
<p><strong>Table of Contents</strong></p>
<h1 id="overview">Overview</h1>
<p>To get true multi-color support, you need support from <code>terminal emulator</code> + <code>tmux</code> + <code>vim</code>. Here&rsquo;s my programming environment: Windows VM (Ubuntu 18.04) + SecureCRT + Tmux + Vim 8.1.
So what I need to do is enable True-Color support in each of these components.</p>
<h1 id="terminal-emulator">Terminal Emulator</h1>
<p>My preferred terminal emulator on Windows is SecureCRT, but when checking the True-Color support list at <a href="https://gist.github.com/XVilka/8346728#now-supporting-truecolour">https://gist.github.com/XVilka/8346728#now-supporting-truecolour</a>, I found SecureCRT wasn&rsquo;t on it. After trying several Windows-supported terminal emulators, I settled on <code>MobaXTerm</code>.</p>
<p>The reasons are its similar operation logic to SecureCRT, and it provides a Personal Edition.</p>
<p>As for color preferences, I still chose the dark green background solarized theme.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h1 id="tmux">tmux</h1>
<p>The conventional method didn&rsquo;t work here. After much searching, I found <a href="http://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00635.html">http://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00635.html</a> - a method to modify terminfo to enable Tc (True-Color) for tmux. (I used the latest tmux source code compiled from git)</p>
<p>Conventional method - modify <code>$HOME/.tmux.conf</code> file:</p>
<pre tabindex="0"><code class="language-.tmux.conf" data-lang=".tmux.conf"># !!!important!!! Enable 24-bit color, other methods don&#39;t work
set -g default-terminal &#34;tmux-256color&#34;
set -ga terminal-overrides &#34;,*256col*:Tc&#34;
</code></pre><p>Modify Terminfo method (tested and working):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>/usr/bin/infocmp &gt; /tmp/.infocmp
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;  Tc,&#34;</span> &gt;&gt; /tmp/.infocmp
</span></span><span style="display:flex;"><span>/usr/bin/tic -x /tmp/.infocmp
</span></span></code></pre></div><p>Verify True-Color:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>awk <span style="color:#e6db74">&#39;BEGIN{
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    s=&#34;/\\/\\/\\/\\/\\&#34;; s=s s s s s s s s;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    for (colnum = 0; colnum&lt;77; colnum++) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        r = 255-(colnum*255/76);
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        g = (colnum*510/76);
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        b = (colnum*255/76);
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        if (g&gt;255) g = 510-g;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        printf &#34;\033[48;2;%d;%d;%dm&#34;, r,g,b;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        printf &#34;\033[38;2;%d;%d;%dm&#34;, 255-r,255-g,255-b;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        printf &#34;%s\033[0m&#34;, substr(s,colnum+1,1);
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    printf &#34;\n&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">}&#39;</span>
</span></span></code></pre></div><p>Screenshot:</p>
<p><img src="/images/2018/09/true-color-in-vim.png" alt="True-Color effect"></p>
<h1 id="vim">vim</h1>
<p>Three main changes needed here:</p>
<ol>
<li>Modify <code>.vimrc</code> file according to documentation</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-.vimrc" data-lang=".vimrc"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">has</span>(<span style="color:#e6db74">&#34;termguicolors&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e">    &#34; fix bug for vim</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> &amp;<span style="color:#a6e22e">t_8f</span> = <span style="color:#e6db74">&#34;\&lt;Esc&gt;[38;2;%lu;%lu;%lum&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> &amp;<span style="color:#a6e22e">t_8b</span> = <span style="color:#e6db74">&#34;\&lt;Esc&gt;[48;2;%lu;%lu;%lum&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    &#34; enable true color</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">set</span> <span style="color:#a6e22e">termguicolors</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">endif</span>
</span></span></code></pre></div><ol start="2">
<li>Install a suitable vim theme that supports true color. I chose <code>solarized8</code> with matching colors</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-.vimrc" data-lang=".vimrc"><span style="display:flex;"><span><span style="color:#a6e22e">Plugin</span> <span style="color:#e6db74">&#39;lifepillar/vim-solarized8&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">&#34; Enhanced contrast</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">set</span> <span style="color:#a6e22e">background</span>=dark
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">colorscheme</span> <span style="color:#a6e22e">solarized8_high</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">g</span>:<span style="color:#a6e22e">solarized_extra_hi_groups</span>=<span style="color:#ae81ff">1</span>
</span></span></code></pre></div><ol start="3">
<li>After entering vim&rsquo;s VISUAL mode, I found that selected lines couldn&rsquo;t reverse display - they just lost highlighting. This makes it hard to see the exact selected characters (for example, comments also have no highlighting, making it hard to find selection boundaries).
The search result was a terminal support issue - it can&rsquo;t recognize the <code>hi Visual gui=reverse</code> reverse command. So I manually adjusted the selected text colors.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-.vimrc" data-lang=".vimrc"><span style="display:flex;"><span><span style="color:#66d9ef">hi</span> <span style="color:#a6e22e">Visual</span> <span style="color:#a6e22e">gui</span>=<span style="color:#a6e22e">reverse</span> <span style="color:#a6e22e">guifg</span>=<span style="color:#a6e22e">Black</span> <span style="color:#a6e22e">guibg</span>=<span style="color:#a6e22e">Grey</span>
</span></span></code></pre></div><p>(Still in .vimrc, specifically right after theme colors)</p>
<p>before:
<img src="/images/2018/09/vim-default-reverse.gif" alt="before"></p>
<p>after:
<img src="/images/2018/09/vim-custom-reverse.gif" alt="after"></p>
<blockquote>
<p>Both images show selecting lines 358-363</p>
</blockquote>
<h1 id="addendum">Addendum</h1>
<p>During use, I also found a conflict between termdebug supported in vim 8.1 and true-color: if you <code>set termguicolors</code> after <code>packadd termdebug</code>, the currently executing line won&rsquo;t be highlighted during actual debugging. The correct approach is to put <code>packadd termdebug</code> after <code>set termguicolors</code>.</p>
<h1 id="significance">Significance</h1>
<p>From 256 color support to 160,000 color support, the literal meaning is smoother color transitions. In vim, it means code looks more comfortable (I wonder if there are corresponding True-color extensions for code syntax highlighting)</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>8-bit color is also known as 256 color, 24-bit color is also known as true color, with 16,777,216 colors total&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>The history with this color scheme goes back to <a href="https://blog.csdn.net/zklth/article/details/8937905">https://blog.csdn.net/zklth/article/details/8937905</a> - unfortunately most images in the article are broken, but I have a local Evernote version saved.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>Inspired by <a href="http://blog.acgtyrant.com/%E5%9C%A8-Linux-%E4%B8%8B%E5%85%A8%E9%9D%A2%E4%BD%BF%E7%94%A8%E7%9C%9F%E5%BD%A9.html">http://blog.acgtyrant.com/%E5%9C%A8-Linux-%E4%B8%8B%E5%85%A8%E9%9D%A2%E4%BD%BF%E7%94%A8%E7%9C%9F%E5%BD%A9.html</a>, I learned about the existence of True-Color beyond 256 colors in the terminal world<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.
For heavy Vim users who never leave the terminal, this multi-color support is irresistibly attractive.</p>
<p><strong>Table of Contents</strong></p>
<h1 id="overview">Overview</h1>
<p>To get true multi-color support, you need support from <code>terminal emulator</code> + <code>tmux</code> + <code>vim</code>. Here&rsquo;s my programming environment: Windows VM (Ubuntu 18.04) + SecureCRT + Tmux + Vim 8.1.
So what I need to do is enable True-Color support in each of these components.</p>]]></summary></entry><entry><title>Migraines and Rizatriptan</title><link href="https://www.ogura.io/posts/2018/09/migraine-and-rizatriptan/"/><id>https://www.ogura.io/posts/2018/09/migraine-and-rizatriptan/</id><published>20180926T14:58:42.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>This might be the biggest thing that enlightened me recently: being diagnosed with migraines and learning about rizatriptan.</p>
<h1 id="migraines">Migraines</h1>
<blockquote>
<p>A migraine is a chronic condition characterized by recurring moderate to severe headaches, usually accompanied by various autonomic nervous system symptoms. The English word &ldquo;Migraine&rdquo; comes from the Greek ἡμικρανία (hemikrania), meaning &ldquo;pain on one side of the head,&rdquo; where ἡμι- (hemi-) means &ldquo;half,&rdquo; and κρανίον (kranion) means &ldquo;skull.&rdquo;</p>
<p>Typically this headache is unilateral (involving only one side of the head) and pulsating, lasting 2-72 hours. Related symptoms may include nausea, vomiting, increased sensitivity to light and sound, and physical activity worsening the pain. One-third of migraine sufferers experience an aura: brief visual, sensory, speech, or motor disturbances that signal an impending headache.
(Quoted from <a href="https://en.wikipedia.org/wiki/Migraine">https://en.wikipedia.org/wiki/Migraine</a>)</p>
</blockquote>
<h1 id="rizatriptan">Rizatriptan</h1>
<blockquote>
<p>Rizatriptan is a triptan 5-HT1 agonist developed by Merck for treating migraines, sold under the brand name Maxalt.
(Quoted from <a href="https://en.wikipedia.org/wiki/Rizatriptan">https://en.wikipedia.org/wiki/Rizatriptan</a>)</p>
</blockquote>
<h1 id="my-experience">My Experience</h1>
<p>Under the bombardment of irregular &ldquo;discomfort&rdquo;<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> including headaches, stomachaches, photophobia, chest tightness, shortness of breath, and rhinitis attacks, I went to the hospital almost once a month for checkups. Of course, these checkups were just routine examinations in neurology, ENT, ophthalmology, and gastroenterology. After these checkups, doctors would prescribe medication for the corresponding symptoms, and some doctors would even prescribe &ldquo;Chinese patent medicines&rdquo;<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. After episode after episode, I also felt the futility of these methods.</p>
<p>On the internet outside the hospital, on Wikipedia, I saw descriptions of migraine symptoms. Comparing my symptoms with them, I found a high degree of overlap. But as someone not in the medical profession, I naturally wouldn&rsquo;t self-diagnose based on a description, as this could be a form of &ldquo;hypochondria&rdquo;<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>During one of my many medical visits, a doctor suggested I find out when a vertigo specialist was available. Eventually, the specialist confirmed my migraine diagnosis. The doctor mentioned rizatriptan, an internationally recognized migraine treatment drug. Although the hospital didn&rsquo;t have this prescription drug at the time, at my request, the doctor wrote the drug name on a piece of paper for me to find.</p>
<p>Again with the help of the internet, I made an appointment on JD Health, got a prescription, and bought rizatriptan.</p>
<h1 id="results">Results</h1>
<p>During my most recent migraine attack (symptoms had appeared but were still mild), I took rizatriptan. Without ruling out the placebo effect, the symptoms disappeared.</p>
<p>Having relied on alternative medicine<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> for years, I finally managed to overcome this chronic condition<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> using modern scientific methods. Enlightening.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>&ldquo;Discomfort&rdquo; is used here to indicate the difficulty of diagnosis, mainly reflected in uncertain symptoms&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Chinese patent medicines are more meaningful for hospital and doctor revenue than for their actual effectiveness&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Hypochondria, now classified as somatic symptom disorder, mainly refers to patients worrying or believing they have one or more serious physical illnesses. Patients report physical symptoms, repeatedly seek medical attention, and even after repeated medical tests showing negative results and doctors explaining there&rsquo;s no corresponding disease, patients&rsquo; concerns cannot be dispelled. Often accompanied by anxiety or depression&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>This refers to acupuncture, moxibustion, etc. in Traditional Chinese Medicine, which generally have some effect when migraines occur&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>&ldquo;Overcome&rdquo; here means being able to eliminate symptoms with medication when migraines occur&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>This might be the biggest thing that enlightened me recently: being diagnosed with migraines and learning about rizatriptan.</p>
<h1 id="migraines">Migraines</h1>
<blockquote>
<p>A migraine is a chronic condition characterized by recurring moderate to severe headaches, usually accompanied by various autonomic nervous system symptoms. The English word &ldquo;Migraine&rdquo; comes from the Greek ἡμικρανία (hemikrania), meaning &ldquo;pain on one side of the head,&rdquo; where ἡμι- (hemi-) means &ldquo;half,&rdquo; and κρανίον (kranion) means &ldquo;skull.&rdquo;</p>
<p>Typically this headache is unilateral (involving only one side of the head) and pulsating, lasting 2-72 hours. Related symptoms may include nausea, vomiting, increased sensitivity to light and sound, and physical activity worsening the pain. One-third of migraine sufferers experience an aura: brief visual, sensory, speech, or motor disturbances that signal an impending headache.
(Quoted from <a href="https://en.wikipedia.org/wiki/Migraine">https://en.wikipedia.org/wiki/Migraine</a>)</p>]]></summary></entry><entry><title>NexT &amp; izhengfan Combined Theme</title><link href="https://www.ogura.io/posts/2018/09/izhengfan-next-mixed-hexo-theme/"/><id>https://www.ogura.io/posts/2018/09/izhengfan-next-mixed-hexo-theme/</id><published>20180912T17:49:19.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>I&rsquo;ve always coveted the super-minimalist theme of <code>yinwang.org</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> - it looks like some kind of advanced archive. After finding and trying the related Hexo theme <code>mickeyouyou/yinwang</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, I wasn&rsquo;t satisfied (mainly with the font, layout, and code highlighting).
Hexo users basically all know the <code>NexT</code> theme - powerful customization features, easy to find suitable layouts, fonts, highlighting, third-party plugins. But unfortunately too many people use it, and it feels a bit heavy.</p>
<p>While searching for <code>vim termdebug</code> issues, I accidentally discovered <a href="https://fzheng.me">https://fzheng.me</a>&rsquo;s blog. Its minimalist approach and beautiful fonts made me want to try the related theme. I checked the related source code on GitHub - it&rsquo;s a <code>Jekyll</code> template. I spent some time converting it to a <code>hexo</code> template, and during the conversion, I ported over a few extension features I was comfortable with from the <code>NexT</code> theme (since there are only three page templates, the workload was small).</p>
<p>The ported features are:</p>
<ul>
<li>Disqus comments. I modified it - if you choose lazyload, it&rsquo;s not triggered by scrolling, but by clicking a button (forgive me for not knowing how to do button hover effects)</li>
<li>Baidu / Google / Bing website verification and analytics features</li>
<li>Social network buttons</li>
<li>Website license</li>
<li>Custom website footer</li>
</ul>
<p>Theme project at: <a href="https://github.com/wukra/izhengfan.github.io.git">https://github.com/wukra/izhengfan.github.io.git</a></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Wang Yin&rsquo;s blog: <a href="http://www.yinwang.org/">http://www.yinwang.org/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Project that mimics Wang Yin&rsquo;s blog theme: <a href="https://github.com/mickeyouyou/yinwang">https://github.com/mickeyouyou/yinwang</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>I&rsquo;ve always coveted the super-minimalist theme of <code>yinwang.org</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> - it looks like some kind of advanced archive. After finding and trying the related Hexo theme <code>mickeyouyou/yinwang</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, I wasn&rsquo;t satisfied (mainly with the font, layout, and code highlighting).
Hexo users basically all know the <code>NexT</code> theme - powerful customization features, easy to find suitable layouts, fonts, highlighting, third-party plugins. But unfortunately too many people use it, and it feels a bit heavy.</p>]]></summary></entry><entry><title>Perl Syntax Sugar: do { local $/; &lt;FILEHANDLE&gt; }</title><link href="https://www.ogura.io/posts/2018/09/read-file-the-perl-way/"/><id>https://www.ogura.io/posts/2018/09/read-file-the-perl-way/</id><published>20180907T11:17:27.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-perl" data-lang="perl"><span style="display:flex;"><span>open FILEHANDLE, <span style="color:#e6db74">&#39;/etc/hosts&#39;</span> <span style="color:#f92672">or</span> die $!;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">my</span> $string <span style="color:#f92672">=</span> <span style="color:#66d9ef">do</span> { local $/; <span style="color:#e6db74">&lt;FILEHANDLE&gt;</span> };
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">print</span>($string);
</span></span></code></pre></div><p>The output of this Perl code is roughly:</p>
<pre tabindex="0"><code class="language-hosts" data-lang="hosts">127.0.0.1       localhost

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
</code></pre><p>That is, the entire contents of <code>/etc/hosts</code>.</p>
<h1 id="qa">Q&amp;A</h1>
<p>The difficult part to understand is line 2. In conventional Perl, reading a file&rsquo;s complete contents would use a while loop to read line by line:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-perl" data-lang="perl"><span style="display:flex;"><span>open FILEHANDLE, <span style="color:#e6db74">&#39;/etc/hosts&#39;</span> <span style="color:#f92672">or</span> die $!;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">my</span> $string;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">while</span> (<span style="color:#e6db74">&lt;FILEHANDLE&gt;</span>) {
</span></span><span style="display:flex;"><span>    $string <span style="color:#f92672">.=</span> $_;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Here&rsquo;s a detailed explanation of line 2:</p>
<ul>
<li><code>do { &lt;block&gt; }</code> is a code block in Perl. The function&rsquo;s return value is the last statement in <code>&lt;block&gt;</code>.</li>
<li><code>&lt;FILEHANDLE&gt;</code> is the last statement in the above <code>do { &lt;block&gt; }</code>, thus the return value. In Perl, <code>&lt;FILEHANDLE&gt;</code>&rsquo;s return value has two different results depending on context:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-perl" data-lang="perl"><span style="display:flex;"><span><span style="color:#66d9ef">my</span> $scalar <span style="color:#f92672">=</span> <span style="color:#e6db74">&lt;FILEHANDLE&gt;</span>;    <span style="color:#75715e"># When left side is scalar, returns single line of file</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">my</span> @array  <span style="color:#f92672">=</span> <span style="color:#e6db74">&lt;FILEHANDLE&gt;</span>;    <span style="color:#75715e"># When left side is array, returns entire file (each line maps to array element)</span>
</span></span></code></pre></div><ul>
<li>The <code>$/</code> variable setting is key to this syntax sugar. <code>$/</code> is the <em>input record separator</em>, which defaults to newline. That is, <code>&lt;FILEHANDLE&gt;</code>&rsquo;s default behavior of returning single-line content in scalar context is determined by the <code>$/</code> variable. And <code>local $/</code> is equivalent to <code>local $/ = undef</code></li>
<li>Difference between <code>local</code> and <code>my</code>: <code>my</code> creates a new variable, while <code>local</code> temporarily changes a variable&rsquo;s value (within scope)</li>
</ul>
<p>Overall, this creates a temporary scope through <code>do {}</code>, changes the value of <code>$/</code> within the scope, changes the separator for <code>&lt;FILEHANDLE&gt;</code> in scalar context (from <code>\n</code> to <code>undef</code>), achieving the goal of reading the entire file content.</p>
<h1 id="references">References</h1>
<ul>
<li>The difference between my and local <a href="https://www.perlmonks.org/?node_id=94007">https://www.perlmonks.org/?node_id=94007</a></li>
<li>Perl Idioms Explained - my $string = do { local $/; <!-- raw HTML omitted --> }; <a href="https://www.perlmonks.org/?node_id=287647">https://www.perlmonks.org/?node_id=287647</a></li>
</ul>
]]></content><summary type="html"><![CDATA[ <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-perl" data-lang="perl"><span style="display:flex;"><span>open FILEHANDLE, <span style="color:#e6db74">&#39;/etc/hosts&#39;</span> <span style="color:#f92672">or</span> die $!;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">my</span> $string <span style="color:#f92672">=</span> <span style="color:#66d9ef">do</span> { local $/; <span style="color:#e6db74">&lt;FILEHANDLE&gt;</span> };
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">print</span>($string);
</span></span></code></pre></div><p>The output of this Perl code is roughly:</p>
<pre tabindex="0"><code class="language-hosts" data-lang="hosts">127.0.0.1       localhost

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
</code></pre><p>That is, the entire contents of <code>/etc/hosts</code>.</p>
<h1 id="qa">Q&amp;A</h1>
<p>The difficult part to understand is line 2. In conventional Perl, reading a file&rsquo;s complete contents would use a while loop to read line by line:</p>]]></summary></entry><entry><title>DNS Basics</title><link href="https://www.ogura.io/posts/2018/08/dns-basic-knowledge/"/><id>https://www.ogura.io/posts/2018/08/dns-basic-knowledge/</id><published>20180830T22:10:05.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>This article is mainly an interpretation of <a href="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035</a>.</p>
<h1 id="zone">Zone</h1>
<p>A DNS zone consists of two parts: Resource Records (RRs) and Directives</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zone" data-lang="zone"><span style="display:flex;"><span><span style="color:#75715e">; zone file for example.com</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">$TTL</span> <span style="color:#e6db74">2d</span>    <span style="color:#75715e">; 172800 secs default TTL for zone</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">$ORIGIN</span> example.com.
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@</span>             <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">SOA</span>   ns1.example.com. hostmaster.example.com. (
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">2003080800</span> <span style="color:#75715e">; se = serial number</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">12h</span>        <span style="color:#75715e">; ref = refresh</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">15m</span>        <span style="color:#75715e">; ret = update retry</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">3w</span>         <span style="color:#75715e">; ex = expiry</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">3h</span>         <span style="color:#75715e">; min = minimum</span>
</span></span><span style="display:flex;"><span>                        )
</span></span><span style="display:flex;"><span>              <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">NS</span>      ns1.example.com.
</span></span><span style="display:flex;"><span>              <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">MX</span>  <span style="color:#e6db74">10</span>  mail.example.net.
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">joe</span>           <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">A</span>       <span style="color:#ae81ff">192.168.254.3</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">www</span>           <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">CNAME</span>   <span style="color:#960050;background-color:#1e0010">joe</span>
</span></span></code></pre></div><ol>
<li>A DNS zone file consists of comments, directives, and records (RRs)</li>
<li>Comments start with ; and continue to the end of the line</li>
<li>Directives start with <code>$</code>. <code>$ORIGIN</code> and <code>$INCLUDE</code> are defined in <a href="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035</a>, while <code>$GENERATE</code> is a non-standard directive provided by <code>BIND</code>.</li>
<li>The <code>$TTL</code> directive must appear before the first RR</li>
<li>The first RR must be SOA (Start of Authority)</li>
</ol>
<h1 id="dns-message">DNS Message</h1>
<p>The message here refers to the message protocol between <code>Resolver</code> and the <code>DNS</code> system.</p>
<p>Format:</p>
<pre tabindex="0"><code>    +---------------------+
    |        Header       |
    +---------------------+
    |       Question      | the question for the name server
    +---------------------+
    |        Answer       | RRs answering the question
    +---------------------+
    |      Authority      | RRs pointing toward an authority
    +---------------------+
    |      Additional     | RRs holding additional information
    +---------------------+
</code></pre><p>(Diagram from RFC-1035)</p>
<h1 id="header">Header</h1>
<pre tabindex="0"><code>                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      ID                       |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    QDCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ANCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    NSCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ARCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
</code></pre><p>(Diagram from RFC-1035)</p>
<ul>
<li>ID: A 16-bit request ID, returned as-is in the response, used to identify the uniqueness of requests</li>
<li>QR: Request/response identifier bit. Set to 0 for request, 1 for response</li>
<li>OPCODE:
<ul>
<li>0: QUERY. Standard query</li>
<li>1: IQUERY. Inverse query (optional support)</li>
<li>2: STATUS. DNS status</li>
<li>3-15: Reserved</li>
</ul>
</li>
<li>AA(res only): Authoritative Answer. Responses from zone owners are authoritative answers, while responses from other DNS servers based on cache are non-authoritative</li>
<li>TC: Truncated. Message is truncated when the message body exceeds maximum transmittable size</li>
<li>RD: Recursion Desired. Requests recursive query in request message; if server supports recursive queries, set to 1, otherwise 0</li>
<li>RA(res only): Recursion Available. Whether this NS server supports recursive queries</li>
<li>Z: Reserved bits, must be 0 in both request and response</li>
<li>RCODE(res only): Identifies server response type (similar to error codes)
<ul>
<li>0: No error</li>
<li>1: Format error: Server cannot parse request</li>
<li>2: Server error: Server failed due to some reason, temporarily unable to respond</li>
<li>3: Response when authority-only server (no recursive support) doesn&rsquo;t find the domain</li>
<li>4: Not implemented: Current query type not supported</li>
<li>5: Refused: Service denied due to policy or other reasons</li>
</ul>
</li>
<li>QDCOUNT: 16-bit unsigned integer, indicates number of Question Section entries</li>
<li>ANCOUNT: 16-bit unsigned integer, indicates number of RR entries in Answer Section</li>
<li>NSCOUNT: 16-bit unsigned integer, indicates number of Authority Section entries</li>
<li>ARCOUNT: 16-bit unsigned integer, indicates number of Additional Section entries</li>
</ul>
<h1 id="question">Question</h1>
<pre tabindex="0"><code>                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
</code></pre><p>(Diagram from RFC-1035)</p>
<p>Note: Each request usually has only one <code>Question Section</code>, but you can actually specify any number of <code>Question Section</code>s through <code>QDCOUNT</code></p>
<ul>
<li>QNAME: The queried domain name. Format: <code>no. of chars</code> <code>domain name</code> <code>no. of chars</code> <code>domain name</code> &hellip;
Where <code>no. of chars</code> is the string length of adjacent <code>domain name</code>
Example:</li>
</ul>
<pre tabindex="0"><code>08 6D 79 64 6F 6D 61 69 6E 03 63 6F 6D 00
// printable
 !  m  y  d  o  m  a  i  n  !  c  o  m  !
// note ! = unprintable
</code></pre><p>(Diagram from zytrax.open)</p>
<ul>
<li>QTYPE: Query type. Corresponds to <code>RR</code>&rsquo;s <code>TYPE</code></li>
<li>QCLASS: Query class. Most common value is <code>x'0001</code> representing <code>IN or Internet</code></li>
</ul>
<h1 id="answer">Answer</h1>
<blockquote>
<p>Answer / Authority / Additional Section / RR all use the same format</p>
</blockquote>
<p>RR / Answer format:</p>
<pre tabindex="0"><code>                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                                               /
    /                      NAME                     /
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     CLASS                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TTL                      |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                   RDLENGTH                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
    /                     RDATA                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
</code></pre><p>(Diagram from RFC-1035)</p>
<ul>
<li><strong>NAME</strong>: Response domain name.
<ul>
<li>Format 1: label format. Same as QNAME above</li>
<li>Format 2: Pointer format. Compressed data format. A 16-bit value: first two bits fixed as <code>1</code> (distinguished from label format due to label format&rsquo;s max value limit of 63), <code>OFFSET</code> bit value is offset relative to message start. Where 0 represents the first bit of ID.</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code>    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    | 1  1|                OFFSET                   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
</code></pre><p>(Diagram from RFC-1035)</p>
<ul>
<li><strong>TYPE</strong>: RR type.
<ul>
<li><code>x'0001(1)</code>: A record</li>
<li><code>x'0002(2)</code>: NS record</li>
<li><code>x'0005(5)</code>: CNAME record</li>
<li><code>x'0006(6)</code>: SOA record</li>
<li><code>x'000B(11)</code>: WKS record &ndash; Well Known Source used to describe common services (like <code>SMTP</code>) using specific protocols (like <code>TCP(6)</code>) on the Internet <a href="https://www.ietf.org/rfc/rfc1010.txt">RFC1010</a></li>
<li><code>x'000C(12)</code>: PTR record. Reverse record for A and AAAA records (IP points to domain)</li>
<li><code>x'000F(15)</code>: MX record. Domain used by <code>SMTP</code> <code>Agent</code> to receive mail</li>
<li><code>x'0021(33)</code>: SRV record. <a href="https://www.ietf.org/rfc/rfc2782.txt">RFC 2782</a> MX record is a special case. SRV record is a record field used by other specific services (like OpenLDAP)</li>
<li><code>x'001C(28)</code>: AAAA record. IPv6 address</li>
</ul>
</li>
<li><strong>CLASS</strong>: RR class. e.g.: <code>Internet</code> <a href="https://en.wikipedia.org/wiki/Chaosnet"><code>Chaos</code></a></li>
<li><strong>TTL</strong>: Time (seconds) the record should be cached</li>
<li><strong>RDLENGTH</strong>: Length of RDATA</li>
<li><strong>RDATA</strong>: Each different type of RR data has a specific format.
<ul>
<li>SOA: SOA record controls domain record update policy</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code>    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    /                     MNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    /                     RNAME                     /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    SERIAL                     |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    REFRESH                    |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     RETRY                     |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    EXPIRE                     |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    MINIMUM                    |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
</code></pre><pre><code>	- Primary NS: Primary NS server. Variable length. label/pointer/mixed
	- Admin MB: Administrator mailbox. Variable length. label/pointer/mixed
	- Serial Number: Serial number. 32-bit unsigned integer. Format is &quot;YYYYMMDDnn&quot;
	- Refresh interval: 32-bit unsigned integer. Interval for secondary NS servers to check zone file updates
	- Retry interval: 32-bit unsigned integer. Retry interval when primary NS server is unreachable
	- Expiration Limit: 32-bit unsigned integer. How long DNS resolver can cache; for some DNS servers, it's the cache duration for responses to resolvers
	- Minimum TTL: 32-bit unsigned integer. Field meaning depends on NS implementation, with three possibilities:
		- Minimum cache duration for the domain by NS, rarely used by servers (officially deprecated)
		- Default TTL value. (Used when no TTL record exists)
		- Defines cache duration when domain has no records (distinct from TTL cache duration when records exist) [RFC 2308](https://www.ietf.org/rfc/rfc2308.txt) (officially recommended)
- MX:
</code></pre>
<pre tabindex="0"><code>    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                  PREFERENCE                   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    /                   EXCHANGE                    /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
</code></pre><pre><code>	- PREFERENCE: Priority. Lower value = higher priority. Generally use `0`(max) for mail server records, use `10` for domain ownership verification
	- Mail Exchanger: Domain providing service. Variable length. label/pointer/mixed
- A: 32-bit unsigned integer, IP address
- AAAA: 16 octets, IPv6 address
- PTR, NS: Address. label/pointer/mixed
</code></pre>
<ul>
<li>
<p><strong>Authority</strong>: (res only) Value is 0 in requests. Same format as <code>Answer</code>. Data is usually <code>NS</code> type <code>RR</code></p>
</li>
<li>
<p><strong>Additional</strong>: (res only) Value is 0 in requests. Same format as <code>Answer</code>. Theoretically, any type of <code>RR</code> is valid. In practice, this field provides <code>A</code> or <code>AAAA</code> records corresponding to <code>NS</code> domains mentioned in <code>Authority Section</code></p>
<p>Note:
<code>(res only)</code> means fields only valid in DNS responses</p>
</li>
</ul>
<p>References:</p>
<ul>
<li>Chapter 8. DNS Resource Records (RRs) <a href="http://www.zytrax.com/books/dns/ch8/">http://www.zytrax.com/books/dns/ch8/</a></li>
<li>Chapter 15 DNS Messages <a href="http://www.zytrax.com/books/dns/ch15/">http://www.zytrax.com/books/dns/ch15/</a></li>
<li>Chaosnet <a href="https://en.wikipedia.org/wiki/Chaosnet">https://en.wikipedia.org/wiki/Chaosnet</a></li>
<li>DNS: Understanding The SOA Record <a href="http://www.peerwisdom.org/2013/05/15/dns-understanding-the-soa-record/">http://www.peerwisdom.org/2013/05/15/dns-understanding-the-soa-record/</a></li>
<li>MX record <a href="https://en.wikipedia.org/wiki/MX_record#Priority">https://en.wikipedia.org/wiki/MX_record#Priority</a></li>
</ul>
]]></content><summary type="html"><![CDATA[ <p>This article is mainly an interpretation of <a href="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035</a>.</p>
<h1 id="zone">Zone</h1>
<p>A DNS zone consists of two parts: Resource Records (RRs) and Directives</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zone" data-lang="zone"><span style="display:flex;"><span><span style="color:#75715e">; zone file for example.com</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">$TTL</span> <span style="color:#e6db74">2d</span>    <span style="color:#75715e">; 172800 secs default TTL for zone</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">$ORIGIN</span> example.com.
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@</span>             <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">SOA</span>   ns1.example.com. hostmaster.example.com. (
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">2003080800</span> <span style="color:#75715e">; se = serial number</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">12h</span>        <span style="color:#75715e">; ref = refresh</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">15m</span>        <span style="color:#75715e">; ret = update retry</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">3w</span>         <span style="color:#75715e">; ex = expiry</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#e6db74">3h</span>         <span style="color:#75715e">; min = minimum</span>
</span></span><span style="display:flex;"><span>                        )
</span></span><span style="display:flex;"><span>              <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">NS</span>      ns1.example.com.
</span></span><span style="display:flex;"><span>              <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">MX</span>  <span style="color:#e6db74">10</span>  mail.example.net.
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">joe</span>           <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">A</span>       <span style="color:#ae81ff">192.168.254.3</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">www</span>           <span style="color:#66d9ef">IN</span>      <span style="color:#66d9ef">CNAME</span>   <span style="color:#960050;background-color:#1e0010">joe</span>
</span></span></code></pre></div><ol>
<li>A DNS zone file consists of comments, directives, and records (RRs)</li>
<li>Comments start with ; and continue to the end of the line</li>
<li>Directives start with <code>$</code>. <code>$ORIGIN</code> and <code>$INCLUDE</code> are defined in <a href="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035</a>, while <code>$GENERATE</code> is a non-standard directive provided by <code>BIND</code>.</li>
<li>The <code>$TTL</code> directive must appear before the first RR</li>
<li>The first RR must be SOA (Start of Authority)</li>
</ol>
<h1 id="dns-message">DNS Message</h1>
<p>The message here refers to the message protocol between <code>Resolver</code> and the <code>DNS</code> system.</p>]]></summary></entry><entry><title>Single-file Version of Lua Auto-indent Processing</title><link href="https://www.ogura.io/posts/2018/08/lua-resty-luatidy/"/><id>https://www.ogura.io/posts/2018/08/lua-resty-luatidy/</id><published>20180802T16:31:08.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>I found a script at <code>https://www.oschina.net/code/snippet_563463_19381</code> that uses Perl regex to auto-indent Lua files. The approach seemed good - a single file can do simple automatic indentation. But after searching GitHub for a long time, I couldn&rsquo;t find any related Lua standalone project code.</p>
<p>Side note about <code>LuaRocks</code>: to use scripts installed by <code>LuaRocks</code>, you need to have <code>LuaRocks</code> installed on the machine. So even if you resolve dependencies when packaging by calling <code>LuaRocks</code>, it won&rsquo;t work unless you add <code>LuaRocks</code> to the dependencies. This is very unfriendly for someone like me with a standalone obsession.</p>
<p>I rewrote the Perl version of auto-indent into a Lua version. The regex uses <code>ngx.re</code> from OpenResty, and it needs to be started using the <code>resty</code> command line or within <code>openresty</code> (two startup methods).</p>
<p><a href="https://github.com/xiaocang/lua-resty-luatidy">https://github.com/xiaocang/lua-resty-luatidy</a></p>
]]></content><summary type="html"><![CDATA[ <p>I found a script at <code>https://www.oschina.net/code/snippet_563463_19381</code> that uses Perl regex to auto-indent Lua files. The approach seemed good - a single file can do simple automatic indentation. But after searching GitHub for a long time, I couldn&rsquo;t find any related Lua standalone project code.</p>
<p>Side note about <code>LuaRocks</code>: to use scripts installed by <code>LuaRocks</code>, you need to have <code>LuaRocks</code> installed on the machine. So even if you resolve dependencies when packaging by calling <code>LuaRocks</code>, it won&rsquo;t work unless you add <code>LuaRocks</code> to the dependencies. This is very unfriendly for someone like me with a standalone obsession.</p>]]></summary></entry><entry><title>Serving Hidden Static Files with Hexo + GitLab</title><link href="https://www.ogura.io/posts/2018/08/hexo-serve-static-hidden-file/"/><id>https://www.ogura.io/posts/2018/08/hexo-serve-static-hidden-file/</id><published>20180801T20:42:12.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <h1 id="background">Background</h1>
<p>Since <a href="https://bitcron.com/">https://bitcron.com/</a>, where I had been hosting my blog, couldn&rsquo;t update certificates, and their Let&rsquo;s Encrypt auto-renewal script was broken (is this site going to shut down? (whispers)), I decided to take some time to migrate my blog to a pure static blog.</p>
<p>I had previous experience with hexo, plus the Node ecosystem is exceptionally rich for personal blogs (lots of third-party plugins available), so I decided to migrate.</p>
<p>After <code>Github</code> was acquired by Microsoft, although its CEO immediately became a billionaire, my goodwill towards it dropped to rock bottom. I only kept a few small open source projects and previously forked projects there, and stopped the paid plan for private repos. Now I&rsquo;ve moved most of my code and private repos to GitLab. So the initial tech choice of GitLab Pages + hexo was settled.</p>
<h1 id="smooth-progress">Smooth Progress</h1>
<p>First, following hexo&rsquo;s official documentation step by step, I set up the main framework. Combined with GitLab CI for automatic static page generation and deployment, everything seemed surprisingly simple.</p>
<h1 id="problem-encountered">Problem Encountered</h1>
<p>In GitLab&rsquo;s pages options, it&rsquo;s easy to find options for binding domains and setting up certificates. I installed Let&rsquo;s Encrypt&rsquo;s CLI tool <code>certbot</code> on my VPS and followed the official documentation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>certbot certonly --manual -d www.ogura.io
</span></span></code></pre></div><p>Next, next, until it stopped at the step requiring website ownership verification:</p>
<p>Access <code>http://www.ogura.io/.well-known/acme-challenge/JDbJQP50-rI3LgKGLy7U7EhOkSxr73P0bP2UeTIh1yE</code> to get a static file with specified content.</p>
<p>Serving static files on my own VPS couldn&rsquo;t be simpler, but how do you do it with GitLab + hexo?</p>
<p>After experimentation, I learned that files in the <code>source/</code> folder are directly placed in the <code>public/</code> folder, but the special case is that <code>.well-known</code> is a hidden folder, and <code>hexo generate</code> ignores hidden files and folders when generating static files.</p>
<p>GitLab pages gave an example solution for Jekyll using an <code>xxx.md</code> file with content:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span>layout: null
</span></span><span style="display:flex;"><span>permalink: /.well-known/acme-challenge/5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM
</span></span><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM.ewlbSYgvIxVOqiP1lD2zeDKWBGEZMRfO_4kJyLRP_4U
</span></span></code></pre></div><p>But in hexo (at least with the <code>next</code> theme, there&rsquo;s no <code>null</code> layout), and the generated link has a <code>.html</code> suffix, which doesn&rsquo;t pass verification.</p>
<p>After messing around with custom layouts for a long time without success, I finally added a manual copy command in <code>.gitlab-ci.yml</code>, and it worked. (Wasted half an hour on layout templates = =)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#75715e"># This file is a template, and might need editing before it works on your project.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Full project: https://gitlab.com/pages/hexo</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">image</span>: <span style="color:#ae81ff">node:6.10.0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">pages</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">script</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">npm install</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">./node_modules/hexo/bin/hexo generate</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">/bin/cp -rv static/.well-known public/</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">./node_modules/.bin/hexo algolia</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">artifacts</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">paths</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ae81ff">public</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">cache</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">paths</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">node_modules</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">key</span>: <span style="color:#ae81ff">project</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">only</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">master</span>
</span></span></code></pre></div>]]></content><summary type="html"><![CDATA[ <h1 id="background">Background</h1>
<p>Since <a href="https://bitcron.com/">https://bitcron.com/</a>, where I had been hosting my blog, couldn&rsquo;t update certificates, and their Let&rsquo;s Encrypt auto-renewal script was broken (is this site going to shut down? (whispers)), I decided to take some time to migrate my blog to a pure static blog.</p>
<p>I had previous experience with hexo, plus the Node ecosystem is exceptionally rich for personal blogs (lots of third-party plugins available), so I decided to migrate.</p>
<p>After <code>Github</code> was acquired by Microsoft, although its CEO immediately became a billionaire, my goodwill towards it dropped to rock bottom. I only kept a few small open source projects and previously forked projects there, and stopped the paid plan for private repos. Now I&rsquo;ve moved most of my code and private repos to GitLab. So the initial tech choice of GitLab Pages + hexo was settled.</p>]]></summary></entry><entry><title>Lua Stack and Memory Limits</title><link href="https://www.ogura.io/posts/2018/05/lua_stack_limits/"/><id>https://www.ogura.io/posts/2018/05/lua_stack_limits/</id><published>20180502T12:12:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>When using dynamically generated Lua code, you often need to be aware of Lua&rsquo;s stack and memory limits. Here are the conclusions first:</p>
<blockquote>
<p>Conclusions are based on actual runs of Lua 5.1.5 (Ubuntu official distribution) on Ubuntu 18.04</p>
</blockquote>
<ul>
<li>A Lua file cannot exceed 262144 constants</li>
<li>A control structure cannot exceed 32895 stacks</li>
<li>The number of upvalues cannot exceed 60</li>
<li>Each function in Lua cannot have more than 200 local variables</li>
</ul>
<p>This article uses <a href="http://www.template-toolkit.org/docs/manual/Intro.html">tt2</a> technology,
and uses the command line <code>tpage</code> provided by <a href="https://metacpan.org/pod/distribution/Template-Toolkit/lib/Template/Tools/tpage.pod">Template::Tools::tpage</a> to generate Lua code</p>
<h2 id="a-lua-file-cannot-exceed-262144-constants">A Lua file cannot exceed 262144 constants</h2>
<p>Where 262144 = 2^18^</p>
<h3 id="tt2-code">tt2 code</h3>
<pre tabindex="0"><code class="language-tt2" data-lang="tt2">[% SET array = [1..262144] %]

local a = {
    &#34;[% array.join(&#34;\&#34;, \&#34;&#34;) %]&#34;
}
</code></pre><h3 id="lua-code">Lua code</h3>
<p>Generated Lua code gist link (large file):
<a href="https://gist.github.com/xiaocang/cd947e57cdb6d16b83d7bdc9c4d0cecd#file-too_much_constant-lua">https://gist.github.com/xiaocang/cd947e57cdb6d16b83d7bdc9c4d0cecd#file-too_much_constant-lua</a></p>
<h3 id="execution-result">Execution result</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ lua too_much_constant.lua
</span></span><span style="display:flex;"><span>lua: constant table overflow
</span></span></code></pre></div><h3 id="source-code-location">Source code location</h3>
<p>TODO</p>
<hr>
<h2 id="a-control-structure-cannot-exceed-32895-stacks">A control structure cannot exceed 32895 stacks</h2>
<p>Where 32895 = 2^15^ + 2^7^ - 1</p>
<h3 id="tt2-code-1">tt2 code</h3>
<pre tabindex="0"><code class="language-tt2" data-lang="tt2">[% DEFAULT max = 32895 %]

if
[% FOREACH i IN [1..max] -%]
    ([% i %] &gt; 0) [% IF i &lt; max %] and [% END %]
[%- IF i % 10 == 0 %]
[% END -%]
[% END %]
then
    print(&#34;ok&#34;)
end
</code></pre><h3 id="lua-code-1">Lua code</h3>
<p>Generated Lua code gist link (large file):
<a href="https://gist.github.com/xiaocang/99a5b636694bab0fd870c17ef97fa472#file-control_pattern_too_long-lua">https://gist.github.com/xiaocang/99a5b636694bab0fd870c17ef97fa472#file-control_pattern_too_long-lua</a></p>
<h3 id="execution-result-1">Execution result</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ lua control_pattern_too_long.lua
</span></span><span style="display:flex;"><span>control_pattern_too_long.lua:3297: control structure too long near <span style="color:#e6db74">&#39;&lt;eof&gt;&#39;</span>
</span></span></code></pre></div><h3 id="source-code-location-1">Source code location</h3>
<p>TODO</p>
<hr>
<h2 id="the-number-of-upvalues-cannot-exceed-60">The number of upvalues cannot exceed 60</h2>
<h3 id="tt2-code-2">tt2 code</h3>
<pre tabindex="0"><code class="language-tt2" data-lang="tt2">[% FOREACH i IN [1..200] -%]
local a_[% i %]
[% END %]

function closure()
    [% FOREACH i IN [1..61] -%]
    a_[% i %] = 0
    [% END %]
end
</code></pre><h3 id="lua-code-2">Lua code</h3>
<p>Generated Lua code gist link (large file):
<a href="https://gist.github.com/xiaocang/baf52c6e7fffa31d684ad55ddfc47867#file-too_much_upvalue-lua">https://gist.github.com/xiaocang/baf52c6e7fffa31d684ad55ddfc47867#file-too_much_upvalue-lua</a></p>
<h3 id="execution-result-2">Execution result</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ lua too_much_upvalue
</span></span><span style="display:flex;"><span>lua: stdin:264: <span style="color:#66d9ef">function</span> at line <span style="color:#ae81ff">203</span> has more than <span style="color:#ae81ff">60</span> upvalues
</span></span></code></pre></div><h3 id="source-code-location-2">Source code location</h3>
<p>TODO</p>
<hr>
<h2 id="each-function-in-lua-cannot-have-more-than-200-local-variables">Each function in Lua cannot have more than 200 local variables</h2>
<h3 id="tt2-code-3">tt2 code</h3>
<pre tabindex="0"><code class="language-tt2" data-lang="tt2">[% FOREACH i IN [1..201] -%]
local a_[% i %]
[% END %]
print(&#34;ok&#34;)
</code></pre><h3 id="lua-code-3">Lua code</h3>
<p>Generated Lua code gist link (large file):
<a href="https://gist.github.com/xiaocang/f4fd0b56bbdec00e4794e661b0ffd994#file-variables_in_function-lua">https://gist.github.com/xiaocang/f4fd0b56bbdec00e4794e661b0ffd994#file-variables_in_function-lua</a></p>
<h3 id="execution-result-3">Execution result</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ lua variables_in_function.lua
</span></span><span style="display:flex;"><span>main <span style="color:#66d9ef">function</span> has more than <span style="color:#ae81ff">200</span> local variables
</span></span></code></pre></div><h3 id="source-code-location-3">Source code location</h3>
<p>from lua5.2.2 <code>src/lparser.c</code> line 30-32</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#75715e">/* maximum number of local variables per function (must be smaller
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">   than 250, due to the bytecode format) */</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#define MAXVARS     200
</span></span></span></code></pre></div>]]></content><summary type="html"><![CDATA[ <p>When using dynamically generated Lua code, you often need to be aware of Lua&rsquo;s stack and memory limits. Here are the conclusions first:</p>
<blockquote>
<p>Conclusions are based on actual runs of Lua 5.1.5 (Ubuntu official distribution) on Ubuntu 18.04</p>
</blockquote>
<ul>
<li>A Lua file cannot exceed 262144 constants</li>
<li>A control structure cannot exceed 32895 stacks</li>
<li>The number of upvalues cannot exceed 60</li>
<li>Each function in Lua cannot have more than 200 local variables</li>
</ul>
<p>This article uses <a href="http://www.template-toolkit.org/docs/manual/Intro.html">tt2</a> technology,
and uses the command line <code>tpage</code> provided by <a href="https://metacpan.org/pod/distribution/Template-Toolkit/lib/Template/Tools/tpage.pod">Template::Tools::tpage</a> to generate Lua code</p>]]></summary></entry><entry><title>Self-hosted Image Hosting with Uppy</title><link href="https://www.ogura.io/posts/2018/04/uppy/"/><id>https://www.ogura.io/posts/2018/04/uppy/</id><published>20180411T23:45:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <h2 id="choice">Choice</h2>
<p>There aren&rsquo;t many self-hosted image hosting solutions. I first used Lychee &ndash; developed in the world&rsquo;s best language (PHP). After using it, I felt the features were too heavy: a bunch of features, only a few actually useful. I also had to install a bunch of image processing plugins for image scaling (which I didn&rsquo;t even need).</p>
<p>Currently I&rsquo;m using uppy. The advantage is easy deployment - one <code>npm install</code> solves it. The backend uses the Go version of <code>tusd</code>, which is even easier to deploy - just throw a binary file on the server.</p>
<h2 id="confusion--solution">Confusion &amp; Solution</h2>
<p>After choosing uppy, I read the official documentation for a long time but couldn&rsquo;t find how to deploy uppy. I even thought uppy was the backend for uppy server. When I was at a dead end, I went to check uppy&rsquo;s official example page and found that deploying uppy only requires a static page - all options are written in the <code>&lt;script&gt;</code> tag on the page.</p>
<p>Example page:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">html</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">head</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">link</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/static/uppy.min.css&#34;</span> <span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stylesheet&#34;</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">head</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>      &lt;<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;dashborad-container&#34;</span>&gt;&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>      &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/static/uppy.min.js&#34;</span>&gt;&lt;/<span style="color:#f92672">script</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">script</span>&gt;
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">uppy</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">Uppy</span>.<span style="color:#a6e22e">Core</span>({ <span style="color:#a6e22e">autoProceed</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> })
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">uppy</span>.<span style="color:#a6e22e">use</span>(<span style="color:#a6e22e">Uppy</span>.<span style="color:#a6e22e">Dashboard</span>, { <span style="color:#a6e22e">target</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#dashborad-container&#39;</span>, <span style="color:#a6e22e">inline</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>, <span style="color:#a6e22e">replaceTargetContent</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> })
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">uppy</span>.<span style="color:#a6e22e">use</span>(<span style="color:#a6e22e">Uppy</span>.<span style="color:#a6e22e">Tus</span>, { <span style="color:#a6e22e">endpoint</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;/files/&#39;</span>, <span style="color:#a6e22e">uploadUrl</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;/files/&#39;</span>, <span style="color:#a6e22e">overridePatchMethod</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>, <span style="color:#a6e22e">resume</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>})
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">uppy</span>.<span style="color:#a6e22e">run</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">uppy</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">&#39;complete&#39;</span>, <span style="color:#a6e22e">result</span> =&gt; {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;successful files:&#39;</span>, <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">successful</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;failed files:&#39;</span>, <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">failed</span>)
</span></span><span style="display:flex;"><span>      })
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">script</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">html</span>&gt;
</span></span></code></pre></div><p>Screenshots:
<img src="/images/2018/04/uppy-screenshot-1.png" alt="">
<img src="/images/2018/04/uppy-screenshot-2.png" alt=""></p>
<p>Very simple functionality: upload/generate link</p>
]]></content><summary type="html"><![CDATA[ <h2 id="choice">Choice</h2>
<p>There aren&rsquo;t many self-hosted image hosting solutions. I first used Lychee &ndash; developed in the world&rsquo;s best language (PHP). After using it, I felt the features were too heavy: a bunch of features, only a few actually useful. I also had to install a bunch of image processing plugins for image scaling (which I didn&rsquo;t even need).</p>
<p>Currently I&rsquo;m using uppy. The advantage is easy deployment - one <code>npm install</code> solves it. The backend uses the Go version of <code>tusd</code>, which is even easier to deploy - just throw a binary file on the server.</p>
<h2 id="confusion--solution">Confusion &amp; Solution</h2>
<p>After choosing uppy, I read the official documentation for a long time but couldn&rsquo;t find how to deploy uppy. I even thought uppy was the backend for uppy server. When I was at a dead end, I went to check uppy&rsquo;s official example page and found that deploying uppy only requires a static page - all options are written in the <code>&lt;script&gt;</code> tag on the page.</p>]]></summary></entry><entry><title>lua-resty-etcd-discovery-client</title><link href="https://www.ogura.io/posts/2018/04/lua_resty_etcd_discovery_client/"/><id>https://www.ogura.io/posts/2018/04/lua_resty_etcd_discovery_client/</id><published>20180401T17:45:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>In microservice architecture, service discovery is an essential component. When using etcd for service registration and discovery, service registration across multiple frameworks, multiple languages, and different network environments becomes repetitive and inefficient development.</p>
<p>The proposal is to introduce OpenResty as a proxy web frontend, using OpenResty&rsquo;s timer to monitor backend HTTP services (and various network services), then collect information and send unified service registration and heartbeat requests to the etcd server.</p>
<p><img src="/images/2018/04/lua-resty-etcd-discovery-client-overview.png" alt="">
<strong>Diagram</strong></p>
<p>Project at: <a href="https://github.com/xiaocang/lua-resty-etcd-discovery-client">https://github.com/xiaocang/lua-resty-etcd-discovery-client</a></p>
]]></content><summary type="html"> &lt;p>In microservice architecture, service discovery is an essential component. When using etcd for service registration and discovery, service registration across multiple frameworks, multiple languages, and different network environments becomes repetitive and inefficient development.&lt;/p></summary></entry><entry><title>Lua Source Code Reading (Part 6)</title><link href="https://www.ogura.io/posts/2018/01/lua_source_code_6/"/><id>https://www.ogura.io/posts/2018/01/lua_source_code_6/</id><published>20180119T18:12:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li>Lua Source Code Reading (Part 6)</li>
</ul>
</blockquote>
<p>Chapter 6 of &ldquo;Lua Source Code Appreciation&rdquo; covers content related to Lua coroutines.</p>
<p><strong>Contents</strong></p>
<h2 id="lua-objects">Lua Objects</h2>
<p>From the C level, Lua&rsquo;s state is a <code>lua_State</code>. Within the same Lua VM, multiple <code>lua_State</code>s share one <code>global_State</code>. The <code>lua_State</code> should not be viewed as a simple static data structure, but as a state machine in a Lua &ldquo;thread&rdquo;, containing information about the current &ldquo;thread&rsquo;s&rdquo; execution state, data stack, call stack, etc.</p>
<h2 id="lua-data-stack">Lua Data Stack</h2>
<p>Lua data can be divided into value types and reference types. The union <code>Value</code> in <code>lstate.h</code> is used to represent them.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#66d9ef">union</span> Value {
</span></span><span style="display:flex;"><span>    GCObject <span style="color:#f92672">*</span>gc;    <span style="color:#75715e">/* collectable objects */</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">void</span> <span style="color:#f92672">*</span>p;         <span style="color:#75715e">/* light userdata */</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">int</span> b;           <span style="color:#75715e">/* booleans */</span>
</span></span><span style="display:flex;"><span>    lua_CFunction f; <span style="color:#75715e">/* light C functions */</span>
</span></span><span style="display:flex;"><span>    numfield         <span style="color:#75715e">/* numbers */</span>
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>As you can see, reference types use a <code>GCObject</code> pointer for indirect reference, while other value types are stored directly in the union. To distinguish the types in the union, an additional type field is also needed.</p>
<p>In Lua, the data stack size is <code>2 * LUA_MINSTACK</code>, while in Lua&rsquo;s C calls, the data stack size is only <code>LUA_MINSTACK</code>. (<code>LUA_MINSTACK</code> defaults to 20)</p>
<p>So when making Lua C calls, you need to explicitly extend the stack size using the <code>luaD_growstack</code> function in <code>ldo.c</code>. Each call extends by at least double the size. When extending the data stack, value type data can be copied directly, while reference type data needs to call <code>correctstack</code> for correction.</p>
<h2 id="call-stack">Call Stack</h2>
<p>Lua&rsquo;s call stack is in a CallInfo structure, stored as a doubly linked list in the &ldquo;thread&rdquo; object.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#75715e"># define next_ci (L) (L-&gt;ci = (L-&gt;ci -&gt; next ? L-&gt;ci -&gt; next : luaE_extendCI (L)))
</span></span></span></code></pre></div><p>As you can see, in the Lua 5.2 implementation, the call stack is encapsulated as an infinitely extensible stack, with unused linked list nodes only cleaned up during GC.</p>
<h2 id="thread-execution-and-interruption">Thread Execution and Interruption</h2>
<p>Lua, as an embedded language, uses C&rsquo;s <code>longjmp</code> mechanism uniformly for interruption and exception handling to implement coroutines not bound to hardware. When embedded in C++, it uses the <code>try / catch</code> mechanism instead. These are switched through macros.</p>
<h2 id="function-calls">Function Calls</h2>
<p>Lua&rsquo;s pcall is implemented as a function rather than a language mechanism. Implementing pcall involves saving and restoring state at the C level stack.</p>
<p>In pure Lua function calls, C functions are generally not involved. The process is:</p>
<blockquote>
<p>Generate a new CallInfo, adjust the data stack, then jump the bytecode execution position to the beginning of the called function. Lua&rsquo;s return operation does the opposite - restore the data stack, pop the CallInfo, modify the bytecode execution position, and restore to the original execution sequence.</p>
</blockquote>
<p>In Lua&rsquo;s underlying API, there are <code>luaD_precall</code> and <code>luaD_poscall</code>, because:</p>
<ul>
<li>In Lua calls, <code>luaD_precall</code> needs to be executed first to specify the bytecode execution position, then bytecode is executed in <code>luaD_poscall</code> and state is restored.</li>
<li>In C calls, there&rsquo;s no need to execute bytecode to restore state, only <code>luaD_precall</code> needs to be executed.</li>
</ul>
<h2 id="c-tricks">C Tricks</h2>
<p>TValue uses the NaN Trick to save memory (Lua 5.2 feature). According to IEEE754, when exponent bits are all 1 and mantissa bits are all 0 (<code>0xfff8000000000000</code>), it represents &ldquo;not a number&rdquo;. It&rsquo;s used to represent infinity and the result of division by 0. When a double-precision floating point number is greater than <code>0xfff8000000000000</code>, it can be considered a deliberately crafted number for special purposes.
NaN Trick exploits this situation. On 32-bit machines, the mantissa bits (52 bits) of a double-precision floating point can be used to store types other than numbers (32 bits) and value types (int), which is sufficient. This saves 4 bytes of memory per value.</p>
<h2 id="reference">Reference</h2>
<ol>
<li><a href="https://en.wikipedia.org/wiki/IEEE_754">IEEE_754</a></li>
<li>float uses IEEE R32.24, double uses IEEE R64.53</li>
</ol>
<hr>
<p><img src="/images/2018/01/ieee-754-float.png" alt="ieee-754-float">
<strong>float</strong></p>
<p><img src="/images/2018/01/ieee-754-double.png" alt="ieee-754-double">
<strong>double</strong></p>
<blockquote>
<p><a href="https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/data_n_call_stack">https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/data_n_call_stack</a></p>
</blockquote>
]]></content><summary type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li>Lua Source Code Reading (Part 6)</li>
</ul>
</blockquote>
<p>Chapter 6 of &ldquo;Lua Source Code Appreciation&rdquo; covers content related to Lua coroutines.</p>
<p><strong>Contents</strong></p>
<h2 id="lua-objects">Lua Objects</h2>
<p>From the C level, Lua&rsquo;s state is a <code>lua_State</code>. Within the same Lua VM, multiple <code>lua_State</code>s share one <code>global_State</code>. The <code>lua_State</code> should not be viewed as a simple static data structure, but as a state machine in a Lua &ldquo;thread&rdquo;, containing information about the current &ldquo;thread&rsquo;s&rdquo; execution state, data stack, call stack, etc.</p>
<h2 id="lua-data-stack">Lua Data Stack</h2>
<p>Lua data can be divided into value types and reference types. The union <code>Value</code> in <code>lstate.h</code> is used to represent them.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#66d9ef">union</span> Value {
</span></span><span style="display:flex;"><span>    GCObject <span style="color:#f92672">*</span>gc;    <span style="color:#75715e">/* collectable objects */</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">void</span> <span style="color:#f92672">*</span>p;         <span style="color:#75715e">/* light userdata */</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">int</span> b;           <span style="color:#75715e">/* booleans */</span>
</span></span><span style="display:flex;"><span>    lua_CFunction f; <span style="color:#75715e">/* light C functions */</span>
</span></span><span style="display:flex;"><span>    numfield         <span style="color:#75715e">/* numbers */</span>
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>As you can see, reference types use a <code>GCObject</code> pointer for indirect reference, while other value types are stored directly in the union. To distinguish the types in the union, an additional type field is also needed.</p>]]></summary></entry><entry><title>Lua Source Code Reading (Part 5)</title><link href="https://www.ogura.io/posts/2018/01/lua_source_code_5/"/><id>https://www.ogura.io/posts/2018/01/lua_source_code_5/</id><published>20180115T23:17:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li>Lua Source Code Reading (Part 5)</li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
</blockquote>
<p>Chapter 5 of &ldquo;Lua Source Code Appreciation&rdquo; covers content related to Lua closures.</p>
<p><strong>Contents</strong></p>
<h2 id="lua-closure-concept">Lua Closure Concept</h2>
<p>In Lua, binding a set of data to a specific function is called a closure. The bound data are called upvalues, also known as non-local variables (as distinct from local and global variables).</p>
<h2 id="closure-implementation-and-classification">Closure Implementation and Classification</h2>
<p>Lua functions differ from C functions in that Lua functions can be dynamically generated during VM runtime - functions can be created within Lua functions. Binding a Lua function prototype with a set of upvalues - binding the <code>Proto</code> structure with <code>UpVal</code> together - is how closures are implemented in Lua.</p>
<p>Lua has two types of closures: Lua closures and C closures. Lua closures are more complex.</p>
<ol>
<li>Closures generated during VM runtime: Before a function exits, an index on the data stack is used to map local variables. At this point, the upval is called &ldquo;open&rdquo;. When the outer function returns and the data stack space shrinks, <code>luaF_close</code> is called to close it, and the previous pointer is redirected to a safe place for storage.</li>
<li>Closures generated from loading external source code (binary or text): Instead of using local variables on the data stack as upvalues, new upvalues are created. Additional note: When Lua loads external code, Lua code is compiled into function prototypes, but Lua&rsquo;s external calls don&rsquo;t return function prototypes - instead, they convert the function prototype into a closure.</li>
</ol>
<p>C closures are simpler than Lua closures. Since C closures don&rsquo;t reference external upvalues, upvalues are all in a closed state. Just bind the upvalues to the Closure structure. C closures also have a special case with no upvalues, called light C functions. Light functions don&rsquo;t need the UpVal structure and don&rsquo;t need GC management.</p>
<h2 id="c-tricks">C Tricks</h2>
<p>In Lua source code, the <code>UNUSED()</code> macro is used to avoid compiler unused variable warnings. The source code is:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#75715e">#define UNUSED(x) ((void)(x))
</span></span></span></code></pre></div><blockquote>
<p><a href="https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_closure_06">https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_closure_06</a></p>
</blockquote>
]]></content><summary type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li>Lua Source Code Reading (Part 5)</li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
</blockquote>
<p>Chapter 5 of &ldquo;Lua Source Code Appreciation&rdquo; covers content related to Lua closures.</p>
<p><strong>Contents</strong></p>
<h2 id="lua-closure-concept">Lua Closure Concept</h2>
<p>In Lua, binding a set of data to a specific function is called a closure. The bound data are called upvalues, also known as non-local variables (as distinct from local and global variables).</p>]]></summary></entry><entry><title>Lua Source Code Reading (Part 4)</title><link href="https://www.ogura.io/posts/2018/01/lua_source_code_4/"/><id>https://www.ogura.io/posts/2018/01/lua_source_code_4/</id><published>20180111T14:01:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li>Lua Source Code Reading (Part 4)</li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
</blockquote>
<p>Content from Chapter 4.2.2 to the end of Chapter 4 of &ldquo;Lua Source Code Appreciation&rdquo; still mainly covers source code related to Lua tables.</p>
<p><strong>Contents</strong></p>
<h2 id="hashing-of-numeric-types">Hashing of Numeric Types</h2>
<p>Regarding hashing of numeric types, several version iteration improvements were mentioned.</p>
<p>Lua 5.1 directly reads and adds data from corresponding memory blocks for hashing. This algorithm could produce a bug on 64-bit systems where the same number produces different hash results:</p>
<blockquote>
<p>On 64-bit systems, long double is considered 16 bytes instead of 12 bytes on 32-bit systems to maintain alignment. But
the value itself is still 12 bytes, with the other 4 bytes filled with random garbage data</p>
</blockquote>
<p>Lua 5.2 improved this algorithm with two user-configurable algorithms. One uses a union to calculate number hashes (conditions: number type is double, target machine uses IEEE574 standard floating point). The other has lower performance but uses a more general and standard hash algorithm.</p>
<h2 id="table-iteration-next">Table Iteration (next)</h2>
<p>Table iteration is frequently used in Lua. After carefully reading the source code, I understood the principle behind checking whether a table structure is empty that I had seen online.</p>
<p>First, let me explain Lua&rsquo;s definition of table length: if t[n] is non-empty and t[n + 1] is empty, then n is the table&rsquo;s length. Additionally, when the array part of the table is full or empty, the hash table part&rsquo;s length is also calculated.</p>
<p>Another point I hadn&rsquo;t noticed before: in other languages, any operations on a table are prohibited during iteration. But in Lua, during table iteration, you can modify or even delete existing values (assign to nil) without affecting the iteration result.</p>
<p>When deleting elements from a table being iterated, if GC happens to occur, key-value pairs set to nil will be marked as dead, and the <code>findindex</code> function handles this situation.
If you create new elements in a table being iterated, the new key might not be traversed, or if the creation triggers a <code>rehash</code> action, it may cause duplicate traversal issues.</p>
<p>Lua&rsquo;s <code>next(table [,index])</code> function accepts two parameters. I always use it like this to check if a table is empty:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#66d9ef">local</span> tal_empty <span style="color:#f92672">=</span> next(t)
</span></span></code></pre></div><h2 id="metamethod-optimization">Metamethod Optimization</h2>
<blockquote>
<p>Lua&rsquo;s implementation of complex data structures heavily relies on attaching a metatable to a table</p>
</blockquote>
<p>Therefore, operations on metatables are hotspots in Lua, and the necessity of optimizing hotspots goes without saying.</p>
<p>From the moment Lua creates a table, these metamethods are directly attached to the Lua table, and using the <code>flags</code> bits in the <code>Table</code> structure to mark metamethods can greatly improve metamethod query speed. In actual queries, you can directly query the corresponding metamethod using the corresponding bits.
When Lua creates metatables, I noticed that Lua uses <code>luaS_fix</code> to mark data structures in the state machine. When marked as <code>FIXEDBIT</code>, the structure won&rsquo;t be garbage collected.</p>
<blockquote>
<p><a href="https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_table_metatable_04">https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_table_metatable_04</a></p>
</blockquote>
]]></content><summary type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li>Lua Source Code Reading (Part 4)</li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
</blockquote>
<p>Content from Chapter 4.2.2 to the end of Chapter 4 of &ldquo;Lua Source Code Appreciation&rdquo; still mainly covers source code related to Lua tables.</p>
<p><strong>Contents</strong></p>
<h2 id="hashing-of-numeric-types">Hashing of Numeric Types</h2>
<p>Regarding hashing of numeric types, several version iteration improvements were mentioned.</p>
<p>Lua 5.1 directly reads and adds data from corresponding memory blocks for hashing. This algorithm could produce a bug on 64-bit systems where the same number produces different hash results:</p>
<blockquote>
<p>On 64-bit systems, long double is considered 16 bytes instead of 12 bytes on 32-bit systems to maintain alignment. But
the value itself is still 12 bytes, with the other 4 bytes filled with random garbage data</p>
</blockquote>
<p>Lua 5.2 improved this algorithm with two user-configurable algorithms. One uses a union to calculate number hashes (conditions: number type is double, target machine uses IEEE574 standard floating point). The other has lower performance but uses a more general and standard hash algorithm.</p>]]></summary></entry><entry><title>Lua Source Code Reading (Part 3)</title><link href="https://www.ogura.io/posts/2018/01/lua_source_code_3/"/><id>https://www.ogura.io/posts/2018/01/lua_source_code_3/</id><published>20180109T19:36:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <blockquote>
</blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li>Lua Source Code Reading (Part 3)</li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
<p>Content from Chapter 4 to 4.2.1 of &ldquo;Lua Source Code Appreciation&rdquo; mainly covers source code related to Lua tables.</p>
<p><strong>Contents</strong></p>
<h2 id="lua-table">Lua Table</h2>
<p>A Lua table structure consists of at most three contiguous memory blocks: a Table structure, an array storing consecutive integer indices, and a hash table with size as a power of 2. The size of each part is reallocated by the <code>computesize</code> function when calling the <code>rehash</code> function, ensuring utilization is above 50%.
The hash table uses closed hashing for collision resolution. When a collision occurs, the two colliding keys are linked together with a singly linked list.</p>
<blockquote>
<p><a href="https://www.jianshu.com/p/f9239c9377c5">Hash Algorithm Details</a></p>
</blockquote>
<h2 id="hash">Hash</h2>
<p>During table queries, lazy hash computation for long strings is also encountered. Yun Feng explains the reason for this optimization in his book:</p>
<blockquote>
<p>In most applications, long strings are objects for text processing rather than comparison operations, and internal uniquification would bring additional overhead</p>
</blockquote>
<p>However, string optimization was only added after Lua 5.2.0. Whether this is also present in the LuaJIT that comes with OpenResty is still a question mark.</p>
<blockquote>
<p><a href="https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_table_04">https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_table_04</a></p>
</blockquote>
]]></content><summary type="html"><![CDATA[ <blockquote>
</blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li>Lua Source Code Reading (Part 3)</li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
<p>Content from Chapter 4 to 4.2.1 of &ldquo;Lua Source Code Appreciation&rdquo; mainly covers source code related to Lua tables.</p>
<p><strong>Contents</strong></p>
<h2 id="lua-table">Lua Table</h2>
<p>A Lua table structure consists of at most three contiguous memory blocks: a Table structure, an array storing consecutive integer indices, and a hash table with size as a power of 2. The size of each part is reallocated by the <code>computesize</code> function when calling the <code>rehash</code> function, ensuring utilization is above 50%.
The hash table uses closed hashing for collision resolution. When a collision occurs, the two colliding keys are linked together with a singly linked list.</p>
<blockquote>
<p><a href="https://www.jianshu.com/p/f9239c9377c5">Hash Algorithm Details</a></p>
</blockquote>]]></summary></entry><entry><title>Vienna Development Method</title><link href="https://www.ogura.io/posts/2017/12/vienna_development_method/"/><id>https://www.ogura.io/posts/2017/12/vienna_development_method/</id><published>20171221T22:13:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>In &ldquo;Lua Source Code Appreciation&rdquo;, it&rsquo;s mentioned that Lua&rsquo;s author Roberto drew partial inspiration from VDM when designing Lua, yet there&rsquo;s very little Chinese documentation about VDM. Here I provide a simple Chinese version as a memo.</p>
<blockquote>
<p>Wiki original <a href="https://en.wikipedia.org/wiki/Vienna_Development_Method">https://en.wikipedia.org/wiki/Vienna_Development_Method</a></p>
</blockquote>
<p>The Vienna Development Method (VDM) is one of the longest-established formal methods<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> for the development of computer systems. Its origins were in work done at the IBM Vienna Laboratory in the 1970s. VDM has since evolved into a family of techniques and tools based on a formal specification language—the VDM Specification Language (VDM-SL). VDM also has an extended form: VDM++, which supports object-oriented and concurrent systems. Support for VDM includes tools for analyzing models in commercial and academic contexts, as well as testing and verification of models and code generation from VDM models. VDM has a history of industrial applications and research that continues to evolve, making notable contributions to the engineering of critical systems, compilers, concurrent systems, and logic in computer science.</p>
<h1 id="philosophy">Philosophy</h1>
<p>In VDM-SL, computing systems can be modeled at a higher level of abstraction than programming language implementations, allowing analysis of designs and identification of key characteristics and flaws in the early stages of system development. Validated models can be transformed into detailed system designs through a refinement process. The language has a formal semantics, allowing properties of models to be proved at higher levels of abstraction. It also has an executable subset, enabling models to be analyzed through testing and displayed through graphical interfaces so that models can be evaluated by domain experts who are not familiar with the modeling language itself.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://en.wikipedia.org/wiki/Formal_methods">https://en.wikipedia.org/wiki/Formal_methods</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>In &ldquo;Lua Source Code Appreciation&rdquo;, it&rsquo;s mentioned that Lua&rsquo;s author Roberto drew partial inspiration from VDM when designing Lua, yet there&rsquo;s very little Chinese documentation about VDM. Here I provide a simple Chinese version as a memo.</p>
<blockquote>
<p>Wiki original <a href="https://en.wikipedia.org/wiki/Vienna_Development_Method">https://en.wikipedia.org/wiki/Vienna_Development_Method</a></p>
</blockquote>
<p>The Vienna Development Method (VDM) is one of the longest-established formal methods<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> for the development of computer systems. Its origins were in work done at the IBM Vienna Laboratory in the 1970s. VDM has since evolved into a family of techniques and tools based on a formal specification language—the VDM Specification Language (VDM-SL). VDM also has an extended form: VDM++, which supports object-oriented and concurrent systems. Support for VDM includes tools for analyzing models in commercial and academic contexts, as well as testing and verification of models and code generation from VDM models. VDM has a history of industrial applications and research that continues to evolve, making notable contributions to the engineering of critical systems, compilers, concurrent systems, and logic in computer science.</p>]]></summary></entry><entry><title>Misuse of MVC Architecture in Production</title><link href="https://www.ogura.io/posts/2017/12/mvc_abuse_in_production/"/><id>https://www.ogura.io/posts/2017/12/mvc_abuse_in_production/</id><published>20171221T11:33:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>I have an external-facing API that uses the Mojolicious framework, mainly implementing some general and customer-customized requirements. As requirements kept increasing, it became bloated and unwieldy. Here I&rsquo;ll analyze it as a negative example.
The general design structure is as follows:</p>
<pre tabindex="0"><code class="language-flow" data-lang="flow">api=&gt;start: API
route=&gt;condition: Route
common_route=&gt;subroutine: Common Route
custom_route=&gt;subroutine: Custom Route
common_controller=&gt;condition: Common Controller
custom_controller=&gt;subroutine: Custom Controller
hacked_code=&gt;operation: hacked code
hacked_middleware=&gt;operation: hacked middleware
common_io=&gt;inputoutput: Common io/db process
hacked_user_io=&gt;inputoutput: Hacked io/db process
render=&gt;end: Render

api-&gt;route(no)-&gt;custom_route-&gt;custom_controller-&gt;render
api-&gt;route(yes)-&gt;common_route-&gt;common_controller-&gt;common_io-&gt;render
api-&gt;route(yes)-&gt;common_route-&gt;common_controller(no)-&gt;hacked_code-&gt;hacked_user_io-&gt;render
api-&gt;route(yes)-&gt;common_route-&gt;common_controller(yes)-&gt;hacked_middleware-&gt;hacked_user_io-&gt;render
</code></pre><blockquote>
<p>I haven&rsquo;t drawn the Model part of this structure here, because it&rsquo;s also a mess.</p>
</blockquote>
<p>Let me briefly explain this misused structure.</p>
<h2 id="ideal">Ideal</h2>
<p>Routes are mainly divided into two parts: one for common feature API routes, and one for customer-customized API routes. This is the ideal situation: when customers don&rsquo;t have customization needs, they just use the common route&rsquo;s API; when customization is needed, new paths are added separately in the custom routes.</p>
<h2 id="reality">Reality</h2>
<p>In practice, I found this isn&rsquo;t really a universal solution. Because some customers using the common API sometimes have customization needs, such as:</p>
<ul>
<li>During POST operations, instead of calling the common random function to generate strings, use customer-customized rules to generate strings before database operations. So I added ugly uid judgment logic in the code - if conditions are met, perform operation A, otherwise perform the common operation. After multiple similar code hacks, I added a middleware for uid judgment, but it&rsquo;s still ugly.</li>
<li>In the <code>Common Route</code> section, there&rsquo;s an authentication middleware to ensure legitimacy. Here again, there are customer-customized authentication schemes to implement.</li>
<li>In customer-customized features, there might be new data to store, so I would create new tables or fields in the <code>Model</code>, but these customized fields can&rsquo;t have good access control.</li>
</ul>
<p>In summary, in this architecture, some modules or structures were fixed at design time, but in actual use, almost every module is required to be customizable and replaceable (at the user level).</p>
<h2 id="solution-to-be-continued">Solution (To be continued)</h2>
]]></content><summary type="html"> &lt;p>I have an external-facing API that uses the Mojolicious framework, mainly implementing some general and customer-customized requirements. As requirements kept increasing, it became bloated and unwieldy. Here I&amp;rsquo;ll analyze it as a negative example.
The general design structure is as follows:&lt;/p></summary></entry><entry><title>Lua Source Code Reading (Part 2)</title><link href="https://www.ogura.io/posts/2017/12/lua_source_code_2/"/><id>https://www.ogura.io/posts/2017/12/lua_source_code_2/</id><published>20171216T21:41:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li>Lua Source Code Reading (Part 2)</li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
</blockquote>
<p>After reading Yun Feng&rsquo;s section on Lua strings, I have a corresponding understanding of Lua&rsquo;s string handling.</p>
<p><strong>Contents</strong></p>
<h2 id="string">String</h2>
<p>Lua strings are internally divided into long strings and short strings. This classification is transparent at the Lua level and is likely just an optimization within the Lua interpreter.</p>
<p>Addendum<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>: The maximum length of short strings is determined by the <code>LUAI_MAXSHORTLEN</code> macro. Long strings call <code>createstrobj</code> to create.</p>
<p>Long and short strings are handled differently during string creation and comparison.</p>
<p>When creating strings, short strings are directly internalized, and the <code>extra</code> bit in the <code>TString</code> structure marks whether it&rsquo;s an internal reserved field. When creating long strings, memory is directly copied, and the <code>extra</code> bit is marked for lazy hashing during comparison or internalization.</p>
<p>When comparing strings, short strings directly compare pointer addresses. Long strings perform character-by-character comparison.</p>
<p>Addendum<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>: The long/short string optimization was added starting from Lua 5.2.1. Before that, all strings were stored in a global hash table.</p>
<p>Addendum<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>: When concatenating strings, using <code>..</code> is inefficient as it involves memory allocation and memory copying. It&rsquo;s recommended to use <code>table.concat</code><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> or <code>string.format</code><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>.</p>
<h2 id="userdata">userdata</h2>
<p>Lua also has a <code>UData</code> structure called userdata. This data structure is mainly used when Lua interacts with C and C++, handing C data structures to Lua&rsquo;s GC for management.
Specifically, in C you can use <code>lua_newuserdata()</code> which creates a data structure in Lua and returns a pointer. This is similar to calling <code>malloc</code>, but the difference is you don&rsquo;t need to manually call <code>free</code> to release memory - just leave it to Lua&rsquo;s GC.
A good example is Lua&rsquo;s <code>io</code> library, which puts the C data structure <code>FILE *</code> into Lua&rsquo;s userdata. By implementing a <code>__gc</code> metamethod, file handles can be automatically closed during GC.</p>
<blockquote>
<p><a href="https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_string_02">https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_string_02</a></p>
</blockquote>
<p>References:</p>
<hr>
<p><a href="https://blog.csdn.net/MaximusZhou/article/details/44786515">Source Code Implementation of String Type in Lua</a></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Added 2018-11-01&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Added 2018-11-01&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Added 2018-11-01&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><code>table.concat</code> also uses the <code>..</code> operator at the bottom layer for string concatenation, but it uses an algorithm to reduce the number of <code>..</code> operations, reducing GC and thus improving efficiency. Main idea: Using a binary approach with a stack to store strings. Newly pushed strings are compared in length with strings below. If longer, use the <code>..</code> operator to concatenate into a new string and remove the top string, continuing downward until encountering a longer string or reaching the stack bottom. This keeps the longest string at the bottom, making the stack pyramid-shaped, and finally uses the <code>..</code> operator to concatenate all strings in the stack into the final string.
Author: AaronChanFighting
Source: CSDN
Original: <a href="https://blog.csdn.net/qq_26958473/article/details/79392222">https://blog.csdn.net/qq_26958473/article/details/79392222</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Each <code>%s</code> placeholder in <code>string.format</code> has a 512 character limit&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li>Lua Source Code Reading (Part 2)</li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
</blockquote>
<p>After reading Yun Feng&rsquo;s section on Lua strings, I have a corresponding understanding of Lua&rsquo;s string handling.</p>
<p><strong>Contents</strong></p>
<h2 id="string">String</h2>
<p>Lua strings are internally divided into long strings and short strings. This classification is transparent at the Lua level and is likely just an optimization within the Lua interpreter.</p>]]></summary></entry><entry><title>Introduction to Hash Algorithms</title><link href="https://www.ogura.io/posts/2017/12/hashing/"/><id>https://www.ogura.io/posts/2017/12/hashing/</id><published>20171216T20:16:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>While reading the Lua source code, I learned that short string internalization is implemented using a hash table with <code>open hashing</code>, while the hash part of Lua tables uses closed hashing.
Both open hashing and closed hashing are implementation methods for hash tables, with the following characteristics:</p>
<ul>
<li>Open hash table: Uses linked list storage, doesn&rsquo;t produce clustering, but increases space overhead due to additional pointer fields.</li>
<li>Closed hash table: Uses sequential storage, more storage-efficient, but prone to clustering, harder to implement lookups, requires secondary probing.</li>
<li>Overflow table: A combination of open and closed hashing - the first sequential table stores pointer-like fields, the second stores overflow.</li>
</ul>
<p>For detailed descriptions of specific algorithms for open hashing, closed hashing, etc., please refer to the original article.</p>
<p>Original article: <a href="http://blog.csdn.net/u014613043/article/details/50726630">http://blog.csdn.net/u014613043/article/details/50726630</a></p>
]]></content><summary type="html"><![CDATA[ <p>While reading the Lua source code, I learned that short string internalization is implemented using a hash table with <code>open hashing</code>, while the hash part of Lua tables uses closed hashing.
Both open hashing and closed hashing are implementation methods for hash tables, with the following characteristics:</p>
<ul>
<li>Open hash table: Uses linked list storage, doesn&rsquo;t produce clustering, but increases space overhead due to additional pointer fields.</li>
<li>Closed hash table: Uses sequential storage, more storage-efficient, but prone to clustering, harder to implement lookups, requires secondary probing.</li>
<li>Overflow table: A combination of open and closed hashing - the first sequential table stores pointer-like fields, the second stores overflow.</li>
</ul>]]></summary></entry><entry><title>Consistent Hash Implementation in Perl</title><link href="https://www.ogura.io/posts/2017/12/perl_balancer/"/><id>https://www.ogura.io/posts/2017/12/perl_balancer/</id><published>20171216T19:08:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>OpenResty has consistent hash implementations in Lua and C that produce the same results as nginx, used to achieve consistent hash results identical to nginx within OpenResty.
However, there was no such module in Perl, so I referenced the Lua and C code (basically pixel-level copying) and implemented consistent hash in Perl with identical results (test cases verified).</p>
<p>Project at: <a href="https://github.com/xiaocang/perl-balancer">https://github.com/xiaocang/perl-balancer</a></p>
<p>TODO: No XS version implementation yet</p>
]]></content><summary type="html"><![CDATA[ <p>OpenResty has consistent hash implementations in Lua and C that produce the same results as nginx, used to achieve consistent hash results identical to nginx within OpenResty.
However, there was no such module in Perl, so I referenced the Lua and C code (basically pixel-level copying) and implemented consistent hash in Perl with identical results (test cases verified).</p>
<p>Project at: <a href="https://github.com/xiaocang/perl-balancer">https://github.com/xiaocang/perl-balancer</a></p>
<p>TODO: No XS version implementation yet</p>]]></summary></entry><entry><title>Laravel Testing Framework</title><link href="https://www.ogura.io/posts/2017/12/laravel_5.5_testing/"/><id>https://www.ogura.io/posts/2017/12/laravel_5.5_testing/</id><published>20171216T13:06:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>This is based on Laravel 5.5.</p>
<p>The <code>phpUnit</code> documentation is at <a href="https://phpunit.de/manual/current/en/index.html">https://phpunit.de/manual/current/en/index.html</a></p>
<h2 id="create">create</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e"># Create a test case</span>
</span></span><span style="display:flex;"><span>php artisan make:test XXControllerTest
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Create a unit test</span>
</span></span><span style="display:flex;"><span>php artisan make:test XXUnitTest --unit
</span></span></code></pre></div><h2 id="usage">usage</h2>
<h3 id="http">HTTP</h3>
<ul>
<li>json</li>
<li>get</li>
<li>post</li>
<li>put</li>
<li>delete</li>
</ul>
<p>ex:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">testHttpSample</span>()
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">array</span> $json <span style="color:#f92672">=</span> [];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">json</span>(<span style="color:#e6db74">&#34;PUT&#34;</span>, $url, $json)
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">assertExactJson</span>([<span style="color:#e6db74">&#39;errno&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">0</span>]);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p><a href="https://laravel.com/docs/5.5/http-tests#available-assertions">Available Assertions</a></p>
</blockquote>
<h3 id="database">Database</h3>
<p>The database section is very detailed in the official documentation.
The main assertions check whether a certain entry exists in the database, whether it doesn&rsquo;t exist, and soft delete checks.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">testDbSample</span>()
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">assertDatabaseHas</span>(<span style="color:#e6db74">&#39;sample_table&#39;</span>, [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;id&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;100&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;data&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;sample&#39;</span>
</span></span><span style="display:flex;"><span>    ]);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p><a href="https://laravel.com/docs/5.5/database-testing#available-assertions">Available Assertions</a></p>
</blockquote>
<h3 id="mocking">Mocking</h3>
<p>The official documentation only provides a few examples in this section. In practical applications, I have more useful usages, such as the Log object:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">testLogSample</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// This method can be used to simulate that after an error is triggered,
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// the error log printed in the code matches expectations,
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// and from this we can infer whether the expected error was triggered
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Log</span><span style="color:#f92672">::</span><span style="color:#a6e22e">shouldRecive</span>(<span style="color:#e6db74">&#39;error&#39;</span>)<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">once</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">with</span>(<span style="color:#e6db74">&#39;error happened&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// run test...
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="guzzle">Guzzle</h3>
<p>Guzzle, as an important module for making HTTP sub-requests, should also be covered by test cases in Laravel.
Guzzle provides its own Mock method:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">testGuzzleSample</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    $mockResponses <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>        [
</span></span><span style="display:flex;"><span>            <span style="color:#ae81ff">200</span>, <span style="color:#75715e">// code
</span></span></span><span style="display:flex;"><span>            [<span style="color:#e6db74">&#39;Content-Type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;application/json&#39;</span>], <span style="color:#75715e">// headers
</span></span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;{&#34;errno&#34;: 0}&#39;</span> <span style="color:#75715e">// body
</span></span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>        [
</span></span><span style="display:flex;"><span>            <span style="color:#ae81ff">500</span>, <span style="color:#75715e">// code
</span></span></span><span style="display:flex;"><span>            [<span style="color:#e6db74">&#39;Content-Type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;application/json&#39;</span>], <span style="color:#75715e">// headers
</span></span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;{&#34;errno&#34;: 99}&#39;</span> <span style="color:#75715e">// body
</span></span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>    $mockHandler <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Client</span>([
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;handler&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">HandlerStack</span><span style="color:#f92672">::</span><span style="color:#a6e22e">create</span>(
</span></span><span style="display:flex;"><span>            (<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MockHandler</span>($mockResponses))
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    ]);
</span></span><span style="display:flex;"><span>    $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">app</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">instance</span>(<span style="color:#e6db74">&#39;GuzzleHttp\Client&#39;</span>, $mockHandler);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// run tests...
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// During testing, guzzle will return the predefined responses in order to test expected cases.
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Extended usage: To specifically test the input and output of sub-requests, you can write a simple MVC class that takes the expected sub-request input (url, method, header, body) to dynamically generate the <code>$mockResponses</code> array in the example.</p>
<h2 id="env">env</h2>
<p>In the Laravel project directory, there&rsquo;s a <code>phpunit.xml</code> file where you can specify some environment variables to be used during test runs.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#75715e">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;phpunit</span> <span style="color:#a6e22e">backupGlobals=</span><span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">backupStaticAttributes=</span><span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">bootstrap=</span><span style="color:#e6db74">&#34;bootstrap/autoload.php&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">colors=</span><span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">convertErrorsToExceptions=</span><span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">convertNoticesToExceptions=</span><span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">convertWarningsToExceptions=</span><span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">processIsolation=</span><span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">stopOnFailure=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;testsuites&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;testsuite</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Feature&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;directory</span> <span style="color:#a6e22e">suffix=</span><span style="color:#e6db74">&#34;Test.php&#34;</span><span style="color:#f92672">&gt;</span>./tests/Feature<span style="color:#f92672">&lt;/directory&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/testsuite&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;testsuite</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Unit&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;directory</span> <span style="color:#a6e22e">suffix=</span><span style="color:#e6db74">&#34;Test.php&#34;</span><span style="color:#f92672">&gt;</span>./tests/Unit<span style="color:#f92672">&lt;/directory&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/testsuite&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/testsuites&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;filter&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;whitelist</span> <span style="color:#a6e22e">processUncoveredFilesFromWhitelist=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;directory</span> <span style="color:#a6e22e">suffix=</span><span style="color:#e6db74">&#34;.php&#34;</span><span style="color:#f92672">&gt;</span>./app<span style="color:#f92672">&lt;/directory&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/whitelist&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/filter&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;php&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;env</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;APP_ENV&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;testing&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;env</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;CACHE_DRIVER&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;array&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;env</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;SESSION_DRIVER&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;array&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;env</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;QUEUE_DRIVER&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;sync&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;env</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;DB_HOST&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;127.0.0.1&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;env</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;DB_PORT&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;3306&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;env</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;DB_DATABASE&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;lavaral_test&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/php&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/phpunit&gt;</span>
</span></span></code></pre></div><p>I forced the specification of the database address and name used during testing in the project to prevent someone from running this script in production with the .env file settings and operating on the production database.</p>
<p>Of course, the official configuration documentation mentions that during test runs, if a <code>.env.testing</code> file exists, when using the <code>--env=testing</code> parameter, <code>.env.testing</code> will override the <code>.env</code> file.</p>
<h2 id="run">run</h2>
<p>Running test cases is very simple - just execute the <code>phpunit</code> file in the project directory.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./vendor/bin/phpunit
</span></span></code></pre></div><p><code>phpunit</code> also has some <a href="https://phpunit.de/manual/current/en/textui.html#textui.clioptions">options</a> available. The one I mainly use is <code>--filter</code>, to execute a specific test case or batch of test cases.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./vendor/bin/phpunit --filter<span style="color:#f92672">=</span>testHttp
</span></span></code></pre></div>]]></content><summary type="html"><![CDATA[ <p>This is based on Laravel 5.5.</p>
<p>The <code>phpUnit</code> documentation is at <a href="https://phpunit.de/manual/current/en/index.html">https://phpunit.de/manual/current/en/index.html</a></p>]]></summary></entry><entry><title>Lua Source Code Reading (Part 1)</title><link href="https://www.ogura.io/posts/2017/12/lua_source_code_1/"/><id>https://www.ogura.io/posts/2017/12/lua_source_code_1/</id><published>20171216T13:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <blockquote>
</blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li>Lua Source Code Reading (Part 1)</li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
<p><strong>Contents</strong></p>
<h2 id="memory-management">Memory Management</h2>
<p>I just finished reading Yun Feng&rsquo;s understanding of Lua&rsquo;s memory management section, and I&rsquo;ve also read and commented on the corresponding code myself.
The main characteristics of Lua&rsquo;s memory management are:</p>
<ol>
<li>When the Lua VM initializes, it saves the main thread and global state on the first allocated memory block, which helps avoid memory fragmentation later</li>
<li>Lua uses wrapped memory management functions that always have the exact original memory size when allocating, resizing, or freeing memory, thus saving memory that would otherwise be used for cookies like in the standard library</li>
<li>Lua has wrapped memory management functions that can handle variable-length arrays, providing flexibility for data structures in the Lua language</li>
</ol>
<blockquote>
<p><a href="https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lmem_01">https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lmem_01</a></p>
</blockquote>
<h2 id="initialization">Initialization</h2>
<p>The global state section involves many subsequent parts like gc, string, table, etc., so I can only understand a little bit.
Key points to understand:</p>
<ul>
<li>During Lua initialization, the main thread&rsquo;s data stack is created differently from other threads&rsquo; data stacks</li>
<li>The buff in the global state is used to handle string operations in Lua</li>
<li>Lua implements a version check function to prevent multiple loading issues, which also shows that Lua was designed as an embedded language from the beginning</li>
<li>For VM robustness, Lua needs to check whether memory allocation succeeds each time</li>
</ul>
<blockquote>
<p><a href="https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/global_state_02">https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/global_state_02</a></p>
</blockquote>
]]></content><summary type="html"><![CDATA[ <blockquote>
</blockquote>
<p>Lua source code series:</p>
<ul>
<li><a href="/posts/2017/12/lua_source_code_plan/">Lua Source Code Reading Plan</a></li>
<li>Lua Source Code Reading (Part 1)</li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
<p><strong>Contents</strong></p>
<h2 id="memory-management">Memory Management</h2>
<p>I just finished reading Yun Feng&rsquo;s understanding of Lua&rsquo;s memory management section, and I&rsquo;ve also read and commented on the corresponding code myself.
The main characteristics of Lua&rsquo;s memory management are:</p>
<ol>
<li>When the Lua VM initializes, it saves the main thread and global state on the first allocated memory block, which helps avoid memory fragmentation later</li>
<li>Lua uses wrapped memory management functions that always have the exact original memory size when allocating, resizing, or freeing memory, thus saving memory that would otherwise be used for cookies like in the standard library</li>
<li>Lua has wrapped memory management functions that can handle variable-length arrays, providing flexibility for data structures in the Lua language</li>
</ol>]]></summary></entry><entry><title>REST API Design Style</title><link href="https://www.ogura.io/posts/2017/12/rest_http_api/"/><id>https://www.ogura.io/posts/2017/12/rest_http_api/</id><published>20171215T16:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Regarding HTTP REST-style API design, I currently use two styles:</p>
<ol>
<li>Write separate GET/POST/PUT/DELETE endpoints for operations on each type of object, then add four corresponding endpoints for each new object type.</li>
<li>Have unified GET/POST/PUT/DELETE endpoints for all objects. If objects are related, use glue code to wrap these four endpoints for customized functionality.</li>
</ol>
<p>Both design approaches are close to extremes. The Type 1 API that I mainly work on has a terrifying amount of work - I have to make thousands of lines of changes for each new requirement. This is &ldquo;terrifying&rdquo; level workload for me, while the API implementation looks simple and non-technical to others, and the deadlines are extremely tight.</p>
<p>So I started looking for a middle ground between these two API approaches. (To be continued)</p>
]]></content><summary type="html"><![CDATA[ <p>Regarding HTTP REST-style API design, I currently use two styles:</p>
<ol>
<li>Write separate GET/POST/PUT/DELETE endpoints for operations on each type of object, then add four corresponding endpoints for each new object type.</li>
<li>Have unified GET/POST/PUT/DELETE endpoints for all objects. If objects are related, use glue code to wrap these four endpoints for customized functionality.</li>
</ol>
<p>Both design approaches are close to extremes. The Type 1 API that I mainly work on has a terrifying amount of work - I have to make thousands of lines of changes for each new requirement. This is &ldquo;terrifying&rdquo; level workload for me, while the API implementation looks simple and non-technical to others, and the deadlines are extremely tight.</p>]]></summary></entry><entry><title>Lua Source Code Reading Plan</title><link href="https://www.ogura.io/posts/2017/12/lua_source_code_plan/"/><id>https://www.ogura.io/posts/2017/12/lua_source_code_plan/</id><published>20171215T15:36:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li>Lua Source Code Reading Plan</li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
</blockquote>
<p>Using Lua in OpenResty projects is both enjoyable and frustrating. Lua&rsquo;s minimalist design allowed me to build a CDN control system in just a few days two years ago, but problems emerged as the business system gradually took shape. Now I have two solutions before me:</p>
<ol>
<li>Use a state machine - implement the core part of the framework with a state machine, changing the previous control flow into individual states</li>
<li>Use sandboxing - abstract new phases. Use preset sandbox environments to do specified things in each phase.</li>
</ol>
<p>If using a state machine, it might greatly complicate the core part of the system, making it unmaintainable later. For sandboxing, the problem is that I only half-understand Lua&rsquo;s underlying implementation. After seeing Yun Feng&rsquo;s blog, I decided to take time to read through Lua&rsquo;s source code to deepen my understanding of Lua. During the reading, I also hope to come up with an ideal Lua architecture for OpenResty projects.</p>
<p>Here&rsquo;s Yun Feng&rsquo;s <a href="https://www.codingnow.com/temp/readinglua.pdf">Lua Source Code Appreciation</a></p>
<p>I&rsquo;ll update here with some thoughts and knowledge gained after reading the Lua source code.</p>
<p>This is my project on GitHub with Chinese comments added to the lua-5.2.2 source code: <a href="https://github.com/xiaocang/lua-5.2.2_with_comments">https://github.com/xiaocang/lua-5.2.2_with_comments</a></p>
]]></content><summary type="html"><![CDATA[ <blockquote>
<p>Lua source code series:</p>
<ul>
<li>Lua Source Code Reading Plan</li>
<li><a href="/posts/2017/12/lua_source_code_1/">Lua Source Code Reading (Part 1)</a></li>
<li><a href="/posts/2017/12/lua_source_code_2/">Lua Source Code Reading (Part 2)</a></li>
<li><a href="/posts/2018/01/lua_source_code_3/">Lua Source Code Reading (Part 3)</a></li>
<li><a href="/posts/2018/01/lua_source_code_4/">Lua Source Code Reading (Part 4)</a></li>
<li><a href="/posts/2018/01/lua_source_code_5/">Lua Source Code Reading (Part 5)</a></li>
<li><a href="/posts/2018/01/lua_source_code_6/">Lua Source Code Reading (Part 6)</a></li>
</ul>
</blockquote>
<p>Using Lua in OpenResty projects is both enjoyable and frustrating. Lua&rsquo;s minimalist design allowed me to build a CDN control system in just a few days two years ago, but problems emerged as the business system gradually took shape. Now I have two solutions before me:</p>
<ol>
<li>Use a state machine - implement the core part of the framework with a state machine, changing the previous control flow into individual states</li>
<li>Use sandboxing - abstract new phases. Use preset sandbox environments to do specified things in each phase.</li>
</ol>
<p>If using a state machine, it might greatly complicate the core part of the system, making it unmaintainable later. For sandboxing, the problem is that I only half-understand Lua&rsquo;s underlying implementation. After seeing Yun Feng&rsquo;s blog, I decided to take time to read through Lua&rsquo;s source code to deepen my understanding of Lua. During the reading, I also hope to come up with an ideal Lua architecture for OpenResty projects.</p>]]></summary></entry><entry><title>addcp Existence Confirmed</title><link href="https://www.ogura.io/posts/2014/12/domain-name-do-not-update/"/><id>https://www.ogura.io/posts/2014/12/domain-name-do-not-update/</id><published>20141214T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>This year I dutifully renewed the <code>addcp.com</code> domain<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and didn&rsquo;t splurge on other domains. So this blog won&rsquo;t need to migrate and won&rsquo;t die<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. -w-</p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>After this renewal expired, I didn&rsquo;t renew it again&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>A flag that was collected (i.e., this prediction turned out to be wrong)&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>This year I dutifully renewed the <code>addcp.com</code> domain<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and didn&rsquo;t splurge on other domains. So this blog won&rsquo;t need to migrate and won&rsquo;t die<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. -w-</p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>After this renewal expired, I didn&rsquo;t renew it again&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>A flag that was collected (i.e., this prediction turned out to be wrong)&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></summary></entry><entry><title>x264 Segmented Encoding Option to Prevent Artifacts</title><link href="https://www.ogura.io/posts/2014/11/x264-segmented-encoding/"/><id>https://www.ogura.io/posts/2014/11/x264-segmented-encoding/</id><published>20141110T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Recently I&rsquo;ve been using x264&rsquo;s 2-pass encoding to perform segmented encoding on the same file, then merging the segments. This enables multi-process transcoding and even distributed cluster transcoding to speed up the encoding process.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mencoder -really-quiet <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /root/video/2159034.mp4 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ss <span style="color:#ae81ff">0</span> -endpos <span style="color:#ae81ff">60</span> -of rawvideo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ovc raw -nosound  -ofps <span style="color:#ae81ff">23</span>  <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add scale<span style="color:#f92672">=</span>404:720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add format<span style="color:#f92672">=</span>i420 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add hqdn3d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add harddup <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o - 2&gt;/dev/null | x264 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-progress --preset slower <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --tune film --weightp <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --keyint <span style="color:#ae81ff">250</span> --min-keyint <span style="color:#ae81ff">25</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --non-deterministic --bframes <span style="color:#ae81ff">3</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-fast-pskip --qcomp 0.6 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --merange <span style="color:#ae81ff">24</span> --threads auto <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-csp i420  --level 3.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --profile high --crf <span style="color:#ae81ff">20</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-res 404x720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --fps <span style="color:#ae81ff">23</span> - --pass <span style="color:#ae81ff">1</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --stats <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /tmp/video/1415072454_part1/3000/part_12159034/1415072454.passLog <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o /dev/null
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$ mencoder -really-quiet <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /root/video/2159034.mp4 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ss <span style="color:#ae81ff">0</span> -endpos <span style="color:#ae81ff">60</span> -of rawvideo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ovc raw -nosound  -ofps <span style="color:#ae81ff">23</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add scale<span style="color:#f92672">=</span>404:720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add format<span style="color:#f92672">=</span>i420 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add hqdn3d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add harddup <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o - 2&gt;/dev/null |x264 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-progress --preset slower <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --tune film --weightp <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --keyint <span style="color:#ae81ff">250</span> --min-keyint <span style="color:#ae81ff">25</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --non-deterministic --bframes <span style="color:#ae81ff">3</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-fast-pskip --qcomp 0.6 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --merange <span style="color:#ae81ff">24</span> --threads auto <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-csp i420  --level 3.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --profile high -B <span style="color:#ae81ff">3000</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-res 404x720 --fps <span style="color:#ae81ff">23</span> - --pass <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --stats /tmp/video/1415072454_part1/3000/part_12159034/1415072454.passLog <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o part1.264
</span></span></code></pre></div><p>However, I encountered a problem during transcoding. The individual <code>.264</code> output files with MP4 container could play normally. But after merging the <code>.264</code> files into one, seeking would cause visual artifacts.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ MP4Box -add part1.264#video -cat part2.264 -fps 29.97 -add video.m4a#audio -new result.mp4
</span></span></code></pre></div><p>After re-transcoding multiple times with the same result, it became clear that simply appending files was causing the artifacts.</p>
<p>After Googling, I finally discovered that x264 by default optimizes the header of the output <code>.264</code> file, which causes errors when merging <code>.264</code> files. Simply adding the <code>--stitchable</code> option prevents the artifacts.</p>
<pre tabindex="0"><code>--stitchable        Don&#39;t optimize headers based on video content
                    Ensures ability to recombine a segmented encode
</code></pre><p>The result is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mencoder -really-quiet <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /root/video/2159034.mp4 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ss <span style="color:#ae81ff">0</span> -endpos <span style="color:#ae81ff">60</span> -of rawvideo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ovc raw -nosound  -ofps <span style="color:#ae81ff">23</span>  <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add scale<span style="color:#f92672">=</span>404:720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add format<span style="color:#f92672">=</span>i420 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add hqdn3d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add harddup <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o - 2&gt;/dev/null | x264 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-progress --stitchable <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --preset slower <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --tune film --weightp <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --keyint <span style="color:#ae81ff">250</span> --min-keyint <span style="color:#ae81ff">25</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --non-deterministic --bframes <span style="color:#ae81ff">3</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-fast-pskip --qcomp 0.6 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --merange <span style="color:#ae81ff">24</span> --threads auto <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-csp i420  --level 3.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --profile high --crf <span style="color:#ae81ff">20</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-res 404x720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --fps <span style="color:#ae81ff">23</span> - --pass <span style="color:#ae81ff">1</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --stats <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /tmp/video/1415072454_part1/3000/part_12159034/1415072454.passLog <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o /dev/null
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$ mencoder -really-quiet <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /root/video/2159034.mp4 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ss <span style="color:#ae81ff">0</span> -endpos <span style="color:#ae81ff">60</span> -of rawvideo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ovc raw -nosound  -ofps <span style="color:#ae81ff">23</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add scale<span style="color:#f92672">=</span>404:720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add format<span style="color:#f92672">=</span>i420 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add hqdn3d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add harddup <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o - 2&gt;/dev/null |x264 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-progress --stitchable <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --preset slower <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --tune film --weightp <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --keyint <span style="color:#ae81ff">250</span> --min-keyint <span style="color:#ae81ff">25</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --non-deterministic --bframes <span style="color:#ae81ff">3</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-fast-pskip --qcomp 0.6 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --merange <span style="color:#ae81ff">24</span> --threads auto <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-csp i420  --level 3.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --profile high -B <span style="color:#ae81ff">3000</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-res 404x720 --fps <span style="color:#ae81ff">23</span> - --pass <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --stats /tmp/video/1415072454_part1/3000/part_12159034/1415072454.passLog <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o part1.264
</span></span></code></pre></div>]]></content><summary type="html"><![CDATA[ <p>Recently I&rsquo;ve been using x264&rsquo;s 2-pass encoding to perform segmented encoding on the same file, then merging the segments. This enables multi-process transcoding and even distributed cluster transcoding to speed up the encoding process.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mencoder -really-quiet <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /root/video/2159034.mp4 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ss <span style="color:#ae81ff">0</span> -endpos <span style="color:#ae81ff">60</span> -of rawvideo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ovc raw -nosound  -ofps <span style="color:#ae81ff">23</span>  <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add scale<span style="color:#f92672">=</span>404:720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add format<span style="color:#f92672">=</span>i420 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add hqdn3d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add harddup <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o - 2&gt;/dev/null | x264 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-progress --preset slower <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --tune film --weightp <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --keyint <span style="color:#ae81ff">250</span> --min-keyint <span style="color:#ae81ff">25</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --non-deterministic --bframes <span style="color:#ae81ff">3</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-fast-pskip --qcomp 0.6 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --merange <span style="color:#ae81ff">24</span> --threads auto <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-csp i420  --level 3.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --profile high --crf <span style="color:#ae81ff">20</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-res 404x720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --fps <span style="color:#ae81ff">23</span> - --pass <span style="color:#ae81ff">1</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --stats <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /tmp/video/1415072454_part1/3000/part_12159034/1415072454.passLog <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o /dev/null
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$ mencoder -really-quiet <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    /root/video/2159034.mp4 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ss <span style="color:#ae81ff">0</span> -endpos <span style="color:#ae81ff">60</span> -of rawvideo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -ovc raw -nosound  -ofps <span style="color:#ae81ff">23</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add scale<span style="color:#f92672">=</span>404:720 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add format<span style="color:#f92672">=</span>i420 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add hqdn3d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -vf-add harddup <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o - 2&gt;/dev/null |x264 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-progress --preset slower <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --tune film --weightp <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --keyint <span style="color:#ae81ff">250</span> --min-keyint <span style="color:#ae81ff">25</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --non-deterministic --bframes <span style="color:#ae81ff">3</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --no-fast-pskip --qcomp 0.6 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --merange <span style="color:#ae81ff">24</span> --threads auto <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-csp i420  --level 3.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --profile high -B <span style="color:#ae81ff">3000</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --input-res 404x720 --fps <span style="color:#ae81ff">23</span> - --pass <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --stats /tmp/video/1415072454_part1/3000/part_12159034/1415072454.passLog <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    -o part1.264
</span></span></code></pre></div><p>However, I encountered a problem during transcoding. The individual <code>.264</code> output files with MP4 container could play normally. But after merging the <code>.264</code> files into one, seeking would cause visual artifacts.</p>]]></summary></entry><entry><title>Chinese New Year</title><link href="https://www.ogura.io/posts/2014/02/chinese-new-year/"/><id>https://www.ogura.io/posts/2014/02/chinese-new-year/</id><published>20140207T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Exhausted.</p>
<p><img src="/images/2014/02/chinese-new-year-at-home.png" alt="Front door of my home"></p>
]]></content><summary type="html"><![CDATA[ <p>Exhausted.</p>
<p><img src="/images/2014/02/chinese-new-year-at-home.png" alt="Front door of my home"></p>]]></summary></entry><entry><title>Setting Up Jekyll on Windows Azure</title><link href="https://www.ogura.io/posts/2014/01/azure-jekyll/"/><id>https://www.ogura.io/posts/2014/01/azure-jekyll/</id><published>20140112T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>I just<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> received an email from Windows Azure:</p>
<blockquote>
<p>You are invited to participate in the Windows Azure beta free trial operated by 21Vianet</p>
</blockquote>
<p>I was lucky enough to get a Windows Azure beta trial VPS, and they didn&rsquo;t specify an expiration date, so it looks like I can use it for free for at least three months.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Of course, this VPS is on the domestic network, so &ldquo;setting up a ladder&rdquo; (VPN) is out of the question. But I can still host a blog on it.</p>
<p>So I migrated my Jekyll blog from GitHub Pages<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> (at least I don&rsquo;t need to start a VM to write blog posts - I can just log into the VPS directly).</p>
<h1 id="setting-up-a-jekyll-environment">Setting Up a Jekyll Environment</h1>
<p>When creating the virtual machine on Windows Azure, I chose CentOS (mainly because I use it at work), then next, next&hellip; and the VM was created.</p>
<p>Then I opened an SSH terminal, entered the hostname, port, username, and password that I filled in when creating the VM, and logged into the virtual machine.</p>
<p>The first thing after logging in was to edit the <code>/etc/yum.conf</code> file:</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[main]
cachedir=/var/cache/yum/$basearch/$releasever
keepcache=0
debuglevel=2
logfile=/var/log/yum.log
exactarch=1
obsoletes=1
gpgcheck=1
plugins=1
installonly_limit=5
bugtracker_url=http://bugs.centos.org/set_project.php?project_id=16&amp;ref=http://bugs.centos.org/bug_report_page.php?category=yum
distroverpkg=centos-release
# exclude=kernel*
#  This is the default, if you make this bigger yum won&#39;t see if the metadata
# is newer on the remote and so you&#39;ll &#34;gain&#34; the bandwidth of not having to
# download the new metadata and &#34;pay&#34; for it by yum not having correct
# information.
#  It is esp. important, to have correct metadata, for distributions like
# Fedora which don&#39;t keep old packages around. If you don&#39;t like this checking
# interupting your command line usage, it&#39;s much better to have something
# manually check the metadata once an hour (yum-updatesd will do this).
# metadata_expire=90m

# PUT YOUR REPOS HERE OR IN separate files named file.repo
# in /etc/yum.repos.d
</code></pre><p>Comment out <code>exclude=kernel*</code>, otherwise you&rsquo;ll get errors when installing packages.</p>
<p>Since Jekyll requires a Ruby environment, execute the following command in the terminal to install Ruby:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ curl -L https://get.rvm.io | bash -s stable --ruby
</span></span></code></pre></div><p>For Jekyll installation, as stated in the official Jekyll documentation, execute in the terminal:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gem install jekyll
</span></span></code></pre></div><p>But when I ran this command, I found that gem wasn&rsquo;t responding. I then ran:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gem install jekyll -V
</span></span></code></pre></div><p>I discovered that the lack of response was because the gem source was inaccessible due to domestic network restrictions (you know what I mean).</p>
<p>I immediately followed the documentation at <a href="http://ruby.taobao.org/">http://ruby.taobao.org/</a>, removed the inaccessible source, and updated to Taobao&rsquo;s mirror.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gem sources --remove https://rubygems.org/
</span></span><span style="display:flex;"><span>$ gem sources -a http://ruby.taobao.org/
</span></span><span style="display:flex;"><span>$ gem sources -l
</span></span><span style="display:flex;"><span>*** CURRENT SOURCES ***
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>http://ruby.taobao.org
</span></span></code></pre></div><p>Then I ran:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gem install jekyll
</span></span></code></pre></div><p>It was fast and smooth. ^_^</p>
<hr>
<p><strong>Postscript</strong></p>
<p>But I still don&rsquo;t know if I can point a domain without ICP registration to it&hellip; so I&rsquo;m still using that long, ugly, and hard-to-remember <code>xxxxxx.chinacloudapp.cn</code><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>&hellip; =w=</p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I should avoid using words like &ldquo;just&rdquo; in articles in the future - looking back years later only adds to the sentiment&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>After searching through emails, this trial ended on <code>2015/3/18</code>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Looking back now, this migration was completely unnecessary - being able to dig up old blog posts is entirely thanks to GitHub&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>I have no memory of this domain at all - seems like writing blog posts is indeed necessary&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>I just<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> received an email from Windows Azure:</p>
<blockquote>
<p>You are invited to participate in the Windows Azure beta free trial operated by 21Vianet</p>
</blockquote>
<p>I was lucky enough to get a Windows Azure beta trial VPS, and they didn&rsquo;t specify an expiration date, so it looks like I can use it for free for at least three months.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Of course, this VPS is on the domestic network, so &ldquo;setting up a ladder&rdquo; (VPN) is out of the question. But I can still host a blog on it.</p>]]></summary></entry><entry><title>Bootstrap Tips</title><link href="https://www.ogura.io/posts/2014/01/bootstrap-recommend-website/"/><id>https://www.ogura.io/posts/2014/01/bootstrap-recommend-website/</id><published>20140110T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>After building some interfaces with Bootstrap, I realized that &ldquo;copying&rdquo; is the essence of using Bootstrap.</p>
<p>So I found this site: <a href="http://bootsnipp.com/">bootsnipp</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Opening this website, although the appearance has changed significantly, it&rsquo;s still the same one from back then.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>After building some interfaces with Bootstrap, I realized that &ldquo;copying&rdquo; is the essence of using Bootstrap.</p>
<p>So I found this site: <a href="http://bootsnipp.com/">bootsnipp</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Opening this website, although the appearance has changed significantly, it&rsquo;s still the same one from back then.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></summary></entry><entry><title>Got an iPhone 5s</title><link href="https://www.ogura.io/posts/2013/12/ip5s/"/><id>https://www.ogura.io/posts/2013/12/ip5s/</id><published>20131225T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Near the end of 2013, I finally switched to a smartphone, which cheered me up quite a bit.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>The days without even WeChat<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> are finally gone forever, but is that really surprising?</p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I clearly remember that day: because of a shady real estate agent, the landlord kicked me out. I had just moved all my belongings into a newly rented small room (although this room was later demolished due to illegal partition inspection). That&rsquo;s when I received the iPhone 5s I had just bought, playing with it on that small bed by the window, absolutely loving it.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>The phones I used before were Nokia and BlackBerry, and WeChat was only available on iOS and Android.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>Near the end of 2013, I finally switched to a smartphone, which cheered me up quite a bit.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>The days without even WeChat<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> are finally gone forever, but is that really surprising?</p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I clearly remember that day: because of a shady real estate agent, the landlord kicked me out. I had just moved all my belongings into a newly rented small room (although this room was later demolished due to illegal partition inspection). That&rsquo;s when I received the iPhone 5s I had just bought, playing with it on that small bed by the window, absolutely loving it.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>]]></summary></entry><entry><title>The Risks of git reset</title><link href="https://www.ogura.io/posts/2013/11/git-abuse/"/><id>https://www.ogura.io/posts/2013/11/git-abuse/</id><published>20131122T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>When using <code>git pull origin master</code> to update code, you sometimes encounter conflicts that prevent merging. When you know the remote code is correct and you just need to overwrite the local code, I used to do this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git fetch --all
</span></span><span style="display:flex;"><span>git reset --hard origin/master
</span></span></code></pre></div><p>And I would get the result I wanted.</p>
<p>But today, when I repeated the same operation, an accident happened. A lot of useful data was deleted after this operation and couldn&rsquo;t be recovered.</p>
<p>My Git environment was like this:</p>
<p>This was a system running in production that generates a lot of historical data in real-time in the same directory. However, due to the large data volume, it wasn&rsquo;t added to git tracking. But the <code>.gitignore</code> file wasn&rsquo;t properly written. And since it was a multi-person project, someone had executed <code>git add --all</code> at some point.</p>
<p>Just like that&hellip; precious historical data was deleted after an irreversible <code>git reset --hard</code> operation.</p>
<p>The data was never recovered&hellip;<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>So when managing projects with git, keep these points in mind:</p>
<ol>
<li>Use <code>git reset --hard</code> with caution</li>
<li>Write a proper <code>.gitignore</code> file</li>
<li>Back up production data</li>
<li>Don&rsquo;t add untracked files carelessly</li>
</ol>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This was the biggest production incident I&rsquo;ve caused due to my operational mistake - losing operational historical data. I still take this as a warning to this day.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>When using <code>git pull origin master</code> to update code, you sometimes encounter conflicts that prevent merging. When you know the remote code is correct and you just need to overwrite the local code, I used to do this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git fetch --all
</span></span><span style="display:flex;"><span>git reset --hard origin/master
</span></span></code></pre></div><p>And I would get the result I wanted.</p>
<p>But today, when I repeated the same operation, an accident happened. A lot of useful data was deleted after this operation and couldn&rsquo;t be recovered.</p>]]></summary></entry><entry><title>Merging Commits with git rebase</title><link href="https://www.ogura.io/posts/2013/09/git-rebase/"/><id>https://www.ogura.io/posts/2013/09/git-rebase/</id><published>20130907T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Recently I&rsquo;ve been frequently using git for version control and code commits, and gradually discovered the usage and benefits of git rebase. It&rsquo;s especially flexible when organizing and merging git commit history.</p>
<h1 id="use-case">Use Case</h1>
<p>After making a commit, you immediately notice that a line or two of code needs to be changed. After making the change, you commit again, then find something else that&rsquo;s not right&hellip;</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># vim test.txt</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># git commit -am &#34;add test.txt&#34;</span>
</span></span></code></pre></div><p>Found some code that needs minor changes:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># vim test.txt</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># git commit -am &#34;update test.txt&#34;</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div><p>Found issues for the Nth time:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># vim test.txt</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># git commit -am &#34;update test.txt again and again&#34;</span>
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div><h1 id="the-problem">The Problem</h1>
<p>After doing this multiple times, the git commit history becomes messy - a bunch of small changes taking up many commit entries:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># git log</span>
</span></span><span style="display:flex;"><span>commit abe26fe0f5b1788e8f7b1949082e1477c5337aa0
</span></span><span style="display:flex;"><span>Author: xiaocang &lt;xiaocang@addcp.com&gt;
</span></span><span style="display:flex;"><span>Date:   Sat Sep <span style="color:#ae81ff">7</span> 01:29:48 <span style="color:#ae81ff">2013</span> +0800
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    update test.txt again and again
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>commit c3633d5153bacabeceaeaeb552484e7f9b6a2b9c
</span></span><span style="display:flex;"><span>Author: xiaocang &lt;xiaocang@addcp.com&gt;
</span></span><span style="display:flex;"><span>Date:   Sat Sep <span style="color:#ae81ff">7</span> 01:29:21 <span style="color:#ae81ff">2013</span> +0800
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    update test.txt again
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>commit c8bc032daac7d0859a0e73cd83a464cee0f59625
</span></span><span style="display:flex;"><span>Author: xiaocang &lt;xiaocang@addcp.com&gt;
</span></span><span style="display:flex;"><span>Date:   Sat Sep <span style="color:#ae81ff">7</span> 01:28:51 <span style="color:#ae81ff">2013</span> +0800
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    update test.txt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>commit 0e80061e90503189dd7f189297aed809953e5e8b
</span></span><span style="display:flex;"><span>Author: xiaocang &lt;xiaocang@addcp.com&gt;
</span></span><span style="display:flex;"><span>Date:   Sat Sep <span style="color:#ae81ff">7</span> 01:28:22 <span style="color:#ae81ff">2013</span> +0800
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    add test.txt
</span></span><span style="display:flex;"><span>......
</span></span></code></pre></div><h1 id="rebase-example-and-usage">Rebase Example and Usage</h1>
<p>Using git rebase to solve this problem is very easy.</p>
<p>The git rebase syntax is:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git rebase <span style="color:#f92672">[</span>--interactive | -i<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>-v<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>--force-rebase | -f<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>--no-ff<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>--onto &lt;newbase&gt;<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>&lt;upstream&gt;|--root<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>&lt;branch&gt;<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>--quiet | -q<span style="color:#f92672">]</span>
</span></span></code></pre></div><p>Here we use the <code>-i</code> option, which is interactive rebase:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># git -i HEAD~3</span>
</span></span></code></pre></div><p>Where <code>HEAD~3</code> refers to the last three commits of this branch. You can also add a <code>&lt;branch&gt;</code> parameter to specify the branch to rebase.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pick c8bc032 update test.txt
</span></span><span style="display:flex;"><span>f c3633d5 update test.txt again
</span></span><span style="display:flex;"><span>f abe26fe update test.txt again and again
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Rebase 0e80061..abe26fe onto 0e80061</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Commands:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#  p, pick = use commit</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#  r, reword = use commit, but edit the commit message</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#  e, edit = use commit, but stop for amending</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#  s, squash = use commit, but meld into previous commit</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#  f, fixup = like &#34;squash&#34;, but discard this commit&#39;s log message</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># If you remove a line here THAT COMMIT WILL BE LOST.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># However, if you remove everything, the rebase will be aborted.</span>
</span></span></code></pre></div><p>Where <code>pick</code> commits as-is.</p>
<p><code>reword</code> also commits, but lets you edit the commit message before committing.</p>
<p><code>edit</code> is similar to <code>reword</code>, but after editing the commit message, it waits for you to <code>git commit --amend</code>, meaning it waits for you to add some files and changes. Then use <code>git rebase --continue</code> to complete the entire rebase.</p>
<p><code>squash</code> merges this commit into the previous one, but combines the log content of this commit into the previous one.</p>
<p><code>fixup</code> differs from <code>squash</code> in that it automatically ignores this commit&rsquo;s log content. This is exactly what I needed for this commit cleanup.</p>
<h1 id="rebase-result">Rebase Result</h1>
<p>As shown above, I get a log like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># git log</span>
</span></span><span style="display:flex;"><span>commit 3e5d79d149942f7dc0741a9a42ce43d98beafcd3
</span></span><span style="display:flex;"><span>Author: xiaocang &lt;xiaocang@addcp.com&gt;
</span></span><span style="display:flex;"><span>Date:   Sat Sep <span style="color:#ae81ff">7</span> 01:28:51 <span style="color:#ae81ff">2013</span> +0800
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    update test.txt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>commit 0e80061e90503189dd7f189297aed809953e5e8b
</span></span><span style="display:flex;"><span>Author: xiaocang &lt;xiaocang@addcp.com&gt;
</span></span><span style="display:flex;"><span>Date:   Sat Sep <span style="color:#ae81ff">7</span> 01:28:22 <span style="color:#ae81ff">2013</span> +0800
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    add test.txt
</span></span></code></pre></div><p>Now everything is clean and tidy.</p>
<hr>
<p><strong>Note</strong></p>
<p>Anyone who has read the official git documentation should know:</p>
<blockquote>
<p>Do not rebase commits that have been pushed to a public repository</p>
</blockquote>
<p>Because changing the commit history on a public repository is not good for a multi-person collaborative project.</p>
<p>Other people on the project will be confused because a commit that previously existed suddenly disappears.</p>
<p>So rebasing is mainly a method for cleaning up commit history before pushing to a public repository.</p>
]]></content><summary type="html"><![CDATA[ <p>Recently I&rsquo;ve been frequently using git for version control and code commits, and gradually discovered the usage and benefits of git rebase. It&rsquo;s especially flexible when organizing and merging git commit history.</p>
<h1 id="use-case">Use Case</h1>
<p>After making a commit, you immediately notice that a line or two of code needs to be changed. After making the change, you commit again, then find something else that&rsquo;s not right&hellip;</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># vim test.txt</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>root@addcp gitdemo<span style="color:#f92672">]</span><span style="color:#75715e"># git commit -am &#34;add test.txt&#34;</span>
</span></span></code></pre></div><p>Found some code that needs minor changes:</p>]]></summary></entry><entry><title>A Blog That Changes Domain Every Year</title><link href="https://www.ogura.io/posts/2013/09/about-blog/"/><id>https://www.ogura.io/posts/2013/09/about-blog/</id><published>20130904T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>This is a blog that changes its domain every year&hellip; (why did I say that out loud)
I&rsquo;ve tentatively settled on this domain <a href="http://addcp.com">addcp.com</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Writing some thoughts, random ideas, and technical articles<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I no longer renew this domain. Looking back at my domain purchasing history, I can no longer understand that kind of enthusiasm <img src="/images/2013/09/linost-domain-histroy-screenshot.png" alt="domains">&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I&rsquo;m glad that this is still the case today&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>This is a blog that changes its domain every year&hellip; (why did I say that out loud)
I&rsquo;ve tentatively settled on this domain <a href="http://addcp.com">addcp.com</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Writing some thoughts, random ideas, and technical articles<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I no longer renew this domain. Looking back at my domain purchasing history, I can no longer understand that kind of enthusiasm <img src="/images/2013/09/linost-domain-histroy-screenshot.png" alt="domains">&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I&rsquo;m glad that this is still the case today&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></summary></entry><entry><title>School Starts</title><link href="https://www.ogura.io/posts/2013/09/starting-school/"/><id>https://www.ogura.io/posts/2013/09/starting-school/</id><published>20130901T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>September 1st is the annual day when primary, middle, and high schools start.</p>
<p>In previous years, I would feel a bit sad because the vacation was ending.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>This year, having just graduated, I still feel a bit sad though I don&rsquo;t know why.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I&rsquo;ve already forgotten this feeling of sadness&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I&rsquo;ve gotten used to this feeling now&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>September 1st is the annual day when primary, middle, and high schools start.</p>
<p>In previous years, I would feel a bit sad because the vacation was ending.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>This year, having just graduated, I still feel a bit sad though I don&rsquo;t know why.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I&rsquo;ve already forgotten this feeling of sadness&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I&rsquo;ve gotten used to this feeling now&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></summary></entry><entry><title>Using Truncated Output in Jekyll</title><link href="https://www.ogura.io/posts/2013/04/jekyll-excerpt/"/><id>https://www.ogura.io/posts/2013/04/jekyll-excerpt/</id><published>20130411T00:00:00.008Z</published><updated>20260209T12:36:15.008Z</updated><content type="html"><![CDATA[ <p>Since I had been using <a href="http://wordpress.org">WordPress</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> before, after switching to Jekyll I naturally wanted to find a way to recognize <code>&lt;!-- more --&gt;</code> for truncating output.</p>
<h1 id="plugins">Plugins</h1>
<p>After a quick Google search, I found relevant plugins:</p>
<ul>
<li><a href="https://github.com/sebcioz/jekyll-only_first_p">jekyll-only_first_p</a>: A plugin that outputs only the first paragraph <strong>requires <a href="http://nokogiri.org/">Nokogiri</a> support</strong></li>
<li><a href="https://gist.github.com/stympy/986665">excerpt.rb</a>: A plugin that recognizes <code>&lt;!-- more --&gt;</code></li>
</ul>
<p>However, if your Jekyll is also hosted on GitHub, you can&rsquo;t use plugins.
For security reasons, GitHub runs Jekyll with the <code>--safe</code> parameter, rendering all third-party plugins ineffective.</p>
<h1 id="liquid">Liquid</h1>
<p>Just when I was feeling a bit disappointed, I found this article: <a href="http://foldl.me/2012/jekyll-excerpts/">Post excerpts in Jekyll</a><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>You can achieve this using a filter in the Liquid template language:</p>
<pre tabindex="0"><code class="language-liquid" data-lang="liquid">{{ post.content | split: &#34;&lt;!-- more --&gt;&#34; | first }}
</code></pre><p>Then add a <code>Read More</code> link after the truncated article:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;#more</span>&gt;Read More →&lt;/<span style="color:#f92672">a</span>&gt;
</span></span></code></pre></div><p>This way, even Jekyll hosted on GitHub can have truncated output.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<blockquote>
<p>Notes added on 2018-10-10</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I vaguely remember setting up a WordPress blog on free PHP hosting during college&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Unfortunately, this link is no longer working, and I don&rsquo;t know what exactly I saw through this link back in 2013&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Truncating article output on the homepage is not strictly necessary&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><summary type="html"><![CDATA[ <p>Since I had been using <a href="http://wordpress.org">WordPress</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> before, after switching to Jekyll I naturally wanted to find a way to recognize <code>&lt;!-- more --&gt;</code> for truncating output.</p>
<h1 id="plugins">Plugins</h1>
<p>After a quick Google search, I found relevant plugins:</p>
<ul>
<li><a href="https://github.com/sebcioz/jekyll-only_first_p">jekyll-only_first_p</a>: A plugin that outputs only the first paragraph <strong>requires <a href="http://nokogiri.org/">Nokogiri</a> support</strong></li>
<li><a href="https://gist.github.com/stympy/986665">excerpt.rb</a>: A plugin that recognizes <code>&lt;!-- more --&gt;</code></li>
</ul>
<p>However, if your Jekyll is also hosted on GitHub, you can&rsquo;t use plugins.
For security reasons, GitHub runs Jekyll with the <code>--safe</code> parameter, rendering all third-party plugins ineffective.</p>]]></summary></entry></rss>